Compare commits
101 Commits
CdrskinZer
...
ZeroTwoSix
Author | SHA1 | Date | |
---|---|---|---|
9ab5290059 | |||
ec1aa0c49a | |||
80639a7749 | |||
8c82e3c437 | |||
de02cc26f9 | |||
35a1a1f7d7 | |||
54d3702513 | |||
c3f9764e0a | |||
ab4096e720 | |||
26fa325ee4 | |||
314027631a | |||
6e09b56c29 | |||
08f541a7a8 | |||
26412b4793 | |||
9ea689d7e4 | |||
f3b994f845 | |||
46906c7f65 | |||
4daed7c4a9 | |||
ebb94ee212 | |||
18a592ad28 | |||
ba48efbc52 | |||
0513a4eb2f | |||
254852ee36 | |||
ef6d76c22d | |||
66c3ead5d6 | |||
908a6b336b | |||
373e401671 | |||
06d008d6c6 | |||
11c9444b6c | |||
ff284b3f51 | |||
628e935fe5 | |||
13641fcbae | |||
595b877a24 | |||
17d1d2e8a6 | |||
c1b3d3341e | |||
725b6515ff | |||
61a535f3ac | |||
7c312a33d9 | |||
b7d369dbb6 | |||
16cbb18f15 | |||
4f83818ff8 | |||
9e383a3b9e | |||
a9b7d8250e | |||
1ec94d9226 | |||
c1607eeac4 | |||
8927e3df9f | |||
7458223cc0 | |||
5a0b597e3f | |||
bf20758906 | |||
1bfd7b5a5f | |||
aa813303a4 | |||
9baf20e907 | |||
1947c3a25d | |||
6b22dadcdd | |||
4efccd15b3 | |||
496fb67c29 | |||
bc97bdfa71 | |||
68784d8703 | |||
10c01ebc83 | |||
6c22b98235 | |||
62bd5963d0 | |||
726cd2664d | |||
5378638ba7 | |||
347aa3fe0c | |||
c9478cc15e | |||
ce0d783e82 | |||
5057fe48ab | |||
fcc755a5b8 | |||
ab8127f980 | |||
dad50348db | |||
140ff9ade4 | |||
2a64a0d3b1 | |||
82983cd7c1 | |||
b31e882582 | |||
dd80b2fc88 | |||
3051fc1987 | |||
e54be83b27 | |||
448aac8880 | |||
db4522bbdc | |||
d7c34f05e4 | |||
b96fbd5bd8 | |||
25d5a2afe4 | |||
66515523c1 | |||
684cfd0cd2 | |||
a0e745c54a | |||
a239d54bd7 | |||
c98dd9b0a7 | |||
193d674852 | |||
15e37555ae | |||
d337722920 | |||
5af92d13fe | |||
114307a679 | |||
99ca953b1d | |||
b837cc134d | |||
d3ee0b72e4 | |||
6914e2f7e2 | |||
37017119b0 | |||
447d2365c7 | |||
ece61a1860 | |||
acc6bdd95c | |||
559811e8ef |
2
CONTRIBUTORS
Normal file
2
CONTRIBUTORS
Normal file
@ -0,0 +1,2 @@
|
||||
Joe Neeman
|
||||
Philippe Rouquier
|
67
Makefile.am
67
Makefile.am
@ -1,7 +1,7 @@
|
||||
pkgconfigdir=$(libdir)/pkgconfig
|
||||
libincludedir=$(includedir)/libburn
|
||||
|
||||
lib_LTLIBRARIES = libburn/libburn.la libisofs/libisofs.la
|
||||
lib_LTLIBRARIES = libburn/libburn.la
|
||||
|
||||
## ========================================================================= ##
|
||||
|
||||
@ -39,6 +39,7 @@ libburn_libburn_la_SOURCES = \
|
||||
libburn/null.h \
|
||||
libburn/options.c \
|
||||
libburn/options.h \
|
||||
libburn/os.h \
|
||||
libburn/read.c \
|
||||
libburn/read.h \
|
||||
libburn/sbc.c \
|
||||
@ -64,44 +65,18 @@ libburn_libburn_la_SOURCES = \
|
||||
|
||||
## libburn/sg-@ARCH@.c \
|
||||
|
||||
libisofs_libisofs_la_LDFLAGS = \
|
||||
-version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE)
|
||||
libisofs_libisofs_la_SOURCES = \
|
||||
libisofs/tree.h \
|
||||
libisofs/tree.c \
|
||||
libisofs/volume.h \
|
||||
libisofs/volume.c \
|
||||
libisofs/util.h \
|
||||
libisofs/util.c \
|
||||
libisofs/ecma119.c \
|
||||
libisofs/ecma119.h \
|
||||
libisofs/ecma119_tree.c \
|
||||
libisofs/ecma119_tree.h \
|
||||
libisofs/susp.h \
|
||||
libisofs/susp.c \
|
||||
libisofs/rockridge.h \
|
||||
libisofs/rockridge.c \
|
||||
libisofs/joliet.c \
|
||||
libisofs/joliet.h \
|
||||
libisofs/exclude.c \
|
||||
libisofs/exclude.h \
|
||||
libisofs/hash.h \
|
||||
libisofs/hash.c
|
||||
|
||||
libinclude_HEADERS = \
|
||||
libburn/libburn.h \
|
||||
libisofs/libisofs.h
|
||||
libburn/libburn.h
|
||||
|
||||
## ========================================================================= ##
|
||||
|
||||
## Build test applications
|
||||
noinst_PROGRAMS = \
|
||||
test/libburner \
|
||||
test/telltoc \
|
||||
test/dewav \
|
||||
test/fake_au \
|
||||
test/iso \
|
||||
test/poll \
|
||||
test/toc \
|
||||
test/structest
|
||||
|
||||
bin_PROGRAMS = \
|
||||
@ -110,6 +85,9 @@ bin_PROGRAMS = \
|
||||
test_libburner_CPPFLAGS = -Ilibburn
|
||||
test_libburner_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS)
|
||||
test_libburner_SOURCES = test/libburner.c
|
||||
test_telltoc_CPPFLAGS = -Ilibburn
|
||||
test_telltoc_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS)
|
||||
test_telltoc_SOURCES = test/telltoc.c
|
||||
test_dewav_CPPFLAGS = -Ilibburn
|
||||
test_dewav_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS)
|
||||
test_dewav_SOURCES = test/dewav.c
|
||||
@ -119,22 +97,15 @@ test_fake_au_SOURCES = test/fake_au.c
|
||||
test_poll_CPPFLAGS = -Ilibburn
|
||||
test_poll_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS)
|
||||
test_poll_SOURCES = test/poll.c
|
||||
test_toc_CPPFLAGS = -Ilibburn
|
||||
test_toc_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS)
|
||||
test_toc_SOURCES = test/toc.c
|
||||
test_structest_CPPFLAGS = -Ilibburn
|
||||
test_structest_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS)
|
||||
test_structest_SOURCES = test/structest.c
|
||||
test_iso_CPPFLAGS = -Ilibisofs
|
||||
test_iso_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS)
|
||||
test_iso_SOURCES = test/iso.c
|
||||
|
||||
## cdrskin construction site - ts A60816
|
||||
cdrskin_cdrskin_CPPFLAGS = -Ilibburn
|
||||
cdrskin_cdrskin_CFLAGS = -DCdrskin_libburn_0_2_3
|
||||
cdrskin_cdrskin_CFLAGS = -DCdrskin_libburn_0_2_6
|
||||
cdrskin_cdrskin_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS)
|
||||
cdrskin_cdrskin_SOURCES = cdrskin/cdrskin.c cdrskin/cdrfifo.c cdrskin/cdrfifo.h cdrskin/cdrskin_timestamp.h
|
||||
## cdrskin_cdrskin_SOURCES = cdrskin/cdrskin.c cdrskin/cdrfifo.c cdrskin/cdrfifo.h cdrskin/cleanup.c cdrskin/cleanup.h cdrskin/cdrskin_timestamp.h
|
||||
##
|
||||
## Open questions: how to compute $timestamp and express -DX="$timestamp"
|
||||
##
|
||||
@ -174,13 +145,9 @@ uninstall-local:
|
||||
|
||||
# Indent source files
|
||||
indent_files = \
|
||||
$(libisofs_libisofs_la_SOURCES) \
|
||||
$(libburn_libburn_la_SOURCES) \
|
||||
$(test_libburner_SOURCES) \
|
||||
$(test_poll_SOURCES) \
|
||||
$(test_toc_SOURCES) \
|
||||
$(test_structest_SOURCES) \
|
||||
$(test_iso_SOURCES)
|
||||
$(test_structest_SOURCES)
|
||||
|
||||
|
||||
indent: $(indent_files)
|
||||
@ -196,12 +163,13 @@ indent: $(indent_files)
|
||||
|
||||
# Extra things
|
||||
nodist_pkgconfig_DATA = \
|
||||
libburn-1.pc \
|
||||
libisofs-1.pc
|
||||
libburn-1.pc
|
||||
|
||||
# http://www.nada.kth.se/cgi-bin/info?(automake.info)Man%20pages
|
||||
man_MANS = cdrskin/cdrskin.1
|
||||
|
||||
EXTRA_DIST = \
|
||||
libburn-1.pc.in \
|
||||
libisofs-1.pc.in \
|
||||
version.h.in \
|
||||
doc/comments \
|
||||
doc/doxygen.conf.in \
|
||||
@ -215,5 +183,12 @@ EXTRA_DIST = \
|
||||
cdrskin/changelog.txt \
|
||||
cdrskin/cdrskin_eng.html \
|
||||
cdrskin/wiki_plain.txt \
|
||||
COPYING
|
||||
cdrskin/cleanup.h \
|
||||
cdrskin/cleanup.c \
|
||||
libburn/os-freebsd.h \
|
||||
libburn/os-linux.h \
|
||||
libburn/sg-freebsd.c \
|
||||
libburn/sg-linux.c \
|
||||
COPYING \
|
||||
$(man_MANS)
|
||||
|
||||
|
33
README
33
README
@ -27,8 +27,8 @@ This libburn.pykix.org toplevel README (C) 2006 Thomas Schmitt
|
||||
Our build system is based on autotools. For preparing the build of a SVN
|
||||
snapshot you will need autotools of at least version 1.7.
|
||||
Check out from SVN by
|
||||
svn co http://libburn-svn.pykix.org/trunk libburn_pykix
|
||||
and apply autotools by
|
||||
svn co http://libburn-svn.pykix.org/libburn/trunk libburn_pykix
|
||||
go into directory libburn_pykix and apply autotools by
|
||||
./bootstrap
|
||||
|
||||
Alternatively you may unpack a release tarball for which you do not need
|
||||
@ -42,22 +42,29 @@ into its toplevel directory (here: "libburn_pykix") and execute
|
||||
To make the libraries accessible for running resp. developing applications
|
||||
make install
|
||||
|
||||
|
||||
The other half of the project, libisofs, is hosted in the libburn SVN, too:
|
||||
svn co http://libburn-svn.pykix.org/libisofs/trunk libisofs_pykix
|
||||
See README file there.
|
||||
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
|
||||
Overview of libburn.pykix.org
|
||||
|
||||
libburn.pykix.org is an open-source library for reading, mastering and writing
|
||||
optical discs. For now this means only CD-R and CD-RW.
|
||||
libburn.pykix.org is an open-source software project for reading, mastering and
|
||||
writing optical discs. For now this means only CD-R and CD-RW.
|
||||
|
||||
The project comprises of several more or less interdependent parts which
|
||||
together strive to be a usable foundation for application development.
|
||||
These are libraries, language bindings, and middleware binaries which emulate
|
||||
classical (and valuable) Linux tools.
|
||||
|
||||
Our scope is currently Linux 2.4 and 2.6 and we will have a hard time to widen
|
||||
this for now, because of our history. The project could need advise from or
|
||||
membership of skilled kernel people and people who know how to talk CD/DVD
|
||||
drives into doing things.
|
||||
Our scope is currently Linux 2.4 and 2.6 only. For ports to other systems
|
||||
we would need : login on a development machine resp. a live OS on CD or DVD,
|
||||
advise from a system person about the equivalent of Linux sg or FreeBSD CAM,
|
||||
volunteers for testing of realistic use cases.
|
||||
|
||||
We do have a workable code base for burning data CDs, though. The burn API is
|
||||
quite comprehensively documented and can be used to build a presentable
|
||||
@ -152,6 +159,16 @@ Project history as far as known to me:
|
||||
|
||||
- 16th September 2006 feature freeze for release of libburn-0.2.2 .
|
||||
|
||||
- 20th September 2006 release of libburn-0.2.2 .
|
||||
|
||||
- 26th October 2006 feature freeze for cdrskin-0.2.4 based on libburn-0.2.3 .
|
||||
This version of cdrskin is much more cdrecord compatible in repect
|
||||
to drive addressing and audio features.
|
||||
|
||||
- 30th October 2006 release of cdrskin-0.2.4 .
|
||||
|
||||
- 13th November 2006 splitting releases of libburn+cdrskin from libisofs.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
|
@ -3,5 +3,8 @@
|
||||
aclocal
|
||||
libtoolize --copy --force
|
||||
autoconf
|
||||
autoheader
|
||||
|
||||
# ts A61101 : libburn is not prepared for config.h
|
||||
# autoheader
|
||||
|
||||
automake --foreign --add-missing --copy --include-deps
|
||||
|
@ -26,12 +26,12 @@ following possible.
|
||||
cdrskin. By Thomas Schmitt <scdbackup@gmx.net>
|
||||
Integrated sub project of libburn.pykix.org but also published via:
|
||||
http://scdbackup.sourceforge.net/cdrskin_eng.html
|
||||
http://scdbackup.sourceforge.net/cdrskin-0.2.3.tar.gz
|
||||
http://scdbackup.sourceforge.net/cdrskin-0.2.6.pl01.tar.gz
|
||||
Copyright (C) 2006 Thomas Schmitt
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
On top of libburn there is implemented cdrskin 0.2.3, a limited cdrecord
|
||||
On top of libburn there is implemented cdrskin 0.2.6, a limited cdrecord
|
||||
compatibility wrapper which allows to use some libburn features from
|
||||
the command line.
|
||||
Interested users of cdrecord are invited to participate in the development
|
||||
@ -59,16 +59,16 @@ systems, including 64 bit systems. (Further reports are welcome.)
|
||||
|
||||
Compilation, First Glimpse, Installation
|
||||
|
||||
Obtain cdrskin-0.2.3.tar.gz , take it to a directory of your choice and do:
|
||||
Obtain cdrskin-0.2.6.pl02.tar.gz, take it to a directory of your choice and do:
|
||||
|
||||
tar xzf cdrskin-0.2.3.tar.gz
|
||||
cd cdrskin-0.2.3
|
||||
tar xzf cdrskin-0.2.6.pl02.tar.gz
|
||||
cd cdrskin-0.2.6
|
||||
|
||||
Or obtain a libburn.pykix.org SVN snapshot,
|
||||
go into the toplevel directory of the snapshot (e.g. cd libburn_pykix ),
|
||||
and execute the autotools script ./bootstrap . Use autools version >= 1.7 .
|
||||
|
||||
Within that toplevel directory of either cdrskin-0.2.3 or libburn then execute:
|
||||
Within that toplevel directory of either cdrskin-0.2.6 or libburn then execute:
|
||||
|
||||
./configure
|
||||
make
|
||||
@ -86,10 +86,11 @@ In order to surely get a standalone binary, execute
|
||||
|
||||
cdrskin/compile_cdrskin.sh
|
||||
|
||||
Version identification an help texts available afterwards:
|
||||
Version identification and help texts available afterwards:
|
||||
cdrskin/cdrskin -version
|
||||
cdrskin/cdrskin --help
|
||||
cdrskin/cdrskin -help
|
||||
man cdrskin/cdrskin.1
|
||||
|
||||
Install (eventually as superuser) cdrskin to a directory where it can be found:
|
||||
If cdrskin was already installed by a previous version, or by "make install"
|
||||
@ -108,6 +109,10 @@ It will not collide with an installed version of libburn either.
|
||||
But libpthread must be installed on the system and glibc has to match. (See
|
||||
below for a way to create a statically linked binary.)
|
||||
|
||||
To install the man page, you may do: echo $MANPATH and choose one of the
|
||||
listed directories to copy the man-page under its ./man1 directory. Like:
|
||||
cp cdrskin/cdrskin.1 /usr/share/man/man1/cdrskin.1
|
||||
|
||||
|
||||
Usage
|
||||
|
||||
@ -169,8 +174,8 @@ Burn image file my_image.iso to CD
|
||||
-eject my_image.iso
|
||||
|
||||
Burn a compressed afio archive to CD on-the-fly
|
||||
find . | afio -oZ - | cdrskin -v dev=0,1,0 fs=32m speed=8 -sao \
|
||||
driveropts=burnfree padsize=300k tsize=650m -
|
||||
find . | afio -oZ - | cdrskin -v dev=0,1,0 fs=32m speed=8 -tao \
|
||||
driveropts=burnfree padsize=300k -
|
||||
|
||||
Burn 6 audio tracks from files with different formats to CD.
|
||||
Anything except .wav or .au files has to be converted into raw format first.
|
||||
@ -181,7 +186,7 @@ See below "Audio CD" for specifications.
|
||||
madplay -o raw:track04.cd /path/to/track4.mp3
|
||||
mppdec --raw-le /path/to/track5.mpc track05.cd
|
||||
|
||||
cdrskin dev=0,1,0 blank=fast -eject speed=48 \
|
||||
cdrskin -v dev=0,1,0 blank=fast -eject speed=48 -sao \
|
||||
-audio -swab track0[1-5].cd /path/to/track6.wav
|
||||
|
||||
|
||||
@ -198,7 +203,7 @@ or a device file address as listed by --devices with an accessible drive :
|
||||
|
||||
Set usage of cdrskin with appropriate options rather than cdrecord :
|
||||
|
||||
export SCDBACKUP_CDRECORD="cdrskin -v -v tao_to_sao_tsize=650m"
|
||||
export SCDBACKUP_CDRECORD="cdrskin -v -v"
|
||||
|
||||
Run a backup :
|
||||
|
||||
@ -207,12 +212,11 @@ Run a backup :
|
||||
|
||||
Restrictions
|
||||
|
||||
The convenient burn mode TAO is not available with libburn yet.
|
||||
Therefore it has to be defaulted to mode SAO which needs to know the track
|
||||
size in advance. non-cdrecord option tao_to_sao_tsize=650m causes each CD
|
||||
to get burned up to 650 MB regardless of the payload size.
|
||||
The major restrictions are lifted now: audio, TAO, multi-session do work.
|
||||
Many cdrecord options are still unsupported, though.
|
||||
|
||||
If you have use cases for them, please report your wishes and expectations.
|
||||
|
||||
No multi session yet ... Please report your wishes.
|
||||
|
||||
|
||||
Inspiration and Standard
|
||||
@ -324,7 +328,7 @@ will be the right one to replace "1,0,0" in above example.
|
||||
|
||||
If not --no_rc is the first argument then cdrskin attempts on startup to read
|
||||
arguments from the following three files:
|
||||
/etc/defaults/cdrskin
|
||||
/etc/default/cdrskin
|
||||
/etc/opt/cdrskin/rc
|
||||
$HOME/.cdrskinrc
|
||||
The files are read in the sequence given above.
|
||||
|
206
cdrskin/add_ts_changes_to_libburn_0_2_6
Executable file
206
cdrskin/add_ts_changes_to_libburn_0_2_6
Executable file
@ -0,0 +1,206 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -x
|
||||
|
||||
# This script documents how this cdrskin version was derived from
|
||||
# a vanilla libburn version. It is not intended nor needed for any
|
||||
# use of cdrskin but included here only to show the technical
|
||||
# relationship between both projects - which are close friends
|
||||
# and issue roughly the same software.
|
||||
#
|
||||
# Package maintainers are advised to cover rather libburn than
|
||||
# cdrskin unless they put only emphasis on the cdrecord emulation
|
||||
# provided by cdrskin. libburn contains cdrskin - cdrskin is an
|
||||
# oscillating, friendly and coordinated fork of libburn.
|
||||
#
|
||||
# Script results are a source tarball and two binaries
|
||||
# one dynamic and one static in respect to system libs.
|
||||
# Both binaries are static in respect to libburn.
|
||||
#
|
||||
# The script is to be run in the directory above the toplevel
|
||||
# directory of libburn resp. cdrskin development.
|
||||
#
|
||||
# libburn version used: http://libburn.pykix.org
|
||||
# Downloaded by:
|
||||
# $ svn co http://libburn-svn.pykix.org/trunk libburn_pykix
|
||||
# packed up in a tarball just to save it from inadverted changes by
|
||||
# $ tar czf libburn_svn.tgz libburn_pykix
|
||||
original="./libburn_svn.tgz"
|
||||
# Historic moments:
|
||||
# original="./libburn_svn_A60815.tgz"
|
||||
# original="./libburn_cdrskin_A60819.tgz"
|
||||
|
||||
# The top level directory in that snapshot is named
|
||||
intermediate="./libburn_pykix"
|
||||
|
||||
# My changes are in libburn-0.2.3.ts.develop , mainly in ./cdrskin
|
||||
|
||||
changes="./libburn-0.2.3.ts.develop"
|
||||
skin_release="0.2.6"
|
||||
patch_level=".pl01"
|
||||
skin_rev="$skin_release""$patch_level"
|
||||
|
||||
|
||||
# The result directory and the name of the result tarballs
|
||||
target="./cdrskin-${skin_release}"
|
||||
cdrskin_tarball="./cdrskin-${skin_rev}.tar.gz"
|
||||
cdrskin_tarball_svn="./cdrskin-${skin_rev}.svn.tar.gz"
|
||||
|
||||
# (This once earned me an embarrassingly blooping source tarball)
|
||||
# compile_dir="$changes"
|
||||
|
||||
compile_dir="$target"
|
||||
compile_cmd="./cdrskin/compile_cdrskin.sh"
|
||||
compile_static_opts="-static"
|
||||
compile_result="cdrskin/cdrskin"
|
||||
|
||||
bintarget_dynamic="cdrskin_${skin_rev}-x86-suse9_0"
|
||||
bintarget_static="$bintarget_dynamic"-static
|
||||
|
||||
if test -d "$changes"
|
||||
then
|
||||
dummy=dummy
|
||||
else
|
||||
echo "$0 : FATAL : no directory $changes" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for i in "$target" "$intermediate"
|
||||
do
|
||||
if test -e "$i"
|
||||
then
|
||||
echo "$0 : FATAL : already existing $i" >&2
|
||||
exit 2
|
||||
fi
|
||||
done
|
||||
|
||||
if test -f "$original"
|
||||
then
|
||||
dummy=dummy
|
||||
else
|
||||
echo "$0 : FATAL : no file $original" >&2
|
||||
exit 3
|
||||
fi
|
||||
|
||||
|
||||
# Unpack SVN snapshot.
|
||||
tar xzf "$original"
|
||||
|
||||
|
||||
# Rename the directory to the cdrskin name
|
||||
mv "$intermediate" "$target"
|
||||
|
||||
|
||||
# Copy the changes from the development tree
|
||||
#
|
||||
cdrskin_dir="$changes"/cdrskin
|
||||
libburn_dir="$changes"/libburn
|
||||
cdrskin_target="$target"/cdrskin
|
||||
libburn_target="$target"/libburn
|
||||
|
||||
# Create version timestamp
|
||||
timestamp="$(date -u '+%Y.%m.%d.%H%M%S')"
|
||||
echo "$timestamp"
|
||||
echo '#define Cdrskin_timestamP "'"$timestamp"'"' >"$cdrskin_dir"/cdrskin_timestamp.h
|
||||
|
||||
# Add the cdrskin files
|
||||
if test -e "$cdrskin_target"
|
||||
then
|
||||
rm -rf "$cdrskin_target"
|
||||
fi
|
||||
cp -a "$cdrskin_dir" "$cdrskin_target"
|
||||
|
||||
# Remove copied binaries
|
||||
rm "$cdrskin_target"/*.o
|
||||
rm "$cdrskin_target"/cdrfifo
|
||||
rm "$cdrskin_target"/cdrskin
|
||||
rm "$cdrskin_target"/cleanup
|
||||
for i in std new make old
|
||||
do
|
||||
if test -e "$cdrskin_target"/cdrskin_"$i"
|
||||
then
|
||||
rm "$cdrskin_target"/cdrskin_"$i"
|
||||
fi
|
||||
done
|
||||
for i in .deps .dirstamp .libs
|
||||
do
|
||||
if test -e "$cdrskin_target"/"$i"
|
||||
then
|
||||
rm -rf "$cdrskin_target"/"$i"
|
||||
fi
|
||||
done
|
||||
|
||||
# Remove unwanted SVN stuff (TODO: avoid downloading it)
|
||||
for i in "$target"/.svn "$target"/*/.svn
|
||||
do
|
||||
if test "$i" = "$target"'/*/.svn'
|
||||
then
|
||||
dummy=dummy
|
||||
else
|
||||
if test -e "$i"
|
||||
then
|
||||
rm -rf "$i"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
## No more : Add own libburn-README in toplevel
|
||||
# cp -a "$changes"/README "$target"
|
||||
|
||||
## No more : Add modified Makefile.am
|
||||
# cp -a "$changes"/Makefile.am "$target"
|
||||
|
||||
|
||||
# Make SVN state tarball for the libburn team
|
||||
tar czf "$cdrskin_tarball_svn" "$target"
|
||||
|
||||
|
||||
# Get over dependecy on autotools. Rely only on cc, make et. al.
|
||||
# This is not the same as "make dist" but i can do it without
|
||||
# having to evaluate the quality of said "make dist"
|
||||
#
|
||||
( cd "$target" ; ./bootstrap )
|
||||
|
||||
# Remove unwanted stuff after bootstrap
|
||||
for i in "$target"/autom4te.cache
|
||||
do
|
||||
if echo "$i" | grep '\*' >/dev/null
|
||||
then
|
||||
dummy=dummy
|
||||
else
|
||||
if test -e "$i"
|
||||
then
|
||||
rm -rf "$i"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
|
||||
# Pack it up to the new libburn+cdrskin-tarball
|
||||
tar czf "$cdrskin_tarball" "$target"
|
||||
|
||||
# Produce a static and a dynamic binary
|
||||
(
|
||||
cd "$compile_dir" || exit 1
|
||||
./configure
|
||||
make
|
||||
$compile_cmd -do_strip
|
||||
cp "$compile_result" "../$bintarget_dynamic"
|
||||
if test -n "$compile_static_opts"
|
||||
then
|
||||
$compile_cmd $compile_static_opts -do_strip
|
||||
cp "$compile_result" "../$bintarget_static"
|
||||
fi
|
||||
)
|
||||
|
||||
# Remove the build area
|
||||
# Disable this for debugging the merge process
|
||||
rm -rf "$target"
|
||||
|
||||
# Show the result
|
||||
./"$bintarget_dynamic" -version
|
||||
./"$bintarget_static" -version
|
||||
ls -l "$cdrskin_tarball"
|
||||
ls -l "$bintarget_dynamic"
|
||||
ls -l "$bintarget_static"
|
||||
|
@ -22,7 +22,7 @@ set -x
|
||||
#
|
||||
# libburn version used: http://libburn.pykix.org
|
||||
# Downloaded by:
|
||||
# $ svn co http://libburn-svn.pykix.org/trunk libburn_pykix
|
||||
# $ svn co http://libburn-svn.pykix.org/libburn/trunk libburn_pykix
|
||||
# packed up in a tarball just to save it from inadverted changes by
|
||||
# $ tar czf libburn_svn.tgz libburn_pykix
|
||||
original="./libburn_svn.tgz"
|
||||
@ -36,7 +36,7 @@ intermediate="./libburn_pykix"
|
||||
# My changes are in libburn-0.2.3.ts.develop , mainly in ./cdrskin
|
||||
|
||||
changes="./libburn-0.2.3.ts.develop"
|
||||
skin_rev="0.2.3"
|
||||
skin_rev="0.2.7"
|
||||
|
||||
# The result directory and the name of the result tarballs
|
||||
target="./cdrskin-${skin_rev}"
|
||||
@ -108,12 +108,24 @@ fi
|
||||
cp -a "$cdrskin_dir" "$cdrskin_target"
|
||||
|
||||
# Remove copied binaries
|
||||
rm "$cdrskin_target"/*.o
|
||||
rm "$cdrskin_target"/cdrfifo
|
||||
rm "$cdrskin_target"/cdrskin
|
||||
rm "$cdrskin_target"/cleanup
|
||||
rm "$cdrskin_target"/cdrskin_std
|
||||
rm "$cdrskin_target"/cdrskin_new
|
||||
rm "$cdrskin_target"/*.o
|
||||
for i in std new make old
|
||||
do
|
||||
if test -e "$cdrskin_target"/cdrskin_"$i"
|
||||
then
|
||||
rm "$cdrskin_target"/cdrskin_"$i"
|
||||
fi
|
||||
done
|
||||
for i in .deps .dirstamp .libs
|
||||
do
|
||||
if test -e "$cdrskin_target"/"$i"
|
||||
then
|
||||
rm -rf "$cdrskin_target"/"$i"
|
||||
fi
|
||||
done
|
||||
|
||||
# Remove unwanted SVN stuff (TODO: avoid downloading it)
|
||||
for i in "$target"/.svn "$target"/*/.svn
|
@ -48,6 +48,9 @@ struct CdrfifO {
|
||||
int source_fd;
|
||||
double in_counter;
|
||||
|
||||
double fd_in_counter;
|
||||
double fd_in_limit;
|
||||
|
||||
char *buffer;
|
||||
int buffer_size;
|
||||
int buffer_is_full;
|
||||
@ -72,17 +75,27 @@ struct CdrfifO {
|
||||
double empty_counter;
|
||||
double full_counter;
|
||||
|
||||
|
||||
/* (sequential) fd chaining */
|
||||
/* fds: 0=source, 1=dest */
|
||||
int follow_up_fds[Cdrfifo_ffd_maX][2];
|
||||
|
||||
/* index of first byte in buffer which does not belong to predecessor fd */
|
||||
int follow_up_eop[Cdrfifo_ffd_maX];
|
||||
|
||||
/* index of first byte in buffer which belongs to [this] fd pair */
|
||||
int follow_up_sod[Cdrfifo_ffd_maX];
|
||||
|
||||
/* values for fd_in_limit */
|
||||
double follow_up_in_limits[Cdrfifo_ffd_maX];
|
||||
|
||||
/* number of defined follow-ups */
|
||||
int follow_up_fd_counter;
|
||||
|
||||
/* index of currently active (i.e. reading) follow-up */
|
||||
int follow_up_fd_idx;
|
||||
|
||||
|
||||
/* (simultaneous) peer chaining */
|
||||
struct CdrfifO *next;
|
||||
struct CdrfifO *prev;
|
||||
@ -117,6 +130,8 @@ int Cdrfifo_new(struct CdrfifO **ff, int source_fd, int dest_fd,
|
||||
buffer_size+= chunk_size-(buffer_size%chunk_size);
|
||||
o->source_fd= source_fd;
|
||||
o->in_counter= 0.0;
|
||||
o->fd_in_counter= 0;
|
||||
o->fd_in_limit= -1.0;
|
||||
o->buffer= NULL;
|
||||
o->buffer_is_full= 0;
|
||||
o->buffer_size= buffer_size;
|
||||
@ -140,6 +155,7 @@ int Cdrfifo_new(struct CdrfifO **ff, int source_fd, int dest_fd,
|
||||
for(i= 0; i<Cdrfifo_ffd_maX; i++) {
|
||||
o->follow_up_fds[i][0]= o->follow_up_fds[i][1]= -1;
|
||||
o->follow_up_eop[i]= o->follow_up_sod[i]= -1;
|
||||
o->follow_up_in_limits[i]= -1.0;
|
||||
}
|
||||
o->follow_up_fd_counter= 0;
|
||||
o->follow_up_fd_idx= -1;
|
||||
@ -224,6 +240,26 @@ int Cdrfifo_set_speed_limit(struct CdrfifO *o, double bytes_per_second,
|
||||
}
|
||||
|
||||
|
||||
/** Set a fixed size for input in order to cut off any unwanted tail
|
||||
@param o The fifo object
|
||||
@param idx index for fds attached via Cdrfifo_attach_follow_up_fds(),
|
||||
first attached is 0, <0 directs limit to active fd limit
|
||||
(i.e. first track is -1, second track is 0, third is 1, ...)
|
||||
*/
|
||||
int Cdrfifo_set_fd_in_limit(struct CdrfifO *o, double fd_in_limit, int idx,
|
||||
int flag)
|
||||
{
|
||||
if(idx<0) {
|
||||
o->fd_in_limit= fd_in_limit;
|
||||
return(1);
|
||||
}
|
||||
if(idx >= o->follow_up_fd_counter)
|
||||
return(0);
|
||||
o->follow_up_in_limits[idx]= fd_in_limit;
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
int Cdrfifo_set_fds(struct CdrfifO *o, int source_fd, int dest_fd, int flag)
|
||||
{
|
||||
o->source_fd= source_fd;
|
||||
@ -244,6 +280,7 @@ int Cdrfifo_get_fds(struct CdrfifO *o, int *source_fd, int *dest_fd, int flag)
|
||||
fifo buffer when its predecessors are exhausted. Reading will start as
|
||||
soon as reading of the predecessor encounters EOF. Writing will start
|
||||
as soon as all pending predecessor data are written.
|
||||
@return index number of new item + 1, <=0 indicates error
|
||||
*/
|
||||
int Cdrfifo_attach_follow_up_fds(struct CdrfifO *o, int source_fd, int dest_fd,
|
||||
int flag)
|
||||
@ -253,7 +290,7 @@ int Cdrfifo_attach_follow_up_fds(struct CdrfifO *o, int source_fd, int dest_fd,
|
||||
o->follow_up_fds[o->follow_up_fd_counter][0]= source_fd;
|
||||
o->follow_up_fds[o->follow_up_fd_counter][1]= dest_fd;
|
||||
o->follow_up_fd_counter++;
|
||||
return(1);
|
||||
return(o->follow_up_fd_counter);
|
||||
}
|
||||
|
||||
|
||||
@ -567,7 +604,12 @@ after_write:;
|
||||
can_read= o->chunk_size;
|
||||
if(o->write_idx<o->read_idx && o->write_idx+can_read > o->read_idx)
|
||||
can_read= o->read_idx - o->write_idx;
|
||||
ret= read(o->source_fd,o->buffer+o->write_idx,can_read);
|
||||
if(o->fd_in_limit>=0.0)
|
||||
if(can_read > o->fd_in_limit - o->fd_in_counter)
|
||||
can_read= o->fd_in_limit - o->fd_in_counter;
|
||||
ret= 0;
|
||||
if(can_read>0)
|
||||
ret= read(o->source_fd,o->buffer+o->write_idx,can_read);
|
||||
if(ret==-1) {
|
||||
|
||||
/* >>> handle input error */;
|
||||
@ -599,6 +641,8 @@ after_write:;
|
||||
sod= 0;
|
||||
o->follow_up_sod[idx]= sod;
|
||||
o->write_idx= sod;
|
||||
o->fd_in_counter= 0;
|
||||
o->fd_in_limit= o->follow_up_in_limits[idx];
|
||||
if(Cdrfifo_debuG || (flag&1))
|
||||
fprintf(stderr,"\ncdrfio: new fifo source fd : %d\n",o->source_fd);
|
||||
} else {
|
||||
@ -608,6 +652,7 @@ after_write:;
|
||||
did_work= 1;
|
||||
o->put_counter++;
|
||||
o->in_counter+= ret;
|
||||
o->fd_in_counter+= ret;
|
||||
o->write_idx+= ret;
|
||||
if(o->write_idx>=o->buffer_size)
|
||||
o->write_idx= 0;
|
||||
@ -751,7 +796,7 @@ ex:;
|
||||
|
||||
|
||||
/** Fill the fifo as far as possible without writing to destination fd */
|
||||
int Cdrfifo_fill(struct CdrfifO *o, int flag)
|
||||
int Cdrfifo_fill(struct CdrfifO *o, int size, int flag)
|
||||
{
|
||||
int ret,fill= 0,space,state;
|
||||
|
||||
@ -765,6 +810,8 @@ int Cdrfifo_fill(struct CdrfifO *o, int flag)
|
||||
} else if(state!=1)
|
||||
break;
|
||||
if(space<=0)
|
||||
break;
|
||||
if(size>=0 && fill>=size)
|
||||
break;
|
||||
ret= Cdrfifo_try_to_work(o,100000,NULL,NULL,2);
|
||||
if(ret<0) {
|
||||
|
@ -15,7 +15,7 @@
|
||||
|
||||
|
||||
/** The fifo buffer which will smoothen the data stream from data provider
|
||||
to data consumer. Although this is not a mandatory lifesavier for modern
|
||||
to data consumer. Although this is not a mandatory lifesaver for modern
|
||||
burners any more, a fifo can speed up burning of data which is delivered
|
||||
with varying bandwidths (e.g. compressed archives created on the fly
|
||||
or mkisofs running at its speed limit.).
|
||||
@ -64,6 +64,16 @@ int Cdrfifo_get_sizes(struct CdrfifO *o, int *chunk_size, int *buffer_size,
|
||||
int Cdrfifo_set_speed_limit(struct CdrfifO *o, double bytes_per_second,
|
||||
int flag);
|
||||
|
||||
/** Set a fixed size for input in order to cut off any unwanted tail
|
||||
@param o The fifo object
|
||||
@param idx index for fds attached via Cdrfifo_attach_follow_up_fds(),
|
||||
first attached is 0, <0 directs limit to active fd limit
|
||||
(i.e. first track is -1, second track is 0, third is 1, ...)
|
||||
*/
|
||||
int Cdrfifo_set_fd_in_limit(struct CdrfifO *o, double fd_in_limit, int idx,
|
||||
int flag);
|
||||
|
||||
|
||||
int Cdrfifo_set_fds(struct CdrfifO *o, int source_fd, int dest_fd, int flag);
|
||||
int Cdrfifo_get_fds(struct CdrfifO *o, int *source_fd, int *dest_fd, int flag);
|
||||
|
||||
@ -72,6 +82,7 @@ int Cdrfifo_get_fds(struct CdrfifO *o, int *source_fd, int *dest_fd, int flag);
|
||||
fifo buffer when its predecessors are exhausted. Reading will start as
|
||||
soon as reading of the predecessor encounters EOF. Writing will start
|
||||
as soon as all pending predecessor data are written.
|
||||
@return index number of new item + 1, <=0 indicates error
|
||||
*/
|
||||
int Cdrfifo_attach_follow_up_fds(struct CdrfifO *o, int source_fd, int dest_fd,
|
||||
int flag);
|
||||
@ -134,10 +145,11 @@ int Cdrfifo_get_cdr_counters(struct CdrfifO *o,
|
||||
int Cdrfifo_try_to_work(struct CdrfifO *o, int wait_usec,
|
||||
char *reply_buffer, int *reply_count, int flag);
|
||||
|
||||
/** Fill the fifo as far as possible without writing to destination fd
|
||||
/** Fill the fifo as far as possible without writing to destination fd.
|
||||
@param size if >=0 : end filling after the given number of bytes
|
||||
@return 1 on success, <=0 on failure
|
||||
*/
|
||||
int Cdrfifo_fill(struct CdrfifO *o, int flag);
|
||||
int Cdrfifo_fill(struct CdrfifO *o, int size, int flag);
|
||||
|
||||
|
||||
#endif /* Cdrfifo_headerfile_includeD */
|
||||
|
600
cdrskin/cdrskin.1
Normal file
600
cdrskin/cdrskin.1
Normal file
@ -0,0 +1,600 @@
|
||||
.\" Hey, EMACS: -*- nroff -*-
|
||||
.\" First parameter, NAME, should be all caps
|
||||
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
|
||||
.\" other parameters are allowed: see man(7), man(1)
|
||||
.TH CDRSKIN 1 "December 13, 2006"
|
||||
.\" Please adjust this date whenever revising the manpage.
|
||||
.\"
|
||||
.\" Some roff macros, for reference:
|
||||
.\" .nh disable hyphenation
|
||||
.\" .hy enable hyphenation
|
||||
.\" .ad l left justify
|
||||
.\" .ad b justify to both left and right margins
|
||||
.\" .nf disable filling
|
||||
.\" .fi enable filling
|
||||
.\" .br insert line break
|
||||
.\" .sp <n> insert n+1 empty lines
|
||||
.\" for manpage-specific macros, see man(7)
|
||||
.SH NAME
|
||||
cdrskin \- burns preformatted data to CD-R or CD-RW via libburn.
|
||||
.SH SYNOPSIS
|
||||
.B cdrskin
|
||||
.RI [ options | track_source_addresses ]
|
||||
.br
|
||||
.SH DESCRIPTION
|
||||
.PP
|
||||
.\" TeX users may be more comfortable with the \fB<whatever>\fP and
|
||||
.\" \fI<whatever>\fP escape sequences to invode bold face and italics,
|
||||
.\" respectively.
|
||||
.PP
|
||||
\fBcdrskin\fP is a program that provides some of cdrecord's options in a compatible way.
|
||||
You don't need to be root to use it.
|
||||
.PP
|
||||
.B Overview of features:
|
||||
.br
|
||||
Blanking of CD-RW.
|
||||
.br
|
||||
Burning of data or audio tracks to CD.
|
||||
.br
|
||||
Either in versatile Track at Once mode (TAO)
|
||||
.br
|
||||
or in Session at Once mode for seamless tracks.
|
||||
.br
|
||||
Multi session (follow-up sessions in TAO only).
|
||||
.br
|
||||
Bus scan, burnfree, speed options, retrieving media info, padding, fifo.
|
||||
.br
|
||||
See section EXAMPLES at the end of this text.
|
||||
.PP
|
||||
.B Known deficiencies:
|
||||
.br
|
||||
No DVD support yet.
|
||||
.PP
|
||||
.B Track recording model:
|
||||
.br
|
||||
The input-output entities which get processed are called tracks.
|
||||
A track stores a stream of bytes.
|
||||
.br
|
||||
Each track is initiated by one track source address argument, which may either
|
||||
be "-" for standard input or the address of a readable file. If no write mode
|
||||
is given explicitely then one will be chosen which matches the peculiarities
|
||||
of track source and the state of the output media.
|
||||
.PP
|
||||
There can be more than one track burned by a single run of cdrskin.
|
||||
CDs can be kept appendable so that further tracks can
|
||||
be written to them in subsequent runs of cdrskin (see option -multi).
|
||||
Info about the addresses of burned tracks is kept in a table of
|
||||
content (TOC) on media and can be retrieved via cdrskin option -toc.
|
||||
These informations are also used by the operating systems' CD-ROM read drivers.
|
||||
.PP
|
||||
In general there are two types of tracks: data and audio. They differ in
|
||||
sector size, throughput and readability via the systems' CD-ROM drivers
|
||||
resp. by music CD players.
|
||||
.br
|
||||
If not explicitely option -audio is given, then any track is burned as data
|
||||
unless the track source is a file with suffix ".wav" or ".au" and has a
|
||||
header part which identifies it as MS-WAVE resp. SUN Audio with suitable
|
||||
parameters. Such files are burned as audio tracks by default.
|
||||
.PP
|
||||
While audio tracks just contain a given time span of acoustic vibrations,
|
||||
data tracks may have an arbitray meaning. Nevertheless, ISO-9660 filesystems
|
||||
are established as a format which can represent a tree of directories and
|
||||
data files on all major operating systems. Such filesystem images can be
|
||||
produced by programs mkisofs or genisoimage. They can also be extended by
|
||||
follow-up tracks if prepared properly. See the man pages of said programs.
|
||||
cdrskin is able to fulfill the needs about their option -C.
|
||||
.br
|
||||
Another type of data track content are archive formats which originally
|
||||
have been developed for magnetic tapes. Only formats which mark a detectable
|
||||
end-of-archive in their data are suitable with CD, though. Well tested are
|
||||
the archivers afio and star. Not suitable seems GNU tar.
|
||||
.PP
|
||||
.B Recordable CD Media:
|
||||
.br
|
||||
CD-R can be initially written only once and eventually extended until they
|
||||
get closed (or are spoiled because they are overly full). After that they are
|
||||
read-only.
|
||||
.br
|
||||
CD-RW media can be blanked to make them re-usable for another
|
||||
round of overwriting. Blanking damages the previous content but does not
|
||||
make it completely unreadable. It is no effective privacy precaution.
|
||||
Multiple cycles of blanking and overwriting with random numbers might be.
|
||||
.br
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.BI \-\-help
|
||||
Show non-cdrecord compatible options.
|
||||
.TP
|
||||
.BI \-help
|
||||
Show cdrecord compatible options.
|
||||
.TP
|
||||
.BI \-version
|
||||
Print cdrskin id line, compatibility lure line, libburn version, cdrskin
|
||||
version, version timestamp, build timestamp (if available), and then exit.
|
||||
.PP
|
||||
Alphabetical list of options which are intended to be compatible with
|
||||
original cdrecord by Joerg Schilling:
|
||||
.TP
|
||||
.BI \-atip
|
||||
Retrieve some info about media state. With CD-RW print "Is erasable".
|
||||
.TP
|
||||
.BI \-audio
|
||||
Announces that the subsequent tracks are to be burned as audio.
|
||||
The source is supposed to be uncompressed headerless PCM, 44100 Hz, 16 bit,
|
||||
stereo. For little-endian byte order (which is usual on PCs) use option
|
||||
-swab. Input files with suffix .wav are examined wether they have a header
|
||||
in MS-WAVE format confirming those parameters and eventually raw audio data
|
||||
get extracted automatically. Same is done for suffix .au and SUN Audio.
|
||||
.TP
|
||||
.BI blank= type
|
||||
Blank a CD-RW disc. This is combinable with burning in the same run of cdrskin.
|
||||
The type given with blank= selects the particular behavior:
|
||||
.RS
|
||||
.TP
|
||||
help
|
||||
Print this list of blanking types.
|
||||
.TP
|
||||
all
|
||||
Blank the entire disk.
|
||||
.TP
|
||||
fast
|
||||
Minimally blank the entire disk.
|
||||
.RE
|
||||
.TP
|
||||
.BI \-checkdrive
|
||||
Retrieve some info about the addressed drive.
|
||||
Exits with non-zero value if the drive cannot be found and opened.
|
||||
.TP
|
||||
.BI \-dao
|
||||
Alias for option -sao. Write disk in Session at Once mode.
|
||||
.TP
|
||||
.BI \-data
|
||||
Subsequent tracks are data tracks. This option is default and only needed
|
||||
to mark the end of the range of an eventual option -audio.
|
||||
.TP
|
||||
.BI dev= target
|
||||
Sets the (pseudo-)SCSI address of the drive to use. Valid are at least the
|
||||
X,Y,Z addresses listed with option -scanbus, ATA:X,Y,Z addresses listed with
|
||||
options dev=ATA -scanbus, the device file addresses listed with
|
||||
option --devices , volatile libburn drive numbers (numbering starts at "0").
|
||||
Other device file addresses which lead to the same drive might work too.
|
||||
.br
|
||||
If no dev= is given, volatile address "dev=0" is assumed. That is the first
|
||||
drive found being available. Better avoid this on multi-drive systems.
|
||||
.br
|
||||
The special target "help" lists hints about available addressing formats.
|
||||
Be aware that option --old_pseudo_scsi_adr changes the meaning of
|
||||
Bus,Target,Lun addresses.
|
||||
.TP
|
||||
.BI driveropts= opt
|
||||
Set "driveropts=burnfree" to enable the drive's eventual protection mechanism
|
||||
against temporary lack of source data (i.e. buffer underrun).
|
||||
It is not an error to do this with a drive that has no such capabilities.
|
||||
.TP
|
||||
.BI \-dummy
|
||||
Try to perform the drive operations without actually affecting the inserted
|
||||
media. There is no guarantee that this will work with a particular drive
|
||||
in a particular write mode. Blanking is prevented reliably, though.
|
||||
.TP
|
||||
.BI \-eject
|
||||
Eject the disk after work is done.
|
||||
.TP
|
||||
.BI \-force
|
||||
Assume that the user knows better in situations when cdrskin or libburn are
|
||||
insecure about drive or media state. This includes the attempt to blank
|
||||
media which are classified as unknown or unsuitable, and the attempt to use
|
||||
write modes which libburn believes they are not supported by the drive.
|
||||
.br
|
||||
Use this only when in urgent need.
|
||||
.TP
|
||||
.BI fs= size
|
||||
Set the fifo size to the given value. The value may have appended letters which
|
||||
multiply the preceding number:
|
||||
.br
|
||||
"k" or "K" = 1024 , "m" or "M" = 1024k , "g" or "G" = 1024m , "s" or "S" = 2048
|
||||
.br
|
||||
Set size to 0 in order to disable the fifo (default is "4m").
|
||||
.br
|
||||
The fifo buffers an eventual temporary surplus of track source data in order to
|
||||
provide the drive with a steady stream during times of temporary lack of track
|
||||
source supply.
|
||||
The larger the fifo, the longer periods of poor source supply can be
|
||||
compensated.
|
||||
But a large fifo needs substantial time to fill up if not curbed via
|
||||
option fifo_start_at=size.
|
||||
.TP
|
||||
.BI gracetime= seconds
|
||||
Set the grace time before starting to write. (Default is 0)
|
||||
.TP
|
||||
.BI \-msinfo
|
||||
Retrieve multi-session info for preparing a follow-up session by option -C
|
||||
of programs mkisofs or genisoimage. Print result to standard output.
|
||||
This option redirects to stderr all
|
||||
message output besides its own result string, which consists of two numbers.
|
||||
The result string shall be used as argument of option -C with said programs.
|
||||
It gives the start address of the most recent session and the predicted
|
||||
start address of the next session to be appended. The string is empty if
|
||||
the most recent session was not written with option -multi.
|
||||
.TP
|
||||
.BI \-multi
|
||||
This option keeps the CD appendable after the current session has been written.
|
||||
Without it the disk gets closed and may not be written any more - unless it
|
||||
is a CD-RW and gets blanked which causes loss of its content.
|
||||
.br
|
||||
The following sessions can only be written in -tao mode.
|
||||
.br
|
||||
In order to have all filesystem content accessible, the eventual ISO-9660
|
||||
filesystem of a follow-up
|
||||
session needs to be prepared in a special way by the filesystem formatter
|
||||
program. mkisofs and genisoimage expect particular info about the situation
|
||||
which can be retrieved by cdrskin option -msinfo.
|
||||
.br
|
||||
To retrieve an archive file which was written as follow-up session,
|
||||
you may use option -toc to learn about the "lba" of the desired track number.
|
||||
.TP
|
||||
.BI \-nopad
|
||||
Do not add trailing zeros to the data stream. Nevertheless, since there seems
|
||||
to be no use for audio tracks with incomplete last sector, this option applies
|
||||
only to data tracks. There it is default.
|
||||
.TP
|
||||
.BI \-pad
|
||||
Add 30 kB of trailing zeros to each data track. (This is not sufficient to
|
||||
avoid problems with various CD-ROM read drivers.)
|
||||
.TP
|
||||
.BI padsize= size
|
||||
Add the given amount of trailing zeros to the next data track. This option
|
||||
gets reset to padsize=0 after that next track is written. It may be set
|
||||
again before the next track argument. About size specifiers, see option fs=.
|
||||
.TP
|
||||
.BI \-raw96r
|
||||
Write disk in RAW/RAW96R mode. This mode allows to put more payload bytes
|
||||
into a CD sector but obviously at the cost of error correction. It can only
|
||||
be used for tracks of fixely predicted size. Some drives allow this mode but
|
||||
then behave strange or even go bad for the next few attempts to burn a CD.
|
||||
One should use it only if inavoidable.
|
||||
.TP
|
||||
.BI \-sao
|
||||
Write disk in Session At Once mode. This mode is able to put several audio
|
||||
tracks on CD without producing audible gaps between them. It can only
|
||||
be used for tracks of fixely predicted size. This implies that track arguments
|
||||
which depict stdin or named pipes need to be preceeded by option tsize= or
|
||||
by option tao_to_sao_tsize=.
|
||||
.TP
|
||||
.BI \-scanbus
|
||||
Scan the system for drives. On Linux the drives at /dev/s* and at /dev/hd*
|
||||
are to be scanned by two separate runs. One without dev= for /dev/s* and
|
||||
one with dev=ATA for /dev/hd* devices. (Option --drives lists all available
|
||||
drives in a single run.)
|
||||
.br
|
||||
Drives which are busy or which offer no rw-permission to the user of cdrskin
|
||||
are not listed. Busy drives get reported in form of warning messages.
|
||||
.br
|
||||
The useful fields in a result line are:
|
||||
.br
|
||||
Bus,Target,Lun Number) 'Vendor' 'Mode' 'Revision'
|
||||
.TP
|
||||
.BI speed= number
|
||||
Set speed of drive. With data CD, 1x speed corresponds to a throughput of
|
||||
150 kB/s. It is not an error to set a speed higher than is suitable for drive
|
||||
and media. One should stay within a realistic speed range, though.
|
||||
.TP
|
||||
.BI \-swab
|
||||
Announce that the raw audio data source of subsequent tracks is byte swapped
|
||||
versus the expectations of cdrecord. This option is suitable for audio where
|
||||
the least significant byte of a 16 bit word is first (little-endian, Intel).
|
||||
Most raw audio data on PC systems are available in this byte order.
|
||||
Less guesswork is needed if track sources are in format MS-WAVE in a file with
|
||||
suffix ".wav".
|
||||
.TP
|
||||
.BI \-tao
|
||||
Write disk in Track At Once (TAO) mode. This mode can be used with track
|
||||
sources of unpredictable size, like standard input or named pipes. It is
|
||||
also the only mode that can be used for writing to appendable CD which
|
||||
already hold data.
|
||||
.TP
|
||||
.BI \-toc
|
||||
Print the table of content (TOC) which describes the tracks recorded on CD.
|
||||
The output contains all info from option -atip plus lines which begin with
|
||||
"track: " followed by the track number, the word "lba:" and a number which
|
||||
gives the start address of the track. Addresses are counted in CD sectors which
|
||||
with data tracks hold 2048 bytes each.
|
||||
.RS
|
||||
.TP
|
||||
Example. Retrieve an afio archive from track number 2:
|
||||
.br
|
||||
tracknumber=2
|
||||
.br
|
||||
lba=$(cdrskin dev=/dev/cdrom -toc 2>&1 | \\
|
||||
.br
|
||||
grep '^track: [ 0-9][0-9]' | \\
|
||||
.br
|
||||
tail +"$tracknumber" | head -1 | \\
|
||||
.br
|
||||
awk '{ print $4}' )
|
||||
.br
|
||||
dd if=/dev/cdrom bs=2048 skip="$lba" | \\
|
||||
.br
|
||||
afio -t - | less
|
||||
.RE
|
||||
.TP
|
||||
.BI tsize= size
|
||||
Announces the exact size of the next track source. This is necessary with any
|
||||
write mode other than -tao if the track source is not a regular disk file, but
|
||||
e.g. "-" (standard input) or a named pipe.
|
||||
About size specifiers, see option fs=.
|
||||
.br
|
||||
If the track source does not deliver the predicted amount of bytes, the
|
||||
remainder of the track is padded with zeros. This is not considered an error.
|
||||
If on the other hand the track source delivers more than the announced bytes
|
||||
then the track on CD gets truncated to the predicted size and cdrskin exits
|
||||
with non-zero value.
|
||||
.TP
|
||||
.BI \-v
|
||||
Increment verbose level by one. Startlevel is 0 with only few messages.
|
||||
Level 1 prints progress report with long running operations and also causes
|
||||
some extra lines to be put out with info retrieval options.
|
||||
Level 2 additionally reports about option settings derived from arguments or
|
||||
startup files. Level 3 is for debugging and useful mainly in conjunction with
|
||||
somebody who had a look into the program sourcecode.
|
||||
.PP
|
||||
Alphabetical list of options which are genuine to cdrskin and intended for
|
||||
normal use:
|
||||
.TP
|
||||
.BI \--allow_setuid
|
||||
Disable the program abort triggered by an insecure discrepance between
|
||||
login user and
|
||||
effective user which indicates application of chmod u+s to the program binary.
|
||||
One should not do this chmod u+s , but it is an old cdrecord tradition.
|
||||
.TP
|
||||
.BI \--any_track
|
||||
Allow source_addresses to begin with "-" (plus further characters) or to
|
||||
contain a "=" character.
|
||||
By default such arguments are seen as misspelled options. It is nevertheless
|
||||
not possible to use one of the options of cdrecord-2.01.
|
||||
.TP
|
||||
.BI \--demand_a_drive
|
||||
Exit with a nonzero value if no drive can be found during a bus scan.
|
||||
.TP
|
||||
.BI \--devices
|
||||
List the device file addresses of all accessible drives. In order to get
|
||||
listed a drive has to offer rw-permission for the cdrskin user and it may
|
||||
not be busy.
|
||||
Busy drives are reported as "SORRY" messages on standard error.
|
||||
.br
|
||||
Each available drive gets listed by a line containing the following fields:
|
||||
.br
|
||||
Number dev='Devicefile' rw-Permissions : 'Vendor' 'Model'
|
||||
.br
|
||||
Number and Devicefile can both be used with option dev=, but number is
|
||||
volatile (numbering changes if drives become busy).
|
||||
Normal users might not see all drives unless the superuser enabled access
|
||||
by chmod o+rw
|
||||
after using cdrskin --devices to get an overview of the situation.
|
||||
That's why current rw-Permissions are listed.
|
||||
.TP
|
||||
.BI fifo_start_at= size
|
||||
Do not wait for full fifo but start burning as soon as the given number
|
||||
of bytes is read. This option may be helpful to bring the average throughput
|
||||
near to the maximum throughput of a drive. A large fs= and a small
|
||||
fifo_start_at= combine a quick burn start and a large savings buffer to
|
||||
compensate for temporary lack of source data. At the beginning of burning,
|
||||
the software protection against buffer underun is as weak as the size of
|
||||
fifo_start_at= . So it is best if the drive offers hardware protection which
|
||||
has to be enabled by driveropts=burnfree.
|
||||
.TP
|
||||
.BI \--no_rc
|
||||
Only if used as first command line argument this option prevents reading and
|
||||
interpretation of eventual startup files. See section FILES below.
|
||||
.TP
|
||||
.BI \--single_track
|
||||
Accept only the last argument of the command line as track source address.
|
||||
.PP
|
||||
Alphabetical list of options which are only intended for very special
|
||||
situations and not for normal use:
|
||||
.TP
|
||||
.BI \--abort_handler
|
||||
Establish default signal handling not to leave a drive in busy state
|
||||
but rather to shut it down and to wait until it has ended the final operations.
|
||||
This option is only needed for revoking eventual --ignore_signals or
|
||||
--no_abort_handler.
|
||||
.TP
|
||||
.BI dev_translation= <sep><from><sep><to>
|
||||
Set drive address alias. This was necessary before cdrskin-0.2.4 to manually
|
||||
translate cdrecord addresses into cdrskin addresses.
|
||||
.br
|
||||
<sep> is a single character which may not occur in the address string
|
||||
<from>. <from> is an address as expected to be given by the user via option
|
||||
dev=. <to> is the address to be used instead whenever <from> is given.
|
||||
More than one translation instruction can be given in one cdrskin run.
|
||||
.br
|
||||
E.g.: dev_translation=+ATA:1,0,0+/dev/sg1 dev_translation=+ATA:1,1,0+/dev/sg2
|
||||
.TP
|
||||
.BI \--drive_abort_on_busy
|
||||
Linux specific: Abort process if a busy drive is encountered.
|
||||
.TP
|
||||
.BI \--drive_blocking
|
||||
Linux specific: Try to wait for a busy drive to become free.
|
||||
This is not guaranteed to work with all drivers. Some need nonblocking i/o.
|
||||
.TP
|
||||
.BI \--drive_not_exclusive
|
||||
Linux specific: Do not ask the operating system to prevent opening busy drives.
|
||||
Wether this leads to senseful behavior depends on operating system and kernel.
|
||||
.TP
|
||||
.BI \--drive_scsi_exclusive
|
||||
Linux specific:
|
||||
Try to exclusively reserve device files /dev/srN, /dev/scdM, /dev/stK of drive.
|
||||
this would be helpful to protect against collisions with program growisofs.
|
||||
Regrettably on Linux kernel 2.4 with ide-scsi emulation this seems not to
|
||||
work. Wether it becomes helpful with new Linux systems has to be evaluated.
|
||||
.TP
|
||||
.BI \--fifo_disable
|
||||
Disable fifo despite any fs=.
|
||||
.TP
|
||||
.BI \--fifo_per_track
|
||||
Use a separate fifo for each track.
|
||||
.TP
|
||||
.BI grab_drive_and_wait= seconds
|
||||
Open the addressed drive, wait the given number of seconds, release the drive,
|
||||
and do normal work as indicated by the other options used. This option helps
|
||||
to explore the program behavior when faced with busy drives. Just start a
|
||||
second cdrskin with option --devices while grab_drive_and_wait= is still
|
||||
active.
|
||||
.TP
|
||||
.BI \--ignore_signals
|
||||
Try to ignore any signals rather than to abort the program. This is not a
|
||||
very good idea. You might end up waiting a very long time for cdrskin
|
||||
to finish.
|
||||
.TP
|
||||
.BI \--no_abort_handler
|
||||
On signals exit even if the drive is in busy state. This is not a very good
|
||||
idea. You might end up with a stuck drive that refuses to hand out the media.
|
||||
.TP
|
||||
.BI \--no_blank_appendable
|
||||
Refuse to blank appendable CD-RW. This is a feature that was once builtin with
|
||||
libburn. No information available for what use case it was needed.
|
||||
.TP
|
||||
.BI \--no_convert_fs_adr
|
||||
Do only literal translations of dev=. This prevents cdrskin from test-opening
|
||||
device files in order to find one that matches the given dev= specifier.
|
||||
.br
|
||||
Partly Linux specific:
|
||||
Such opening is needed for Bus,Target,Lun addresses unless option
|
||||
--old_pseudo_scsi_adr is given. It is also needed to resolve device file
|
||||
addresses which are not listed with cdrskin --devices but nevertheless point
|
||||
to a usable drive. (Like /dev/sr0 using the same SCSI address as /dev/sg0.)
|
||||
.TP
|
||||
.BI \--old_pseudo_scsi_adr
|
||||
Linux specific:
|
||||
Use and report literal Bus,Target,Lun addresses rather than real SCSI and
|
||||
pseudo ATA addresses. This method is outdated and was never compatible with
|
||||
original cdrecord.
|
||||
.TP
|
||||
.BI tao_to_sao_tsize= size
|
||||
Set an exact fixed size for the next track to be in effect only if the track
|
||||
source cannot deliver a size prediction and no tsize= was specified.
|
||||
This is the fallback from bad old times when cdrskin was unable to burn
|
||||
in mode -tao.
|
||||
.br
|
||||
.SH EXAMPLES
|
||||
.SS
|
||||
.B Get an overview of drives:
|
||||
.br
|
||||
cdrskin -scanbus
|
||||
.br
|
||||
cdrskin dev=ATA -scanbus
|
||||
.br
|
||||
cdrskin --devices
|
||||
.SS
|
||||
.B Get info about a particular drive or loaded media:
|
||||
.br
|
||||
cdrskin dev=0,1,0 -checkdrive
|
||||
.br
|
||||
cdrskin dev=ATA:1,0,0 -atip
|
||||
.br
|
||||
cdrskin dev=/dev/hdc -toc
|
||||
.SS
|
||||
.B Make used CD-RW writable again:
|
||||
.br
|
||||
cdrskin -v dev=/dev/sg1 blank=all -eject
|
||||
.br
|
||||
cdrskin -v dev=/dev/dvd blank=fast -eject
|
||||
.SS
|
||||
.B Write ISO-9660 filesystem image:
|
||||
.br
|
||||
cdrskin -v dev=/dev/hdc speed=12 fs=8m \\
|
||||
.br
|
||||
driveropts=burnfree -sao -eject \\
|
||||
.br
|
||||
padsize=300k my_image.iso
|
||||
.SS
|
||||
.B Write compressed afio archive on-the-fly:
|
||||
.br
|
||||
find . | afio -oZ - | \\
|
||||
.br
|
||||
cdrskin -v dev=0,1,0 fs=32m speed=8 driveropts=burnfree \\
|
||||
.br
|
||||
padsize=300k -tao -
|
||||
.SS
|
||||
.B Write several sessions to the same CD:
|
||||
.br
|
||||
cdrskin dev=/dev/hdc padsize=300k -multi 1.iso
|
||||
.br
|
||||
cdrskin dev=/dev/hdc padsize=300k -multi -tao 2.afio
|
||||
.br
|
||||
cdrskin dev=/dev/hdc padsize=300k -multi -tao 3.afio
|
||||
.br
|
||||
cdrskin dev=/dev/hdc padsize=300k -tao 4.afio
|
||||
.SS
|
||||
.B Get the multi-session info for option -C of program mkisofs:
|
||||
.br
|
||||
c_values=$(cdrskin dev=/dev/sr0 -msinfo 2>/dev/null)
|
||||
.br
|
||||
mkisofs ... -C "$c_values" ...
|
||||
.SS
|
||||
.B Write audio tracks:
|
||||
.br
|
||||
cdrskin -v dev=ATA:1,0,0 speed=48 \\
|
||||
.br
|
||||
driveropts=burnfree -sao \\
|
||||
.br
|
||||
track1.wav track2.au -audio -swab track3.raw
|
||||
.br
|
||||
.SH FILES
|
||||
If not --no_rc is given as the first argument then cdrskin attempts on
|
||||
startup to read the arguments from the following files:
|
||||
.PP
|
||||
.br
|
||||
.B /etc/default/cdrskin
|
||||
.br
|
||||
.B /etc/opt/cdrskin/rc
|
||||
.br
|
||||
.B $HOME/.cdrskinrc
|
||||
.br
|
||||
.PP
|
||||
The files are read in the sequence given above, but none of them is
|
||||
required for cdrskin to function properly. Each readable line is treated
|
||||
as one single argument. No extra blanks.
|
||||
A first character '#' marks a comment, empty lines are ignored.
|
||||
.SS
|
||||
.B Example content of a startup file:
|
||||
.br
|
||||
# This is the default device
|
||||
.br
|
||||
dev=0,1,0
|
||||
.br
|
||||
# To accomodate to remnant cdrskin-0.2.2 addresses
|
||||
.br
|
||||
dev_translation=+1,0,0+0,1,0
|
||||
.br
|
||||
# Some more options
|
||||
.br
|
||||
fifo_start_at=0
|
||||
.br
|
||||
fs=16m
|
||||
.br
|
||||
.SH SEE ALSO
|
||||
.TP
|
||||
Formatting track sources for cdrskin:
|
||||
.br
|
||||
.BR mkisofs (8),
|
||||
.BR genisoimage (8),
|
||||
.BR afio (1),
|
||||
.BR star (1)
|
||||
.br
|
||||
.TP
|
||||
Other CD burn programs:
|
||||
.br
|
||||
.BR cdrecord (1),
|
||||
.BR wodim (1)
|
||||
.br
|
||||
.TP
|
||||
For DVD burning:
|
||||
.br
|
||||
.BR growisofs (1)
|
||||
.br
|
||||
.SH AUTHOR
|
||||
cdrskin was written by Thomas Schmitt <scdbackup@gmx.net>.
|
||||
.PP
|
||||
This manual page was written by George Danchev <danchev@spnet.net> and
|
||||
Thomas Schmitt, for the Debian project and for all others.
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -51,7 +51,7 @@ A CD recorder suitable for
|
||||
GPL software included:<BR>
|
||||
</H2>
|
||||
<DL>
|
||||
<DT>libburn-0.2.2</DT>
|
||||
<DT>libburn-0.2.6</DT>
|
||||
<DD>(by Derek Foreman, Ben Jansens, and team of libburn.pykix.org)</DD>
|
||||
<DD>transfers data to CD</DD>
|
||||
</DL>
|
||||
@ -80,10 +80,31 @@ Ports to other usable systems are appreciated. Reports are welcome.
|
||||
This has been achieved quite sufficiently for the needs of backup tool
|
||||
<A HREF="http://scdbackup.sourceforge.net/main_eng.html">scdbackup</A>
|
||||
and for data CD projects of <A HREF="http://www.k3b.org">K3b</A>
|
||||
(see <A HREF="#examples">examples</A>).<BR>
|
||||
(see <A HREF="#examples">examples</A>).
|
||||
Suitability for audio CD frontends has been improved much and is now being
|
||||
evaluated.<BR>
|
||||
Further enhancements depend on people who can describe and discuss their
|
||||
wishes as well as on the development of libburn.</DT>
|
||||
<BR><BR>
|
||||
<DT>Get an overview of drives:</DT>
|
||||
<DD>$ cdrskin -scanbus</DD>
|
||||
<DD>$ cdrskin dev=ATA -scanbus</DD>
|
||||
<DD>$ cdrskin --devices</DD>
|
||||
<DT>Get info about a particular drive or loaded media:</DT>
|
||||
<DD>$ cdrskin dev=0,1,0 -checkdrive</DD>
|
||||
<DD>$ cdrskin dev=ATA:1,0,0 -atip</DD>
|
||||
<DD>$ cdrskin dev=/dev/hdc -toc</DD>
|
||||
<DT>Make used CD-RW writable again:</DT>
|
||||
<DD>$ cdrskin -v dev=/dev/sg1 blank=all -eject</DD>
|
||||
<DD>$ cdrskin -v dev=/dev/dvd blank=fast -eject</DD>
|
||||
<DT>Write ISO-9660 filesystem image:</DT>
|
||||
<DD>$ cdrskin -v dev=/dev/hdc speed=12 fs=8m driveropts=burnfree -sao -eject padsize=300k my_image.iso</DD>
|
||||
<DT>Write compressed afio archive on-the-fly :</DT>
|
||||
<DD>$ find . | afio -oZ - | cdrskin -v dev=0,1,0 fs=32m speed=8 driveropts=burnfree padsize=300k -tao -</DD>
|
||||
<DT>Write audio tracks:</DT>
|
||||
<DD>$ cdrskin -v dev=ATA:1,0,0 speed=48 driveropts=burnfree -sao track1.wav track2.au -audio -swab track3.raw
|
||||
<DD>
|
||||
<BR>
|
||||
<DT><A HREF="cdrskin_help">cdrskin -help</A></DT>
|
||||
<DD>reports the cdrecord compatible options</DD>
|
||||
<DT><A HREF="cdrskin__help">cdrskin --help</A></DT>
|
||||
@ -94,15 +115,6 @@ wishes as well as on the development of libburn.</DT>
|
||||
(Be cursed if you install cdrskin as "cdrecord" without clearly forwarding
|
||||
this "don't bother Joerg" demand.)
|
||||
</DD>
|
||||
<DT>Currently (and sparsely) supported gestures :</DT>
|
||||
<DD>$ cdrskin -scanbus</DD>
|
||||
<DD>$ cdrskin dev=1,1,0 -checkdrive</DD>
|
||||
<DD>$ cdrskin dev=1,1,0 -atip</DD>
|
||||
<DD>$ cdrskin -v dev=1,1,0 blank=all -eject</DD>
|
||||
<DD>$ cdrskin -v dev=1,1,0 blank=fast -eject</DD>
|
||||
<DD>$ cdrskin -v dev=1,1,0 speed=12 fs=8m -sao driveropts=burnfree -eject padsize=300k my_image.iso</DD>
|
||||
<DD>$ cdrskin -v dev=1,1,0 ... ... track_1.iso padsize=300k track_2.afio</DD>
|
||||
<DD>$ find . | afio -oZ - | cdrskin -v dev=1,1,0 fs=32m speed=8 -sao driveropts=burnfree padsize=300k tsize=650m -</DD>
|
||||
</DL>
|
||||
</P>
|
||||
|
||||
@ -111,17 +123,7 @@ wishes as well as on the development of libburn.</DT>
|
||||
<UL>
|
||||
<DT></DT>
|
||||
<LI>
|
||||
Option -audio is enabled but by default not advertised via option -help,
|
||||
because no beheading of .au or .wav files takes place yet.
|
||||
<BR>Note: In development version 0.2.3 there is automatic extraction of
|
||||
.wav and .au. So -audio is fully supported.
|
||||
</LI>
|
||||
<LI>
|
||||
Burns only a single closed session. No -multi option yet.
|
||||
</LI>
|
||||
<LI>
|
||||
No TAO mode and therefore no writing on-the-fly without a predefined
|
||||
source size.
|
||||
Appending sessions to unclosed media is restricted to write mode TAO.
|
||||
</LI>
|
||||
<LI>
|
||||
cdrskin -scanbus or --devices hangs for quite a while if there is
|
||||
@ -143,9 +145,10 @@ rw-permissions and retry the bus scan as non-superuser.
|
||||
<P>
|
||||
<DL>
|
||||
<DT>Download as source code (see README):</DT>
|
||||
<DD><A HREF="cdrskin-0.2.2.tar.gz">cdrskin-0.2.2.tar.gz</A>
|
||||
(450 KB).
|
||||
<DD><A HREF="cdrskin-0.2.6.pl02.tar.gz">cdrskin-0.2.6.pl02.tar.gz</A>
|
||||
(510 KB).
|
||||
</DD>
|
||||
<DD>(Most recent patch: backported man page from cdrskin-0.2.7)</DD>
|
||||
<DD>
|
||||
The "stable" cdrskin tarballs are source code identical with "stable"
|
||||
libburn releases or with "stabilized" libburn SVN snapshots. They get
|
||||
@ -154,19 +157,18 @@ cdrskin is part of libburn - full libburn is provided with cdrskin releases.
|
||||
</DD>
|
||||
<DD> </DD>
|
||||
<DT>Download as single x86 binaries (untar and move to /usr/bin/cdrskin):</DT>
|
||||
<DD><A HREF="cdrskin_0.2.2-x86-suse9_0.tar.gz">
|
||||
cdrskin_0.2.2-x86-suse9_0.tar.gz</A>, (60 KB),
|
||||
<DD><A HREF="cdrskin_0.2.6.pl01-x86-suse9_0.tar.gz">
|
||||
cdrskin_0.2.6.pl01-x86-suse9_0.tar.gz</A>, (60 KB),
|
||||
<DL>
|
||||
<DD>runs on SuSE 9.0 (2.4.21) , RIP-14.4 (2.6.14) ,
|
||||
Gentoo (2.6.15 x86_64 Athlon).</DD>
|
||||
</DL>
|
||||
<DD><A HREF="cdrskin_0.2.2-x86-suse9_0-static.tar.gz">
|
||||
cdrskin_0.2.2-x86-suse9_0-static.tar.gz</A>, (250 KB), -static compiled,
|
||||
<DD><A HREF="cdrskin_0.2.6.pl01-x86-suse9_0-static.tar.gz">
|
||||
cdrskin_0.2.6.pl01-x86-suse9_0-static.tar.gz</A>, (260 KB), -static compiled,
|
||||
<DL>
|
||||
<DD>runs on SuSE 7.2 (2.4.4), and on the systems above.</DD>
|
||||
</DL>
|
||||
</DD>
|
||||
<DD> </DD>
|
||||
</DL>
|
||||
<DL><DT>Documentation:</DT>
|
||||
<DD><A HREF="README_cdrskin">README</A> a short introduction</DD>
|
||||
@ -185,34 +187,43 @@ cdrskin_0.2.2-x86-suse9_0-static.tar.gz</A>, (250 KB), -static compiled,
|
||||
|
||||
<HR>
|
||||
|
||||
<P>
|
||||
Enhancements towards previous stable version cdrskin-0.2.4:
|
||||
<UL>
|
||||
<LI>Option <KBD><B>-tao</B></KBD> is fully enabled.<BR>
|
||||
SAO is still the preferred default but TAO is default if a track of
|
||||
unpredicted size is present (stdin, named pipe, ...) or if a follow-up
|
||||
session is written to an appendable CD.
|
||||
(This is an intentional deviation from cdrecord defaults which themselves
|
||||
have changed with the newest cdrecord versions.)
|
||||
</LI>
|
||||
<LI>Status report during blank, preparation and finalization improved.</LI>
|
||||
<LI>Bug fixed: Trailing trash appended to .wav files caused error message
|
||||
and, if exceeding fifo size, could even stall a burn.
|
||||
(Workaround: disable fifo by <KBD><B>fs=0</B></KBD>)</LI>
|
||||
<LI>Bug fixed: False speed with first pacifier cycle. Potential program
|
||||
abort by floating point exception (NaN).</LI>
|
||||
<LI>multi-session CDs: <KBD><B>-multi</B></KBD>, <KBD><B>-msinfo</B></KBD>,
|
||||
writing to appendable CDs (for now restricted to write mode TAO).</LI>
|
||||
</UL>
|
||||
</P>
|
||||
|
||||
<HR>
|
||||
|
||||
<P>
|
||||
<DL>
|
||||
<DT>Development snapshot, version 0.2.3 :</DT>
|
||||
<DT><H3>Development snapshot, version 0.2.7 :</H3></DT>
|
||||
<DD>Enhancements towards stable version 0.2.6:
|
||||
(none yet)
|
||||
</DD>
|
||||
<DD> </DD>
|
||||
<DD>Contains non-critical bug fix about failure to eject.</DD>
|
||||
<DD> </DD>
|
||||
<DD>Enhancements towards stable version:
|
||||
<UL>
|
||||
<LI>cdrecord compatibility with addresses of form [ATA:]Bus,Target,Lun.</LI>
|
||||
<LI>Drives adressable via links and device siblings (/dev/cdrom , /dev/scd0).
|
||||
</LI>
|
||||
<LI>Automatic -audio extraction with .wav files and .au files.
|
||||
<BR> $ cdrskin -v dev=1,1,0 track1.wav track2.au -audio -swab track3.raw
|
||||
</LI>
|
||||
<LI>Comments and empty lines allowed in startup files.</LI>
|
||||
<LI>Options -scanbus and --devices print SORRY messages about busy drives.
|
||||
</LI>
|
||||
<LI>Drive buffer fill indicator reports realistic percentage numbers.</LI>
|
||||
<LI>Option -toc is supported, drive firmware revision gets displayed.</LI>
|
||||
</UL></DD>
|
||||
<DD> </DD>
|
||||
<DD><A HREF="README_cdrskin_devel">README 0.2.3</A>
|
||||
<DD><A HREF="cdrskin__help_devel">cdrskin_0.2.3 --help</A></DD>
|
||||
<DD><A HREF="cdrskin_help_devel">cdrskin_0.2.3 -help</A></DD>
|
||||
<DD><A HREF="README_cdrskin_devel">README 0.2.7</A>
|
||||
<DD><A HREF="cdrskin__help_devel">cdrskin_0.2.7 --help</A></DD>
|
||||
<DD><A HREF="cdrskin_help_devel">cdrskin_0.2.7 -help</A></DD>
|
||||
<DD> </DD>
|
||||
<DT>Maintainers of cdrskin unstable packages please use SVN of
|
||||
<A HREF="http://libburn.pykix.org"> libburn.pykix.org</A></DT>
|
||||
<DD>Download: <KBD><B>svn co http://libburn-svn.pykix.org/trunk libburn_pykix</B>
|
||||
<DD>Download: <KBD><B>svn co http://libburn-svn.pykix.org/libburn/trunk libburn_pykix</B>
|
||||
</KBD></DD>
|
||||
<DD>Build: <KBD><B>cd libburn_pykix ; ./bootstrap ; ./configure ; make</B>
|
||||
</KBD></DD>
|
||||
@ -228,15 +239,15 @@ admins with full system souvereignty.</DT>
|
||||
<A HREF="README_cdrskin_devel">upcoming README</A> ):
|
||||
</DD>
|
||||
<DD>
|
||||
<A HREF="cdrskin-0.2.3.tar.gz">cdrskin-0.2.3.tar.gz</A>
|
||||
<A HREF="cdrskin-0.2.7.tar.gz">cdrskin-0.2.7.tar.gz</A>
|
||||
(500 KB).
|
||||
</DD>
|
||||
<DD>Binary (untar and move to /usr/bin/cdrskin):</DD>
|
||||
<DD><A HREF="cdrskin_0.2.3-x86-suse9_0.tar.gz">
|
||||
cdrskin_0.2.3-x86-suse9_0.tar.gz</A>, (60 KB).
|
||||
<DD><A HREF="cdrskin_0.2.7-x86-suse9_0.tar.gz">
|
||||
cdrskin_0.2.7-x86-suse9_0.tar.gz</A>, (60 KB).
|
||||
</DD>
|
||||
<DD><A HREF="cdrskin_0.2.3-x86-suse9_0-static.tar.gz">
|
||||
cdrskin_0.2.3-x86-suse9_0-static.tar.gz</A>, (260 KB)
|
||||
<DD><A HREF="cdrskin_0.2.7-x86-suse9_0-static.tar.gz">
|
||||
cdrskin_0.2.7-x86-suse9_0-static.tar.gz</A>, (260 KB)
|
||||
</DD>
|
||||
</DL>
|
||||
</P>
|
||||
@ -288,24 +299,12 @@ is a GUI frontend which uses cdrecord for CD burning.)
|
||||
<DL>
|
||||
<DT>Example for a test session with a cdrecord based scdbackup installation:</DT>
|
||||
<DD>$ <KBD><B>cdrskin -scanbus</B></KBD></DD>
|
||||
<DD><KBD>...</KBD></DD>
|
||||
<DD><KBD>scsibus1:</KBD></DD>
|
||||
<DD><KBD> 1,0,0 0) 'TEAC' 'CD-ROM CD-532S' '?' Removable CD-ROM</KBD></DD>
|
||||
<DD><KBD>scsibus2:</KBD></DD>
|
||||
<DD><KBD> 2,2,0 1) 'LITE-ON' 'LTR-48125S' '?' Removable CD-ROM</KBD></DD>
|
||||
<DD>$ <KBD><B>export SCDBACKUP_SCSI_ADR="2,2,0"</B></KBD></DD>
|
||||
|
||||
<!-- 0.2.4
|
||||
|
||||
<DD><KBD>...</KBD></DD>
|
||||
<DD><KBD> 2,0,0 0) 'TEAC' 'CD-ROM CD-532S' '?' Removable CD-ROM</KBD></DD>
|
||||
<DD>$ <KBD><B>cdrskin -scanbus dev=ATA</B></KBD></DD>
|
||||
<DD><KBD>...</KBD></DD>
|
||||
<DD><KBD> 1,0,0 1) 'LITE-ON' 'LTR-48125S' '?' Removable CD-ROM</KBD></DD>
|
||||
<DD>$ <KBD><B>export SCDBACKUP_SCSI_ADR="ATA:1,0,0"</B></KBD></DD>
|
||||
|
||||
-->
|
||||
|
||||
<DD>$ <KBD><B>export SCDBACKUP_CDRECORD="cdrskin -v -v tao_to_sao_tsize=650m"</B></KBD></DD>
|
||||
<DD>$ <KBD><B>scdbackup_home</B></KBD></DD>
|
||||
</DL>
|
||||
@ -313,12 +312,9 @@ is a GUI frontend which uses cdrecord for CD burning.)
|
||||
<DT>Example for a permanent configuration of cdrskin based scdbackup</DT>
|
||||
<DD>$ <KBD><B>cd scdbackup-0.8.6/inst</B></KBD></DD>
|
||||
<DD>$ <KBD><B>export SCDBACKUP_USE_CDRSKIN=1</B></KBD></DD>
|
||||
<DD>$ <KBD><B>export SCDBACKUP_EJECT_ADR=/dev/cdrecorder</B></KBD></DD>
|
||||
<DD>$ <KBD><B>./CONFIGURE_CD</B></KBD></DD>
|
||||
<DD><KBD>...</KBD></DD>
|
||||
<DD><KBD>cdrskin 0.2.2 : limited cdrecord compatibility wrapper for libburn</KBD></DD>
|
||||
<DD><KBD>...</KBD></DD>
|
||||
<DD><KBD> ------------------- SCSI devices. To be used like 0,0,0</KBD></DD>
|
||||
<DD><KBD>cdrskin 0.2.6 : limited cdrecord compatibility wrapper for libburn</KBD></DD>
|
||||
</DL>
|
||||
If your system is stricken with some ill CD device then this can stall
|
||||
and you will have to press <KBD>Ctrl+C</KBD> to abort.
|
||||
@ -327,26 +323,18 @@ In this case, you may execute
|
||||
and try again.
|
||||
<DL>
|
||||
<DT></DT>
|
||||
<DD><KBD> 1,0,0 0) 'TEAC' 'CD-ROM CD-532S' '?' Removable CD-ROM</KBD></DD>
|
||||
<DD><KBD> 2,2,0 1) 'LITE-ON' 'LTR-48125S' '?' Removable CD-ROM</KBD></DD>
|
||||
<DD><KBD>------------------- end of SCSI device list</KBD></DD>
|
||||
|
||||
<!-- 0.2.4
|
||||
|
||||
<DD><KBD> ------------------- SCSI devices. To be used like 0,0,0</KBD></DD>
|
||||
<DD><KBD> 2,0,0 0) 'TEAC' 'CD-ROM CD-532S' '?' Removable CD-ROM</KBD></DD>
|
||||
<DD><KBD> ------------------- end of SCSI device list</KBD></DD>
|
||||
<DD><KBD> ------------------- ATA devices. To be used like ATA:0,0,0
|
||||
<DD><KBD> 1,0,0 1) 'LITE-ON' 'LTR-48125S' '?' Removable CD-ROM</KBD></DD>
|
||||
|
||||
-->
|
||||
|
||||
<DD><KBD>...</KBD></DD>
|
||||
<DD><KBD> * Your cdrecord offers -driveropts=burnfree with your recorder.</KBD></DD>
|
||||
<DD><KBD>...</KBD></DD>
|
||||
<DD><KBD>scdbackup for CD 0.8.6 : First stage of installation done.</KBD></DD>
|
||||
<DD><KBD>...</KBD></DD>
|
||||
<DD><KBD>Now give it a try. Run : scdbackup_home</KBD></DD>
|
||||
<DD>$ <KBD><B>unset SCDBACKUP_USE_CDRSKIN SCDBACKUP_EJECT_ADR</B></KBD></DD>
|
||||
<DD>$ <KBD><B>unset SCDBACKUP_USE_CDRSKIN</B></KBD></DD>
|
||||
</DL>
|
||||
<DL>
|
||||
<DT>To get back to using cdrecord :</DT>
|
||||
|
@ -1 +1 @@
|
||||
#define Cdrskin_timestamP "2006.10.24.165610"
|
||||
#define Cdrskin_timestamP "2006.12.16.090001"
|
||||
|
@ -1,5 +1,23 @@
|
||||
------------------------------------------------------------------------------
|
||||
libburn.pykix.org scdbackup.sourceforge.net/cdrskin
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
Deliberate deviations of cdrskin from cdrecord compatibility:
|
||||
|
||||
+ cdrskin does drive operations (on its drive 0) without a dev= option
|
||||
|
||||
+ gracetime=0 is allowed and set by default
|
||||
|
||||
+ -pad is always set for audio tracks
|
||||
|
||||
+ -sao is default where possible, -tao is default where needed
|
||||
(Note: cdrecord write mode defaults have changed in cdrtools-2.01.01a20)
|
||||
|
||||
+ premature end of source is not an error and leads to full announced tracksize
|
||||
|
||||
+ -msinfo pushes all other messages to stderr. It works independent of
|
||||
other options which would prevent it with cdrecord (-atip, -scanbus, ...)
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
Changelog
|
||||
------------------------------------------------------------------------------
|
||||
@ -742,6 +760,7 @@ libburn/sg.h
|
||||
libburn/sg.c
|
||||
libburn/libdax_msgs.h
|
||||
cdrskin/cdrskin.c
|
||||
Disabled but did not discarded failed attempt to lock against growisofs
|
||||
|
||||
2006.09.27.120656 [234]
|
||||
libburn/drive.h
|
||||
@ -934,7 +953,6 @@ libburn/transport.h
|
||||
cdrskin/cleanup.c
|
||||
Made libburn and cdrskin build on my Linux again
|
||||
|
||||
|
||||
2006.10.13.114554 [271]
|
||||
cdrskin/cdrskin.c
|
||||
libburn/write.c
|
||||
@ -942,7 +960,6 @@ libburn/sg-linux.c
|
||||
libburn/libdax_msgs.h
|
||||
Removed bug in burn_disc_write_sync(): BURN_DRIVE_IDLE, then d->sync_cache()
|
||||
|
||||
|
||||
2006.10.14.105224 [272]
|
||||
libburn/sg.h
|
||||
libburn/sg-freebsd.c
|
||||
@ -954,7 +971,6 @@ Introduced burn_drive_enumerator_t to allow more complete sg-freebsd implementat
|
||||
cdrskin/cdrskin.c
|
||||
Changed ambigous include statement of libburn.h
|
||||
|
||||
|
||||
15 Oct 2006 2006 [275]
|
||||
Makefile.am
|
||||
libburn/libdax_msgs.h
|
||||
@ -1027,25 +1043,715 @@ cdrskin/cdrskin.c
|
||||
cdrskin/compile_cdrskin.sh
|
||||
Made cdrskin use libburn/cleanup.[oh] by default (not cdrskin/cleanup.[ch])
|
||||
|
||||
------------------------------------ cycled - cdrskin-0.2.3 - 2006.10.19
|
||||
------------------------------------ cycled - cdrskin-0.2.3 - 2006.10.19.105730
|
||||
|
||||
2006.10.19.162254 [294]
|
||||
cdrskin/cdrskin.c
|
||||
cdrskin/cdrskin_eng.html
|
||||
cdrskin/changelog.txt
|
||||
Updated documentation about cdrskin-0.2.3
|
||||
|
||||
2006.10.19.164742 [295]
|
||||
cdrskin/cdrskin.c
|
||||
Corrected -help text
|
||||
|
||||
2006.10.20.113421 [297]
|
||||
libburn/libburn.h
|
||||
libburn/mmc.c
|
||||
libburn/sg-linux.c
|
||||
libburn/sg-freebsd.c
|
||||
libburn/drive.c
|
||||
cdrskin/cdrskin.c
|
||||
Made cdrskin produce "ATIP start of lead" (on non-blank media for now)
|
||||
|
||||
20 Oct 2006 [298]
|
||||
doc/comments
|
||||
Updated help text of libburner
|
||||
|
||||
2006.10.20.151602 [299]
|
||||
libburn/libburn.h
|
||||
libburn/drive.c
|
||||
libburn/mmc.c
|
||||
libburn/libdax_msgs.h
|
||||
cdrskin/cdrskin.c
|
||||
Classified media with TOC read error as unsuitable (rather than as blank)
|
||||
|
||||
2006.10.21.103352 [300]
|
||||
libburn/libburn.h
|
||||
libburn/transport.h
|
||||
libburn/drive.c
|
||||
libburn/mmc.c
|
||||
libburn/spc.c
|
||||
libburn/sg-linux.c
|
||||
libburn/sg-freebsd.c
|
||||
libburn/libdax_msgs.h
|
||||
cdrskin/cdrskin.c
|
||||
Implemented some ATIP functionality
|
||||
|
||||
2006.10.21.103653 [301] [302]
|
||||
libburn/libburn.h
|
||||
Clarified relation of burn_disc_read_atip() and burn_drive_get_start_end_lba()
|
||||
|
||||
2006.10.21.185102 [303]
|
||||
libburn/sg.h
|
||||
libburn/mmc.h
|
||||
libburn/mmc.c
|
||||
libburn/sbc.h
|
||||
libburn/sbc.c
|
||||
libburn/spc.h
|
||||
libburn/spc.c
|
||||
libburn/drive.h
|
||||
libburn/drive.c
|
||||
libburn/transport.h
|
||||
libburn/sg-linux.c
|
||||
libburn/sg-freebsd.c
|
||||
Split enumerate_common() into logic-layer, command-layer, transport-layer
|
||||
|
||||
2006.10.22.130341 [304]
|
||||
libburn/libburn.h
|
||||
libburn/mmc.c
|
||||
libburn/drive.c
|
||||
libburn/cleanup.c
|
||||
cdrskin/cleanup.c
|
||||
cdrskin/cdrskin.c
|
||||
Implemented cdrskin -toc
|
||||
|
||||
2006.10.22.131452 [305]
|
||||
cdrskin/cdrskin.c
|
||||
Regulated coexistence of options -toc and -atip
|
||||
|
||||
23 Oct 2006 [306]
|
||||
libburn/mmc.c
|
||||
Made clarification in remark about atip speed conversion
|
||||
|
||||
2006.10.23.074430 [307]
|
||||
cdrskin/cdrskin.c
|
||||
Made use of burn_drive_info.revision as firmware revision text
|
||||
|
||||
2006.10.23.113116 [308]
|
||||
libburn/libburn.h
|
||||
libburn/transport.h
|
||||
libburn/mmc.h
|
||||
libburn/mmc.c
|
||||
libburn/write.c
|
||||
cdrskin/cdrskin.c
|
||||
cdrskin/cdrskin_eng.html
|
||||
cdrskin/README
|
||||
Made available drive buffer fill during write
|
||||
|
||||
23 Oct 2006 [309]
|
||||
libburn/sg-freebsd.c
|
||||
Updated tangling of FreeBSD code with mmc.c :(
|
||||
|
||||
2006.10.23.134719 [310]
|
||||
cdrskin/cdrskin.c
|
||||
Corrected -toc track counter and notified about "hidden" tracks
|
||||
|
||||
2006.10.24.075039 [311]
|
||||
libburn/libdax_audioxtr.h
|
||||
libburn/libdax_audioxtr.c
|
||||
test/dewav.c
|
||||
cdrskin/cdrskin.c
|
||||
Introduced extraction of .au (but not its usage within cdrskin)
|
||||
|
||||
2006.10.24.102107 [312]
|
||||
libburn/libburn.h
|
||||
libburn/structure.h
|
||||
libburn/structure.c
|
||||
libburn/sector.c
|
||||
cdrskin/cdrskin.c
|
||||
cdrskin/README
|
||||
Enabled byte swapping for audio track sources, added anti option -swab
|
||||
|
||||
2006.10.24.130259 [313]
|
||||
test/dewav.c
|
||||
cdrskin/cdrskin.c
|
||||
cdrskin/README
|
||||
Enabled automatic extraction of .au
|
||||
|
||||
24 Oct 2006 [314]
|
||||
Makefile.am
|
||||
+ test/fake_au.c
|
||||
Introduced temporary test program to produce SUN .au files
|
||||
|
||||
------------------------------------ cycled - cdrskin-0.2.3 - 2006.10.24.144650
|
||||
|
||||
2006.10.24.165427 [315]
|
||||
libburn/sector.c
|
||||
Closed some loopholes for byte swapping.
|
||||
|
||||
|
||||
2006 []
|
||||
Open O_EXCL all devices in address resolution chain
|
||||
(Might help with non-standard hdX device siblings)
|
||||
2006.10.24.165610 [316]
|
||||
cdrskin/cdrskin.c
|
||||
Enabled audio byte swapping by default (to be disabled via option -swab)
|
||||
|
||||
2006 []
|
||||
Convert libburn_experimental: into LIBDAX_MSGS_SEV_DEBUG
|
||||
Convert burn_print() into libdax_msgs_submit()
|
||||
------------------------------------ cycled - cdrskin-0.2.3 - 2006.10.24.173602
|
||||
|
||||
2006 []
|
||||
Test unlocking of all drives by burn_drive_info_free()
|
||||
Test unlocking of single drive by burn_drive_grab(), burn_drive_release()
|
||||
25 Oct 2006 [317]
|
||||
cdrskin/README
|
||||
cdrskin/cdrskin_eng.html
|
||||
cdrskin/wiki_plain.txt
|
||||
Announced full -audio compatibility with cdrecord
|
||||
|
||||
------------------------------------ cycled - cdrskin-0.2.3 - 2006.10.25.160540
|
||||
|
||||
2006.10.27.114326 [319 branch]
|
||||
- cdrskin/add_ts_changes_to_libburn_0_2_3
|
||||
+ cdrskin/add_ts_changes_to_libburn_0_2_4
|
||||
cdrskin/README
|
||||
cdrskin/cdrskin.c
|
||||
cdrskin/cdrskin_eng.html
|
||||
cdrskin/changelog.txt
|
||||
README
|
||||
Performed cdrskin version leap to cdrskin-0.2.4
|
||||
|
||||
2006.10.28.093922 [320 branch]
|
||||
cdrskin/cdrskin_timestamp.h
|
||||
Set final timestamp 2006.10.28.093922
|
||||
|
||||
------------------------------------ cycled - cdrskin-0.2.4 - 2006.10.28.093922
|
||||
|
||||
2006.10.28.115213 [321 trunk] [322 trunk]
|
||||
- cdrskin/add_ts_changes_to_libburn_0_2_3
|
||||
+ cdrskin/add_ts_changes_to_libburn_0_2_4
|
||||
+ cdrskin/add_ts_changes_to_libburn_0_2_5
|
||||
cdrskin/README
|
||||
cdrskin/cdrskin.c
|
||||
cdrskin/cdrskin_eng.html
|
||||
cdrskin/changelog.txt
|
||||
README
|
||||
Performed cdrskin version leap to cdrskin-0.2.5
|
||||
|
||||
28 Oct 2006 [323 branch]
|
||||
- cdrskin/add_ts_changes_to_libburn_0_2_3
|
||||
cdrskin/add_ts_changes_to_libburn_0_2_4
|
||||
Corrected misnaming of my development directory
|
||||
|
||||
28 Oct 2006 [324 trunk]
|
||||
cdrskin/add_ts_changes_to_libburn_0_2_4
|
||||
cdrskin/add_ts_changes_to_libburn_0_2_5
|
||||
Corrected misnaming of my development directory
|
||||
|
||||
2006.10.28.132532 [325 branch]
|
||||
cdrskin/cdrskin.c
|
||||
Corrected last-minute bug which made every track from file an audio track
|
||||
|
||||
2006.10.28.151521 [326 trunk]
|
||||
cdrskin/cdrskin.c
|
||||
Corrected bug which made every track from file an audio track
|
||||
|
||||
29 Oct 2006 [327 branch] [328 trunk]
|
||||
+ CONTRIBUTORS
|
||||
Copied missing file from libburn-0.2.2
|
||||
|
||||
29 Oct 2006 [329 branch] [330 trunk]
|
||||
Makefile.am
|
||||
Added to EXTRA_DIST cdrskin/cleanup.[ch]
|
||||
|
||||
29 Oct 2006 [331 branch] [332 trunk]
|
||||
Makefile.am
|
||||
Added to EXTRA_DIST libburn/sg-*.[ch]
|
||||
|
||||
29 Oct 2006 [333 branch] [334 trunk]
|
||||
Makefile.am
|
||||
Deleted from to EXTRA_DIST libburn/sg-*.h
|
||||
|
||||
30 Oct 2006 [337]
|
||||
libburn/transport.h
|
||||
libburn/mmc.c
|
||||
libburn/sg-freebsd.c
|
||||
Made MMC command CLOSE TRACK/SESSION available to struct burn_drive
|
||||
|
||||
2006.10.31.115606 [338]
|
||||
libburn/transport.h
|
||||
libburn/spc.c
|
||||
libburn/mmc.c
|
||||
libburn/write.h
|
||||
libburn/write.c
|
||||
libburn/sector.c
|
||||
libburn/libdax_msgs.h
|
||||
libburn/sg.h
|
||||
libburn/sg-linux.c
|
||||
cdrskin/cdrskin.c
|
||||
Made single track TAO work (sector i/o still wants fixed size, though)
|
||||
|
||||
2006.10.31.184736 [339]
|
||||
libburn/sector.c
|
||||
libburn/source.c
|
||||
libburn/structure.h
|
||||
libburn/structure.c
|
||||
libburn/write.c
|
||||
cdrskin/cdrskin.c
|
||||
Made single track TAO work without fixed size (compile -experimental)
|
||||
|
||||
2006.11.01.163934 [340]
|
||||
libburn/libburn.h
|
||||
libburn/source.c
|
||||
libburn/write.h
|
||||
libburn/write.c
|
||||
libburn/structure.c
|
||||
libburn/structure.h
|
||||
libburn/sector.c
|
||||
cdrskin/cdrskin.c
|
||||
cdrskin/compile_cdrskin.sh
|
||||
Adapted cdrskin pacifier to possibly unknown track size
|
||||
|
||||
2006.11.01.172004 [341]
|
||||
configure.ac
|
||||
bootstrap
|
||||
test/libburner.c
|
||||
Repaired broken macro settings during Linux build
|
||||
|
||||
2 Nov 2006 [342]
|
||||
cdrskin/README
|
||||
Mentioned -tao and experimental compile
|
||||
|
||||
2006.11.02.211816 [343]
|
||||
libburn/libburn.h
|
||||
libburn/write.c
|
||||
cdrskin/cdrskin.c
|
||||
Installed status communications about closing session ("Fixating")
|
||||
|
||||
3 Nov 2006 [344]
|
||||
test/libburner.c
|
||||
Changed status report during blanking (there are no "sectors")
|
||||
|
||||
2006.11.03.063307 [345]
|
||||
cdrskin/cdrskin.c
|
||||
Removed some obsolete debugging messages
|
||||
|
||||
2006.11.03.151137 [346]
|
||||
libburn/structure.h
|
||||
libburn/structure.c
|
||||
libburn/write.h
|
||||
libburn/write.c
|
||||
libburn/sector.c
|
||||
libburn/libdax_msgs.h
|
||||
Installed decent abort behavior with TAO
|
||||
|
||||
2006.11.03.202403 [347]
|
||||
libburn/write.c
|
||||
Enabled TAO for multiple -data tracks (-audio still ends after 0 bytes)
|
||||
|
||||
2006.11.04.092909 [348]
|
||||
libburn/spc.c
|
||||
libburn/sector.c
|
||||
Enabled audio tracks with TAO
|
||||
|
||||
2006.11.02.140329 (pl01) [351 tags/CdrskinZeroTwoFourPlZeroOne]
|
||||
../cdrskin-0.2.4.patch01/configure.ac
|
||||
../cdrskin-0.2.4.patch01/bootstrap
|
||||
../cdrskin-0.2.4.patch01/cdrskin/README
|
||||
../cdrskin-0.2.4.patch01/cdrskin/add_ts_changes_to_libburn_0_2_4_patch01
|
||||
../cdrskin-0.2.4.patch01/cdrskin/cdrskin_timestamp.h
|
||||
../cdrskin-0.2.4.patch01/test/libburner.c
|
||||
Revoked autotools aspect of revision 290
|
||||
|
||||
2006.11.06.073810 [352]
|
||||
cdrskin/cdrskin.c
|
||||
cdrskin/cdrskin_eng.html
|
||||
cdrskin/changelog.txt
|
||||
Adapted documentation to reflect experimental TAO mode
|
||||
|
||||
------------------------------------ cycle - cdrskin-0.2.5 - 2006.11.06.085056
|
||||
|
||||
2006.11.06.114159 [353]
|
||||
libburn/libburn.h
|
||||
libburn/spc.c
|
||||
libburn/options.c
|
||||
cdrskin/cdrskin.c
|
||||
New API burn_write_opts_set_multi(). (But libburn cannot burn next session yet)
|
||||
|
||||
2006.11.06.121409 [354]
|
||||
cdrskin/cdrskin.c
|
||||
Made -toc on multiple sessions more compatible
|
||||
|
||||
2006.11.06.155237 [355]
|
||||
libburn/libburn.h
|
||||
libburn/drive.c
|
||||
libburn/write.c
|
||||
libburn/mmc.c
|
||||
libburn/sg-linux.c
|
||||
cdrskin/cdrskin.c
|
||||
Made CD with unclosed track blankable (by -force)
|
||||
|
||||
2006.11.06.195743 [356]
|
||||
libburn/transport.h
|
||||
libburn/mmc.c
|
||||
libburn/spc.c
|
||||
libburn/write.c
|
||||
libburn/sg-linux.c
|
||||
Cared for some SCSI error conditions which were ignored up to now
|
||||
|
||||
2006.11.07.114514 [357]
|
||||
cdrskin/cdrskin.c
|
||||
Made -tao default for single track or stdin, -sao for any other multi-track
|
||||
|
||||
7 Nov 2006 [358]
|
||||
cdrskin/cdrskin_eng.html
|
||||
cdrskin/changelog.txt
|
||||
Prepared next cdrskin-0.2.5 upload cycle
|
||||
|
||||
7 Nov 2006 [359]
|
||||
cdrskin/changelog.txt
|
||||
cdrskin/README
|
||||
cdrskin/wiki_plain.txt
|
||||
Updated documentation about TAO
|
||||
|
||||
2006.11.07.152018 [360]
|
||||
cdrskin/cdrskin.c
|
||||
cdrskin/changelog.txt
|
||||
Updated documentation about TAO
|
||||
|
||||
------------------------------------ cycle - cdrskin-0.2.5 - 2006.11.07.152018
|
||||
* Enabled TAO
|
||||
|
||||
|
||||
2006.11.08.165648 [361]
|
||||
cdrskin/cdrfifo.h
|
||||
cdrskin/cdrfifo.c
|
||||
cdrskin/cdrskin.c
|
||||
Avoided error message and nonzero exit with trailing trash on .wav
|
||||
|
||||
8 Nov 2006 [362]
|
||||
cdrskin/cdrskin_eng.html
|
||||
Mentioned bug fix about trailing trash
|
||||
|
||||
2006.11.08.172918 [363]
|
||||
libburn/write.c
|
||||
Made track write counter of SAO count rather too much than too few bytes
|
||||
|
||||
8 Nov 2006 [364]
|
||||
cdrskin/changelog.txt
|
||||
Prepared next cdrskin-0.2.5 upload cycle
|
||||
|
||||
------------------------------------ cycle - cdrskin-0.2.5 - 2006.11.08.181016
|
||||
* Bug fixed: Trailing trash appended to .wav files caused error message
|
||||
and, if exceeding fifo size, could even stall a burn.
|
||||
|
||||
|
||||
2006.11.09.112832 [365]
|
||||
cdrskin/cdrskin.c
|
||||
Check desired write and block type with burn_drive_info.*_block_types
|
||||
|
||||
2006.11.09.113729 [366]
|
||||
libburn/sg-linux.c
|
||||
Silenced SCSI error (debugging) messages about missing media
|
||||
|
||||
2006.11.09.151431 [367]
|
||||
cdrskin/cdrskin.c
|
||||
Corrected first speed measurement report in TAO mode (which was random)
|
||||
|
||||
2006.11.09.193030 [368]
|
||||
libburn/libburn.h
|
||||
libburn/write.c
|
||||
cdrskin/cdrskin.c
|
||||
Experimentally enabled burning to BURN_DISC_APPENDABLE (tested with TAO only)
|
||||
|
||||
2006.11.10.093843 [369]
|
||||
cdrskin/cdrskin.c
|
||||
doc/comments
|
||||
Expressing more self-confidence
|
||||
|
||||
2006.11.10.172212 [370]
|
||||
cdrskin/cdrskin.c
|
||||
Provisory -msinfo (very verbous on stderr)
|
||||
|
||||
2006.11.10.184047 [371]
|
||||
cdrskin/cdrskin.c
|
||||
Made it work with fifos and other non-plain files as track sources
|
||||
|
||||
2006.11.10.185209 [372]
|
||||
cdrskin/cdrskin.c
|
||||
Read -msinfo from first track of last session and not from last track
|
||||
|
||||
2006.11.11.122402 [373]
|
||||
libburn/libburn.h
|
||||
libburn/transport.h
|
||||
libburn/mmc.h
|
||||
libburn/mmc.c
|
||||
libburn/write.c
|
||||
libburn/drive.c
|
||||
libburn/libdax_msgs.h
|
||||
New API function burn_disc_track_lba_nwa()
|
||||
|
||||
2006.11.11.122907 [374] [375]
|
||||
cdrskin/cdrskin.c
|
||||
Implemented not so provisory -msinfo
|
||||
|
||||
2006.11.11.124020 [376] [377]
|
||||
cdrskin/cdrskin.c
|
||||
Reacted on some warnings of gcc -O2
|
||||
|
||||
2006.11.11.134752 [378]
|
||||
cdrskin/cdrskin.c
|
||||
Implemented handling of unsuitable disk states with -msinfo
|
||||
|
||||
2006.11.11.152037 [379]
|
||||
cdrskin/cdrskin.c
|
||||
Demanded (for now) -tao for writing to appendable CDs
|
||||
|
||||
2006.11.11.152748 [380]
|
||||
cdrskin/cdrskin.c
|
||||
Generally enabled -multi, -msinfo and writing to appendable CDs in TAO mode
|
||||
|
||||
11 Nov 2006 [381]
|
||||
cdrskin/changelog.txt
|
||||
Prepared next cdrskin-0.2.5 cycle
|
||||
|
||||
11 Nov 2006 [382]
|
||||
cdrskin/cdrskin_eng.html
|
||||
cdrskin/README
|
||||
Updated docs about multi-session
|
||||
|
||||
------------------------------------ cycle - cdrskin-0.2.5 - 2006.11.11.163625
|
||||
* Multi-session CDs (for now restricted to write mode TAO), -multi, -msinfo
|
||||
* Bug fixed: False speed with first pacifier cycle. Potential SIGFPE by NaN.
|
||||
|
||||
|
||||
2006.11.12.085808 [383]
|
||||
libburn/mmc.c
|
||||
Made speed 0 in burn_drive_set_speed() really maximum speed (i.e. FFFFh)
|
||||
|
||||
2006.11.12.113629 [384]
|
||||
cdrskin/cdrskin.c
|
||||
Made SAO preferrable default write mode, kept TAO as default where needed
|
||||
|
||||
2006.11.12.152723 [385]
|
||||
libburn/mmc.c
|
||||
libburn/libdax_msgs.h
|
||||
Reacted on error condition during write operation
|
||||
|
||||
2006.11.12.185342 [386]
|
||||
cdrskin/cdrskin.c
|
||||
Made -toc on blank CD exit with value 0 (rather than 7 with no media)
|
||||
|
||||
13 Nov 2006 [390]
|
||||
README
|
||||
cdrskin/add_ts_changes_to_libburn_0_2_5
|
||||
cdrskin/cdrskin_eng.html
|
||||
Changed SVN URLs to reflect new structure
|
||||
|
||||
13 Nov 2006 [391]
|
||||
README
|
||||
Refered to both libburn and libisofs in SVN description, updated history
|
||||
|
||||
------------------------------------ cycle - cdrskin-0.2.5 - 2006.11.13.122418
|
||||
* Enabled named pipes and other non-plain file types as track sources
|
||||
(actually already in previous cycle)
|
||||
* Bug fixed: Default speed was not highest possible but announced as "MAX"
|
||||
* SAO is preferred default write mode, TAO is default when needed
|
||||
|
||||
|
||||
2006.11.13.122418 [392]
|
||||
cdrskin/changelog.txt
|
||||
Next cdrskin-0.2.5 cycle
|
||||
|
||||
13 Nov 2006 [393]
|
||||
cdrskin/cdrskin_eng.html
|
||||
Adjusted description of write mode default
|
||||
|
||||
13 Nov 2006 [394]
|
||||
Makefile.am
|
||||
configure.ac
|
||||
Removed references to libisofs
|
||||
|
||||
14 Nov 2006 [399]
|
||||
README
|
||||
Changed references to libisofs
|
||||
|
||||
14 Nov 2006 [400]
|
||||
cdrskin/README
|
||||
Corrected a typo reported by George Danchev
|
||||
|
||||
2006.11.14.104023 [401]
|
||||
cdrskin/cdrskin.c
|
||||
Implemented try to find on restricted drives a suitable write mode as default
|
||||
|
||||
14 Nov 2006 [402]
|
||||
libburn/libburn.h
|
||||
Fixed a wrong name in API description
|
||||
|
||||
15 Nov 2006 [403]
|
||||
libburn/write.h
|
||||
libburn/write.c
|
||||
Prepared tests for eventual drive which might support SAO to appendable CD
|
||||
|
||||
15 Nov 2006 [404]
|
||||
libburn/sg-linux.c
|
||||
Enhanced optional SCSI command logging
|
||||
|
||||
2006.11.15.091329 [405]
|
||||
cdrskin/cdrskin.c
|
||||
Adjusted some texts to new multi-session situation
|
||||
|
||||
2006.11.15.170927 [406]
|
||||
libburn/sg.h
|
||||
libburn/sg-linux.c
|
||||
libburn/sg-freebsd.c
|
||||
+ libburn/sg-freebsd-port.c
|
||||
libburn/spc.h
|
||||
libburn/spc.c
|
||||
libburn/drive.c
|
||||
Made portability clarifications
|
||||
|
||||
15 Nov 2006 [407]
|
||||
libburn/sg-linux.c
|
||||
libburn/sg-freebsd-port.c
|
||||
Narrowed system specific part of enumerate_common()
|
||||
|
||||
2006.11.16.111656 [408] [409]
|
||||
Makefile.am
|
||||
+ libburn/os.h
|
||||
+ libburn/os-linux.h
|
||||
+ libburn/os-freebsd.h
|
||||
libburn/sg.h
|
||||
libburn/sg.c
|
||||
libburn/transport.h
|
||||
libburn/cleanup.c
|
||||
libburn/sg-linux.c
|
||||
libburn/sg-freebsd-port.c
|
||||
cdrskin/cleanup.c
|
||||
cdrskin/compile_cdrskin.sh
|
||||
Made consolidaed operating system adapters for ease of porting
|
||||
|
||||
2006.11.16.133951 [410]
|
||||
libburn/sg-linux.c
|
||||
libburn/sg-freebsd-port.c
|
||||
Polished porting hints and self-compliance to newly established specs
|
||||
|
||||
16 Nov 2006 [411]
|
||||
libburn/sg-linux.c
|
||||
libburn/sg-freebsd-port.c
|
||||
Polished porting hints
|
||||
|
||||
16 Nov 2006 [412]
|
||||
test/libburner.c
|
||||
Obsoleted --stdin_size by automatic TAO, cared for non-plain track files
|
||||
|
||||
16 Nov 2006 [413]
|
||||
doc/comments
|
||||
Updated help text of libburner
|
||||
|
||||
17 Nov 2006 [414]
|
||||
Makefile.am
|
||||
+ test/telltoc.c
|
||||
Created info retrieval companion for libburner
|
||||
|
||||
17 Nov 2006 [415]
|
||||
test/libburner.c
|
||||
Enabled multi-session with libburner
|
||||
|
||||
17 Nov 2006 [416]
|
||||
Makefile.am
|
||||
- test/toc.c
|
||||
Obsoleted old test program
|
||||
|
||||
17 Nov 2006 [417] [418]
|
||||
test/telltoc.c
|
||||
Shifted a comma
|
||||
|
||||
2006.11.18.194606 [419]
|
||||
libburn/transport.h
|
||||
libburn/sbc.h
|
||||
libburn/sbc.c
|
||||
libburn/drive.c
|
||||
Test wether SCSI 1Bh START UNIT would be helpful with ticket 90
|
||||
|
||||
2006.11.19.114413 [420]
|
||||
libburn/libburn.h
|
||||
libburn/mmc.c
|
||||
libburn/sector.c
|
||||
libburn/write.c
|
||||
cdrskin/cdrskin.c
|
||||
Implemented libburn builtin fine granulated drive buffer min-fill recording
|
||||
|
||||
2006.11.19.162900 [421]
|
||||
cdrskin/cdrskin.c
|
||||
Avoided self contradiction with "Min drive buffer fill"
|
||||
|
||||
2006.11.19.163646 [422]
|
||||
cdrskin/cdrskin.c
|
||||
Fixed missing brackets from revison 421
|
||||
|
||||
2006.11.20.090207 [423]
|
||||
libburn/sector.c
|
||||
Silenced compiler warnings
|
||||
|
||||
2006.11.20.090503 [424]
|
||||
libburn/drive.c
|
||||
Removed a redundant d->start_unit() of revision 419
|
||||
|
||||
|
||||
------------------------------------ cycle - cdrskin-0.2.5 - 2006.11.20.092808
|
||||
* libisofs unbundled from libburn+cdrskin
|
||||
* Hints for porting to other operating systems are now in sg-*.c
|
||||
|
||||
20 Nov 2006 [425]
|
||||
cdrskin/changelog.txt
|
||||
Next cdrskin-0.2.5 cycle
|
||||
|
||||
2006.11.20.132717 [426]
|
||||
cdrskin/cdrskin.c
|
||||
cdrskin/cdrfifo.h
|
||||
cdrskin/cdrfifo.c
|
||||
cdrskin/wiki_plain.txt
|
||||
Implemented new option fifo_start_at=
|
||||
|
||||
20 Nov 2006 [427]
|
||||
cdrskin/wiki_plain.txt
|
||||
Updated in respect to multi-session
|
||||
|
||||
2006.11.22.122228 [428]
|
||||
libburn/spc.h
|
||||
libburn/spc.c
|
||||
Coordinated scsi_notify_error() and scsi_error()
|
||||
|
||||
|
||||
------------------------------------ cycle - cdrskin-0.2.5 - 2006.11.22.142517
|
||||
|
||||
23 Nov 2006 [433]
|
||||
cdrskin/changelog.txt
|
||||
Last cdrskin-0.2.5 cycle
|
||||
|
||||
|
||||
23 Nov 2006 [434] [tag ]
|
||||
- test/burn.c
|
||||
- test/master.c
|
||||
- test/tree.py
|
||||
- test/tree.pyc
|
||||
- test/rip.c
|
||||
Removed obsolete test programs
|
||||
|
||||
|
||||
2006.11.23.102340 [435] [tag ]
|
||||
Makefile.am
|
||||
cdrskin/compile_cdrskin.sh
|
||||
cdrskin/cdrskin.c
|
||||
cdrskin/cdrskin_eng.html
|
||||
cdrskin/README
|
||||
cdrskin/wiki_plain.txt
|
||||
+ cdrskin/add_ts_changes_to_libburn_0_2_6
|
||||
+ cdrskin/add_ts_changes_to_libburn_0_2_7
|
||||
- cdrskin/add_ts_changes_to_libburn_0_2_4
|
||||
- cdrskin/add_ts_changes_to_libburn_0_2_5
|
||||
Version leap to 0.2.6
|
||||
|
||||
|
||||
2006.11.24.121745 [437]
|
||||
Version leap to 0.2.7
|
||||
|
||||
|
||||
2006.12.16.090001 []
|
||||
Makefile.am
|
||||
+ cdrskin-0.2.6/cdrskin/cdrskin.1
|
||||
cdrskin-0.2.6/cdrskin/README
|
||||
cdrskin-0.2.6/cdrskin/cdrskin_eng.html
|
||||
cdrskin-0.2.6/cdrskin/changelog.txt
|
||||
cdrskin-0.2.6/cdrskin/cdrskin_timestamp.h
|
||||
Backported manpage to libburn-0.2.6 -> libburn-0.2.6.2
|
||||
|
||||
|
||||
|
||||
2006 []
|
||||
Clear outdated persistent read buffer after small CD image was read (ticket 57)
|
||||
|
||||
|
||||
----------------------------------------- long term intentions:
|
||||
|
||||
|
@ -23,31 +23,27 @@ typedef void (*sighandler_t)(int);
|
||||
|
||||
#include "cleanup.h"
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
|
||||
#ifndef Cleanup_has_no_libburn_os_H
|
||||
|
||||
|
||||
#include "../libburn/os.h"
|
||||
|
||||
/* see os.h for name of particular os-*.h where this is defined */
|
||||
static int signal_list[]= { BURN_OS_SIGNAL_MACRO_LIST , -1};
|
||||
static char *signal_name_list[]= { BURN_OS_SIGNAL_NAME_LIST , "@"};
|
||||
static int signal_list_count= BURN_OS_SIGNAL_COUNT;
|
||||
static int non_signal_list[]= { BURN_OS_NON_SIGNAL_MACRO_LIST, -1};
|
||||
static int non_signal_list_count= BURN_OS_NON_SIGNAL_COUNT;
|
||||
|
||||
|
||||
#else /* ! Cleanup_has_no_libburn_os_H */
|
||||
|
||||
|
||||
/* Outdated. Linux only. For backward compatibility with pre-libburn-0.2.3 */
|
||||
|
||||
/* Signals to be caught */
|
||||
static int signal_list[]= {
|
||||
SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT,
|
||||
SIGFPE, SIGSEGV, SIGPIPE, SIGALRM, SIGTERM,
|
||||
SIGUSR1, SIGUSR2, SIGXCPU, SIGTSTP, SIGTTIN,
|
||||
SIGTTOU,
|
||||
SIGBUS, SIGPROF, SIGSYS, SIGTRAP,
|
||||
SIGVTALRM, SIGXCPU, SIGXFSZ, -1
|
||||
};
|
||||
static char *signal_name_list[]= {
|
||||
"SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGABRT",
|
||||
"SIGFPE", "SIGSEGV", "SIGPIPE", "SIGALRM", "SIGTERM",
|
||||
"SIGUSR1", "SIGUSR2", "SIGXCPU", "SIGTSTP", "SIGTTIN",
|
||||
"SIGTTOU",
|
||||
"SIGBUS", "SIGPROF", "SIGSYS", "SIGTRAP",
|
||||
"SIGVTALRM", "SIGXCPU", "SIGXFSZ", "@"
|
||||
};
|
||||
static int signal_list_count= 23;
|
||||
|
||||
#else /* __FreeBSD__ */
|
||||
|
||||
/* Signals to be caught */
|
||||
static int signal_list[]= {
|
||||
static int signal_list[]= {
|
||||
SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT,
|
||||
SIGFPE, SIGSEGV, SIGPIPE, SIGALRM, SIGTERM,
|
||||
SIGUSR1, SIGUSR2, SIGXCPU, SIGTSTP, SIGTTIN,
|
||||
@ -55,7 +51,7 @@ static int signal_list[]= {
|
||||
SIGBUS, SIGPOLL, SIGPROF, SIGSYS, SIGTRAP,
|
||||
SIGVTALRM, SIGXCPU, SIGXFSZ, -1
|
||||
};
|
||||
static char *signal_name_list[]= {
|
||||
static char *signal_name_list[]= {
|
||||
"SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGABRT",
|
||||
"SIGFPE", "SIGSEGV", "SIGPIPE", "SIGALRM", "SIGTERM",
|
||||
"SIGUSR1", "SIGUSR2", "SIGXCPU", "SIGTSTP", "SIGTTIN",
|
||||
@ -65,15 +61,17 @@ static char *signal_name_list[]= {
|
||||
};
|
||||
static int signal_list_count= 24;
|
||||
|
||||
#endif /* ! __FreeBSD__ */
|
||||
|
||||
/* Signals not to be caught */
|
||||
static int non_signal_list[]= {
|
||||
SIGKILL, SIGCHLD, SIGSTOP, SIGURG, -1
|
||||
};
|
||||
};
|
||||
static int non_signal_list_count= 4;
|
||||
|
||||
|
||||
#endif /* Cleanup_has_no_libburn_os_H */
|
||||
|
||||
|
||||
|
||||
/* run time dynamic part */
|
||||
static char cleanup_msg[4096]= {""};
|
||||
static int cleanup_exiting= 0;
|
||||
|
@ -6,7 +6,8 @@
|
||||
|
||||
debug_opts=
|
||||
def_opts=
|
||||
libvers="-DCdrskin_libburn_0_2_3"
|
||||
largefile_opts="-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE=1"
|
||||
libvers="-DCdrskin_libburn_0_2_6"
|
||||
cleanup_src_or_obj="libburn/cleanup.o"
|
||||
libdax_msgs_o="libburn/libdax_msgs.o"
|
||||
libdax_audioxtr_o="libburn/libdax_audioxtr.o"
|
||||
@ -31,16 +32,16 @@ do
|
||||
libvers="-DCdrskin_libburn_cvs_A60220_tS"
|
||||
libdax_audioxtr_o=
|
||||
libdax_msgs_o="libburn/message.o"
|
||||
cleanup_src_or_obj="cdrskin/cleanup.c"
|
||||
elif test "$i" = "-libburn_0_2_2"
|
||||
cleanup_src_or_obj="-DCleanup_has_no_libburn_os_H cdrskin/cleanup.c"
|
||||
elif test "$i" = "-libburn_0_2_6"
|
||||
then
|
||||
libvers="-DCdrskin_libburn_0_2_2"
|
||||
libdax_audioxtr_o=
|
||||
libdax_msgs_o="libburn/message.o"
|
||||
cleanup_src_or_obj="cdrskin/cleanup.c"
|
||||
elif test "$i" = "-libburn_0_2_3"
|
||||
libvers="-DCdrskin_libburn_0_2_6"
|
||||
libdax_audioxtr_o="libburn/libdax_audioxtr.o"
|
||||
libdax_msgs_o="libburn/libdax_msgs.o"
|
||||
cleanup_src_or_obj="libburn/cleanup.o"
|
||||
elif test "$i" = "-libburn_svn"
|
||||
then
|
||||
libvers="-DCdrskin_libburn_0_2_3"
|
||||
libvers="-DCdrskin_libburn_0_2_6"
|
||||
libdax_audioxtr_o="libburn/libdax_audioxtr.o"
|
||||
libdax_msgs_o="libburn/libdax_msgs.o"
|
||||
cleanup_src_or_obj="libburn/cleanup.o"
|
||||
@ -50,6 +51,10 @@ do
|
||||
elif test "$i" = "-oldfashioned"
|
||||
then
|
||||
def_opts="$def_opts -DCdrskin_oldfashioned_api_usE"
|
||||
cleanup_src_or_obj="-DCleanup_has_no_libburn_os_H cdrskin/cleanup.c"
|
||||
elif test "$i" = "-no_largefile"
|
||||
then
|
||||
largefile_opts=
|
||||
elif test "$i" = "-do_not_compile_cdrskin"
|
||||
then
|
||||
compile_cdrskin=0
|
||||
@ -74,8 +79,9 @@ do
|
||||
echo " -compile_cdrfifo compile program cdrskin/cdrfifo."
|
||||
echo " -compile_dewav compile program test/dewav without libburn."
|
||||
echo " -cvs_A60220 set macro to match libburn-CVS of 20 Feb 2006."
|
||||
echo " -libburn_0_2_2 set macro to match libburn-0.2.2."
|
||||
echo " -libburn_0_2_3 set macro to match current libburn-SVN."
|
||||
echo " -libburn_0_2_6 set macro to match libburn-0.2.6."
|
||||
echo " -libburn_svn set macro to match current libburn-SVN."
|
||||
echo " -no_largefile do not use 64 bit off_t (must match libburn)."
|
||||
echo " -do_not_compile_cdrskin omit compilation of cdrskin/cdrskin."
|
||||
echo " -experimental use newly introduced libburn features."
|
||||
echo " -oldfashioned use pre-0.2.2 libburn features only."
|
||||
@ -99,7 +105,14 @@ echo "Build timestamp : $timestamp"
|
||||
if test "$compile_cdrskin"
|
||||
then
|
||||
echo "compiling program cdrskin/cdrskin.c $static_opts $debug_opts $libvers $def_opts $cleanup_src_or_obj"
|
||||
cc $warn_opts -I. $static_opts $debug_opts $libvers $def_opts \
|
||||
cc -I. \
|
||||
$warn_opts \
|
||||
$static_opts \
|
||||
$debug_opts \
|
||||
$libvers \
|
||||
$largefile_opts \
|
||||
$def_opts \
|
||||
\
|
||||
-DCdrskin_build_timestamP='"'"$timestamp"'"' \
|
||||
\
|
||||
-o cdrskin/cdrskin \
|
||||
|
@ -2,7 +2,7 @@
|
||||
cdrskin Wiki - plain text copy
|
||||
--------------------------------------------------------------------------
|
||||
|
||||
cdrskin-0.2.3 is the cdrecord compatibility middleware of libburn.
|
||||
cdrskin is the cdrecord compatibility middleware of libburn.
|
||||
|
||||
Its paragon, cdrecord, is a powerful GPL'ed burn program included in Joerg
|
||||
Schilling's cdrtools. cdrskin strives to be a second source for the services
|
||||
@ -12,12 +12,16 @@ Its future ability to burn DVD media depends on the development of libburn.
|
||||
cdrskin does not contain any bytes copied from cdrecord's sources.
|
||||
Many bytes have been copied from the message output of cdrecord
|
||||
runs, though. The most comprehensive technical overview of cdrskin
|
||||
can be found in cdrskin/README .
|
||||
can be found in cdrskin/README . Online available as :
|
||||
http://libburn.pykix.org/browser/trunk/cdrskin/README?format=raw
|
||||
|
||||
About libburn API for burning CD: http://libburn-api.pykix.org
|
||||
|
||||
--------------------------------------------------------------------------
|
||||
|
||||
cdrskin with CD media fails to match its paragon cdrecord on two major
|
||||
fields: convenient TAO burn mode and multi session.
|
||||
Appending sessions to an unclosed CD is restricted to write mode TAO.
|
||||
(Users who have a burner which succeeds with a follow-up session via
|
||||
cdrecord -sao : please contact us.)
|
||||
|
||||
cdrskin does not provide DVD burning yet. See advise to use dvd+rw-tools
|
||||
at the end of this text.
|
||||
@ -32,9 +36,9 @@ cdrskin for its task to emulate cdrecord. There are some, nevertheless,
|
||||
which provide rather exotic unique features of cdrskin.
|
||||
|
||||
The cdrecord-compatible options are listed in the output of
|
||||
|
||||
{{{
|
||||
cdrskin -help
|
||||
|
||||
}}}
|
||||
where the option "help" has *one* dash.
|
||||
For these options you may expect program behavior that is roughly the
|
||||
same as described in original man 1 cdrecord .
|
||||
@ -42,9 +46,9 @@ same as described in original man 1 cdrecord .
|
||||
Online: http://cdrecord.berlios.de/old/private/man/cdrecord-2.0.html
|
||||
|
||||
The cdrskin-specific options are listed by
|
||||
|
||||
{{{
|
||||
cdrskin --help
|
||||
|
||||
}}}
|
||||
where the option "help" has *two* dashes.
|
||||
|
||||
Those have no man page yet. Some are very experimental and should only be
|
||||
@ -56,11 +60,10 @@ Some are of general user interest, though:
|
||||
--devices allows the sysadmin to scan the system for possible drives
|
||||
and displays their detected properties.
|
||||
The drives are listed one per line, with fields:
|
||||
|
||||
libburn-drive-number sysadmin-device-file permissions : vendor type
|
||||
|
||||
libburn-drive-number, sysadmin-device-file, permissions, vendor, type
|
||||
{{{
|
||||
0 dev='/dev/sg0' rwrw-- : 'HL-DT-ST' 'DVDRAM GSA-4082B'
|
||||
|
||||
}}}
|
||||
This feature is valuable since cdrskin -scanbus will not give you
|
||||
the device file name and its current permissions.
|
||||
cdrskin will accept of course the proposed dev= option as address
|
||||
@ -74,7 +77,7 @@ has to offer both, r- and w-permission.
|
||||
|
||||
--------------------------------------------------------------------------
|
||||
|
||||
--fifo_start_empty is a throughput enhancer for unsteady data streams
|
||||
fifo_start_at=<num> is a throughput enhancer for unsteady data streams
|
||||
like they are produced by a compressing archiver program when piping to
|
||||
CD on-the-fly. It makes better use of the general property of a FIFO
|
||||
buffer to transport surplus bandwidth into the future. Yep. A time machine.
|
||||
@ -98,10 +101,16 @@ underruns, of course.
|
||||
With a very fat fs=# buffer (128 MB for 12x CD is not unrealistic) this
|
||||
can cause a big delay until burning finally starts and takes its due time.
|
||||
|
||||
--fifo_start_empty makes cdrskin start burning without waiting for the
|
||||
FIFO to be full resp. the data stream to end. It can make use of the
|
||||
seconds spend with drive preparation and lead-in, it risks a few drive
|
||||
buffer underruns at the beginning of burn - but modern drives stand this.
|
||||
fifo_start_at=<num> makes cdrskin start burning after the given number of bytes
|
||||
is read rather than waiting for the FIFO to be completely full resp. the data
|
||||
stream to end. It risks a few drive buffer underruns at the beginning of burn
|
||||
- but modern drives stand this.
|
||||
|
||||
Usage examples:
|
||||
{{{
|
||||
cdrskin ... fs=128m fifo_start_at=20m ...
|
||||
cdrskin ... fifo_start_at=0 ...
|
||||
}}}
|
||||
|
||||
Note: no FIFO can give you better average throughput than the average
|
||||
throughput of the data source and the throughput of the burner.
|
||||
@ -125,6 +134,10 @@ tao_to_sao_tsize=<num> allows the - actually unsupported - cdrecord option
|
||||
-tao and defines a default track size to be used if - as custom with -tao -
|
||||
no option tsize=# is given.
|
||||
|
||||
Since -tao is supported in cdrskin-0.2.6 the TAO-to-SAO workaround has become
|
||||
quite obsolete. Nevertheless, tao_to_sao_tsize= allows to preset a default
|
||||
size for SAO mode which is in effect only if no track size is available.
|
||||
|
||||
As in general with cdrskin tsize=# the data source does not have to provide
|
||||
the full annouced amount of data. Missing data will be padded up by 0-bytes.
|
||||
Surplus data is supposed to cause an error, though. The burn will then
|
||||
@ -147,15 +160,15 @@ cdrskin the necessary hint.
|
||||
Example: Your frontend insists in using "0,0,0" and --devices reported
|
||||
dev='/dev/hdc' resp. cdrskin dev=ATA -scanbus reported "1,0,0" then this
|
||||
would be the appropriate translation:
|
||||
|
||||
{{{
|
||||
dev_translation=+0,0,0+/dev/hdc
|
||||
|
||||
}}}
|
||||
The "+" character is a separator to be choosen by you.
|
||||
Currently i am not aware of the need to choose any other than "+"
|
||||
unless you get playful with custom translations like
|
||||
|
||||
{{{
|
||||
dev_translation=-"cd+dvd"-1,0,0
|
||||
|
||||
}}}
|
||||
See http://scdbackup.sourceforge.net/k3b_on_cdrskin.html
|
||||
for an illustrated example with K3b 0.10 .
|
||||
|
||||
|
11
configure.ac
11
configure.ac
@ -1,4 +1,4 @@
|
||||
AC_INIT([libburn], [0.2.3], [http://libburn.pykix.org])
|
||||
AC_INIT([libburn], [0.2.6.2], [http://libburn.pykix.org])
|
||||
AC_PREREQ([2.50])
|
||||
dnl AC_CONFIG_HEADER([config.h])
|
||||
|
||||
@ -7,7 +7,11 @@ AC_CANONICAL_TARGET
|
||||
|
||||
AM_INIT_AUTOMAKE([subdir-objects])
|
||||
|
||||
AM_CONFIG_HEADER(config.h)
|
||||
dnl A61101 This breaks Linux build (makes 32 bit off_t)
|
||||
dnl http://sourceware.org/autobook/autobook/autobook_96.html says
|
||||
dnl one must include some config.h and this was a pitfall.
|
||||
dnl So why dig the pit at all ?
|
||||
dnl AM_CONFIG_HEADER(config.h)
|
||||
|
||||
dnl Making releases:
|
||||
dnl BURN_MICRO_VERSION += 1;
|
||||
@ -21,7 +25,7 @@ dnl if MAJOR or MINOR version changes, be sure to change AC_INIT above to match
|
||||
dnl
|
||||
BURN_MAJOR_VERSION=0
|
||||
BURN_MINOR_VERSION=2
|
||||
BURN_MICRO_VERSION=3
|
||||
BURN_MICRO_VERSION=6.2
|
||||
BURN_INTERFACE_AGE=0
|
||||
BURN_BINARY_AGE=0
|
||||
BURN_VERSION=$BURN_MAJOR_VERSION.$BURN_MINOR_VERSION.$BURN_MICRO_VERSION
|
||||
@ -108,6 +112,5 @@ AC_CONFIG_FILES([
|
||||
doc/doxygen.conf
|
||||
version.h
|
||||
libburn-1.pc
|
||||
libisofs-1.pc
|
||||
])
|
||||
AC_OUTPUT
|
||||
|
15
doc/comments
15
doc/comments
@ -13,10 +13,10 @@ together strive to be a usable foundation for application development.
|
||||
These are libraries, language bindings, and middleware binaries which emulate
|
||||
classical (and valuable) Linux tools.
|
||||
|
||||
Our scope is currently Linux 2.4 and 2.6 and we will have a hard time to widen
|
||||
this for now, because of our history. The project could need advise from or
|
||||
membership of skilled kernel people and people who know how to talk CD/DVD
|
||||
drives into doing things.
|
||||
Our scope is currently Linux 2.4 and 2.6 only. For ports to other systems
|
||||
we would need : login on a development machine resp. a live OS on CD or DVD,
|
||||
advise from a system person about the equivalent of Linux sg or FreeBSD CAM,
|
||||
volunteers for testing of realistic use cases.
|
||||
|
||||
We do have a workable code base for burning data CDs, though. The burn API is
|
||||
quite comprehensively documented and can be used to build a presentable
|
||||
@ -91,9 +91,8 @@ stay upward compatible for a good while.
|
||||
@subsection libburner-help Libburner --help
|
||||
<pre>
|
||||
Usage: test/libburner
|
||||
[--drive <address>|<driveno>|"-"]
|
||||
[--blank_fast|--blank_full] [--audio]
|
||||
[--try_to_simulate] [--stdin_size <bytes>]
|
||||
[--drive <address>|<driveno>|"-"] [--audio]
|
||||
[--blank_fast|--blank_full] [--try_to_simulate]
|
||||
[<one or more imagefiles>|"-"]
|
||||
Examples
|
||||
A bus scan (needs rw-permissions to see a drive):
|
||||
@ -110,7 +109,7 @@ Burn two audio tracks
|
||||
test/libburner --drive /dev/hdc --audio track1.cd track2.cd
|
||||
Burn a compressed afio archive on-the-fly, pad up to 700 MB:
|
||||
( cd my_directory ; find . -print | afio -oZ - ) | \
|
||||
test/libburner --drive /dev/hdc --stdin_size 734003200 -
|
||||
test/libburner --drive /dev/hdc -
|
||||
To be read from *not mounted* CD via: afio -tvZ /dev/hdc
|
||||
Program tar would need a clean EOF which our padded CD cannot deliver.
|
||||
</pre>
|
||||
|
@ -56,8 +56,8 @@ WARN_LOGFILE =
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to the input files
|
||||
#---------------------------------------------------------------------------
|
||||
INPUT = libburn libisofs doc test
|
||||
FILE_PATTERNS = libburn.h libisofs.h comments libburner.c
|
||||
INPUT = libburn doc test
|
||||
FILE_PATTERNS = libburn.h comments libburner.c
|
||||
RECURSIVE = NO
|
||||
EXCLUDE =
|
||||
EXCLUDE_SYMLINKS = NO
|
||||
|
@ -23,31 +23,27 @@ typedef void (*sighandler_t)(int);
|
||||
|
||||
#include "cleanup.h"
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
|
||||
#ifndef Cleanup_has_no_libburn_os_H
|
||||
|
||||
|
||||
#include "../libburn/os.h"
|
||||
|
||||
/* see os.h for name of particular os-*.h where this is defined */
|
||||
static int signal_list[]= { BURN_OS_SIGNAL_MACRO_LIST , -1};
|
||||
static char *signal_name_list[]= { BURN_OS_SIGNAL_NAME_LIST , "@"};
|
||||
static int signal_list_count= BURN_OS_SIGNAL_COUNT;
|
||||
static int non_signal_list[]= { BURN_OS_NON_SIGNAL_MACRO_LIST, -1};
|
||||
static int non_signal_list_count= BURN_OS_NON_SIGNAL_COUNT;
|
||||
|
||||
|
||||
#else /* ! Cleanup_has_no_libburn_os_H */
|
||||
|
||||
|
||||
/* Outdated. Linux only. For backward compatibility with pre-libburn-0.2.3 */
|
||||
|
||||
/* Signals to be caught */
|
||||
static int signal_list[]= {
|
||||
SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT,
|
||||
SIGFPE, SIGSEGV, SIGPIPE, SIGALRM, SIGTERM,
|
||||
SIGUSR1, SIGUSR2, SIGXCPU, SIGTSTP, SIGTTIN,
|
||||
SIGTTOU,
|
||||
SIGBUS, SIGPROF, SIGSYS, SIGTRAP,
|
||||
SIGVTALRM, SIGXCPU, SIGXFSZ, -1
|
||||
};
|
||||
static char *signal_name_list[]= {
|
||||
"SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGABRT",
|
||||
"SIGFPE", "SIGSEGV", "SIGPIPE", "SIGALRM", "SIGTERM",
|
||||
"SIGUSR1", "SIGUSR2", "SIGXCPU", "SIGTSTP", "SIGTTIN",
|
||||
"SIGTTOU",
|
||||
"SIGBUS", "SIGPROF", "SIGSYS", "SIGTRAP",
|
||||
"SIGVTALRM", "SIGXCPU", "SIGXFSZ", "@"
|
||||
};
|
||||
static int signal_list_count= 23;
|
||||
|
||||
#else /* __FreeBSD__ */
|
||||
|
||||
/* Signals to be caught */
|
||||
static int signal_list[]= {
|
||||
static int signal_list[]= {
|
||||
SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT,
|
||||
SIGFPE, SIGSEGV, SIGPIPE, SIGALRM, SIGTERM,
|
||||
SIGUSR1, SIGUSR2, SIGXCPU, SIGTSTP, SIGTTIN,
|
||||
@ -55,7 +51,7 @@ static int signal_list[]= {
|
||||
SIGBUS, SIGPOLL, SIGPROF, SIGSYS, SIGTRAP,
|
||||
SIGVTALRM, SIGXCPU, SIGXFSZ, -1
|
||||
};
|
||||
static char *signal_name_list[]= {
|
||||
static char *signal_name_list[]= {
|
||||
"SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGABRT",
|
||||
"SIGFPE", "SIGSEGV", "SIGPIPE", "SIGALRM", "SIGTERM",
|
||||
"SIGUSR1", "SIGUSR2", "SIGXCPU", "SIGTSTP", "SIGTTIN",
|
||||
@ -65,15 +61,17 @@ static char *signal_name_list[]= {
|
||||
};
|
||||
static int signal_list_count= 24;
|
||||
|
||||
#endif /* ! __FreeBSD__ */
|
||||
|
||||
/* Signals not to be caught */
|
||||
static int non_signal_list[]= {
|
||||
SIGKILL, SIGCHLD, SIGSTOP, SIGURG, -1
|
||||
};
|
||||
};
|
||||
static int non_signal_list_count= 4;
|
||||
|
||||
|
||||
#endif /* Cleanup_has_no_libburn_os_H */
|
||||
|
||||
|
||||
|
||||
/* run time dynamic part */
|
||||
static char cleanup_msg[4096]= {""};
|
||||
static int cleanup_exiting= 0;
|
||||
|
@ -52,7 +52,7 @@ void burn_drive_free(struct burn_drive *d)
|
||||
return;
|
||||
/* ts A60822 : close open fds before forgetting them */
|
||||
if (burn_drive_is_open(d))
|
||||
sg_close_drive(d);
|
||||
d->release(d);
|
||||
free((void *) d->idata);
|
||||
free((void *) d->mdata);
|
||||
if(d->toc_entry != NULL)
|
||||
@ -160,6 +160,7 @@ int burn_drive_grab(struct burn_drive *d, int le)
|
||||
d->load(d);
|
||||
|
||||
d->lock(d);
|
||||
d->start_unit(d);
|
||||
|
||||
/* ts A61020 : this was BURN_DISC_BLANK as pure guess */
|
||||
d->status = BURN_DISC_UNREADY;
|
||||
@ -578,8 +579,10 @@ int burn_drive_scan_sync(struct burn_drive_info *drives[],
|
||||
#endif /* 0 */
|
||||
|
||||
/* refresh the lib's drives */
|
||||
sg_enumerate();
|
||||
ata_enumerate();
|
||||
|
||||
/* ts A61115 : formerly sg_enumerate(); ata_enumerate(); */
|
||||
scsi_enumerate_drives();
|
||||
|
||||
count = burn_drive_count();
|
||||
if (count)
|
||||
*drives =
|
||||
@ -1176,6 +1179,16 @@ int burn_disc_pretend_blank(struct burn_drive *d)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ts A61106 API function */
|
||||
int burn_disc_pretend_full(struct burn_drive *d)
|
||||
{
|
||||
if (d->status != BURN_DISC_UNREADY &&
|
||||
d->status != BURN_DISC_UNSUITABLE)
|
||||
return 0;
|
||||
d->status = BURN_DISC_FULL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ts A61021: new API function */
|
||||
int burn_disc_read_atip(struct burn_drive *d)
|
||||
{
|
||||
@ -1192,3 +1205,31 @@ int burn_disc_read_atip(struct burn_drive *d)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ts A61110 : new API function */
|
||||
int burn_disc_track_lba_nwa(struct burn_drive *d, struct burn_write_opts *o,
|
||||
int trackno, int *lba, int *nwa)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (burn_drive_is_released(d)) {
|
||||
libdax_msgs_submit(libdax_messenger,
|
||||
d->global_index, 0x0002011b,
|
||||
LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH,
|
||||
"Attempt to read track info from ungrabbed drive",
|
||||
0, 0);
|
||||
return -1;
|
||||
}
|
||||
if (d->busy != BURN_DRIVE_IDLE) {
|
||||
libdax_msgs_submit(libdax_messenger,
|
||||
d->global_index, 0x0002011c,
|
||||
LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH,
|
||||
"Attempt to read track info from busy drive",
|
||||
0, 0);
|
||||
return -1;
|
||||
}
|
||||
if (o!=NULL)
|
||||
d->send_write_parameters(d, o);
|
||||
ret = d->get_nwa(d, trackno, lba, nwa);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
expressing a file or stream size.
|
||||
|
||||
XXX we should enforce 64-bitness for off_t
|
||||
ts A61101 : this is usually done by the build system (if it is not broken)
|
||||
*/
|
||||
#include <sys/types.h>
|
||||
|
||||
@ -40,6 +41,10 @@ struct burn_session;
|
||||
/** References a single track on a disc */
|
||||
struct burn_track;
|
||||
|
||||
/* ts A61111 */
|
||||
/** References a set of write parameters */
|
||||
struct burn_write_opts;
|
||||
|
||||
/** Session format for normal audio or data discs */
|
||||
#define BURN_CDROM 0
|
||||
/** Session format for obsolete CD-I discs */
|
||||
@ -216,7 +221,16 @@ enum burn_drive_status
|
||||
/** The drive is erasing a disc */
|
||||
BURN_DRIVE_ERASING,
|
||||
/** The drive is being grabbed */
|
||||
BURN_DRIVE_GRABBING
|
||||
BURN_DRIVE_GRABBING,
|
||||
|
||||
/* ts A61102 */
|
||||
/** The drive gets written zeroes before the track payload data */
|
||||
BURN_DRIVE_WRITING_PREGAP,
|
||||
/** The drive is told to close a track (TAO only) */
|
||||
BURN_DRIVE_CLOSING_TRACK,
|
||||
/** The drive is told to close a session (TAO only) */
|
||||
BURN_DRIVE_CLOSING_SESSION
|
||||
|
||||
};
|
||||
|
||||
/** Information about a track on a disc - this is from the q sub channel of the
|
||||
@ -374,9 +388,11 @@ struct burn_progress {
|
||||
int index;
|
||||
/** The starting logical block address */
|
||||
int start_sector;
|
||||
/** The number of sector */
|
||||
/** On write: The number of sectors.
|
||||
On blank: 0x10000 as upper limit for relative progress steps */
|
||||
int sectors;
|
||||
/** The current sector being processed */
|
||||
/** On write: The current sector being processed.
|
||||
On blank: Relative progress steps 0 to 0x10000 */
|
||||
int sector;
|
||||
|
||||
/* ts A61023 */
|
||||
@ -384,6 +400,14 @@ struct burn_progress {
|
||||
unsigned buffer_capacity;
|
||||
/** The free space in the drive buffer (might be slightly outdated) */
|
||||
unsigned buffer_available;
|
||||
|
||||
/* ts A61119 */
|
||||
/** The number of bytes sent to the drive buffer */
|
||||
off_t buffered_bytes;
|
||||
/** The minimum number of buffered bytes. (Caution: Before surely
|
||||
one buffer size of bytes was processed, this value is 0xffffffff.)
|
||||
*/
|
||||
unsigned buffer_min_fill;
|
||||
};
|
||||
|
||||
/** Initialize the library.
|
||||
@ -629,7 +653,7 @@ void burn_drive_release(struct burn_drive *drive, int eject);
|
||||
|
||||
|
||||
/** Returns what kind of disc a drive is holding. This function may need to be
|
||||
called more than once to get a proper status from it. See burn_status
|
||||
called more than once to get a proper status from it. See burn_disc_status
|
||||
for details.
|
||||
@param drive The drive to query for a disc.
|
||||
@return The status of the drive, or what kind of disc is in it.
|
||||
@ -648,6 +672,15 @@ enum burn_disc_status burn_disc_get_status(struct burn_drive *drive);
|
||||
int burn_disc_pretend_blank(struct burn_drive *drive);
|
||||
|
||||
|
||||
/* ts A61106 */
|
||||
/** WARNING: This overrides the safety measures against unsuitable media.
|
||||
Sets the drive status to BURN_DISC_FULL if it is BURN_DISC_UNREADY
|
||||
or BURN_DISC_UNSUITABLE. Thus marking media as blankable which actually
|
||||
failed to declare themselves either blank or (partially) filled.
|
||||
*/
|
||||
int burn_disc_pretend_full(struct burn_drive *drive);
|
||||
|
||||
|
||||
/* ts A61021 */
|
||||
/** Reads ATIP information from inserted media. To be obtained via
|
||||
burn_drive_get_write_speed(), burn_drive_get_min_write_speed(),
|
||||
@ -672,6 +705,21 @@ int burn_disc_read_atip(struct burn_drive *drive);
|
||||
int burn_drive_get_start_end_lba(struct burn_drive *drive,
|
||||
int *start_lba, int *end_lba, int flag);
|
||||
|
||||
/* ts A61110 */
|
||||
/** Read start lba and Next Writeable Address of a track from media.
|
||||
Usually a track lba is obtained from the result of burn_track_get_entry().
|
||||
This call retrieves an updated lba, eventual nwa, and can address the
|
||||
invisible track to come.
|
||||
The drive must be grabbed for this call. One may not issue this call
|
||||
during ongoing burn_disc_write() or burn_disc_erase().
|
||||
@param d The drive to query.
|
||||
@param o If not NULL: write parameters to be set on drive before query
|
||||
@param trackno 0=next track to come, >0 number of existing track
|
||||
@return 1=nwa is valid , 0=nwa is not valid , -1=error
|
||||
*/
|
||||
int burn_disc_track_lba_nwa(struct burn_drive *d, struct burn_write_opts *o,
|
||||
int trackno, int *lba, int *nwa);
|
||||
|
||||
|
||||
/** Tells whether a disc can be erased or not
|
||||
@return Non-zero means erasable
|
||||
@ -721,6 +769,7 @@ void burn_read_opts_free(struct burn_read_opts *opts);
|
||||
*/
|
||||
void burn_disc_erase(struct burn_drive *drive, int fast);
|
||||
|
||||
/* ts A61109 : this is defunct */
|
||||
/** Read a disc from the drive and write it to an fd pair. The drive must be
|
||||
grabbed successfully BEFORE calling this function. Always ensure that the
|
||||
drive reports a status of BURN_DISC_FULL before calling this function.
|
||||
@ -729,10 +778,9 @@ void burn_disc_erase(struct burn_drive *drive, int fast);
|
||||
*/
|
||||
void burn_disc_read(struct burn_drive *drive, const struct burn_read_opts *o);
|
||||
|
||||
/** Write a disc in the drive. The drive must be grabbed successfully BEFORE
|
||||
/** Write a disc in the drive. The drive must be grabbed successfully before
|
||||
calling this function. Always ensure that the drive reports a status of
|
||||
BURN_DISC_BLANK or BURN_STATUS_FULL (to append a new session to the
|
||||
disc) before calling this function.
|
||||
BURN_DISC_BLANK before calling this function.
|
||||
@param o The options for the writing operation.
|
||||
@param disc The struct burn_disc * that described the disc to be created
|
||||
*/
|
||||
@ -917,10 +965,19 @@ struct burn_source *burn_file_source_new(const char *path,
|
||||
*/
|
||||
struct burn_source *burn_fd_source_new(int datafd, int subfd, off_t size);
|
||||
|
||||
/** Tells how long a track will be on disc */
|
||||
/** Tells how long a track will be on disc
|
||||
>>> NOTE: Not reliable with tracks of undefined length
|
||||
*/
|
||||
int burn_track_get_sectors(struct burn_track *);
|
||||
|
||||
|
||||
/* ts A61101 */
|
||||
/** Tells how many source bytes have been read and how many data bytes have
|
||||
been written by the track during burn */
|
||||
int burn_track_get_counters(struct burn_track *t,
|
||||
off_t *read_bytes, off_t *written_bytes);
|
||||
|
||||
|
||||
/** Sets drive read and write speed
|
||||
@param d The drive to set speed for
|
||||
@param read Read speed in k/s (0 is max)
|
||||
@ -985,6 +1042,15 @@ void burn_write_opts_set_has_mediacatalog(struct burn_write_opts *opts, int has_
|
||||
|
||||
void burn_write_opts_set_mediacatalog(struct burn_write_opts *opts, unsigned char mediacatalog[13]);
|
||||
|
||||
|
||||
/* ts A61106 */
|
||||
/* Sets the multi flag which eventually marks the emerging session as not being
|
||||
the last one and thus creating a BURN_DISC_APPENDABLE media.
|
||||
@param multi 1=media will be appendable, 0=media will be closed (default)
|
||||
*/
|
||||
void burn_write_opts_set_multi(struct burn_write_opts *opts, int multi);
|
||||
|
||||
|
||||
/** Sets whether to read in raw mode or not
|
||||
@param opts The read opts to change
|
||||
@param raw_mode If non-zero, reading will be done in raw mode, so that everything in the data tracks on the
|
||||
|
@ -303,7 +303,7 @@ Range "scdbackup" : 0x00020000 to 0x0002ffff
|
||||
0x00020006 (FATAL,HIGH) = Too many scsi siblings
|
||||
0x00020007 (NOTE,HIGH) = Closed O_EXCL scsi siblings
|
||||
|
||||
From the hunt on Assert:
|
||||
General library operations:
|
||||
|
||||
0x00020101 (WARNING,HIGH) = Cannot find given worker item
|
||||
0x00020102 (SORRY,HIGH) = A drive operation is still going on
|
||||
@ -320,7 +320,7 @@ Range "scdbackup" : 0x00020000 to 0x0002ffff
|
||||
0x0002010c (FATAL,HIGH) = Failed to transfer command to drive
|
||||
0x0002010d (DEBUG,HIGH) = Could not inquire TOC
|
||||
0x0002010e (FATAL,HIGH) = Attempt to read ATIP from ungrabbed drive
|
||||
0x0002010f
|
||||
0x0002010f (DEBUG,HIGH) = SCSI error condition on command
|
||||
0x00020110 (FATAL,HIGH) = Persistent drive address too long
|
||||
0x00020111 (FATAL,HIGH) = Could not allocate new auxiliary object
|
||||
0x00020112 (SORRY,HIGH) = Bad combination of write_type and block_type
|
||||
@ -329,6 +329,12 @@ Range "scdbackup" : 0x00020000 to 0x0002ffff
|
||||
0x00020115 (SORRY,HIGH) = Attempt to set track mode to unusable value
|
||||
0x00020116 (FATAL,HIGH) = Track mode has unusable value
|
||||
0x00020117 (FATAL,HIGH) = toc_entry of drive is already in use
|
||||
0x00020118 (DEBUG,HIGH) = Closing track
|
||||
0x00020119 (DEBUG,HIGH) = Closing session
|
||||
0x0002011a (NOTE,HIGH) = Padding up track to minimum size
|
||||
0x0002011b (FATAL,HIGH) = Attempt to read track info from ungrabbed drive
|
||||
0x0002011c (FATAL,HIGH) = Attempt to read track info from busy drive
|
||||
0x0002011d (FATAL,HIGH) = SCSI error condition on write
|
||||
|
||||
libdax_audioxtr:
|
||||
0x00020200 (SORRY,HIGH) = Cannot open audio source file
|
||||
|
@ -20,6 +20,15 @@
|
||||
#include "structure.h"
|
||||
#include "options.h"
|
||||
|
||||
|
||||
#ifdef Libburn_log_in_and_out_streaM
|
||||
/* <<< ts A61031 */
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#endif /* Libburn_log_in_and_out_streaM */
|
||||
|
||||
|
||||
/* ts A61005 */
|
||||
#include "libdax_msgs.h"
|
||||
extern struct libdax_msgs *libdax_messenger;
|
||||
@ -90,7 +99,9 @@ void mmc_send_cue_sheet(struct burn_drive *d, struct cue_sheet *s)
|
||||
d->issue_command(d, &c);
|
||||
}
|
||||
|
||||
int mmc_get_nwa(struct burn_drive *d)
|
||||
/* ts A61110 : added parameters trackno, lba, nwa. Redefined return value.
|
||||
@return 1=nwa is valid , 0=nwa is not valid , -1=error */
|
||||
int mmc_get_nwa(struct burn_drive *d, int trackno, int *lba, int *nwa)
|
||||
{
|
||||
struct buffer buf;
|
||||
struct command c;
|
||||
@ -101,13 +112,27 @@ int mmc_get_nwa(struct burn_drive *d)
|
||||
c.oplen = sizeof(MMC_TRACK_INFO);
|
||||
memcpy(c.opcode, MMC_TRACK_INFO, sizeof(MMC_TRACK_INFO));
|
||||
c.opcode[1] = 1;
|
||||
c.opcode[5] = 0xFF;
|
||||
if(trackno<=0)
|
||||
c.opcode[5] = 0xFF;
|
||||
else
|
||||
c.opcode[5] = trackno;
|
||||
c.page = &buf;
|
||||
c.dir = FROM_DRIVE;
|
||||
d->issue_command(d, &c);
|
||||
data = c.page->data;
|
||||
return (data[12] << 24) + (data[13] << 16)
|
||||
|
||||
*lba = (data[8] << 24) + (data[9] << 16)
|
||||
+ (data[10] << 8) + data[11];
|
||||
*nwa = (data[12] << 24) + (data[13] << 16)
|
||||
+ (data[14] << 8) + data[15];
|
||||
/* ts A61106 : MMC-1 Table 142 : NWA_V = NWA Valid Flag */
|
||||
if (!(data[7]&1)) {
|
||||
libdax_msgs_submit(libdax_messenger, -1, 0x00000002,
|
||||
LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO,
|
||||
"mmc_get_nwa: Track Info Block: NWA_V == 0", 0, 0);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ts A61009 : function is obviously unused. */
|
||||
@ -158,14 +183,13 @@ void mmc_close(struct burn_drive *d, int session, int track)
|
||||
|
||||
mmc_function_spy("mmc_close");
|
||||
|
||||
libdax_msgs_submit(libdax_messenger, -1, 0x00000002,
|
||||
LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO,
|
||||
"HOW THAT ? mmc_close() was called", 0, 0);
|
||||
|
||||
c.retry = 1;
|
||||
c.oplen = sizeof(MMC_CLOSE);
|
||||
memcpy(c.opcode, MMC_CLOSE, sizeof(MMC_CLOSE));
|
||||
c.opcode[2] = session | !!track;
|
||||
|
||||
/* ts A61030 : shifted !!session rather than or-ing plain session */
|
||||
c.opcode[2] = ((!!session)<<1) | !!track;
|
||||
|
||||
c.opcode[4] = track >> 8;
|
||||
c.opcode[5] = track & 0xFF;
|
||||
c.page = NULL;
|
||||
@ -230,6 +254,14 @@ int mmc_write(struct burn_drive *d, int start, struct buffer *buf)
|
||||
struct command c;
|
||||
int len;
|
||||
|
||||
#ifdef Libburn_log_in_and_out_streaM
|
||||
/* <<< ts A61031 */
|
||||
static int tee_fd= -1;
|
||||
if(tee_fd==-1)
|
||||
tee_fd= open("/tmp/libburn_sg_written",
|
||||
O_WRONLY|O_CREAT|O_TRUNC,S_IRUSR|S_IWUSR);
|
||||
#endif /* Libburn_log_in_and_out_streaM */
|
||||
|
||||
mmc_function_spy("mmc_write");
|
||||
pthread_mutex_lock(&d->access_lock);
|
||||
cancelled = d->cancel;
|
||||
@ -261,9 +293,35 @@ int mmc_write(struct burn_drive *d, int start, struct buffer *buf)
|
||||
burn_print(12, "%d, %d, %d, %d\n", c->opcode[6], c->opcode[7], c->opcode[8], c->opcode[9]);
|
||||
*/
|
||||
|
||||
/* write(fileno(stderr), c.page->data, c.page->bytes);*/
|
||||
#ifdef Libburn_log_in_and_out_streaM
|
||||
/* <<< ts A61031 */
|
||||
if(tee_fd!=-1) {
|
||||
write(tee_fd,c.page->data,len*2048);
|
||||
}
|
||||
#endif /* Libburn_log_in_and_out_streaM */
|
||||
|
||||
d->issue_command(d, &c);
|
||||
|
||||
/* ts A61112 : react on eventual error condition */
|
||||
if (c.error && c.sense[2]!=0) {
|
||||
|
||||
/* >>> make this scsi_notify_error() when liberated */
|
||||
if (c.sense[2]!=0) {
|
||||
char msg[80];
|
||||
sprintf(msg,
|
||||
"SCSI error condition on write : key=%X asc=%2.2Xh ascq=%2.2Xh",
|
||||
c.sense[2],c.sense[12],c.sense[13]);
|
||||
libdax_msgs_submit(libdax_messenger, d->global_index,
|
||||
0x0002011d,
|
||||
LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH,
|
||||
msg, 0, 0);
|
||||
}
|
||||
pthread_mutex_lock(&d->access_lock);
|
||||
d->cancel = 1;
|
||||
pthread_mutex_unlock(&d->access_lock);
|
||||
return BE_CANCELLED;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -291,6 +349,7 @@ void mmc_read_toc(struct burn_drive *d)
|
||||
if (c.error) {
|
||||
|
||||
/* ts A61020 : this snaps on non-blank DVD media */
|
||||
/* ts A61106 : also snaps on CD with unclosed track/session */
|
||||
/* Very unsure wether this old measure is ok.
|
||||
Obviously higher levels do not care about this.
|
||||
DVD+RW burns go on after passing through here.
|
||||
@ -634,8 +693,12 @@ void mmc_read_sectors(struct burn_drive *d,
|
||||
len >>= 8;
|
||||
c.opcode[6] = len & 0xFF;
|
||||
req = 0xF8;
|
||||
|
||||
/* ts A61106 : LG GSA-4082B dislikes this. key=5h asc=24h ascq=00h
|
||||
|
||||
if (d->busy == BURN_DRIVE_GRABBING || o->report_recovered_errors)
|
||||
req |= 2;
|
||||
*/
|
||||
|
||||
c.opcode[10] = 0;
|
||||
/* always read the subcode, throw it away later, since we don't know
|
||||
@ -721,6 +784,13 @@ void mmc_set_speed(struct burn_drive *d, int r, int w)
|
||||
{
|
||||
struct command c;
|
||||
|
||||
/* ts A61112 : MMC standards prescribe FFFFh as max speed.
|
||||
But libburn.h prescribes 0. */
|
||||
if (r<=0 || r>0xffff)
|
||||
r = 0xffff;
|
||||
if (w<=0 || w>0xffff)
|
||||
w = 0xffff;
|
||||
|
||||
mmc_function_spy("mmc_set_speed");
|
||||
memcpy(c.opcode, MMC_SET_SPEED, sizeof(MMC_SET_SPEED));
|
||||
c.retry = 1;
|
||||
@ -804,6 +874,14 @@ int mmc_read_buffer_capacity(struct burn_drive *d)
|
||||
(data[4]<<24)|(data[5]<<16)|(data[6]<<8)|data[7];
|
||||
d->progress.buffer_available =
|
||||
(data[8]<<24)|(data[9]<<16)|(data[10]<<8)|data[11];
|
||||
if (d->progress.buffered_bytes >= d->progress.buffer_capacity){
|
||||
double fill;
|
||||
|
||||
fill = d->progress.buffer_capacity
|
||||
- d->progress.buffer_available;
|
||||
if (fill < d->progress.buffer_min_fill && fill>=0)
|
||||
d->progress.buffer_min_fill = fill;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -824,6 +902,7 @@ int mmc_setup_drive(struct burn_drive *d)
|
||||
d->get_nwa = mmc_get_nwa;
|
||||
d->close_disc = mmc_close_disc;
|
||||
d->close_session = mmc_close_session;
|
||||
d->close_track_session = mmc_close;
|
||||
d->read_buffer_capacity = mmc_read_buffer_capacity;
|
||||
|
||||
/* ts A61020 */
|
||||
|
@ -37,7 +37,11 @@ void mmc_set_speed(struct burn_drive *, int, int);
|
||||
void mmc_read_lead_in(struct burn_drive *, struct buffer *);
|
||||
void mmc_perform_opc(struct burn_drive *);
|
||||
void mmc_get_configuration(struct burn_drive *);
|
||||
int mmc_get_nwa(struct burn_drive *);
|
||||
|
||||
/* ts A61110 : added parameters trackno, lba, nwa. Redefined return value.
|
||||
@return 1=nwa is valid , 0=nwa is not valid , -1=error */
|
||||
int mmc_get_nwa(struct burn_drive *d, int trackno, int *lba, int *nwa);
|
||||
|
||||
void mmc_send_cue_sheet(struct burn_drive *, struct cue_sheet *);
|
||||
|
||||
/* ts A61023 : get size and free space of drive buffer */
|
||||
|
@ -152,6 +152,14 @@ void burn_write_opts_set_mediacatalog(struct burn_write_opts *opts,
|
||||
memcpy(opts->mediacatalog, &mediacatalog, 13);
|
||||
}
|
||||
|
||||
/* ts A61106 */
|
||||
void burn_write_opts_set_multi(struct burn_write_opts *opts, int multi)
|
||||
{
|
||||
opts->multi = !!multi;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void burn_read_opts_set_raw(struct burn_read_opts *opts, int raw)
|
||||
{
|
||||
opts->raw = raw;
|
||||
@ -198,3 +206,4 @@ void burn_read_opts_set_hardware_error_retries(struct burn_read_opts *opts,
|
||||
{
|
||||
opts->hardware_error_retries = hardware_error_retries;
|
||||
}
|
||||
|
||||
|
72
libburn/os-freebsd.h
Normal file
72
libburn/os-freebsd.h
Normal file
@ -0,0 +1,72 @@
|
||||
|
||||
/* os-freebsd.h
|
||||
Operating system specific libburn definitions and declarations. Included
|
||||
by os.h in case of compilation for
|
||||
FreeBSD with CAM
|
||||
|
||||
Copyright (C) 2006 Thomas Schmitt <scdbackup@gmx.net>, provided under GPL
|
||||
*/
|
||||
|
||||
#ifndef BURN_OS_H_INCLUDED
|
||||
#define BURN_OS_H_INCLUDED 1
|
||||
|
||||
|
||||
/** List of all signals which shall be caught by signal handlers and trigger
|
||||
a graceful abort of libburn. (See man 7 signal.)
|
||||
*/
|
||||
/* Once as system defined macros */
|
||||
#define BURN_OS_SIGNAL_MACRO_LIST \
|
||||
SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, \
|
||||
SIGFPE, SIGSEGV, SIGPIPE, SIGALRM, SIGTERM, \
|
||||
SIGUSR1, SIGUSR2, SIGXCPU, SIGTSTP, SIGTTIN, \
|
||||
SIGTTOU, \
|
||||
SIGBUS, SIGPROF, SIGSYS, SIGTRAP, \
|
||||
SIGVTALRM, SIGXCPU, SIGXFSZ
|
||||
|
||||
/* Once as text 1:1 list of strings for messages and interpreters */
|
||||
#define BURN_OS_SIGNAL_NAME_LIST \
|
||||
"SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGABRT", \
|
||||
"SIGFPE", "SIGSEGV", "SIGPIPE", "SIGALRM", "SIGTERM", \
|
||||
"SIGUSR1", "SIGUSR2", "SIGXCPU", "SIGTSTP", "SIGTTIN", \
|
||||
"SIGTTOU", \
|
||||
"SIGBUS", "SIGPROF", "SIGSYS", "SIGTRAP", \
|
||||
"SIGVTALRM", "SIGXCPU", "SIGXFSZ"
|
||||
|
||||
/* The number of above list items */
|
||||
#define BURN_OS_SIGNAL_COUNT 23
|
||||
|
||||
/** To list all signals which shall surely not be caught */
|
||||
#define BURN_OS_NON_SIGNAL_MACRO_LIST \
|
||||
SIGKILL, SIGCHLD, SIGSTOP, SIGURG
|
||||
|
||||
/* The number of above list items */
|
||||
#define BURN_OS_NON_SIGNAL_COUNT 4
|
||||
|
||||
|
||||
/* The maximum size for a (SCSI) i/o transaction */
|
||||
#define BURN_OS_TRANSPORT_BUFFER_SIZE 65536/2
|
||||
|
||||
|
||||
/** To hold all state information of BSD device enumeration
|
||||
which are now local in sg_enumerate() . So that sg_give_next_adr()
|
||||
can work in BSD and sg_enumerate() can use it.
|
||||
*/
|
||||
#define BURN_OS_DEFINE_DRIVE_ENUMERATOR_T \
|
||||
struct burn_drive_enumeration_state { \
|
||||
union ccb ccb; \
|
||||
int bufsize, fd; \
|
||||
unsigned int i; \
|
||||
int skip_device; \
|
||||
}; \
|
||||
typedef struct burn_drive_enumeration_state burn_drive_enumerator_t;
|
||||
|
||||
|
||||
/* The list of operating system dependent elements in struct burn_drive.
|
||||
To be initialized and used within sg-*.c .
|
||||
*/
|
||||
#define BURN_OS_TRANSPORT_DRIVE_ELEMENTS \
|
||||
struct cam_device* cam;
|
||||
|
||||
|
||||
#endif /* ! BURN_OS_H_INCLUDED */
|
||||
|
63
libburn/os-linux.h
Normal file
63
libburn/os-linux.h
Normal file
@ -0,0 +1,63 @@
|
||||
|
||||
/* os-linux.h
|
||||
Operating system specific libburn definitions and declarations. Included
|
||||
by os.h in case of compilation for
|
||||
Linux kernels 2.4 and 2.6 with Linux SCSI Generic (sg)
|
||||
|
||||
Copyright (C) 2006 Thomas Schmitt <scdbackup@gmx.net>, provided under GPL
|
||||
*/
|
||||
|
||||
|
||||
/** List of all signals which shall be caught by signal handlers and trigger
|
||||
a graceful abort of libburn. (See man 7 signal.)
|
||||
*/
|
||||
/* Once as system defined macros */
|
||||
#define BURN_OS_SIGNAL_MACRO_LIST \
|
||||
SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, \
|
||||
SIGFPE, SIGSEGV, SIGPIPE, SIGALRM, SIGTERM, \
|
||||
SIGUSR1, SIGUSR2, SIGXCPU, SIGTSTP, SIGTTIN, \
|
||||
SIGTTOU, \
|
||||
SIGBUS, SIGPOLL, SIGPROF, SIGSYS, SIGTRAP, \
|
||||
SIGVTALRM, SIGXCPU, SIGXFSZ
|
||||
|
||||
/* Once as text 1:1 list of strings for messages and interpreters */
|
||||
#define BURN_OS_SIGNAL_NAME_LIST \
|
||||
"SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGABRT", \
|
||||
"SIGFPE", "SIGSEGV", "SIGPIPE", "SIGALRM", "SIGTERM", \
|
||||
"SIGUSR1", "SIGUSR2", "SIGXCPU", "SIGTSTP", "SIGTTIN", \
|
||||
"SIGTTOU", \
|
||||
"SIGBUS", "SIGPOLL", "SIGPROF", "SIGSYS", "SIGTRAP", \
|
||||
"SIGVTALRM", "SIGXCPU", "SIGXFSZ"
|
||||
|
||||
/* The number of above list items */
|
||||
#define BURN_OS_SIGNAL_COUNT 24
|
||||
|
||||
/** To list all signals which shall surely not be caught */
|
||||
#define BURN_OS_NON_SIGNAL_MACRO_LIST \
|
||||
SIGKILL, SIGCHLD, SIGSTOP, SIGURG
|
||||
|
||||
/* The number of above list items */
|
||||
#define BURN_OS_NON_SIGNAL_COUNT 4
|
||||
|
||||
|
||||
/* The maximum size for a (SCSI) i/o transaction */
|
||||
#define BURN_OS_TRANSPORT_BUFFER_SIZE 65536
|
||||
|
||||
|
||||
/* To hold the index number of the most recently delivered address from
|
||||
device enumeration.
|
||||
*/
|
||||
#define BURN_OS_DEFINE_DRIVE_ENUMERATOR_T \
|
||||
typedef int burn_drive_enumerator_t;
|
||||
|
||||
|
||||
/* The list of operating system dependent elements in struct burn_drive.
|
||||
Usually they are initialized in sg-*.c:enumerate_common().
|
||||
*/
|
||||
#define BURN_OS_TRANSPORT_DRIVE_ELEMENTS \
|
||||
int fd; \
|
||||
\
|
||||
/* ts A60926 : trying to lock against growisofs /dev/srN, /dev/scdN */ \
|
||||
int sibling_count; \
|
||||
int sibling_fds[LIBBURN_SG_MAX_SIBLINGS];
|
||||
|
34
libburn/os.h
Normal file
34
libburn/os.h
Normal file
@ -0,0 +1,34 @@
|
||||
|
||||
/* os.h
|
||||
Operating system specific libburn definitions and declarations.
|
||||
The macros defined here are used by libburn modules in order to
|
||||
avoid own system dependent case distinctions.
|
||||
Copyright (C) 2006 Thomas Schmitt <scdbackup@gmx.net>, provided under GPL
|
||||
*/
|
||||
|
||||
#ifndef BURN_OS_H_INCLUDED
|
||||
#define BURN_OS_H_INCLUDED 1
|
||||
|
||||
/*
|
||||
Operating system case distinction
|
||||
*/
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
|
||||
|
||||
/* ----------------------------- FreeBSD with CAM -------------------------- */
|
||||
#include "os-freebsd.h"
|
||||
|
||||
|
||||
#else /* operating system case distinction */
|
||||
|
||||
|
||||
/* --------- Linux kernels 2.4 and 2.6 with Linux SCSI Generic (sg) -------- */
|
||||
#include "os-linux.h"
|
||||
|
||||
|
||||
#endif /* End of operating system case distinction */
|
||||
|
||||
|
||||
#endif /* ! BURN_OS_H_INCLUDED */
|
||||
|
@ -11,6 +11,7 @@
|
||||
/* spc command set */
|
||||
static char SBC_LOAD[] = { 0x1b, 0, 0, 0, 3, 0 };
|
||||
static char SBC_UNLOAD[] = { 0x1b, 0, 0, 0, 2, 0 };
|
||||
static char SBC_START_UNIT[] = { 0x1b, 0, 0, 0, 1, 0 };
|
||||
|
||||
void sbc_load(struct burn_drive *d)
|
||||
{
|
||||
@ -37,12 +38,28 @@ void sbc_eject(struct burn_drive *d)
|
||||
d->issue_command(d, &c);
|
||||
}
|
||||
|
||||
/* ts A61118 : is it necessary to tell the drive to get ready for use ? */
|
||||
int sbc_start_unit(struct burn_drive *d)
|
||||
{
|
||||
struct command c;
|
||||
|
||||
memcpy(c.opcode, SBC_START_UNIT, sizeof(SBC_START_UNIT));
|
||||
c.retry = 1;
|
||||
c.oplen = sizeof(SBC_START_UNIT);
|
||||
c.dir = NO_TRANSFER;
|
||||
c.page = NULL;
|
||||
d->issue_command(d, &c);
|
||||
return (c.error==0);
|
||||
}
|
||||
|
||||
|
||||
/* ts A61021 : the sbc specific part of sg.c:enumerate_common()
|
||||
*/
|
||||
int sbc_setup_drive(struct burn_drive *d)
|
||||
{
|
||||
d->eject = sbc_eject;
|
||||
d->load = sbc_load;
|
||||
d->start_unit = sbc_start_unit;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,9 @@ struct burn_drive;
|
||||
void sbc_load(struct burn_drive *);
|
||||
void sbc_eject(struct burn_drive *);
|
||||
|
||||
/* ts A61118 */
|
||||
int sbc_start_unit(struct burn_drive *);
|
||||
|
||||
/* ts A61021 : the sbc specific part of sg.c:enumerate_common()
|
||||
*/
|
||||
int sbc_setup_drive(struct burn_drive *d);
|
||||
|
120
libburn/sector.c
120
libburn/sector.c
@ -19,6 +19,15 @@
|
||||
#include "toc.h"
|
||||
#include "write.h"
|
||||
|
||||
|
||||
#ifdef Libburn_log_in_and_out_streaM
|
||||
/* <<< ts A61031 */
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#endif /* Libburn_log_in_and_out_streaM */
|
||||
|
||||
|
||||
/*static unsigned char isrc[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";*/
|
||||
|
||||
#define sector_common(X) d->alba++; d->rlba X;
|
||||
@ -44,7 +53,8 @@ static void uncook_subs(unsigned char *dest, unsigned char *source)
|
||||
int sector_get_outmode(enum burn_write_types write_type,
|
||||
enum burn_block_types block_type)
|
||||
{
|
||||
if (write_type == BURN_WRITE_SAO)
|
||||
/* ts A61103 : extended SAO condition to TAO */
|
||||
if (write_type == BURN_WRITE_SAO || write_type == BURN_WRITE_TAO)
|
||||
return 0;
|
||||
else
|
||||
switch (block_type) {
|
||||
@ -81,6 +91,15 @@ static void get_bytes(struct burn_track *track, int count, unsigned char *data)
|
||||
{
|
||||
int valid, shortage, curr, i, tr;
|
||||
|
||||
#ifdef Libburn_log_in_and_out_streaM
|
||||
/* <<< ts A61031 */
|
||||
static int tee_fd= -1;
|
||||
if(tee_fd==-1)
|
||||
tee_fd= open("/tmp/libburn_sg_readin",
|
||||
O_WRONLY|O_CREAT|O_TRUNC,S_IRUSR|S_IWUSR);
|
||||
#endif /* Libburn_log_in_and_out_streaM */
|
||||
|
||||
|
||||
/* no track pointer means we're just generating 0s */
|
||||
if (!track) {
|
||||
memset(data, 0, count);
|
||||
@ -107,10 +126,18 @@ static void get_bytes(struct burn_track *track, int count, unsigned char *data)
|
||||
valid = track->source->read(track->source, data + curr, count - curr);
|
||||
} else valid = 0;
|
||||
|
||||
if (valid == -1) {
|
||||
if (valid <= 0) { /* ts A61031 : extended from (valid == -1) */
|
||||
track->eos = 1;
|
||||
valid = 0;
|
||||
}
|
||||
track->sourcecount += valid;
|
||||
|
||||
#ifdef Libburn_log_in_and_out_streaM
|
||||
/* <<< ts A61031 */
|
||||
if(tee_fd!=-1 && valid>0) {
|
||||
write(tee_fd, data + curr, valid);
|
||||
}
|
||||
#endif /* Libburn_log_in_and_out_streaM */
|
||||
|
||||
curr += valid;
|
||||
shortage = count - curr;
|
||||
@ -134,6 +161,12 @@ static void get_bytes(struct burn_track *track, int count, unsigned char *data)
|
||||
if (!shortage)
|
||||
goto ex;
|
||||
|
||||
/* ts A61031 */
|
||||
if (shortage >= count)
|
||||
track->track_data_done = 1;
|
||||
if (track->open_ended)
|
||||
goto ex;
|
||||
|
||||
/* If we're still short, and there's a "next" pointer, we pull from that.
|
||||
if that depletes, we'll just fill with 0s.
|
||||
*/
|
||||
@ -152,15 +185,6 @@ ex:;
|
||||
if(shortage)
|
||||
memset(data + curr, 0, shortage); /* this is old icculus.org */
|
||||
if (track->swap_source_bytes == 1) {
|
||||
|
||||
/*
|
||||
{ static int swapping_count= 0;
|
||||
fprintf(stderr,"\rlibburn_debug: swapping #%d \r",
|
||||
swapping_count);
|
||||
swapping_count++;
|
||||
}
|
||||
*/
|
||||
|
||||
for (i = 1; i < count; i += 2) {
|
||||
tr = data[i];
|
||||
data[i] = data[i-1];
|
||||
@ -171,7 +195,9 @@ ex:;
|
||||
|
||||
/* ts A61009 : seems to hand out sector start pointer in opts->drive->buffer
|
||||
and to count hand outs as well as reserved bytes */
|
||||
static unsigned char *get_sector(struct burn_write_opts *opts, int inmode)
|
||||
/* ts A61101 : added parameter track for counting written bytes */
|
||||
static unsigned char *get_sector(struct burn_write_opts *opts,
|
||||
struct burn_track *track, int inmode)
|
||||
{
|
||||
struct burn_drive *d = opts->drive;
|
||||
struct buffer *out = d->buffer;
|
||||
@ -196,6 +222,15 @@ static unsigned char *get_sector(struct burn_write_opts *opts, int inmode)
|
||||
err = d->write(d, d->nwa, out);
|
||||
if (err == BE_CANCELLED)
|
||||
return NULL;
|
||||
|
||||
/* ts A61101 */
|
||||
if(track != NULL) {
|
||||
track->writecount += out->bytes;
|
||||
track->written_sectors += out->sectors;
|
||||
}
|
||||
/* ts A61119 */
|
||||
d->progress.buffered_bytes += out->bytes;
|
||||
|
||||
d->nwa += out->sectors;
|
||||
out->bytes = 0;
|
||||
out->sectors = 0;
|
||||
@ -208,6 +243,32 @@ static unsigned char *get_sector(struct burn_write_opts *opts, int inmode)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* ts A61031 */
|
||||
/* Revoke the counting of the most recent sector handed out by get_sector() */
|
||||
static void unget_sector(struct burn_write_opts *opts, int inmode)
|
||||
{
|
||||
struct burn_drive *d = opts->drive;
|
||||
struct buffer *out = d->buffer;
|
||||
int outmode;
|
||||
int seclen;
|
||||
|
||||
outmode = get_outmode(opts);
|
||||
if (outmode == 0)
|
||||
outmode = inmode;
|
||||
|
||||
/* ts A61009 : react on eventual failure of burn_sector_length()
|
||||
(should not happen if API tested properly).
|
||||
Ensures out->bytes >= out->sectors */
|
||||
seclen = burn_sector_length(outmode);
|
||||
if (seclen <= 0)
|
||||
return;
|
||||
seclen += burn_subcode_length(outmode);
|
||||
|
||||
out->bytes -= seclen;
|
||||
out->sectors--;
|
||||
}
|
||||
|
||||
|
||||
/* either inmode == outmode, or outmode == raw. anything else is bad news */
|
||||
/* ts A61010 : changed type to int in order to propagate said bad news */
|
||||
/** @return 1 is ok, <= 0 is failure */
|
||||
@ -289,6 +350,8 @@ static void convert_subs(struct burn_write_opts *o, int inmode,
|
||||
out[0] = ~out[0];
|
||||
out[1] = ~out[1];
|
||||
break;
|
||||
/* ts A61119 : to silence compiler warnings */
|
||||
default:;
|
||||
}
|
||||
}
|
||||
|
||||
@ -335,7 +398,7 @@ int sector_toc(struct burn_write_opts *o, int mode)
|
||||
unsigned char *data;
|
||||
unsigned char subs[96];
|
||||
|
||||
data = get_sector(o, mode);
|
||||
data = get_sector(o, NULL, mode);
|
||||
if (data == NULL)
|
||||
return 0;
|
||||
/* ts A61010 */
|
||||
@ -355,7 +418,7 @@ int sector_pregap(struct burn_write_opts *o,
|
||||
unsigned char *data;
|
||||
unsigned char subs[96];
|
||||
|
||||
data = get_sector(o, mode);
|
||||
data = get_sector(o, NULL, mode);
|
||||
if (data == NULL)
|
||||
return 0;
|
||||
/* ts A61010 */
|
||||
@ -375,7 +438,7 @@ int sector_postgap(struct burn_write_opts *o,
|
||||
unsigned char subs[96];
|
||||
unsigned char *data;
|
||||
|
||||
data = get_sector(o, mode);
|
||||
data = get_sector(o, NULL, mode);
|
||||
if (data == NULL)
|
||||
return 0;
|
||||
/* ts A61010 */
|
||||
@ -547,7 +610,7 @@ int sector_lout(struct burn_write_opts *o, unsigned char control, int mode)
|
||||
unsigned char subs[96];
|
||||
unsigned char *data;
|
||||
|
||||
data = get_sector(o, mode);
|
||||
data = get_sector(o, NULL, mode);
|
||||
if (!data)
|
||||
return 0;
|
||||
/* ts A61010 */
|
||||
@ -566,13 +629,19 @@ int sector_data(struct burn_write_opts *o, struct burn_track *t, int psub)
|
||||
unsigned char subs[96];
|
||||
unsigned char *data;
|
||||
|
||||
data = get_sector(o, t->mode);
|
||||
data = get_sector(o, t, t->mode);
|
||||
if (!data)
|
||||
return 0;
|
||||
/* ts A61010 */
|
||||
if (convert_data(o, t, t->mode, data) <= 0)
|
||||
return 0;
|
||||
|
||||
/* ts A61031 */
|
||||
if (t->open_ended && t->track_data_done) {
|
||||
unget_sector(o, t->mode);
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (!t->source->read_sub)
|
||||
subcode_user(o, subs, t->entry->point,
|
||||
t->entry->control, 1, &t->isrc, psub);
|
||||
@ -622,6 +691,11 @@ int sector_headers_is_ok(struct burn_write_opts *o, int mode)
|
||||
return 1;
|
||||
if (o->write_type == BURN_WRITE_SAO)
|
||||
return 1;
|
||||
|
||||
/* ts A61031 */
|
||||
if (o->write_type == BURN_WRITE_TAO)
|
||||
return 1;
|
||||
|
||||
if (mode & BURN_MODE1)
|
||||
return 2;
|
||||
return 0;
|
||||
@ -650,6 +724,11 @@ void sector_headers(struct burn_write_opts *o, unsigned char *out,
|
||||
return;
|
||||
if (o->write_type == BURN_WRITE_SAO)
|
||||
return;
|
||||
|
||||
/* ts A61031 */
|
||||
if (o->write_type == BURN_WRITE_TAO)
|
||||
return;
|
||||
|
||||
if (mode & BURN_MODE1)
|
||||
modebyte = 1;
|
||||
|
||||
@ -736,7 +815,14 @@ void process_q(struct burn_drive *d, unsigned char *q)
|
||||
#endif
|
||||
|
||||
/* this needs more info. subs in the data? control/adr? */
|
||||
|
||||
/* ts A61119 : One should not use inofficial compiler extensions.
|
||||
>>> Some day this function needs to be implemented. At least for now
|
||||
the result does not match the "mode" of cdrecord -toc.
|
||||
*/
|
||||
/*
|
||||
#warning sector_identify needs to be written
|
||||
*/
|
||||
int sector_identify(unsigned char *data)
|
||||
{
|
||||
scramble(data);
|
||||
|
559
libburn/sg-freebsd-port.c
Normal file
559
libburn/sg-freebsd-port.c
Normal file
@ -0,0 +1,559 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
|
||||
/*
|
||||
|
||||
This is the main operating system dependent SCSI part of libburn. It implements
|
||||
the transport level aspects of SCSI control and command i/o.
|
||||
|
||||
Present implementation: FreeBSD CAM (untested)
|
||||
|
||||
|
||||
PORTING:
|
||||
|
||||
Porting libburn typically will consist of adding a new operating system case
|
||||
to the following switcher files:
|
||||
os.h Operating system specific libburn definitions and declarations.
|
||||
sg.c Operating system dependent transport level modules.
|
||||
and of deriving the following system specific files from existing examples:
|
||||
os-*.h Included by os.h. You will need some general system knowledge
|
||||
about signals and knowledge about the storage object needs of your
|
||||
transport level module sg-*.c.
|
||||
|
||||
sg-*.c This source module. You will need special system knowledge about
|
||||
how to detect all potentially available drives, how to open them,
|
||||
eventually how to exclusively reserve them, how to perform
|
||||
SCSI transactions, how to inquire the (pseudo-)SCSI driver.
|
||||
You will not need to care about CD burning, MMC or other high-level
|
||||
SCSI aspects.
|
||||
|
||||
Said sg-*.c operations are defined by a public function interface, which has
|
||||
to be implemented in a way that provides libburn with the desired services:
|
||||
|
||||
sg_give_next_adr() iterates over the set of potentially useful drive
|
||||
address strings.
|
||||
|
||||
scsi_enumerate_drives() brings all available, not-whitelist-banned, and
|
||||
accessible drives into libburn's list of drives.
|
||||
|
||||
sg_drive_is_open() tells wether libburn has the given drive in use.
|
||||
|
||||
sg_grab() opens the drive for SCSI commands and ensures
|
||||
undisturbed access.
|
||||
|
||||
sg_release() closes a drive opened by sg_grab()
|
||||
|
||||
sg_issue_command() sends a SCSI command to the drive, receives reply,
|
||||
and evaluates wether the command succeeded or shall
|
||||
be retried or finally failed.
|
||||
|
||||
sg_obtain_scsi_adr() tries to obtain SCSI address parameters.
|
||||
|
||||
|
||||
Porting hints are marked by the text "PORTING:".
|
||||
Send feedback to libburn-hackers@pykix.org .
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/** PORTING : ------- OS dependent headers and definitions ------ */
|
||||
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/poll.h>
|
||||
#include <camlib.h>
|
||||
#include <cam/scsi/scsi_message.h>
|
||||
#include <cam/scsi/scsi_pass.h>
|
||||
|
||||
#include <err.h> /* XXX */
|
||||
|
||||
|
||||
/** PORTING : ------ libburn portable headers and definitions ----- */
|
||||
|
||||
#include "transport.h"
|
||||
#include "drive.h"
|
||||
#include "sg.h"
|
||||
#include "spc.h"
|
||||
#include "mmc.h"
|
||||
#include "sbc.h"
|
||||
#include "debug.h"
|
||||
#include "toc.h"
|
||||
#include "util.h"
|
||||
|
||||
#include "libdax_msgs.h"
|
||||
extern struct libdax_msgs *libdax_messenger;
|
||||
|
||||
|
||||
/* is in portable part of libburn */
|
||||
int burn_drive_is_banned(char *device_address);
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
/* ts A61115: Private functions. Port only if needed by public functions */
|
||||
/* (Public functions are listed below) */
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
|
||||
/* Helper function for scsi_give_next_adr() */
|
||||
static int sg_init_enumerator(burn_drive_enumerator_t *idx)
|
||||
{
|
||||
idx->skip_device = 0;
|
||||
|
||||
if ((idx->fd = open(XPT_DEVICE, O_RDWR)) == -1) {
|
||||
warn("couldn't open %s", XPT_DEVICE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
bzero(&(idx->ccb), sizeof(union ccb));
|
||||
|
||||
idx->ccb.ccb_h.path_id = CAM_XPT_PATH_ID;
|
||||
idx->ccb.ccb_h.target_id = CAM_TARGET_WILDCARD;
|
||||
idx->ccb.ccb_h.target_lun = CAM_LUN_WILDCARD;
|
||||
|
||||
idx->ccb.ccb_h.func_code = XPT_DEV_MATCH;
|
||||
idx->bufsize = sizeof(struct dev_match_result) * 100;
|
||||
idx->ccb.cdm.match_buf_len = idx->bufsize;
|
||||
idx->ccb.cdm.matches = (struct dev_match_result *)malloc(idx->bufsize);
|
||||
if (idx->ccb.cdm.matches == NULL) {
|
||||
warnx("can't malloc memory for matches");
|
||||
close(idx->fd);
|
||||
return -1;
|
||||
}
|
||||
idx->ccb.cdm.num_matches = 0;
|
||||
idx->i = idx->ccb.cdm.num_matches; /* to trigger buffer load */
|
||||
|
||||
/*
|
||||
* We fetch all nodes, since we display most of them in the default
|
||||
* case, and all in the verbose case.
|
||||
*/
|
||||
idx->ccb.cdm.num_patterns = 0;
|
||||
idx->ccb.cdm.pattern_buf_len = 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* Helper function for scsi_give_next_adr() */
|
||||
static int sg_next_enumeration_buffer(burn_drive_enumerator_t *idx)
|
||||
{
|
||||
/*
|
||||
* We do the ioctl multiple times if necessary, in case there are
|
||||
* more than 100 nodes in the EDT.
|
||||
*/
|
||||
if (ioctl(idx->fd, CAMIOCOMMAND, &(idx->ccb)) == -1) {
|
||||
warn("error sending CAMIOCOMMAND ioctl");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((idx->ccb.ccb_h.status != CAM_REQ_CMP)
|
||||
|| ((idx->ccb.cdm.status != CAM_DEV_MATCH_LAST)
|
||||
&& (idx->ccb.cdm.status != CAM_DEV_MATCH_MORE))) {
|
||||
warnx("got CAM error %#x, CDM error %d\n",
|
||||
idx->ccb.ccb_h.status, idx->ccb.cdm.status);
|
||||
return -1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int sg_close_drive(struct burn_drive * d)
|
||||
{
|
||||
if (d->cam != NULL) {
|
||||
cam_close_device(d->cam);
|
||||
d->cam = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
/* PORTING: Private functions which contain publicly needed functionality. */
|
||||
/* Their portable part must be performed. So it is probably best */
|
||||
/* to replace the non-portable part and to call these functions */
|
||||
/* in your port, too. */
|
||||
/* ----------------------------------------------------------------------- */
|
||||
|
||||
|
||||
/** Wraps a detected drive into libburn structures and hands it over to
|
||||
libburn drive list.
|
||||
*/
|
||||
static void enumerate_common(char *fname, int bus_no, int host_no,
|
||||
int channel_no, int target_no, int lun_no)
|
||||
{
|
||||
int ret;
|
||||
struct burn_drive out;
|
||||
|
||||
/* General libburn drive setup */
|
||||
burn_setup_drive(&out, fname);
|
||||
|
||||
/* This transport adapter uses SCSI-family commands and models
|
||||
(seems the adapter would know better than its boss, if ever) */
|
||||
ret = burn_scsi_setup_drive(&out, bus_no, host_no, channel_no,
|
||||
target_no, lun_no, 0);
|
||||
if (ret<=0)
|
||||
return;
|
||||
|
||||
/* PORTING: ------------------- non portable part --------------- */
|
||||
|
||||
/* Operating system adapter is CAM */
|
||||
/* Adapter specific handles and data */
|
||||
out.cam = NULL;
|
||||
|
||||
/* PORTING: ---------------- end of non portable part ------------ */
|
||||
|
||||
/* Adapter specific functions with standardized names */
|
||||
out.grab = sg_grab;
|
||||
out.release = sg_release;
|
||||
out.drive_is_open = sg_drive_is_open;
|
||||
out.issue_command = sg_issue_command;
|
||||
/* Finally register drive and inquire drive information */
|
||||
burn_drive_finish_enum(&out);
|
||||
}
|
||||
|
||||
|
||||
/* ts A61115 */
|
||||
/* ------------------------------------------------------------------------ */
|
||||
/* PORTING: Public functions. These MUST be ported. */
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
|
||||
/** Returns the next index number and the next enumerated drive address.
|
||||
The enumeration has to cover all available and accessible drives. It is
|
||||
allowed to return addresses of drives which are not available but under
|
||||
some (even exotic) circumstances could be available. It is on the other
|
||||
hand allowed, only to hand out addresses which can really be used right
|
||||
in the moment of this call. (This implementation chooses the latter.)
|
||||
@param idx An opaque handle. Make no own theories about it.
|
||||
@param adr Takes the reply
|
||||
@param adr_size Gives maximum size of reply including final 0
|
||||
@param initialize 1 = start new,
|
||||
0 = continue, use no other values for now
|
||||
-1 = finish
|
||||
@return 1 = reply is a valid address , 0 = no further address available
|
||||
-1 = severe error (e.g. adr_size too small)
|
||||
*/
|
||||
int sg_give_next_adr(burn_drive_enumerator_t *idx,
|
||||
char adr[], int adr_size, int initialize)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (initialize == 1) {
|
||||
ret = sg_init_enumerator(idx);
|
||||
if (ret<=0)
|
||||
return ret;
|
||||
} else if (initialize == -1) {
|
||||
if(idx->fd != -1)
|
||||
close(idx->fd);
|
||||
idx->fd = -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
try_item:; /* This spaghetti loop keeps the number of tabs small */
|
||||
|
||||
/* Loop content from old scsi_enumerate_drives() */
|
||||
|
||||
while (idx->i >= idx->ccb.cdm.num_matches) {
|
||||
ret = sg_next_enumeration_buffer(idx);
|
||||
if (ret<=0)
|
||||
return -1;
|
||||
if (!((idx->ccb.ccb_h.status == CAM_REQ_CMP)
|
||||
&& (idx->ccb.cdm.status == CAM_DEV_MATCH_MORE)) )
|
||||
return 0;
|
||||
idx->i = 0;
|
||||
}
|
||||
|
||||
switch (idx->ccb.cdm.matches[idx->i].type) {
|
||||
case DEV_MATCH_BUS:
|
||||
break;
|
||||
case DEV_MATCH_DEVICE: {
|
||||
struct device_match_result* result;
|
||||
|
||||
result = &(idx->ccb.cdm.matches[i].result.device_result);
|
||||
if (result->flags & DEV_RESULT_UNCONFIGURED)
|
||||
idx->skip_device = 1;
|
||||
else
|
||||
idx->skip_device = 0;
|
||||
break;
|
||||
}
|
||||
case DEV_MATCH_PERIPH: {
|
||||
struct periph_match_result* result;
|
||||
char buf[64];
|
||||
|
||||
result = &(idx->ccb.cdm.matches[i].result.periph_result);
|
||||
if (idx->skip_device ||
|
||||
strcmp(result->periph_name, "pass") == 0)
|
||||
break;
|
||||
snprintf(buf, sizeof (buf), "/dev/%s%d",
|
||||
result->periph_name, result->unit_number);
|
||||
if(adr_size <= strlen(buf)
|
||||
return -1;
|
||||
strcpy(adr, buf);
|
||||
|
||||
/* Found next enumerable address */
|
||||
return 1;
|
||||
|
||||
}
|
||||
default:
|
||||
/* printf(stderr, "unknown match type\n"); */
|
||||
break;
|
||||
}
|
||||
|
||||
(idx->i)++;
|
||||
goto try_item; /* Regular function exit is return 1 above */
|
||||
}
|
||||
|
||||
|
||||
/** Brings all available, not-whitelist-banned, and accessible drives into
|
||||
libburn's list of drives.
|
||||
*/
|
||||
int scsi_enumerate_drives(void)
|
||||
{
|
||||
burn_drive_enumerator_t idx;
|
||||
int initialize = 1;
|
||||
char buf[64];
|
||||
|
||||
while(1) {
|
||||
ret = sg_give_next_adr(&idx, buf, sizeof(buf), initialize);
|
||||
initialize = 0;
|
||||
if (ret <= 0)
|
||||
break;
|
||||
if (burn_drive_is_banned(buf))
|
||||
continue;
|
||||
enumerate_common(buf, idx.result->path_id, idx.result->path_id,
|
||||
0, idx.result->target_id,
|
||||
idx.result->target_lun);
|
||||
}
|
||||
sg_give_next_adr(&idx, buf, sizeof(buf), -1);
|
||||
}
|
||||
|
||||
|
||||
/** Tells wether libburn has the given drive in use or exclusively reserved.
|
||||
If it is "open" then libburn will eventually call sg_release() on it when
|
||||
it is time to give up usage resp. reservation.
|
||||
*/
|
||||
/** Published as burn_drive.drive_is_open() */
|
||||
int sg_drive_is_open(struct burn_drive * d)
|
||||
{
|
||||
return (d->cam != NULL);
|
||||
}
|
||||
|
||||
|
||||
/** Opens the drive for SCSI commands and - if burn activities are prone
|
||||
to external interference on your system - obtains an exclusive access lock
|
||||
on the drive. (Note: this is not physical tray locking.)
|
||||
A drive that has been opened with sg_grab() will eventually be handed
|
||||
over to sg_release() for closing and unreserving.
|
||||
*/
|
||||
int sg_grab(struct burn_drive *d)
|
||||
{
|
||||
int count;
|
||||
struct cam_device *cam;
|
||||
|
||||
if(d->cam != NULL)
|
||||
return 0;
|
||||
|
||||
cam = cam_open_device(d->devname, O_RDWR);
|
||||
if (cam == NULL) {
|
||||
libdax_msgs_submit(libdax_messenger, d->global_index,
|
||||
0x00020003,
|
||||
LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
|
||||
"Could not grab drive", 0/*os_errno*/, 0);
|
||||
return 0;
|
||||
}
|
||||
d->cam = cam;
|
||||
fcntl(cam->fd, F_SETOWN, getpid());
|
||||
d->released = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/** PORTING: Is mainly about the call to sg_close_drive() and wether it
|
||||
implements the demanded functionality.
|
||||
*/
|
||||
/** Gives up the drive for SCSI commands and releases eventual access locks.
|
||||
(Note: this is not physical tray locking.)
|
||||
*/
|
||||
int sg_release(struct burn_drive *d)
|
||||
{
|
||||
if (d->cam == NULL) {
|
||||
burn_print(1, "release an ungrabbed drive. die\n");
|
||||
return 0;
|
||||
}
|
||||
sg_close_drive(d);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/** Sends a SCSI command to the drive, receives reply and evaluates wether
|
||||
the command succeeded or shall be retried or finally failed.
|
||||
Returned SCSI errors shall not lead to a return value indicating failure.
|
||||
The callers get notified by c->error. An SCSI failure which leads not to
|
||||
a retry shall be notified via scsi_notify_error().
|
||||
The Libburn_log_sg_commandS facility might be of help when problems with
|
||||
a drive have to be examined. It shall stay disabled for normal use.
|
||||
@return: 1 success , <=0 failure
|
||||
*/
|
||||
int sg_issue_command(struct burn_drive *d, struct command *c)
|
||||
{
|
||||
int done = 0;
|
||||
int err;
|
||||
union ccb *ccb;
|
||||
|
||||
if (d->cam == NULL) {
|
||||
c->error = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
c->error = 0;
|
||||
|
||||
ccb = cam_getccb(d->cam);
|
||||
cam_fill_csio(&ccb->csio,
|
||||
1, /* retries */
|
||||
NULL, /* cbfncp */
|
||||
CAM_DEV_QFRZDIS, /* flags */
|
||||
MSG_SIMPLE_Q_TAG, /* tag_action */
|
||||
NULL, /* data_ptr */
|
||||
0, /* dxfer_len */
|
||||
sizeof (ccb->csio.sense_data), /* sense_len */
|
||||
0, /* cdb_len */
|
||||
30*1000); /* timeout */
|
||||
switch (c->dir) {
|
||||
case TO_DRIVE:
|
||||
ccb->csio.ccb_h.flags |= CAM_DIR_OUT;
|
||||
break;
|
||||
case FROM_DRIVE:
|
||||
ccb->csio.ccb_h.flags |= CAM_DIR_IN;
|
||||
break;
|
||||
case NO_TRANSFER:
|
||||
ccb->csio.ccb_h.flags |= CAM_DIR_NONE;
|
||||
break;
|
||||
}
|
||||
|
||||
ccb->csio.cdb_len = c->oplen;
|
||||
memcpy(&ccb->csio.cdb_io.cdb_bytes, &c->opcode, c->oplen);
|
||||
|
||||
memset(&ccb->csio.sense_data, 0, sizeof (ccb->csio.sense_data));
|
||||
|
||||
if (c->page) {
|
||||
ccb->csio.data_ptr = c->page->data;
|
||||
if (c->dir == FROM_DRIVE) {
|
||||
ccb->csio.dxfer_len = BUFFER_SIZE;
|
||||
/* touch page so we can use valgrind */
|
||||
memset(c->page->data, 0, BUFFER_SIZE);
|
||||
} else {
|
||||
|
||||
/* ts A61115: removed a ssert() */
|
||||
if(c->page->bytes <= 0)
|
||||
return 0;
|
||||
|
||||
ccb->csio.dxfer_len = c->page->bytes;
|
||||
}
|
||||
} else {
|
||||
ccb->csio.data_ptr = NULL;
|
||||
ccb->csio.dxfer_len = 0;
|
||||
}
|
||||
|
||||
do {
|
||||
err = cam_send_ccb(d->cam, ccb);
|
||||
if (err == -1) {
|
||||
libdax_msgs_submit(libdax_messenger,
|
||||
d->global_index, 0x0002010c,
|
||||
LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH,
|
||||
"Failed to transfer command to drive",
|
||||
errno, 0);
|
||||
cam_freeccb(ccb);
|
||||
sg_close_drive(d);
|
||||
d->released = 1;
|
||||
d->busy = BURN_DRIVE_IDLE;
|
||||
c->error = 1;
|
||||
return -1;
|
||||
}
|
||||
/* XXX */
|
||||
memcpy(c->sense, &ccb->csio.sense_data, ccb->csio.sense_len);
|
||||
if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
|
||||
if (!c->retry) {
|
||||
c->error = 1;
|
||||
cam_freeccb(ccb);
|
||||
return 1;
|
||||
}
|
||||
switch (scsi_error(d, c->sense, 0)) {
|
||||
case RETRY:
|
||||
done = 0;
|
||||
break;
|
||||
case FAIL:
|
||||
done = 1;
|
||||
c->error = 1;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
done = 1;
|
||||
}
|
||||
} while (!done);
|
||||
cam_freeccb(ccb);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/** Tries to obtain SCSI address parameters.
|
||||
@return 1 is success , 0 is failure
|
||||
*/
|
||||
int sg_obtain_scsi_adr(char *path, int *bus_no, int *host_no, int *channel_no,
|
||||
int *target_no, int *lun_no)
|
||||
{
|
||||
burn_drive_enumerator_t idx;
|
||||
int initialize = 1;
|
||||
char buf[64];
|
||||
struct periph_match_result* result;
|
||||
|
||||
while(1) {
|
||||
ret = sg_give_next_adr(&idx, buf, sizeof(buf), initialize);
|
||||
initialize = 0;
|
||||
if (ret <= 0)
|
||||
break;
|
||||
if (strcmp(adr, buf) != 0)
|
||||
continue;
|
||||
result = &(idx->ccb.cdm.matches[i].result.periph_result);
|
||||
*bus_no = result->path_id;
|
||||
*host_no = result->path_id;
|
||||
*channel_no = 0;
|
||||
*target_no = result->target_id
|
||||
*lun_no = result->target_lun;
|
||||
sg_give_next_adr(&idx, buf, sizeof(buf), -1);
|
||||
return 1;
|
||||
}
|
||||
sg_give_next_adr(&idx, buf, sizeof(buf), -1);
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
/** Tells wether a text is a persistent address as listed by the enumeration
|
||||
functions.
|
||||
*/
|
||||
int sg_is_enumerable_adr(char* adr)
|
||||
{
|
||||
burn_drive_enumerator_t idx;
|
||||
int initialize = 1;
|
||||
char buf[64];
|
||||
|
||||
while(1) {
|
||||
ret = sg_give_next_adr(&idx, buf, sizeof(buf), initialize);
|
||||
initialize = 0;
|
||||
if (ret <= 0)
|
||||
break;
|
||||
if (strcmp(adr, buf) == 0) {
|
||||
sg_give_next_adr(&idx, buf, sizeof(buf), -1);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
sg_give_next_adr(&idx, buf, sizeof(buf), -1);
|
||||
return (0);
|
||||
}
|
||||
|
@ -1,19 +1,17 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
|
||||
|
||||
/* >>> ts A61021 : for testing the new arrangement of code
|
||||
please outcomment these defines : */
|
||||
|
||||
/* Keeps alive old enumerate_common(). New version delegates much work
|
||||
/* Revives old enumerate_common(). New version delegates much work
|
||||
to methods in drive, mmc, spc, and sbc .
|
||||
*/
|
||||
#define Scsi_freebsd_make_own_enumeratE 1
|
||||
|
||||
|
||||
/* Keeps alive old sg_enumerate(). New version delegates most work to
|
||||
/* Revives old scsi_enumerate_drives(). New version delegates most work to
|
||||
sg_give_next_adr().
|
||||
*/
|
||||
#define Scsi_freebsd_old_sg_enumeratE 1
|
||||
#define Scsi_freebsd_old_scsi_enumeratE 1
|
||||
|
||||
|
||||
#include <assert.h>
|
||||
@ -59,7 +57,7 @@ int burn_drive_is_banned(char *device_address);
|
||||
int mmc_function_spy(char * text);
|
||||
|
||||
|
||||
#ifdef Scsi_freebsd_old_sg_enumeratE
|
||||
#ifdef Scsi_freebsd_old_scsi_enumeratE
|
||||
|
||||
int sg_give_next_adr(burn_drive_enumerator_t *idx,
|
||||
char adr[], int adr_size, int initialize)
|
||||
@ -78,10 +76,11 @@ int sg_obtain_scsi_adr(char *path, int *bus_no, int *host_no, int *channel_no,
|
||||
return (0);
|
||||
}
|
||||
|
||||
#else /* Scsi_freebsd_old_sg_enumeratE */
|
||||
#else /* Scsi_freebsd_old_scsi_enumeratE */
|
||||
|
||||
/* ts A61021 : Moved most code from sg_enumerate under sg_give_next_adr() */
|
||||
/* Some helper functions for sg_give_next_adr() */
|
||||
/* ts A61021 : Moved most code from scsi_enumerate_drives under
|
||||
sg_give_next_adr() */
|
||||
/* Some helper functions for scsi_give_next_adr() */
|
||||
|
||||
static int sg_init_enumerator(burn_drive_enumerator_t *idx)
|
||||
{
|
||||
@ -172,7 +171,7 @@ int sg_give_next_adr(burn_drive_enumerator_t *idx,
|
||||
|
||||
try_item:; /* This spaghetti loop keeps the number of tabs small */
|
||||
|
||||
/* Loop content from old sg_enumerate() */
|
||||
/* Loop content from old scsi_enumerate_drives() */
|
||||
|
||||
while (idx->i >= idx->ccb.cdm.num_matches) {
|
||||
ret = sg_next_enumeration_buffer(idx);
|
||||
@ -277,7 +276,7 @@ int sg_obtain_scsi_adr(char *path, int *bus_no, int *host_no, int *channel_no,
|
||||
return (0);
|
||||
}
|
||||
|
||||
#endif /* ! Scsi_freebsd_old_sg_enumeratE */
|
||||
#endif /* ! Scsi_freebsd_old_scsi_enumeratE */
|
||||
|
||||
|
||||
int sg_close_drive(struct burn_drive * d)
|
||||
@ -294,20 +293,10 @@ int sg_drive_is_open(struct burn_drive * d)
|
||||
return (d->cam != NULL);
|
||||
}
|
||||
|
||||
|
||||
void ata_enumerate(void)
|
||||
{
|
||||
/* ts A61021: Only a dummy function is needed in FreeBSD */
|
||||
/* The difference between sg and ata should be encapsulated
|
||||
in sg-linux.c */
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
void sg_enumerate(void)
|
||||
int scsi_enumerate_drives(void)
|
||||
{
|
||||
|
||||
#ifdef Scsi_freebsd_old_sg_enumeratE
|
||||
#ifdef Scsi_freebsd_old_scsi_enumeratE
|
||||
|
||||
union ccb ccb;
|
||||
int bufsize, fd;
|
||||
@ -404,7 +393,7 @@ void sg_enumerate(void)
|
||||
|
||||
close(fd);
|
||||
|
||||
#else /* Scsi_freebsd_old_sg_enumeratE */
|
||||
#else /* Scsi_freebsd_old_scsi_enumeratE */
|
||||
|
||||
burn_drive_enumerator_t idx;
|
||||
int initialize = 1;
|
||||
@ -423,7 +412,7 @@ void sg_enumerate(void)
|
||||
}
|
||||
sg_give_next_adr(&idx, buf, sizeof(buf), -1);
|
||||
|
||||
#endif /* ! Scsi_freebsd_old_sg_enumeratE */
|
||||
#endif /* ! Scsi_freebsd_old_scsi_enumeratE */
|
||||
|
||||
}
|
||||
|
||||
@ -481,6 +470,7 @@ static void enumerate_common(char *fname, int bus_no, int host_no,
|
||||
out.get_nwa = mmc_get_nwa;
|
||||
out.close_disc = mmc_close_disc;
|
||||
out.close_session = mmc_close_session;
|
||||
out.close_track_session = mmc_close;
|
||||
out.read_buffer_capacity = mmc_read_buffer_capacity;
|
||||
out.idata = malloc(sizeof(struct burn_scsi_inquiry_data));
|
||||
out.idata->valid = 0;
|
||||
@ -705,64 +695,3 @@ int sg_issue_command(struct burn_drive *d, struct command *c)
|
||||
return 1;
|
||||
}
|
||||
|
||||
enum response scsi_error(struct burn_drive *d, unsigned char *sense,
|
||||
int senselen)
|
||||
{
|
||||
int key, asc, ascq;
|
||||
|
||||
senselen = senselen;
|
||||
key = sense[2];
|
||||
asc = sense[12];
|
||||
ascq = sense[13];
|
||||
|
||||
burn_print(12, "CONDITION: 0x%x 0x%x 0x%x on %s %s\n",
|
||||
key, asc, ascq, d->idata->vendor, d->idata->product);
|
||||
|
||||
switch (asc) {
|
||||
case 0:
|
||||
burn_print(12, "NO ERROR!\n");
|
||||
return RETRY;
|
||||
|
||||
case 2:
|
||||
burn_print(1, "not ready\n");
|
||||
return RETRY;
|
||||
case 4:
|
||||
burn_print(1,
|
||||
"logical unit is in the process of becoming ready\n");
|
||||
return RETRY;
|
||||
case 0x20:
|
||||
if (key == 5)
|
||||
burn_print(1, "bad opcode\n");
|
||||
return FAIL;
|
||||
case 0x21:
|
||||
burn_print(1, "invalid address or something\n");
|
||||
return FAIL;
|
||||
case 0x24:
|
||||
if (key == 5)
|
||||
burn_print(1, "invalid field in cdb\n");
|
||||
else
|
||||
break;
|
||||
return FAIL;
|
||||
case 0x26:
|
||||
if (key == 5)
|
||||
burn_print( 1, "invalid field in parameter list\n" );
|
||||
return FAIL;
|
||||
case 0x28:
|
||||
if (key == 6)
|
||||
burn_print(1,
|
||||
"Not ready to ready change, medium may have changed\n");
|
||||
else
|
||||
break;
|
||||
return RETRY;
|
||||
case 0x3A:
|
||||
burn_print(12, "Medium not present in %s %s\n",
|
||||
d->idata->vendor, d->idata->product);
|
||||
|
||||
d->status = BURN_DISC_EMPTY;
|
||||
return FAIL;
|
||||
}
|
||||
burn_print(1, "unknown failure\n");
|
||||
burn_print(1, "key:0x%x, asc:0x%x, ascq:0x%x\n", key, asc, ascq);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,64 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
|
||||
/* ts A61010 */
|
||||
/* #include <a ssert.h> */
|
||||
/*
|
||||
|
||||
This is the main operating system dependent SCSI part of libburn. It implements
|
||||
the transport level aspects of SCSI control and command i/o.
|
||||
|
||||
Present implementation: Linux SCSI Generic (sg)
|
||||
|
||||
|
||||
PORTING:
|
||||
|
||||
Porting libburn typically will consist of adding a new operating system case
|
||||
to the following switcher files:
|
||||
os.h Operating system specific libburn definitions and declarations.
|
||||
sg.c Operating system dependent transport level modules.
|
||||
and of deriving the following system specific files from existing examples:
|
||||
os-*.h Included by os.h. You will need some general system knowledge
|
||||
about signals and knowledge about the storage object needs of your
|
||||
transport level module sg-*.c.
|
||||
|
||||
sg-*.c This source module. You will need special system knowledge about
|
||||
how to detect all potentially available drives, how to open them,
|
||||
eventually how to exclusively reserve them, how to perform
|
||||
SCSI transactions, how to inquire the (pseudo-)SCSI driver.
|
||||
You will not need to care about CD burning, MMC or other high-level
|
||||
SCSI aspects.
|
||||
|
||||
Said sg-*.c operations are defined by a public function interface, which has
|
||||
to be implemented in a way that provides libburn with the desired services:
|
||||
|
||||
sg_give_next_adr() iterates over the set of potentially useful drive
|
||||
address strings.
|
||||
|
||||
scsi_enumerate_drives() brings all available, not-whitelist-banned, and
|
||||
accessible drives into libburn's list of drives.
|
||||
|
||||
sg_drive_is_open() tells wether libburn has the given drive in use.
|
||||
|
||||
sg_grab() opens the drive for SCSI commands and ensures
|
||||
undisturbed access.
|
||||
|
||||
sg_release() closes a drive opened by sg_grab()
|
||||
|
||||
sg_issue_command() sends a SCSI command to the drive, receives reply,
|
||||
and evaluates wether the command succeeded or shall
|
||||
be retried or finally failed.
|
||||
|
||||
sg_obtain_scsi_adr() tries to obtain SCSI address parameters.
|
||||
|
||||
|
||||
Porting hints are marked by the text "PORTING:".
|
||||
Send feedback to libburn-hackers@pykix.org .
|
||||
|
||||
Hint: You should also look into sg-freebsd-port.c, which is a younger and
|
||||
in some aspects more straightforward implementation of this interface.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/** PORTING : ------- OS dependent headers and definitions ------ */
|
||||
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
@ -10,9 +67,6 @@
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
/* #include <m alloc.h> ts A61013 : not in Linux man 3 malloc */
|
||||
|
||||
#include <string.h>
|
||||
#include <sys/poll.h>
|
||||
#include <linux/hdreg.h>
|
||||
@ -20,6 +74,9 @@
|
||||
#include <scsi/sg.h>
|
||||
#include <scsi/scsi.h>
|
||||
|
||||
|
||||
/** PORTING : ------ libburn portable headers and definitions ----- */
|
||||
|
||||
#include "transport.h"
|
||||
#include "drive.h"
|
||||
#include "sg.h"
|
||||
@ -30,20 +87,24 @@
|
||||
#include "toc.h"
|
||||
#include "util.h"
|
||||
|
||||
/* kludge! glibc headers don't define all the SCSI stuff that we use! */
|
||||
#ifndef SG_GET_ACCESS_COUNT
|
||||
# define SG_GET_ACCESS_COUNT 0x2289
|
||||
#endif
|
||||
|
||||
#include "libdax_msgs.h"
|
||||
extern struct libdax_msgs *libdax_messenger;
|
||||
|
||||
static void enumerate_common(char *fname, int bus_no, int host_no,
|
||||
int channel_no, int target_no, int lun_no);
|
||||
|
||||
/* ts A51221 */
|
||||
int burn_drive_is_banned(char *device_address);
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
/* PORTING: Private definitions. Port only if needed by public functions. */
|
||||
/* (Public functions are listed below) */
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
|
||||
static void enumerate_common(char *fname, int bus_no, int host_no,
|
||||
int channel_no, int target_no, int lun_no);
|
||||
|
||||
|
||||
/* >>> ts A61115 : this needs mending. A Linux aspect shows up in cdrskin. */
|
||||
/* ts A60813 : storage objects are in libburn/init.c
|
||||
wether to use O_EXCL
|
||||
wether to use O_NOBLOCK with open(2) on devices
|
||||
@ -58,6 +119,12 @@ extern int burn_sg_open_abort_busy;
|
||||
int mmc_function_spy(char * text);
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
/* PORTING: Private functions. Port only if needed by public functions */
|
||||
/* (Public functions are listed below) */
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
|
||||
static int sgio_test(int fd)
|
||||
{
|
||||
unsigned char test_ops[] = { 0, 0, 0, 0, 0, 0 };
|
||||
@ -73,45 +140,8 @@ static int sgio_test(int fd)
|
||||
}
|
||||
|
||||
|
||||
/* ts A60925 : ticket 74 */
|
||||
int sg_close_drive_fd(char *fname, int driveno, int *fd, int sorry)
|
||||
{
|
||||
int ret, os_errno, sevno= LIBDAX_MSGS_SEV_DEBUG;
|
||||
char msg[4096+100];
|
||||
|
||||
if(*fd < 0)
|
||||
return(0);
|
||||
ret = close(*fd);
|
||||
*fd = -1337;
|
||||
if(ret != -1)
|
||||
return 1;
|
||||
os_errno= errno;
|
||||
|
||||
if (fname != NULL)
|
||||
sprintf(msg, "Encountered error when closing drive '%s'",
|
||||
fname);
|
||||
else
|
||||
sprintf(msg, "Encountered error when closing drive");
|
||||
|
||||
if (sorry)
|
||||
sevno = LIBDAX_MSGS_SEV_SORRY;
|
||||
libdax_msgs_submit(libdax_messenger, driveno, 0x00020002,
|
||||
sevno, LIBDAX_MSGS_PRIO_HIGH, msg, os_errno, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sg_drive_is_open(struct burn_drive * d)
|
||||
{
|
||||
/* a bit more detailed case distinction than needed */
|
||||
if (d->fd == -1337)
|
||||
return 0;
|
||||
if (d->fd < 0)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ts A60924 */
|
||||
int sg_handle_busy_device(char *fname, int os_errno)
|
||||
static int sg_handle_busy_device(char *fname, int os_errno)
|
||||
{
|
||||
char msg[4096];
|
||||
|
||||
@ -135,87 +165,8 @@ int sg_handle_busy_device(char *fname, int os_errno)
|
||||
}
|
||||
|
||||
|
||||
/* ts A60922 ticket 33 */
|
||||
/** Returns the next index number and the next enumerated drive address.
|
||||
@param idx An opaque handle. Make no own theories about it.
|
||||
@param adr Takes the reply
|
||||
@param adr_size Gives maximum size of reply including final 0
|
||||
@param initialize 1 = start new,
|
||||
0 = continue, use no other values for now
|
||||
-1 = finish
|
||||
@return 1 = reply is a valid address , 0 = no further address available
|
||||
-1 = severe error (e.g. adr_size too small)
|
||||
*/
|
||||
int sg_give_next_adr(burn_drive_enumerator_t *idx,
|
||||
char adr[], int adr_size, int initialize)
|
||||
{
|
||||
/* sg.h : typedef int burn_drive_enumerator_t; */
|
||||
static int sg_limit = 32, ata_limit = 26;
|
||||
int baseno = 0;
|
||||
|
||||
if (initialize == -1)
|
||||
return 0;
|
||||
|
||||
if (initialize == 1)
|
||||
*idx = -1;
|
||||
(*idx)++;
|
||||
if (*idx >= sg_limit)
|
||||
goto next_ata;
|
||||
if (adr_size < 10)
|
||||
return -1;
|
||||
sprintf(adr, "/dev/sg%d", *idx);
|
||||
return 1;
|
||||
next_ata:;
|
||||
baseno += sg_limit;
|
||||
if (*idx - baseno >= ata_limit)
|
||||
goto next_nothing;
|
||||
if (adr_size < 9)
|
||||
return -1;
|
||||
sprintf(adr, "/dev/hd%c", 'a' + (*idx - baseno));
|
||||
return 1;
|
||||
next_nothing:;
|
||||
baseno += ata_limit;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sg_is_enumerable_adr(char *adr)
|
||||
{
|
||||
char fname[4096];
|
||||
int i, ret = 0, first = 1;
|
||||
|
||||
while (1) {
|
||||
ret= sg_give_next_adr(&i, fname, sizeof(fname), first);
|
||||
if(ret <= 0)
|
||||
break;
|
||||
first = 0;
|
||||
if (strcmp(adr, fname) == 0)
|
||||
return 1;
|
||||
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
/* ts A60926 */
|
||||
int sg_release_siblings(int sibling_fds[], int *sibling_count)
|
||||
{
|
||||
int i;
|
||||
char msg[81];
|
||||
|
||||
for(i= 0; i < *sibling_count; i++)
|
||||
sg_close_drive_fd(NULL, -1, &(sibling_fds[i]), 0);
|
||||
if(*sibling_count > 0) {
|
||||
sprintf(msg, "Closed %d O_EXCL scsi siblings", *sibling_count);
|
||||
libdax_msgs_submit(libdax_messenger, -1, 0x00020007,
|
||||
LIBDAX_MSGS_SEV_NOTE, LIBDAX_MSGS_PRIO_HIGH, msg, 0,0);
|
||||
}
|
||||
*sibling_count = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* ts A60926 */
|
||||
int sg_open_drive_fd(char *fname, int scan_mode)
|
||||
static int sg_open_drive_fd(char *fname, int scan_mode)
|
||||
{
|
||||
int open_mode = O_RDWR, fd;
|
||||
char msg[81];
|
||||
@ -266,8 +217,67 @@ int sg_open_drive_fd(char *fname, int scan_mode)
|
||||
}
|
||||
|
||||
|
||||
/* ts A60925 : ticket 74 */
|
||||
static int sg_close_drive_fd(char *fname, int driveno, int *fd, int sorry)
|
||||
{
|
||||
int ret, os_errno, sevno= LIBDAX_MSGS_SEV_DEBUG;
|
||||
char msg[4096+100];
|
||||
|
||||
if(*fd < 0)
|
||||
return(0);
|
||||
ret = close(*fd);
|
||||
*fd = -1337;
|
||||
if(ret != -1)
|
||||
return 1;
|
||||
os_errno= errno;
|
||||
|
||||
if (fname != NULL)
|
||||
sprintf(msg, "Encountered error when closing drive '%s'",
|
||||
fname);
|
||||
else
|
||||
sprintf(msg, "Encountered error when closing drive");
|
||||
|
||||
if (sorry)
|
||||
sevno = LIBDAX_MSGS_SEV_SORRY;
|
||||
libdax_msgs_submit(libdax_messenger, driveno, 0x00020002,
|
||||
sevno, LIBDAX_MSGS_PRIO_HIGH, msg, os_errno, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* ts A60926 */
|
||||
int sg_open_scsi_siblings(char *path, int driveno,
|
||||
static int sg_release_siblings(int sibling_fds[], int *sibling_count)
|
||||
{
|
||||
int i;
|
||||
char msg[81];
|
||||
|
||||
for(i= 0; i < *sibling_count; i++)
|
||||
sg_close_drive_fd(NULL, -1, &(sibling_fds[i]), 0);
|
||||
if(*sibling_count > 0) {
|
||||
sprintf(msg, "Closed %d O_EXCL scsi siblings", *sibling_count);
|
||||
libdax_msgs_submit(libdax_messenger, -1, 0x00020007,
|
||||
LIBDAX_MSGS_SEV_NOTE, LIBDAX_MSGS_PRIO_HIGH, msg, 0,0);
|
||||
}
|
||||
*sibling_count = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* ts A60926 */
|
||||
static int sg_close_drive(struct burn_drive *d)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!burn_drive_is_open(d))
|
||||
return 0;
|
||||
sg_release_siblings(d->sibling_fds, &(d->sibling_count));
|
||||
ret = sg_close_drive_fd(d->devname, d->global_index, &(d->fd), 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* ts A60926 */
|
||||
static int sg_open_scsi_siblings(char *path, int driveno,
|
||||
int sibling_fds[], int *sibling_count,
|
||||
int host_no, int channel_no, int id_no, int lun_no)
|
||||
{
|
||||
@ -324,19 +334,9 @@ failed:;
|
||||
}
|
||||
|
||||
|
||||
/* ts A60926 */
|
||||
int sg_close_drive(struct burn_drive *d)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!burn_drive_is_open(d))
|
||||
return 0;
|
||||
sg_release_siblings(d->sibling_fds, &(d->sibling_count));
|
||||
ret = sg_close_drive_fd(d->devname, d->global_index, &(d->fd), 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ata_enumerate(void)
|
||||
/** Speciality of Linux: detect non-SCSI ATAPI (EIDE) which will from
|
||||
then on used used via generic SCSI as is done with (emulated) SCSI drives */
|
||||
static void ata_enumerate(void)
|
||||
{
|
||||
struct hd_driveid tm;
|
||||
int i, fd;
|
||||
@ -372,7 +372,9 @@ void ata_enumerate(void)
|
||||
}
|
||||
}
|
||||
|
||||
void sg_enumerate(void)
|
||||
|
||||
/** Detects (probably emulated) SCSI drives */
|
||||
static void sg_enumerate(void)
|
||||
{
|
||||
struct sg_scsi_id sid;
|
||||
int i, fd, sibling_fds[LIBBURN_SG_MAX_SIBLINGS], sibling_count= 0, ret;
|
||||
@ -428,6 +430,19 @@ void sg_enumerate(void)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ts A61115 */
|
||||
/* ----------------------------------------------------------------------- */
|
||||
/* PORTING: Private functions which contain publicly needed functionality. */
|
||||
/* Their portable part must be performed. So it is probably best */
|
||||
/* to replace the non-portable part and to call these functions */
|
||||
/* in your port, too. */
|
||||
/* ----------------------------------------------------------------------- */
|
||||
|
||||
|
||||
/** Wraps a detected drive into libburn structures and hands it over to
|
||||
libburn drive list.
|
||||
*/
|
||||
/* ts A60923 - A61005 : introduced new SCSI parameters */
|
||||
/* ts A61021 : moved non os-specific code to spc,sbc,mmc,drive */
|
||||
static void enumerate_common(char *fname, int bus_no, int host_no,
|
||||
@ -446,13 +461,18 @@ static void enumerate_common(char *fname, int bus_no, int host_no,
|
||||
if (ret<=0)
|
||||
return;
|
||||
|
||||
/* PORTING: ------------------- non portable part --------------- */
|
||||
|
||||
/* Operating system adapter is Linux Generic SCSI (sg) */
|
||||
/* Adapter specific handles and data */
|
||||
out.fd = -1337;
|
||||
out.sibling_count = 0;
|
||||
for(i= 0; i<LIBBURN_SG_MAX_SIBLINGS; i++)
|
||||
out.sibling_fds[i] = -1337;
|
||||
/* Adapter specific functions */
|
||||
|
||||
/* PORTING: ---------------- end of non portable part ------------ */
|
||||
|
||||
/* Adapter specific functions with standardized names */
|
||||
out.grab = sg_grab;
|
||||
out.release = sg_release;
|
||||
out.drive_is_open= sg_drive_is_open;
|
||||
@ -462,17 +482,116 @@ static void enumerate_common(char *fname, int bus_no, int host_no,
|
||||
burn_drive_finish_enum(&out);
|
||||
}
|
||||
|
||||
/*
|
||||
we use the sg reference count to decide whether we can use the
|
||||
drive or not.
|
||||
if refcount is not one, drive is open somewhere else.
|
||||
|
||||
ts A60813: this test is too late. O_EXCL is the stronger solution.
|
||||
After all the test was disabled already in icculus.org/burn CVS.
|
||||
/* ts A61115 */
|
||||
/* ------------------------------------------------------------------------ */
|
||||
/* PORTING: Public functions. These MUST be ported. */
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
|
||||
/** PORTING:
|
||||
In this Linux implementation, this function mirrors the enumeration
|
||||
done in sg_enumerate and ata_enumerate(). It would be better to base those
|
||||
functions on this sg_give_next_adr() but the situation is not inviting.
|
||||
*/
|
||||
/* ts A60922 ticket 33 : called from drive.c */
|
||||
/** Returns the next index number and the next enumerated drive address.
|
||||
The enumeration has to cover all available and accessible drives. It is
|
||||
allowed to return addresses of drives which are not available but under
|
||||
some (even exotic) circumstances could be available. It is on the other
|
||||
hand allowed, only to hand out addresses which can really be used right
|
||||
in the moment of this call. (This implementation chooses the former.)
|
||||
@param idx An opaque handle. Make no own theories about it.
|
||||
@param adr Takes the reply
|
||||
@param adr_size Gives maximum size of reply including final 0
|
||||
@param initialize 1 = start new,
|
||||
0 = continue, use no other values for now
|
||||
-1 = finish
|
||||
@return 1 = reply is a valid address , 0 = no further address available
|
||||
-1 = severe error (e.g. adr_size too small)
|
||||
*/
|
||||
int sg_give_next_adr(burn_drive_enumerator_t *idx,
|
||||
char adr[], int adr_size, int initialize)
|
||||
{
|
||||
/* sg.h : typedef int burn_drive_enumerator_t; */
|
||||
static int sg_limit = 32, ata_limit = 26;
|
||||
int baseno = 0;
|
||||
|
||||
if (initialize == -1)
|
||||
return 0;
|
||||
|
||||
if (initialize == 1)
|
||||
*idx = -1;
|
||||
(*idx)++;
|
||||
if (*idx >= sg_limit)
|
||||
goto next_ata;
|
||||
if (adr_size < 10)
|
||||
return -1;
|
||||
sprintf(adr, "/dev/sg%d", *idx);
|
||||
return 1;
|
||||
next_ata:;
|
||||
baseno += sg_limit;
|
||||
if (*idx - baseno >= ata_limit)
|
||||
goto next_nothing;
|
||||
if (adr_size < 9)
|
||||
return -1;
|
||||
sprintf(adr, "/dev/hd%c", 'a' + (*idx - baseno));
|
||||
return 1;
|
||||
next_nothing:;
|
||||
baseno += ata_limit;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/** Brings all available, not-whitelist-banned, and accessible drives into
|
||||
libburn's list of drives.
|
||||
*/
|
||||
/** PORTING:
|
||||
If not stricken with an incompletely unified situation like in Linux
|
||||
one would rather implement this by a loop calling sg_give_next_adr().
|
||||
If needed with your sg_give_next_adr() results, do a test for existence
|
||||
and accessability. If burn activities are prone to external interference
|
||||
on your system it is also necessary to obtain exclusive access locks on
|
||||
the drives.
|
||||
Hand over each accepted drive to enumerate_common() resp. its replacement
|
||||
within your port.
|
||||
|
||||
See FreeBSD port sketch sg-freebsd-port.c for such an implementation.
|
||||
*/
|
||||
/* ts A61115: replacing call to sg-implementation internals from drive.c */
|
||||
int scsi_enumerate_drives(void)
|
||||
{
|
||||
sg_enumerate();
|
||||
ata_enumerate();
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/** Tells wether libburn has the given drive in use or exclusively reserved.
|
||||
If it is "open" then libburn will eventually call sg_release() on it when
|
||||
it is time to give up usage resp. reservation.
|
||||
*/
|
||||
/** Published as burn_drive.drive_is_open() */
|
||||
int sg_drive_is_open(struct burn_drive * d)
|
||||
{
|
||||
/* a bit more detailed case distinction than needed */
|
||||
if (d->fd == -1337)
|
||||
return 0;
|
||||
if (d->fd < 0)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/** Opens the drive for SCSI commands and - if burn activities are prone
|
||||
to external interference on your system - obtains an exclusive access lock
|
||||
on the drive. (Note: this is not physical tray locking.)
|
||||
A drive that has been opened with sg_grab() will eventually be handed
|
||||
over to sg_release() for closing and unreserving.
|
||||
*/
|
||||
int sg_grab(struct burn_drive *d)
|
||||
{
|
||||
int fd, count, os_errno= 0, ret;
|
||||
int fd, os_errno= 0, ret;
|
||||
|
||||
/* ts A60813 */
|
||||
int open_mode = O_RDWR;
|
||||
@ -495,12 +614,11 @@ int sg_grab(struct burn_drive *d)
|
||||
if(burn_sg_open_o_nonblock)
|
||||
open_mode |= O_NONBLOCK;
|
||||
|
||||
/* ts A60813
|
||||
/* ts A60813 - A60822
|
||||
After enumeration the drive fd is probably still open.
|
||||
-1337 is the initial value of burn_drive.fd and the value after
|
||||
relase of drive. Unclear why not the official error return
|
||||
value -1 of open(2) war used. */
|
||||
/* ts A60822: was if(d->fd == -1337) { */
|
||||
if(! burn_drive_is_open(d)) {
|
||||
|
||||
/* ts A60821
|
||||
@ -523,49 +641,34 @@ int sg_grab(struct burn_drive *d)
|
||||
} else
|
||||
fd= d->fd;
|
||||
|
||||
/* ts A61007 : this is redundant */
|
||||
/* a ssert(fd != -1337); */
|
||||
|
||||
if (fd >= 0) {
|
||||
|
||||
/* ts A60814:
|
||||
according to my experiments this test would work now ! */
|
||||
|
||||
/* ts A60926 : this was disabled */
|
||||
/* Tests with growisofs on kernel 2.4.21 yielded that this
|
||||
does not help against blocking on busy drives.
|
||||
*/
|
||||
/* <<< the old dummy */
|
||||
/* er = ioctl(fd, SG_GET_ACCESS_COUNT, &count);*/
|
||||
count = 1;
|
||||
|
||||
if (1 == count) {
|
||||
d->fd = fd;
|
||||
fcntl(fd, F_SETOWN, getpid());
|
||||
d->released = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
drive_is_in_use:;
|
||||
libdax_msgs_submit(libdax_messenger, d->global_index,
|
||||
0x00020003,
|
||||
LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
|
||||
"Could not grab drive - already in use", 0, 0);
|
||||
sg_close_drive(d);
|
||||
d->fd = -1337;
|
||||
return 0;
|
||||
d->fd = fd;
|
||||
fcntl(fd, F_SETOWN, getpid());
|
||||
d->released = 0;
|
||||
return 1;
|
||||
}
|
||||
libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020003,
|
||||
LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
|
||||
"Could not grab drive", os_errno, 0);
|
||||
return 0;
|
||||
|
||||
drive_is_in_use:;
|
||||
libdax_msgs_submit(libdax_messenger, d->global_index,
|
||||
0x00020003,
|
||||
LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
|
||||
"Could not grab drive - already in use", 0, 0);
|
||||
sg_close_drive(d);
|
||||
d->fd = -1337;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
non zero return means you still have the drive and it's not
|
||||
in a state to be released? (is that even possible?)
|
||||
*/
|
||||
|
||||
/** PORTING: Is mainly about the call to sg_close_drive() and wether it
|
||||
implements the demanded functionality.
|
||||
*/
|
||||
/** Gives up the drive for SCSI commands and releases eventual access locks.
|
||||
(Note: this is not physical tray locking.)
|
||||
*/
|
||||
int sg_release(struct burn_drive *d)
|
||||
{
|
||||
/* ts A60821
|
||||
@ -585,19 +688,54 @@ int sg_release(struct burn_drive *d)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/** Sends a SCSI command to the drive, receives reply and evaluates wether
|
||||
the command succeeded or shall be retried or finally failed.
|
||||
Returned SCSI errors shall not lead to a return value indicating failure.
|
||||
The callers get notified by c->error. An SCSI failure which leads not to
|
||||
a retry shall be notified via scsi_notify_error().
|
||||
The Libburn_log_sg_commandS facility might be of help when problems with
|
||||
a drive have to be examined. It shall stay disabled for normal use.
|
||||
@return: 1 success , <=0 failure
|
||||
*/
|
||||
int sg_issue_command(struct burn_drive *d, struct command *c)
|
||||
{
|
||||
int done = 0, no_c_page = 0;
|
||||
int err;
|
||||
sg_io_hdr_t s;
|
||||
/*
|
||||
#define Libburn_log_sg_commandS 1
|
||||
*/
|
||||
|
||||
/* ts A60821
|
||||
<<< debug: for tracing calls which might use open drive fds */
|
||||
#ifdef Libburn_log_sg_commandS
|
||||
/* ts A61030 */
|
||||
static FILE *fp= NULL;
|
||||
static int fpcount= 0;
|
||||
int i;
|
||||
#endif /* Libburn_log_sg_commandS */
|
||||
|
||||
/* <<< ts A60821
|
||||
debug: for tracing calls which might use open drive fds */
|
||||
char buf[161];
|
||||
sprintf(buf,"sg_issue_command d->fd= %d d->released= %d\n",
|
||||
d->fd,d->released);
|
||||
mmc_function_spy(buf);
|
||||
|
||||
#ifdef Libburn_log_sg_commandS
|
||||
/* ts A61030 */
|
||||
if(fp==NULL) {
|
||||
fp= fopen("/tmp/libburn_sg_command_log","a");
|
||||
fprintf(fp,"\n-----------------------------------------\n");
|
||||
}
|
||||
if(fp!=NULL) {
|
||||
for(i=0;i<10;i++)
|
||||
fprintf(fp,"%2.2x ", c->opcode[i]);
|
||||
fprintf(fp,"\n");
|
||||
fpcount++;
|
||||
}
|
||||
#endif /* Libburn_log_sg_commandS */
|
||||
|
||||
|
||||
/* ts A61010 : with no fd there is no chance to send an ioctl */
|
||||
if (d->fd < 0) {
|
||||
c->error = 1;
|
||||
@ -670,7 +808,9 @@ int sg_issue_command(struct burn_drive *d, struct command *c)
|
||||
if (s.sb_len_wr) {
|
||||
if (!c->retry) {
|
||||
c->error = 1;
|
||||
return 1;
|
||||
|
||||
/* A61106: rather than : return 1 */
|
||||
goto ex;
|
||||
}
|
||||
switch (scsi_error(d, s.sbp, s.sb_len_wr)) {
|
||||
case RETRY:
|
||||
@ -685,72 +825,27 @@ int sg_issue_command(struct burn_drive *d, struct command *c)
|
||||
done = 1;
|
||||
}
|
||||
} while (!done);
|
||||
|
||||
/* ts A61106 */
|
||||
ex:;
|
||||
if (c->error) {
|
||||
scsi_notify_error(d, c, s.sbp, s.sb_len_wr, 0);
|
||||
|
||||
#ifdef Libburn_log_sg_commandS
|
||||
if(fp!=NULL) {
|
||||
fprintf(fp,"+++ key=%X asc=%2.2Xh ascq=%2.2Xh\n",
|
||||
s.sbp[2], s.sbp[12], s.sbp[13]);
|
||||
fpcount++;
|
||||
}
|
||||
#endif /* Libburn_log_sg_commandS */
|
||||
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
enum response scsi_error(struct burn_drive *d, unsigned char *sense,
|
||||
int senselen)
|
||||
{
|
||||
int key, asc, ascq;
|
||||
|
||||
senselen = senselen;
|
||||
key = sense[2];
|
||||
asc = sense[12];
|
||||
ascq = sense[13];
|
||||
|
||||
burn_print(12, "CONDITION: 0x%x 0x%x 0x%x on %s %s\n",
|
||||
key, asc, ascq, d->idata->vendor, d->idata->product);
|
||||
|
||||
switch (asc) {
|
||||
case 0:
|
||||
burn_print(12, "NO ERROR!\n");
|
||||
return RETRY;
|
||||
|
||||
case 2:
|
||||
burn_print(1, "not ready\n");
|
||||
return RETRY;
|
||||
case 4:
|
||||
burn_print(1,
|
||||
"logical unit is in the process of becoming ready\n");
|
||||
return RETRY;
|
||||
case 0x20:
|
||||
if (key == 5)
|
||||
burn_print(1, "bad opcode\n");
|
||||
return FAIL;
|
||||
case 0x21:
|
||||
burn_print(1, "invalid address or something\n");
|
||||
return FAIL;
|
||||
case 0x24:
|
||||
if (key == 5)
|
||||
burn_print(1, "invalid field in cdb\n");
|
||||
else
|
||||
break;
|
||||
return FAIL;
|
||||
case 0x26:
|
||||
if ( key == 5 )
|
||||
burn_print( 1, "invalid field in parameter list\n" );
|
||||
return FAIL;
|
||||
case 0x28:
|
||||
if (key == 6)
|
||||
burn_print(1,
|
||||
"Not ready to ready change, medium may have changed\n");
|
||||
else
|
||||
break;
|
||||
return RETRY;
|
||||
case 0x3A:
|
||||
burn_print(12, "Medium not present in %s %s\n",
|
||||
d->idata->vendor, d->idata->product);
|
||||
|
||||
d->status = BURN_DISC_EMPTY;
|
||||
return FAIL;
|
||||
}
|
||||
burn_print(1, "unknown failure\n");
|
||||
burn_print(1, "key:0x%x, asc:0x%x, ascq:0x%x\n", key, asc, ascq);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
/* ts A60922 */
|
||||
/** Try to obtain SCSI address parameters.
|
||||
/** Tries to obtain SCSI address parameters.
|
||||
@return 1 is success , 0 is failure
|
||||
*/
|
||||
int sg_obtain_scsi_adr(char *path, int *bus_no, int *host_no, int *channel_no,
|
||||
@ -795,3 +890,31 @@ int sg_obtain_scsi_adr(char *path, int *bus_no, int *host_no, int *channel_no,
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* ts A60922 ticket 33 : called from drive.c */
|
||||
/** Tells wether a text is a persistent address as listed by the enumeration
|
||||
functions.
|
||||
*/
|
||||
int sg_is_enumerable_adr(char *adr)
|
||||
{
|
||||
char fname[4096];
|
||||
int ret = 0, first = 1;
|
||||
burn_drive_enumerator_t idx;
|
||||
|
||||
while (1) {
|
||||
ret= sg_give_next_adr(&idx, fname, sizeof(fname), first);
|
||||
if(ret <= 0)
|
||||
break;
|
||||
first = 0;
|
||||
if (strcmp(adr, fname) == 0) {
|
||||
sg_give_next_adr(&idx, fname, sizeof(fname), -1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
sg_give_next_adr(&idx, fname, sizeof(fname), -1);
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,5 +1,9 @@
|
||||
|
||||
/* ts A61013 : It would be nice if autotools could do that job */
|
||||
/* sg.c
|
||||
Switcher for operating system dependent transport level modules of libburn.
|
||||
Copyright (C) 2006 Thomas Schmitt <scdbackup@gmx.net>, provided under GPL
|
||||
*/
|
||||
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
|
||||
|
48
libburn/sg.h
48
libburn/sg.h
@ -3,45 +3,17 @@
|
||||
#ifndef __SG
|
||||
#define __SG
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
|
||||
/* >>> To hold all state information of BSD device enumeration
|
||||
which are now local in sg_enumerate() . So that sg_give_next_adr()
|
||||
can work in BSD and sg_enumerate() can use it. */
|
||||
struct burn_drive_enumeration_state {
|
||||
#include "os.h"
|
||||
|
||||
#ifdef Scsi_freebsd_old_sg_enumeratE
|
||||
int dummy;
|
||||
#else
|
||||
union ccb ccb;
|
||||
int bufsize, fd;
|
||||
unsigned int i;
|
||||
int skip_device;
|
||||
#endif /* ! Scsi_freebsd_old_sg_enumeratE */
|
||||
|
||||
};
|
||||
typedef struct burn_drive_enumeration_state burn_drive_enumerator_t;
|
||||
/* see os.h for name of particular os-*.h where this is defined */
|
||||
BURN_OS_DEFINE_DRIVE_ENUMERATOR_T
|
||||
|
||||
#else /* __FreeBSD__ */
|
||||
|
||||
/* <<< just for testing the C syntax */
|
||||
struct burn_drive_enumeration_state {
|
||||
int dummy;
|
||||
};
|
||||
typedef struct burn_drive_enumeration_state burn_drive_enumerator_tX;
|
||||
|
||||
typedef int burn_drive_enumerator_t;
|
||||
|
||||
#endif /* ! __FreeBSD__ */
|
||||
|
||||
struct burn_drive;
|
||||
struct command;
|
||||
|
||||
enum response
|
||||
{ RETRY, FAIL };
|
||||
|
||||
/* ts A60925 : ticket 74 */
|
||||
int sg_close_drive_fd(char *fname, int driveno, int *fd, int sorry);
|
||||
|
||||
/* ts A60922 ticket 33 */
|
||||
int sg_give_next_adr(burn_drive_enumerator_t *enm_context,
|
||||
@ -50,19 +22,13 @@ int sg_is_enumerable_adr(char *adr);
|
||||
int sg_obtain_scsi_adr(char *path, int *bus_no, int *host_no, int *channel_no,
|
||||
int *target_no, int *lun_no);
|
||||
|
||||
/* ts A60926 : ticket 33 ++ */
|
||||
int sg_open_scsi_siblings(char *fname, int driveno,
|
||||
int sibling_fds[], int *sibling_count,
|
||||
int host_no, int channel_no, int id_no, int lun_no);
|
||||
int sg_release_siblings(int sibling_fds[], int *sibling_count);
|
||||
int sg_close_drive(struct burn_drive *d);
|
||||
|
||||
void sg_enumerate(void);
|
||||
void ata_enumerate(void);
|
||||
int sg_grab(struct burn_drive *);
|
||||
int sg_release(struct burn_drive *);
|
||||
int sg_issue_command(struct burn_drive *, struct command *);
|
||||
enum response scsi_error(struct burn_drive *, unsigned char *, int);
|
||||
|
||||
/* ts A61115 : formerly sg_enumerate();ata_enumerate() */
|
||||
int scsi_enumerate_drives(void);
|
||||
|
||||
int sg_drive_is_open(struct burn_drive * d);
|
||||
|
||||
#endif /* __SG */
|
||||
|
@ -22,6 +22,10 @@ enum burn_source_status burn_track_set_source(struct burn_track *t,
|
||||
return BURN_SOURCE_FAILED;
|
||||
s->refcount++;
|
||||
t->source = s;
|
||||
|
||||
/* ts A61031 */
|
||||
t->open_ended = (s->get_size(s) <= 0);
|
||||
|
||||
return BURN_SOURCE_OK;
|
||||
}
|
||||
|
||||
|
158
libburn/spc.c
158
libburn/spc.c
@ -274,6 +274,11 @@ void spc_sense_write_params(struct burn_drive *d)
|
||||
mmc_read_disc_info(d);
|
||||
}
|
||||
|
||||
/* remark ts A61104 :
|
||||
Although command MODE SELECT is SPC, the content of the
|
||||
Write Parameters Mode Page (05h) is MMC (Table 108 in MMC-1).
|
||||
Thus the filling of the mode page should be done by a mmc_ function.
|
||||
*/
|
||||
void spc_select_write_params(struct burn_drive *d,
|
||||
const struct burn_write_opts *o)
|
||||
{
|
||||
@ -284,6 +289,13 @@ void spc_select_write_params(struct burn_drive *d,
|
||||
/* ts A61007 : All current callers are safe. */
|
||||
/* a ssert(o->drive == d); */
|
||||
|
||||
/* <<< A61030
|
||||
fprintf(stderr,"libburn_debug: write_type=%d multi=%d control=%d\n",
|
||||
o->write_type,o->multi,o->control);
|
||||
fprintf(stderr,"libburn_debug: block_type=%d spc_block_type=%d\n",
|
||||
o->block_type,spc_block_type(o->block_type));
|
||||
*/
|
||||
|
||||
memcpy(c.opcode, SPC_MODE_SELECT, sizeof(SPC_MODE_SELECT));
|
||||
c.retry = 1;
|
||||
c.oplen = sizeof(SPC_MODE_SELECT);
|
||||
@ -307,8 +319,17 @@ void spc_select_write_params(struct burn_drive *d,
|
||||
c.page->data[10] = (bufe << 6)
|
||||
+ (sim << 4)
|
||||
+ o->write_type;
|
||||
c.page->data[11] = (o->multi << 6) | o->control;
|
||||
|
||||
/* ts A61106 : MMC-1 table 110 : multi==0 or multi==3 */
|
||||
c.page->data[11] = ((3 * !!o->multi) << 6) | o->control;
|
||||
|
||||
c.page->data[12] = spc_block_type(o->block_type);
|
||||
|
||||
/* ts A61104 */
|
||||
if(!(o->control&4)) /* audio (MMC-1 table 61) */
|
||||
if(o->write_type == BURN_WRITE_TAO) /* ??? for others too ? */
|
||||
c.page->data[12] = 0; /* Data Block Type: Raw Data */
|
||||
|
||||
c.page->data[22] = 0;
|
||||
c.page->data[23] = 150; /* audio pause length */
|
||||
/*XXX need session format! */
|
||||
@ -361,7 +382,10 @@ void spc_probe_write_modes(struct burn_drive *d)
|
||||
c.page->data[12] = try_block_type;
|
||||
c.page->data[23] = 150;
|
||||
c.dir = TO_DRIVE;
|
||||
|
||||
d->silent_on_scsi_error = 1;
|
||||
d->issue_command(d, &c);
|
||||
d->silent_on_scsi_error = 0;
|
||||
|
||||
key = c.sense[2];
|
||||
asc = c.sense[12];
|
||||
@ -470,6 +494,10 @@ int burn_scsi_setup_drive(struct burn_drive *d, int bus_no, int host_no,
|
||||
d->channel = channel_no;
|
||||
d->lun = lun_no;
|
||||
|
||||
/* ts A61106 */
|
||||
d->silent_on_scsi_error = 0;
|
||||
|
||||
|
||||
d->idata = malloc(sizeof(struct burn_scsi_inquiry_data));
|
||||
d->idata->valid = 0;
|
||||
d->mdata = malloc(sizeof(struct scsi_mode_data));
|
||||
@ -495,3 +523,131 @@ int burn_scsi_setup_drive(struct burn_drive *d, int bus_no, int host_no,
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* ts A61122 */
|
||||
enum response scsi_error_msg(struct burn_drive *d, unsigned char *sense,
|
||||
int senselen, char msg[161],
|
||||
int *key, int *asc, int *ascq)
|
||||
{
|
||||
*key= *asc= *ascq= -1;
|
||||
|
||||
if (senselen<=0 || senselen>2)
|
||||
*key = sense[2];
|
||||
if (senselen<=0 || senselen>12)
|
||||
*asc = sense[12];
|
||||
if (senselen<=0 || senselen>13)
|
||||
*ascq = sense[13];
|
||||
|
||||
burn_print(12, "CONDITION: 0x%x 0x%x 0x%x on %s %s\n",
|
||||
*key, *asc, *ascq, d->idata->vendor, d->idata->product);
|
||||
|
||||
switch (*asc) {
|
||||
case 0:
|
||||
sprintf(msg, "(no error reported by SCSI transaction)");
|
||||
return RETRY;
|
||||
|
||||
case 2:
|
||||
sprintf(msg, "not ready");
|
||||
return RETRY;
|
||||
case 4:
|
||||
sprintf(msg,
|
||||
"logical unit is in the process of becoming ready");
|
||||
return RETRY;
|
||||
case 0x20:
|
||||
if (*key == 5)
|
||||
sprintf(msg, "bad opcode");
|
||||
return FAIL;
|
||||
case 0x21:
|
||||
sprintf(msg, "invalid address");
|
||||
return FAIL;
|
||||
case 0x24:
|
||||
if (*key == 5)
|
||||
sprintf(msg, "invalid field in cdb");
|
||||
else
|
||||
break;
|
||||
return FAIL;
|
||||
case 0x26:
|
||||
if (*key == 5 )
|
||||
sprintf(msg, "invalid field in parameter list" );
|
||||
return FAIL;
|
||||
case 0x28:
|
||||
if (*key == 6)
|
||||
sprintf(msg, "Medium may have changed");
|
||||
else
|
||||
break;
|
||||
return RETRY;
|
||||
case 0x3A:
|
||||
sprintf(msg, "Medium not present");
|
||||
d->status = BURN_DISC_EMPTY;
|
||||
return FAIL;
|
||||
}
|
||||
sprintf(msg,
|
||||
"Failure. See mmc3r10g.pdf: Sense Key %X ASC %2.2X ASCQ %2.2X",
|
||||
*key, *asc, *ascq);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
|
||||
/* ts A61115 moved from sg-*.c */
|
||||
/* ts A61122 made it frontend to scsi_error_msg() */
|
||||
enum response scsi_error(struct burn_drive *d, unsigned char *sense,
|
||||
int senselen)
|
||||
{
|
||||
int key, asc, ascq;
|
||||
char msg[160];
|
||||
enum response resp;
|
||||
|
||||
resp = scsi_error_msg(d, sense, senselen, msg, &key, &asc, &ascq);
|
||||
if (asc == 0 || asc == 0x3A)
|
||||
burn_print(12, "%s\n", msg);
|
||||
else
|
||||
burn_print(1, "%s\n", msg);
|
||||
return resp;
|
||||
}
|
||||
|
||||
|
||||
/* ts A61030 - A61115 */
|
||||
/* @param flag bit0=do report conditions which are considered not an error */
|
||||
int scsi_notify_error(struct burn_drive *d, struct command *c,
|
||||
unsigned char *sense, int senselen, int flag)
|
||||
{
|
||||
int key= -1, asc= -1, ascq= -1, ret;
|
||||
char msg[320],scsi_msg[160];
|
||||
|
||||
if (d->silent_on_scsi_error)
|
||||
return 1;
|
||||
|
||||
strcpy(scsi_msg, " \"");
|
||||
scsi_error_msg(d, sense, senselen, scsi_msg + strlen(scsi_msg),
|
||||
&key, &asc, &ascq);
|
||||
strcat(scsi_msg, "\"");
|
||||
|
||||
if(!(flag & 1)) {
|
||||
/* SPC : TEST UNIT READY command */
|
||||
if (c->opcode[0] == 0)
|
||||
return 1;
|
||||
/* MMC : READ DISC INFORMATION command */
|
||||
if (c->opcode[0] == 0x51)
|
||||
if (key == 0x2 && asc == 0x3A &&
|
||||
ascq>=0 && ascq <= 0x02) /* MEDIUM NOT PRESENT */
|
||||
return 1;
|
||||
}
|
||||
|
||||
sprintf(msg,"SCSI error condition on command %2.2Xh :", c->opcode[0]);
|
||||
if (key>=0)
|
||||
sprintf(msg+strlen(msg), " key=%Xh", key);
|
||||
if (asc>=0)
|
||||
sprintf(msg+strlen(msg), " asc=%2.2Xh", asc);
|
||||
if (ascq>=0)
|
||||
sprintf(msg+strlen(msg), " ascq=%2.2Xh", ascq);
|
||||
ret = libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002010f,
|
||||
LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, msg,0,0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002010f,
|
||||
LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH,
|
||||
scsi_msg,0,0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -33,5 +33,18 @@ int spc_setup_drive(struct burn_drive *d);
|
||||
int burn_scsi_setup_drive(struct burn_drive *d, int bus_no, int host_no,
|
||||
int channel_no, int target_no, int lun_no, int flag);
|
||||
|
||||
/* ts A61115 moved from sg-*.h */
|
||||
enum response { RETRY, FAIL };
|
||||
enum response scsi_error(struct burn_drive *, unsigned char *, int);
|
||||
|
||||
/* ts A61122 */
|
||||
enum response scsi_error_msg(struct burn_drive *d, unsigned char *sense,
|
||||
int senselen, char msg[161],
|
||||
int *key, int *asc, int *ascq);
|
||||
|
||||
/* ts A61030 */
|
||||
/* @param flag bit0=do report conditions which are considered not an error */
|
||||
int scsi_notify_error(struct burn_drive *, struct command *c,
|
||||
unsigned char *sense, int senselen, int flag);
|
||||
|
||||
#endif /*__SPC*/
|
||||
|
@ -115,6 +115,17 @@ struct burn_track *burn_track_create(void)
|
||||
t->pad = 1;
|
||||
t->entry = NULL;
|
||||
t->source = NULL;
|
||||
t->eos = 0;
|
||||
|
||||
/* ts A61101 */
|
||||
t->sourcecount = 0;
|
||||
t->writecount = 0;
|
||||
t->written_sectors = 0;
|
||||
|
||||
/* ts A61031 */
|
||||
t->open_ended = 0;
|
||||
t->track_data_done = 0;
|
||||
|
||||
t->postgap = 0;
|
||||
t->pregap1 = 0;
|
||||
t->pregap2 = 0;
|
||||
@ -318,6 +329,31 @@ int burn_track_get_sectors(struct burn_track *t)
|
||||
return sectors;
|
||||
}
|
||||
|
||||
/* ts A61031 */
|
||||
int burn_track_is_open_ended(struct burn_track *t)
|
||||
{
|
||||
return !!t->open_ended;
|
||||
}
|
||||
|
||||
/* ts A61101 : API function */
|
||||
int burn_track_get_counters(struct burn_track *t,
|
||||
off_t *read_bytes, off_t *written_bytes)
|
||||
{
|
||||
/*
|
||||
fprintf(stderr, "libburn_experimental: sizeof(off_t)=%d\n",
|
||||
sizeof(off_t));
|
||||
*/
|
||||
*read_bytes = t->sourcecount;
|
||||
*written_bytes = t->writecount;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ts A61031 */
|
||||
int burn_track_is_data_done(struct burn_track *t)
|
||||
{
|
||||
return !!t->track_data_done;
|
||||
}
|
||||
|
||||
int burn_track_get_shortage(struct burn_track *t)
|
||||
{
|
||||
int size;
|
||||
|
@ -31,6 +31,18 @@ struct burn_track
|
||||
struct burn_source *source;
|
||||
/** End of Source flag */
|
||||
int eos;
|
||||
|
||||
/* ts A61101 */
|
||||
off_t sourcecount;
|
||||
off_t writecount;
|
||||
off_t written_sectors;
|
||||
|
||||
/* ts A61031 */
|
||||
/** Source is of undefined length */
|
||||
int open_ended;
|
||||
/** End of open ended track flag : offset+payload+tail are delivered */
|
||||
int track_data_done;
|
||||
|
||||
/** The audio/data mode for the entry. Derived from control and
|
||||
possibly from reading the track's first sector. */
|
||||
int mode;
|
||||
@ -71,4 +83,10 @@ struct burn_disc
|
||||
|
||||
int burn_track_get_shortage(struct burn_track *t);
|
||||
|
||||
|
||||
/* ts A61031 : might go to libburn.h */
|
||||
int burn_track_is_open_ended(struct burn_track *t);
|
||||
int burn_track_is_data_done(struct burn_track *t);
|
||||
|
||||
|
||||
#endif /* BURN__STRUCTURE_H */
|
||||
|
@ -4,20 +4,16 @@
|
||||
#define __TRANSPORT
|
||||
|
||||
#include "libburn.h"
|
||||
#include "os.h"
|
||||
|
||||
#include <pthread.h>
|
||||
/* sg data structures */
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
|
||||
#define BUFFER_SIZE 65536/2
|
||||
/* see os.h for name of particular os-*.h where this is defined */
|
||||
#define BUFFER_SIZE BURN_OS_TRANSPORT_BUFFER_SIZE
|
||||
|
||||
#else /* __FreeBSD__ */
|
||||
|
||||
#define BUFFER_SIZE 65536
|
||||
|
||||
#endif /* ! __FreeBSD__ */
|
||||
|
||||
enum transfer_direction
|
||||
{ TO_DRIVE, FROM_DRIVE, NO_TRANSFER };
|
||||
@ -107,15 +103,10 @@ struct burn_drive
|
||||
int lun;
|
||||
char *devname;
|
||||
|
||||
#if defined(__FreeBSD__)
|
||||
struct cam_device* cam;
|
||||
#else
|
||||
int fd;
|
||||
|
||||
/* ts A60926 : trying to lock against growisofs /dev/srN, /dev/scdN */
|
||||
int sibling_count;
|
||||
int sibling_fds[LIBBURN_SG_MAX_SIBLINGS];
|
||||
#endif
|
||||
/* see os.h for name of particular os-*.h where this is defined */
|
||||
BURN_OS_TRANSPORT_DRIVE_ELEMENTS
|
||||
|
||||
|
||||
/* ts A60904 : ticket 62, contribution by elmom */
|
||||
/**
|
||||
@ -129,6 +120,10 @@ struct burn_drive
|
||||
enum burn_disc_status status;
|
||||
int erasable;
|
||||
volatile int released;
|
||||
|
||||
/* ts A61106 */
|
||||
int silent_on_scsi_error;
|
||||
|
||||
int nwa; /* next writeable address */
|
||||
int alba; /* absolute lba */
|
||||
int rlba; /* relative lba in section */
|
||||
@ -164,6 +159,7 @@ struct burn_drive
|
||||
void (*unlock) (struct burn_drive *);
|
||||
void (*eject) (struct burn_drive *);
|
||||
void (*load) (struct burn_drive *);
|
||||
int (*start_unit) (struct burn_drive *);
|
||||
void (*read_disc_info) (struct burn_drive *);
|
||||
void (*read_sectors) (struct burn_drive *,
|
||||
int start,
|
||||
@ -178,7 +174,7 @@ struct burn_drive
|
||||
void (*send_cue_sheet) (struct burn_drive *, struct cue_sheet *);
|
||||
void (*sync_cache) (struct burn_drive *);
|
||||
int (*get_erase_progress) (struct burn_drive *);
|
||||
int (*get_nwa) (struct burn_drive *);
|
||||
int (*get_nwa) (struct burn_drive *, int trackno, int *lba, int *nwa);
|
||||
|
||||
/* ts A61009 : removed d in favor of o->drive */
|
||||
/* void (*close_disc) (struct burn_drive * d,
|
||||
@ -189,6 +185,10 @@ struct burn_drive
|
||||
void (*close_disc) (struct burn_write_opts * o);
|
||||
void (*close_session) ( struct burn_write_opts * o);
|
||||
|
||||
/* ts A61029 */
|
||||
void (*close_track_session) ( struct burn_drive *d,
|
||||
int session, int track);
|
||||
|
||||
int (*test_unit_ready) (struct burn_drive * d);
|
||||
void (*probe_write_modes) (struct burn_drive * d);
|
||||
struct params params;
|
||||
|
318
libburn/write.c
318
libburn/write.c
@ -7,6 +7,18 @@
|
||||
/* #include <a ssert.h> */
|
||||
|
||||
|
||||
/* ts A61106 : Deliberate defect provocation macros
|
||||
DO NOT DEFINE THESE IF YOU WANT SUCCESSFUL TAO !
|
||||
#define Libburn_experimental_no_close_tracK 1
|
||||
#define Libburn_experimental_no_close_sessioN 1
|
||||
*/
|
||||
|
||||
/* ts A61114 : Highly experimental : try to achieve SAO on appendables
|
||||
THIS DOES NOT WORK YET !
|
||||
#define Libburn_sao_can_appenD 1
|
||||
*/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
@ -99,7 +111,7 @@ void type_to_form(int mode, unsigned char *ctladr, int *form)
|
||||
*form |= 0x40;
|
||||
}
|
||||
|
||||
int burn_write_flush(struct burn_write_opts *o)
|
||||
int burn_write_flush(struct burn_write_opts *o, struct burn_track *track)
|
||||
{
|
||||
struct burn_drive *d = o->drive;
|
||||
|
||||
@ -108,13 +120,115 @@ int burn_write_flush(struct burn_write_opts *o)
|
||||
err = d->write(d, d->nwa, d->buffer);
|
||||
if (err == BE_CANCELLED)
|
||||
return 0;
|
||||
/* A61101 */
|
||||
if(track != NULL) {
|
||||
track->writecount += d->buffer->bytes;
|
||||
track->written_sectors += d->buffer->sectors;
|
||||
}
|
||||
/* ts A61119 */
|
||||
d->progress.buffered_bytes += d->buffer->bytes;
|
||||
|
||||
d->nwa += d->buffer->sectors;
|
||||
d->buffer->bytes = 0;
|
||||
d->buffer->sectors = 0;
|
||||
}
|
||||
d->sync_cache(d);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* ts A61030 */
|
||||
int burn_write_close_track(struct burn_write_opts *o, struct burn_session *s,
|
||||
int tnum)
|
||||
{
|
||||
char msg[81];
|
||||
struct burn_drive *d;
|
||||
struct burn_track *t;
|
||||
int todo, step, cancelled, seclen;
|
||||
|
||||
/* ts A61106 */
|
||||
#ifdef Libburn_experimental_no_close_tracK
|
||||
return 1;
|
||||
#endif
|
||||
|
||||
d = o->drive;
|
||||
t = s->track[tnum];
|
||||
|
||||
/* ts A61103 : pad up track to minimum size of 600 sectors */
|
||||
if (t->written_sectors < 300) {
|
||||
todo = 300 - t->written_sectors;
|
||||
sprintf(msg,"Padding up track to minimum size (+ %d sectors)",
|
||||
todo);
|
||||
libdax_msgs_submit(libdax_messenger, o->drive->global_index,
|
||||
0x0002011a,
|
||||
LIBDAX_MSGS_SEV_NOTE, LIBDAX_MSGS_PRIO_HIGH, msg,0,0);
|
||||
step = BUFFER_SIZE / 4096; /* shall fit any sector size */
|
||||
if (step <= 0)
|
||||
step = 1;
|
||||
seclen = burn_sector_length(t->mode);
|
||||
if (seclen <= 0)
|
||||
seclen = 2048;
|
||||
memset(d->buffer, 0, sizeof(struct buffer));
|
||||
cancelled = d->cancel;
|
||||
for (; todo > 0; todo -= step) {
|
||||
if (step > todo)
|
||||
step = todo;
|
||||
d->buffer->bytes = step*seclen;
|
||||
d->buffer->sectors = step;
|
||||
d->cancel = 0;
|
||||
d->write(d, d->nwa, d->buffer);
|
||||
d->nwa += d->buffer->sectors;
|
||||
t->writecount += d->buffer->bytes;
|
||||
t->written_sectors += d->buffer->sectors;
|
||||
d->progress.buffered_bytes += d->buffer->bytes;
|
||||
}
|
||||
d->cancel = cancelled;
|
||||
}
|
||||
|
||||
/* ts A61102 */
|
||||
d->busy = BURN_DRIVE_CLOSING_TRACK;
|
||||
|
||||
sprintf(msg, "Closing track %2.2d", tnum+1);
|
||||
libdax_msgs_submit(libdax_messenger, o->drive->global_index,0x00020119,
|
||||
LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, msg,0,0);
|
||||
|
||||
/* MMC-1 mentions track number 0xFF for "the incomplete track",
|
||||
MMC-3 does not. I tried both. 0xFF was in effect when other
|
||||
bugs finally gave up and made way for readable tracks. */
|
||||
d->close_track_session(o->drive, 0, 0xff); /* tnum+1); */
|
||||
|
||||
/* ts A61102 */
|
||||
d->busy = BURN_DRIVE_WRITING;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* ts A61030 */
|
||||
int burn_write_close_session(struct burn_write_opts *o, struct burn_session *s)
|
||||
{
|
||||
|
||||
/* ts A61106 */
|
||||
#ifdef Libburn_experimental_no_close_sessioN
|
||||
return 1;
|
||||
#endif
|
||||
|
||||
libdax_msgs_submit(libdax_messenger, o->drive->global_index,0x00020119,
|
||||
LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH,
|
||||
"Closing session", 0, 0);
|
||||
|
||||
/* ts A61102 */
|
||||
o->drive->busy = BURN_DRIVE_CLOSING_SESSION;
|
||||
|
||||
o->drive->close_track_session(o->drive, 1, 0);
|
||||
|
||||
/* ts A61102 */
|
||||
o->drive->busy = BURN_DRIVE_WRITING;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* ts A60819:
|
||||
This is unused since about Feb 2006, icculus.org/burn CVS.
|
||||
The compiler complains. We shall please our compiler.
|
||||
@ -179,8 +293,10 @@ static int add_cue(struct cue_sheet *sheet, unsigned char ctladr,
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ts A61114: added parameter nwa */
|
||||
struct cue_sheet *burn_create_toc_entries(struct burn_write_opts *o,
|
||||
struct burn_session *session)
|
||||
struct burn_session *session,
|
||||
int nwa)
|
||||
{
|
||||
int i, m, s, f, form, pform, runtime = -150, ret;
|
||||
unsigned char ctladr;
|
||||
@ -193,6 +309,11 @@ struct cue_sheet *burn_create_toc_entries(struct burn_write_opts *o,
|
||||
|
||||
d = o->drive;
|
||||
|
||||
#ifdef Libburn_sao_can_appenD
|
||||
if (d->status == BURN_DISC_APPENDABLE)
|
||||
runtime = nwa-150;
|
||||
#endif
|
||||
|
||||
sheet = malloc(sizeof(struct cue_sheet));
|
||||
|
||||
/* ts A61009 : react on failures of malloc(), add_cue_sheet()
|
||||
@ -228,11 +349,20 @@ struct cue_sheet *burn_create_toc_entries(struct burn_write_opts *o,
|
||||
/* ts A61009 */
|
||||
/* a ssert(d->toc_entry == NULL); */
|
||||
if (d->toc_entry != NULL) {
|
||||
|
||||
/* ts A61109 : this happens with appendable CDs
|
||||
>>> Open question: is the existing TOC needed ? */
|
||||
|
||||
/* ts A61109 : for non-SAO, this sheet is thrown away later */
|
||||
free((char *) d->toc_entry);
|
||||
|
||||
/*
|
||||
libdax_msgs_submit(libdax_messenger,
|
||||
d->global_index, 0x00020117,
|
||||
LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH,
|
||||
"toc_entry of drive is already in use", 0, 0);
|
||||
goto failed;
|
||||
*/
|
||||
}
|
||||
|
||||
d->toc_entry = calloc(d->toc_entries, sizeof(struct burn_toc_entry));
|
||||
@ -302,8 +432,17 @@ XXX this is untested :)
|
||||
*/
|
||||
if (!tar[i]->pad) {
|
||||
rem += burn_track_get_shortage(tar[i]);
|
||||
if (i +1 != ntr)
|
||||
tar[i]->source->next = tar[i+1]->source;
|
||||
|
||||
/* ts A61101 : I doubt that linking would yield a
|
||||
desireable effect. With TAO it is
|
||||
counterproductive in any way.
|
||||
*/
|
||||
if (o->write_type == BURN_WRITE_TAO)
|
||||
tar[i]->source->next = NULL;
|
||||
else
|
||||
|
||||
if (i +1 != ntr)
|
||||
tar[i]->source->next = tar[i+1]->source;
|
||||
} else if (rem) {
|
||||
rem = 0;
|
||||
runtime++;
|
||||
@ -417,7 +556,7 @@ int burn_write_session(struct burn_write_opts *o, struct burn_session *s)
|
||||
{
|
||||
struct burn_drive *d = o->drive;
|
||||
struct burn_track *prev = NULL, *next = NULL;
|
||||
int i;
|
||||
int i, ret;
|
||||
|
||||
d->rlba = 0;
|
||||
burn_print(1, " writing a session\n");
|
||||
@ -430,9 +569,15 @@ int burn_write_session(struct burn_write_opts *o, struct burn_session *s)
|
||||
next = NULL;
|
||||
|
||||
if (!burn_write_track(o, s, i))
|
||||
return 0;
|
||||
{ ret = 0; goto ex; }
|
||||
}
|
||||
return 1;
|
||||
|
||||
/* ts A61103 */
|
||||
ret = 1;
|
||||
ex:;
|
||||
if (o->write_type == BURN_WRITE_TAO)
|
||||
burn_write_close_session(o, s);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int burn_write_track(struct burn_write_opts *o, struct burn_session *s,
|
||||
@ -440,13 +585,18 @@ int burn_write_track(struct burn_write_opts *o, struct burn_session *s,
|
||||
{
|
||||
struct burn_track *t = s->track[tnum];
|
||||
struct burn_drive *d = o->drive;
|
||||
int i, tmp = 0;
|
||||
int i, tmp = 0, open_ended = 0, ret= 0, nwa, lba;
|
||||
int sectors;
|
||||
char msg[80];
|
||||
|
||||
d->rlba = -150;
|
||||
|
||||
/* XXX for tao, we don't want the pregaps but still want post? */
|
||||
if (o->write_type != BURN_WRITE_TAO) {
|
||||
|
||||
/* ts A61102 */
|
||||
d->busy = BURN_DRIVE_WRITING_PREGAP;
|
||||
|
||||
if (t->pregap1)
|
||||
d->rlba += 75;
|
||||
if (t->pregap2)
|
||||
@ -462,20 +612,36 @@ int burn_write_track(struct burn_write_opts *o, struct burn_session *s,
|
||||
for (i = 0; i < 75; i++)
|
||||
if (!sector_pregap(o, t->entry->point,
|
||||
pt->entry->control, pt->mode))
|
||||
return 0;
|
||||
{ ret = 0; goto ex; }
|
||||
}
|
||||
if (t->pregap2)
|
||||
for (i = 0; i < 150; i++)
|
||||
if (!sector_pregap(o, t->entry->point,
|
||||
t->entry->control, t->mode))
|
||||
return 0;
|
||||
{ ret = 0; goto ex; }
|
||||
} else {
|
||||
o->control = t->entry->control;
|
||||
d->send_write_parameters(d, o);
|
||||
|
||||
/* ts A61103 */
|
||||
ret = d->get_nwa(d, -1, &lba, &nwa);
|
||||
sprintf(msg,
|
||||
"pre-track %2.2d : get_nwa(%d), ret= %d , d->nwa= %d\n",
|
||||
tnum+1, nwa, ret, d->nwa);
|
||||
libdax_msgs_submit(libdax_messenger, d->global_index, 0x000002,
|
||||
LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO,
|
||||
msg,0,0);
|
||||
if (nwa > d->nwa)
|
||||
d->nwa = nwa;
|
||||
}
|
||||
|
||||
/* user data */
|
||||
|
||||
/* ts A61102 */
|
||||
d->busy = BURN_DRIVE_WRITING;
|
||||
|
||||
sectors = burn_track_get_sectors(t);
|
||||
open_ended = burn_track_is_open_ended(t);
|
||||
|
||||
/* Update progress */
|
||||
d->progress.start_sector = d->nwa;
|
||||
@ -488,10 +654,11 @@ int burn_write_track(struct burn_write_opts *o, struct burn_session *s,
|
||||
|
||||
burn_print(12, "track %d is %d sectors long\n", tnum, sectors);
|
||||
|
||||
/* ts A61030 : this cannot happen. tnum is always < s->tracks */
|
||||
if (tnum == s->tracks)
|
||||
tmp = sectors > 150 ? 150 : sectors;
|
||||
|
||||
for (i = 0; i < sectors - tmp; i++) {
|
||||
for (i = 0; open_ended || i < sectors - tmp; i++) {
|
||||
|
||||
/* ts A61023 : http://libburn.pykix.org/ticket/14
|
||||
From time to time inquire drive buffer */
|
||||
@ -499,12 +666,24 @@ int burn_write_track(struct burn_write_opts *o, struct burn_session *s,
|
||||
d->read_buffer_capacity(d);
|
||||
|
||||
if (!sector_data(o, t, 0))
|
||||
return 0;
|
||||
{ ret = 0; goto ex; }
|
||||
|
||||
/* ts A61031 */
|
||||
if (open_ended) {
|
||||
d->progress.sectors = sectors = i;
|
||||
if (burn_track_is_data_done(t))
|
||||
break;
|
||||
}
|
||||
|
||||
/* update current progress */
|
||||
d->progress.sector++;
|
||||
}
|
||||
for (; i < sectors; i++) {
|
||||
|
||||
/* ts A61030: program execution never gets to this point */
|
||||
fprintf(stderr,"LIBBURN_DEBUG: TNUM=%d TRACKS=%d TMP=%d\n",
|
||||
tnum, s->tracks, tmp);
|
||||
|
||||
burn_print(1, "last track, leadout prep\n");
|
||||
|
||||
/* ts A61023 */
|
||||
@ -512,7 +691,7 @@ int burn_write_track(struct burn_write_opts *o, struct burn_session *s,
|
||||
d->read_buffer_capacity(d);
|
||||
|
||||
if (!sector_data(o, t, 1))
|
||||
return 0;
|
||||
{ ret = 0; goto ex; }
|
||||
|
||||
/* update progress */
|
||||
d->progress.sector++;
|
||||
@ -522,23 +701,43 @@ int burn_write_track(struct burn_write_opts *o, struct burn_session *s,
|
||||
for (i = 0; i < 150; i++)
|
||||
if (!sector_postgap(o, t->entry->point, t->entry->control,
|
||||
t->mode))
|
||||
return 0;
|
||||
{ ret = 0; goto ex; }
|
||||
i = t->offset;
|
||||
if (o->write_type == BURN_WRITE_SAO) {
|
||||
if (d->buffer->bytes) {
|
||||
int err;
|
||||
err = d->write(d, d->nwa, d->buffer);
|
||||
if (err == BE_CANCELLED)
|
||||
return 0;
|
||||
{ ret = 0; goto ex; }
|
||||
|
||||
/* A61101 : probably this is not all payload data */
|
||||
/* A61108 : but audio count is short without this */
|
||||
t->writecount += d->buffer->bytes;
|
||||
t->written_sectors += d->buffer->sectors;
|
||||
d->progress.buffered_bytes += d->buffer->bytes;
|
||||
|
||||
d->nwa += d->buffer->sectors;
|
||||
d->buffer->bytes = 0;
|
||||
d->buffer->sectors = 0;
|
||||
}
|
||||
}
|
||||
if (o->write_type == BURN_WRITE_TAO)
|
||||
if (!burn_write_flush(o))
|
||||
return 0;
|
||||
return 1;
|
||||
|
||||
/* ts A61103 */
|
||||
ret = 1;
|
||||
ex:;
|
||||
if (o->write_type == BURN_WRITE_TAO) {
|
||||
|
||||
/* ts A61103 */
|
||||
/* >>> if cancelled: ensure that at least 600 kB get written */
|
||||
|
||||
if (!burn_write_flush(o, t))
|
||||
ret = 0;
|
||||
|
||||
/* ts A61030 */
|
||||
if (burn_write_close_track(o, s, tnum) <= 0)
|
||||
ret = 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* ts A61009 */
|
||||
@ -568,8 +767,8 @@ void burn_disc_write_sync(struct burn_write_opts *o, struct burn_disc *disc)
|
||||
struct burn_drive *d = o->drive;
|
||||
struct buffer buf;
|
||||
struct burn_track *lt;
|
||||
int first = 1, i;
|
||||
int res;
|
||||
int first = 1, i, ret, lba, nwa = 0;
|
||||
char msg[80];
|
||||
|
||||
/* ts A60924 : libburn/message.c gets obsoleted
|
||||
burn_message_clear_queue();
|
||||
@ -586,15 +785,24 @@ void burn_disc_write_sync(struct burn_write_opts *o, struct burn_disc *disc)
|
||||
/* Apparently some drives require this command to be sent, and a few drives
|
||||
return crap. so we send the command, then ignore the result.
|
||||
*/
|
||||
res = d->get_nwa(d);
|
||||
/* printf("ignored nwa: %d\n", res);*/
|
||||
|
||||
d->alba = d->start_lba;
|
||||
/* ts A61107 : moved up send_write_parameters because LG GSA-4082B
|
||||
seems to dislike get_nwa() in advance */
|
||||
d->alba = d->start_lba; /* ts A61114: this looks senseless */
|
||||
d->nwa = d->alba;
|
||||
if (o->write_type == BURN_WRITE_TAO) {
|
||||
nwa = 0; /* get_nwa() will be called in burn_track() */
|
||||
} else {
|
||||
|
||||
if (o->write_type != BURN_WRITE_TAO)
|
||||
d->send_write_parameters(d, o);
|
||||
|
||||
ret = d->get_nwa(d, -1, &lba, &nwa);
|
||||
sprintf(msg, "Inquired nwa: %d (ret=%d)", nwa, ret);
|
||||
libdax_msgs_submit(libdax_messenger, d->global_index,
|
||||
0x00000002,
|
||||
LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO,
|
||||
msg,0,0);
|
||||
}
|
||||
|
||||
/* init progress before showing the state */
|
||||
d->progress.session = 0;
|
||||
d->progress.sessions = disc->sessions;
|
||||
@ -612,6 +820,8 @@ return crap. so we send the command, then ignore the result.
|
||||
/* ts A61023 */
|
||||
d->progress.buffer_capacity = 0;
|
||||
d->progress.buffer_available = 0;
|
||||
d->progress.buffered_bytes = 0;
|
||||
d->progress.buffer_min_fill = 0xffffffff;
|
||||
|
||||
d->busy = BURN_DRIVE_WRITING;
|
||||
|
||||
@ -620,7 +830,8 @@ return crap. so we send the command, then ignore the result.
|
||||
d->progress.session = i;
|
||||
d->progress.tracks = disc->session[i]->tracks;
|
||||
|
||||
sheet = burn_create_toc_entries(o, disc->session[i]);
|
||||
/* ts A61114: added parameter nwa */
|
||||
sheet = burn_create_toc_entries(o, disc->session[i], nwa);
|
||||
|
||||
/* ts A61009 */
|
||||
if (sheet == NULL)
|
||||
@ -636,8 +847,39 @@ return crap. so we send the command, then ignore the result.
|
||||
goto fail;
|
||||
} else {
|
||||
if (first) {
|
||||
d->nwa = -150;
|
||||
d->alba = -150;
|
||||
|
||||
/* ts A61030 : 0 made the burner take data. */
|
||||
/* ts A61103 : Meanwhile d->nwa is updated in
|
||||
burn_write_track() */
|
||||
if(o->write_type == BURN_WRITE_TAO) {
|
||||
d->nwa= d->alba = 0;
|
||||
} else {
|
||||
|
||||
#ifdef Libburn_sao_can_appenD
|
||||
/* ts A61114: address for d->write() */
|
||||
if (d->status == BURN_DISC_APPENDABLE
|
||||
&& o->write_type == BURN_WRITE_SAO) {
|
||||
d->nwa = d->alba = nwa-150;
|
||||
|
||||
sprintf(msg,
|
||||
"SAO appendable d->nwa= %d\n", d->nwa);
|
||||
libdax_msgs_submit(
|
||||
libdax_messenger, d->global_index, 0x000002,
|
||||
LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO,
|
||||
msg,0,0);
|
||||
|
||||
} else {
|
||||
d->nwa = -150;
|
||||
d->alba = -150;
|
||||
}
|
||||
#else
|
||||
d->nwa = -150;
|
||||
d->alba = -150;
|
||||
#endif /* ! Libburn_sao_can_appenD */
|
||||
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
d->nwa += 4500;
|
||||
d->alba += 4500;
|
||||
@ -652,8 +894,13 @@ return crap. so we send the command, then ignore the result.
|
||||
lt->mode))
|
||||
goto fail;
|
||||
} else {
|
||||
if (!burn_write_flush(o))
|
||||
goto fail;
|
||||
|
||||
/* ts A61030 */
|
||||
if (o->write_type != BURN_WRITE_TAO)
|
||||
|
||||
if (!burn_write_flush(o, NULL))
|
||||
goto fail;
|
||||
|
||||
d->nwa += first ? 6750 : 2250;
|
||||
d->alba += first ? 6750 : 2250;
|
||||
}
|
||||
@ -665,15 +912,18 @@ return crap. so we send the command, then ignore the result.
|
||||
d->progress.start_sector = 0;
|
||||
d->progress.sectors = 0;
|
||||
}
|
||||
if (o->write_type != BURN_WRITE_SAO)
|
||||
if (!burn_write_flush(o))
|
||||
|
||||
/* ts A61030: extended skipping of flush to TAO: session is closed */
|
||||
if (o->write_type != BURN_WRITE_SAO && o->write_type != BURN_WRITE_TAO)
|
||||
if (!burn_write_flush(o, NULL))
|
||||
goto fail;
|
||||
|
||||
sleep(1);
|
||||
|
||||
burn_print(1, "done\n");
|
||||
d->busy = BURN_DRIVE_IDLE;
|
||||
|
||||
/* ts A61012 : This return was traditionally missing, a suspect this
|
||||
/* ts A61012 : This return was traditionally missing. I suspect this
|
||||
to have caused Cdrskin_eject() failures */
|
||||
return;
|
||||
|
||||
|
@ -9,7 +9,8 @@ struct burn_write_opts;
|
||||
struct burn_disc;
|
||||
|
||||
struct cue_sheet *burn_create_toc_entries(struct burn_write_opts *o,
|
||||
struct burn_session *session);
|
||||
struct burn_session *session,
|
||||
int nwa);
|
||||
int burn_sector_length(int trackmode);
|
||||
int burn_subcode_length(int trackmode);
|
||||
|
||||
@ -24,6 +25,11 @@ int burn_write_leadout(struct burn_write_opts *o,
|
||||
int burn_write_session(struct burn_write_opts *o, struct burn_session *s);
|
||||
int burn_write_track(struct burn_write_opts *o, struct burn_session *s,
|
||||
int tnum);
|
||||
int burn_write_flush(struct burn_write_opts *o);
|
||||
int burn_write_flush(struct burn_write_opts *o, struct burn_track *track);
|
||||
|
||||
/* ts A61030 : necessary for TAO */
|
||||
int burn_write_close_track(struct burn_write_opts *o, struct burn_session *s,
|
||||
int tnum);
|
||||
int burn_write_close_session(struct burn_write_opts *o,struct burn_session *s);
|
||||
|
||||
#endif /* BURN__WRITE_H */
|
||||
|
@ -1,11 +0,0 @@
|
||||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@
|
||||
|
||||
Name: libisofs
|
||||
Description: ISO9660 filesystem creation library
|
||||
Version: @VERSION@
|
||||
Requires:
|
||||
Libs: -L${libdir} -lisofs
|
||||
Cflags: -I${includedir}/libburn
|
@ -1,4 +0,0 @@
|
||||
all clean:
|
||||
$(MAKE) -C .. -$(MAKEFLAGS) $@
|
||||
|
||||
.PHONY: all clean
|
@ -1,49 +0,0 @@
|
||||
pkgconfigdir=$(libdir)/pkgconfig
|
||||
libincludedir=$(includedir)/libburn
|
||||
|
||||
##bin_PROGRAMS = test
|
||||
|
||||
lib_LTLIBRARIES = libisofs.la
|
||||
|
||||
libisofs_la_SOURCES = \
|
||||
tree.h \
|
||||
tree.c \
|
||||
volume.h \
|
||||
volume.c \
|
||||
util.h \
|
||||
util.c \
|
||||
ecma119.c \
|
||||
ecma119.h \
|
||||
ecma119_tree.c \
|
||||
ecma119_tree.h \
|
||||
susp.h \
|
||||
susp.c \
|
||||
rockridge.h \
|
||||
rockridge.c \
|
||||
joliet.c \
|
||||
joliet.h
|
||||
|
||||
libinclude_HEADERS = libisofs.h
|
||||
|
||||
##test_SOURCES = test.c
|
||||
##test_LDADD = libisofs.la
|
||||
|
||||
##noinst_PROGRAMS = test
|
||||
##test_SOURCES = test.c
|
||||
##test_LDADD = $(libisofs_la_OBJECTS)
|
||||
|
||||
##INCLUDES = -I../burn/libburn
|
||||
|
||||
## ========================================================================= ##
|
||||
indent_files = $(libisofs_la_SOURCES)
|
||||
|
||||
indent: $(indent_files)
|
||||
indent -bad -bap -nbbb -nbbo -nbc -bli0 -br -bls \
|
||||
-cdw -ce -cli0 -ncs -nbfda -i8 -l79 -lc79 \
|
||||
-lp -saf -sai -nprs -npsl -saw -sob -ss -ut \
|
||||
-sbi0 -nsc -ts8 -npcs -ncdb -fca \
|
||||
$^
|
||||
|
||||
.PHONY: indent
|
||||
|
||||
## ========================================================================= ##
|
@ -1,694 +0,0 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
/* vim: set noet ts=8 sts=8 sw=8 : */
|
||||
|
||||
#include <string.h>
|
||||
#include <wchar.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <assert.h>
|
||||
#include <err.h>
|
||||
|
||||
#include "ecma119.h"
|
||||
#include "ecma119_tree.h"
|
||||
#include "susp.h"
|
||||
#include "rockridge.h"
|
||||
#include "joliet.h"
|
||||
#include "volume.h"
|
||||
#include "tree.h"
|
||||
#include "util.h"
|
||||
#include "libisofs.h"
|
||||
#include "libburn/libburn.h"
|
||||
|
||||
/* burn-source compatible stuff */
|
||||
static int
|
||||
bs_read(struct burn_source *bs, unsigned char *buf, int size);
|
||||
static off_t
|
||||
bs_get_size(struct burn_source *bs);
|
||||
static void
|
||||
bs_free_data(struct burn_source *bs);
|
||||
|
||||
typedef void (*write_fn)(struct ecma119_write_target*, uint8_t*);
|
||||
|
||||
/* return true if the given state is only required for Joliet volumes */
|
||||
static int
|
||||
is_joliet_state(enum ecma119_write_state);
|
||||
|
||||
static void
|
||||
next_state(struct ecma119_write_target *t);
|
||||
|
||||
/* write t->state_data to the buf, one block at a time */
|
||||
static void
|
||||
write_data_chunk(struct ecma119_write_target *t, uint8_t *buf);
|
||||
|
||||
/* writing functions. All these functions assume the buf is large enough */
|
||||
static void
|
||||
write_pri_vol_desc(struct ecma119_write_target *t, uint8_t *buf);
|
||||
static void
|
||||
write_vol_desc_terminator(struct ecma119_write_target *t, uint8_t *buf);
|
||||
static void
|
||||
write_path_table(struct ecma119_write_target *t, int l_type, uint8_t *buf);
|
||||
static void
|
||||
write_l_path_table(struct ecma119_write_target *t, uint8_t *buf);
|
||||
static void
|
||||
write_m_path_table(struct ecma119_write_target *t, uint8_t *buf);
|
||||
static void
|
||||
write_one_dir_record(struct ecma119_write_target *t,
|
||||
struct ecma119_tree_node *dir,
|
||||
int file_id,
|
||||
uint8_t *buf);
|
||||
static void
|
||||
write_one_dir(struct ecma119_write_target *t,
|
||||
struct ecma119_tree_node *dir,
|
||||
uint8_t *buf);
|
||||
static void
|
||||
write_dirs(struct ecma119_write_target *t, uint8_t *buf);
|
||||
|
||||
/* wrapper functions for writing */
|
||||
static void wr_system_area(struct ecma119_write_target*, uint8_t*);
|
||||
static void wr_pri_vol_desc(struct ecma119_write_target*, uint8_t*);
|
||||
static void wr_vol_desc_term(struct ecma119_write_target*, uint8_t*);
|
||||
static void wr_l_path_table(struct ecma119_write_target*, uint8_t*);
|
||||
static void wr_m_path_table(struct ecma119_write_target*, uint8_t*);
|
||||
static void wr_dir_records(struct ecma119_write_target*, uint8_t*);
|
||||
static void wr_files(struct ecma119_write_target*, uint8_t*);
|
||||
|
||||
static const write_fn writers[] =
|
||||
{
|
||||
NULL,
|
||||
wr_system_area,
|
||||
wr_pri_vol_desc,
|
||||
joliet_wr_sup_vol_desc,
|
||||
wr_vol_desc_term,
|
||||
wr_l_path_table,
|
||||
wr_m_path_table,
|
||||
joliet_wr_l_path_table,
|
||||
joliet_wr_m_path_table,
|
||||
wr_dir_records,
|
||||
joliet_wr_dir_records,
|
||||
wr_files
|
||||
};
|
||||
|
||||
/* When a writer is created, we
|
||||
* 1) create an ecma119 tree
|
||||
* 2) add SUSP fields (if necessary)
|
||||
* 3) calculate the size and position of all nodes in the tree
|
||||
* 4) finalize SUSP fields (if necessary)
|
||||
*/
|
||||
|
||||
static void
|
||||
add_susp_fields_rec(struct ecma119_write_target *t,
|
||||
struct ecma119_tree_node *node)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (!node->iso_self)
|
||||
return;
|
||||
|
||||
rrip_add_PX(t, node);
|
||||
rrip_add_NM(t, node);
|
||||
rrip_add_TF(t, node);
|
||||
if (node->iso_self->attrib.st_rdev)
|
||||
rrip_add_PN(t, node);
|
||||
if (S_ISLNK(node->iso_self->attrib.st_mode))
|
||||
rrip_add_SL(t, node);
|
||||
if (node->type == ECMA119_FILE && node->file.real_me)
|
||||
rrip_add_CL(t, node);
|
||||
if (node->type == ECMA119_DIR
|
||||
&& node->dir.real_parent != node->parent) {
|
||||
rrip_add_RE(t, node);
|
||||
rrip_add_PL(t, node);
|
||||
}
|
||||
susp_add_CE(t, node);
|
||||
|
||||
if (node->type == ECMA119_DIR) {
|
||||
for (i = 0; i < node->dir.nchildren; i++) {
|
||||
add_susp_fields_rec(t, node->dir.children[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
add_susp_fields(struct ecma119_write_target *t)
|
||||
{
|
||||
susp_add_SP(t, t->root);
|
||||
rrip_add_ER(t, t->root);
|
||||
add_susp_fields_rec(t, t->root);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill out the dir.len and dir.CE_len fields for each
|
||||
* ecma119_tree_node that is a directory. Also calculate the total number of
|
||||
* directories and the number of files for which we need to write out data.
|
||||
* (dirlist_len and filelist_len)
|
||||
*/
|
||||
static void
|
||||
calc_dir_size(struct ecma119_write_target *t,
|
||||
struct ecma119_tree_node *dir)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
assert(dir->type == ECMA119_DIR);
|
||||
|
||||
t->dirlist_len++;
|
||||
dir->dir.len = 34 + dir->dir.self_susp.non_CE_len
|
||||
+ 34 + dir->dir.parent_susp.non_CE_len;
|
||||
dir->dir.CE_len = dir->dir.self_susp.CE_len
|
||||
+ dir->dir.parent_susp.CE_len;
|
||||
for (i = 0; i < dir->dir.nchildren; i++) {
|
||||
struct ecma119_tree_node *ch = dir->dir.children[i];
|
||||
|
||||
dir->dir.len += ch->dirent_len + ch->susp.non_CE_len;
|
||||
dir->dir.CE_len += ch->susp.CE_len;
|
||||
}
|
||||
t->total_dir_size += round_up(dir->dir.len + dir->dir.CE_len,
|
||||
t->block_size);
|
||||
|
||||
for (i = 0; i < dir->dir.nchildren; i++) {
|
||||
struct ecma119_tree_node *ch = dir->dir.children[i];
|
||||
struct iso_tree_node *iso = ch->iso_self;
|
||||
if (ch->type == ECMA119_DIR) {
|
||||
calc_dir_size(t, ch);
|
||||
} else if (iso && iso->attrib.st_size
|
||||
&& iso->loc.type == LIBISO_FILESYS
|
||||
&& iso->loc.path) {
|
||||
t->filelist_len++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill out the block field in each ecma119_tree_node that is a directory and
|
||||
* fill out t->dirlist.
|
||||
*/
|
||||
static void
|
||||
calc_dir_pos(struct ecma119_write_target *t,
|
||||
struct ecma119_tree_node *dir)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
assert(dir->type == ECMA119_DIR);
|
||||
|
||||
/* we don't need to set iso_self->block since each tree writes
|
||||
* its own directories */
|
||||
dir->block = t->curblock;
|
||||
t->curblock += div_up(dir->dir.len + dir->dir.CE_len, t->block_size);
|
||||
t->dirlist[t->curfile++] = dir;
|
||||
for (i = 0; i < dir->dir.nchildren; i++) {
|
||||
struct ecma119_tree_node *ch = dir->dir.children[i];
|
||||
if (ch->type == ECMA119_DIR)
|
||||
calc_dir_pos(t, ch);
|
||||
}
|
||||
|
||||
/* reset curfile when we're finished */
|
||||
if (!dir->parent) {
|
||||
t->curfile = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill out the block field for each ecma119_tree_node that is a file and fill
|
||||
* out t->filelist.
|
||||
*/
|
||||
static void
|
||||
calc_file_pos(struct ecma119_write_target *t,
|
||||
struct ecma119_tree_node *dir)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
assert(dir->type == ECMA119_DIR);
|
||||
|
||||
for (i = 0; i < dir->dir.nchildren; i++) {
|
||||
struct ecma119_tree_node *ch = dir->dir.children[i];
|
||||
if (ch->type == ECMA119_FILE && ch->iso_self) {
|
||||
struct iso_tree_node *iso = ch->iso_self;
|
||||
off_t size = iso->attrib.st_size;
|
||||
|
||||
iso->block = ch->block = t->curblock;
|
||||
t->curblock += div_up(size, t->block_size);
|
||||
if (size && iso->loc.type == LIBISO_FILESYS
|
||||
&& iso->loc.path)
|
||||
t->filelist[t->curfile++] = ch;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < dir->dir.nchildren; i++) {
|
||||
struct ecma119_tree_node *ch = dir->dir.children[i];
|
||||
if (ch->type == ECMA119_DIR)
|
||||
calc_file_pos(t, ch);
|
||||
}
|
||||
|
||||
/* reset curfile when we're finished */
|
||||
if (!dir->parent) {
|
||||
t->curfile = 0;
|
||||
}
|
||||
}
|
||||
|
||||
struct ecma119_write_target*
|
||||
ecma119_target_new(struct iso_volset *volset,
|
||||
int volnum,
|
||||
int level,
|
||||
int flags)
|
||||
{
|
||||
struct ecma119_write_target *t =
|
||||
calloc(1, sizeof(struct ecma119_write_target));
|
||||
size_t i, j, cur;
|
||||
struct iso_tree_node *iso_root = volset->volume[volnum]->root;
|
||||
|
||||
volset->refcount++;
|
||||
t->root = ecma119_tree_create(t, iso_root);
|
||||
t->joliet = (flags & ECMA119_JOLIET) ? 1 : 0;
|
||||
if (t->joliet)
|
||||
t->joliet_root = joliet_tree_create(t, iso_root);
|
||||
t->volset = volset;
|
||||
t->volnum = volnum;
|
||||
t->now = time(NULL);
|
||||
|
||||
t->rockridge = (flags & ECMA119_ROCKRIDGE) ? 1 : 0;
|
||||
t->iso_level = level;
|
||||
t->block_size = 2048;
|
||||
|
||||
if (t->rockridge)
|
||||
add_susp_fields(t);
|
||||
calc_dir_size(t, t->root);
|
||||
if (t->joliet) {
|
||||
joliet_calc_dir_size(t, t->joliet_root);
|
||||
t->pathlist_joliet = calloc(1, sizeof(void*) * t->dirlist_len);
|
||||
t->dirlist_joliet = calloc(1, sizeof(void*) * t->dirlist_len);
|
||||
}
|
||||
|
||||
t->dirlist = calloc(1, sizeof(void*) * t->dirlist_len);
|
||||
t->pathlist = calloc(1, sizeof(void*) * t->dirlist_len);
|
||||
t->filelist = calloc(1, sizeof(void*) * t->filelist_len);
|
||||
|
||||
/* fill out the pathlist */
|
||||
t->pathlist[0] = t->root;
|
||||
t->path_table_size = 10; /* root directory record */
|
||||
cur = 1;
|
||||
for (i = 0; i < t->dirlist_len; i++) {
|
||||
struct ecma119_tree_node *dir = t->pathlist[i];
|
||||
for (j = 0; j < dir->dir.nchildren; j++) {
|
||||
struct ecma119_tree_node *ch = dir->dir.children[j];
|
||||
if (ch->type == ECMA119_DIR) {
|
||||
size_t len = 8 + strlen(ch->name);
|
||||
t->pathlist[cur++] = ch;
|
||||
t->path_table_size += len + len % 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
t->curblock = 16 /* system area */
|
||||
+ 1 /* volume desc */
|
||||
+ 1; /* volume desc terminator */
|
||||
|
||||
if (t->joliet) /* supplementary vol desc */
|
||||
t->curblock += div_up (2048, t->block_size);
|
||||
|
||||
t->l_path_table_pos = t->curblock;
|
||||
t->curblock += div_up(t->path_table_size, t->block_size);
|
||||
t->m_path_table_pos = t->curblock;
|
||||
t->curblock += div_up(t->path_table_size, t->block_size);
|
||||
if (t->joliet) {
|
||||
joliet_prepare_path_tables(t);
|
||||
t->l_path_table_pos_joliet = t->curblock;
|
||||
t->curblock += div_up(t->path_table_size_joliet, t->block_size);
|
||||
t->m_path_table_pos_joliet = t->curblock;
|
||||
t->curblock += div_up(t->path_table_size_joliet, t->block_size);
|
||||
}
|
||||
|
||||
calc_dir_pos(t, t->root);
|
||||
if (t->joliet)
|
||||
joliet_calc_dir_pos(t, t->joliet_root);
|
||||
calc_file_pos(t, t->root);
|
||||
if (t->joliet)
|
||||
joliet_update_file_pos (t, t->joliet_root);
|
||||
|
||||
if (t->rockridge) {
|
||||
susp_finalize(t, t->root);
|
||||
rrip_finalize(t, t->root);
|
||||
}
|
||||
|
||||
t->total_size = t->curblock * t->block_size;
|
||||
t->vol_space_size = t->curblock;
|
||||
|
||||
/* prepare for writing */
|
||||
t->curblock = 0;
|
||||
t->state = ECMA119_WRITE_SYSTEM_AREA;
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
static int
|
||||
is_joliet_state(enum ecma119_write_state state)
|
||||
{
|
||||
return state == ECMA119_WRITE_SUP_VOL_DESC_JOLIET
|
||||
|| state == ECMA119_WRITE_L_PATH_TABLE_JOLIET
|
||||
|| state == ECMA119_WRITE_M_PATH_TABLE_JOLIET
|
||||
|| state == ECMA119_WRITE_DIR_RECORDS_JOLIET;
|
||||
}
|
||||
|
||||
static void
|
||||
next_state(struct ecma119_write_target *t)
|
||||
{
|
||||
t->state++;
|
||||
while (!t->joliet && is_joliet_state(t->state))
|
||||
t->state++;
|
||||
|
||||
printf ("now in state %d, curblock=%d\n", (int)t->state, (int)t->curblock);
|
||||
}
|
||||
|
||||
static void
|
||||
wr_system_area(struct ecma119_write_target *t, uint8_t *buf)
|
||||
{
|
||||
memset(buf, 0, t->block_size);
|
||||
if (t->curblock == 15) {
|
||||
next_state(t);
|
||||
}
|
||||
}
|
||||
static void
|
||||
wr_pri_vol_desc(struct ecma119_write_target *t, uint8_t *buf)
|
||||
{
|
||||
ecma119_start_chunking(t, write_pri_vol_desc, 2048, buf);
|
||||
}
|
||||
|
||||
static void
|
||||
wr_vol_desc_term(struct ecma119_write_target *t, uint8_t *buf)
|
||||
{
|
||||
ecma119_start_chunking(t, write_vol_desc_terminator, 2048, buf);
|
||||
}
|
||||
|
||||
static void
|
||||
wr_l_path_table(struct ecma119_write_target *t, uint8_t *buf)
|
||||
{
|
||||
ecma119_start_chunking(t, write_l_path_table, t->path_table_size, buf);
|
||||
}
|
||||
|
||||
static void
|
||||
wr_m_path_table(struct ecma119_write_target *t, uint8_t *buf)
|
||||
{
|
||||
ecma119_start_chunking(t, write_m_path_table, t->path_table_size, buf);
|
||||
}
|
||||
|
||||
static void
|
||||
wr_dir_records(struct ecma119_write_target *t, uint8_t *buf)
|
||||
{
|
||||
ecma119_start_chunking(t, write_dirs, t->total_dir_size, buf);
|
||||
}
|
||||
|
||||
static void
|
||||
wr_files(struct ecma119_write_target *t, uint8_t *buf)
|
||||
{
|
||||
struct state_files *f_st = &t->state_files;
|
||||
size_t nread;
|
||||
struct ecma119_tree_node *f = t->filelist[f_st->file];
|
||||
const char *path = f->iso_self->loc.path;
|
||||
|
||||
if (!f_st->fd) {
|
||||
f_st->data_len = f->iso_self->attrib.st_size;
|
||||
f_st->fd = fopen(path, "r");
|
||||
if (!f_st->fd)
|
||||
err(1, "couldn't open %s for reading", path);
|
||||
assert(t->curblock == f->block);
|
||||
}
|
||||
|
||||
nread = fread(buf, 1, t->block_size, f_st->fd);
|
||||
f_st->pos += t->block_size;
|
||||
if (nread < 0)
|
||||
warn("problem reading from %s", path);
|
||||
else if (nread != t->block_size && f_st->pos < f_st->data_len)
|
||||
warnx("incomplete read from %s", path);
|
||||
if (f_st->pos >= f_st->data_len) {
|
||||
fclose(f_st->fd);
|
||||
f_st->fd = 0;
|
||||
f_st->pos = 0;
|
||||
f_st->file++;
|
||||
if (f_st->file >= t->filelist_len)
|
||||
next_state(t);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
write_pri_vol_desc(struct ecma119_write_target *t, uint8_t *buf)
|
||||
{
|
||||
struct ecma119_pri_vol_desc *vol = (struct ecma119_pri_vol_desc*)buf;
|
||||
struct iso_volume *volume = t->volset->volume[t->volnum];
|
||||
char *vol_id = str2ascii(volume->volume_id);
|
||||
char *pub_id = str2ascii(volume->publisher_id);
|
||||
char *data_id = str2ascii(volume->data_preparer_id);
|
||||
char *volset_id = str2ascii(t->volset->volset_id);
|
||||
|
||||
vol->vol_desc_type[0] = 1;
|
||||
memcpy(vol->std_identifier, "CD001", 5);
|
||||
vol->vol_desc_version[0] = 1;
|
||||
memcpy(vol->system_id, "SYSID", 5);
|
||||
if (vol_id)
|
||||
strncpy((char*)vol->volume_id, vol_id, 32);
|
||||
iso_bb(vol->vol_space_size, t->vol_space_size, 4);
|
||||
iso_bb(vol->vol_set_size, t->volset->volset_size, 2);
|
||||
iso_bb(vol->vol_seq_number, t->volnum + 1, 2);
|
||||
iso_bb(vol->block_size, t->block_size, 2);
|
||||
iso_bb(vol->path_table_size, t->path_table_size, 4);
|
||||
iso_lsb(vol->l_path_table_pos, t->l_path_table_pos, 4);
|
||||
iso_msb(vol->m_path_table_pos, t->m_path_table_pos, 4);
|
||||
|
||||
write_one_dir_record(t, t->root, 3, vol->root_dir_record);
|
||||
|
||||
strncpy((char*)vol->vol_set_id, volset_id, 128);
|
||||
strncpy((char*)vol->publisher_id, pub_id, 128);
|
||||
strncpy((char*)vol->data_prep_id, data_id, 128);
|
||||
strncpy((char*)vol->application_id, "APPID", 128);
|
||||
|
||||
iso_datetime_17(vol->vol_creation_time, t->now);
|
||||
iso_datetime_17(vol->vol_modification_time, t->now);
|
||||
iso_datetime_17(vol->vol_effective_time, t->now);
|
||||
vol->file_structure_version[0] = 1;
|
||||
|
||||
free(vol_id);
|
||||
free(volset_id);
|
||||
free(pub_id);
|
||||
free(data_id);
|
||||
}
|
||||
|
||||
static void
|
||||
write_vol_desc_terminator(struct ecma119_write_target *t, uint8_t *buf)
|
||||
{
|
||||
struct ecma119_vol_desc_terminator *vol =
|
||||
(struct ecma119_vol_desc_terminator*) buf;
|
||||
|
||||
vol->vol_desc_type[0] = 255;
|
||||
memcpy(vol->std_identifier, "CD001", 5);
|
||||
vol->vol_desc_version[0] = 1;
|
||||
}
|
||||
|
||||
static void
|
||||
write_path_table(struct ecma119_write_target *t, int l_type, uint8_t *buf)
|
||||
{
|
||||
void (*write_int)(uint8_t*, uint32_t, int) = l_type ? iso_lsb
|
||||
: iso_msb;
|
||||
size_t i;
|
||||
struct ecma119_path_table_record *rec;
|
||||
struct ecma119_tree_node *dir;
|
||||
int parent = 0;
|
||||
|
||||
for (i = 0; i < t->dirlist_len; i++) {
|
||||
dir = t->pathlist[i];
|
||||
while ((i) && t->pathlist[parent] != dir->parent)
|
||||
parent++;
|
||||
assert(parent < i || i == 0);
|
||||
|
||||
rec = (struct ecma119_path_table_record*) buf;
|
||||
rec->len_di[0] = dir->parent ? (uint8_t) strlen(dir->name) : 1;
|
||||
rec->len_xa[0] = 0;
|
||||
write_int(rec->block, dir->block, 4);
|
||||
write_int(rec->parent, parent + 1, 2);
|
||||
if (dir->parent)
|
||||
memcpy(rec->dir_id, dir->name, rec->len_di[0]);
|
||||
buf += 8 + rec->len_di[0] + (rec->len_di[0] % 2);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
write_l_path_table(struct ecma119_write_target *t, uint8_t *buf)
|
||||
{
|
||||
write_path_table(t, 1, buf);
|
||||
}
|
||||
|
||||
static void
|
||||
write_m_path_table(struct ecma119_write_target *t, uint8_t *buf)
|
||||
{
|
||||
write_path_table(t, 0, buf);
|
||||
}
|
||||
|
||||
/* if file_id is >= 0, we use it instead of the filename. As a magic number,
|
||||
* file_id == 3 means that we are writing the root directory record (in order
|
||||
* to distinguish it from the "." entry in the root directory) */
|
||||
static void
|
||||
write_one_dir_record(struct ecma119_write_target *t,
|
||||
struct ecma119_tree_node *node,
|
||||
int file_id,
|
||||
uint8_t *buf)
|
||||
{
|
||||
uint8_t len_dr = (file_id >= 0) ? 34 : node->dirent_len;
|
||||
uint8_t len_fi = (file_id >= 0) ? 1 : strlen(node->name);
|
||||
uint8_t f_id = (uint8_t) ((file_id == 3) ? 0 : file_id);
|
||||
uint8_t *name = (file_id >= 0) ? &f_id : (uint8_t*)node->name;
|
||||
uint32_t len = (node->type == ECMA119_DIR) ? node->dir.len
|
||||
: node->file.real_me ? 0 : node->iso_self->attrib.st_size;
|
||||
struct ecma119_dir_record *rec = (struct ecma119_dir_record*)buf;
|
||||
|
||||
/* we don't write out susp fields for the root node */
|
||||
if (t->rockridge) {
|
||||
if (file_id == 0) {
|
||||
susp_write(t, &node->dir.self_susp, &buf[len_dr]);
|
||||
len_dr += node->dir.self_susp.non_CE_len;
|
||||
} else if (file_id == 1) {
|
||||
susp_write(t, &node->dir.parent_susp, &buf[len_dr]);
|
||||
len_dr += node->dir.parent_susp.non_CE_len;
|
||||
} else if (file_id < 0) {
|
||||
susp_write(t, &node->susp, &buf[len_dr]);
|
||||
len_dr += node->susp.non_CE_len;
|
||||
}
|
||||
}
|
||||
if (file_id == 1 && node->parent)
|
||||
node = node->parent;
|
||||
|
||||
rec->len_dr[0] = len_dr;
|
||||
iso_bb(rec->block, node->block, 4);
|
||||
iso_bb(rec->length, len, 4);
|
||||
iso_datetime_7(rec->recording_time, t->now);
|
||||
rec->flags[0] = (node->type == ECMA119_DIR) ? 2 : 0;
|
||||
iso_bb(rec->vol_seq_number, t->volnum + 1, 2);
|
||||
rec->len_fi[0] = len_fi;
|
||||
memcpy(rec->file_id, name, len_fi);
|
||||
}
|
||||
|
||||
static void
|
||||
write_one_dir(struct ecma119_write_target *t,
|
||||
struct ecma119_tree_node *dir,
|
||||
uint8_t *buf)
|
||||
{
|
||||
size_t i;
|
||||
uint8_t *orig_buf = buf;
|
||||
|
||||
assert(dir->type == ECMA119_DIR);
|
||||
/* write the "." and ".." entries first */
|
||||
write_one_dir_record(t, dir, 0, buf);
|
||||
buf += ((struct ecma119_dir_record*) buf)->len_dr[0];
|
||||
|
||||
write_one_dir_record(t, dir, 1, buf);
|
||||
buf += ((struct ecma119_dir_record*) buf)->len_dr[0];
|
||||
|
||||
for (i = 0; i < dir->dir.nchildren; i++) {
|
||||
write_one_dir_record(t, dir->dir.children[i], -1, buf);
|
||||
buf += ((struct ecma119_dir_record*) buf)->len_dr[0];
|
||||
}
|
||||
|
||||
/* write the susp continuation areas */
|
||||
if (t->rockridge) {
|
||||
susp_write_CE(t, &dir->dir.self_susp, buf);
|
||||
buf += dir->dir.self_susp.CE_len;
|
||||
susp_write_CE(t, &dir->dir.parent_susp, buf);
|
||||
buf += dir->dir.parent_susp.CE_len;
|
||||
for (i = 0; i < dir->dir.nchildren; i++) {
|
||||
susp_write_CE(t, &dir->dir.children[i]->susp, buf);
|
||||
buf += dir->dir.children[i]->susp.CE_len;
|
||||
}
|
||||
}
|
||||
assert (buf - orig_buf == dir->dir.len + dir->dir.CE_len);
|
||||
}
|
||||
|
||||
static void
|
||||
write_dirs(struct ecma119_write_target *t, uint8_t *buf)
|
||||
{
|
||||
size_t i;
|
||||
struct ecma119_tree_node *dir;
|
||||
for (i = 0; i < t->dirlist_len; i++) {
|
||||
dir = t->dirlist[i];
|
||||
write_one_dir(t, dir, buf);
|
||||
buf += round_up(dir->dir.len + dir->dir.CE_len, t->block_size);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ecma119_start_chunking(struct ecma119_write_target *t,
|
||||
write_fn writer,
|
||||
off_t data_size,
|
||||
uint8_t *buf)
|
||||
{
|
||||
if (data_size != t->state_data_size) {
|
||||
data_size = round_up(data_size, t->block_size);
|
||||
t->state_data = realloc(t->state_data, data_size);
|
||||
t->state_data_size = data_size;
|
||||
}
|
||||
memset(t->state_data, 0, t->state_data_size);
|
||||
t->state_data_off = 0;
|
||||
t->state_data_valid = 1;
|
||||
writer(t, t->state_data);
|
||||
write_data_chunk(t, buf);
|
||||
}
|
||||
|
||||
static void
|
||||
write_data_chunk(struct ecma119_write_target *t, uint8_t *buf)
|
||||
{
|
||||
memcpy(buf, t->state_data + t->state_data_off, t->block_size);
|
||||
t->state_data_off += t->block_size;
|
||||
if (t->state_data_off >= t->state_data_size) {
|
||||
assert (t->state_data_off <= t->state_data_size);
|
||||
t->state_data_valid = 0;
|
||||
next_state(t);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
bs_read(struct burn_source *bs, unsigned char *buf, int size)
|
||||
{
|
||||
struct ecma119_write_target *t = (struct ecma119_write_target*)bs->data;
|
||||
if (size != t->block_size) {
|
||||
warnx("you must read data in block-sized chunks (%d bytes)",
|
||||
(int)t->block_size);
|
||||
return 0;
|
||||
} else if (t->curblock >= t->vol_space_size) {
|
||||
return 0;
|
||||
}
|
||||
if (t->state_data_valid)
|
||||
write_data_chunk(t, buf);
|
||||
else
|
||||
writers[t->state](t, buf);
|
||||
t->curblock++;
|
||||
return size;
|
||||
}
|
||||
|
||||
static off_t
|
||||
bs_get_size(struct burn_source *bs)
|
||||
{
|
||||
struct ecma119_write_target *t = (struct ecma119_write_target*)bs->data;
|
||||
return t->total_size;
|
||||
}
|
||||
|
||||
static void
|
||||
bs_free_data(struct burn_source *bs)
|
||||
{
|
||||
struct ecma119_write_target *t = (struct ecma119_write_target*)bs->data;
|
||||
ecma119_tree_free(t->root);
|
||||
free(t->dirlist);
|
||||
free(t->pathlist);
|
||||
free(t->dirlist_joliet);
|
||||
free(t->pathlist_joliet);
|
||||
free(t->filelist);
|
||||
free(t->state_data);
|
||||
if (t->state_files.fd)
|
||||
fclose(t->state_files.fd);
|
||||
}
|
||||
|
||||
struct burn_source *iso_source_new_ecma119(struct iso_volset *volset,
|
||||
int volnum,
|
||||
int level,
|
||||
int flags)
|
||||
{
|
||||
struct burn_source *ret = calloc(1, sizeof(struct burn_source));
|
||||
ret->refcount = 1;
|
||||
ret->read = bs_read;
|
||||
ret->get_size = bs_get_size;
|
||||
ret->free_data = bs_free_data;
|
||||
ret->data = ecma119_target_new(volset, volnum, level, flags);
|
||||
return ret;
|
||||
}
|
@ -1,267 +0,0 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
/* vim: set noet ts=8 sts=8 sw=8 : */
|
||||
|
||||
/**
|
||||
* \file ecma119.h
|
||||
*
|
||||
* Structures and definitions used for writing an emca119 (ISO9660) compatible
|
||||
* volume.
|
||||
*/
|
||||
|
||||
#ifndef LIBISO_ECMA119_H
|
||||
#define LIBISO_ECMA119_H
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h> /* for FILE */
|
||||
#include <sys/types.h>
|
||||
#include "susp.h"
|
||||
|
||||
struct ecma119_tree_node;
|
||||
struct joliet_tree_node;
|
||||
|
||||
/**
|
||||
* The possible states that the ecma119 writer can be in.
|
||||
*/
|
||||
enum ecma119_write_state
|
||||
{
|
||||
ECMA119_WRITE_BEFORE,
|
||||
|
||||
ECMA119_WRITE_SYSTEM_AREA,
|
||||
ECMA119_WRITE_PRI_VOL_DESC,
|
||||
ECMA119_WRITE_SUP_VOL_DESC_JOLIET,
|
||||
ECMA119_WRITE_VOL_DESC_TERMINATOR,
|
||||
ECMA119_WRITE_L_PATH_TABLE,
|
||||
ECMA119_WRITE_M_PATH_TABLE,
|
||||
ECMA119_WRITE_L_PATH_TABLE_JOLIET,
|
||||
ECMA119_WRITE_M_PATH_TABLE_JOLIET,
|
||||
ECMA119_WRITE_DIR_RECORDS,
|
||||
ECMA119_WRITE_DIR_RECORDS_JOLIET,
|
||||
ECMA119_WRITE_FILES,
|
||||
|
||||
ECMA119_WRITE_DONE
|
||||
};
|
||||
|
||||
/**
|
||||
* Data describing the state of the ecma119 writer. Everything here should be
|
||||
* considered private!
|
||||
*/
|
||||
struct ecma119_write_target
|
||||
{
|
||||
struct ecma119_tree_node *root;
|
||||
struct joliet_tree_node *joliet_root;
|
||||
struct iso_volset *volset;
|
||||
int volnum;
|
||||
|
||||
time_t now; /**< Time at which writing began. */
|
||||
off_t total_size; /**< Total size of the output. This only
|
||||
* includes the current volume. */
|
||||
uint32_t vol_space_size;
|
||||
|
||||
unsigned int rockridge:1;
|
||||
unsigned int joliet:1;
|
||||
unsigned int iso_level:2;
|
||||
|
||||
int curblock;
|
||||
uint16_t block_size;
|
||||
uint32_t path_table_size;
|
||||
uint32_t path_table_size_joliet;
|
||||
uint32_t l_path_table_pos;
|
||||
uint32_t m_path_table_pos;
|
||||
uint32_t l_path_table_pos_joliet;
|
||||
uint32_t m_path_table_pos_joliet;
|
||||
uint32_t total_dir_size;
|
||||
uint32_t total_dir_size_joliet;
|
||||
|
||||
struct ecma119_tree_node **dirlist;
|
||||
/**< A pre-order list of directories
|
||||
* (this is the order in which we write
|
||||
* out directory records).
|
||||
*/
|
||||
struct ecma119_tree_node **pathlist;
|
||||
/**< A breadth-first list of
|
||||
* directories. This is used for
|
||||
* writing out the path tables.
|
||||
*/
|
||||
size_t dirlist_len; /**< The length of the previous 2 lists.
|
||||
*/
|
||||
|
||||
struct ecma119_tree_node **filelist;
|
||||
/**< A pre-order list of files with
|
||||
* non-NULL paths and non-zero sizes.
|
||||
*/
|
||||
size_t filelist_len; /* Length of the previous list. */
|
||||
|
||||
int curfile; /**< Used as a helper field for writing
|
||||
out filelist and dirlist */
|
||||
|
||||
/* Joliet versions of the above lists. Since Joliet doesn't require
|
||||
* directory relocation, the order of these lists might be different
|
||||
* from the lists above (but they will be the same length).
|
||||
*/
|
||||
struct joliet_tree_node **dirlist_joliet;
|
||||
struct joliet_tree_node **pathlist_joliet;
|
||||
|
||||
enum ecma119_write_state state; /* The current state of the writer. */
|
||||
|
||||
/* Most writers work by
|
||||
* 1) making sure state_data is big enough for their data
|
||||
* 2) writing _all_ their data into state_data
|
||||
* 3) relying on write_data_chunk to write the data block
|
||||
* by block.
|
||||
*/
|
||||
uint8_t *state_data;
|
||||
off_t state_data_size;
|
||||
off_t state_data_off;
|
||||
int state_data_valid;
|
||||
|
||||
/* for writing out files */
|
||||
struct state_files {
|
||||
off_t pos; /* The number of bytes we have written
|
||||
* so far in the current file.
|
||||
*/
|
||||
off_t data_len;/* The number of bytes in the currently
|
||||
* open file.
|
||||
*/
|
||||
FILE *fd; /* The currently open file. */
|
||||
int file; /* The index in filelist that we are
|
||||
* currently writing (or about to write). */
|
||||
} state_files;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new ecma119_write_target from the given volume number of the
|
||||
* given volume set.
|
||||
*
|
||||
* \pre \p volnum is less than \p volset-\>volset_size.
|
||||
* \post For each node in the tree, writer_data has been allocated.
|
||||
* \post The directory heirarchy has been reorganised to be ecma119-compatible.
|
||||
*/
|
||||
struct ecma119_write_target *ecma119_target_new(struct iso_volset *volset,
|
||||
int volnum,
|
||||
int level,
|
||||
int flags);
|
||||
|
||||
#define BP(a,b) [(b) - (a) + 1]
|
||||
|
||||
struct ecma119_pri_vol_desc
|
||||
{
|
||||
uint8_t vol_desc_type BP(1, 1);
|
||||
uint8_t std_identifier BP(2, 6);
|
||||
uint8_t vol_desc_version BP(7, 7);
|
||||
uint8_t unused1 BP(8, 8);
|
||||
uint8_t system_id BP(9, 40);
|
||||
uint8_t volume_id BP(41, 72);
|
||||
uint8_t unused2 BP(73, 80);
|
||||
uint8_t vol_space_size BP(81, 88);
|
||||
uint8_t unused3 BP(89, 120);
|
||||
uint8_t vol_set_size BP(121, 124);
|
||||
uint8_t vol_seq_number BP(125, 128);
|
||||
uint8_t block_size BP(129, 132);
|
||||
uint8_t path_table_size BP(133, 140);
|
||||
uint8_t l_path_table_pos BP(141, 144);
|
||||
uint8_t opt_l_path_table_pos BP(145, 148);
|
||||
uint8_t m_path_table_pos BP(149, 152);
|
||||
uint8_t opt_m_path_table_pos BP(153, 156);
|
||||
uint8_t root_dir_record BP(157, 190);
|
||||
uint8_t vol_set_id BP(191, 318);
|
||||
uint8_t publisher_id BP(319, 446);
|
||||
uint8_t data_prep_id BP(447, 574);
|
||||
uint8_t application_id BP(575, 702);
|
||||
uint8_t copyright_file_id BP(703, 739);
|
||||
uint8_t abstract_file_id BP(740, 776);
|
||||
uint8_t bibliographic_file_id BP(777, 813);
|
||||
uint8_t vol_creation_time BP(814, 830);
|
||||
uint8_t vol_modification_time BP(831, 847);
|
||||
uint8_t vol_expiration_time BP(848, 864);
|
||||
uint8_t vol_effective_time BP(865, 881);
|
||||
uint8_t file_structure_version BP(882, 882);
|
||||
uint8_t reserved1 BP(883, 883);
|
||||
uint8_t app_use BP(884, 1395);
|
||||
uint8_t reserved2 BP(1396, 2048);
|
||||
};
|
||||
|
||||
struct ecma119_sup_vol_desc
|
||||
{
|
||||
uint8_t vol_desc_type BP(1, 1);
|
||||
uint8_t std_identifier BP(2, 6);
|
||||
uint8_t vol_desc_version BP(7, 7);
|
||||
uint8_t vol_flags BP(8, 8);
|
||||
uint8_t system_id BP(9, 40);
|
||||
uint8_t volume_id BP(41, 72);
|
||||
uint8_t unused2 BP(73, 80);
|
||||
uint8_t vol_space_size BP(81, 88);
|
||||
uint8_t esc_sequences BP(89, 120);
|
||||
uint8_t vol_set_size BP(121, 124);
|
||||
uint8_t vol_seq_number BP(125, 128);
|
||||
uint8_t block_size BP(129, 132);
|
||||
uint8_t path_table_size BP(133, 140);
|
||||
uint8_t l_path_table_pos BP(141, 144);
|
||||
uint8_t opt_l_path_table_pos BP(145, 148);
|
||||
uint8_t m_path_table_pos BP(149, 152);
|
||||
uint8_t opt_m_path_table_pos BP(153, 156);
|
||||
uint8_t root_dir_record BP(157, 190);
|
||||
uint8_t vol_set_id BP(191, 318);
|
||||
uint8_t publisher_id BP(319, 446);
|
||||
uint8_t data_prep_id BP(447, 574);
|
||||
uint8_t application_id BP(575, 702);
|
||||
uint8_t copyright_file_id BP(703, 739);
|
||||
uint8_t abstract_file_id BP(740, 776);
|
||||
uint8_t bibliographic_file_id BP(777, 813);
|
||||
uint8_t vol_creation_time BP(814, 830);
|
||||
uint8_t vol_modification_time BP(831, 847);
|
||||
uint8_t vol_expiration_time BP(848, 864);
|
||||
uint8_t vol_effective_time BP(865, 881);
|
||||
uint8_t file_structure_version BP(882, 882);
|
||||
uint8_t reserved1 BP(883, 883);
|
||||
uint8_t app_use BP(884, 1395);
|
||||
uint8_t reserved2 BP(1396, 2048);
|
||||
};
|
||||
|
||||
struct ecma119_vol_desc_terminator
|
||||
{
|
||||
uint8_t vol_desc_type BP(1, 1);
|
||||
uint8_t std_identifier BP(2, 6);
|
||||
uint8_t vol_desc_version BP(7, 7);
|
||||
uint8_t reserved BP(8, 2048);
|
||||
};
|
||||
|
||||
struct ecma119_dir_record
|
||||
{
|
||||
uint8_t len_dr BP(1, 1);
|
||||
uint8_t len_xa BP(2, 2);
|
||||
uint8_t block BP(3, 10);
|
||||
uint8_t length BP(11, 18);
|
||||
uint8_t recording_time BP(19, 25);
|
||||
uint8_t flags BP(26, 26);
|
||||
uint8_t file_unit_size BP(27, 27);
|
||||
uint8_t interleave_gap_size BP(28, 28);
|
||||
uint8_t vol_seq_number BP(29, 32);
|
||||
uint8_t len_fi BP(33, 33);
|
||||
uint8_t file_id BP(34, 34); /* 34 to 33+len_fi */
|
||||
/* padding field (if len_fi is even) */
|
||||
/* system use (len_dr - len_su + 1 to len_dr) */
|
||||
};
|
||||
|
||||
struct ecma119_path_table_record
|
||||
{
|
||||
uint8_t len_di BP(1, 1);
|
||||
uint8_t len_xa BP(2, 2);
|
||||
uint8_t block BP(3, 6);
|
||||
uint8_t parent BP(7, 8);
|
||||
uint8_t dir_id BP(9, 9); /* 9 to 8+len_di */
|
||||
/* padding field (if len_di is odd) */
|
||||
};
|
||||
|
||||
/**
|
||||
* A utility function for writers that want to write their data all at once
|
||||
* rather than block-by-block. This creates a buffer of size \p size, passes
|
||||
* it to the given writer, then hands out block-sized chunks.
|
||||
*/
|
||||
void
|
||||
ecma119_start_chunking(struct ecma119_write_target *t,
|
||||
void (*)(struct ecma119_write_target*, uint8_t*),
|
||||
off_t size,
|
||||
uint8_t *buf);
|
||||
|
||||
#endif /* LIBISO_ECMA119_H */
|
@ -1,312 +0,0 @@
|
||||
/* vim: set noet ts=8 sts=8 sw=8 : */
|
||||
|
||||
#include <string.h>
|
||||
#include <wchar.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "ecma119.h"
|
||||
#include "ecma119_tree.h"
|
||||
#include "tree.h"
|
||||
#include "util.h"
|
||||
|
||||
static size_t calc_dirent_len(struct ecma119_tree_node *n)
|
||||
{
|
||||
int ret = n->name ? strlen(n->name) + 33 : 34;
|
||||
if (ret % 2) ret++;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct ecma119_tree_node*
|
||||
create_dir(struct ecma119_write_target *t,
|
||||
struct ecma119_tree_node *parent,
|
||||
struct iso_tree_node *iso)
|
||||
{
|
||||
struct ecma119_tree_node *ret;
|
||||
|
||||
assert(t && (!parent || parent->type == ECMA119_DIR)
|
||||
&& iso && S_ISDIR(iso->attrib.st_mode));
|
||||
|
||||
ret = calloc(1, sizeof(struct ecma119_tree_node));
|
||||
ret->name = iso->name ? ((t->iso_level == 1) ? iso_1_dirid(iso->name)
|
||||
: iso_2_dirid(iso->name))
|
||||
: NULL;
|
||||
ret->dirent_len = calc_dirent_len(ret);
|
||||
ret->iso_self = iso;
|
||||
ret->target = t;
|
||||
ret->type = ECMA119_DIR;
|
||||
ret->parent = ret->dir.real_parent = parent;
|
||||
ret->dir.depth = parent ? parent->dir.depth + 1 : 1;
|
||||
ret->dir.nchildren = iso->nchildren;
|
||||
ret->dir.children = calloc(1, sizeof(void*) * iso->nchildren);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct ecma119_tree_node*
|
||||
create_file(struct ecma119_write_target *t,
|
||||
struct ecma119_tree_node *parent,
|
||||
struct iso_tree_node *iso)
|
||||
{
|
||||
struct ecma119_tree_node *ret;
|
||||
|
||||
assert(t && iso && parent && parent->type == ECMA119_DIR);
|
||||
|
||||
ret = calloc(1, sizeof(struct ecma119_tree_node));
|
||||
ret->name = iso->name ? ((t->iso_level == 1) ? iso_1_fileid(iso->name)
|
||||
: iso_2_fileid(iso->name))
|
||||
: NULL;
|
||||
ret->dirent_len = calc_dirent_len(ret);
|
||||
ret->parent = parent;
|
||||
ret->iso_self = iso;
|
||||
ret->target = t;
|
||||
ret->type = ECMA119_FILE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct ecma119_tree_node*
|
||||
create_tree(struct ecma119_write_target *t,
|
||||
struct ecma119_tree_node *parent,
|
||||
struct iso_tree_node *iso)
|
||||
{
|
||||
struct ecma119_tree_node *ret;
|
||||
size_t i;
|
||||
|
||||
assert(t && iso);
|
||||
|
||||
ret = (S_ISDIR(iso->attrib.st_mode) ? create_dir : create_file)
|
||||
(t, parent, iso);
|
||||
for (i = 0; i < iso->nchildren; i++) {
|
||||
ret->dir.children[i] = create_tree(t, ret, iso->children[i]);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
ecma119_tree_free(struct ecma119_tree_node *root)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (root->type == ECMA119_DIR) {
|
||||
for (i=0; i < root->dir.nchildren; i++) {
|
||||
ecma119_tree_free(root->dir.children[i]);
|
||||
}
|
||||
free(root->dir.children);
|
||||
}
|
||||
free(root->name);
|
||||
free(root);
|
||||
}
|
||||
|
||||
static size_t
|
||||
max_child_name_len(struct ecma119_tree_node *root)
|
||||
{
|
||||
size_t ret = 0, i;
|
||||
|
||||
assert(root->type == ECMA119_DIR);
|
||||
for (i=0; i < root->dir.nchildren; i++) {
|
||||
size_t len = strlen(root->dir.children[i]->name);
|
||||
ret = MAX(ret, len);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
reparent(struct ecma119_tree_node *child,
|
||||
struct ecma119_tree_node *parent)
|
||||
{
|
||||
int found = 0;
|
||||
size_t i;
|
||||
struct ecma119_tree_node *placeholder;
|
||||
|
||||
assert(child && parent && parent->type == ECMA119_DIR && child->parent);
|
||||
|
||||
/* replace the child in the original parent with a placeholder */
|
||||
for (i=0; i < child->parent->dir.nchildren; i++) {
|
||||
if (child->parent->dir.children[i] == child) {
|
||||
placeholder = create_file(child->target,
|
||||
child->parent,
|
||||
child->iso_self);
|
||||
placeholder->file.real_me = child;
|
||||
child->parent->dir.children[i] = placeholder;
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert(found);
|
||||
|
||||
/* add the child to its new parent */
|
||||
child->parent = parent;
|
||||
parent->dir.nchildren++;
|
||||
parent->dir.children = realloc( parent->dir.children,
|
||||
sizeof(void*) * parent->dir.nchildren );
|
||||
parent->dir.children[parent->dir.nchildren-1] = child;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reorder the tree, if necessary, to ensure that
|
||||
* - the depth is at most 8
|
||||
* - each path length is at most 255 characters
|
||||
*/
|
||||
static void
|
||||
reorder_tree(struct ecma119_tree_node *root,
|
||||
struct ecma119_tree_node *cur)
|
||||
{
|
||||
size_t max_path;
|
||||
|
||||
assert(root && cur && cur->type == ECMA119_DIR);
|
||||
|
||||
cur->dir.depth = cur->parent ? cur->parent->dir.depth + 1 : 1;
|
||||
cur->dir.path_len = cur->parent ? cur->parent->dir.path_len
|
||||
+ strlen(cur->name) : 0;
|
||||
max_path = cur->dir.path_len + cur->dir.depth + max_child_name_len(cur);
|
||||
|
||||
if (cur->dir.depth > 8 || max_path > 255) {
|
||||
reparent(cur, root);
|
||||
/* we are appended to the root's children now, so there is no
|
||||
* need to recurse (the root will hit us again) */
|
||||
} else {
|
||||
size_t i;
|
||||
|
||||
for (i=0; i < cur->dir.nchildren; i++) {
|
||||
if (cur->dir.children[i]->type == ECMA119_DIR)
|
||||
reorder_tree(root, cur->dir.children[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
cmp_node(const void *f1, const void *f2)
|
||||
{
|
||||
struct ecma119_tree_node *f = *((struct ecma119_tree_node**)f1);
|
||||
struct ecma119_tree_node *g = *((struct ecma119_tree_node**)f2);
|
||||
return strcmp(f->name, g->name);
|
||||
}
|
||||
|
||||
static void
|
||||
sort_tree(struct ecma119_tree_node *root)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
assert(root && root->type == ECMA119_DIR);
|
||||
|
||||
qsort(root->dir.children, root->dir.nchildren, sizeof(void*), cmp_node);
|
||||
for (i=0; i < root->dir.nchildren; i++) {
|
||||
if (root->dir.children[i]->type == ECMA119_DIR)
|
||||
sort_tree(root->dir.children[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Change num_change characters of the given filename in order to ensure the
|
||||
* name is unique. If the name is short enough (depending on the ISO level),
|
||||
* we can append the characters instead of changing them.
|
||||
*
|
||||
* \p seq_num is the index of this file in the sequence of identical filenames.
|
||||
*
|
||||
* For example, seq_num=3, num_change=2, name="HELLOTHERE.TXT" changes name to
|
||||
* "HELLOTHE03.TXT"
|
||||
*/
|
||||
static void
|
||||
mangle_name(char **name, int num_change, int level, int seq_num)
|
||||
{
|
||||
char *dot = strrchr(*name, '.');
|
||||
char *semi = strrchr(*name, ';');
|
||||
size_t len = strlen(*name);
|
||||
char base[len+1], ext[len+1];
|
||||
char fmt[12];
|
||||
size_t baselen, extlen;
|
||||
|
||||
if (num_change >= len) {
|
||||
return;
|
||||
}
|
||||
strncpy(base, *name, len+1);
|
||||
if (dot) {
|
||||
base[dot - *name] = '\0';
|
||||
strncpy(ext, dot+1, len+1);
|
||||
if (semi) {
|
||||
ext[semi - dot - 1] = '\0';
|
||||
}
|
||||
} else {
|
||||
base[len-2] = '\0';
|
||||
ext[0] = '\0';
|
||||
}
|
||||
baselen = strlen(base);
|
||||
extlen = strlen(ext);
|
||||
if (level == 1 && baselen + num_change > 8) {
|
||||
base[8 - num_change] = '\0';
|
||||
} else if (level != 1 && baselen + extlen + num_change > 30) {
|
||||
base[30 - extlen - num_change] = '\0';
|
||||
}
|
||||
|
||||
sprintf(fmt, "%%s%%0%1dd.%%s;1", num_change);
|
||||
*name = realloc(*name, baselen + extlen + num_change + 4);
|
||||
sprintf(*name, fmt, base, seq_num, ext);
|
||||
}
|
||||
|
||||
static void
|
||||
mangle_all(struct ecma119_tree_node *dir)
|
||||
{
|
||||
size_t i, j, k;
|
||||
struct ecma119_dir_info d = dir->dir;
|
||||
size_t n_change;
|
||||
int changed;
|
||||
|
||||
assert(dir->type == ECMA119_DIR);
|
||||
do {
|
||||
changed = 0;
|
||||
for (i=0; i < d.nchildren; i++) {
|
||||
/* find the number of consecutive equal names */
|
||||
j = 1;
|
||||
while ( i+j < d.nchildren &&
|
||||
!strcmp(d.children[i]->name,
|
||||
d.children[i+j]->name) )
|
||||
j++;
|
||||
if (j == 1) continue;
|
||||
|
||||
/* mangle the names */
|
||||
changed = 1;
|
||||
n_change = j / 10 + 1;
|
||||
for (k=0; k < j; k++) {
|
||||
mangle_name(&(d.children[i+k]->name),
|
||||
n_change,
|
||||
dir->target->iso_level,
|
||||
k);
|
||||
d.children[i+k]->dirent_len =
|
||||
calc_dirent_len(d.children[i+k]);
|
||||
}
|
||||
|
||||
/* skip ahead by the number of mangled names */
|
||||
i += j - 1;
|
||||
}
|
||||
} while (changed);
|
||||
|
||||
for (i=0; i < d.nchildren; i++) {
|
||||
if (d.children[i]->type == ECMA119_DIR)
|
||||
mangle_all(d.children[i]);
|
||||
}
|
||||
}
|
||||
|
||||
struct ecma119_tree_node*
|
||||
ecma119_tree_create(struct ecma119_write_target *t,
|
||||
struct iso_tree_node *iso_root)
|
||||
{
|
||||
t->root = create_tree(t, NULL, iso_root);
|
||||
reorder_tree(t->root, t->root);
|
||||
sort_tree(t->root);
|
||||
mangle_all(t->root);
|
||||
return t->root;
|
||||
}
|
||||
|
||||
void
|
||||
ecma119_tree_print(struct ecma119_tree_node *root, int spaces)
|
||||
{
|
||||
size_t i;
|
||||
char sp[spaces+1];
|
||||
|
||||
memset(sp, ' ', spaces);
|
||||
sp[spaces] = '\0';
|
||||
|
||||
printf("%s%s\n", sp, root->name);
|
||||
if (root->type == ECMA119_DIR)
|
||||
for (i=0; i < root->dir.nchildren; i++)
|
||||
ecma119_tree_print(root->dir.children[i], spaces+2);
|
||||
}
|
@ -1,95 +0,0 @@
|
||||
/* vim: set noet ts=8 sts=8 sw=8 : */
|
||||
|
||||
/**
|
||||
* \file ecma119_tree.h
|
||||
*
|
||||
* Declarations for creating, modifying and printing filesystem trees that
|
||||
* are compatible with ecma119.
|
||||
*/
|
||||
|
||||
#ifndef LIBISO_ECMA119_TREE_H
|
||||
#define LIBISO_ECMA119_TREE_H
|
||||
|
||||
struct ecma119_write_target;
|
||||
|
||||
enum {
|
||||
ECMA119_FILE,
|
||||
ECMA119_DIR
|
||||
};
|
||||
|
||||
struct ecma119_dir_info {
|
||||
struct susp_info self_susp; /**< susp entries for "." */
|
||||
struct susp_info parent_susp; /**< susp entries for ".." */
|
||||
|
||||
size_t len; /**< sum of the lengths of children's
|
||||
* Directory Records (including SU) */
|
||||
size_t CE_len; /**< sum of the lengths of children's
|
||||
* SUSP CE areas */
|
||||
|
||||
int depth;
|
||||
size_t path_len; /**< The length of a path up to, and
|
||||
* including, this directory. This
|
||||
* cannot exceed 255. */
|
||||
size_t nchildren;
|
||||
struct ecma119_tree_node **children;
|
||||
|
||||
struct ecma119_tree_node *real_parent;
|
||||
/**< The parent before relocation */
|
||||
};
|
||||
|
||||
struct ecma119_file_info
|
||||
{
|
||||
struct ecma119_tree_node *real_me;
|
||||
/**< If this is non-NULL, the file is
|
||||
* a placeholder for a relocated
|
||||
* directory and this field points to
|
||||
* that relocated directory.
|
||||
*/
|
||||
};
|
||||
|
||||
/**
|
||||
* A node for a tree containing all the information necessary for writing
|
||||
* an ISO9660 volume.
|
||||
*/
|
||||
struct ecma119_tree_node
|
||||
{
|
||||
char *name; /**< in ASCII, conforming to the
|
||||
* current ISO level. */
|
||||
size_t dirent_len; /**< Length of the directory record,
|
||||
* not including SU. */
|
||||
size_t block;
|
||||
|
||||
struct ecma119_tree_node *parent;
|
||||
struct iso_tree_node *iso_self;
|
||||
struct ecma119_write_target *target;
|
||||
|
||||
struct susp_info susp;
|
||||
|
||||
int type; /**< file or directory */
|
||||
/* union {*/
|
||||
struct ecma119_dir_info dir;
|
||||
struct ecma119_file_info file;
|
||||
/* };*/
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new ecma119_tree that corresponds to the tree represented by
|
||||
* \p iso_root.
|
||||
*/
|
||||
struct ecma119_tree_node*
|
||||
ecma119_tree_create(struct ecma119_write_target *target,
|
||||
struct iso_tree_node *iso_root);
|
||||
|
||||
/**
|
||||
* Free an ecma119 tree.
|
||||
*/
|
||||
void
|
||||
ecma119_tree_free(struct ecma119_tree_node *root);
|
||||
|
||||
/**
|
||||
* Print an ecma119 tree.
|
||||
*/
|
||||
void
|
||||
ecma119_tree_print(struct ecma119_tree_node *root, int spaces);
|
||||
|
||||
#endif /* LIBISO_ECMA119_TREE_H */
|
@ -1,42 +0,0 @@
|
||||
#include "hash.h"
|
||||
#include "exclude.h"
|
||||
|
||||
static struct iso_hash_node *table[HASH_NODES]={0,};
|
||||
static int num=0;
|
||||
|
||||
void
|
||||
iso_exclude_add_path(const char *path)
|
||||
{
|
||||
if (!path)
|
||||
return;
|
||||
|
||||
num += iso_hash_insert(table, path);
|
||||
}
|
||||
|
||||
void
|
||||
iso_exclude_remove_path(const char *path)
|
||||
{
|
||||
if (!num || !path)
|
||||
return;
|
||||
|
||||
num -= iso_hash_remove(table, path);
|
||||
}
|
||||
|
||||
void
|
||||
iso_exclude_empty(void)
|
||||
{
|
||||
if (!num)
|
||||
return;
|
||||
|
||||
iso_hash_empty(table);
|
||||
num=0;
|
||||
}
|
||||
|
||||
int
|
||||
iso_exclude_lookup(const char *path)
|
||||
{
|
||||
if (!num || !path)
|
||||
return 0;
|
||||
|
||||
return iso_hash_lookup(table, path);
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
#ifndef ISO_EXCLUDE_H
|
||||
#define ISO_EXCLUDE_H
|
||||
|
||||
/**
|
||||
* Add a path to ignore when adding a directory recursively.
|
||||
*
|
||||
* \param path The path, on the local filesystem, of the file.
|
||||
*/
|
||||
int
|
||||
iso_exclude_lookup(const char *path);
|
||||
|
||||
#endif /* ISO_EXCLUDE */
|
158
libisofs/hash.c
158
libisofs/hash.c
@ -1,158 +0,0 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "hash.h"
|
||||
|
||||
static unsigned int
|
||||
iso_hash_path(const char *path)
|
||||
{
|
||||
unsigned int hash_num=0;
|
||||
const char *c;
|
||||
|
||||
c=path;
|
||||
while(*c)
|
||||
hash_num = (hash_num << 15) + (hash_num << 3) + (hash_num >> 3) + *c++;
|
||||
|
||||
return hash_num % HASH_NODES;
|
||||
}
|
||||
|
||||
int
|
||||
iso_hash_lookup(struct iso_hash_node **table, const char *path)
|
||||
{
|
||||
struct iso_hash_node *node;
|
||||
unsigned int hash_num;
|
||||
|
||||
hash_num = iso_hash_path(path);
|
||||
|
||||
node=table[hash_num];
|
||||
|
||||
if (!node)
|
||||
return 0;
|
||||
|
||||
if (!strcmp(path, node->path))
|
||||
return 1;
|
||||
|
||||
while (node->next) {
|
||||
node=node->next;
|
||||
|
||||
if (!strcmp(path, node->path))
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct iso_hash_node*
|
||||
iso_hash_node_new (const char *path)
|
||||
{
|
||||
struct iso_hash_node *node;
|
||||
|
||||
/*create an element to be inserted in the hash table */
|
||||
node=malloc(sizeof(struct iso_hash_node));
|
||||
node->path=strdup(path);
|
||||
node->next=NULL;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
int
|
||||
iso_hash_insert(struct iso_hash_node **table, const char *path)
|
||||
{
|
||||
struct iso_hash_node *node;
|
||||
unsigned int hash_num;
|
||||
|
||||
/* find the hash number */
|
||||
hash_num = iso_hash_path(path);
|
||||
|
||||
/* insert it */
|
||||
node = table[hash_num];
|
||||
|
||||
/* unfortunately, we can't safely consider that a path
|
||||
* won't be twice in the hash table so make sure it
|
||||
* doesn't already exists */
|
||||
if (!node) {
|
||||
table[hash_num]=iso_hash_node_new(path);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* if it's already in, we don't do anything */
|
||||
if (!strcmp(path, node->path))
|
||||
return 0;
|
||||
|
||||
while (node->next) {
|
||||
node = node->next;
|
||||
|
||||
/* if it's already in, we don't do anything */
|
||||
if (!strcmp (path, node->path))
|
||||
return 0;
|
||||
}
|
||||
|
||||
node->next = iso_hash_node_new(path);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
iso_hash_node_free(struct iso_hash_node *node)
|
||||
{
|
||||
free(node->path);
|
||||
free(node);
|
||||
}
|
||||
|
||||
int
|
||||
iso_hash_remove(struct iso_hash_node **table, const char *path)
|
||||
{
|
||||
unsigned int hash_num;
|
||||
struct iso_hash_node *node;
|
||||
|
||||
hash_num = iso_hash_path(path);
|
||||
|
||||
node=table[hash_num];
|
||||
if (!node)
|
||||
return 0;
|
||||
|
||||
if (!strcmp(path, node->path)) {
|
||||
table[hash_num]=node->next;
|
||||
iso_hash_node_free(node);
|
||||
return 1;
|
||||
}
|
||||
|
||||
while (node->next) {
|
||||
struct iso_hash_node *prev;
|
||||
|
||||
prev = node;
|
||||
node = node->next;
|
||||
|
||||
if (!strcmp (path, node->path)) {
|
||||
prev->next=node->next;
|
||||
iso_hash_node_free(node);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
iso_hash_empty(struct iso_hash_node **table)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; i < HASH_NODES; i++) {
|
||||
struct iso_hash_node *node;
|
||||
|
||||
node=table[i];
|
||||
if (!node)
|
||||
continue;
|
||||
|
||||
table[i]=NULL;
|
||||
|
||||
do {
|
||||
struct iso_hash_node *next;
|
||||
|
||||
next=node->next;
|
||||
iso_hash_node_free(node);
|
||||
node=next;
|
||||
} while (node);
|
||||
}
|
||||
}
|
||||
|
@ -1,46 +0,0 @@
|
||||
#ifndef ISO_HASH_H
|
||||
#define ISO_HASH_H
|
||||
|
||||
struct iso_hash_node {
|
||||
struct iso_hash_node *next;
|
||||
char *path;
|
||||
};
|
||||
|
||||
#define HASH_NODES 128
|
||||
|
||||
/**
|
||||
* Searches in the hash table if the path exists.
|
||||
*
|
||||
* \param table The hash table.
|
||||
* \param path The path of the file to look for.
|
||||
*
|
||||
* \return 1 if the path exists in the hash table, 0 otherwise.
|
||||
*/
|
||||
int iso_hash_lookup(struct iso_hash_node **table, const char *path);
|
||||
|
||||
/**
|
||||
* Insert a new path in the hash table.
|
||||
*
|
||||
* \param table The hash table.
|
||||
* \param path The path of a file to add to the hash table.
|
||||
*
|
||||
* \return 1 if the file wasn't already in the hash table, 0 otherwise.
|
||||
*/
|
||||
int iso_hash_insert(struct iso_hash_node **table, const char *path);
|
||||
|
||||
/**
|
||||
* Remove a path from the hash table.
|
||||
*
|
||||
* \param table The hash table.
|
||||
* \param path The path of a file to remove from the hash table.
|
||||
*
|
||||
* \return 1 if the file was found and removed, 0 otherwise.
|
||||
*/
|
||||
int iso_hash_remove(struct iso_hash_node **table, const char *path);
|
||||
|
||||
/**
|
||||
* Empty the hash table.
|
||||
*/
|
||||
void iso_hash_empty(struct iso_hash_node **table);
|
||||
|
||||
#endif /* ISO_HASH_H */
|
@ -1,379 +0,0 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
/* vim: set noet ts=8 sts=8 sw=8 : */
|
||||
|
||||
#include "joliet.h"
|
||||
#include "ecma119.h"
|
||||
#include "ecma119_tree.h"
|
||||
#include "tree.h"
|
||||
#include "util.h"
|
||||
#include "volume.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
static struct joliet_tree_node*
|
||||
create_node(struct ecma119_write_target *t,
|
||||
struct joliet_tree_node *parent,
|
||||
struct iso_tree_node *iso)
|
||||
{
|
||||
struct joliet_tree_node *ret =
|
||||
calloc(1, sizeof(struct joliet_tree_node));
|
||||
|
||||
ret->name = iso_j_id(iso->name);
|
||||
ret->dirent_len = 34 + (ret->name ? ucslen(ret->name) * 2 : 0);
|
||||
ret->len = iso->attrib.st_size; /* for dirs, we'll change this */
|
||||
ret->block = iso->block; /* only actually for files, not dirs */
|
||||
ret->parent = parent;
|
||||
ret->iso_self = iso;
|
||||
ret->target = t;
|
||||
ret->nchildren = iso->nchildren;
|
||||
if (ret->nchildren)
|
||||
ret->children = calloc(sizeof(void*), ret->nchildren);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct joliet_tree_node*
|
||||
create_tree(struct ecma119_write_target *t,
|
||||
struct joliet_tree_node *parent,
|
||||
struct iso_tree_node *iso_root)
|
||||
{
|
||||
struct joliet_tree_node *root = create_node(t, parent, iso_root);
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < root->nchildren; i++) {
|
||||
struct iso_tree_node *iso_ch = iso_root->children[i];
|
||||
if (ISO_ISDIR(iso_ch))
|
||||
root->children[i] = create_tree(t, root, iso_ch);
|
||||
else
|
||||
root->children[i] = create_node(t, root, iso_ch);
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
static int
|
||||
cmp_node(const void *f1, const void *f2)
|
||||
{
|
||||
struct joliet_tree_node *f = *((struct joliet_tree_node**)f1);
|
||||
struct joliet_tree_node *g = *((struct joliet_tree_node**)f2);
|
||||
return ucscmp(f->name, g->name);
|
||||
}
|
||||
|
||||
static void
|
||||
sort_tree(struct joliet_tree_node *root)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
assert(root && ISO_ISDIR(root->iso_self));
|
||||
|
||||
qsort(root->children, root->nchildren, sizeof(void*), cmp_node);
|
||||
for (i = 0; i < root->nchildren; i++)
|
||||
if (ISO_ISDIR(root->children[i]->iso_self))
|
||||
sort_tree(root->children[i]);
|
||||
}
|
||||
|
||||
void
|
||||
joliet_prepare_path_tables(struct ecma119_write_target *t)
|
||||
{
|
||||
size_t cur, i, j;
|
||||
|
||||
t->pathlist_joliet[0] = t->joliet_root;
|
||||
t->path_table_size_joliet = 10; /* root directory record */
|
||||
cur = 1;
|
||||
|
||||
for (i = 0; i < t->dirlist_len; i++) {
|
||||
struct joliet_tree_node *dir = t->pathlist_joliet[i];
|
||||
for (j = 0; j < dir->nchildren; j++) {
|
||||
struct joliet_tree_node *ch = dir->children[j];
|
||||
if (ISO_ISDIR(ch->iso_self)) {
|
||||
size_t len = 8 + ucslen(ch->name)*2;
|
||||
t->pathlist_joliet[cur++] = ch;
|
||||
t->path_table_size_joliet += len;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the size of each directory.
|
||||
*/
|
||||
void
|
||||
joliet_calc_dir_size(struct ecma119_write_target *t,
|
||||
struct joliet_tree_node *root)
|
||||
{
|
||||
size_t i;
|
||||
struct joliet_tree_node *ch;
|
||||
|
||||
assert(root && ISO_ISDIR(root->iso_self));
|
||||
|
||||
root->len = 68; /* for "." and ".." entries */
|
||||
for (i = 0; i < root->nchildren; i++) {
|
||||
ch = root->children[i];
|
||||
root->len += ch->dirent_len;
|
||||
if (ISO_ISDIR(ch->iso_self))
|
||||
joliet_calc_dir_size(t, ch);
|
||||
}
|
||||
t->total_dir_size_joliet += round_up (root->len, t->block_size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the position of each directory. Also fill out t->dirlist_joliet.
|
||||
*/
|
||||
void
|
||||
joliet_calc_dir_pos(struct ecma119_write_target *t,
|
||||
struct joliet_tree_node *root)
|
||||
{
|
||||
size_t i;
|
||||
struct joliet_tree_node *ch;
|
||||
|
||||
assert(root && ISO_ISDIR(root->iso_self));
|
||||
|
||||
root->block = t->curblock;
|
||||
t->curblock += div_up(root->len, t->block_size);
|
||||
|
||||
t->dirlist_joliet[t->curfile++] = root;
|
||||
for (i = 0; i < root->nchildren; i++) {
|
||||
ch = root->children[i];
|
||||
if (ISO_ISDIR(ch->iso_self))
|
||||
joliet_calc_dir_pos(t, ch);
|
||||
}
|
||||
|
||||
/* reset curfile when we're finished */
|
||||
if (!root->parent)
|
||||
t->curfile = 0;
|
||||
}
|
||||
|
||||
void
|
||||
joliet_update_file_pos(struct ecma119_write_target *t,
|
||||
struct joliet_tree_node *dir)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
assert(dir && ISO_ISDIR(dir->iso_self));
|
||||
for (i = 0; i < dir->nchildren; i++) {
|
||||
struct joliet_tree_node *ch;
|
||||
ch = dir->children[i];
|
||||
|
||||
if (!ISO_ISDIR (ch->iso_self)) {
|
||||
struct iso_tree_node *iso = ch->iso_self;
|
||||
ch->block = iso->block;
|
||||
}
|
||||
else
|
||||
joliet_update_file_pos(t, ch);
|
||||
}
|
||||
|
||||
/* reset curfile when we're finished */
|
||||
if (!dir->parent)
|
||||
t->curfile = 0;
|
||||
}
|
||||
|
||||
struct joliet_tree_node*
|
||||
joliet_tree_create(struct ecma119_write_target *t,
|
||||
struct iso_tree_node *iso_root)
|
||||
{
|
||||
struct joliet_tree_node *root = create_tree(t, NULL, iso_root);
|
||||
|
||||
sort_tree(root);
|
||||
return root;
|
||||
}
|
||||
|
||||
/* ugh. this is mostly C&P */
|
||||
static void
|
||||
write_path_table(struct ecma119_write_target *t,
|
||||
int l_type,
|
||||
uint8_t *buf)
|
||||
{
|
||||
void (*write_int)(uint8_t*, uint32_t, int) = l_type ?
|
||||
iso_lsb : iso_msb;
|
||||
|
||||
size_t i;
|
||||
struct ecma119_path_table_record *rec;
|
||||
struct joliet_tree_node *dir;
|
||||
int parent = 0;
|
||||
|
||||
assert (t->joliet);
|
||||
|
||||
for (i = 0; i < t->dirlist_len; i++) {
|
||||
dir = t->pathlist_joliet[i];
|
||||
while ((i) && t->pathlist_joliet[parent] != dir->parent)
|
||||
parent++;
|
||||
assert(parent < i || i == 0);
|
||||
|
||||
rec = (struct ecma119_path_table_record*) buf;
|
||||
rec->len_di[0] = dir->parent ?
|
||||
(uint8_t) ucslen(dir->name) * 2 : 1;
|
||||
rec->len_xa[0] = 0;
|
||||
write_int(rec->block, dir->block, 4);
|
||||
write_int(rec->parent, parent + 1, 2);
|
||||
if (dir->parent)
|
||||
memcpy(rec->dir_id, dir->name, rec->len_di[0]);
|
||||
buf += 8 + rec->len_di[0] + (rec->len_di[0] % 2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* if file_id is >= 0, we use it instead of the filename. As a magic number,
|
||||
* file_id == 3 means that we are writing the root directory record (in order
|
||||
* to distinguish it from the "." entry in the root directory) */
|
||||
static void
|
||||
write_one_dir_record(struct ecma119_write_target *t,
|
||||
struct joliet_tree_node *node,
|
||||
int file_id,
|
||||
uint8_t *buf)
|
||||
{
|
||||
uint8_t len_dr = (file_id >= 0) ? 34 : node->dirent_len;
|
||||
uint8_t len_fi = (file_id >= 0) ? 1 : ucslen(node->name) * 2;
|
||||
uint8_t f_id = (uint8_t) ((file_id == 3) ? 0 : file_id);
|
||||
uint8_t *name = (file_id >= 0) ? &f_id : (uint8_t*)node->name;
|
||||
struct ecma119_dir_record *rec = (struct ecma119_dir_record*)buf;
|
||||
|
||||
if (file_id == 1 && node->parent)
|
||||
node = node->parent;
|
||||
|
||||
rec->len_dr[0] = len_dr;
|
||||
iso_bb(rec->block, node->block, 4);
|
||||
iso_bb(rec->length, node->len, 4);
|
||||
iso_datetime_7(rec->recording_time, t->now);
|
||||
rec->flags[0] = ISO_ISDIR(node->iso_self) ? 2 : 0;
|
||||
iso_bb(rec->vol_seq_number, t->volnum + 1, 2);
|
||||
rec->len_fi[0] = len_fi;
|
||||
memcpy(rec->file_id, name, len_fi);
|
||||
}
|
||||
|
||||
static void
|
||||
write_l_path_table(struct ecma119_write_target *t, uint8_t *buf)
|
||||
{
|
||||
write_path_table (t, 1, buf);
|
||||
}
|
||||
|
||||
static void
|
||||
write_m_path_table(struct ecma119_write_target *t, uint8_t *buf)
|
||||
{
|
||||
write_path_table (t, 0, buf);
|
||||
}
|
||||
|
||||
static void
|
||||
write_sup_vol_desc(struct ecma119_write_target *t, uint8_t *buf)
|
||||
{
|
||||
struct ecma119_sup_vol_desc *vol = (struct ecma119_sup_vol_desc*)buf;
|
||||
struct iso_volume *volume = t->volset->volume[t->volnum];
|
||||
uint16_t *vol_id = str2ucs(volume->volume_id);
|
||||
uint16_t *pub_id = str2ucs(volume->publisher_id);
|
||||
uint16_t *data_id = str2ucs(volume->data_preparer_id);
|
||||
uint16_t *volset_id = str2ucs(t->volset->volset_id);
|
||||
int vol_id_len = MIN(32, ucslen(vol_id) * 2);
|
||||
int pub_id_len = MIN(128, ucslen(pub_id) * 2);
|
||||
int data_id_len = MIN(128, ucslen(data_id) * 2);
|
||||
int volset_id_len = MIN(128, ucslen(volset_id) * 2);
|
||||
|
||||
vol->vol_desc_type[0] = 2;
|
||||
memcpy(vol->std_identifier, "CD001", 5);
|
||||
vol->vol_desc_version[0] = 1;
|
||||
memcpy(vol->system_id, "SYSID", 5);
|
||||
if (vol_id)
|
||||
memcpy(vol->volume_id, vol_id, vol_id_len);
|
||||
memcpy(vol->esc_sequences, "%/E", 3);
|
||||
iso_bb(vol->vol_space_size, t->vol_space_size, 4);
|
||||
iso_bb(vol->vol_set_size, t->volset->volset_size, 2);
|
||||
iso_bb(vol->vol_seq_number, t->volnum + 1, 2);
|
||||
iso_bb(vol->block_size, t->block_size, 2);
|
||||
iso_bb(vol->path_table_size, t->path_table_size_joliet, 4);
|
||||
iso_lsb(vol->l_path_table_pos, t->l_path_table_pos_joliet, 4);
|
||||
iso_msb(vol->m_path_table_pos, t->m_path_table_pos_joliet, 4);
|
||||
|
||||
write_one_dir_record(t, t->joliet_root, 3, vol->root_dir_record);
|
||||
|
||||
memcpy(vol->vol_set_id, volset_id, volset_id_len);
|
||||
memcpy(vol->publisher_id, pub_id, pub_id_len);
|
||||
memcpy(vol->data_prep_id, data_id, data_id_len);
|
||||
/*memcpy(vol->application_id, "APPID", app_id_len);*/
|
||||
|
||||
iso_datetime_17(vol->vol_creation_time, t->now);
|
||||
iso_datetime_17(vol->vol_modification_time, t->now);
|
||||
iso_datetime_17(vol->vol_effective_time, t->now);
|
||||
vol->file_structure_version[0] = 1;
|
||||
|
||||
free(vol_id);
|
||||
free(volset_id);
|
||||
free(pub_id);
|
||||
free(data_id);
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
write_one_dir(struct ecma119_write_target *t,
|
||||
struct joliet_tree_node *dir,
|
||||
uint8_t *buf)
|
||||
{
|
||||
size_t i;
|
||||
uint8_t *orig_buf = buf;
|
||||
|
||||
assert(ISO_ISDIR (dir->iso_self));
|
||||
/* write the "." and ".." entries first */
|
||||
write_one_dir_record(t, dir, 0, buf);
|
||||
buf += ((struct ecma119_dir_record*) buf)->len_dr[0];
|
||||
|
||||
write_one_dir_record(t, dir, 1, buf);
|
||||
buf += ((struct ecma119_dir_record*) buf)->len_dr[0];
|
||||
|
||||
for (i = 0; i < dir->nchildren; i++) {
|
||||
write_one_dir_record(t, dir->children[i], -1, buf);
|
||||
buf += ((struct ecma119_dir_record*) buf)->len_dr[0];
|
||||
}
|
||||
|
||||
assert (buf - orig_buf == dir->len);
|
||||
}
|
||||
|
||||
static void
|
||||
write_dirs(struct ecma119_write_target *t, uint8_t *buf)
|
||||
{
|
||||
size_t i;
|
||||
struct joliet_tree_node *dir;
|
||||
|
||||
assert (t->curblock == t->dirlist_joliet[0]->block);
|
||||
for (i = 0; i < t->dirlist_len; i++) {
|
||||
dir = t->dirlist_joliet[i];
|
||||
write_one_dir(t, dir, buf);
|
||||
buf += round_up(dir->len, t->block_size);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
joliet_wr_sup_vol_desc(struct ecma119_write_target *t,
|
||||
uint8_t *buf)
|
||||
{
|
||||
ecma119_start_chunking(t,
|
||||
write_sup_vol_desc,
|
||||
2048,
|
||||
buf);
|
||||
}
|
||||
|
||||
void
|
||||
joliet_wr_l_path_table(struct ecma119_write_target *t,
|
||||
uint8_t *buf)
|
||||
{
|
||||
ecma119_start_chunking(t,
|
||||
write_l_path_table,
|
||||
t->path_table_size_joliet,
|
||||
buf);
|
||||
}
|
||||
|
||||
void
|
||||
joliet_wr_m_path_table(struct ecma119_write_target *t,
|
||||
uint8_t *buf)
|
||||
{
|
||||
ecma119_start_chunking(t,
|
||||
write_m_path_table,
|
||||
t->path_table_size_joliet,
|
||||
buf);
|
||||
}
|
||||
|
||||
void
|
||||
joliet_wr_dir_records(struct ecma119_write_target *t,
|
||||
uint8_t *buf)
|
||||
{
|
||||
ecma119_start_chunking(t,
|
||||
write_dirs,
|
||||
t->total_dir_size_joliet,
|
||||
buf);
|
||||
}
|
||||
|
@ -1,84 +0,0 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
/* vim: set noet ts=8 sts=8 sw=8 : */
|
||||
|
||||
/**
|
||||
* \file joliet.h
|
||||
*
|
||||
* Declare the filesystems trees that are Joliet-compatible and the public
|
||||
* functions for tying them into an ecma119 volume.
|
||||
*/
|
||||
|
||||
#ifndef LIBISO_JOLIET_H
|
||||
#define LIBISO_JOLIET_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
struct ecma119_write_target;
|
||||
struct iso_tree_node;
|
||||
|
||||
struct joliet_tree_node
|
||||
{
|
||||
uint16_t *name; /**< In UCS-2BE. */
|
||||
size_t dirent_len;
|
||||
size_t len;
|
||||
size_t block;
|
||||
|
||||
struct joliet_tree_node *parent;
|
||||
struct iso_tree_node *iso_self;
|
||||
struct ecma119_write_target *target;
|
||||
|
||||
struct joliet_tree_node **children;
|
||||
size_t nchildren;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new joliet_tree that corresponds to the tree represented by
|
||||
* \p iso_root.
|
||||
*/
|
||||
struct joliet_tree_node*
|
||||
joliet_tree_create(struct ecma119_write_target *target,
|
||||
struct iso_tree_node *iso_root);
|
||||
|
||||
/**
|
||||
* Calculate the size of each directory in the joliet heirarchy.
|
||||
*/
|
||||
void
|
||||
joliet_calc_dir_size(struct ecma119_write_target *t, struct joliet_tree_node*);
|
||||
|
||||
/**
|
||||
* Calculate the position of each directory in the joliet heirarchy.
|
||||
*/
|
||||
void
|
||||
joliet_calc_dir_pos(struct ecma119_write_target *t, struct joliet_tree_node*);
|
||||
|
||||
/**
|
||||
* Update the position of each file in the joliet hierarchy (to be called
|
||||
* AFTER the file positions in the iso tree have been set).
|
||||
*/
|
||||
void
|
||||
joliet_update_file_pos(struct ecma119_write_target *t, struct joliet_tree_node*);
|
||||
|
||||
/**
|
||||
* Calculate the size of the joliet path table and fill in the list of
|
||||
* directories.
|
||||
*/
|
||||
void
|
||||
joliet_prepare_path_tables(struct ecma119_write_target *t);
|
||||
|
||||
void
|
||||
joliet_tree_free(struct joliet_tree_node *root);
|
||||
|
||||
void
|
||||
joliet_wr_sup_vol_desc(struct ecma119_write_target *t, uint8_t *buf);
|
||||
|
||||
void
|
||||
joliet_wr_l_path_table(struct ecma119_write_target *t, uint8_t *buf);
|
||||
|
||||
void
|
||||
joliet_wr_m_path_table(struct ecma119_write_target *t, uint8_t *buf);
|
||||
|
||||
void
|
||||
joliet_wr_dir_records(struct ecma119_write_target *t, uint8_t *buf);
|
||||
|
||||
#endif /* LIBISO_JOLIET_H */
|
@ -1,225 +0,0 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
/* vim: set noet ts=8 sts=8 sw=8 : */
|
||||
|
||||
/**
|
||||
* Create an ISO-9660 data volume with Rock Ridge and Joliet extensions.
|
||||
* Usage is easy:
|
||||
* - Create a new volume.
|
||||
* - Add files and directories.
|
||||
* - Write the volume to a file or create a burn source for use with Libburn.
|
||||
*/
|
||||
|
||||
#ifndef LIBISO_LIBISOFS_H
|
||||
#define LIBISO_LIBISOFS_H
|
||||
|
||||
/* #include <libburn.h> */
|
||||
struct burn_source;
|
||||
|
||||
/**
|
||||
* Data volume.
|
||||
* @see volume.h for details.
|
||||
*/
|
||||
struct iso_volume;
|
||||
|
||||
/**
|
||||
* A set of data volumes.
|
||||
* @see volume.h for details.
|
||||
*/
|
||||
struct iso_volset;
|
||||
|
||||
/**
|
||||
* A node in the filesystem tree.
|
||||
* \see tree.h
|
||||
*/
|
||||
struct iso_tree_node;
|
||||
|
||||
enum ecma119_extension_flag {
|
||||
ECMA119_ROCKRIDGE = (1<<0),
|
||||
ECMA119_JOLIET = (1<<1)
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new volume.
|
||||
* The parameters can be set to NULL if you wish to set them later.
|
||||
*/
|
||||
struct iso_volume *iso_volume_new(const char *volume_id,
|
||||
const char *publisher_id,
|
||||
const char *data_preparer_id);
|
||||
|
||||
struct iso_volume *iso_volume_new_with_root(const char *volume_id,
|
||||
const char *publisher_id,
|
||||
const char *data_preparer_id,
|
||||
struct iso_tree_node *root);
|
||||
|
||||
/**
|
||||
* Free a volume.
|
||||
*/
|
||||
void iso_volume_free(struct iso_volume *volume);
|
||||
|
||||
/**
|
||||
* Free a set of data volumes.
|
||||
*/
|
||||
void iso_volset_free(struct iso_volset *volume);
|
||||
|
||||
/**
|
||||
* Get the root directory for a volume.
|
||||
*/
|
||||
struct iso_tree_node *iso_volume_get_root(const struct iso_volume *volume);
|
||||
|
||||
/**
|
||||
* Fill in the volume identifier for a volume.
|
||||
*/
|
||||
void iso_volume_set_volume_id(struct iso_volume *volume,
|
||||
const char *volume_id);
|
||||
|
||||
/**
|
||||
* Fill in the publisher for a volume.
|
||||
*/
|
||||
void iso_volume_set_publisher_id(struct iso_volume *volume,
|
||||
const char *publisher_id);
|
||||
|
||||
/**
|
||||
* Fill in the data preparer for a volume.
|
||||
*/
|
||||
void iso_volume_set_data_preparer_id(struct iso_volume *volume,
|
||||
const char *data_preparer_id);
|
||||
|
||||
/**
|
||||
* Locate a node by its path on disc.
|
||||
*
|
||||
* \param volume The volume to search in.
|
||||
* \param path The path, in the image, of the file.
|
||||
*
|
||||
* \return The node found or NULL.
|
||||
*
|
||||
*/
|
||||
struct iso_tree_node *iso_tree_volume_path_to_node(struct iso_volume *volume, const char *path);
|
||||
|
||||
/**
|
||||
* Add a file or a directory (recursively) to a volume by specifying its path on the volume.
|
||||
*
|
||||
* \param volume The volume to add the file to.
|
||||
* \param disc_path The path on the disc at which to add the disc.
|
||||
* \param path The path, on the local filesystem, of the file.
|
||||
*
|
||||
* \return The node for the file or NULL if the parent doesn't exists on the disc.
|
||||
*/
|
||||
struct iso_tree_node *iso_tree_volume_add_path(struct iso_volume *volume,
|
||||
const char *disc_path,
|
||||
const char *path);
|
||||
|
||||
/**
|
||||
* Creates a new, empty directory on the volume.
|
||||
*
|
||||
* \param volume The volume to add the directory to.
|
||||
* \param disc_path The path on the volume at which to add the directory.
|
||||
*
|
||||
* \return A pointer to the newly created directory.
|
||||
*/
|
||||
struct iso_tree_node *iso_tree_volume_add_new_dir(struct iso_volume *volume,
|
||||
const char *disc_path);
|
||||
|
||||
/**
|
||||
* Create a new Volume Set consisting of only one volume.
|
||||
* @param volume The first and only volume for the volset to contain.
|
||||
* @param volset_id The Volume Set ID.
|
||||
* @return A new iso_volset.
|
||||
*/
|
||||
struct iso_volset *iso_volset_new(struct iso_volume *volume,
|
||||
const char *volset_id);
|
||||
|
||||
/**
|
||||
* Add a file to a directory.
|
||||
*
|
||||
* \param path The path, on the local filesystem, of the file.
|
||||
*
|
||||
* \pre \p parent is NULL or is a directory.
|
||||
* \pre \p path is non-NULL and is a valid path to a non-directory on the local
|
||||
* filesystem.
|
||||
* \return An iso_tree_node whose path is \p path and whose parent is \p parent.
|
||||
*/
|
||||
struct iso_tree_node *iso_tree_add_node(struct iso_tree_node *parent,
|
||||
const char *path);
|
||||
|
||||
/**
|
||||
* Recursively add an existing directory to the tree.
|
||||
* Warning: when using this, you'll lose pointers to files or subdirectories.
|
||||
* If you want to have pointers to all files and directories,
|
||||
* use iso_tree_add_file and iso_tree_add_dir.
|
||||
*
|
||||
* \param path The path, on the local filesystem, of the directory to add.
|
||||
*
|
||||
* \pre \p parent is NULL or is a directory.
|
||||
* \pre \p path is non-NULL and is a valid path to a directory on the local
|
||||
* filesystem.
|
||||
* \return a pointer to the newly created directory.
|
||||
*/
|
||||
struct iso_tree_node *iso_tree_radd_dir(struct iso_tree_node *parent,
|
||||
const char *path);
|
||||
|
||||
|
||||
/**
|
||||
* Add the path of a file or directory to ignore when adding a directory recursively.
|
||||
*
|
||||
* \param path The path, on the local filesystem, of the file.
|
||||
*/
|
||||
void iso_exclude_add_path(const char *path);
|
||||
|
||||
/**
|
||||
* Remove a path that was set to be ignored when adding a directory recusively.
|
||||
*
|
||||
* \param path The path, on the local filesystem, of the file.
|
||||
*/
|
||||
void iso_exclude_remove_path(const char *path);
|
||||
|
||||
/**
|
||||
* Remove all paths that were set to be ignored when adding a directory recusively.
|
||||
*/
|
||||
void iso_exclude_empty(void);
|
||||
|
||||
/**
|
||||
* Creates a new, empty directory on the volume.
|
||||
*
|
||||
* \pre \p parent is NULL or is a directory.
|
||||
* \pre \p name is unique among the children and files belonging to \p parent.
|
||||
* Also, it doesn't contain '/' characters.
|
||||
*
|
||||
* \post \p parent contains a child directory whose name is \p name and whose
|
||||
* POSIX attributes are the same as \p parent's.
|
||||
* \return a pointer to the newly created directory.
|
||||
*/
|
||||
struct iso_tree_node *iso_tree_add_new_dir(struct iso_tree_node *parent,
|
||||
const char *name);
|
||||
|
||||
/**
|
||||
* Set the name of a file (using the current locale).
|
||||
*/
|
||||
void iso_tree_node_set_name(struct iso_tree_node *file, const char *name);
|
||||
|
||||
/**
|
||||
* Recursively print a directory to stdout.
|
||||
* \param spaces The initial number of spaces on the left. Set to 0 if you
|
||||
* supply a root directory.
|
||||
*/
|
||||
void iso_tree_print(const struct iso_tree_node *root, int spaces);
|
||||
|
||||
/** Create a burn_source which can be used as a data source for a track
|
||||
*
|
||||
* The volume set used to create the libburn_source can _not_ be modified
|
||||
* until the libburn_source is freed.
|
||||
*
|
||||
* \param volumeset The volume set from which you want to write
|
||||
* \param volnum The volume in the set which you want to write (usually 0)
|
||||
* \param level ISO level to write at.
|
||||
* \param flags Which extensions to support.
|
||||
*
|
||||
* \pre \p volumeset is non-NULL
|
||||
* \pre \p volnum is less than \p volset->volset_size.
|
||||
* \return A burn_source to be used for the data source for a track
|
||||
*/
|
||||
struct burn_source* iso_source_new_ecma119 (struct iso_volset *volumeset,
|
||||
int volnum,
|
||||
int level,
|
||||
int flags);
|
||||
|
||||
#endif /* LIBISO_LIBISOFS_H */
|
@ -1,300 +0,0 @@
|
||||
/* vim: set noet ts=8 sts=8 sw=8 : */
|
||||
|
||||
#include "rockridge.h"
|
||||
#include "util.h"
|
||||
#include "ecma119.h"
|
||||
#include "ecma119_tree.h"
|
||||
#include "tree.h"
|
||||
#include "susp.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
/* create a PX field from the permissions on the current node. */
|
||||
uint8_t *rrip_make_PX(struct ecma119_write_target *t,
|
||||
struct ecma119_tree_node *node)
|
||||
{
|
||||
uint8_t *PX = malloc(44);
|
||||
|
||||
PX[0] = 'P';
|
||||
PX[1] = 'X';
|
||||
PX[2] = 44;
|
||||
PX[3] = 1;
|
||||
iso_bb(&PX[4], node->iso_self->attrib.st_mode, 4);
|
||||
iso_bb(&PX[12], node->iso_self->attrib.st_nlink, 4);
|
||||
iso_bb(&PX[20], node->iso_self->attrib.st_uid, 4);
|
||||
iso_bb(&PX[28], node->iso_self->attrib.st_gid, 4);
|
||||
iso_bb(&PX[36], node->iso_self->attrib.st_ino, 4);
|
||||
return PX;
|
||||
}
|
||||
|
||||
/** See IEEE 1282 4.1.1 */
|
||||
void rrip_add_PX(struct ecma119_write_target *t, struct ecma119_tree_node *node)
|
||||
{
|
||||
susp_append(t, &node->susp, rrip_make_PX(t, node));
|
||||
if (node->type == ECMA119_DIR) {
|
||||
susp_append(t, &node->dir.self_susp, rrip_make_PX(t, node));
|
||||
susp_append(t, &node->dir.parent_susp, rrip_make_PX(t, node));
|
||||
}
|
||||
}
|
||||
|
||||
void rrip_add_PN(struct ecma119_write_target *t, struct ecma119_tree_node *node)
|
||||
{
|
||||
uint8_t *PN = malloc(20);
|
||||
|
||||
PN[0] = 'P';
|
||||
PN[1] = 'N';
|
||||
PN[2] = 20;
|
||||
PN[3] = 1;
|
||||
iso_bb(&PN[4], node->iso_self->attrib.st_dev >> 32, 4);
|
||||
iso_bb(&PN[12], node->iso_self->attrib.st_dev & 0xffffffff, 4);
|
||||
susp_append(t, &node->susp, PN);
|
||||
}
|
||||
|
||||
static void rrip_SL_append_comp(int *n, uint8_t ***comps,
|
||||
char *s, int size, char fl)
|
||||
{
|
||||
uint8_t *comp = malloc(size + 2);
|
||||
|
||||
(*n)++;
|
||||
comp[0] = fl;
|
||||
comp[1] = size;
|
||||
*comps = realloc(*comps, (*n) * sizeof(void*));
|
||||
(*comps)[(*n) - 1] = comp;
|
||||
|
||||
if (size) {
|
||||
memcpy(&comp[2], s, size);
|
||||
}
|
||||
}
|
||||
|
||||
static void rrip_SL_add_component(char *prev, char *cur, int *n_comp,
|
||||
uint8_t ***comps)
|
||||
{
|
||||
int size = cur - prev;
|
||||
|
||||
if (size == 0) {
|
||||
rrip_SL_append_comp(n_comp, comps, prev, 0, 1 << 3);
|
||||
return;
|
||||
}
|
||||
|
||||
if (size == 1 && prev[0] == '.') {
|
||||
rrip_SL_append_comp(n_comp, comps, prev, 0, 1 << 1);
|
||||
return;
|
||||
}
|
||||
if (size == 2 && !strncmp(prev, "..", 2)) {
|
||||
rrip_SL_append_comp(n_comp, comps, prev, 0, 1 << 2);
|
||||
return;
|
||||
}
|
||||
|
||||
/* we can't make a component any bigger than 250 (is this really a
|
||||
problem)? because then it won't fit inside the SL field */
|
||||
while (size > 248) {
|
||||
size -= 248;
|
||||
rrip_SL_append_comp(n_comp, comps, prev, 248, 1 << 0);
|
||||
}
|
||||
|
||||
rrip_SL_append_comp(n_comp, comps, prev, size, 0);
|
||||
}
|
||||
|
||||
void rrip_add_SL(struct ecma119_write_target *t, struct ecma119_tree_node *node)
|
||||
{
|
||||
int ret, pathsize = 0;
|
||||
char *path = NULL, *cur, *prev;
|
||||
int i, j;
|
||||
|
||||
uint8_t **comp = NULL;
|
||||
int n_comp = 0;
|
||||
int total_comp_len = 0;
|
||||
int written = 0, pos;
|
||||
|
||||
uint8_t *SL;
|
||||
|
||||
do {
|
||||
pathsize += 128;
|
||||
path = realloc(path, pathsize);
|
||||
/* FIXME: what if the file is not on the local fs? */
|
||||
ret = readlink(node->iso_self->loc.path, path, pathsize);
|
||||
} while (ret == pathsize);
|
||||
if (ret == -1) {
|
||||
fprintf(stderr, "Error: couldn't read symlink: %s\n",
|
||||
strerror(errno));
|
||||
return;
|
||||
}
|
||||
path[ret] = '\0';
|
||||
|
||||
prev = path;
|
||||
for (cur = strchr(path, '/'); cur && *cur; cur = strchr(cur, '/')) {
|
||||
rrip_SL_add_component(prev, cur, &n_comp, &comp);
|
||||
cur++;
|
||||
prev = cur;
|
||||
}
|
||||
|
||||
/* if there was no trailing '/', we need to add the last component. */
|
||||
if (prev == path || prev != &path[ret - 1]) {
|
||||
rrip_SL_add_component(prev, &path[ret], &n_comp, &comp);
|
||||
}
|
||||
|
||||
for (i = 0; i < n_comp; i++) {
|
||||
total_comp_len += comp[i][1] + 2;
|
||||
if (total_comp_len > 250) {
|
||||
total_comp_len -= comp[i][1] + 2;
|
||||
SL = malloc(total_comp_len + 5);
|
||||
SL[0] = 'S';
|
||||
SL[1] = 'L';
|
||||
SL[2] = total_comp_len + 5;
|
||||
SL[3] = 1;
|
||||
SL[4] = 1; /* CONTINUE */
|
||||
pos = 5;
|
||||
for (j = written; j < i; j++) {
|
||||
memcpy(&SL[pos], comp[j], comp[j][2]);
|
||||
pos += comp[j][2];
|
||||
}
|
||||
susp_append(t, &node->susp, SL);
|
||||
written = i - 1;
|
||||
total_comp_len = comp[i][1];
|
||||
}
|
||||
}
|
||||
SL = malloc(total_comp_len + 5);
|
||||
SL[0] = 'S';
|
||||
SL[1] = 'L';
|
||||
SL[2] = total_comp_len + 5;
|
||||
SL[3] = 1;
|
||||
SL[4] = 0;
|
||||
pos = 5;
|
||||
|
||||
for (j = written; j < n_comp; j++) {
|
||||
memcpy(&SL[pos], comp[j], comp[j][1] + 2);
|
||||
pos += comp[j][1] + 2;
|
||||
}
|
||||
susp_append(t, &node->susp, SL);
|
||||
|
||||
free(path);
|
||||
/* free the components */
|
||||
for (i = 0; i < n_comp; i++) {
|
||||
free(comp[i]);
|
||||
}
|
||||
free(comp);
|
||||
}
|
||||
|
||||
static void rrip_add_NM_single(struct ecma119_write_target *t,
|
||||
struct susp_info *susp,
|
||||
char *name, int size, int flags)
|
||||
{
|
||||
uint8_t *NM = malloc(size + 5);
|
||||
|
||||
NM[0] = 'N';
|
||||
NM[1] = 'M';
|
||||
NM[2] = size + 5;
|
||||
NM[3] = 1;
|
||||
NM[4] = flags;
|
||||
if (size) {
|
||||
memcpy(&NM[5], name, size);
|
||||
}
|
||||
susp_append(t, susp, NM);
|
||||
}
|
||||
|
||||
void
|
||||
rrip_add_NM(struct ecma119_write_target *t, struct ecma119_tree_node *node)
|
||||
{
|
||||
char *name = iso_p_fileid(node->iso_self->name);
|
||||
int len = name ? strlen(name) : 0;
|
||||
char *pos = name;
|
||||
|
||||
if (!len)
|
||||
return;
|
||||
|
||||
if (node->type == ECMA119_DIR) {
|
||||
rrip_add_NM_single(t, &node->dir.self_susp, pos, 0, 1 << 1);
|
||||
rrip_add_NM_single(t, &node->dir.parent_susp, pos, 0, 1 << 2);
|
||||
}
|
||||
|
||||
while (len > 250) {
|
||||
rrip_add_NM_single(t, &node->susp, pos, 250, 1);
|
||||
len -= 250;
|
||||
pos += 250;
|
||||
}
|
||||
rrip_add_NM_single(t, &node->susp, pos, len, 0);
|
||||
}
|
||||
|
||||
void rrip_add_CL(struct ecma119_write_target *t, struct ecma119_tree_node *node)
|
||||
{
|
||||
uint8_t *CL = calloc(1, 12);
|
||||
|
||||
CL[0] = 'C';
|
||||
CL[1] = 'L';
|
||||
CL[2] = 12;
|
||||
CL[3] = 1;
|
||||
susp_append(t, &node->susp, CL);
|
||||
}
|
||||
|
||||
void
|
||||
rrip_add_PL(struct ecma119_write_target *t, struct ecma119_tree_node *node)
|
||||
{
|
||||
uint8_t *PL = calloc(1, 12);
|
||||
|
||||
PL[0] = 'P';
|
||||
PL[1] = 'L';
|
||||
PL[2] = 12;
|
||||
PL[3] = 1;
|
||||
susp_append(t, &node->dir.parent_susp, PL);
|
||||
}
|
||||
|
||||
void
|
||||
rrip_add_RE(struct ecma119_write_target *t, struct ecma119_tree_node *node)
|
||||
{
|
||||
uint8_t *RE = malloc(4);
|
||||
|
||||
RE[0] = 'R';
|
||||
RE[1] = 'E';
|
||||
RE[2] = 4;
|
||||
RE[3] = 1;
|
||||
susp_append(t, &node->susp, RE);
|
||||
}
|
||||
|
||||
void
|
||||
rrip_add_TF(struct ecma119_write_target *t, struct ecma119_tree_node *node)
|
||||
{
|
||||
uint8_t *TF = malloc(5 + 3 * 7);
|
||||
|
||||
TF[0] = 'T';
|
||||
TF[1] = 'F';
|
||||
TF[2] = 5 + 3 * 7;
|
||||
TF[3] = 1;
|
||||
TF[4] = (1 << 1) | (1 << 2) | (1 << 3) | (1 << 7);
|
||||
iso_datetime_7(&TF[5], node->iso_self->attrib.st_mtime);
|
||||
iso_datetime_7(&TF[12], node->iso_self->attrib.st_atime);
|
||||
iso_datetime_7(&TF[19], node->iso_self->attrib.st_ctime);
|
||||
susp_append(t, &node->susp, TF);
|
||||
}
|
||||
|
||||
void
|
||||
rrip_finalize(struct ecma119_write_target *t, struct ecma119_tree_node *dir)
|
||||
{
|
||||
int i;
|
||||
|
||||
assert(dir->type == ECMA119_DIR);
|
||||
|
||||
if (dir->parent != dir->dir.real_parent) {
|
||||
uint8_t *PL = susp_find(&dir->dir.parent_susp, "PL");
|
||||
|
||||
assert(PL);
|
||||
iso_bb(&PL[4], dir->dir.real_parent->block, 4);
|
||||
}
|
||||
|
||||
for (i = 0; i < dir->dir.nchildren; i++) {
|
||||
struct ecma119_tree_node *ch = dir->dir.children[i];
|
||||
|
||||
if (ch->type == ECMA119_FILE && ch->file.real_me) {
|
||||
uint8_t *CL = susp_find(&ch->susp, "CL");
|
||||
|
||||
assert(CL);
|
||||
iso_bb(&CL[4], ch->file.real_me->block, 4);
|
||||
} else if (ch->type == ECMA119_DIR) {
|
||||
rrip_finalize(t, ch);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
/* vim: set noet ts=8 sts=8 sw=8 : */
|
||||
|
||||
/** Functions and structures used for Rock Ridge support. */
|
||||
|
||||
#ifndef ISO_ROCKRIDGE_H
|
||||
#define ISO_ROCKRIDGE_H
|
||||
|
||||
struct ecma119_write_target;
|
||||
struct ecma119_tree_node;
|
||||
|
||||
void rrip_add_PX(struct ecma119_write_target *, struct ecma119_tree_node *);
|
||||
void rrip_add_PN(struct ecma119_write_target *, struct ecma119_tree_node *);
|
||||
void rrip_add_SL(struct ecma119_write_target *, struct ecma119_tree_node *);
|
||||
void rrip_add_NM(struct ecma119_write_target *, struct ecma119_tree_node *);
|
||||
void rrip_add_CL(struct ecma119_write_target *, struct ecma119_tree_node *);
|
||||
void rrip_add_RE(struct ecma119_write_target *, struct ecma119_tree_node *);
|
||||
void rrip_add_TF(struct ecma119_write_target *, struct ecma119_tree_node *);
|
||||
|
||||
/* This is special because it doesn't modify the susp fields of the directory
|
||||
* that gets passed to it; it modifies the susp fields of the ".." entry in
|
||||
* that directory. */
|
||||
void rrip_add_PL(struct ecma119_write_target *, struct ecma119_tree_node *);
|
||||
|
||||
void rrip_finalize(struct ecma119_write_target *, struct ecma119_tree_node *);
|
||||
|
||||
#endif /* ISO_ROCKRIDGE_H */
|
280
libisofs/susp.c
280
libisofs/susp.c
@ -1,280 +0,0 @@
|
||||
/* vim: set noet ts=8 sts=8 sw=8 : */
|
||||
|
||||
#include "susp.h"
|
||||
#include "util.h"
|
||||
#include "ecma119.h"
|
||||
#include "ecma119_tree.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
void susp_insert(struct ecma119_write_target *t,
|
||||
struct susp_info *susp,
|
||||
uint8_t *data,
|
||||
int pos)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (pos < 0) {
|
||||
pos = susp->n_susp_fields;
|
||||
}
|
||||
|
||||
assert(pos <= susp->n_susp_fields);
|
||||
susp->n_susp_fields++;
|
||||
susp->susp_fields = realloc(susp->susp_fields,
|
||||
sizeof(void*) * susp->n_susp_fields);
|
||||
|
||||
for (i = susp->n_susp_fields-1; i > pos; i--) {
|
||||
susp->susp_fields[i] = susp->susp_fields[i - 1];
|
||||
}
|
||||
susp->susp_fields[pos] = data;
|
||||
}
|
||||
|
||||
void susp_append(struct ecma119_write_target *t,
|
||||
struct susp_info *susp,
|
||||
uint8_t *data)
|
||||
{
|
||||
susp_insert(t, susp, data, susp->n_susp_fields);
|
||||
}
|
||||
|
||||
uint8_t *susp_find(struct susp_info *susp, const char *name)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < susp->n_susp_fields; i++) {
|
||||
if (!strncmp((char *)susp->susp_fields[i], name, 2)) {
|
||||
return susp->susp_fields[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** Utility function for susp_add_CE because susp_add_CE needs to act 3 times
|
||||
* on directories (for the "." and ".." entries.
|
||||
*
|
||||
* \param len The amount of space available for the System Use area.
|
||||
*/
|
||||
#define CE_LEN 28
|
||||
static unsigned char *susp_add_single_CE(struct ecma119_write_target *t,
|
||||
struct susp_info *susp,
|
||||
int len)
|
||||
{
|
||||
int susp_length = 0, tmp_len;
|
||||
int i;
|
||||
unsigned char *CE;
|
||||
|
||||
for (i = 0; i < susp->n_susp_fields; i++) {
|
||||
susp_length += susp->susp_fields[i][2];
|
||||
}
|
||||
if (susp_length <= len) {
|
||||
/* no need for a CE field */
|
||||
susp->non_CE_len = susp_length;
|
||||
susp->n_fields_fit = susp->n_susp_fields;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tmp_len = susp_length;
|
||||
for (i = susp->n_susp_fields - 1; i >= 0; i--) {
|
||||
tmp_len -= susp->susp_fields[i][2];
|
||||
if (tmp_len + CE_LEN <= len) {
|
||||
susp->non_CE_len = tmp_len + CE_LEN;
|
||||
susp->CE_len = susp_length - tmp_len;
|
||||
|
||||
/* i+1 because we have to count the CE field */
|
||||
susp->n_fields_fit = i + 1;
|
||||
|
||||
CE = calloc(1, CE_LEN);
|
||||
/* we don't fill in the BLOCK LOCATION or OFFSET
|
||||
fields yet. */
|
||||
CE[0] = 'C';
|
||||
CE[1] = 'E';
|
||||
CE[2] = (char)CE_LEN;
|
||||
CE[3] = (char)1;
|
||||
iso_bb(&CE[20], susp_length - tmp_len, 4);
|
||||
|
||||
return CE;
|
||||
}
|
||||
}
|
||||
assert(0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
try_add_CE(struct ecma119_write_target *t,
|
||||
struct susp_info *susp,
|
||||
size_t dirent_len)
|
||||
{
|
||||
uint8_t *CE = susp_add_single_CE(t, susp, 255 - dirent_len);
|
||||
if (CE)
|
||||
susp_insert(t, susp, CE, susp->n_fields_fit - 1);
|
||||
}
|
||||
|
||||
/** See IEEE P1281 Draft Version 1.12/5.2. Because this function depends on the
|
||||
* length of the other SUSP fields, it should always be calculated last. */
|
||||
void
|
||||
susp_add_CE(struct ecma119_write_target *t, struct ecma119_tree_node *node)
|
||||
{
|
||||
try_add_CE(t, &node->susp, node->dirent_len);
|
||||
if (node->type == ECMA119_DIR) {
|
||||
try_add_CE(t, &node->dir.self_susp, 34);
|
||||
try_add_CE(t, &node->dir.parent_susp, 34);
|
||||
}
|
||||
}
|
||||
|
||||
/** See IEEE P1281 Draft Version 1.12/5.3 */
|
||||
void
|
||||
susp_add_SP(struct ecma119_write_target *t, struct ecma119_tree_node *dir)
|
||||
{
|
||||
unsigned char *SP = malloc(7);
|
||||
|
||||
assert(dir->type == ECMA119_DIR);
|
||||
|
||||
SP[0] = 'S';
|
||||
SP[1] = 'P';
|
||||
SP[2] = (char)7;
|
||||
SP[3] = (char)1;
|
||||
SP[4] = 0xbe;
|
||||
SP[5] = 0xef;
|
||||
SP[6] = 0;
|
||||
susp_append(t, &dir->dir.self_susp, SP);
|
||||
}
|
||||
|
||||
#if 0
|
||||
/** See IEEE P1281 Draft Version 1.12/5.4 */
|
||||
static void susp_add_ST(struct ecma119_write_target *t,
|
||||
struct iso_tree_node *node)
|
||||
{
|
||||
unsigned char *ST = malloc(4);
|
||||
|
||||
ST[0] = 'S';
|
||||
ST[1] = 'T';
|
||||
ST[2] = (char)4;
|
||||
ST[3] = (char)1;
|
||||
susp_append(t, node, ST);
|
||||
}
|
||||
#endif
|
||||
|
||||
/** See IEEE P1281 Draft Version 1.12/5.5 FIXME: this is rockridge */
|
||||
void
|
||||
rrip_add_ER(struct ecma119_write_target *t, struct ecma119_tree_node *dir)
|
||||
{
|
||||
unsigned char *ER = malloc(182);
|
||||
|
||||
assert(dir->type == ECMA119_DIR);
|
||||
|
||||
ER[0] = 'E';
|
||||
ER[1] = 'R';
|
||||
ER[2] = 182;
|
||||
ER[3] = 1;
|
||||
ER[4] = 9;
|
||||
ER[5] = 72;
|
||||
ER[6] = 93;
|
||||
ER[7] = 1;
|
||||
memcpy(&ER[8], "IEEE_1282", 9);
|
||||
memcpy(&ER[17], "THE IEEE 1282 PROTOCOL PROVIDES SUPPORT FOR POSIX "
|
||||
"FILE SYSTEM SEMANTICS.", 72);
|
||||
memcpy(&ER[89], "PLEASE CONTACT THE IEEE STANDARDS DEPARTMENT, "
|
||||
"PISCATAWAY, NJ, USA FOR THE 1282 SPECIFICATION.", 93);
|
||||
susp_append(t, &dir->dir.self_susp, ER);
|
||||
}
|
||||
|
||||
/* calculate the location of the CE areas. Since CE areas don't need to be
|
||||
* aligned to a block boundary, we contatenate all CE areas from a single
|
||||
* directory and dump them immediately after all the directory records.
|
||||
*
|
||||
* Requires that the following be known:
|
||||
* - position of the current directory (dir->block)
|
||||
* - length of the current directory (dir->dir.len)
|
||||
* - sum of the children's CE lengths (dir->dir.CE_len)
|
||||
*/
|
||||
static void
|
||||
susp_fin_1_CE(struct ecma119_write_target *t,
|
||||
struct susp_info *susp,
|
||||
size_t block,
|
||||
size_t *offset)
|
||||
{
|
||||
uint8_t *CE = susp->susp_fields[susp->n_fields_fit - 1];
|
||||
|
||||
if (!susp->CE_len) {
|
||||
return;
|
||||
}
|
||||
iso_bb(&CE[4], block + (*offset) / t->block_size, 4);
|
||||
iso_bb(&CE[12], (*offset) % t->block_size, 4);
|
||||
*offset += susp->CE_len;
|
||||
}
|
||||
|
||||
static void susp_fin_CE(struct ecma119_write_target *t,
|
||||
struct ecma119_tree_node *dir)
|
||||
{
|
||||
int i;
|
||||
size_t CE_offset = dir->dir.len;
|
||||
|
||||
assert(dir->type == ECMA119_DIR);
|
||||
|
||||
susp_fin_1_CE(t, &dir->dir.self_susp, dir->block, &CE_offset);
|
||||
susp_fin_1_CE(t, &dir->dir.parent_susp, dir->block, &CE_offset);
|
||||
|
||||
for (i = 0; i < dir->dir.nchildren; i++) {
|
||||
struct ecma119_tree_node *ch = dir->dir.children[i];
|
||||
susp_fin_1_CE(t, &ch->susp, dir->block, &CE_offset);
|
||||
}
|
||||
assert(CE_offset == dir->dir.len + dir->dir.CE_len);
|
||||
}
|
||||
|
||||
void
|
||||
susp_finalize(struct ecma119_write_target *t, struct ecma119_tree_node *dir)
|
||||
{
|
||||
int i;
|
||||
|
||||
assert(dir->type = ECMA119_DIR);
|
||||
|
||||
if (dir->dir.depth != 1) {
|
||||
susp_fin_CE(t, dir);
|
||||
}
|
||||
|
||||
for (i = 0; i < dir->dir.nchildren; i++) {
|
||||
if (dir->dir.children[i]->type == ECMA119_DIR)
|
||||
susp_finalize(t, dir->dir.children[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void susp_write(struct ecma119_write_target *t,
|
||||
struct susp_info *susp,
|
||||
unsigned char *buf)
|
||||
{
|
||||
int i;
|
||||
int pos = 0;
|
||||
|
||||
for (i = 0; i < susp->n_fields_fit; i++) {
|
||||
memcpy(&buf[pos], susp->susp_fields[i],
|
||||
susp->susp_fields[i][2]);
|
||||
pos += susp->susp_fields[i][2];
|
||||
}
|
||||
}
|
||||
|
||||
void susp_write_CE(struct ecma119_write_target *t, struct susp_info *susp,
|
||||
unsigned char *buf)
|
||||
{
|
||||
int i;
|
||||
int pos = 0;
|
||||
|
||||
for (i = susp->n_fields_fit; i < susp->n_susp_fields; i++) {
|
||||
memcpy(&buf[pos], susp->susp_fields[i],
|
||||
susp->susp_fields[i][2]);
|
||||
pos += susp->susp_fields[i][2];
|
||||
}
|
||||
}
|
||||
|
||||
void susp_free_fields(struct susp_info *susp)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; i<susp->n_susp_fields; i++) {
|
||||
free(susp->susp_fields[i]);
|
||||
}
|
||||
if (susp->susp_fields) {
|
||||
free(susp->susp_fields);
|
||||
}
|
||||
memset(susp, 0, sizeof(struct susp_info));
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
/* vim: set noet ts=8 sts=8 sw=8 : */
|
||||
|
||||
/** Functions and structures used for SUSP (IEEE 1281).
|
||||
*/
|
||||
|
||||
#ifndef __ISO_SUSP
|
||||
#define __ISO_SUSP
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* SUSP is only present in standard ecma119 */
|
||||
struct ecma119_write_target;
|
||||
struct ecma119_tree_node;
|
||||
|
||||
/** This contains the information that needs to go in the SUSP area of a file.
|
||||
*/
|
||||
struct susp_info
|
||||
{
|
||||
int n_susp_fields; /**< Number of SUSP fields */
|
||||
uint8_t **susp_fields; /**< Data for each SUSP field */
|
||||
|
||||
/* the next 3 relate to CE and are filled out by susp_add_CE. */
|
||||
int n_fields_fit; /**< How many of the above SUSP fields fit
|
||||
* within this node's dirent. */
|
||||
int non_CE_len; /**< Length of the part of the SUSP area that
|
||||
* fits in the dirent. */
|
||||
int CE_len; /**< Length of the part of the SUSP area that
|
||||
* will go in a CE area. */
|
||||
};
|
||||
|
||||
void susp_add_CE(struct ecma119_write_target *, struct ecma119_tree_node *);
|
||||
|
||||
/* these next 2 are special because they don't modify the susp fields of the
|
||||
* directory; they modify the susp fields of the
|
||||
* "." entry in the directory. */
|
||||
void susp_add_SP(struct ecma119_write_target *, struct ecma119_tree_node *);
|
||||
void rrip_add_ER(struct ecma119_write_target *, struct ecma119_tree_node *);
|
||||
|
||||
/** Once all the directories and files are laid out, recurse through the tree
|
||||
* and finalize all SUSP CE entries. */
|
||||
void susp_finalize(struct ecma119_write_target *, struct ecma119_tree_node *);
|
||||
|
||||
void susp_append(struct ecma119_write_target *,
|
||||
struct susp_info *,
|
||||
uint8_t *);
|
||||
void susp_insert(struct ecma119_write_target *,
|
||||
struct susp_info *,
|
||||
uint8_t *,
|
||||
int pos);
|
||||
uint8_t *susp_find(struct susp_info *,
|
||||
const char *);
|
||||
|
||||
void susp_write(struct ecma119_write_target *,
|
||||
struct susp_info *,
|
||||
uint8_t *);
|
||||
void susp_write_CE(struct ecma119_write_target *,
|
||||
struct susp_info *,
|
||||
uint8_t *);
|
||||
|
||||
void susp_free_fields(struct susp_info *);
|
||||
|
||||
#endif /* __ISO_SUSP */
|
223
libisofs/tree.c
223
libisofs/tree.c
@ -1,223 +0,0 @@
|
||||
/* vim: set noet ts=8 sts=8 sw=8 : */
|
||||
|
||||
/**
|
||||
* \file tree.c
|
||||
*
|
||||
* Implement filesystem trees.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <dirent.h>
|
||||
#include <libgen.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <err.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "tree.h"
|
||||
#include "util.h"
|
||||
#include "volume.h"
|
||||
#include "exclude.h"
|
||||
|
||||
static void
|
||||
set_default_stat(struct stat *s)
|
||||
{
|
||||
time_t now = time(NULL);
|
||||
|
||||
memset(s, 0, sizeof(struct stat));
|
||||
s->st_mode = 0777 | S_IFREG;
|
||||
s->st_atime = s->st_mtime = s->st_ctime = now;
|
||||
}
|
||||
|
||||
static struct stat
|
||||
get_attrib(const struct iso_tree_node *node)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
if (node) {
|
||||
return node->attrib;
|
||||
}
|
||||
set_default_stat(&st);
|
||||
return st;
|
||||
}
|
||||
|
||||
static void
|
||||
append_node(struct iso_tree_node *parent,
|
||||
struct iso_tree_node *child)
|
||||
{
|
||||
assert((!parent || S_ISDIR(parent->attrib.st_mode)) && child);
|
||||
if (!parent)
|
||||
return;
|
||||
|
||||
parent->nchildren++;
|
||||
parent->children =
|
||||
realloc(parent->children, parent->nchildren * sizeof(void*));
|
||||
parent->children[parent->nchildren-1] = child;
|
||||
}
|
||||
|
||||
struct iso_tree_node*
|
||||
iso_tree_new_root(struct iso_volume *vol)
|
||||
{
|
||||
assert(vol);
|
||||
|
||||
if (vol->root) {
|
||||
iso_tree_free(vol->root);
|
||||
}
|
||||
|
||||
vol->root = calloc(1, sizeof(struct iso_tree_node));
|
||||
vol->root->volume = vol;
|
||||
set_default_stat(&vol->root->attrib);
|
||||
vol->root->attrib.st_mode = S_IFDIR | 0777;
|
||||
vol->root->loc.type = LIBISO_NONE;
|
||||
return vol->root;
|
||||
}
|
||||
|
||||
struct iso_tree_node*
|
||||
iso_tree_add_new_file(struct iso_tree_node *parent, const char *name)
|
||||
{
|
||||
struct iso_tree_node *f = calloc(1, sizeof(struct iso_tree_node));
|
||||
|
||||
assert((!parent || S_ISDIR(parent->attrib.st_mode)) && name);
|
||||
|
||||
f->volume = parent ? parent->volume : NULL;
|
||||
f->parent = parent;
|
||||
f->name = parent ? strdup(name) : NULL;
|
||||
f->attrib = get_attrib(parent);
|
||||
f->attrib.st_mode = 0777 | S_IFREG;
|
||||
f->loc.type = LIBISO_NONE;
|
||||
append_node(parent, f);
|
||||
return f;
|
||||
}
|
||||
|
||||
struct iso_tree_node*
|
||||
iso_tree_add_new_dir(struct iso_tree_node *parent, const char *name)
|
||||
{
|
||||
struct iso_tree_node *d = iso_tree_add_new_file(parent, name);
|
||||
|
||||
assert((!parent || S_ISDIR(parent->attrib.st_mode)) && name);
|
||||
|
||||
d->attrib.st_mode = (d->attrib.st_mode & ~S_IFMT) | S_IFDIR;
|
||||
return d;
|
||||
}
|
||||
|
||||
struct iso_tree_node*
|
||||
iso_tree_add_node(struct iso_tree_node *parent, const char *path)
|
||||
{
|
||||
char *p;
|
||||
struct stat st;
|
||||
struct iso_tree_node *ret;
|
||||
|
||||
assert((!parent || S_ISDIR(parent->attrib.st_mode)) && path);
|
||||
|
||||
if (lstat(path, &st) == -1)
|
||||
return NULL;
|
||||
|
||||
p = strdup(path); /* because basename() might modify its arg */
|
||||
|
||||
/* it doesn't matter if we add a file or directory since we modify
|
||||
* attrib anyway. */
|
||||
ret = iso_tree_add_new_file(parent, basename(p));
|
||||
ret->attrib = st;
|
||||
ret->loc.type = LIBISO_FILESYS;
|
||||
ret->loc.path = strdup(path);
|
||||
free(p);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct iso_tree_node*
|
||||
iso_tree_radd_dir (struct iso_tree_node *parent, const char *path)
|
||||
{
|
||||
struct iso_tree_node *new;
|
||||
DIR *dir;
|
||||
struct dirent *ent;
|
||||
|
||||
assert((!parent || S_ISDIR(parent->attrib.st_mode)) && path);
|
||||
|
||||
new = iso_tree_add_node(parent, path);
|
||||
if (!new || !S_ISDIR(new->attrib.st_mode)) {
|
||||
return new;
|
||||
}
|
||||
|
||||
dir = opendir(path);
|
||||
if (!dir) {
|
||||
warn("couldn't open directory %s: %s\n", path, strerror(errno));
|
||||
return new;
|
||||
}
|
||||
|
||||
while ((ent = readdir(dir))) {
|
||||
char child[strlen(ent->d_name) + strlen(path) + 2];
|
||||
|
||||
if (strcmp(ent->d_name, ".") == 0 ||
|
||||
strcmp(ent->d_name, "..") == 0)
|
||||
continue;
|
||||
|
||||
sprintf(child, "%s/%s", path, ent->d_name);
|
||||
|
||||
/* see if this child is excluded. */
|
||||
if (iso_exclude_lookup(child))
|
||||
continue;
|
||||
|
||||
iso_tree_radd_dir(new, child);
|
||||
}
|
||||
closedir(dir);
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
void
|
||||
iso_tree_free(struct iso_tree_node *root)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i=0; i < root->nchildren; i++) {
|
||||
iso_tree_free(root->children[i]);
|
||||
}
|
||||
free(root->name);
|
||||
free(root->children);
|
||||
free(root);
|
||||
}
|
||||
|
||||
void
|
||||
iso_tree_print(const struct iso_tree_node *root, int spaces)
|
||||
{
|
||||
size_t i;
|
||||
char sp[spaces+1];
|
||||
|
||||
memset(sp, ' ', spaces);
|
||||
sp[spaces] = '\0';
|
||||
|
||||
printf("%s%sn", sp, root->name);
|
||||
for (i=0; i < root->nchildren; i++) {
|
||||
iso_tree_print(root->children[i], spaces+2);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
iso_tree_print_verbose(const struct iso_tree_node *root,
|
||||
print_dir_callback dir,
|
||||
print_file_callback file,
|
||||
void *callback_data,
|
||||
int spaces)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
(S_ISDIR(root->attrib.st_mode) ? dir : file)
|
||||
(root, callback_data, spaces);
|
||||
for (i=0; i < root->nchildren; i++) {
|
||||
iso_tree_print_verbose(root->children[i], dir,
|
||||
file, callback_data, spaces+2);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
iso_tree_node_set_name(struct iso_tree_node *file, const char *name)
|
||||
{
|
||||
free(file->name);
|
||||
file->name = strdup(name);
|
||||
}
|
159
libisofs/tree.h
159
libisofs/tree.h
@ -1,159 +0,0 @@
|
||||
/* vim: set noet ts=8 sts=8 sw=8 : */
|
||||
|
||||
/**
|
||||
* \file tree.h
|
||||
*
|
||||
* Declare the structure of a libisofs filesystem tree. The files in this
|
||||
* tree can come from either the local filesystem or from another .iso image
|
||||
* (for multisession).
|
||||
*
|
||||
* This tree preserves as much information as it can about the files; names
|
||||
* are stored in wchar_t and we preserve POSIX attributes. This tree does
|
||||
* *not* include information that is necessary for writing out, for example,
|
||||
* an ISO level 1 tree. That information will go in a different tree because
|
||||
* the structure is sufficiently different.
|
||||
*/
|
||||
|
||||
#ifndef LIBISO_TREE_H
|
||||
#define LIBISO_TREE_H
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdint.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#include "libisofs.h"
|
||||
|
||||
enum file_location {
|
||||
LIBISO_FILESYS,
|
||||
LIBISO_PREVSESSION,
|
||||
LIBISO_NONE /**< for files/dirs that were added with
|
||||
* iso_tree_add_new_XXX. */
|
||||
};
|
||||
|
||||
/**
|
||||
* This tells us where to read the data from a file. Either we read from the
|
||||
* local filesystem or we just point to the block on a previous session.
|
||||
*/
|
||||
struct iso_file_location
|
||||
{
|
||||
enum file_location type;
|
||||
/* union {*/
|
||||
char *path; /* in the current locale */
|
||||
uint32_t block;
|
||||
/* };*/
|
||||
};
|
||||
|
||||
/**
|
||||
* A node in the filesystem tree.
|
||||
*/
|
||||
struct iso_tree_node
|
||||
{
|
||||
struct iso_volume *volume;
|
||||
struct iso_tree_node *parent;
|
||||
char *name;
|
||||
struct stat attrib; /**< The POSIX attributes of this node as
|
||||
* documented in "man 2 stat". */
|
||||
struct iso_file_location loc;
|
||||
/**< Only used for regular files and symbolic
|
||||
* links (ie. files for which we might have to
|
||||
* copy data). */
|
||||
|
||||
size_t nchildren; /**< The number of children of this
|
||||
* directory (if this is a directory). */
|
||||
struct iso_tree_node **children;
|
||||
|
||||
size_t block; /**< The block at which this file will
|
||||
* reside on disk. We store this here as
|
||||
* well as in the various mangled trees
|
||||
* because many different trees might point
|
||||
* to the same file and they need to share the
|
||||
* block location. */
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new root directory for a volume.
|
||||
*
|
||||
* \param vol The volume for which to create a new root directory.
|
||||
*
|
||||
* \pre \p vol is non-NULL.
|
||||
* \post \p vol has a non-NULL, empty root directory with permissions 777.
|
||||
* \return \p vol's new non-NULL, empty root directory.
|
||||
*/
|
||||
struct iso_tree_node *iso_tree_new_root(struct iso_volume *vol);
|
||||
|
||||
/**
|
||||
* Create a new, empty, file.
|
||||
*
|
||||
* \param parent The parent directory of the new file. If this is null, create
|
||||
* and return a new file node without adding it to any tree.
|
||||
* \param name The name of the new file, encoded in the current locale.
|
||||
* \pre \p name is non-NULL and it does not match any other file or directory
|
||||
* name in \p parent.
|
||||
* \post \p parent (if non-NULL) contains a file with the following properties:
|
||||
* - the file's name is \p name (converted to wchar_t)
|
||||
* - the file's POSIX permissions are the same as \p parent's
|
||||
* - the file is a regular file
|
||||
* - the file is empty
|
||||
*
|
||||
* \return \p parent's newly created file.
|
||||
*/
|
||||
struct iso_tree_node *iso_tree_add_new_file(struct iso_tree_node *parent,
|
||||
const char *name);
|
||||
|
||||
/**
|
||||
* Recursively free a directory.
|
||||
*
|
||||
* \param root The root of the directory heirarchy to free.
|
||||
*
|
||||
* \pre \p root is non-NULL.
|
||||
*/
|
||||
void iso_tree_free(struct iso_tree_node *root);
|
||||
|
||||
/**
|
||||
* A function that prints verbose information about a directory.
|
||||
*
|
||||
* \param dir The directory about which to print information.
|
||||
* \param data Unspecified function-dependent data.
|
||||
* \param spaces The number of spaces to prepend to the output.
|
||||
*
|
||||
* \see iso_tree_print_verbose
|
||||
*/
|
||||
typedef void (*print_dir_callback) (const struct iso_tree_node *dir,
|
||||
void *data,
|
||||
int spaces);
|
||||
/**
|
||||
* A function that prints verbose information about a file.
|
||||
*
|
||||
* \param dir The file about which to print information.
|
||||
* \param data Unspecified function-dependent data.
|
||||
* \param spaces The number of spaces to prepend to the output.
|
||||
*
|
||||
* \see iso_tree_print_verbose
|
||||
*/
|
||||
typedef void (*print_file_callback) (const struct iso_tree_node *file,
|
||||
void *data,
|
||||
int spaces);
|
||||
|
||||
/**
|
||||
* Recursively print a directory heirarchy. For each node in the directory
|
||||
* heirarchy, call a callback function to print information more verbosely.
|
||||
*
|
||||
* \param root The root of the directory heirarchy to print.
|
||||
* \param dir The callback function to call for each directory in the tree.
|
||||
* \param file The callback function to call for each file in the tree.
|
||||
* \param callback_data The data to pass to the callback functions.
|
||||
* \param spaces The number of spaces to prepend to the output.
|
||||
*
|
||||
* \pre \p root is not NULL.
|
||||
* \pre Neither of the callback functions modifies the directory heirarchy.
|
||||
*/
|
||||
void iso_tree_print_verbose(const struct iso_tree_node *root,
|
||||
print_dir_callback dir,
|
||||
print_file_callback file,
|
||||
void *callback_data,
|
||||
int spaces);
|
||||
|
||||
#define ISO_ISDIR(n) S_ISDIR(n->attrib.st_mode)
|
||||
|
||||
#endif /* LIBISO_TREE_H */
|
584
libisofs/util.c
584
libisofs/util.c
@ -1,584 +0,0 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
/* vim: set noet ts=8 sts=8 sw=8 : */
|
||||
|
||||
/**
|
||||
* Utility functions for the Libisofs library.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <wchar.h>
|
||||
#include <iconv.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <locale.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
/* avoids warning and names in iso, joliet and rockridge can't be > 255 bytes
|
||||
* anyway. There are at most 31 characters in iso level 1, 255 for rockridge,
|
||||
* 64 characters (* 2 since UCS) for joliet. */
|
||||
#define NAME_BUFFER_SIZE 255
|
||||
|
||||
int div_up(int n, int div)
|
||||
{
|
||||
return (n + div - 1) / div;
|
||||
}
|
||||
|
||||
int round_up(int n, int mul)
|
||||
{
|
||||
return div_up(n, mul) * mul;
|
||||
}
|
||||
|
||||
/* this function must always return a name
|
||||
* since the caller never checks if a NULL
|
||||
* is returned. It also avoids some warnings. */
|
||||
char *str2ascii(const char *src_arg)
|
||||
{
|
||||
wchar_t wsrc_[NAME_BUFFER_SIZE];
|
||||
char *src = (char*)wsrc_;
|
||||
char *ret_;
|
||||
char *ret;
|
||||
mbstate_t state;
|
||||
iconv_t conv;
|
||||
size_t numchars;
|
||||
size_t outbytes;
|
||||
size_t inbytes;
|
||||
size_t n;
|
||||
|
||||
if (!src_arg)
|
||||
return NULL;
|
||||
|
||||
/* convert the string to a wide character string. Note: outbytes
|
||||
* is in fact the number of characters in the string and doesn't
|
||||
* include the last NULL character. */
|
||||
memset(&state, 0, sizeof(state));
|
||||
numchars = mbsrtowcs(wsrc_, &src_arg, NAME_BUFFER_SIZE-1, &state);
|
||||
if (numchars < 0)
|
||||
return NULL;
|
||||
|
||||
inbytes = numchars * sizeof(wchar_t);
|
||||
|
||||
ret_ = malloc(numchars+1);
|
||||
outbytes = numchars;
|
||||
ret = ret_;
|
||||
|
||||
/* initialize iconv */
|
||||
conv = iconv_open("ASCII", "WCHAR_T");
|
||||
if (conv == (iconv_t)-1)
|
||||
return NULL;
|
||||
|
||||
n = iconv(conv, &src, &inbytes, &ret, &outbytes);
|
||||
while(n == -1) {
|
||||
/* The destination buffer is too small. Stops here. */
|
||||
if(errno == E2BIG)
|
||||
break;
|
||||
|
||||
/* An incomplete multi bytes sequence was found. We
|
||||
* can't do anything here. That's quite unlikely. */
|
||||
if(errno == EINVAL)
|
||||
break;
|
||||
|
||||
/* The last possible error is an invalid multi bytes
|
||||
* sequence. Just replace the character with a "_".
|
||||
* Probably the character doesn't exist in ascii like
|
||||
* "é, è, à, ç, ..." in French. */
|
||||
*ret++ = '_';
|
||||
outbytes--;
|
||||
|
||||
if(!outbytes)
|
||||
break;
|
||||
|
||||
/* There was an error with one character but some other remain
|
||||
* to be converted. That's probably a multibyte character.
|
||||
* See above comment. */
|
||||
src += sizeof(wchar_t);
|
||||
inbytes -= sizeof(wchar_t);
|
||||
|
||||
if(!inbytes)
|
||||
break;
|
||||
|
||||
n = iconv(conv, &src, &inbytes, &ret, &outbytes);
|
||||
}
|
||||
|
||||
iconv_close(conv);
|
||||
|
||||
*ret='\0';
|
||||
return ret_;
|
||||
}
|
||||
|
||||
/* FIXME: C&P */
|
||||
uint16_t *str2ucs(const char *src_arg)
|
||||
{
|
||||
wchar_t wsrc_[NAME_BUFFER_SIZE];
|
||||
char *src = (char*)wsrc_;
|
||||
char *ret_;
|
||||
char *ret;
|
||||
mbstate_t state;
|
||||
iconv_t conv;
|
||||
size_t outbytes;
|
||||
size_t numchars;
|
||||
size_t inbytes;
|
||||
size_t n;
|
||||
|
||||
if (!src_arg)
|
||||
return calloc(2, 1); /* empty UCS string */
|
||||
|
||||
/* convert the string to a wide character string. Note: outbytes
|
||||
* is in fact the number of characters in the string and doesn't
|
||||
* include the last NULL character. */
|
||||
memset(&state, 0, sizeof(state));
|
||||
numchars = mbsrtowcs(wsrc_, &src_arg, NAME_BUFFER_SIZE-1, &state);
|
||||
if (numchars < 0)
|
||||
return calloc(2, 1); /* empty UCS string */
|
||||
|
||||
inbytes = numchars * sizeof(wchar_t);
|
||||
|
||||
outbytes = numchars * sizeof(uint16_t);
|
||||
ret_ = malloc ((numchars+1) * sizeof(uint16_t));
|
||||
ret = ret_;
|
||||
|
||||
/* initialize iconv */
|
||||
conv = iconv_open("UCS-2BE", "WCHAR_T");
|
||||
if (conv == (iconv_t)-1)
|
||||
return calloc(2, 1); /* empty UCS string */
|
||||
|
||||
n = iconv(conv, &src, &inbytes, &ret, &outbytes);
|
||||
while(n == -1) {
|
||||
/* The destination buffer is too small. Stops here. */
|
||||
if(errno == E2BIG)
|
||||
break;
|
||||
|
||||
/* An incomplete multi bytes sequence was found. We
|
||||
* can't do anything here. That's quite unlikely. */
|
||||
if(errno == EINVAL)
|
||||
break;
|
||||
|
||||
/* The last possible error is an invalid multi bytes
|
||||
* sequence. Just replace the character with a "_".
|
||||
* Probably the character doesn't exist in ascii like
|
||||
* "é, è, à, ç, ..." in French. */
|
||||
*((uint16_t*) ret) = '_';
|
||||
ret += sizeof(uint16_t);
|
||||
outbytes -= sizeof(uint16_t);
|
||||
|
||||
if(!outbytes)
|
||||
break;
|
||||
|
||||
/* There was an error with one character but some other remain
|
||||
* to be converted. That's probably a multibyte character.
|
||||
* See above comment. */
|
||||
src += sizeof(wchar_t);
|
||||
inbytes -= sizeof(wchar_t);
|
||||
|
||||
if(!inbytes)
|
||||
break;
|
||||
|
||||
n = iconv(conv, &src, &inbytes, &ret, &outbytes);
|
||||
}
|
||||
|
||||
iconv_close(conv);
|
||||
|
||||
/* close the ucs string */
|
||||
*((uint16_t*) ret) = 0;
|
||||
|
||||
return (uint16_t*)ret_;
|
||||
}
|
||||
|
||||
|
||||
static int valid_d_char(char c)
|
||||
{
|
||||
return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c == '_');
|
||||
}
|
||||
|
||||
static int valid_a_char(char c)
|
||||
{
|
||||
return (c >= ' ' && c <= '"') || (c >= '%' && c <= '?')
|
||||
|| (c >= 'A' && c <= 'Z')
|
||||
|| (c == '_');
|
||||
}
|
||||
|
||||
static int valid_j_char(uint16_t c)
|
||||
{
|
||||
return !(c < (uint16_t)' ' || c == (uint16_t)'*' || c == (uint16_t)'/'
|
||||
|| c == (uint16_t)':' || c == (uint16_t)';'
|
||||
|| c == (uint16_t)'?' || c == (uint16_t)'\\');
|
||||
}
|
||||
|
||||
/* FIXME: where are these documented? */
|
||||
static int valid_p_char(char c)
|
||||
{
|
||||
return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z')
|
||||
|| (c >= 'a' && c <= 'z')
|
||||
|| (c == '.') || (c == '_') || (c == '-');
|
||||
}
|
||||
|
||||
static char *iso_dirid(const char *src, int size)
|
||||
{
|
||||
char *ret = str2ascii(src);
|
||||
size_t len, i;
|
||||
|
||||
if (!ret)
|
||||
return NULL;
|
||||
|
||||
len = strlen(ret);
|
||||
if (len > size) {
|
||||
ret[size] = '\0';
|
||||
len = size;
|
||||
}
|
||||
for (i = 0; i < len; i++) {
|
||||
char c = toupper(ret[i]);
|
||||
ret[i] = valid_d_char(c) ? c : '_';
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *iso_1_dirid(const char *src)
|
||||
{
|
||||
return iso_dirid(src, 8);
|
||||
}
|
||||
|
||||
char *iso_2_dirid(const char *src)
|
||||
{
|
||||
return iso_dirid(src, 31);
|
||||
}
|
||||
|
||||
char *iso_1_fileid(const char *src_arg)
|
||||
{
|
||||
char *src = str2ascii(src_arg);
|
||||
char *dest;
|
||||
char *dot; /* Position of the last dot in the
|
||||
filename, will be used to calculate
|
||||
lname and lext. */
|
||||
int lname, lext, pos, i;
|
||||
|
||||
if (!src)
|
||||
return NULL;
|
||||
|
||||
dest = malloc(15); /* 15 = 8 (name) + 1 (.) + 3 (ext) + 2
|
||||
(;1) + 1 (\0) */
|
||||
dot = strrchr(src, '.');
|
||||
|
||||
lext = dot ? strlen(dot + 1) : 0;
|
||||
lname = strlen(src) - lext - (dot ? 1 : 0);
|
||||
|
||||
/* If we can't build a filename, return NULL. */
|
||||
if (lname == 0 && lext == 0) {
|
||||
free(src);
|
||||
free(dest);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pos = 0;
|
||||
/* Convert up to 8 characters of the filename. */
|
||||
for (i = 0; i < lname && i < 8; i++) {
|
||||
char c = toupper(src[i]);
|
||||
|
||||
dest[pos++] = valid_d_char(c) ? c : '_';
|
||||
}
|
||||
/* This dot is mandatory, even if there is no extension. */
|
||||
dest[pos++] = '.';
|
||||
/* Convert up to 3 characters of the extension, if any. */
|
||||
for (i = 0; i < lext && i < 3; i++) {
|
||||
char c = toupper(src[lname + 1 + i]);
|
||||
|
||||
dest[pos++] = valid_d_char(c) ? c : '_';
|
||||
}
|
||||
/* File versions are mandatory, even if they aren't used. */
|
||||
dest[pos++] = ';';
|
||||
dest[pos++] = '1';
|
||||
dest[pos] = '\0';
|
||||
dest = (char *)realloc(dest, pos + 1);
|
||||
|
||||
free(src);
|
||||
return dest;
|
||||
}
|
||||
|
||||
char *iso_2_fileid(const char *src_arg)
|
||||
{
|
||||
char *src = str2ascii(src_arg);
|
||||
char *dest;
|
||||
char *dot;
|
||||
int lname, lext, lnname, lnext, pos, i;
|
||||
|
||||
if (!src)
|
||||
return NULL;
|
||||
|
||||
dest = malloc(34); /* 34 = 30 (name + ext) + 1 (.) + 2
|
||||
(;1) + 1 (\0) */
|
||||
dot = strrchr(src, '.');
|
||||
|
||||
/* Since the maximum length can be divided freely over the name and
|
||||
extension, we need to calculate their new lengths (lnname and
|
||||
lnext). If the original filename is too long, we start by trimming
|
||||
the extension, but keep a minimum extension length of 3. */
|
||||
if (dot == NULL || dot == src || *(dot + 1) == '\0') {
|
||||
lname = strlen(src);
|
||||
lnname = (lname > 30) ? 30 : lname;
|
||||
lext = lnext = 0;
|
||||
} else {
|
||||
lext = strlen(dot + 1);
|
||||
lname = strlen(src) - lext - 1;
|
||||
lnext = (strlen(src) > 31 && lext > 3)
|
||||
? (lname < 27 ? 30 - lname : 3) : lext;
|
||||
lnname = (strlen(src) > 31) ? 30 - lnext : lname;
|
||||
}
|
||||
|
||||
if (lnname == 0 && lnext == 0) {
|
||||
free(src);
|
||||
free(dest);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pos = 0;
|
||||
/* Convert up to lnname characters of the filename. */
|
||||
for (i = 0; i < lnname; i++) {
|
||||
char c = toupper(src[i]);
|
||||
|
||||
dest[pos++] = valid_d_char(c) ? c : '_';
|
||||
}
|
||||
dest[pos++] = '.';
|
||||
/* Convert up to lnext characters of the extension, if any. */
|
||||
for (i = 0; i < lnext; i++) {
|
||||
char c = toupper(src[lname + 1 + i]);
|
||||
|
||||
dest[pos++] = valid_d_char(c) ? c : '_';
|
||||
}
|
||||
dest[pos++] = ';';
|
||||
dest[pos++] = '1';
|
||||
dest[pos] = '\0';
|
||||
dest = (char *)realloc(dest, pos + 1);
|
||||
|
||||
free(src);
|
||||
return dest;
|
||||
}
|
||||
|
||||
char *
|
||||
iso_p_fileid(const char *src)
|
||||
{
|
||||
char *ret = str2ascii(src);
|
||||
size_t i, len;
|
||||
|
||||
if (!ret)
|
||||
return NULL;
|
||||
len = strlen(ret);
|
||||
for (i = 0; i < len; i++) {
|
||||
if (!valid_p_char(ret[i])) {
|
||||
ret[i] = (uint16_t)'_';
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint16_t *
|
||||
iso_j_id(const char *src_arg)
|
||||
{
|
||||
uint16_t *j_str = str2ucs(src_arg);
|
||||
size_t len = ucslen(j_str);
|
||||
size_t n;
|
||||
|
||||
if (len > 128) {
|
||||
j_str[128] = '\0';
|
||||
len = 128;
|
||||
}
|
||||
|
||||
for (n = 0; n < len; n++)
|
||||
if (!valid_j_char(j_str[n]))
|
||||
j_str[n] = '_';
|
||||
return j_str;
|
||||
}
|
||||
|
||||
void iso_lsb(uint8_t *buf, uint32_t num, int bytes)
|
||||
{
|
||||
int i;
|
||||
|
||||
assert(bytes <= 4);
|
||||
|
||||
for (i = 0; i < bytes; ++i)
|
||||
buf[i] = (num >> (8 * i)) & 0xff;
|
||||
}
|
||||
|
||||
void iso_msb(uint8_t *buf, uint32_t num, int bytes)
|
||||
{
|
||||
int i;
|
||||
|
||||
assert(bytes <= 4);
|
||||
|
||||
for (i = 0; i < bytes; ++i)
|
||||
buf[bytes - 1 - i] = (num >> (8 * i)) & 0xff;
|
||||
}
|
||||
|
||||
void iso_bb(uint8_t *buf, uint32_t num, int bytes)
|
||||
{
|
||||
iso_lsb(buf, num, bytes);
|
||||
iso_msb(buf+bytes, num, bytes);
|
||||
}
|
||||
|
||||
|
||||
void iso_datetime_7(unsigned char *buf, time_t t)
|
||||
{
|
||||
static int tzsetup = 0;
|
||||
int tzoffset;
|
||||
struct tm tm;
|
||||
|
||||
if (!tzsetup) {
|
||||
tzset();
|
||||
tzsetup = 1;
|
||||
}
|
||||
|
||||
localtime_r(&t, &tm);
|
||||
|
||||
buf[0] = tm.tm_year;
|
||||
buf[1] = tm.tm_mon + 1;
|
||||
buf[2] = tm.tm_mday;
|
||||
buf[3] = tm.tm_hour;
|
||||
buf[4] = tm.tm_min;
|
||||
buf[5] = tm.tm_sec;
|
||||
#ifdef HAVE_TM_GMTOFF
|
||||
tzoffset = -tm.tm_gmtoff / 60 / 15;
|
||||
#else
|
||||
tzoffset = -timezone / 60 / 15;
|
||||
#endif
|
||||
if (tzoffset < -48)
|
||||
tzoffset += 101;
|
||||
buf[6] = tzoffset;
|
||||
}
|
||||
|
||||
time_t iso_datetime_read_7(const uint8_t *buf)
|
||||
{
|
||||
struct tm tm;
|
||||
|
||||
tm.tm_year = buf[0];
|
||||
tm.tm_mon = buf[1] + 1;
|
||||
tm.tm_mday = buf[2];
|
||||
tm.tm_hour = buf[3];
|
||||
tm.tm_min = buf[4];
|
||||
tm.tm_sec = buf[5];
|
||||
|
||||
return mktime(&tm) - buf[6] * 60 * 60;
|
||||
}
|
||||
|
||||
void iso_datetime_17(unsigned char *buf, time_t t)
|
||||
{
|
||||
static int tzsetup = 0;
|
||||
static int tzoffset;
|
||||
struct tm tm;
|
||||
|
||||
if (t == (time_t) - 1) {
|
||||
/* unspecified time */
|
||||
memset(buf, '0', 16);
|
||||
buf[16] = 0;
|
||||
} else {
|
||||
if (!tzsetup) {
|
||||
tzset();
|
||||
tzsetup = 1;
|
||||
}
|
||||
|
||||
localtime_r(&t, &tm);
|
||||
|
||||
sprintf((char*)&buf[0], "%04d", tm.tm_year + 1900);
|
||||
sprintf((char*)&buf[4], "%02d", tm.tm_mon + 1);
|
||||
sprintf((char*)&buf[6], "%02d", tm.tm_mday);
|
||||
sprintf((char*)&buf[8], "%02d", tm.tm_hour);
|
||||
sprintf((char*)&buf[10], "%02d", tm.tm_min);
|
||||
sprintf((char*)&buf[12], "%02d", MIN(59, tm.tm_sec));
|
||||
memcpy(&buf[14], "00", 2);
|
||||
#ifdef HAVE_TM_GMTOFF
|
||||
tzoffset = -tm.tm_gmtoff / 60 / 15;
|
||||
#else
|
||||
tzoffset = -timezone / 60 / 15;
|
||||
#endif
|
||||
if (tzoffset < -48)
|
||||
tzoffset += 101;
|
||||
buf[16] = tzoffset;
|
||||
}
|
||||
}
|
||||
|
||||
time_t iso_datetime_read_17(const uint8_t *buf)
|
||||
{
|
||||
struct tm tm;
|
||||
|
||||
sscanf((char*)&buf[0], "%4d", &tm.tm_year);
|
||||
sscanf((char*)&buf[4], "%2d", &tm.tm_mon);
|
||||
sscanf((char*)&buf[6], "%2d", &tm.tm_mday);
|
||||
sscanf((char*)&buf[8], "%2d", &tm.tm_hour);
|
||||
sscanf((char*)&buf[10], "%2d", &tm.tm_min);
|
||||
sscanf((char*)&buf[12], "%2d", &tm.tm_sec);
|
||||
tm.tm_year -= 1900;
|
||||
tm.tm_mon -= 1;
|
||||
|
||||
return mktime(&tm) - buf[16] * 60 * 60;
|
||||
}
|
||||
|
||||
size_t ucslen(const uint16_t *str)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; str[i]; i++)
|
||||
;
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
* Although each character is 2 bytes, we actually compare byte-by-byte
|
||||
* (thats what the spec says).
|
||||
*/
|
||||
int ucscmp(const uint16_t *s1, const uint16_t *s2)
|
||||
{
|
||||
const char *s = (const char*)s1;
|
||||
const char *t = (const char*)s2;
|
||||
size_t len1 = ucslen(s1);
|
||||
size_t len2 = ucslen(s2);
|
||||
size_t i, len = MIN(len1, len2) * 2;
|
||||
|
||||
for (i=0; i < len; i++) {
|
||||
if (s[i] < t[i]) {
|
||||
return -1;
|
||||
} else if (s[i] > t[i]) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (len1 < len2)
|
||||
return -1;
|
||||
else if (len1 > len2)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t iso_read_lsb(const uint8_t *buf, int bytes)
|
||||
{
|
||||
int i;
|
||||
uint32_t ret = 0;
|
||||
|
||||
for (i=0; i<bytes; i++) {
|
||||
ret += ((uint32_t) buf[i]) << (i*8);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t iso_read_msb(const uint8_t *buf, int bytes)
|
||||
{
|
||||
int i;
|
||||
uint32_t ret = 0;
|
||||
|
||||
for (i=0; i<bytes; i++) {
|
||||
ret += ((uint32_t) buf[bytes-i-1]) << (i*8);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t iso_read_bb(const uint8_t *buf, int bytes)
|
||||
{
|
||||
uint32_t v1 = iso_read_lsb(buf, bytes);
|
||||
uint32_t v2 = iso_read_msb(buf+bytes, bytes);
|
||||
|
||||
assert(v1 == v2);
|
||||
return v1;
|
||||
}
|
121
libisofs/util.h
121
libisofs/util.h
@ -1,121 +0,0 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
/* vim: set noet ts=8 sts=8 sw=8 : */
|
||||
|
||||
/**
|
||||
* Utility functions for the Libisofs library.
|
||||
*/
|
||||
|
||||
#ifndef LIBISO_UTIL_H
|
||||
#define LIBISO_UTIL_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#ifndef MAX
|
||||
# define MAX(a, b) (((a) > (b)) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
#ifndef MIN
|
||||
# define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
extern inline int div_up(int n, int div)
|
||||
{
|
||||
return (n + div - 1) / div;
|
||||
}
|
||||
|
||||
extern inline int round_up(int n, int mul)
|
||||
{
|
||||
return div_up(n, mul) * mul;
|
||||
}
|
||||
|
||||
wchar_t *towcs(const char *);
|
||||
char *str2ascii(const char*);
|
||||
uint16_t *str2ucs(const char*);
|
||||
|
||||
/**
|
||||
* Create a level 1 directory identifier.
|
||||
*/
|
||||
char *iso_1_dirid(const char *src);
|
||||
|
||||
/**
|
||||
* Create a level 2 directory identifier.
|
||||
*/
|
||||
char *iso_2_dirid(const char *src);
|
||||
|
||||
/**
|
||||
* Create a level 1 file identifier that consists of a name, extension and
|
||||
* version number. The resulting string will have a file name of maximum
|
||||
* length 8, followed by a separator (.), an optional extension of maximum
|
||||
* length 3, followed by a separator (;) and a version number (digit 1).
|
||||
* @return NULL if the original name and extension both are of length 0.
|
||||
*/
|
||||
char *iso_1_fileid(const char *src);
|
||||
|
||||
/**
|
||||
* Create a level 2 file identifier that consists of a name, extension and
|
||||
* version number. The combined file name and extension length will not exceed
|
||||
* 30, the name and extension will be separated (.), and the extension will be
|
||||
* followed by a separator (;) and a version number (digit 1).
|
||||
* @return NULL if the original name and extension both are of length 0.
|
||||
*/
|
||||
char *iso_2_fileid(const char *src);
|
||||
|
||||
/**
|
||||
* Create a Joliet file or directory identifier that consists of a name,
|
||||
* extension and version number. The combined name and extension length will
|
||||
* not exceed 128 bytes, the name and extension will be separated (.),
|
||||
* and the extension will be followed by a separator (;) and a version number
|
||||
* (digit 1). All characters consist of 2 bytes and the resulting string is
|
||||
* NULL-terminated by a 2-byte NULL. Requires the locale to be set correctly.
|
||||
*
|
||||
* @param size will be set to the size (in bytes) of the identifier.
|
||||
* @return NULL if the original name and extension both are of length 0 or the conversion from the current codeset to UCS-2BE is not available.
|
||||
*/
|
||||
uint16_t *iso_j_id(const char *src);
|
||||
|
||||
/**
|
||||
* FIXME: what are the requirements for these next two? Is this for RR?
|
||||
*
|
||||
* Create a POSIX portable file name that consists of a name and extension.
|
||||
* The resulting file name will not exceed 250 characters.
|
||||
* @return NULL if the original name and extension both are of length 0.
|
||||
*/
|
||||
char *iso_p_fileid(const char *src);
|
||||
|
||||
/**
|
||||
* Create a POSIX portable directory name.
|
||||
* The resulting directory name will not exceed 250 characters.
|
||||
* @return NULL if the original name is of length 0.
|
||||
*/
|
||||
char *iso_p_dirid(const char *src);
|
||||
|
||||
void iso_lsb(uint8_t *buf, uint32_t num, int bytes);
|
||||
void iso_msb(uint8_t *buf, uint32_t num, int bytes);
|
||||
void iso_bb(uint8_t *buf, uint32_t num, int bytes);
|
||||
|
||||
uint32_t iso_read_lsb(const uint8_t *buf, int bytes);
|
||||
uint32_t iso_read_msb(const uint8_t *buf, int bytes);
|
||||
uint32_t iso_read_bb(const uint8_t *buf, int bytes);
|
||||
|
||||
/** Records the date/time into a 7 byte buffer (9.1.5) */
|
||||
void iso_datetime_7(uint8_t *buf, time_t t);
|
||||
|
||||
/** Records the date/time into a 17 byte buffer (8.4.26.1) */
|
||||
void iso_datetime_17(uint8_t *buf, time_t t);
|
||||
|
||||
time_t iso_datetime_read_7(const uint8_t *buf);
|
||||
time_t iso_datetime_read_17(const uint8_t *buf);
|
||||
|
||||
/**
|
||||
* Like strlen, but for Joliet strings.
|
||||
*/
|
||||
size_t ucslen(const uint16_t *str);
|
||||
|
||||
/**
|
||||
* Like strcmp, but for Joliet strings.
|
||||
*/
|
||||
int ucscmp(const uint16_t *s1, const uint16_t *s2);
|
||||
|
||||
#endif /* LIBISO_UTIL_H */
|
@ -1,189 +0,0 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
/* vim: set ts=8 sts=8 sw=8 noet : */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <libgen.h>
|
||||
|
||||
#include "libisofs.h"
|
||||
#include "tree.h"
|
||||
#include "util.h"
|
||||
#include "volume.h"
|
||||
|
||||
struct iso_volset*
|
||||
iso_volset_new(struct iso_volume *vol, const char *id)
|
||||
{
|
||||
struct iso_volset *volset = calloc(1, sizeof(struct iso_volset));
|
||||
|
||||
volset->volset_size = 1;
|
||||
volset->refcount = 1;
|
||||
volset->volume = malloc(sizeof(void *));
|
||||
volset->volume[0] = vol;
|
||||
volset->volset_id = strdup(id);
|
||||
|
||||
vol->refcount++;
|
||||
return volset;
|
||||
}
|
||||
|
||||
void
|
||||
iso_volset_free(struct iso_volset *volset)
|
||||
{
|
||||
if (--volset->refcount < 1) {
|
||||
int i;
|
||||
for (i = 0; i < volset->volset_size; i++) {
|
||||
iso_volume_free(volset->volume[i]);
|
||||
}
|
||||
free(volset->volume);
|
||||
free(volset->volset_id);
|
||||
}
|
||||
}
|
||||
|
||||
struct iso_volume*
|
||||
iso_volume_new(const char *volume_id,
|
||||
const char *publisher_id,
|
||||
const char *data_preparer_id)
|
||||
{
|
||||
return iso_volume_new_with_root(volume_id,
|
||||
publisher_id,
|
||||
data_preparer_id,
|
||||
NULL);
|
||||
}
|
||||
|
||||
struct iso_volume*
|
||||
iso_volume_new_with_root(const char *volume_id,
|
||||
const char *publisher_id,
|
||||
const char *data_preparer_id,
|
||||
struct iso_tree_node *root)
|
||||
{
|
||||
struct iso_volume *volume;
|
||||
|
||||
volume = calloc(1, sizeof(struct iso_volume));
|
||||
volume->refcount = 1;
|
||||
|
||||
volume->root = root ? root : iso_tree_new_root(volume);
|
||||
|
||||
if (volume_id != NULL)
|
||||
volume->volume_id = strdup(volume_id);
|
||||
if (publisher_id != NULL)
|
||||
volume->publisher_id = strdup(publisher_id);
|
||||
if (data_preparer_id != NULL)
|
||||
volume->data_preparer_id = strdup(data_preparer_id);
|
||||
return volume;
|
||||
}
|
||||
|
||||
void
|
||||
iso_volume_free(struct iso_volume *volume)
|
||||
{
|
||||
/* Only free if no references are in use. */
|
||||
if (--volume->refcount < 1) {
|
||||
iso_tree_free(volume->root);
|
||||
|
||||
free(volume->volume_id);
|
||||
free(volume->publisher_id);
|
||||
free(volume->data_preparer_id);
|
||||
|
||||
free(volume);
|
||||
}
|
||||
}
|
||||
|
||||
struct iso_tree_node *
|
||||
iso_volume_get_root(const struct iso_volume *volume)
|
||||
{
|
||||
return volume->root;
|
||||
}
|
||||
|
||||
struct iso_tree_node *
|
||||
iso_tree_volume_path_to_node(struct iso_volume *volume, const char *path)
|
||||
{
|
||||
struct iso_tree_node *node;
|
||||
char *ptr, *brk_info, *component;
|
||||
|
||||
/* get the first child at the root of the volume
|
||||
* that is "/" */
|
||||
node=iso_volume_get_root(volume);
|
||||
if (!strcmp (path, "/"))
|
||||
return node;
|
||||
|
||||
if (!node->nchildren)
|
||||
return NULL;
|
||||
|
||||
/* the name of the nodes is in wide characters so first convert path
|
||||
* into wide characters. */
|
||||
ptr = strdup(path);
|
||||
|
||||
/* get the first component of the path */
|
||||
component=strtok_r(ptr, "/", &brk_info);
|
||||
while (component) {
|
||||
size_t max;
|
||||
size_t i;
|
||||
|
||||
/* search among all the children of this directory if this path component exists */
|
||||
max=node->nchildren;
|
||||
for (i=0; i < max; i++) {
|
||||
if (!strcmp(component, node->children[i]->name)) {
|
||||
node=node->children[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* see if a node could be found */
|
||||
if (i==max) {
|
||||
node=NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
component=strtok_r(NULL, "/", &brk_info);
|
||||
}
|
||||
|
||||
free(ptr);
|
||||
return node;
|
||||
}
|
||||
|
||||
struct iso_tree_node *
|
||||
iso_tree_volume_add_path(struct iso_volume *volume,
|
||||
const char *disc_path,
|
||||
const char *path)
|
||||
{
|
||||
char *tmp;
|
||||
struct iso_tree_node *node;
|
||||
struct iso_tree_node *parent_node;
|
||||
|
||||
tmp=strdup(disc_path);
|
||||
parent_node = iso_tree_volume_path_to_node(volume, dirname(tmp));
|
||||
free(tmp);
|
||||
|
||||
if (!parent_node)
|
||||
return NULL;
|
||||
|
||||
node = iso_tree_radd_dir(parent_node, path);
|
||||
if (!node)
|
||||
return NULL;
|
||||
|
||||
tmp=strdup(disc_path);
|
||||
iso_tree_node_set_name(node, basename(tmp));
|
||||
free(tmp);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
struct iso_tree_node *
|
||||
iso_tree_volume_add_new_dir(struct iso_volume *volume,
|
||||
const char *disc_path)
|
||||
{
|
||||
char *tmp;
|
||||
struct iso_tree_node *node;
|
||||
struct iso_tree_node *parent_node;
|
||||
|
||||
tmp=strdup(disc_path);
|
||||
parent_node = iso_tree_volume_path_to_node(volume, dirname(tmp));
|
||||
free(tmp);
|
||||
|
||||
if (!parent_node)
|
||||
return NULL;
|
||||
|
||||
tmp=strdup(disc_path);
|
||||
node = iso_tree_add_new_dir(parent_node, basename(tmp));
|
||||
free(tmp);
|
||||
|
||||
return node;
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
/* vim: set noet sts=8 ts=8 sw=8 : */
|
||||
|
||||
/**
|
||||
* Extra declarations for use with the iso_volume structure.
|
||||
*/
|
||||
|
||||
#ifndef LIBISO_VOLUME_H
|
||||
#define LIBISO_VOLUME_H
|
||||
|
||||
#include "libisofs.h"
|
||||
|
||||
/**
|
||||
* Data volume.
|
||||
*/
|
||||
struct iso_volume
|
||||
{
|
||||
int refcount; /**< Number of used references to this
|
||||
volume. */
|
||||
|
||||
struct iso_tree_node *root; /**< Root of the directory tree for the
|
||||
volume. */
|
||||
|
||||
char *volume_id; /**< Volume identifier. */
|
||||
char *publisher_id; /**< Volume publisher. */
|
||||
char *data_preparer_id; /**< Volume data preparer. */
|
||||
};
|
||||
|
||||
/**
|
||||
* A set of data volumes.
|
||||
*/
|
||||
struct iso_volset
|
||||
{
|
||||
int refcount;
|
||||
|
||||
struct iso_volume **volume; /**< The volumes belonging to this
|
||||
volume set. */
|
||||
int volset_size; /**< The number of volumes in this
|
||||
volume set. */
|
||||
|
||||
char *volset_id; /**< The id of this volume set, encoded
|
||||
in the current locale. */
|
||||
};
|
||||
|
||||
#endif /* __ISO_VOLUME */
|
@ -1,4 +0,0 @@
|
||||
int main()
|
||||
{
|
||||
return 0;
|
||||
}
|
107
test/iso.c
107
test/iso.c
@ -1,107 +0,0 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
/* vim: set ts=8 sts=8 sw=8 noet : */
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include "libisofs.h"
|
||||
#include "libburn/libburn.h"
|
||||
#include <getopt.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <assert.h>
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
#include <string.h>
|
||||
#include <err.h>
|
||||
|
||||
#define SECSIZE 2048
|
||||
|
||||
const char * const optstring = "JRL:h";
|
||||
extern char *optarg;
|
||||
extern int optind;
|
||||
|
||||
void usage()
|
||||
{
|
||||
printf("test [OPTIONS] DIRECTORY OUTPUT\n");
|
||||
}
|
||||
|
||||
void help()
|
||||
{
|
||||
printf(
|
||||
"Options:\n"
|
||||
" -J Add Joliet support\n"
|
||||
" -R Add Rock Ridge support\n"
|
||||
" -L <num> Set the ISO level (1 or 2)\n"
|
||||
" -h Print this message\n"
|
||||
);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct iso_volset *volset;
|
||||
struct iso_volume *volume;
|
||||
struct iso_tree_node *root;
|
||||
struct burn_source *src;
|
||||
unsigned char buf[2048];
|
||||
FILE *fd;
|
||||
int c;
|
||||
int level=1, flags=0;
|
||||
|
||||
while ((c = getopt(argc, argv, optstring)) != -1) {
|
||||
switch(c) {
|
||||
case 'h':
|
||||
usage();
|
||||
help();
|
||||
exit(0);
|
||||
break;
|
||||
case 'J':
|
||||
flags |= ECMA119_JOLIET;
|
||||
break;
|
||||
case 'R':
|
||||
flags |= ECMA119_ROCKRIDGE;
|
||||
break;
|
||||
case 'L':
|
||||
level = atoi(optarg);
|
||||
break;
|
||||
case '?':
|
||||
usage();
|
||||
exit(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (argc < 2) {
|
||||
printf ("must pass directory to build iso from\n");
|
||||
usage();
|
||||
return 1;
|
||||
}
|
||||
if (argc < 3) {
|
||||
printf ("must supply output file\n");
|
||||
usage();
|
||||
return 1;
|
||||
}
|
||||
fd = fopen(argv[optind+1], "w");
|
||||
if (!fd) {
|
||||
err(1, "error opening output file");
|
||||
}
|
||||
|
||||
root = iso_tree_radd_dir(NULL, argv[optind]);
|
||||
if (!root) {
|
||||
err(1, "error opening input directory");
|
||||
}
|
||||
volume = iso_volume_new_with_root( "VOLID", "PUBID", "PREPID", root );
|
||||
volset = iso_volset_new( volume, "VOLSETID" );
|
||||
|
||||
src = iso_source_new_ecma119(volset, 0, level, flags);
|
||||
|
||||
while (src->read(src, buf, 2048) == 2048) {
|
||||
fwrite(buf, 1, 2048, fd);
|
||||
}
|
||||
fclose(fd);
|
||||
|
||||
return 0;
|
||||
}
|
297
test/iso.py
297
test/iso.py
@ -1,297 +0,0 @@
|
||||
|
||||
import struct
|
||||
import tree
|
||||
import sys
|
||||
|
||||
voldesc_fmt = "B" "5s" "B" "2041x"
|
||||
|
||||
# all these fields are common between the pri and sec voldescs
|
||||
privoldesc_fmt = "B" "5s" "B" "x" "32s" "32s" "8x" "8s" "32x" "4s" "4s" "4s" "8s" "4s4s" "4s4s" "34s" "128s" \
|
||||
"128s" "128s" "128s" "37s" "37s" "37s" "17s" "17s" "17s" "17s" "B" "x" "512s" "653x"
|
||||
|
||||
# the fields unique to the sec_vol_desc
|
||||
secvoldesc_fmt = "x" "5x" "x" "B" "32x" "32x" "8x" "8x" "32s" "4x" "4x" "4x" "8x" "4x4x" "4x4x" "34x" "128x" \
|
||||
"128x" "128x" "128x" "37x" "37x" "37x" "17x" "17x" "17x" "17x" "x" "x" "512x" "653x"
|
||||
|
||||
dirrecord_fmt = "B" "B" "8s" "8s" "7s" "B" "B" "B" "4s" "B" # + file identifier, padding field and SU area
|
||||
|
||||
pathrecord_fmt = "B" "B" "4s" "2s" # + directory identifier and padding field
|
||||
|
||||
def read_bb(str, le, be):
|
||||
val1, = struct.unpack(le, str)
|
||||
val2, = struct.unpack(be, str)
|
||||
if val1 != val2:
|
||||
print "val1=%d, val2=%d" % (val1, val2)
|
||||
raise AssertionError, "values are not equal in dual byte-order field"
|
||||
return val1
|
||||
|
||||
def read_bb4(str):
|
||||
return read_bb(str, "<I4x", ">4xI")
|
||||
|
||||
def read_bb2(str):
|
||||
return read_bb(str, "<H2x", ">2xH")
|
||||
|
||||
def read_lsb4(str):
|
||||
return struct.unpack("<I", str)[0]
|
||||
|
||||
def read_lsb2(str):
|
||||
return struct.unpack("<H", str)[0]
|
||||
|
||||
def read_msb4(str):
|
||||
return struct.unpack(">I", str)[0]
|
||||
|
||||
def read_msb2(str):
|
||||
return struct.unpack(">H", str)[0]
|
||||
|
||||
class VolDesc(object):
|
||||
def __init__(self, data):
|
||||
print "fmt len=%d, data len=%d" % ( struct.calcsize(voldesc_fmt), len(data) )
|
||||
self.vol_desc_type, self.standard_id, self.vol_desc_version = struct.unpack(voldesc_fmt, data)
|
||||
|
||||
class PriVolDesc(VolDesc):
|
||||
def __init__(self, data):
|
||||
self.vol_desc_type, \
|
||||
self.standard_id, \
|
||||
self.vol_desc_version, \
|
||||
self.system_id, \
|
||||
self.volume_id, \
|
||||
self.vol_space_size, \
|
||||
self.vol_set_size, \
|
||||
self.vol_seq_num, \
|
||||
self.block_size, \
|
||||
self.path_table_size, \
|
||||
self.l_table_pos, \
|
||||
self.l_table2_pos, \
|
||||
self.m_table_pos, \
|
||||
self.m_table2_pos, \
|
||||
self.root_record, \
|
||||
self.volset_id, \
|
||||
self.publisher_id, \
|
||||
self.preparer_id, \
|
||||
self.application_id, \
|
||||
self.copyright_file, \
|
||||
self.abstract_file, \
|
||||
self.bibliographic_file, \
|
||||
self.creation_timestamp, \
|
||||
self.modification_timestamp, \
|
||||
self.expiration_timestamp, \
|
||||
self.effective_timestamp, \
|
||||
self.file_struct_version, \
|
||||
self.application_use = struct.unpack(privoldesc_fmt, data)
|
||||
|
||||
# take care of reading the integer types
|
||||
self.vol_space_size = read_bb4(self.vol_space_size)
|
||||
self.vol_set_size = read_bb2(self.vol_set_size)
|
||||
self.vol_seq_num = read_bb2(self.vol_seq_num)
|
||||
self.block_size = read_bb2(self.block_size)
|
||||
self.path_table_size = read_bb4(self.path_table_size)
|
||||
self.l_table_pos = read_lsb4(self.l_table_pos)
|
||||
self.l_table2_pos = read_lsb4(self.l_table2_pos)
|
||||
self.m_table_pos = read_msb4(self.m_table_pos)
|
||||
self.m_table2_pos = read_msb4(self.m_table2_pos)
|
||||
|
||||
# parse the root directory record
|
||||
self.root_record = DirRecord(self.root_record)
|
||||
|
||||
def readPathTables(self, file):
|
||||
file.seek( self.block_size * self.l_table_pos )
|
||||
self.l_table = PathTable( file.read(self.path_table_size), 0 )
|
||||
file.seek( self.block_size * self.m_table_pos )
|
||||
self.m_table = PathTable( file.read(self.path_table_size), 1 )
|
||||
|
||||
if self.l_table2_pos:
|
||||
file.seek( self.block_size * self.l_table2_pos )
|
||||
self.l_table2 = PathTable( file.read(self.path_table_size), 0 )
|
||||
else:
|
||||
self.l_table2 = None
|
||||
|
||||
if self.m_table2_pos:
|
||||
file.seek( self.block_size * self.m_table2_pos )
|
||||
self.m_table2 = PathTable( file.read(self.path_table_size), 1 )
|
||||
else:
|
||||
self.m_table2 = None
|
||||
|
||||
def toTree(self, isofile):
|
||||
ret = tree.Tree(isofile=isofile.name)
|
||||
ret.root = self.root_record.toTreeNode(parent=None, isofile=isofile)
|
||||
return ret
|
||||
|
||||
class SecVolDesc(PriVolDesc):
|
||||
def __init__(self, data):
|
||||
super(SecVolDesc,self).__init__(data)
|
||||
self.flags, self.escape_sequences = struct.unpack(secvoldesc_fmt, data)
|
||||
|
||||
# return a single volume descriptor of the appropriate type
|
||||
def readVolDesc(data):
|
||||
desc = VolDesc(data)
|
||||
if desc.standard_id != "CD001":
|
||||
print "Unexpected standard_id " +desc.standard_id
|
||||
return None
|
||||
if desc.vol_desc_type == 1:
|
||||
return PriVolDesc(data)
|
||||
elif desc.vol_desc_type == 2:
|
||||
return SecVolDesc(data)
|
||||
elif desc.vol_desc_type == 3:
|
||||
print "I don't know about partitions yet!"
|
||||
return None
|
||||
elif desc.vol_desc_type == 255:
|
||||
return desc
|
||||
else:
|
||||
print "Unknown volume descriptor type %d" % (desc.vol_desc_type,)
|
||||
return None
|
||||
|
||||
def readVolDescSet(file):
|
||||
ret = [ readVolDesc(file.read(2048)) ]
|
||||
while ret[-1].vol_desc_type != 255:
|
||||
ret.append( readVolDesc(file.read(2048)) )
|
||||
|
||||
for vol in ret:
|
||||
if vol.vol_desc_type == 1 or vol.vol_desc_type == 2:
|
||||
vol.readPathTables(file)
|
||||
|
||||
return ret
|
||||
|
||||
class DirRecord:
|
||||
def __init__(self, data):
|
||||
self.len_dr, \
|
||||
self.len_xa, \
|
||||
self.block, \
|
||||
self.len_data, \
|
||||
self.timestamp, \
|
||||
self.flags, \
|
||||
self.unit_size, \
|
||||
self.gap_size, \
|
||||
self.vol_seq_number, \
|
||||
self.len_fi = struct.unpack(dirrecord_fmt, data[:33])
|
||||
self.children = []
|
||||
|
||||
if self.len_dr > len(data):
|
||||
raise AssertionError, "Error: not enough data to read in DirRecord()"
|
||||
elif self.len_dr < 34:
|
||||
raise AssertionError, "Error: directory record too short"
|
||||
|
||||
fmt = str(self.len_fi) + "s"
|
||||
if self.len_fi % 2 == 0:
|
||||
fmt += "1x"
|
||||
len_su = self.len_dr - (33 + self.len_fi + 1 - (self.len_fi % 2))
|
||||
fmt += str(len_su) + "s"
|
||||
|
||||
if len(data) >= self.len_dr:
|
||||
self.file_id, self.su = struct.unpack(fmt, data[33 : self.len_dr])
|
||||
else:
|
||||
print "Error: couldn't read file_id: not enough data"
|
||||
self.file_id = "BLANK"
|
||||
self.su = ""
|
||||
|
||||
# convert to integers
|
||||
self.block = read_bb4(self.block)
|
||||
self.len_data = read_bb4(self.len_data)
|
||||
self.vol_seq_number = read_bb2(self.vol_seq_number)
|
||||
|
||||
def toTreeNode(self, parent, isofile, path=""):
|
||||
ret = tree.TreeNode(parent=parent, isofile=isofile.name)
|
||||
if len(path) > 0:
|
||||
path += "/"
|
||||
path += self.file_id
|
||||
ret.path = path
|
||||
|
||||
if self.flags & 2: # we are a directory, recurse
|
||||
isofile.seek( 2048 * self.block )
|
||||
data = isofile.read( self.len_data )
|
||||
pos = 0
|
||||
while pos < self.len_data:
|
||||
try:
|
||||
child = DirRecord( data[pos:] )
|
||||
pos += child.len_dr
|
||||
if child.len_fi == 1 and (child.file_id == "\x00" or child.file_id == "\x01"):
|
||||
continue
|
||||
print "read child named " +child.file_id
|
||||
self.children.append( child )
|
||||
ret.children.append( child.toTreeNode(ret, isofile, path) )
|
||||
except AssertionError:
|
||||
print "Couldn't read child of directory %s, position is %d, len is %d" % \
|
||||
(path, pos, self.len_data)
|
||||
raise
|
||||
|
||||
return ret
|
||||
|
||||
class PathTableRecord:
|
||||
def __init__(self, data, readint2, readint4):
|
||||
self.len_di, self.len_xa, self.block, self.parent_number = struct.unpack(pathrecord_fmt, data[:8])
|
||||
|
||||
if len(data) < self.len_di + 8:
|
||||
raise AssertionError, "Error: not enough data to read path table record"
|
||||
|
||||
fmt = str(self.len_di) + "s"
|
||||
self.dir_id, = struct.unpack(fmt, data[8:8+self.len_di])
|
||||
|
||||
self.block = readint4(self.block)
|
||||
self.parent_number = readint2(self.parent_number)
|
||||
|
||||
class PathTable:
|
||||
def __init__(self, data, m_type):
|
||||
if m_type:
|
||||
readint2 = read_msb2
|
||||
readint4 = read_msb4
|
||||
else:
|
||||
readint2 = read_lsb2
|
||||
readint4 = read_lsb4
|
||||
pos = 0
|
||||
self.records = []
|
||||
while pos < len(data):
|
||||
try:
|
||||
self.records.append( PathTableRecord(data[pos:], readint2, readint4) )
|
||||
print "Read path record %d: dir_id %s, block %d, parent_number %d" %\
|
||||
(len(self.records), self.records[-1].dir_id, self.records[-1].block, self.records[-1].parent_number)
|
||||
pos += self.records[-1].len_di + 8
|
||||
pos += pos % 2
|
||||
except AssertionError:
|
||||
print "Last successfully read path table record had dir_id %s, block %d, parent_number %d" % \
|
||||
(self.records[-1].dir_id, self.records[-1].block, self.records[-1].parent_number)
|
||||
print "Error was near offset %x" % (pos,)
|
||||
raise
|
||||
|
||||
def findRecord(self, dir_id, block, parent_number):
|
||||
number=1
|
||||
for record in self.records:
|
||||
if record.dir_id == dir_id and record.block == block and record.parent_number == parent_number:
|
||||
return number, record
|
||||
number += 1
|
||||
|
||||
return None, None
|
||||
|
||||
# check this path table for consistency against the actual directory heirarchy
|
||||
def crossCheckDirRecords(self, root, parent_number=1):
|
||||
number, rec = self.findRecord(root.file_id, root.block, parent_number)
|
||||
|
||||
if not rec:
|
||||
print "Error: directory record parent_number %d, dir_id %s, block %d doesn't match a path table record" % \
|
||||
(parent_number, root.file_id, root.block)
|
||||
parent = self.records[parent_number]
|
||||
print "Parent has parent_number %d, dir_id %s, block %d" % (parent.parent_number, parent.dir_id, parent.block)
|
||||
return 0
|
||||
|
||||
for child in root.children:
|
||||
if child.flags & 2:
|
||||
self.crossCheckDirRecords(child, number)
|
||||
|
||||
|
||||
if len(sys.argv) != 2:
|
||||
print "Please enter the name of the .iso file to open"
|
||||
sys.exit(1)
|
||||
|
||||
f = file(sys.argv[1])
|
||||
f.seek(2048 * 16) # system area
|
||||
volumes = readVolDescSet(f)
|
||||
vol = volumes[0]
|
||||
t = vol.toTree(f)
|
||||
vol.l_table.crossCheckDirRecords(vol.root_record)
|
||||
vol.m_table.crossCheckDirRecords(vol.root_record)
|
||||
|
||||
vol = volumes[1]
|
||||
try:
|
||||
t = vol.toTree(f)
|
||||
vol.l_table.crossCheckDirRecords(vol.root_record)
|
||||
vol.m_table.crossCheckDirRecords(vol.root_record)
|
||||
except AttributeError:
|
||||
pass
|
156
test/libburner.c
156
test/libburner.c
@ -1,7 +1,7 @@
|
||||
|
||||
/* test/libburner.c , API illustration of burning a single data track to CD */
|
||||
/* Copyright (C) 2005 - 2006 Thomas Schmitt <scdbackup@gmx.net> */
|
||||
/* Provided under GPL, see also "License and copyright aspects" at file end */
|
||||
/* test/libburner.c , API illustration of burning data or audio tracks to CD */
|
||||
/* Copyright (C) 2005 - 2006 Thomas Schmitt <scdbackup@gmx.net> */
|
||||
/* Provided under GPL, see also "License and copyright aspects" at file end */
|
||||
|
||||
|
||||
/** Overview
|
||||
@ -35,17 +35,10 @@
|
||||
|
||||
*/
|
||||
|
||||
/* We shall prepare for times when more than 2 GB of data are to be handled.
|
||||
This gives POSIX-ly 64 bit off_t */
|
||||
#ifndef _LARGEFILE_SOURCE
|
||||
#define _LARGEFILE_SOURCE 1
|
||||
#endif
|
||||
#ifndef _FILE_OFFSET_BITS
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
#endif
|
||||
|
||||
/** See this for the decisive API specs . libburn.h is The Original */
|
||||
#include <libburn/libburn.h>
|
||||
/* For using the installed header file : #include <libburn/libburn.h> */
|
||||
/* This program insists in the own headerfile. */
|
||||
#include "../libburn/libburn.h"
|
||||
|
||||
/* libburn is intended for Linux systems with kernel 2.4 or 2.6 for now */
|
||||
#include <stdio.h>
|
||||
@ -56,6 +49,8 @@
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
|
||||
/** For simplicity i use global variables to represent the drives.
|
||||
@ -120,9 +115,6 @@ int libburner_aquire_by_adr(char *drive_adr)
|
||||
if (ret <= 0) {
|
||||
fprintf(stderr,"FAILURE with persistent drive address '%s'\n",
|
||||
drive_adr);
|
||||
if (strncmp(drive_adr,"/dev/sg",7) != 0 &&
|
||||
strncmp(drive_adr,"/dev/hd",7) != 0)
|
||||
fprintf(stderr,"\nHINT: Consider addresses like '/dev/hdc' or '/dev/sg0'\n");
|
||||
} else {
|
||||
printf("Done\n");
|
||||
drive_is_grabbed = 1;
|
||||
@ -182,7 +174,6 @@ int libburner_aquire_by_driveno(int *driveno)
|
||||
}
|
||||
printf("-----------------------------------------------------------------------------\n\n");
|
||||
|
||||
|
||||
/*
|
||||
On multi-drive systems save yourself from sysadmins' revenge.
|
||||
|
||||
@ -249,7 +240,8 @@ int libburner_aquire_by_driveno(int *driveno)
|
||||
int libburner_blank_disc(struct burn_drive *drive, int blank_fast)
|
||||
{
|
||||
enum burn_disc_status disc_state;
|
||||
struct burn_progress progress;
|
||||
struct burn_progress p;
|
||||
int percent = 1;
|
||||
|
||||
while (burn_drive_get_status(drive, NULL) != BURN_DRIVE_IDLE)
|
||||
usleep(1001);
|
||||
@ -281,11 +273,13 @@ int libburner_blank_disc(struct burn_drive *drive, int blank_fast)
|
||||
}
|
||||
printf(
|
||||
"Beginning to %s-blank CD media.\n", (blank_fast?"fast":"full"));
|
||||
printf(
|
||||
"Expect some garbage sector numbers and some zeros at first.\n");
|
||||
burn_disc_erase(drive, blank_fast);
|
||||
while (burn_drive_get_status(drive, &progress) != BURN_DRIVE_IDLE) {
|
||||
printf("Blanking sector %d\n", progress.sector);
|
||||
sleep(1);
|
||||
while (burn_drive_get_status(drive, &p) != BURN_DRIVE_IDLE) {
|
||||
if(p.sectors>0 && p.sector>=0) /* display 1 to 99 percent */
|
||||
percent = 1.0 + ((double) p.sector+1.0)
|
||||
/ ((double) p.sectors) * 98.0;
|
||||
printf("Blanking ( %d%% done )\n", percent);
|
||||
sleep(1);
|
||||
}
|
||||
printf("Done\n");
|
||||
@ -324,7 +318,7 @@ int libburner_regrab(struct burn_drive *drive) {
|
||||
*/
|
||||
int libburner_payload(struct burn_drive *drive,
|
||||
char source_adr[][4096], int source_adr_count,
|
||||
off_t stdin_size, int simulate_burn, int all_tracks_type)
|
||||
int multi, int simulate_burn, int all_tracks_type)
|
||||
{
|
||||
struct burn_source *data_src;
|
||||
struct burn_disc *target_disc;
|
||||
@ -334,8 +328,10 @@ int libburner_payload(struct burn_drive *drive,
|
||||
struct burn_track *track, *tracklist[99];
|
||||
struct burn_progress progress;
|
||||
time_t start_time;
|
||||
int last_sector = 0, padding = 0, trackno;
|
||||
int last_sector = 0, padding = 0, trackno, write_mode_tao = 0, fd;
|
||||
off_t fixed_size;
|
||||
char *adr;
|
||||
struct stat stbuf;
|
||||
|
||||
if (all_tracks_type != BURN_AUDIO) {
|
||||
all_tracks_type = BURN_MODE1;
|
||||
@ -352,12 +348,21 @@ int libburner_payload(struct burn_drive *drive,
|
||||
burn_track_define_data(track, 0, padding, 1, all_tracks_type);
|
||||
|
||||
adr = source_adr[trackno];
|
||||
fixed_size = 0;
|
||||
if (adr[0] == '-' && adr[1] == 0) {
|
||||
data_src = burn_fd_source_new(0, -1, stdin_size);
|
||||
printf("Note: using standard input as source with %.f bytes\n",
|
||||
(double) stdin_size);
|
||||
} else
|
||||
data_src = burn_file_source_new(adr, NULL);
|
||||
fd = 0;
|
||||
} else {
|
||||
fd = open(adr, O_RDONLY);
|
||||
if (fd>=0)
|
||||
if (fstat(fd,&stbuf)!=-1)
|
||||
if((stbuf.st_mode&S_IFMT)==S_IFREG)
|
||||
fixed_size = stbuf.st_size;
|
||||
}
|
||||
if (fixed_size==0)
|
||||
write_mode_tao = 1;
|
||||
data_src = NULL;
|
||||
if (fd>=0)
|
||||
data_src = burn_fd_source_new(fd, -1, fixed_size);
|
||||
if (data_src == NULL) {
|
||||
fprintf(stderr,
|
||||
"FATAL: Could not open data source '%s'.\n",adr);
|
||||
@ -382,39 +387,30 @@ int libburner_payload(struct burn_drive *drive,
|
||||
/* Evaluate drive and media */
|
||||
while ((disc_state = burn_disc_get_status(drive)) == BURN_DISC_UNREADY)
|
||||
usleep(100001);
|
||||
if (disc_state != BURN_DISC_BLANK) {
|
||||
if (disc_state == BURN_DISC_FULL ||
|
||||
disc_state == BURN_DISC_APPENDABLE) {
|
||||
fprintf(stderr,
|
||||
"FATAL: Media with data detected. Need blank media.\n");
|
||||
if (disc_state == BURN_DISC_APPENDABLE) {
|
||||
write_mode_tao = 1;
|
||||
} else if (disc_state != BURN_DISC_BLANK) {
|
||||
if (disc_state == BURN_DISC_FULL) {
|
||||
fprintf(stderr, "FATAL: Closed media with data detected. Need blank or appendable media.\n");
|
||||
if (burn_disc_erasable(drive))
|
||||
fprintf(stderr, "HINT: Try --blank_fast\n\n");
|
||||
} else if (disc_state == BURN_DISC_EMPTY)
|
||||
fprintf(stderr,"FATAL: No media detected in drive\n");
|
||||
else
|
||||
fprintf(stderr,
|
||||
"FATAL: Cannot recognize drive and media state\n");
|
||||
"FATAL: Cannot recognize state of drive and media\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
burn_options = burn_write_opts_new(drive);
|
||||
burn_write_opts_set_perform_opc(burn_options, 0);
|
||||
|
||||
#ifdef Libburner_raw_mode_which_i_do_not_likE
|
||||
/* This yields higher CD capacity but hampers my IDE controller
|
||||
with burning on one drive and reading on another simultaneously.
|
||||
My burner does not obey the order --try_to_simulate in this mode.
|
||||
*/
|
||||
burn_write_opts_set_write_type(burn_options,
|
||||
BURN_WRITE_RAW, BURN_BLOCK_RAW96R);
|
||||
#else
|
||||
|
||||
/* This is by what cdrskin competes with cdrecord -sao which
|
||||
i understand is the mode preferrably advised by Joerg Schilling */
|
||||
burn_write_opts_set_write_type(burn_options,
|
||||
BURN_WRITE_SAO, BURN_BLOCK_SAO);
|
||||
|
||||
#endif
|
||||
burn_write_opts_set_multi(burn_options, !!multi);
|
||||
if (write_mode_tao)
|
||||
burn_write_opts_set_write_type(burn_options,
|
||||
BURN_WRITE_TAO, BURN_BLOCK_MODE1);
|
||||
else
|
||||
burn_write_opts_set_write_type(burn_options,
|
||||
BURN_WRITE_SAO, BURN_BLOCK_SAO);
|
||||
if(simulate_burn)
|
||||
printf("\n*** Will TRY to SIMULATE burning ***\n\n");
|
||||
burn_write_opts_set_simulate(burn_options, simulate_burn);
|
||||
@ -434,8 +430,11 @@ int libburner_payload(struct burn_drive *drive,
|
||||
printf(
|
||||
"Thank you for being patient since %d seconds.\n",
|
||||
(int) (time(0) - start_time));
|
||||
else if(write_mode_tao)
|
||||
printf("Track %d : sector %d\n", progress.track+1,
|
||||
progress.sector);
|
||||
else
|
||||
printf("Track %d : sector %d of %d\n", progress.track,
|
||||
printf("Track %d : sector %d of %d\n",progress.track+1,
|
||||
progress.sector, progress.sectors);
|
||||
last_sector = progress.sector;
|
||||
sleep(1);
|
||||
@ -446,19 +445,21 @@ int libburner_payload(struct burn_drive *drive,
|
||||
burn_track_free(tracklist[trackno]);
|
||||
burn_session_free(session);
|
||||
burn_disc_free(target_disc);
|
||||
if(simulate_burn)
|
||||
if (multi)
|
||||
printf("NOTE: Media left appendable.\n");
|
||||
if (simulate_burn)
|
||||
printf("\n*** Did TRY to SIMULATE burning ***\n\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/** The setup parameters of libburn */
|
||||
static char drive_adr[BURN_DRIVE_ADR_LEN]= {""};
|
||||
static int driveno= 0;
|
||||
static int do_blank= 0;
|
||||
/** The setup parameters of libburner */
|
||||
static char drive_adr[BURN_DRIVE_ADR_LEN] = {""};
|
||||
static int driveno = 0;
|
||||
static int do_blank = 0;
|
||||
static char source_adr[99][4096];
|
||||
static int source_adr_count= 0;
|
||||
static off_t stdin_size= 650*1024*1024;
|
||||
static int source_adr_count = 0;
|
||||
static int do_multi = 0;
|
||||
static int simulate_burn = 0;
|
||||
static int all_tracks_type = BURN_MODE1;
|
||||
|
||||
@ -503,15 +504,12 @@ int libburner_setup(int argc, char **argv)
|
||||
}
|
||||
strcpy(drive_adr, argv[i]);
|
||||
}
|
||||
} else if (!strcmp(argv[i], "--stdin_size")) {
|
||||
++i;
|
||||
if (i >= argc) {
|
||||
fprintf(stderr,"--stdin_size requires an argument\n");
|
||||
return 3;
|
||||
} else
|
||||
stdin_size = atoi(argv[i]);
|
||||
if (stdin_size < 600*1024) /* minimum readable track size */
|
||||
stdin_size = 600*1024;
|
||||
} else if (!strcmp(argv[i], "--multi")) {
|
||||
do_multi = 1;
|
||||
|
||||
} else if (!strcmp(argv[i], "--stdin_size")) { /* obsoleted */
|
||||
i++;
|
||||
|
||||
} else if (!strcmp(argv[i], "--try_to_simulate")) {
|
||||
simulate_burn = 1;
|
||||
|
||||
@ -543,17 +541,15 @@ int libburner_setup(int argc, char **argv)
|
||||
insuffient_parameters = 0;
|
||||
if (print_help || insuffient_parameters ) {
|
||||
printf("Usage: %s\n", argv[0]);
|
||||
printf(" [--drive <address>|<driveno>|\"-\"]\n");
|
||||
printf(" [--blank_fast|--blank_full] [--audio]\n");
|
||||
printf(" [--try_to_simulate] [--stdin_size <bytes>]\n");
|
||||
printf(" [<one or more imagefiles>|\"-\"]\n");
|
||||
printf(" [--drive <address>|<driveno>|\"-\"] [--audio]\n");
|
||||
printf(" [--blank_fast|--blank_full] [--try_to_simulate]\n");
|
||||
printf(" [--multi] [<one or more imagefiles>|\"-\"]\n");
|
||||
printf("Examples\n");
|
||||
printf("A bus scan (needs rw-permissions to see a drive):\n");
|
||||
printf(" %s --drive -\n",argv[0]);
|
||||
printf("Burn a file to drive chosen by number:\n");
|
||||
printf(" %s --drive 0 my_image_file\n",
|
||||
argv[0]);
|
||||
printf("Burn a file to drive chosen by persistent address:\n");
|
||||
printf("Burn a file to drive chosen by number, leave appendable:\n");
|
||||
printf(" %s --drive 0 --multi my_image_file\n", argv[0]);
|
||||
printf("Burn a file to drive chosen by persistent address, close:\n");
|
||||
printf(" %s --drive /dev/hdc my_image_file\n", argv[0]);
|
||||
printf("Blank a used CD-RW (is combinable with burning in one run):\n");
|
||||
printf(" %s --drive /dev/hdc --blank_fast\n",argv[0]);
|
||||
@ -561,9 +557,9 @@ int libburner_setup(int argc, char **argv)
|
||||
printf(" lame --decode -t /path/to/track1.mp3 track1.cd\n");
|
||||
printf(" test/dewav /path/to/track2.wav -o track2.cd\n");
|
||||
printf(" %s --drive /dev/hdc --audio track1.cd track2.cd\n", argv[0]);
|
||||
printf("Burn a compressed afio archive on-the-fly, pad up to 700 MB:\n");
|
||||
printf("Burn a compressed afio archive on-the-fly:\n");
|
||||
printf(" ( cd my_directory ; find . -print | afio -oZ - ) | \\\n");
|
||||
printf(" %s --drive /dev/hdc --stdin_size 734003200 -\n", argv[0]);
|
||||
printf(" %s --drive /dev/hdc -\n", argv[0]);
|
||||
printf("To be read from *not mounted* CD via: afio -tvZ /dev/hdc\n");
|
||||
printf("Program tar would need a clean EOF which our padded CD cannot deliver.\n");
|
||||
if (insuffient_parameters)
|
||||
@ -620,8 +616,8 @@ int main(int argc, char **argv)
|
||||
}
|
||||
if (source_adr_count > 0) {
|
||||
ret = libburner_payload(drive_list[driveno].drive,
|
||||
source_adr, source_adr_count, stdin_size,
|
||||
simulate_burn, all_tracks_type);
|
||||
source_adr, source_adr_count,
|
||||
do_multi, simulate_burn, all_tracks_type);
|
||||
if (ret<=0)
|
||||
{ ret = 38; goto release_drive; }
|
||||
}
|
||||
|
126
test/master.c
126
test/master.c
@ -1,126 +0,0 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
#include <libburn/libburn.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static struct burn_drive_info *drives;
|
||||
static unsigned int n_drives;
|
||||
|
||||
void burn_files(struct burn_drive *drive, struct burn_disc *disc)
|
||||
{
|
||||
struct burn_write_opts *o;
|
||||
enum burn_disc_status s;
|
||||
|
||||
if (!burn_drive_grab(drive, 1)) {
|
||||
printf("Unable to open the drive!\n");
|
||||
return;
|
||||
}
|
||||
while (burn_drive_get_status(drive, NULL))
|
||||
usleep(1000);
|
||||
|
||||
while ((s = burn_disc_get_status(drive)) == BURN_DISC_UNREADY)
|
||||
usleep(1000);
|
||||
|
||||
if (s != BURN_DISC_BLANK) {
|
||||
burn_drive_release(drive, 0);
|
||||
printf("Please insert blank media in the drive\n");
|
||||
return;
|
||||
}
|
||||
o = burn_write_opts_new(drive);
|
||||
burn_drive_set_speed(drive, 0, 2816);
|
||||
burn_write_opts_set_perform_opc(o, 0);
|
||||
burn_write_opts_set_write_type(o, BURN_WRITE_TAO, BURN_BLOCK_MODE1);
|
||||
burn_write_opts_set_simulate(o, 1);
|
||||
/* want failure on seggy while debugging :) */
|
||||
burn_write_opts_set_underrun_proof(o, 0);
|
||||
burn_structure_print_disc(disc);
|
||||
burn_disc_write(o, disc);
|
||||
burn_write_opts_free(o);
|
||||
|
||||
while (burn_drive_get_status(drive, NULL)) {
|
||||
sleep(1);
|
||||
}
|
||||
printf("\n");
|
||||
burn_drive_release(drive, 0);
|
||||
burn_disc_free(disc);
|
||||
}
|
||||
|
||||
void parse_args(int argc, char **argv, int *drive, struct burn_disc **disc)
|
||||
{
|
||||
int i, tmode = BURN_AUDIO;
|
||||
int help = 1;
|
||||
struct burn_session *session;
|
||||
struct burn_track *tr;
|
||||
struct burn_source *src;
|
||||
|
||||
*disc = burn_disc_create();
|
||||
session = burn_session_create();
|
||||
burn_disc_add_session(*disc, session, BURN_POS_END);
|
||||
for (i = 1; i < argc; ++i) {
|
||||
if (!strcmp(argv[i], "--drive")) {
|
||||
++i;
|
||||
if (i >= argc)
|
||||
printf("--drive requires an argument\n");
|
||||
else
|
||||
*drive = atoi(argv[i]);
|
||||
} else if (!strcmp(argv[i], "--audio")) {
|
||||
tmode = BURN_AUDIO;
|
||||
} else if (!strcmp(argv[i], "--data")) {
|
||||
tmode = BURN_MODE1;
|
||||
} else if (!strcmp(argv[i], "--verbose")) {
|
||||
++i;
|
||||
if (i >= argc)
|
||||
printf("--verbose requires an argument\n");
|
||||
else
|
||||
burn_set_verbosity(atoi(argv[i]));
|
||||
} else if (!strcmp(argv[i], "--help")) {
|
||||
help = 1; /* who cares */
|
||||
} else {
|
||||
help = 0;
|
||||
printf("%s is a track\n", argv[i]);
|
||||
|
||||
tr = burn_track_create();
|
||||
src = burn_file_source_new(argv[i], NULL);
|
||||
burn_track_set_source(tr, src);
|
||||
burn_source_free(src);
|
||||
burn_track_define_data(tr, 0, 0, 1, tmode);
|
||||
burn_session_add_track(session, tr, BURN_POS_END);
|
||||
}
|
||||
}
|
||||
if (help) {
|
||||
printf("Usage: %s [--drive <num>] [--verbose <level>] files\n",
|
||||
argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int drive = 0;
|
||||
struct burn_disc *disc;
|
||||
|
||||
parse_args(argc, argv, &drive, &disc);
|
||||
printf("Initializing library...");
|
||||
if (burn_initialize())
|
||||
printf("Success\n");
|
||||
else {
|
||||
printf("Failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("Scanning for devices...");
|
||||
while (!burn_drive_scan(&drives, &n_drives)) ;
|
||||
printf("Done\n");
|
||||
|
||||
burn_files(drives[drive].drive, disc);
|
||||
burn_drive_info_free(drives);
|
||||
burn_finish();
|
||||
return 0;
|
||||
}
|
54
test/rip.c
54
test/rip.c
@ -1,54 +0,0 @@
|
||||
/* THIS IS NOT A PROPER EXAMPLE */
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <libburn/libburn.h>
|
||||
|
||||
static struct burn_drive_info *drives;
|
||||
static unsigned int n_drives;
|
||||
|
||||
#warning this example is totally fried
|
||||
int main()
|
||||
{
|
||||
#if 0
|
||||
struct burn_drive *drive;
|
||||
struct burn_read_opts o;
|
||||
|
||||
burn_initialize();
|
||||
o.datafd =
|
||||
open("/xp/burn/blah.data", O_CREAT | O_WRONLY,
|
||||
S_IRUSR | S_IWUSR);
|
||||
o.subfd =
|
||||
open("/xp/burn/blah.sub", O_CREAT | O_WRONLY,
|
||||
S_IRUSR | S_IWUSR);
|
||||
o.raw = 1;
|
||||
o.c2errors = 0;
|
||||
o.subcodes_audio = 1;
|
||||
o.subcodes_data = 1;
|
||||
o.hardware_error_recovery = 1;
|
||||
o.report_recovered_errors = 0;
|
||||
o.transfer_damaged_blocks = 1;
|
||||
o.hardware_error_retries = 1;
|
||||
|
||||
printf("Scanning for devices...");
|
||||
while (!burn_drive_scan(&drives, &n_drives)) ;
|
||||
printf("Done\n");
|
||||
drive = drives[0].drive;
|
||||
burn_drive_set_speed(drive, 0, 0);
|
||||
|
||||
if (!burn_drive_grab(drive, 1)) {
|
||||
printf("Unable to open the drive!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
while (burn_drive_get_status(drive, NULL))
|
||||
usleep(1000);
|
||||
|
||||
burn_disc_read(drive, &o);
|
||||
#endif
|
||||
return EXIT_SUCCESS;
|
||||
}
|
574
test/telltoc.c
Normal file
574
test/telltoc.c
Normal file
@ -0,0 +1,574 @@
|
||||
|
||||
/* test/telltoc.c , API illustration of obtaining media status info */
|
||||
/* Copyright (C) 2006 Thomas Schmitt <scdbackup@gmx.net> Provided under GPL */
|
||||
|
||||
/** Overview
|
||||
|
||||
telltoc is a minimal demo application for the library libburn as provided
|
||||
on http://libburn.pykix.org . It can list the available devices, can display
|
||||
some drive properties, the type of media, eventual table of content and
|
||||
multisession info for mkisofs option -C .
|
||||
It's main purpose, nevertheless, is to show you how to use libburn and also
|
||||
to serve the libburn team as reference application. telltoc.c does indeed
|
||||
define the standard way how above gestures can be implemented and stay upward
|
||||
compatible for a good while.
|
||||
|
||||
Before you can do anything, you have to initialize libburn by
|
||||
burn_initialize()
|
||||
as it is done in main() at the end of this file. Then you aquire a
|
||||
drive in an appropriate way conforming to the API. The two main
|
||||
approaches are shown here in application functions:
|
||||
telltoc_aquire_by_adr() demonstrates usage as of cdrecord traditions
|
||||
telltoc_aquire_by_driveno() demonstrates a scan-and-choose approach
|
||||
With that aquired drive you can call
|
||||
telltoc_media() prints some information about the media in a drive
|
||||
telltoc_toc() prints a table of content (if there is content)
|
||||
telltoc_msinfo() prints parameters for mkisofs option -C
|
||||
When everything is done, main() releases the drive and shuts down libburn:
|
||||
burn_drive_release();
|
||||
burn_finish()
|
||||
|
||||
*/
|
||||
|
||||
/** See this for the decisive API specs . libburn.h is The Original */
|
||||
/* For using the installed header file : #include <libburn/libburn.h> */
|
||||
/* This program insists in the own headerfile. */
|
||||
#include "../libburn/libburn.h"
|
||||
|
||||
/* libburn is intended for Linux systems with kernel 2.4 or 2.6 for now */
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
|
||||
/** For simplicity i use global variables to represent the drives.
|
||||
Drives are systemwide global, so we do not give away much of good style.
|
||||
*/
|
||||
|
||||
/** This list will hold the drives known to libburn. This might be all CD
|
||||
drives of the system and thus might impose severe impact on the system.
|
||||
*/
|
||||
static struct burn_drive_info *drive_list;
|
||||
|
||||
/** If you start a long lasting operation with drive_count > 1 then you are
|
||||
not friendly to the users of other drives on those systems. Beware. */
|
||||
static unsigned int drive_count;
|
||||
|
||||
/** This variable indicates wether the drive is grabbed and must be
|
||||
finally released */
|
||||
static int drive_is_grabbed = 0;
|
||||
|
||||
|
||||
/* Some in-advance definitions to allow a more comprehensive ordering
|
||||
of the functions and their explanations in here */
|
||||
int telltoc_aquire_by_adr(char *drive_adr);
|
||||
int telltoc_aquire_by_driveno(int *drive_no, int silent);
|
||||
|
||||
|
||||
/* ------------------------------- API gestures ---------------------------- */
|
||||
|
||||
/** You need to aquire a drive before burning. The API offers this as one
|
||||
compact call and alternatively as application controllable gestures of
|
||||
whitelisting, scanning for drives and finally grabbing one of them.
|
||||
|
||||
If you have a persistent address of the drive, then the compact call is
|
||||
to prefer because it only touches one drive. On modern Linux kernels,
|
||||
there should be no fatal disturbance of ongoing burns of other libburn
|
||||
instances with any of our approaches. We use open(O_EXCL) by default.
|
||||
On /dev/hdX it should cooperate with growisofs and some cdrecord variants.
|
||||
On /dev/sgN versus /dev/scdM expect it not to respect other programs.
|
||||
*/
|
||||
int telltoc_aquire_drive(char *drive_adr, int *driveno, int silent_drive)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if(drive_adr != NULL && drive_adr[0] != 0)
|
||||
ret = telltoc_aquire_by_adr(drive_adr);
|
||||
else
|
||||
ret = telltoc_aquire_by_driveno(driveno, silent_drive);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/** If the persistent drive address is known, then this approach is much
|
||||
more un-obtrusive to the systemwide livestock of drives. Only the
|
||||
given drive device will be opened during this procedure.
|
||||
*/
|
||||
int telltoc_aquire_by_adr(char *drive_adr)
|
||||
{
|
||||
int ret;
|
||||
|
||||
fprintf(stderr,"Aquiring drive '%s' ...\n",drive_adr);
|
||||
ret = burn_drive_scan_and_grab(&drive_list,drive_adr,1);
|
||||
if (ret <= 0) {
|
||||
fprintf(stderr,"FAILURE with persistent drive address '%s'\n",
|
||||
drive_adr);
|
||||
} else {
|
||||
fprintf(stderr,"Done\n");
|
||||
drive_is_grabbed = 1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/** This method demonstrates how to use libburn without knowing a persistent
|
||||
drive address in advance. It has to make sure that after assessing the list
|
||||
of available drives, all unwanted drives get closed again. As long as they
|
||||
are open, no other libburn instance can see them. This is an intended
|
||||
locking feature. The application is responsible for giving up the locks
|
||||
by either burn_drive_release() (only after burn_drive_grab() !),
|
||||
burn_drive_info_forget(), burn_drive_info_free(), or burn_finish().
|
||||
@param driveno the index number in libburn's drive list. This will get
|
||||
set to 0 on success and will then be the drive index to
|
||||
use in the further dourse of processing.
|
||||
@param silent_drive 1=do not print "Drive found :" line with *driveno >= 0
|
||||
@return 1 success , <= 0 failure
|
||||
*/
|
||||
int telltoc_aquire_by_driveno(int *driveno, int silent_drive)
|
||||
{
|
||||
char adr[BURN_DRIVE_ADR_LEN];
|
||||
int ret, i;
|
||||
|
||||
fprintf(stderr, "Beginning to scan for devices ...\n");
|
||||
while (!burn_drive_scan(&drive_list, &drive_count))
|
||||
usleep(1002);
|
||||
if (drive_count <= 0 && *driveno >= 0) {
|
||||
fprintf(stderr, "FAILED (no drives found)\n");
|
||||
return 0;
|
||||
}
|
||||
fprintf(stderr, "Done\n");
|
||||
|
||||
for (i = 0; i < drive_count; i++) {
|
||||
if (*driveno >= 0 && (silent_drive || *driveno != i))
|
||||
continue;
|
||||
if (burn_drive_get_adr(&(drive_list[i]), adr) <=0)
|
||||
strcpy(adr, "-get_adr_failed-");
|
||||
printf("Drive found : %d --drive '%s' : ", i,adr);
|
||||
printf("%-8s %-16s (%4s)\n",
|
||||
drive_list[i].vendor,drive_list[i].product,
|
||||
drive_list[i].revision);
|
||||
}
|
||||
if (*driveno < 0) {
|
||||
fprintf(stderr,
|
||||
"Pseudo-drive \"-\" given : bus scanning done.\n");
|
||||
return 2; /* the program will end after this */
|
||||
}
|
||||
|
||||
/* We already made our choice via command line. (default is 0)
|
||||
So we just have to keep our desired drive and drop all others.
|
||||
*/
|
||||
if (drive_count <= *driveno) {
|
||||
fprintf(stderr,
|
||||
"Found only %d drives. Number %d not available.\n",
|
||||
drive_count, *driveno);
|
||||
return 0; /* the program will end after this */
|
||||
}
|
||||
|
||||
/* Drop all drives which we do not want to use */
|
||||
for (i = 0; i < drive_count; i++) {
|
||||
if (i == *driveno) /* the one drive we want to keep */
|
||||
continue;
|
||||
ret = burn_drive_info_forget(&(drive_list[i]),0);
|
||||
if (ret != 1)
|
||||
fprintf(stderr, "Cannot drop drive %d. Please report \"ret=%d\" to libburn-hackers@pykix.org\n",
|
||||
i, ret);
|
||||
else
|
||||
fprintf(stderr, "Dropped unwanted drive %d\n",i);
|
||||
}
|
||||
/* Make the one we want ready for inquiry */
|
||||
ret= burn_drive_grab(drive_list[*driveno].drive, 1);
|
||||
if (ret != 1)
|
||||
return 0;
|
||||
drive_is_grabbed = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** This gesture is necessary to get my NEC DVD_RW ND-4570A out of a state
|
||||
of noisy overexcitement after it was inquired for Next Writeable Address.
|
||||
The noise then still lasts 20 seconds. Same with cdrecord -toc, btw.
|
||||
It opens a small gap for losing the drive to another libburn instance.
|
||||
Not a problem in telltoc. This is done as very last drive operation.
|
||||
Eventually the other libburn instance will have the same sanitizing effect.
|
||||
*/
|
||||
int telltoc_regrab(struct burn_drive *drive) {
|
||||
int ret;
|
||||
|
||||
if (drive_is_grabbed)
|
||||
burn_drive_release(drive, 0);
|
||||
drive_is_grabbed = 0;
|
||||
ret = burn_drive_grab(drive, 0);
|
||||
if (ret != 0) {
|
||||
drive_is_grabbed = 1;
|
||||
}
|
||||
return !!ret;
|
||||
}
|
||||
|
||||
|
||||
int telltoc_media(struct burn_drive *drive)
|
||||
{
|
||||
int ret, media_found = 0;
|
||||
double max_speed = 0.0, min_speed = 0.0;
|
||||
enum burn_disc_status s;
|
||||
|
||||
while (burn_drive_get_status(drive, NULL) != BURN_DRIVE_IDLE)
|
||||
usleep(100001);
|
||||
while ((s = burn_disc_get_status(drive)) == BURN_DISC_UNREADY)
|
||||
usleep(100001);
|
||||
|
||||
printf("Media status : ");
|
||||
if (s==BURN_DISC_FULL) {
|
||||
printf("is written , is closed\n");
|
||||
media_found = 1;
|
||||
} else if (s==BURN_DISC_APPENDABLE) {
|
||||
printf("is written , is appendable\n");
|
||||
media_found = 1;
|
||||
} else if (s==BURN_DISC_BLANK) {
|
||||
printf("is blank\n");
|
||||
media_found = 1;
|
||||
} else if (s==BURN_DISC_EMPTY)
|
||||
printf("is not present\n");
|
||||
else
|
||||
printf("is not recognizable\n");
|
||||
|
||||
printf("Media type : ");
|
||||
if (media_found) {
|
||||
if (burn_disc_erasable(drive))
|
||||
printf("is erasable\n");
|
||||
else
|
||||
printf("is not erasable\n");
|
||||
} else
|
||||
printf("is not recognizable\n");
|
||||
|
||||
ret= burn_disc_read_atip(drive);
|
||||
if(ret>0) {
|
||||
ret= burn_drive_get_min_write_speed(drive);
|
||||
min_speed = ((double ) ret) / 176.0;
|
||||
}
|
||||
ret= burn_drive_get_write_speed(drive);
|
||||
max_speed = ((double ) ret) / 176.0;
|
||||
if (!media_found)
|
||||
printf("Drive speed : max=%.f\n", max_speed);
|
||||
else if (min_speed<=0)
|
||||
printf("Media speed : max=%.f\n", max_speed);
|
||||
else
|
||||
printf("Media speed : max=%.f , min=%.f\n",
|
||||
max_speed, min_speed);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int telltoc_toc(struct burn_drive *drive)
|
||||
{
|
||||
int num_sessions = 0 , num_tracks = 0 , lba = 0;
|
||||
int track_count = 0;
|
||||
int session_no, track_no;
|
||||
enum burn_disc_status s;
|
||||
struct burn_disc *disc= NULL;
|
||||
struct burn_session **sessions;
|
||||
struct burn_track **tracks;
|
||||
struct burn_toc_entry toc_entry;
|
||||
|
||||
while (burn_drive_get_status(drive, NULL) != BURN_DRIVE_IDLE)
|
||||
usleep(100001);
|
||||
while ((s = burn_disc_get_status(drive)) == BURN_DISC_UNREADY)
|
||||
usleep(100001);
|
||||
|
||||
disc = burn_drive_get_disc(drive);
|
||||
if (disc==NULL) {
|
||||
fprintf(stderr, "SORRY: Cannot obtain Table Of Content\n");
|
||||
return 2;
|
||||
}
|
||||
sessions = burn_disc_get_sessions(disc, &num_sessions);
|
||||
for (session_no = 0; session_no<num_sessions; session_no++) {
|
||||
tracks = burn_session_get_tracks(sessions[session_no],
|
||||
&num_tracks);
|
||||
if (tracks==NULL)
|
||||
continue;
|
||||
for(track_no= 0; track_no<num_tracks; track_no++) {
|
||||
track_count++;
|
||||
burn_track_get_entry(tracks[track_no], &toc_entry);
|
||||
lba= burn_msf_to_lba(toc_entry.pmin, toc_entry.psec,
|
||||
toc_entry.pframe);
|
||||
printf("Media content: session %2d ", session_no+1);
|
||||
printf("track %2d %s lba: %9d %2.2u:%2.2u:%2.2u\n",
|
||||
track_count,
|
||||
((toc_entry.control&7)<4?"audio":"data "),
|
||||
lba,
|
||||
toc_entry.pmin,
|
||||
toc_entry.psec,
|
||||
toc_entry.pframe);
|
||||
}
|
||||
burn_session_get_leadout_entry(sessions[session_no],
|
||||
&toc_entry);
|
||||
lba = burn_msf_to_lba(toc_entry.pmin,
|
||||
toc_entry.psec, toc_entry.pframe);
|
||||
printf("Media content: session %2d ", session_no+1);
|
||||
printf("leadout lba: %9d %2.2u:%2.2u:%2.2u\n",
|
||||
lba,
|
||||
toc_entry.pmin,
|
||||
toc_entry.psec,
|
||||
toc_entry.pframe);
|
||||
}
|
||||
if (disc!=NULL)
|
||||
burn_disc_free(disc);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int telltoc_msinfo(struct burn_drive *drive,
|
||||
int msinfo_explicit, int msinfo_alone)
|
||||
{
|
||||
int num_sessions, session_no, ret, num_tracks;
|
||||
int nwa = -123456789, lba = -123456789, aux_lba, lout_lba;
|
||||
enum burn_disc_status s;
|
||||
struct burn_disc *disc= NULL;
|
||||
struct burn_session **sessions;
|
||||
struct burn_track **tracks;
|
||||
struct burn_toc_entry toc_entry;
|
||||
struct burn_write_opts *o= NULL;
|
||||
|
||||
while (burn_drive_get_status(drive, NULL) != BURN_DRIVE_IDLE)
|
||||
usleep(100001);
|
||||
while ((s = burn_disc_get_status(drive)) == BURN_DISC_UNREADY)
|
||||
usleep(100001);
|
||||
if (s!=BURN_DISC_APPENDABLE) {
|
||||
if (!msinfo_explicit)
|
||||
return 2;
|
||||
fprintf(stderr,
|
||||
"SORRY: --msinfo can only operate on appendable media.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* man mkisofs , option -C :
|
||||
The first number is the sector number of the first sector in
|
||||
the last session of the disk that should be appended to.
|
||||
*/
|
||||
disc = burn_drive_get_disc(drive);
|
||||
if (disc==NULL) {
|
||||
fprintf(stderr,"SORRY: Cannot obtain info about CD content\n");
|
||||
return 2;
|
||||
}
|
||||
sessions = burn_disc_get_sessions(disc, &num_sessions);
|
||||
for (session_no = 0; session_no<num_sessions; session_no++) {
|
||||
tracks = burn_session_get_tracks(sessions[session_no],
|
||||
&num_tracks);
|
||||
if (tracks==NULL || num_tracks<=0)
|
||||
continue;
|
||||
burn_track_get_entry(tracks[0], &toc_entry);
|
||||
lba= burn_msf_to_lba(toc_entry.pmin, toc_entry.psec,
|
||||
toc_entry.pframe);
|
||||
}
|
||||
if(lba==-123456789) {
|
||||
fprintf(stderr,"SORRY: Cannot find any track on media\n");
|
||||
{ ret = 0; goto ex; }
|
||||
}
|
||||
/* Prepare a qualified guess as fallback for nwa inquiry */
|
||||
burn_session_get_leadout_entry(sessions[num_sessions-1], &toc_entry);
|
||||
lout_lba= burn_msf_to_lba(toc_entry.pmin,toc_entry.psec,
|
||||
toc_entry.pframe);
|
||||
|
||||
/* man mkisofs , option -C :
|
||||
The second number is the starting sector number of the new session.
|
||||
*/
|
||||
/* Set some write opts to be sent to drive. LG GSA-4082B needs it. */
|
||||
o= burn_write_opts_new(drive);
|
||||
if(o!=NULL) {
|
||||
burn_write_opts_set_perform_opc(o, 0);
|
||||
burn_write_opts_set_write_type(o,
|
||||
BURN_WRITE_TAO, BURN_BLOCK_MODE1);
|
||||
}
|
||||
/* Now try to inquire nwa from drive */
|
||||
ret= burn_disc_track_lba_nwa(drive,o,0,&aux_lba,&nwa);
|
||||
telltoc_regrab(drive); /* necessary to calm down my NEC drive */
|
||||
if(ret<=0) {
|
||||
fprintf(stderr,
|
||||
"NOTE: Guessing next writeable address from leadout\n");
|
||||
if(num_sessions>0)
|
||||
nwa= lout_lba+6900;
|
||||
else
|
||||
nwa= lout_lba+11400;
|
||||
}
|
||||
|
||||
if (!msinfo_alone)
|
||||
printf("Media msinfo : mkisofs ... -C ");
|
||||
printf("%d,%d\n",lba,nwa);
|
||||
|
||||
ret = 1;
|
||||
ex:;
|
||||
if (disc!=NULL)
|
||||
burn_disc_free(disc);
|
||||
if (o!=NULL)
|
||||
burn_write_opts_free(o);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/** The setup parameters of telltoc */
|
||||
static char drive_adr[BURN_DRIVE_ADR_LEN] = {""};
|
||||
static int driveno = 0;
|
||||
static int do_media = 0;
|
||||
static int do_toc = 0;
|
||||
static int do_msinfo = 0;
|
||||
static int print_help = 0;
|
||||
|
||||
|
||||
/** Converts command line arguments into above setup parameters.
|
||||
drive_adr[] must provide at least BURN_DRIVE_ADR_LEN bytes.
|
||||
source_adr[] must provide at least 4096 bytes.
|
||||
*/
|
||||
int telltoc_setup(int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 1; i < argc; ++i) {
|
||||
if (!strcmp(argv[i], "--drive")) {
|
||||
++i;
|
||||
if (i >= argc) {
|
||||
fprintf(stderr,"--drive requires an argument\n");
|
||||
return 1;
|
||||
} else if (strcmp(argv[i], "-") == 0) {
|
||||
drive_adr[0] = 0;
|
||||
driveno = -1;
|
||||
} else if (isdigit(argv[i][0])) {
|
||||
drive_adr[0] = 0;
|
||||
driveno = atoi(argv[i]);
|
||||
} else {
|
||||
if(strlen(argv[i]) >= BURN_DRIVE_ADR_LEN) {
|
||||
fprintf(stderr,"--drive address too long (max. %d)\n",
|
||||
BURN_DRIVE_ADR_LEN-1);
|
||||
return 2;
|
||||
}
|
||||
strcpy(drive_adr, argv[i]);
|
||||
}
|
||||
} else if (strcmp(argv[i],"--media")==0) {
|
||||
do_media = 1;
|
||||
|
||||
} else if (!strcmp(argv[i], "--msinfo")) {
|
||||
do_msinfo = 1;
|
||||
|
||||
} else if (!strcmp(argv[i], "--toc")) {
|
||||
do_toc = 1;
|
||||
|
||||
} else if (!strcmp(argv[i], "--help")) {
|
||||
print_help = 1;
|
||||
|
||||
} else {
|
||||
fprintf(stderr, "Unidentified option: %s\n", argv[i]);
|
||||
return 7;
|
||||
}
|
||||
}
|
||||
if (argc==1)
|
||||
print_help = 1;
|
||||
if (print_help) {
|
||||
printf("Usage: %s\n", argv[0]);
|
||||
printf(" [--drive <address>|<driveno>|\"-\"]\n");
|
||||
printf(" [--media] [--toc] [--msinfo]\n");
|
||||
printf("Examples\n");
|
||||
printf("A bus scan (needs rw-permissions to see a drive):\n");
|
||||
printf(" %s --drive -\n",argv[0]);
|
||||
printf("Obtain info about the type of loaded media:\n");
|
||||
printf(" %s --drive /dev/hdc --media\n",argv[0]);
|
||||
printf("Obtain table of content:\n");
|
||||
printf(" %s --drive /dev/hdc --toc\n",argv[0]);
|
||||
printf("Obtain parameters for option -C of program mkisofs:\n");
|
||||
printf(" msinfo=$(%s --drive /dev/hdc --msinfo 2>/dev/null)\n",
|
||||
argv[0]);
|
||||
printf(" mkisofs ... -C \"$msinfo\" ...\n");
|
||||
printf("Obtain what is available about drive 0 and its media\n");
|
||||
printf(" %s --drive 0\n",argv[0]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int ret, toc_failed = 0, msinfo_alone = 0, msinfo_explicit = 0;
|
||||
int full_default = 0;
|
||||
|
||||
ret = telltoc_setup(argc, argv);
|
||||
if (ret)
|
||||
exit(ret);
|
||||
|
||||
/* Behavior shall be different if --msinfo is only option */
|
||||
if (do_msinfo) {
|
||||
msinfo_explicit = 1;
|
||||
if (!(do_media || do_toc))
|
||||
msinfo_alone = 1;
|
||||
}
|
||||
/* Default option is to do everything if possible */
|
||||
if (do_media==0 && do_msinfo==0 && do_toc==0 && driveno!=-1) {
|
||||
if(print_help)
|
||||
exit(0);
|
||||
full_default = do_media = do_msinfo = do_toc = 1;
|
||||
}
|
||||
|
||||
fprintf(stderr, "Initializing libburn.pykix.org ...\n");
|
||||
if (burn_initialize())
|
||||
fprintf(stderr, "Done\n");
|
||||
else {
|
||||
fprintf(stderr,"\nFATAL: Failed to initialize.\n");
|
||||
exit(33);
|
||||
}
|
||||
|
||||
/* Print messages of severity SORRY or more directly to stderr */
|
||||
burn_msgs_set_severities("NEVER", "SORRY", "telltoc : ");
|
||||
|
||||
/** Note: driveno might change its value in this call */
|
||||
ret = telltoc_aquire_drive(drive_adr, &driveno, !full_default);
|
||||
if (ret<=0) {
|
||||
fprintf(stderr,"\nFATAL: Failed to aquire drive.\n");
|
||||
{ ret = 34; goto finish_libburn; }
|
||||
}
|
||||
if (ret == 2)
|
||||
{ ret = 0; goto release_drive; }
|
||||
|
||||
if (do_media) {
|
||||
ret = telltoc_media(drive_list[driveno].drive);
|
||||
if (ret<=0)
|
||||
{ret = 36; goto release_drive; }
|
||||
}
|
||||
if (do_toc) {
|
||||
ret = telltoc_toc(drive_list[driveno].drive);
|
||||
if (ret<=0)
|
||||
{ret = 37; goto release_drive; }
|
||||
if (ret==2)
|
||||
toc_failed = 1;
|
||||
}
|
||||
if (do_msinfo) {
|
||||
ret = telltoc_msinfo(drive_list[driveno].drive,
|
||||
msinfo_explicit, msinfo_alone);
|
||||
if (ret<=0)
|
||||
{ret = 38; goto release_drive; }
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
if (toc_failed)
|
||||
ret = 37;
|
||||
release_drive:;
|
||||
if (drive_is_grabbed)
|
||||
burn_drive_release(drive_list[driveno].drive, 0);
|
||||
|
||||
finish_libburn:;
|
||||
/* This app does not bother to know about exact scan state.
|
||||
Better to accept a memory leak here. We are done anyway. */
|
||||
/* burn_drive_info_free(drive_list); */
|
||||
|
||||
burn_finish();
|
||||
exit(ret);
|
||||
}
|
||||
|
||||
/* License and copyright aspects:
|
||||
See libburner.c
|
||||
*/
|
||||
|
103
test/toc.c
103
test/toc.c
@ -1,103 +0,0 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
|
||||
#include <libburn/libburn.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
static struct burn_drive_info *drives;
|
||||
static unsigned int n_drives;
|
||||
|
||||
static void show_tocs()
|
||||
{
|
||||
struct burn_session **sessions;
|
||||
struct burn_track **tracks;
|
||||
struct burn_disc *disc;
|
||||
int nses, ntracks, hidefirst;
|
||||
unsigned int i, j, k;
|
||||
struct burn_toc_entry e;
|
||||
enum burn_disc_status s;
|
||||
|
||||
for (i = 0; i < n_drives; ++i) {
|
||||
fprintf(stderr, "TOC for disc in %s - %s:\n",
|
||||
drives[i].vendor, drives[i].product);
|
||||
|
||||
if (!burn_drive_grab(drives[i].drive, 1)) {
|
||||
fprintf(stderr, "Unable to open the drive!\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
while (burn_drive_get_status(drives[i].drive, NULL))
|
||||
usleep(1000);
|
||||
|
||||
while ((s = burn_disc_get_status(drives[i].drive))
|
||||
== BURN_DISC_UNREADY)
|
||||
usleep(1000);
|
||||
if (s != BURN_DISC_FULL) {
|
||||
burn_drive_release(drives[i].drive, 0);
|
||||
fprintf(stderr, "No disc found!\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
disc = burn_drive_get_disc(drives[i].drive);
|
||||
|
||||
sessions = burn_disc_get_sessions(disc, &nses);
|
||||
for (k = 0; k < nses; ++k) {
|
||||
tracks = burn_session_get_tracks(sessions[k],
|
||||
&ntracks);
|
||||
hidefirst = burn_session_get_hidefirst(sessions[k]);
|
||||
if (hidefirst)
|
||||
fprintf(stderr,
|
||||
"track: GAP (%2d) lba: %9d (%9d) %02d:%02d:%02d adr: X control: X mode: %d\n",
|
||||
k + 1, 0, 0, 0, 2, 0,
|
||||
burn_track_get_mode(tracks[0]));
|
||||
|
||||
for (j = !!hidefirst; j < ntracks; ++j) {
|
||||
burn_track_get_entry(tracks[j], &e);
|
||||
fprintf(stderr,
|
||||
"track: %3d (%2d) lba: %9d (%9d) %02d:%02d:%02d "
|
||||
"adr: %d control: %d mode: %d\n",
|
||||
e.point, e.session,
|
||||
burn_msf_to_lba(e.pmin, e.psec,
|
||||
e.pframe),
|
||||
burn_msf_to_lba(e.pmin, e.psec,
|
||||
e.pframe) * 4,
|
||||
e.pmin, e.psec, e.pframe, e.adr,
|
||||
e.control,
|
||||
burn_track_get_mode(tracks[j]));
|
||||
}
|
||||
burn_session_get_leadout_entry(sessions[k], &e);
|
||||
fprintf(stderr,
|
||||
"track:lout (%2d) lba: %9d (%9d) %02d:%02d:%02d "
|
||||
"adr: %d control: %d mode: %d\n",
|
||||
k + 1, burn_msf_to_lba(e.pmin, e.psec,
|
||||
e.pframe),
|
||||
burn_msf_to_lba(e.pmin, e.psec,
|
||||
e.pframe) * 4, e.pmin,
|
||||
e.psec, e.pframe, e.adr, e.control, -1);
|
||||
}
|
||||
burn_disc_free(disc);
|
||||
burn_drive_release(drives[i].drive, 0);
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
fprintf(stderr, "Initializing library...");
|
||||
if (burn_initialize())
|
||||
fprintf(stderr, "Success\n");
|
||||
else {
|
||||
printf("Failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
fprintf(stderr, "Scanning for devices...");
|
||||
while (!burn_drive_scan(&drives, &n_drives)) ;
|
||||
fprintf(stderr, "Done\n");
|
||||
|
||||
show_tocs();
|
||||
burn_drive_info_free(drives);
|
||||
burn_finish();
|
||||
return 0;
|
||||
}
|
77
test/tree.py
77
test/tree.py
@ -1,77 +0,0 @@
|
||||
# a module to help with handling of filenames, directory trees, etc.
|
||||
|
||||
import os
|
||||
import os.path
|
||||
import stat
|
||||
|
||||
def pathsubtract(a, b):
|
||||
index = a.find(b)
|
||||
if index == -1:
|
||||
return None
|
||||
res = a[ (index + len(b)): ]
|
||||
|
||||
if res.find("/") == 0:
|
||||
res = res[1:]
|
||||
return res
|
||||
|
||||
# same as C strcmp()
|
||||
def strcmp(a, b):
|
||||
if a < b:
|
||||
return -1
|
||||
if a > b:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
class TreeNode:
|
||||
|
||||
# path is the location of the file/directory. It is either a full path or
|
||||
# a path relative to $PWD
|
||||
def __init__(self, parent, path=".", root=".", isofile=None):
|
||||
if isofile:
|
||||
self.root = os.path.abspath(isofile)
|
||||
self.path = ""
|
||||
else:
|
||||
fullpath = os.path.abspath( path )
|
||||
fullroot = os.path.abspath( root )
|
||||
self.root = fullroot
|
||||
self.path = pathsubtract( fullpath, fullroot )
|
||||
self.parent = parent
|
||||
self.children = []
|
||||
|
||||
if self.path == None:
|
||||
raise NameError, "Invalid paths %s and %s" % (fullpath, fullroot)
|
||||
|
||||
# if this is a directory, add its children recursively
|
||||
def addchildren(self):
|
||||
if not stat.S_ISDIR( os.lstat(self.root + "/" + self.path).st_mode ):
|
||||
return
|
||||
|
||||
children = os.listdir( self.root + "/" + self.path )
|
||||
for child in children:
|
||||
if self.path:
|
||||
child = self.path + "/" + child
|
||||
self.children.append( TreeNode(self, child, self.root) )
|
||||
for child in self.children:
|
||||
child.addchildren()
|
||||
|
||||
def printAll(self, spaces=0):
|
||||
print " "*spaces + self.root + "/" + self.path
|
||||
for child in self.children:
|
||||
child.printAll(spaces + 2)
|
||||
|
||||
def isValidISO1(self):
|
||||
pass
|
||||
|
||||
class Tree:
|
||||
def __init__(self, root=None, isofile=None):
|
||||
if isofile:
|
||||
self.root = TreeNode(parent=None, isofile=isofile)
|
||||
else:
|
||||
self.root = TreeNode(parent=None, path=root, root=root)
|
||||
self.root.addchildren()
|
||||
|
||||
def isValidISO1(self):
|
||||
return root.isValidISO1();
|
||||
|
||||
#t = Tree(root=".")
|
||||
#t.root.printAll()
|
BIN
test/tree.pyc
BIN
test/tree.pyc
Binary file not shown.
Reference in New Issue
Block a user