Compare commits
23 Commits
ZeroTwoSix
...
ZeroTwoTwo
Author | SHA1 | Date | |
---|---|---|---|
9785038c3f | |||
6f52cc391b | |||
76fc21ec73 | |||
6d2d764fc7 | |||
3b5cc599e6 | |||
086154e9e2 | |||
4b9c45e86d | |||
a0b5f33654 | |||
13b562a273 | |||
26c64e5a45 | |||
e18b16149c | |||
1d006990d1 | |||
e2fa483126 | |||
a52eb43dc6 | |||
101c06d4b4 | |||
037fdfb8bf | |||
640cdaff1f | |||
a25c13ec62 | |||
810f13db1e | |||
5f1f2187f2 | |||
6f9c08e089 | |||
3eedf0327b | |||
bf4545e803 |
85
Makefile.am
85
Makefile.am
@ -1,20 +1,17 @@
|
||||
pkgconfigdir=$(libdir)/pkgconfig
|
||||
libincludedir=$(includedir)/libburn
|
||||
|
||||
lib_LTLIBRARIES = libburn/libburn.la
|
||||
lib_LTLIBRARIES = libburn/libburn.la libisofs/libisofs.la
|
||||
|
||||
## ========================================================================= ##
|
||||
|
||||
# Build libraries
|
||||
libburn_libburn_la_LDFLAGS = \
|
||||
-version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE)
|
||||
libburn_libburn_la_LIBADD = $(LIBBURN_ARCH_LIBS) $(THREAD_LIBS)
|
||||
libburn_libburn_la_SOURCES = \
|
||||
libburn/async.c \
|
||||
libburn/async.h \
|
||||
libburn/back_hacks.h \
|
||||
libburn/cleanup.c \
|
||||
libburn/cleanup.h \
|
||||
libburn/crc.c \
|
||||
libburn/crc.h \
|
||||
libburn/debug.c \
|
||||
@ -29,17 +26,14 @@ libburn_libburn_la_SOURCES = \
|
||||
libburn/lec.c \
|
||||
libburn/lec.h \
|
||||
libburn/libburn.h \
|
||||
libburn/libdax_audioxtr.h \
|
||||
libburn/libdax_audioxtr.c \
|
||||
libburn/libdax_msgs.h \
|
||||
libburn/libdax_msgs.c \
|
||||
libburn/message.c \
|
||||
libburn/message.h \
|
||||
libburn/mmc.c \
|
||||
libburn/mmc.h \
|
||||
libburn/null.c \
|
||||
libburn/null.h \
|
||||
libburn/options.c \
|
||||
libburn/options.h \
|
||||
libburn/os.h \
|
||||
libburn/read.c \
|
||||
libburn/read.h \
|
||||
libburn/sbc.c \
|
||||
@ -63,20 +57,42 @@ libburn_libburn_la_SOURCES = \
|
||||
libburn/write.h \
|
||||
version.h
|
||||
|
||||
## 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
|
||||
libburn/libburn.h \
|
||||
libisofs/libisofs.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 = \
|
||||
@ -85,27 +101,24 @@ 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
|
||||
test_fake_au_CPPFLAGS =
|
||||
test_fake_au_LDADD =
|
||||
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_6
|
||||
cdrskin_cdrskin_CFLAGS = -DCdrskin_libburn_0_2_2
|
||||
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"
|
||||
##
|
||||
@ -145,9 +158,13 @@ uninstall-local:
|
||||
|
||||
# Indent source files
|
||||
indent_files = \
|
||||
$(libisofs_libisofs_la_SOURCES) \
|
||||
$(libburn_libburn_la_SOURCES) \
|
||||
$(test_libburner_SOURCES) \
|
||||
$(test_poll_SOURCES) \
|
||||
$(test_structest_SOURCES)
|
||||
$(test_toc_SOURCES) \
|
||||
$(test_structest_SOURCES) \
|
||||
$(test_iso_SOURCES)
|
||||
|
||||
|
||||
indent: $(indent_files)
|
||||
@ -163,13 +180,12 @@ indent: $(indent_files)
|
||||
|
||||
# Extra things
|
||||
nodist_pkgconfig_DATA = \
|
||||
libburn-1.pc
|
||||
|
||||
# http://www.nada.kth.se/cgi-bin/info?(automake.info)Man%20pages
|
||||
man_MANS = cdrskin/cdrskin.1
|
||||
libburn-1.pc \
|
||||
libisofs-1.pc
|
||||
|
||||
EXTRA_DIST = \
|
||||
libburn-1.pc.in \
|
||||
libisofs-1.pc.in \
|
||||
version.h.in \
|
||||
doc/comments \
|
||||
doc/doxygen.conf.in \
|
||||
@ -183,12 +199,5 @@ EXTRA_DIST = \
|
||||
cdrskin/changelog.txt \
|
||||
cdrskin/cdrskin_eng.html \
|
||||
cdrskin/wiki_plain.txt \
|
||||
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)
|
||||
COPYING
|
||||
|
||||
|
43
README
43
README
@ -24,47 +24,34 @@ This libburn.pykix.org toplevel README (C) 2006 Thomas Schmitt
|
||||
|
||||
Build and Installation
|
||||
|
||||
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/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
|
||||
autotools installed.
|
||||
Our build system is based on autotools.
|
||||
User experience tells us that you will need at least autotools version 1.7.
|
||||
|
||||
To build libburn.pykix.org and its subprojects it should be sufficient to go
|
||||
into its toplevel directory (here: "libburn_pykix") and execute
|
||||
into its toplevel directory and execute
|
||||
./bootstrap (needed if you downloaded from SVN and not a release tarball)
|
||||
./configure
|
||||
make
|
||||
|
||||
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 software project for reading, mastering and
|
||||
writing optical discs. For now this means only CD-R and CD-RW.
|
||||
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.
|
||||
|
||||
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 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.
|
||||
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.
|
||||
|
||||
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
|
||||
@ -159,16 +146,6 @@ 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
|
||||
|
25
acinclude.m4
25
acinclude.m4
@ -1,3 +1,18 @@
|
||||
dnl Function to link an architecture specific file
|
||||
dnl LINK_ARCH_SRC(source_dir, arch, source_file)
|
||||
AC_DEFUN([COPY_ARCH_SRC],
|
||||
[
|
||||
echo "copying $1/$2/$3 -> $1/$3"
|
||||
old="$srcdir/$1/$2/$3"
|
||||
new="$srcdir/$1/$3"
|
||||
cat >$new <<__EOF__
|
||||
/* WARNING: This file was automatically generated!
|
||||
* Original: $old
|
||||
*/
|
||||
__EOF__
|
||||
cat >>$new <$old
|
||||
])
|
||||
|
||||
AC_DEFUN([TARGET_SHIZZLE],
|
||||
[
|
||||
ARCH=""
|
||||
@ -7,11 +22,6 @@ AC_DEFUN([TARGET_SHIZZLE],
|
||||
case $target in
|
||||
*-*-linux*)
|
||||
ARCH=linux
|
||||
LIBBURN_ARCH_LIBS=
|
||||
;;
|
||||
*-*-freebsd*)
|
||||
ARCH=freebsd
|
||||
LIBBURN_ARCH_LIBS=-lcam
|
||||
;;
|
||||
*)
|
||||
AC_ERROR([You are attempting to compile for an unsupported platform])
|
||||
@ -19,4 +29,9 @@ AC_DEFUN([TARGET_SHIZZLE],
|
||||
esac
|
||||
|
||||
AC_MSG_RESULT([$ARCH])
|
||||
|
||||
# this doesn't actually do anything yet.. but it will someday when we port
|
||||
# libburn
|
||||
|
||||
#COPY_ARCH_SRC(libburn, $ARCH, transport.c)
|
||||
])
|
||||
|
@ -3,8 +3,4 @@
|
||||
aclocal
|
||||
libtoolize --copy --force
|
||||
autoconf
|
||||
|
||||
# ts A61101 : libburn is not prepared for config.h
|
||||
# autoheader
|
||||
|
||||
automake --foreign --add-missing --copy --include-deps
|
||||
|
185
cdrskin/README
185
cdrskin/README
@ -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.6.pl01.tar.gz
|
||||
http://scdbackup.sourceforge.net/cdrskin-0.2.2.tar.gz
|
||||
Copyright (C) 2006 Thomas Schmitt
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
On top of libburn there is implemented cdrskin 0.2.6, a limited cdrecord
|
||||
On top of libburn there is implemented cdrskin 0.2.2, 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
|
||||
@ -46,10 +46,9 @@ protection against possible damages. You use this on your own risk.
|
||||
Don't blame me or other authors of libburn if anything goes wrong.
|
||||
|
||||
I used it on my own risk with :
|
||||
SuSE 7.2, kernel 2.4.4, ide-scsi emulation, LITE-ON LTR48125S CD burner, 2002
|
||||
SuSE 9.0, kernel 2.4.21, ide-scsi emulation, LG GSA-4082B CD/DVD burner, 2004
|
||||
NEC ND-4570A CD/DVD burner, 2006
|
||||
RIP-14.4, kernel 2.6.14, no ide-scsi, with all above burners
|
||||
SuSE 7.2, kernel 2.4.4, ide-scsi emulation, LITE-ON LTR48125S CD burner
|
||||
SuSE 9.0, kernel 2.4.21, ide-scsi emulation, LG GSA-4082B CD/DVD burner
|
||||
RIP-14.4, kernel 2.6.14, no ide-scsi, with both above burners
|
||||
|
||||
It fails to compile or run on SuSE 6.4 (kernel 2.2.14).
|
||||
It does not find the IDE CD burner on SuSE 7.2 without ide-scsi.
|
||||
@ -59,16 +58,16 @@ systems, including 64 bit systems. (Further reports are welcome.)
|
||||
|
||||
Compilation, First Glimpse, Installation
|
||||
|
||||
Obtain cdrskin-0.2.6.pl02.tar.gz, take it to a directory of your choice and do:
|
||||
Obtain cdrskin-0.2.2.tar.gz , take it to a directory of your choice and do:
|
||||
|
||||
tar xzf cdrskin-0.2.6.pl02.tar.gz
|
||||
cd cdrskin-0.2.6
|
||||
tar xzf cdrskin-0.2.2.tar.gz
|
||||
cd cdrskin-0.2.2
|
||||
|
||||
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.6 or libburn then execute:
|
||||
Within that toplevel directory of either cdrskin-0.2.2 or libburn then execute:
|
||||
|
||||
./configure
|
||||
make
|
||||
@ -86,11 +85,10 @@ In order to surely get a standalone binary, execute
|
||||
|
||||
cdrskin/compile_cdrskin.sh
|
||||
|
||||
Version identification and help texts available afterwards:
|
||||
Version identification an 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"
|
||||
@ -109,10 +107,6 @@ 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
|
||||
|
||||
@ -130,13 +124,17 @@ On Linux 2.4 this would be some /dev/sgN and on 2.6. some /dev/hdX.
|
||||
|
||||
The output of cdrskin --devices might look like
|
||||
|
||||
0 dev='/dev/sg0' rwrwr- : '_NEC' 'DVD_RW ND-4570A'
|
||||
1 dev='/dev/sg1' rwrw-- : 'HL-DT-ST' 'DVDRAM GSA-4082B'
|
||||
0 dev='/dev/sg0' rwrwr- : 'TEAC' 'CD-ROM CD-532S'
|
||||
1 dev='/dev/sg1' rwrw-- : 'LITE-ON' 'LTR-48125S'
|
||||
|
||||
So full and insecure enabling of both for everybody would look like
|
||||
|
||||
chmod a+rw /dev/sg0 /dev/sg1
|
||||
|
||||
(The CD-ROM is in these examples only for demonstrating the presence of another
|
||||
SCSI device. This /dev/sg0 may be left as it is and stay invisible for normal
|
||||
users.)
|
||||
|
||||
I strongly discourage to run cdrskin with setuid root or via sudo !
|
||||
It is not checked for the necessary degree of hacker safety.
|
||||
|
||||
@ -145,57 +143,43 @@ It is not checked for the necessary degree of hacker safety.
|
||||
|
||||
Get an overview of cdrecord style addresses of available devices
|
||||
cdrskin -scanbus
|
||||
cdrskin dev=ATA -scanbus
|
||||
|
||||
Note: Adresses reported with dev=ATA are to be used with prefix "ATA:". You may
|
||||
well use device file addresses as reported with --devices. Examples:
|
||||
dev=0,1,0 dev=/dev/sg1 dev=ATA:1,0,0 dev=/dev/hdc
|
||||
See also "Drive Addressing".
|
||||
Note: Address numbers have changed since cdrskin-0.2.2 in order to become
|
||||
compatible with cdrecord numbers. To get the old number scheme, use
|
||||
option --old_pseudo_scsi_adr . See also "Pseudo-SCSI Adresses".
|
||||
Sorry for any inconvenience.
|
||||
|
||||
|
||||
Obtain some info about the drive
|
||||
cdrskin dev=0,1,0 -checkdrive
|
||||
cdrskin dev=1,1,0 -checkdrive
|
||||
|
||||
Obtain some info about the drive and the inserted media
|
||||
cdrskin dev=0,1,0 -atip
|
||||
cdrskin dev=1,1,0 -atip
|
||||
|
||||
Thoroughly blank a CD-RW
|
||||
cdrskin -v dev=0,1,0 blank=all -eject
|
||||
cdrskin -v dev=1,1,0 blank=all -eject
|
||||
|
||||
Blank CD-RW sufficiently for making it ready for overwrite
|
||||
cdrskin -v dev=0,1,0 blank=fast -eject
|
||||
cdrskin -v dev=1,1,0 blank=fast -eject
|
||||
|
||||
Burn image file my_image.iso to CD
|
||||
cdrskin -v dev=0,1,0 speed=12 fs=8m -sao driveropts=burnfree padsize=300k \
|
||||
cdrskin -v dev=1,1,0 speed=12 fs=8m -sao driveropts=burnfree padsize=300k \
|
||||
-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 -tao \
|
||||
driveropts=burnfree padsize=300k -
|
||||
find . | afio -oZ - | cdrskin -v dev=1,1,0 fs=32m speed=8 -sao \
|
||||
driveropts=burnfree padsize=300k tsize=650m -
|
||||
|
||||
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.
|
||||
See below "Audio CD" for specifications.
|
||||
Burn 5 audio tracks to CD
|
||||
ogg123 -d raw -f track01.cd /path/to/track1.ogg
|
||||
oggdec -R -o track02.cd /path/to/track2.ogg
|
||||
lame --decode -t /path/to/track3.mp3 track03.cd
|
||||
madplay -o raw:track04.cd /path/to/track4.mp3
|
||||
mppdec --raw-le /path/to/track5.mpc track05.cd
|
||||
cdrskin dev=/dev/hdc blank=fast fs=0 -eject -audio track0[1-5].cd
|
||||
|
||||
cdrskin -v dev=0,1,0 blank=fast -eject speed=48 -sao \
|
||||
-audio -swab track0[1-5].cd /path/to/track6.wav
|
||||
|
||||
|
||||
Usage example with http://scdbackup.sourceforge.net
|
||||
|
||||
Address may be a cdrecord-style "scsibus,target,lun" as listed with
|
||||
cdrskin -scanbus (and hopefully as listed with cdrecord -scanbus) :
|
||||
cdrskin -scanbus (but not as listed with cdrecord -scanbus) :
|
||||
|
||||
export SCDBACKUP_SCSI_ADR="0,1,0"
|
||||
export SCDBACKUP_SCSI_ADR="1,1,0"
|
||||
|
||||
or a device file address as listed by --devices with an accessible drive :
|
||||
|
||||
@ -203,7 +187,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"
|
||||
export SCDBACKUP_CDRECORD="cdrskin -v -v tao_to_sao_tsize=650m"
|
||||
|
||||
Run a backup :
|
||||
|
||||
@ -212,11 +196,17 @@ Run a backup :
|
||||
|
||||
Restrictions
|
||||
|
||||
The major restrictions are lifted now: audio, TAO, multi-session do work.
|
||||
Many cdrecord options are still unsupported, though.
|
||||
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.
|
||||
|
||||
If you have use cases for them, please report your wishes and expectations.
|
||||
Audio features are incomplete in respect to cdrecord. Well prepaired track
|
||||
files should get burned flawlessly, thanks to Lorenzo Taylor.
|
||||
Builtin extraction of raw audio data from filetypes .au and .wav is not
|
||||
implemented yet. See chapter "Audio CD" for details.
|
||||
|
||||
No multi session yet ... Please report your wishes.
|
||||
|
||||
|
||||
Inspiration and Standard
|
||||
@ -236,45 +226,12 @@ scdbackup which still runs a bit better with cdrecord than with cdrskin. TAO.
|
||||
I have the hope that Joerg feels more flattered than annoyed by cdrskin.
|
||||
|
||||
|
||||
Drive Addressing
|
||||
|
||||
Drives get addressed either via their cdrecord-style addresses as listed
|
||||
with option -scanbus (see below "Pseudo-SCSI Adresses") or via the paths
|
||||
of device files.
|
||||
Not only device files listed by --devices may be used but also device files
|
||||
which via their major,minor numbers point to the same device driver as
|
||||
a listed device file.
|
||||
|
||||
Helpful with Linux kernel 2.4 is a special SCSI feature:
|
||||
It is possible to address a scsi(-emulated) drive via associated device files
|
||||
which are not listed by option --devices but point to the same SCSI addresses
|
||||
as listed device files. This addressing via e.g. /dev/sr0 or /dev/scd1 is
|
||||
compatible with generic read programs like dd and with write program growisofs.
|
||||
|
||||
Pseudo-SCSI Adresses
|
||||
|
||||
cdrecord and cdrskin share the syntax of SCSI addresses but not necessarily
|
||||
the meaning of the components. A cdrecord-style address for cdrskin
|
||||
[prefix:]scsibus,target,lun
|
||||
can be interpreted in two different modes.
|
||||
|
||||
Standard mode tries to be compatible to original cdrecord. This should be true
|
||||
with (emulated) SCSI where the /dev/sgN with is looked up with matching
|
||||
scsibus,target,lun as given by the operating system.
|
||||
With dev=ATA: or dev=ATAPI: the translation to /dev/hdX is purely literal
|
||||
but matches the cdrecord addresses on all systems tested so far:
|
||||
X = 'a' + 2 * scsibus + target
|
||||
where target only may have the values 0 or 1.
|
||||
|
||||
In this mode, option -scanbus will list only SCSI devices unless option
|
||||
dev=ATA or dev=ATAPI are given, which will suppress SCSI devices and only
|
||||
show IDE drives (i.e. /dev/hdX without ide-scsi emulation).
|
||||
|
||||
|
||||
In mode --old_pseudo_scsi_adr there is a scsibus,target,lun representation
|
||||
which has nothing to do with SCSI and thus is not compatible to cdrecord.
|
||||
Each number triple corresponds either to a device file address or to a
|
||||
libburn drive number.
|
||||
cdrecord and cdrskin share only some syntax of addresses but not the meaning
|
||||
of the components. A cdrecord-style address for cdrskin
|
||||
[[prefix:]scsibus,]target,lun
|
||||
corresponds either to a device file address or to a libburn drive number.
|
||||
Component "scsibus" indicates the translation method. Defined busses are:
|
||||
0 target is the libburn drivenumber as listed with --devices
|
||||
1 associated to device file /dev/sgN , target chooses N
|
||||
@ -289,24 +246,17 @@ drive accessability.
|
||||
Further busses may emerge as libburn evolves. "prefix" and "lun" may get
|
||||
a meaning. To stay upward compatible, use addresses as printed by -scanbus.
|
||||
|
||||
User Defined Device Address Translation
|
||||
|
||||
Some programs or users have their own ideas about the address of their burner.
|
||||
K3b 0.10 for example derives cdrecord addresses by own examination of the
|
||||
devices and not by calling cdrecord -scanbus.
|
||||
Standard mode will hopefully be fully compatible with their ideas.
|
||||
|
||||
Old frontends which do not know dev=ATA or dev=ATAPI and which do ask their
|
||||
"cdrecord" via -scanbus may be well served with option --old_pseudo_scsi_adr .
|
||||
|
||||
To direct any remaining stubborn callers to the appropriate drives, cdrskin
|
||||
allows to define device address aliases. Like
|
||||
To direct such callers to the appropriate drives, cdrskin allows to define
|
||||
device address aliases. Like
|
||||
cdrskin dev_translation=+1,0,0+/dev/sg1 \
|
||||
dev_translation=+ATA:1,0,0+/dev/sg1 \
|
||||
dev_translation=-"cd+dvd"-0,1,0 \
|
||||
dev_translation=-"cd+dvd"-1,1,0 \
|
||||
...
|
||||
Any of the addresses dev=1,0,0, dev=ATA:1,0,0, dev=cd+dvd will be mapped to
|
||||
/dev/sg1 resp. to 0,1,0.
|
||||
/dev/sg1 resp. to its standard alias 1,1,0.
|
||||
The first character after "dev_translation=" defines the character which
|
||||
separates the two parts of the translation pair. (Above: "+" and "-".)
|
||||
|
||||
@ -328,46 +278,35 @@ 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/default/cdrskin
|
||||
/etc/defaults/cdrskin
|
||||
/etc/opt/cdrskin/rc
|
||||
$HOME/.cdrskinrc
|
||||
The files are read in the sequence given above.
|
||||
Each readable line is treated as one single argument. No extra blanks.
|
||||
A first character '#' marks a comment, empty lines are ignored.
|
||||
Each readable line is treated as one single argument. No extra blanks,
|
||||
no comments, no empty lines are permitted.
|
||||
|
||||
Example content of a startup file:
|
||||
# This is the default device
|
||||
dev=0,1,0
|
||||
# To accomodate to eventual remnant cdrskin-0.2.2 addresses
|
||||
dev_translation=+1,0,0+0,1,0
|
||||
|
||||
# Some more options
|
||||
dev=1,1,0
|
||||
dev_translation=+1,0,0+1,1,0
|
||||
--fifo_start_empty
|
||||
fs=16m
|
||||
|
||||
|
||||
Audio CD
|
||||
|
||||
Lorenzo Taylor enabled option -audio in cdrskin (thanks !) and reports neat
|
||||
results with audio data files which are :
|
||||
headerless PCM (i.e. uncompressed)
|
||||
44100 Hz sampling rate
|
||||
16 bits per sample
|
||||
stereo (2 channels)
|
||||
little-endian byte order with option -swab, or big-endian without -swab
|
||||
|
||||
Files with name extension .wav get examined wether they are in Microsoft WAVE
|
||||
format with above parameters and eventually get extracted by cdrskin itself.
|
||||
In the same way files with name extension .au get examined wether they are
|
||||
in SUN's audio format. For both formats, track format -audio and eventual
|
||||
endianness option -swab are enabled automatically.
|
||||
|
||||
Any other formats are to be converted to format .wav with above parameters
|
||||
or to be extracted as raw CD track data by commands like those given above
|
||||
under "Usage examples". Those raw files need option -audio and in most cases
|
||||
option -swab to mark them as little-endian/Intel/LSB-first 16-bit data.
|
||||
Incorrect endianness setting results in random noise on CD.
|
||||
Builtin extraction of raw audio data from filetypes .au and .wav is not
|
||||
implemented yet. Lorenzo Taylor enabled option -audio in cdrskin (thanks !)
|
||||
and reports neat results with audio data files which are :
|
||||
headerless
|
||||
44100Hz
|
||||
16bit, stereo (or 4-channel if the 4-channel bit is set),
|
||||
little-endian byte order
|
||||
He proposes to extract them from usual audio formats by commands like
|
||||
given above under "Usage examples".
|
||||
|
||||
The existence of cdrecord-builtin .wav extraction seems to have
|
||||
hampered the development of a standalone stripping tool. If you know
|
||||
a command line that would do the trick, contact me or libburn.pykix.org .
|
||||
I myself am not into audio. So libburn-hackers@pykix.org might be the
|
||||
best address for suggestions, requests and bug reports.
|
||||
|
||||
|
@ -20,23 +20,18 @@ set -x
|
||||
# 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/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"
|
||||
# Historic moments:
|
||||
# libburn version used: http://libburn.pykix.org SVN of Aug 15 2006
|
||||
# packed up in a tarball just to save it from inadverted changes.
|
||||
# original="./libburn_svn_A60815.tgz"
|
||||
# original="./libburn_cdrskin_A60819.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
|
||||
# My changes are in libburn-0.2.1.ts.develop , mainly in ./cdrskin
|
||||
|
||||
changes="./libburn-0.2.3.ts.develop"
|
||||
skin_rev="0.2.7"
|
||||
changes="./libburn-0.2.1.ts.develop"
|
||||
skin_rev="0.1.5"
|
||||
|
||||
# The result directory and the name of the result tarballs
|
||||
target="./cdrskin-${skin_rev}"
|
||||
@ -51,7 +46,9 @@ compile_cmd="./cdrskin/compile_cdrskin.sh"
|
||||
compile_static_opts="-static"
|
||||
compile_result="cdrskin/cdrskin"
|
||||
|
||||
bintarget_dynamic="cdrskin_${skin_rev}-x86-suse9_0"
|
||||
|
||||
# addresses relative to compile_dir :
|
||||
bintarget_dynamic="../cdrskin_${skin_rev}-x86-suse9_0"
|
||||
bintarget_static="$bintarget_dynamic"-static
|
||||
|
||||
if test -d "$changes"
|
||||
@ -108,24 +105,9 @@ 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
|
||||
@ -141,14 +123,15 @@ do
|
||||
fi
|
||||
done
|
||||
|
||||
## No more : Add own libburn-README in toplevel
|
||||
# cp -a "$changes"/README "$target"
|
||||
# For now: Add own libburn-README in toplevel
|
||||
cp -a "$changes"/README "$target"
|
||||
|
||||
## No more : Add modified Makefile.am
|
||||
# cp -a "$changes"/Makefile.am "$target"
|
||||
# Add modified Makefile.am
|
||||
cp -a "$changes"/Makefile.am "$target"
|
||||
|
||||
|
||||
# Make SVN state tarball for the libburn team
|
||||
# TODO: will probably be obsoleted after sucessful merge
|
||||
tar czf "$cdrskin_tarball_svn" "$target"
|
||||
|
||||
|
||||
@ -158,20 +141,6 @@ tar czf "$cdrskin_tarball_svn" "$target"
|
||||
#
|
||||
( 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"
|
||||
@ -182,22 +151,15 @@ tar czf "$cdrskin_tarball" "$target"
|
||||
./configure
|
||||
make
|
||||
$compile_cmd -do_strip
|
||||
cp "$compile_result" "../$bintarget_dynamic"
|
||||
cp "$compile_result" "$bintarget_dynamic"
|
||||
if test -n "$compile_static_opts"
|
||||
then
|
||||
$compile_cmd $compile_static_opts -do_strip
|
||||
cp "$compile_result" "../$bintarget_static"
|
||||
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"
|
||||
|
@ -1,206 +0,0 @@
|
||||
#!/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"
|
||||
|
@ -48,9 +48,6 @@ 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;
|
||||
@ -75,27 +72,17 @@ 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;
|
||||
@ -130,8 +117,6 @@ 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;
|
||||
@ -155,7 +140,6 @@ 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;
|
||||
@ -240,26 +224,6 @@ 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;
|
||||
@ -280,7 +244,6 @@ 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)
|
||||
@ -290,7 +253,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(o->follow_up_fd_counter);
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
@ -604,12 +567,7 @@ 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;
|
||||
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);
|
||||
ret= read(o->source_fd,o->buffer+o->write_idx,can_read);
|
||||
if(ret==-1) {
|
||||
|
||||
/* >>> handle input error */;
|
||||
@ -641,8 +599,6 @@ 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 {
|
||||
@ -652,7 +608,6 @@ 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;
|
||||
@ -796,7 +751,7 @@ ex:;
|
||||
|
||||
|
||||
/** Fill the fifo as far as possible without writing to destination fd */
|
||||
int Cdrfifo_fill(struct CdrfifO *o, int size, int flag)
|
||||
int Cdrfifo_fill(struct CdrfifO *o, int flag)
|
||||
{
|
||||
int ret,fill= 0,space,state;
|
||||
|
||||
@ -810,8 +765,6 @@ int Cdrfifo_fill(struct CdrfifO *o, int size, 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 lifesaver for modern
|
||||
to data consumer. Although this is not a mandatory lifesavier 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,16 +64,6 @@ 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);
|
||||
|
||||
@ -82,7 +72,6 @@ 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);
|
||||
@ -145,11 +134,10 @@ 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.
|
||||
@param size if >=0 : end filling after the given number of bytes
|
||||
/** Fill the fifo as far as possible without writing to destination fd
|
||||
@return 1 on success, <=0 on failure
|
||||
*/
|
||||
int Cdrfifo_fill(struct CdrfifO *o, int size, int flag);
|
||||
int Cdrfifo_fill(struct CdrfifO *o, int flag);
|
||||
|
||||
|
||||
#endif /* Cdrfifo_headerfile_includeD */
|
||||
|
@ -1,600 +0,0 @@
|
||||
.\" 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.
|
||||
|
2028
cdrskin/cdrskin.c
2028
cdrskin/cdrskin.c
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.6</DT>
|
||||
<DT>libburn-0.2.1 (inofficially stable SVN snapshot)</DT>
|
||||
<DD>(by Derek Foreman, Ben Jansens, and team of libburn.pykix.org)</DD>
|
||||
<DD>transfers data to CD</DD>
|
||||
</DL>
|
||||
@ -80,31 +80,10 @@ 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>).
|
||||
Suitability for audio CD frontends has been improved much and is now being
|
||||
evaluated.<BR>
|
||||
(see <A HREF="#examples">examples</A>).<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>
|
||||
@ -115,6 +94,15 @@ 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_device=/dev/cdrom -eject</DD>
|
||||
<DD>$ cdrskin -v dev=1,1,0 blank=fast eject_device=/dev/cdrom -eject</DD>
|
||||
<DD>$ cdrskin -v dev=1,1,0 speed=12 fs=8m -sao driveropts=burnfree eject_device=/dev/cdrom -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>
|
||||
|
||||
@ -123,7 +111,15 @@ wishes as well as on the development of libburn.</DT>
|
||||
<UL>
|
||||
<DT></DT>
|
||||
<LI>
|
||||
Appending sessions to unclosed media is restricted to write mode TAO.
|
||||
No audio features yet. (Note: Option -audio is enabled in cdrskin-0.1.5 as
|
||||
offered below, but no beheading of .au or .wav files takes place yet.)
|
||||
</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.
|
||||
</LI>
|
||||
<LI>
|
||||
cdrskin -scanbus or --devices hangs for quite a while if there is
|
||||
@ -137,6 +133,12 @@ In severe cases it might be necessary to guess the device name /dev/sgN resp.
|
||||
/dev/hdX of the non-ill burner if it cannot be found otherwise among its
|
||||
ill peers. Alternatively one can guess the address of the ill device, remove
|
||||
rw-permissions and retry the bus scan as non-superuser.
|
||||
<LI>
|
||||
Burners other than /dev/sg0 and /dev/hdX (i.e. without ide-scsi) need a user
|
||||
supplied device address for program eject.
|
||||
(Note: -eject is now working in libburn-0.2.1 SVN and the workaround described
|
||||
here is obsolete with cdrskin-0.1.5 as offered below.)
|
||||
</LI>
|
||||
</UL>
|
||||
</P>
|
||||
|
||||
@ -145,30 +147,30 @@ 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.6.pl02.tar.gz">cdrskin-0.2.6.pl02.tar.gz</A>
|
||||
(510 KB).
|
||||
<DD><A HREF="cdrskin-0.1.4.tar.gz">cdrskin-0.1.4.tar.gz</A>
|
||||
(580 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
|
||||
produced via a different procedure, though.<BR>
|
||||
The "stable" cdrskin tarballs are tested and eventually slightly modified
|
||||
SVN snapshots from libburn.pykix.org . All modifications are to be re-merged
|
||||
into that SVN repository.<BR>
|
||||
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.6.pl01-x86-suse9_0.tar.gz">
|
||||
cdrskin_0.2.6.pl01-x86-suse9_0.tar.gz</A>, (60 KB),
|
||||
<DD><A HREF="cdrskin_0.1.4-x86-suse9_0.tar.gz">
|
||||
cdrskin_0.1.4-x86-suse9_0.tar.gz</A>, (50 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.6.pl01-x86-suse9_0-static.tar.gz">
|
||||
cdrskin_0.2.6.pl01-x86-suse9_0-static.tar.gz</A>, (260 KB), -static compiled,
|
||||
<DD><A HREF="cdrskin_0.1.4-x86-suse9_0-static.tar.gz">
|
||||
cdrskin_0.1.4-x86-suse9_0-static.tar.gz</A>, (250 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>
|
||||
@ -187,43 +189,17 @@ cdrskin_0.2.6.pl01-x86-suse9_0-static.tar.gz</A>, (260 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><H3>Development snapshot, version 0.2.7 :</H3></DT>
|
||||
<DD>Enhancements towards stable version 0.2.6:
|
||||
(none yet)
|
||||
</DD>
|
||||
<DT>Development snapshot:</DT>
|
||||
<DD> </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><A HREF="README_cdrskin_devel">README 0.1.5</A>
|
||||
<DD><A HREF="cdrskin__help_devel">cdrskin_0.1.5 --help</A></DD>
|
||||
<DD><A HREF="cdrskin_help_devel">cdrskin_0.1.5 -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/libburn/trunk libburn_pykix</B>
|
||||
<DD>Maintainers of cdrskin unstable packages please use SVN of
|
||||
<A HREF="http://libburn.pykix.org"> libburn.pykix.org</A></DD>
|
||||
<DD>Download: <KBD><B>svn co http://libburn-svn.pykix.org/trunk libburn_pykix</B>
|
||||
</KBD></DD>
|
||||
<DD>Build: <KBD><B>cd libburn_pykix ; ./bootstrap ; ./configure ; make</B>
|
||||
</KBD></DD>
|
||||
@ -235,19 +211,19 @@ vanilla tools like make and gcc are needed.</DD>
|
||||
<DD> </DD>
|
||||
<DT>The following downloads are intended for adventurous end users or
|
||||
admins with full system souvereignty.</DT>
|
||||
<DD>Source (./bootstrap is already applied, build tested, for more see above
|
||||
<DT>Source (./bootstrap is already applied, build tested, for more see above
|
||||
<A HREF="README_cdrskin_devel">upcoming README</A> ):
|
||||
</DD>
|
||||
</DT>
|
||||
<DD>
|
||||
<A HREF="cdrskin-0.2.7.tar.gz">cdrskin-0.2.7.tar.gz</A>
|
||||
(500 KB).
|
||||
<A HREF="cdrskin-0.1.5.tar.gz">cdrskin-0.1.5.tar.gz</A>
|
||||
(580 KB).
|
||||
</DD>
|
||||
<DD>Binary (untar and move to /usr/bin/cdrskin):</DD>
|
||||
<DD><A HREF="cdrskin_0.2.7-x86-suse9_0.tar.gz">
|
||||
cdrskin_0.2.7-x86-suse9_0.tar.gz</A>, (60 KB).
|
||||
<DT>Binary (untar and move to /usr/bin/cdrskin):</DT>
|
||||
<DD><A HREF="cdrskin_0.1.5-x86-suse9_0.tar.gz">
|
||||
cdrskin_0.1.5-x86-suse9_0.tar.gz</A>, (50 KB).
|
||||
</DD>
|
||||
<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><A HREF="cdrskin_0.1.5-x86-suse9_0-static.tar.gz">
|
||||
cdrskin_0.1.5-x86-suse9_0-static.tar.gz</A>, (250 KB)
|
||||
</DD>
|
||||
</DL>
|
||||
</P>
|
||||
@ -300,21 +276,25 @@ is a GUI frontend which uses cdrecord for CD burning.)
|
||||
<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> 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>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>
|
||||
<DD>$ <KBD><B>export SCDBACKUP_CDRECORD="cdrskin -v -v tao_to_sao_tsize=650m eject_device=/dev/cdrecorder"</B></KBD></DD>
|
||||
<DD>(eject_device= has to be the appropriate address for program eject)</DD>
|
||||
<DD>$ <KBD><B>scdbackup_home</B></KBD></DD>
|
||||
</DL>
|
||||
<DL>
|
||||
<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.6 : limited cdrecord compatibility wrapper for libburn</KBD></DD>
|
||||
<DD><KBD>cdrskin 0.1.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>
|
||||
</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.
|
||||
@ -323,18 +303,16 @@ In this case, you may execute
|
||||
and try again.
|
||||
<DL>
|
||||
<DT></DT>
|
||||
<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> 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>
|
||||
<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</B></KBD></DD>
|
||||
<DD>$ <KBD><B>unset SCDBACKUP_USE_CDRSKIN SCDBACKUP_EJECT_ADR</B></KBD></DD>
|
||||
</DL>
|
||||
<DL>
|
||||
<DT>To get back to using cdrecord :</DT>
|
||||
|
@ -1 +1 @@
|
||||
#define Cdrskin_timestamP "2006.12.16.090001"
|
||||
#define Cdrskin_timestamP "2006.09.20.134219"
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -3,7 +3,7 @@
|
||||
|
||||
A signal handler which cleans up an application and exits.
|
||||
|
||||
Provided under GPL license within GPL projects, BSD license elsewise.
|
||||
Provided under GPL license within cdrskin and under BSD license elsewise.
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -23,27 +23,8 @@ typedef void (*sighandler_t)(int);
|
||||
|
||||
#include "cleanup.h"
|
||||
|
||||
|
||||
#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[]= {
|
||||
static int signal_list[]= {
|
||||
SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT,
|
||||
SIGFPE, SIGSEGV, SIGPIPE, SIGALRM, SIGTERM,
|
||||
SIGUSR1, SIGUSR2, SIGXCPU, SIGTSTP, SIGTTIN,
|
||||
@ -51,7 +32,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",
|
||||
@ -64,14 +45,10 @@ static int signal_list_count= 24;
|
||||
/* 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;
|
||||
@ -85,27 +62,26 @@ static int Cleanup_handler_exit(int exit_value, int signum, int flag)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if(cleanup_msg[0]!=0)
|
||||
fprintf(stderr,"\n%s\n",cleanup_msg);
|
||||
if(cleanup_perform_app_handler_first)
|
||||
if(cleanup_app_handler!=NULL) {
|
||||
ret= (*cleanup_app_handler)(cleanup_app_handle,signum,0);
|
||||
if(ret==2 || ret==-2)
|
||||
if(ret==2)
|
||||
return(2);
|
||||
}
|
||||
if(cleanup_exiting) {
|
||||
if(cleanup_msg[0]!=0)
|
||||
fprintf(stderr,"%s\n",cleanup_msg);
|
||||
fprintf(stderr,"cleanup: ABORT : repeat by pid=%d, signum=%d\n",
|
||||
getpid(),signum);
|
||||
return(0);
|
||||
}
|
||||
cleanup_exiting= 1;
|
||||
if(cleanup_msg[0]!=0)
|
||||
fprintf(stderr,"%s\n",cleanup_msg);
|
||||
alarm(0);
|
||||
if(!cleanup_perform_app_handler_first)
|
||||
if(cleanup_app_handler!=NULL) {
|
||||
ret= (*cleanup_app_handler)(cleanup_app_handle,signum,0);
|
||||
if(ret==2 || ret==-2)
|
||||
return(2);
|
||||
}
|
||||
if(cleanup_app_handler!=NULL)
|
||||
(*cleanup_app_handler)(cleanup_app_handle,signum,0);
|
||||
exit(exit_value);
|
||||
}
|
||||
|
||||
@ -139,12 +115,8 @@ int Cleanup_set_handlers(void *handle, Cleanup_app_handler_T handler, int flag)
|
||||
cleanup_msg[0]= 0;
|
||||
cleanup_app_handle= handle;
|
||||
cleanup_app_handler= handler;
|
||||
|
||||
/* <<< make cleanup_exiting thread safe to get rid of this */
|
||||
if(flag&4)
|
||||
cleanup_perform_app_handler_first= 1;
|
||||
|
||||
|
||||
if(flag&1)
|
||||
sig_handler= SIG_DFL;
|
||||
else if(flag&2)
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
A signal handler which cleans up an application and exits.
|
||||
|
||||
Provided under GPL license within GPL projects, BSD license elsewise.
|
||||
Provided under GPL license within cdrskin and under BSD license elsewise.
|
||||
*/
|
||||
|
||||
#ifndef Cleanup_includeD
|
||||
@ -13,7 +13,7 @@
|
||||
/** Layout of an application provided cleanup function using an application
|
||||
provided handle as first argument and the signal number as second
|
||||
argument. The third argument is a flag bit field with no defined bits yet.
|
||||
If the handler returns 2 or -2 then it has delegated exit() to some other
|
||||
If the handler returns 2 then it has delegated exit() to some other
|
||||
instance and the Cleanup handler shall return rather than exit.
|
||||
*/
|
||||
typedef int (*Cleanup_app_handler_T)(void *, int, int);
|
||||
|
@ -6,55 +6,37 @@
|
||||
|
||||
debug_opts=
|
||||
def_opts=
|
||||
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"
|
||||
libvers="-DCdrskin_libburn_0_2_2"
|
||||
do_strip=0
|
||||
static_opts=
|
||||
warn_opts="-Wall"
|
||||
fifo_source="cdrskin/cdrfifo.c"
|
||||
compile_cdrskin=1
|
||||
compile_cdrfifo=0
|
||||
compile_dewav=0
|
||||
|
||||
for i in "$@"
|
||||
do
|
||||
if test "$i" = "-compile_cdrfifo"
|
||||
then
|
||||
compile_cdrfifo=1
|
||||
elif test "$i" = "-compile_dewav"
|
||||
elif test "$i" = "-tarball_0_2"
|
||||
then
|
||||
compile_dewav=1
|
||||
libvers=
|
||||
elif test "$i" = "-cvs_A51208"
|
||||
then
|
||||
libvers="-DCdrskin_libburn_cvs_A51208_tS"
|
||||
elif test "$i" = "-cvs_A60220"
|
||||
then
|
||||
libvers="-DCdrskin_libburn_cvs_A60220_tS"
|
||||
libdax_audioxtr_o=
|
||||
libdax_msgs_o="libburn/message.o"
|
||||
cleanup_src_or_obj="-DCleanup_has_no_libburn_os_H cdrskin/cleanup.c"
|
||||
elif test "$i" = "-libburn_0_2_6"
|
||||
elif test "$i" = "-libburn_0_2_1"
|
||||
then
|
||||
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"
|
||||
libvers="-DCdrskin_libburn_0_2_1"
|
||||
elif test "$i" = "-libburn_0_2_2"
|
||||
then
|
||||
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"
|
||||
libvers="-DCdrskin_libburn_0_2_2"
|
||||
elif test "$i" = "-newapi" -o "$i" = "-experimental"
|
||||
then
|
||||
def_opts="$def_opts -DCdrskin_new_api_tesT"
|
||||
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,17 +56,16 @@ do
|
||||
debug_opts="$debug_opts -O2"
|
||||
elif test "$i" = "-help" -o "$i" = "--help" -o "$i" = "-h"
|
||||
then
|
||||
echo "cdrskin/compile_cdrskin.sh : to be executed within top level directory"
|
||||
echo "cdrskin/compile_cdrskin.sh : to be executed within ./cdrskin-0.1.3.0.2.ts"
|
||||
echo "Options:"
|
||||
echo " -compile_cdrfifo compile program cdrskin/cdrfifo."
|
||||
echo " -compile_dewav compile program test/dewav without libburn."
|
||||
echo " -tarball_0_2 set macro to match libburn-0.2.ts.tar.gz"
|
||||
echo " -cvs_A51208 set macro to match libburn-CVS of 8 Dec 2005."
|
||||
echo " -cvs_A60220 set macro to match libburn-CVS of 20 Feb 2006."
|
||||
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 " -libburn_0_2_1 set macro to match libburn-SVN of 15 Sep 2006."
|
||||
echo " -libburn_0_2_2 set macro to match libburn-0.2.2"
|
||||
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."
|
||||
echo " -do_diet produce capability reduced lean version."
|
||||
echo " -do_strip apply program strip to compiled programs."
|
||||
echo " -g compile with cc option -g."
|
||||
@ -104,23 +85,15 @@ 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 -I. \
|
||||
$warn_opts \
|
||||
$static_opts \
|
||||
$debug_opts \
|
||||
$libvers \
|
||||
$largefile_opts \
|
||||
$def_opts \
|
||||
\
|
||||
echo "compiling program cdrskin/cdrskin.c $static_opts $debug_opts $libvers $def_opts"
|
||||
cc $warn_opts -I. $static_opts $debug_opts $libvers $def_opts \
|
||||
-DCdrskin_build_timestamP='"'"$timestamp"'"' \
|
||||
\
|
||||
-o cdrskin/cdrskin \
|
||||
\
|
||||
cdrskin/cdrskin.c \
|
||||
$fifo_source \
|
||||
\
|
||||
$cleanup_src_or_obj \
|
||||
cdrskin/cleanup.c \
|
||||
\
|
||||
libburn/async.o \
|
||||
libburn/debug.o \
|
||||
@ -131,10 +104,9 @@ then
|
||||
libburn/source.o \
|
||||
libburn/structure.o \
|
||||
\
|
||||
libburn/message.o \
|
||||
libburn/sg.o \
|
||||
libburn/write.o \
|
||||
$libdax_audioxtr_o \
|
||||
$libdax_msgs_o \
|
||||
\
|
||||
libburn/mmc.o \
|
||||
libburn/sbc.o \
|
||||
@ -148,17 +120,6 @@ then
|
||||
libburn/lec.o \
|
||||
\
|
||||
-lpthread
|
||||
|
||||
ret=$?
|
||||
if test "$ret" = 0
|
||||
then
|
||||
dummy=dummy
|
||||
else
|
||||
echo >&2
|
||||
echo "+++ FATAL : Compilation of cdrskin failed" >&2
|
||||
echo >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "$compile_cdrfifo" = 1
|
||||
@ -168,43 +129,8 @@ then
|
||||
-DCdrfifo_standalonE \
|
||||
-o cdrskin/cdrfifo \
|
||||
cdrskin/cdrfifo.c
|
||||
|
||||
ret=$?
|
||||
if test "$ret" = 0
|
||||
then
|
||||
dummy=dummy
|
||||
else
|
||||
echo >&2
|
||||
echo "+++ FATAL : Compilation of cdrfifo failed" >&2
|
||||
echo >&2
|
||||
exit 2
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "$compile_dewav" = 1
|
||||
then
|
||||
echo "compiling program test/dewav.c -DDewav_without_libburN $static_opts $debug_opts"
|
||||
cc $static_opts $debug_opts \
|
||||
-DDewav_without_libburN \
|
||||
-o test/dewav \
|
||||
test/dewav.c \
|
||||
libburn/libdax_audioxtr.o \
|
||||
libburn/libdax_msgs.o \
|
||||
\
|
||||
-lpthread
|
||||
|
||||
ret=$?
|
||||
if test "$ret" = 0
|
||||
then
|
||||
dummy=dummy
|
||||
else
|
||||
echo >&2
|
||||
echo "+++ FATAL : Compilation of test/dewav failed" >&2
|
||||
echo >&2
|
||||
exit 2
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "$do_strip" = 1
|
||||
then
|
||||
echo "stripping result cdrskin/cdrskin"
|
||||
|
@ -12,16 +12,18 @@ 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 . Online available as :
|
||||
http://libburn.pykix.org/browser/trunk/cdrskin/README?format=raw
|
||||
|
||||
About libburn API for burning CD: http://libburn-api.pykix.org
|
||||
can be found in cdrskin/README .
|
||||
|
||||
--------------------------------------------------------------------------
|
||||
|
||||
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 with CD media fails to match its paragon cdrecord on three major
|
||||
fields: convenient TAO burn mode, multi session, audio features.
|
||||
|
||||
Audio features are the only topic where cdrskin did not yet exploit current
|
||||
libburn to the maximum. This is due to my own lack of audiophile motivation
|
||||
and due to the lack of sincere users who provide me with cdrecord use cases,
|
||||
help me to explore the original cdrecord behavior and serve as dedicated
|
||||
testers for eventual newly introduced cdrskin audio features.
|
||||
|
||||
cdrskin does not provide DVD burning yet. See advise to use dvd+rw-tools
|
||||
at the end of this text.
|
||||
@ -36,9 +38,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 .
|
||||
@ -46,9 +48,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
|
||||
@ -60,10 +62,11 @@ 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
|
||||
@ -77,7 +80,46 @@ has to offer both, r- and w-permission.
|
||||
|
||||
--------------------------------------------------------------------------
|
||||
|
||||
fifo_start_at=<num> is a throughput enhancer for unsteady data streams
|
||||
eject_device=<path> is needed to work around yet broken tray ejection of
|
||||
drives. cdrskin makes a bold shell call to program "eject" and regrettably
|
||||
this program does not like our addresses for SCSI devices.
|
||||
/dev/hdX work fine and /dev/sg0 is quite safely guess-translated to
|
||||
/dev/sr0 . /dev/sg1 et.al. need the user's help. <path> must work with eject.
|
||||
|
||||
--------------------------------------------------------------------------
|
||||
|
||||
dev_translation=<sep><from><sep><to> is needed to foist cdrskin to frontend
|
||||
programs of cdrecord which do *not* ask cdrecord -scanbus but which make
|
||||
own assumptions and guesses about cdrecord's device addresses.
|
||||
cdrskin project - at least for now - refuses to try to provide a similar
|
||||
guesswork but uses own cdrecord style addresses which have a mere
|
||||
semi-automatic text mapping to real libburn addresses. See cdrskin/README,
|
||||
"Pseudo-SCSI Adresses".
|
||||
|
||||
If you need to foist cdrskin under a frontend then you may be lucky and
|
||||
both ideas of an address coincide. Especially if the frontend has the
|
||||
decency to ask its "cdrecord" via option -scanbus for a list of drives.
|
||||
If not, look into the error protocol of the frontend, look at the output
|
||||
of a run of cdrskin --devices and give cdrskin the necessary hint.
|
||||
|
||||
If your frontend insists in using "0,0,0" and --devices reported
|
||||
dev='/dev/sg0' resp. cdrskin -scanbus reported "1,0,0" then this
|
||||
would be the appropriate translation
|
||||
|
||||
dev_translation=+0,0,0+/dev/sg0
|
||||
|
||||
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 .
|
||||
|
||||
--------------------------------------------------------------------------
|
||||
|
||||
--fifo_start_empty 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.
|
||||
@ -101,16 +143,10 @@ 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_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 ...
|
||||
}}}
|
||||
--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.
|
||||
|
||||
Note: no FIFO can give you better average throughput than the average
|
||||
throughput of the data source and the throughput of the burner.
|
||||
@ -134,10 +170,6 @@ 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
|
||||
@ -145,35 +177,6 @@ be a failure in any way.
|
||||
|
||||
--------------------------------------------------------------------------
|
||||
|
||||
dev_translation=<sep><from><sep><to> may be needed to foist cdrskin to
|
||||
frontend programs of cdrecord which do *not* ask cdrecord -scanbus but
|
||||
which make own assumptions and guesses about cdrecord's device addresses.
|
||||
|
||||
Normally, cdrskin understands all addresses which are suitable for cdrecord
|
||||
under Linux. See cdrskin/README, "Pseudo-SCSI Adresses".
|
||||
This option is mainly for (yet unknown) exotic configurations or very
|
||||
stubborn frontend programs.
|
||||
|
||||
If a frontend refuses to work with cdrskin, look into the error protocol
|
||||
of that frontend, look at the output of a run of cdrskin --devices and give
|
||||
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 .
|
||||
|
||||
--------------------------------------------------------------------------
|
||||
|
||||
DVD advise:
|
||||
|
||||
For burning of DVD media the cdrskin project currently advises to use
|
||||
|
19
configure.ac
19
configure.ac
@ -1,4 +1,4 @@
|
||||
AC_INIT([libburn], [0.2.6.2], [http://libburn.pykix.org])
|
||||
AC_INIT([libburn], [0.2.2], [http://libburn.pykix.org])
|
||||
AC_PREREQ([2.50])
|
||||
dnl AC_CONFIG_HEADER([config.h])
|
||||
|
||||
@ -7,12 +7,6 @@ AC_CANONICAL_TARGET
|
||||
|
||||
AM_INIT_AUTOMAKE([subdir-objects])
|
||||
|
||||
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;
|
||||
dnl BURN_INTERFACE_AGE += 1;
|
||||
@ -25,7 +19,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=6.2
|
||||
BURN_MICRO_VERSION=2
|
||||
BURN_INTERFACE_AGE=0
|
||||
BURN_BINARY_AGE=0
|
||||
BURN_VERSION=$BURN_MAJOR_VERSION.$BURN_MINOR_VERSION.$BURN_MICRO_VERSION
|
||||
@ -75,18 +69,10 @@ AC_PROG_INSTALL
|
||||
|
||||
AC_CHECK_HEADERS()
|
||||
|
||||
AC_CHECK_MEMBER([struct tm.tm_gmtoff],
|
||||
[AC_DEFINE(HAVE_TM_GMTOFF, 1,
|
||||
[Define this if tm structure includes a tm_gmtoff entry.])],
|
||||
,
|
||||
[#include <time.h>])
|
||||
|
||||
THREAD_LIBS=-lpthread
|
||||
AC_SUBST(THREAD_LIBS)
|
||||
|
||||
TARGET_SHIZZLE
|
||||
AC_SUBST(ARCH)
|
||||
AC_SUBST(LIBBURN_ARCH_LIBS)
|
||||
|
||||
dnl Add compiler-specific flags
|
||||
|
||||
@ -112,5 +98,6 @@ AC_CONFIG_FILES([
|
||||
doc/doxygen.conf
|
||||
version.h
|
||||
libburn-1.pc
|
||||
libisofs-1.pc
|
||||
])
|
||||
AC_OUTPUT
|
||||
|
30
doc/comments
30
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 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.
|
||||
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.
|
||||
|
||||
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,26 +91,24 @@ stay upward compatible for a good while.
|
||||
@subsection libburner-help Libburner --help
|
||||
<pre>
|
||||
Usage: test/libburner
|
||||
[--drive <address>|<driveno>|"-"] [--audio]
|
||||
[--blank_fast|--blank_full] [--try_to_simulate]
|
||||
[<one or more imagefiles>|"-"]
|
||||
[--drive <address>|<driveno>|"-"]
|
||||
[--verbose <level>] [--blank_fast|--blank_full]
|
||||
[--burn_for_real|--try_to_simulate] [--stdin_size <bytes>]
|
||||
[<imagefile>|"-"]
|
||||
Examples
|
||||
A bus scan (needs rw-permissions to see a drive):
|
||||
test/libburner --drive -
|
||||
Burn a file to drive chosen by number:
|
||||
test/libburner --drive 0 my_image_file
|
||||
test/libburner --drive 0 --burn_for_real my_image_file
|
||||
Burn a file to drive chosen by persistent address:
|
||||
test/libburner --drive /dev/hdc my_image_file
|
||||
test/libburner --drive /dev/hdc --burn_for_real my_image_file
|
||||
Blank a used CD-RW (is combinable with burning in one run):
|
||||
test/libburner --drive /dev/hdc --blank_fast
|
||||
Burn two audio tracks
|
||||
lame --decode -t /path/to/track1.mp3 track1.cd
|
||||
test/dewav /path/to/track2.wav -o track2.cd
|
||||
test/libburner --drive /dev/hdc --audio track1.cd track2.cd
|
||||
test/libburner --drive 0 --blank_fast
|
||||
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 -
|
||||
To be read from *not mounted* CD via: afio -tvZ /dev/hdc
|
||||
test/libburner --drive /dev/hdc --burn_for_real --stdin_size 734003200 -
|
||||
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 doc test
|
||||
FILE_PATTERNS = libburn.h comments libburner.c
|
||||
INPUT = libburn libisofs doc test
|
||||
FILE_PATTERNS = libburn.h libisofs.h comments libburner.c
|
||||
RECURSIVE = NO
|
||||
EXCLUDE =
|
||||
EXCLUDE_SYMLINKS = NO
|
||||
|
@ -7,6 +7,5 @@ Name: libburn
|
||||
Description: Disc reading/writing library
|
||||
Version: @VERSION@
|
||||
Requires:
|
||||
Libs: -L${libdir} -lburn
|
||||
Libs.private: @THREAD_LIBS@ @LIBBURN_ARCH_LIBS@
|
||||
Libs: -L${libdir} -lburn @THREAD_LIBS@
|
||||
Cflags: -I${includedir}/libburn
|
||||
|
@ -1,792 +0,0 @@
|
||||
List of assert() calls in libburn. 6 Oct 2006.
|
||||
|
||||
Format:
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
Number) grep'ed line
|
||||
(++ before number means: is fully done, + means is done so far )
|
||||
function():
|
||||
Description of abort condition.
|
||||
|
||||
Possible callers and their relation to the abort condition.
|
||||
|
||||
: Error Evaluation
|
||||
=> Consequences
|
||||
|
||||
Eventual implementation timestamp
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
++ 1) libburn/async.c: assert(a != NULL); /* wasn't found.. this should not be possible */
|
||||
static remove_worker():
|
||||
A thread describing structure (struct w_list) could not be found in
|
||||
order to be released.
|
||||
|
||||
Called by API burn_drive_scan()
|
||||
Called by static erase_worker_func() , thread under API burn_disc_erase()
|
||||
Called by static write_disc_worker_func(), thread under API burn_disc_write()
|
||||
All three want to clean up after they are done.
|
||||
|
||||
: Severe Libburn Error
|
||||
=> issue LIBDAX_MSGS_SEV_WARNING
|
||||
|
||||
ts A61006
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
++ 2) libburn/async.c: assert(!(workers && workers->drive));
|
||||
API burn_drive_scan():
|
||||
Before spawning a thread, the function refuses work because another
|
||||
drive activity is going on.
|
||||
|
||||
: Severe Application Error
|
||||
=> return -1; redefine @return in API , issue LIBDAX_MSGS_SEV_SORRY
|
||||
|
||||
ts A61006
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
+ 3) libburn/async.c: assert(workers == NULL);
|
||||
API burn_drive_scan():
|
||||
After thread is done and remover_worker() succeeded, there is still a
|
||||
worker registered. Shall probably detect roguely appeared burn or
|
||||
erase runs. (I consider to install a mutex shielded function for that.)
|
||||
|
||||
: Severe Libburn Error
|
||||
=> Same as 1)
|
||||
|
||||
ts A61006
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
++ 4) libburn/async.c: assert(drive);
|
||||
libburn/async.c: assert(!SCAN_GOING());
|
||||
libburn/async.c: assert(!find_worker(drive));
|
||||
API burn_disc_erase():
|
||||
Wants to see a drive (assumes NULL == 0), wants to see no scan and
|
||||
wants to see no other worker on that drive. I.e. this would tolerate
|
||||
a parallel activity on another drive.
|
||||
|
||||
: Severe Application Error
|
||||
=> (no return value), issue LIBDAX_MSGS_SEV_SORRY
|
||||
|
||||
ts A61006
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
++ 5) libburn/async.c: assert(!SCAN_GOING());
|
||||
libburn/async.c: assert(!find_worker(opts->drive));
|
||||
API burn_disc_write():
|
||||
Same as 4)
|
||||
|
||||
: Severe Application Error
|
||||
=> Same as 4)
|
||||
|
||||
ts A61006
|
||||
|
||||
---------------------------------------------------------------------
|
||||
|
||||
++ 6) libburn/drive.c: assert(d->busy == BURN_DRIVE_IDLE);
|
||||
API burn_drive_release():
|
||||
A drive is not idle on release.
|
||||
|
||||
: Severe Application Error
|
||||
=> Same as 4)
|
||||
|
||||
ts A61007
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
++ 7) libburn/drive.c: assert(d->released);
|
||||
burn_wait_all()
|
||||
A drive is found grabbed.
|
||||
|
||||
Called by burn_drive_scan_sync(), thread under API burn_drive_scan()
|
||||
Called by API burn_finish
|
||||
|
||||
: Severe Application Error
|
||||
=> rename and redefine burn_wait_all() : now burn_drives_are_clear()
|
||||
=> change all use of burn_wait_all()
|
||||
=> Move tests up to burn_drive_scan()
|
||||
=> There: return -1; issue LIBDAX_MSGS_SEV_SORRY
|
||||
|
||||
ts A61007
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
++ 8) libburn/drive.c: assert(!d->released);
|
||||
API burn_disc_get_status()
|
||||
Attempt to read status of non-grabbed drive.
|
||||
|
||||
: Severe Application Error
|
||||
=> extend enum burn_disc_status by BURN_DISC_UNGRABBED
|
||||
=> return BURN_DISC_UNGRABBED, issue LIBDAX_MSGS_SEV_SORRY
|
||||
|
||||
ts A61007
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
++ 9) libburn/drive.c: assert( /* (write_type >= BURN_WRITE_PACKET) && */
|
||||
burn_drive_get_block_types():
|
||||
Will not work on BURN_WRITE below BURN_WRITE_RAW.
|
||||
|
||||
Called by -nobody- ?
|
||||
|
||||
: Severe Application Error
|
||||
=> inactivate unused function
|
||||
|
||||
ts A61007
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
++ 10) libburn/drive.c: assert(d->idata);
|
||||
libburn/drive.c: assert(d->mdata);
|
||||
static drive_getcaps():
|
||||
sg.c:enumerate_common() did not succeed in creating a proper struct burn_drive
|
||||
Called by burn_drive_scan_sync()
|
||||
|
||||
: Severe System Error
|
||||
=> This could possibly really stay an abort() because the reason is
|
||||
a plain failure of the system's memory management.
|
||||
=> Detect this failure already in enumerate_common(),
|
||||
issue LIBDAX_MSGS_SEV_FATAL, return
|
||||
|
||||
ts A61007
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
++ 11) libburn/drive.c: assert(burn_running);
|
||||
burn_drive_scan_sync():
|
||||
The library was not initialized.
|
||||
|
||||
Called as thread by API burn_drive_scan()
|
||||
|
||||
: Severe Application Error
|
||||
=> Move this test up to burn_drive_scan()
|
||||
=> There: return -1; redefine @return in API , issue LIBDAX_MSGS_SEV_FATAL
|
||||
|
||||
ts A61007
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
++ 12) libburn/drive.c: assert(d->released == 1);
|
||||
burn_drive_scan_sync():
|
||||
Inactivated
|
||||
|
||||
: (Severe Application Error)
|
||||
=> throw out inactivated code
|
||||
|
||||
ts A61007
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
++ 13) libburn/drive.c: assert(strlen(d->devname) < BURN_DRIVE_ADR_LEN);
|
||||
burn_drive_raw_get_adr():
|
||||
An enumerated device address is longer than the API's maximum length
|
||||
|
||||
Called by API burn_drive_get_adr()
|
||||
Called by API burn_drive_obtain_scsi_adr()
|
||||
|
||||
: Severe Libburn Error
|
||||
=> return -1; in all three functions, enhance burn_drive_get_adr @return docs
|
||||
=> issue LIBDAX_MSGS_SEV_SORRY
|
||||
|
||||
ts A61007
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
++ 14) libburn/drive.c: assert(drive_info->drive!=NULL);
|
||||
API burn_drive_get_adr():
|
||||
Drive info has no drive attached.
|
||||
|
||||
: Severe Libburn Error (unlikely, will eventually SIGSEGV on NULL)
|
||||
=> delete assert
|
||||
|
||||
ts A61007
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
++ 15) libburn/init.c: assert(burn_running);
|
||||
API burn_finish():
|
||||
The library is not initialized
|
||||
|
||||
: Severe Application Error
|
||||
=> return (assume no msg system)
|
||||
|
||||
ts A61007
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
++ 16) libburn/init.c: assert(burn_running);
|
||||
API burn_preset_device_open():
|
||||
The library is not initialized
|
||||
|
||||
: Severe Application Error
|
||||
=> return (assume no msg system)
|
||||
|
||||
ts A61007
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
++ 17) libburn/mmc.c: assert(o->drive == d);
|
||||
mmc_close_disc():
|
||||
alias: struct burn_drive.close_disc()
|
||||
Parameters struct burn_drive and struct burn_write_opts do not match
|
||||
|
||||
Called by -nobody- ?
|
||||
|
||||
( => Disable unused function ? )
|
||||
=> removed redundant parameter struct burn_drive
|
||||
|
||||
ts A61009
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
++ 18) libburn/mmc.c: assert(o->drive == d);
|
||||
mmc_close_session():
|
||||
Same as 17)
|
||||
alias: struct burn_drive.close_session()
|
||||
|
||||
Called by -nobody- ?
|
||||
|
||||
( => Disable unused function ? )
|
||||
=> removed redundant parameter struct burn_drive
|
||||
|
||||
ts A61009
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
++ 19) libburn/mmc.c: assert(buf->bytes >= buf->sectors); /* can be == at 0... */
|
||||
mmc_write_12():
|
||||
- Unclear what .bytes and .sectors mean in struct buffer -
|
||||
|
||||
Called by -nobody- ?
|
||||
|
||||
=> problems with filling the write buffer have to be handled by callers
|
||||
=> delete assert
|
||||
|
||||
ts A61009
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
++ 20) libburn/mmc.c: assert(buf->bytes >= buf->sectors); /* can be == at 0... */
|
||||
mmc_write():
|
||||
- Unclear what .bytes and .sectors mean in struct buffer -
|
||||
|
||||
libburn/mmc.c: c.page->sectors = errorblock - start + 1;
|
||||
mmc_read_sectors() by toc_find_modes() by mmc_read_toc() alias drive.read_toc()
|
||||
by burn_drive_grab()
|
||||
This seems to be unrelated to mmc_write().
|
||||
|
||||
libburn/sector.c: out->sectors++;
|
||||
get_sector()
|
||||
Seems to hand out sector start pointer in opts->drive->buffer
|
||||
and to count reservation transactions as well as reserved bytes.
|
||||
Ensures out->bytes >= out->sectors
|
||||
|
||||
|
||||
libburn/mmc.c: c.page->bytes = s->count * 8;
|
||||
mmc_send_cue_sheet()
|
||||
Does not use mmc_write() but directly (sg_)issue_command()
|
||||
|
||||
libburn/sector.c: out->bytes += seclen;
|
||||
get_sector()
|
||||
See above
|
||||
Ensures out->bytes >= out->sectors
|
||||
|
||||
libburn/spc.c: c.page->bytes = 8 + 2 + d->mdata->retry_page_length;
|
||||
spc_select_error_params()
|
||||
Does not use mmc_write() but directly (sg_)issue_command()
|
||||
|
||||
libburn/spc.c: c.page->bytes = 8 + 2 + d->mdata->write_page_length;
|
||||
spc_select_error_params()
|
||||
Does not use mmc_write() but directly (sg_)issue_command()
|
||||
|
||||
libburn/spc.c: c.page->bytes = 8 + 2 + 0x32;
|
||||
spc_probe_write_modes()
|
||||
Does not use mmc_write() but directly (sg_)issue_command()
|
||||
|
||||
alias struct burn_drive.write()
|
||||
Called by static get_sector, by many
|
||||
Called by burn_write_flush
|
||||
Called by burn_write_track
|
||||
|
||||
=> problems with filling the write buffer have to be handled by callers
|
||||
=> delete assert
|
||||
|
||||
ts A61009
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
++ 21) libburn/mmc.c: assert(((dlen - 2) % 11) == 0);
|
||||
mmc_read_toc():
|
||||
- Is defunct -
|
||||
|
||||
=> :)
|
||||
|
||||
ts A61009
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
++ 22) libburn/mmc.c: assert(len >= 0);
|
||||
mmc_read_sectors():
|
||||
Catches a bad parameter
|
||||
|
||||
alias: struct burn_drive.read_sectors()
|
||||
Called by API burn_disc_read() , - is defunct -, one could catch the problem
|
||||
Called by toc_find_modes(), problem cannot occur: mem.sectors = 1;
|
||||
|
||||
: Severe Libburn Error
|
||||
(=> in burn_disc_read() check page.sectors before d->read_sectors() )
|
||||
=> :)
|
||||
|
||||
ts A61009
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
++ 23) libburn/mmc.c: assert(d->busy);
|
||||
mmc_read_sectors():
|
||||
Catches use of a drive that is not marked as busy
|
||||
|
||||
alias: struct burn_drive.read_sectors()
|
||||
Called by API burn_disc_read() , - is defunct -, busy = BURN_DRIVE_READING;
|
||||
Called by toc_find_modes(), does the same assert. To be solved there.
|
||||
|
||||
: Severe Libburn Error
|
||||
=> :)
|
||||
|
||||
ts A61009
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
++ 24) libburn/options.c: assert(0);
|
||||
API burn_write_opts_set_write_type():
|
||||
Detects unsuitable enum burn_write_types write_type and int block_type.
|
||||
API promises return 0 on failure
|
||||
|
||||
: Severe Application Error
|
||||
=> issue LIBDAX_MSGS_SEV_SORRY
|
||||
=> should also detect problem of 26) : wrong write_type,block_type combination
|
||||
by calling sector_get_outmode() and checking for -1
|
||||
=> should also detect problem of 41) : unknown block_type
|
||||
by spc_block_type() and checking for -1
|
||||
=> delete assert(0)
|
||||
|
||||
ts A61007
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
++ 25) libburn/read.c: assert((o->version & 0xfffff000) == (OPTIONS_VERSION & 0xfffff000));
|
||||
libburn/read.c: assert(!d->busy);
|
||||
libburn/read.c: assert(d->toc->valid);
|
||||
libburn/read.c: assert(o->datafd != -1);
|
||||
API burn_disc_read():
|
||||
- ? -
|
||||
|
||||
burn_disc_read() is defunct
|
||||
OPTIONS_VERSION does not occur outside this line
|
||||
|
||||
( => one would return )
|
||||
( 22) => catch page.sectors<0 before d->read_sectors() )
|
||||
( 37) => catch ! d->mdata->valid )
|
||||
=> :)
|
||||
|
||||
ts A61007
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
++ 26) libburn/sector.c: assert(0); /* return BURN_MODE_UNIMPLEMENTED :) */
|
||||
static get_outmode():
|
||||
burn_write_opts is wrongly programmed with .write_type and .block_type
|
||||
|
||||
: Severe Application Error
|
||||
=> This gets handled by burn_write_opts_set_write_type()
|
||||
ts A61007 by new semi-public sector_get_outmode()
|
||||
=> delete assert()
|
||||
|
||||
ts A61007
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
++ 27) libburn/sector.c: assert(outlen >= inlen);
|
||||
libburn/sector.c: assert(outmode & BURN_MODE_RAW);
|
||||
libburn/sector.c: assert(offset != -1);
|
||||
static convert_data():
|
||||
Several unacceptable settings within struct burn_write_opts
|
||||
|
||||
Called by sector_toc() sector_pregap() sector_postgap() sector_lout()
|
||||
sector_data()
|
||||
|
||||
: Severe Application Error
|
||||
=> change return type of convert_data()
|
||||
=> all callers interpret return value and eventually return failure
|
||||
|
||||
ts A61007
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
++ 28) libburn/sector.c: assert(0);
|
||||
static char_to_isrc():
|
||||
Called by subcode_user() with data set by API burn_track_set_isrc()
|
||||
Some character conversion fails on wrong input
|
||||
|
||||
: Severe Application Error
|
||||
=> burn_track_set_isrc() has to make sure that only good data are set
|
||||
=> char_to_isrc() returns 0 as default
|
||||
=> delete assert()
|
||||
|
||||
ts A61008
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
++ 29) libburn/sector.c: assert(qmode == 1 || qmode == 2 || qmode == 3);
|
||||
subcode_user():
|
||||
- can not happen -
|
||||
|
||||
: Unknown reason of assert()
|
||||
=> remove assert()
|
||||
|
||||
ts A61010
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
++ 30) libburn/sector.c: assert(modebyte == 1);
|
||||
sector_headers():
|
||||
Does only accept modes BURN_AUDIO, BURN_MODE1 or write_type BURN_WRITE_SAO
|
||||
|
||||
Called by sector_toc() sector_pregap() sector_postgap() sector_lout()
|
||||
sector_data()
|
||||
|
||||
: Severe Libburn Error
|
||||
=> new functions sector_headers_is_ok(), burn_disc_write_is_ok()
|
||||
help to catch problem in API burn_disc_write()
|
||||
=> issue LIBDAX_MSGS_SEV_FATAL
|
||||
|
||||
ts A61009
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
++ 31) libburn/sector.c: assert(0);
|
||||
process_q()
|
||||
- defunct -
|
||||
|
||||
=> :)
|
||||
|
||||
ts A61009
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
++ 32) libburn/sg.c: assert("drive busy" == "non fatal");
|
||||
sg_handle_busy_device():
|
||||
Intentional abort preset by the app
|
||||
|
||||
=> change to abort()
|
||||
|
||||
ts A61007
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
++ 33) libburn/sg.c: assert(fd != -1337);
|
||||
sg_grab():
|
||||
The drive device file could not be opened
|
||||
|
||||
:Severe External Problem
|
||||
=> obsolete by normal drive open failure handling
|
||||
|
||||
ts A61007
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
++ 34) libburn/sg.c: assert(!c->page);
|
||||
sg_issue_command():
|
||||
An SCSI command of direction NO_TRANSFER may not have a .page != NULL.
|
||||
|
||||
Since it is about exposing a libburn detail towards the sg driver, i believe
|
||||
it is sufficient to just not use it.
|
||||
|
||||
: Libburn Error
|
||||
=> enhance internal logics of sg_issue_command()
|
||||
|
||||
ts A61007
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
++ 35) libburn/sg.c: assert(c->page->bytes > 0);
|
||||
sg_issue_command():
|
||||
An SCSI command of direction TO_DRIVE wants to transfer 0 bytes.
|
||||
|
||||
: Severe Libburn Error
|
||||
=> set command.error = 1 and return 0
|
||||
|
||||
ts A61010
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
++ 36) libburn/sg.c: assert(err != -1);
|
||||
sg_issue_command():
|
||||
The transfer of the command via ioctl() failed
|
||||
|
||||
: Severe Transport Level Problem
|
||||
=> close drive fd, set idle and released
|
||||
=> set command.error = 1 and return -1
|
||||
|
||||
ts A61010
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
++ 37) libburn/spc.c: assert(d->mdata->valid);
|
||||
spc_select_error_params():
|
||||
Drive was not properly programmed
|
||||
|
||||
alias struct burn_drive.send_parameters()
|
||||
Called by burn_disc_read, - defunct -
|
||||
|
||||
: Severe Application Error
|
||||
=> moved up as mangled assert to burn_disc_read()
|
||||
|
||||
ts A61007
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
++ 38) libburn/spc.c: assert(d->mdata->cdr_write || d->mdata->cdrw_write ||
|
||||
spc_sense_write_params():
|
||||
Drive does not offer write of any known media type
|
||||
|
||||
alias struct burn_drive.read_disc_info()
|
||||
Called by API burn_drive_grab (assert test made there in soft)
|
||||
|
||||
: Severe Command Level Problem
|
||||
=> remove assert()
|
||||
|
||||
ts A61007
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
++ 39) libburn/spc.c: assert(o->drive == d);
|
||||
spc_select_write_params():
|
||||
Drive does not match struct burn_write_opts
|
||||
|
||||
alias struct burn_drive.send_write_parameters()
|
||||
Called by mmc_close_disc() (-defunct- ?), mmc_close_session() (-defunct- ?),
|
||||
burn_write_track() (d = o->drive;),
|
||||
burn_disc_write_sync() d = (o->drive;)
|
||||
|
||||
: Severe Libburn Error
|
||||
=> remove assert()
|
||||
|
||||
ts A61007
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
++ 40) libburn/spc.c: assert(d->mdata->valid);
|
||||
spc_select_write_params():
|
||||
Drive was not properly programmed
|
||||
|
||||
Called by (see 39)
|
||||
burn_write_track() by burn_write_session() by burn_disc_write_sync()
|
||||
burn_disc_write_sync() indirectly by API burn_disc_write()
|
||||
|
||||
: Severe Libburn Error
|
||||
=> caught in burn_disc_write() now
|
||||
|
||||
ts A61007
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
++ 41) libburn/spc.c: assert(0);
|
||||
spc_block_type():
|
||||
Unknown value with enum burn_block_types
|
||||
|
||||
Called by spc_select_write_params, uses burn_write_opts.block_type,
|
||||
set by API burn_write_opts_set_write_type()
|
||||
|
||||
: Severe Application Error
|
||||
=> catch in API burn_write_opts_set_write_type
|
||||
by calling spc_block_type()
|
||||
=> delete assert
|
||||
|
||||
ts A61007
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
++ 42) libburn/structure.c: assert(!(pos > BURN_POS_END));\
|
||||
macro RESIZE
|
||||
An illegal list index is given by the app.
|
||||
|
||||
( TO->NEW##s obviusly means to append "s" to cpp result of TO->NEW )
|
||||
Used by API burn_session_add_track() and API burn_disc_add_session()
|
||||
|
||||
: Severe Application Error
|
||||
=> replace assert by if-and-return-0
|
||||
|
||||
ts A61008
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
++ 43) libburn/structure.c: assert(s->track != NULL);
|
||||
API burn_session_remove_track()
|
||||
An application supplied pointer is NULL
|
||||
|
||||
: Severe Application Error
|
||||
=> replace by if-and-return-0
|
||||
|
||||
ts A61008
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
++ 44) libburn/structure.c: assert((country[i] >= '0' || country[i] < '9') &&
|
||||
libburn/structure.c: assert((owner[i] >= '0' || owner[i] < '9') &&
|
||||
libburn/structure.c: assert(year <= 99);
|
||||
libburn/structure.c: assert(serial <= 99999);
|
||||
API burn_track_set_isrc():
|
||||
Illegal texts supplied by application.
|
||||
The logical expression is always true !
|
||||
|
||||
: Severe Application Error
|
||||
=> issue LIBDAX_MSGS_SEV_SORRY and return
|
||||
=> delete assert
|
||||
=> delete assert 28) in char_to_isrc()
|
||||
|
||||
ts A61008
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
++ 45) libburn/toc.c: assert(0); /* unhandled! find out ccd's
|
||||
static write_clonecd2():
|
||||
|
||||
- defunct -, - unused -
|
||||
|
||||
=> mangle assert
|
||||
|
||||
ts A61008
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
++ 46) libburn/toc.c: assert(d->busy);
|
||||
toc_find_modes():
|
||||
The drive to work on is not marked busy
|
||||
|
||||
Called by mmc_read_toc() alias read_toc() by ... burn_drive_grab()
|
||||
|
||||
: Severe Libburn Error
|
||||
=> to be prevented on the higher levels
|
||||
=> delete assert
|
||||
|
||||
ts A61008
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
++ 47) libburn/util.c: assert(s);
|
||||
burn_strdup()
|
||||
Abort on NULL string which would elsewise cause a SIGSEGV
|
||||
|
||||
Used once in enumerate_common() with a string that worked with open(2) before
|
||||
|
||||
: Severe Libburn Error
|
||||
=> delete assert
|
||||
|
||||
ts A61008
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
++ 48) libburn/util.c: assert(s);
|
||||
burn_strndup(): - unused -
|
||||
Same as 47
|
||||
|
||||
: Severe Libburn Error
|
||||
=> return NULL
|
||||
=> delete assert
|
||||
|
||||
ts A61008
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
++ 49) libburn/util.c: assert(n > 0);
|
||||
burn_strndup(): - unused -
|
||||
Prevent problems by negative copy length
|
||||
|
||||
: Severe Libburn Error
|
||||
=> return NULL
|
||||
=> delete assert
|
||||
|
||||
ts A61008
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
++ 50) libburn/write.c: assert(0);
|
||||
static type_to_ctrl():
|
||||
Unsuitable mode to be converted into "ctrl"
|
||||
Called by static type_to_form() finally burn_create_toc_entries()
|
||||
|
||||
: Severe Application Error
|
||||
=> to be caught in burn_track_define_data by calling for test type_to_form()
|
||||
=> return -1;
|
||||
|
||||
ts A61008
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
++ 51) libburn/write.c: assert(0);
|
||||
libburn/write.c: assert(0); /* XXX someone's gonna want this sometime */
|
||||
static type_to_form():
|
||||
Does not like BURN_MODE0 or BURN_MODE2 but tolerates unknown modes
|
||||
|
||||
Called by static burn_create_toc_entries() by burn_disc_write_sync()
|
||||
|
||||
: Undocumented Libburn Restriction
|
||||
=> set *form = -1 , *ctladr = 0xff , return
|
||||
=> make function non-static
|
||||
=> call for test in API burn_track_define_data()
|
||||
|
||||
ts A61009
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
++ 52) libburn/write.c: assert(ptr);
|
||||
static add_cue():
|
||||
realloc() failed
|
||||
|
||||
Called by burn_create_toc_entries() by burn_disc_write_sync()
|
||||
(burn_create_toc_entries is ignorant towards own potential memory problems)
|
||||
(This could possibly really stay an abort() because the reason is
|
||||
a plain failure of the system's memory management.)
|
||||
|
||||
: Severe System Error
|
||||
=> change return type of add_cue to int
|
||||
=> react on return -1 in burn_create_toc_entries, return NULL on failure
|
||||
=> abort burn_disc_write_sync() on NULL return
|
||||
|
||||
ts A61009
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
++ 53) libburn/write.c: assert(d->toc_entry == NULL);
|
||||
burn_create_toc_entries():
|
||||
Multiple usage of struct burn_drive.toc_entry
|
||||
|
||||
Called by burn_disc_write_sync()
|
||||
This will probably trigger an abort with disc->sessions > 1
|
||||
(disc->sessions is incremented in macro RESIZE() as "NEW##s")
|
||||
|
||||
: Design Problem
|
||||
( => ? disallow multiple sessions ? )
|
||||
=> replace assert by soft means and wait what happens
|
||||
|
||||
ts A61009
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
++ 54) libburn/write.c: assert(0);
|
||||
burn_sector_length():
|
||||
Only BURN_AUDIO, BURN_MODE_RAW, BURN_MODE1 are allowed
|
||||
|
||||
Called by get_sector(), convert_data(), ...
|
||||
|
||||
=> call burn_sector_length() for test in API burn_track_define_data()
|
||||
=> replace assert by -1
|
||||
|
||||
ts A61009
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
107
libburn/async.c
107
libburn/async.c
@ -6,17 +6,11 @@
|
||||
#include "write.h"
|
||||
#include "options.h"
|
||||
#include "async.h"
|
||||
#include "init.h"
|
||||
|
||||
#include <pthread.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/*
|
||||
#include <a ssert.h>
|
||||
*/
|
||||
#include "libdax_msgs.h"
|
||||
extern struct libdax_msgs *libdax_messenger;
|
||||
|
||||
#define SCAN_GOING() (workers && !workers->drive)
|
||||
|
||||
typedef void *(*WorkerFunc) (void *);
|
||||
@ -106,13 +100,7 @@ static void remove_worker(pthread_t th)
|
||||
free(a);
|
||||
break;
|
||||
}
|
||||
|
||||
/* ts A61006 */
|
||||
/* a ssert(a != NULL);/ * wasn't found.. this should not be possible */
|
||||
if (a == NULL)
|
||||
libdax_msgs_submit(libdax_messenger, -1, 0x00020101,
|
||||
LIBDAX_MSGS_SEV_WARNING, LIBDAX_MSGS_PRIO_HIGH,
|
||||
"remove_worker() cannot find given worker item", 0, 0);
|
||||
assert(a != NULL); /* wasn't found.. this should not be possible */
|
||||
}
|
||||
|
||||
static void *scan_worker_func(struct w_list *w)
|
||||
@ -127,41 +115,11 @@ int burn_drive_scan(struct burn_drive_info *drives[], unsigned int *n_drives)
|
||||
struct scan_opts o;
|
||||
int ret = 0;
|
||||
|
||||
/* ts A61006 : moved up from burn_drive_scan_sync , former Assert */
|
||||
if (!burn_running) {
|
||||
libdax_msgs_submit(libdax_messenger, -1, 0x00020109,
|
||||
LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH,
|
||||
"Library not running (on attempt to scan)", 0, 0);
|
||||
*drives = NULL;
|
||||
*n_drives = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* cant be anything working! */
|
||||
|
||||
/* ts A61006 */
|
||||
/* a ssert(!(workers && workers->drive)); */
|
||||
if (workers != NULL && workers->drive != NULL) {
|
||||
drive_is_active:;
|
||||
libdax_msgs_submit(libdax_messenger, -1, 0x00020102,
|
||||
LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
|
||||
"A drive operation is still going on (want to scan)",
|
||||
0, 0);
|
||||
*drives = NULL;
|
||||
*n_drives = 0;
|
||||
return -1;
|
||||
}
|
||||
assert(!(workers && workers->drive));
|
||||
|
||||
if (!workers) {
|
||||
/* start it */
|
||||
|
||||
/* ts A61007 : test moved up from burn_drive_scan_sync()
|
||||
was burn_wait_all() */
|
||||
if (!burn_drives_are_clear())
|
||||
goto drive_is_active;
|
||||
*drives = NULL;
|
||||
*n_drives = 0;
|
||||
|
||||
o.drives = drives;
|
||||
o.n_drives = n_drives;
|
||||
o.done = 0;
|
||||
@ -170,17 +128,7 @@ drive_is_active:;
|
||||
/* its done */
|
||||
ret = workers->u.scan.done;
|
||||
remove_worker(workers->thread);
|
||||
|
||||
/* ts A61006 */
|
||||
/* a ssert(workers == NULL); */
|
||||
if (workers != NULL) {
|
||||
libdax_msgs_submit(libdax_messenger, -1, 0x00020101,
|
||||
LIBDAX_MSGS_SEV_WARNING, LIBDAX_MSGS_PRIO_HIGH,
|
||||
"After scan a drive operation is still going on",
|
||||
0, 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
assert(workers == NULL);
|
||||
} else {
|
||||
/* still going */
|
||||
}
|
||||
@ -198,25 +146,9 @@ void burn_disc_erase(struct burn_drive *drive, int fast)
|
||||
{
|
||||
struct erase_opts o;
|
||||
|
||||
/* ts A61006 */
|
||||
/* a ssert(drive); */
|
||||
/* a ssert(!SCAN_GOING()); */
|
||||
/* a ssert(!find_worker(drive)); */
|
||||
if((drive == NULL)) {
|
||||
libdax_msgs_submit(libdax_messenger, drive->global_index,
|
||||
0x00020104,
|
||||
LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
|
||||
"NULL pointer caught in burn_disc_erase", 0, 0);
|
||||
return;
|
||||
}
|
||||
if ((SCAN_GOING()) || find_worker(drive)) {
|
||||
libdax_msgs_submit(libdax_messenger, drive->global_index,
|
||||
0x00020102,
|
||||
LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
|
||||
"A drive operation is still going on (want to erase)",
|
||||
0, 0);
|
||||
return;
|
||||
}
|
||||
assert(drive);
|
||||
assert(!SCAN_GOING());
|
||||
assert(!find_worker(drive));
|
||||
|
||||
o.drive = drive;
|
||||
o.fast = fast;
|
||||
@ -239,29 +171,8 @@ void burn_disc_write(struct burn_write_opts *opts, struct burn_disc *disc)
|
||||
{
|
||||
struct write_opts o;
|
||||
|
||||
/* ts A61006 */
|
||||
/* a ssert(!SCAN_GOING()); */
|
||||
/* a ssert(!find_worker(opts->drive)); */
|
||||
if ((SCAN_GOING()) || find_worker(opts->drive)) {
|
||||
libdax_msgs_submit(libdax_messenger, opts->drive->global_index,
|
||||
0x00020102,
|
||||
LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
|
||||
"A drive operation is still going on (want to write)",
|
||||
0, 0);
|
||||
return;
|
||||
}
|
||||
/* ts A61007 : obsolete Assert in spc_select_write_params() */
|
||||
if (!opts->drive->mdata->valid) {
|
||||
libdax_msgs_submit(libdax_messenger,
|
||||
opts->drive->global_index, 0x00020113,
|
||||
LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
|
||||
"Drive capabilities not inquired yet", 0, 0);
|
||||
return;
|
||||
}
|
||||
/* ts A61009 : obsolete Assert in sector_headers() */
|
||||
if (! burn_disc_write_is_ok(opts, disc)) /* issues own msgs */
|
||||
return;
|
||||
|
||||
assert(!SCAN_GOING());
|
||||
assert(!find_worker(opts->drive));
|
||||
o.drive = opts->drive;
|
||||
o.opts = opts;
|
||||
o.disc = disc;
|
||||
|
@ -1,212 +0,0 @@
|
||||
/*
|
||||
cleanup.c , Copyright 2006 Thomas Schmitt <scdbackup@gmx.net>
|
||||
|
||||
A signal handler which cleans up an application and exits.
|
||||
|
||||
Provided under GPL license within GPL projects, BSD license elsewise.
|
||||
*/
|
||||
|
||||
/*
|
||||
cc -g -o cleanup -DCleanup_standalonE cleanup.c
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <signal.h>
|
||||
typedef void (*sighandler_t)(int);
|
||||
|
||||
|
||||
#include "cleanup.h"
|
||||
|
||||
|
||||
#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, SIGPOLL, 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", "SIGPOLL", "SIGPROF", "SIGSYS", "SIGTRAP",
|
||||
"SIGVTALRM", "SIGXCPU", "SIGXFSZ", "@"
|
||||
};
|
||||
static int signal_list_count= 24;
|
||||
|
||||
/* 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;
|
||||
|
||||
static void *cleanup_app_handle= NULL;
|
||||
static Cleanup_app_handler_T cleanup_app_handler= NULL;
|
||||
static int cleanup_perform_app_handler_first= 0;
|
||||
|
||||
|
||||
static int Cleanup_handler_exit(int exit_value, int signum, int flag)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if(cleanup_msg[0]!=0)
|
||||
fprintf(stderr,"\n%s\n",cleanup_msg);
|
||||
if(cleanup_perform_app_handler_first)
|
||||
if(cleanup_app_handler!=NULL) {
|
||||
ret= (*cleanup_app_handler)(cleanup_app_handle,signum,0);
|
||||
if(ret==2 || ret==-2)
|
||||
return(2);
|
||||
}
|
||||
if(cleanup_exiting) {
|
||||
fprintf(stderr,"cleanup: ABORT : repeat by pid=%d, signum=%d\n",
|
||||
getpid(),signum);
|
||||
return(0);
|
||||
}
|
||||
cleanup_exiting= 1;
|
||||
alarm(0);
|
||||
if(!cleanup_perform_app_handler_first)
|
||||
if(cleanup_app_handler!=NULL) {
|
||||
ret= (*cleanup_app_handler)(cleanup_app_handle,signum,0);
|
||||
if(ret==2 || ret==-2)
|
||||
return(2);
|
||||
}
|
||||
exit(exit_value);
|
||||
}
|
||||
|
||||
|
||||
static void Cleanup_handler_generic(int signum)
|
||||
{
|
||||
int i;
|
||||
|
||||
sprintf(cleanup_msg,"UNIX-SIGNAL caught: %d errno= %d",signum,errno);
|
||||
for(i= 0; i<signal_list_count; i++)
|
||||
if(signum==signal_list[i]) {
|
||||
sprintf(cleanup_msg,"UNIX-SIGNAL: %s errno= %d",
|
||||
signal_name_list[i],errno);
|
||||
break;
|
||||
}
|
||||
Cleanup_handler_exit(1,signum,0);
|
||||
}
|
||||
|
||||
|
||||
int Cleanup_set_handlers(void *handle, Cleanup_app_handler_T handler, int flag)
|
||||
/*
|
||||
bit0= set to default handlers
|
||||
bit1= set to ignore
|
||||
bit2= set cleanup_perform_app_handler_first
|
||||
bit3= set SIGABRT to handler (makes sense with bits 0 or 1)
|
||||
*/
|
||||
{
|
||||
int i,j,max_sig= -1,min_sig= 0x7fffffff;
|
||||
sighandler_t sig_handler;
|
||||
|
||||
cleanup_msg[0]= 0;
|
||||
cleanup_app_handle= handle;
|
||||
cleanup_app_handler= handler;
|
||||
|
||||
/* <<< make cleanup_exiting thread safe to get rid of this */
|
||||
if(flag&4)
|
||||
cleanup_perform_app_handler_first= 1;
|
||||
|
||||
|
||||
if(flag&1)
|
||||
sig_handler= SIG_DFL;
|
||||
else if(flag&2)
|
||||
sig_handler= SIG_IGN;
|
||||
else
|
||||
sig_handler= Cleanup_handler_generic;
|
||||
/* set all signal numbers between the lowest and highest in the list
|
||||
except those in the non-signal list */
|
||||
for(i= 0; i<signal_list_count; i++) {
|
||||
if(signal_list[i]>max_sig)
|
||||
max_sig= signal_list[i];
|
||||
if(signal_list[i]<min_sig)
|
||||
min_sig= signal_list[i];
|
||||
}
|
||||
for(i= min_sig; i<=max_sig; i++) {
|
||||
for(j= 0; j<non_signal_list_count; j++)
|
||||
if(i==non_signal_list[j])
|
||||
break;
|
||||
if(j>=non_signal_list_count) {
|
||||
if(i==SIGABRT && (flag&8))
|
||||
signal(i,Cleanup_handler_generic);
|
||||
else
|
||||
signal(i,sig_handler);
|
||||
}
|
||||
}
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
#ifdef Cleanup_standalonE
|
||||
|
||||
struct Demo_apP {
|
||||
char *msg;
|
||||
};
|
||||
|
||||
|
||||
int Demo_app_handler(struct Demo_apP *demoapp, int signum, int flag)
|
||||
{
|
||||
printf("Handling exit of demo application on signal %d. msg=\"%s\"\n",
|
||||
signum,demoapp->msg);
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
main()
|
||||
{
|
||||
struct Demo_apP demoapp;
|
||||
|
||||
demoapp.msg= "Good Bye";
|
||||
Cleanup_set_handlers(&demoapp,(Cleanup_app_handler_T) Demo_app_handler,0);
|
||||
|
||||
if(1) { /* change to 0 in order to wait for external signals */
|
||||
char *cpt= NULL,c;
|
||||
printf("Intentionally provoking SIGSEGV ...\n");
|
||||
c= *cpt;
|
||||
} else {
|
||||
printf("killme: %d\n",getpid());
|
||||
sleep(3600);
|
||||
}
|
||||
|
||||
Cleanup_set_handlers(NULL,NULL,1);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
#endif /* Cleanup_standalonE */
|
@ -1,34 +0,0 @@
|
||||
/*
|
||||
cleanup.c , Copyright 2006 Thomas Schmitt <scdbackup@gmx.net>
|
||||
|
||||
A signal handler which cleans up an application and exits.
|
||||
|
||||
Provided under GPL license within GPL projects, BSD license elsewise.
|
||||
*/
|
||||
|
||||
#ifndef Cleanup_includeD
|
||||
#define Cleanup_includeD 1
|
||||
|
||||
|
||||
/** Layout of an application provided cleanup function using an application
|
||||
provided handle as first argument and the signal number as second
|
||||
argument. The third argument is a flag bit field with no defined bits yet.
|
||||
If the handler returns 2 or -2 then it has delegated exit() to some other
|
||||
instance and the Cleanup handler shall return rather than exit.
|
||||
*/
|
||||
typedef int (*Cleanup_app_handler_T)(void *, int, int);
|
||||
|
||||
|
||||
/** Establish exiting signal handlers on (hopefully) all signals that are
|
||||
not ignored by default or non-catchable.
|
||||
@param handle Opaque object which knows how to cleanup application
|
||||
@param handler Function which uses handle to perform application cleanup
|
||||
@param flag Control Bitfield
|
||||
bit0= reset to default signal handling
|
||||
*/
|
||||
int Cleanup_set_handlers(void *handle, Cleanup_app_handler_T handler,
|
||||
int flag);
|
||||
|
||||
|
||||
#endif /* ! Cleanup_includeD */
|
||||
|
692
libburn/drive.c
692
libburn/drive.c
@ -1,17 +1,9 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
/* #include <m alloc.h> ts A61013 : not in Linux man 3 malloc */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <malloc.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
|
||||
/* ts A61007 */
|
||||
/* #include <a ssert.h> */
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
@ -19,6 +11,7 @@
|
||||
#include "libburn.h"
|
||||
#include "drive.h"
|
||||
#include "transport.h"
|
||||
#include "message.h"
|
||||
#include "debug.h"
|
||||
#include "init.h"
|
||||
#include "toc.h"
|
||||
@ -27,22 +20,10 @@
|
||||
#include "structure.h"
|
||||
#include "back_hacks.h"
|
||||
|
||||
#include "libdax_msgs.h"
|
||||
extern struct libdax_msgs *libdax_messenger;
|
||||
|
||||
static struct burn_drive drive_array[255];
|
||||
static int drivetop = -1;
|
||||
|
||||
/* ts A61021 : the unspecific part of sg.c:enumerate_common()
|
||||
*/
|
||||
int burn_setup_drive(struct burn_drive *d, char *fname)
|
||||
{
|
||||
d->devname = burn_strdup(fname);
|
||||
memset(&d->params, 0, sizeof(struct params));
|
||||
d->released = 1;
|
||||
d->status = BURN_DISC_UNREADY;
|
||||
return 1;
|
||||
}
|
||||
int burn_drive_is_open(struct burn_drive *d);
|
||||
|
||||
/* ts A60904 : ticket 62, contribution by elmom */
|
||||
/* splitting former burn_drive_free() (which freed all, into two calls) */
|
||||
@ -52,11 +33,10 @@ void burn_drive_free(struct burn_drive *d)
|
||||
return;
|
||||
/* ts A60822 : close open fds before forgetting them */
|
||||
if (burn_drive_is_open(d))
|
||||
d->release(d);
|
||||
close(d->fd);
|
||||
free((void *) d->idata);
|
||||
free((void *) d->mdata);
|
||||
if(d->toc_entry != NULL)
|
||||
free((void *) d->toc_entry);
|
||||
free((void *) d->toc_entry);
|
||||
free(d->devname);
|
||||
d->global_index = -1;
|
||||
}
|
||||
@ -75,8 +55,12 @@ void burn_drive_free_all(void)
|
||||
/* ts A60822 */
|
||||
int burn_drive_is_open(struct burn_drive *d)
|
||||
{
|
||||
/* ts A61021 : moved decision to sg.c */
|
||||
return d->drive_is_open(d);
|
||||
/* a bit more detailed case distinction than needed */
|
||||
if (d->fd == -1337)
|
||||
return 0;
|
||||
if (d->fd < 0)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@ -160,11 +144,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;
|
||||
|
||||
d->status = BURN_DISC_BLANK;
|
||||
if (d->mdata->cdr_write || d->mdata->cdrw_write ||
|
||||
d->mdata->dvdr_write || d->mdata->dvdram_write) {
|
||||
|
||||
@ -188,8 +168,6 @@ int burn_drive_grab(struct burn_drive *d, int le)
|
||||
old_erasable = new_erasable;
|
||||
|
||||
d->read_disc_info(d);
|
||||
if(d->status == BURN_DISC_UNSUITABLE)
|
||||
break;
|
||||
|
||||
new_speed = burn_drive_get_write_speed(d);
|
||||
new_erasable = burn_disc_erasable(d);
|
||||
@ -266,77 +244,13 @@ struct burn_drive *burn_drive_register(struct burn_drive *d)
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* unregister most recently registered drive */
|
||||
int burn_drive_unregister(struct burn_drive *d)
|
||||
{
|
||||
if(d->global_index != drivetop)
|
||||
return 0;
|
||||
burn_drive_free(d);
|
||||
drivetop--;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* ts A61021 : after-setup activities from sg.c:enumerate_common()
|
||||
*/
|
||||
struct burn_drive *burn_drive_finish_enum(struct burn_drive *d)
|
||||
{
|
||||
struct burn_drive *t;
|
||||
/* ts A60821
|
||||
<<< debug: for tracing calls which might use open drive fds */
|
||||
int mmc_function_spy(char * text);
|
||||
|
||||
t = burn_drive_register(d);
|
||||
|
||||
/* ts A60821 */
|
||||
mmc_function_spy("enumerate_common : -------- doing grab");
|
||||
|
||||
/* try to get the drive info */
|
||||
if (t->grab(t)) {
|
||||
burn_print(2, "getting drive info\n");
|
||||
t->getcaps(t);
|
||||
t->unlock(t);
|
||||
t->released = 1;
|
||||
} else {
|
||||
burn_print(2, "unable to grab new located drive\n");
|
||||
burn_drive_unregister(t);
|
||||
t = NULL;
|
||||
}
|
||||
|
||||
/* ts A60821 */
|
||||
mmc_function_spy("enumerate_common : ----- would release ");
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
|
||||
void burn_drive_release(struct burn_drive *d, int le)
|
||||
{
|
||||
if (d->released) {
|
||||
/* ts A61007 */
|
||||
/* burn_print(1, "second release on drive!\n"); */
|
||||
libdax_msgs_submit(libdax_messenger,
|
||||
d->global_index, 0x00020105,
|
||||
LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
|
||||
"Drive is already released", 0, 0);
|
||||
return;
|
||||
}
|
||||
if (d->released)
|
||||
burn_print(1, "second release on drive!\n");
|
||||
|
||||
/* ts A61007 */
|
||||
/* ts A60906: one should not assume BURN_DRIVE_IDLE == 0 */
|
||||
/* a ssert(d->busy == BURN_DRIVE_IDLE); */
|
||||
if (d->busy != BURN_DRIVE_IDLE) {
|
||||
libdax_msgs_submit(libdax_messenger,
|
||||
d->global_index, 0x00020106,
|
||||
LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
|
||||
"Drive is busy on attempt to close", 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
/* ts A61020 : mark media info as invalid */
|
||||
d->start_lba= -2000000000;
|
||||
d->end_lba= -2000000000;
|
||||
assert(d->busy == BURN_DRIVE_IDLE);
|
||||
|
||||
d->unlock(d);
|
||||
if (le)
|
||||
@ -346,7 +260,7 @@ void burn_drive_release(struct burn_drive *d, int le)
|
||||
|
||||
d->status = BURN_DISC_UNREADY;
|
||||
d->released = 1;
|
||||
if (d->toc_entry != NULL)
|
||||
if (d->toc_entry)
|
||||
free(d->toc_entry);
|
||||
d->toc_entry = NULL;
|
||||
d->toc_entries = 0;
|
||||
@ -356,26 +270,6 @@ void burn_drive_release(struct burn_drive *d, int le)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ts A61007 : former void burn_wait_all() */
|
||||
int burn_drives_are_clear(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = burn_drive_count() - 1; i >= 0; --i) {
|
||||
/* ts A60904 : ticket 62, contribution by elmom */
|
||||
if (drive_array[i].global_index == -1)
|
||||
continue;
|
||||
if (drive_array[i].released)
|
||||
continue;
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
void burn_wait_all(void)
|
||||
{
|
||||
unsigned int i;
|
||||
@ -391,20 +285,16 @@ void burn_wait_all(void)
|
||||
if (d->global_index==-1)
|
||||
continue;
|
||||
|
||||
a ssert(d->released);
|
||||
assert(d->released);
|
||||
}
|
||||
if (!finished)
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void burn_disc_erase_sync(struct burn_drive *d, int fast)
|
||||
{
|
||||
/* ts A60924 : libburn/message.c gets obsoleted
|
||||
burn_message_clear_queue();
|
||||
*/
|
||||
|
||||
burn_print(1, "erasing drive %s %s\n", d->idata->vendor,
|
||||
d->idata->product);
|
||||
@ -439,17 +329,7 @@ void burn_disc_erase_sync(struct burn_drive *d, int fast)
|
||||
|
||||
enum burn_disc_status burn_disc_get_status(struct burn_drive *d)
|
||||
{
|
||||
/* ts A61007 */
|
||||
/* a ssert(!d->released); */
|
||||
if (d->released) {
|
||||
libdax_msgs_submit(libdax_messenger,
|
||||
d->global_index, 0x00020108,
|
||||
LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
|
||||
"Drive is not grabbed on disc status inquiry",
|
||||
0, 0);
|
||||
return BURN_DISC_UNGRABBED;
|
||||
}
|
||||
|
||||
assert(!d->released);
|
||||
return d->status;
|
||||
}
|
||||
|
||||
@ -474,17 +354,14 @@ void burn_drive_cancel(struct burn_drive *d)
|
||||
pthread_mutex_unlock(&d->access_lock);
|
||||
}
|
||||
|
||||
/* ts A61007 : defunct because unused */
|
||||
#if 0
|
||||
int burn_drive_get_block_types(struct burn_drive *d,
|
||||
enum burn_write_types write_type)
|
||||
{
|
||||
burn_print(12, "write type: %d\n", write_type);
|
||||
a ssert( /* (write_type >= BURN_WRITE_PACKET) && */
|
||||
assert( /* (write_type >= BURN_WRITE_PACKET) && */
|
||||
(write_type <= BURN_WRITE_RAW));
|
||||
return d->block_types[write_type];
|
||||
}
|
||||
#endif
|
||||
|
||||
static void strip_spaces(char *str)
|
||||
{
|
||||
@ -509,18 +386,14 @@ static void strip_spaces(char *str)
|
||||
|
||||
static int drive_getcaps(struct burn_drive *d, struct burn_drive_info *out)
|
||||
{
|
||||
struct burn_scsi_inquiry_data *id;
|
||||
|
||||
/* ts A61007 : now prevented in enumerate_common() */
|
||||
#if 0
|
||||
a ssert(d->idata);
|
||||
a ssert(d->mdata);
|
||||
#endif
|
||||
struct scsi_inquiry_data *id;
|
||||
|
||||
assert(d->idata);
|
||||
assert(d->mdata);
|
||||
if (!d->idata->valid || !d->mdata->valid)
|
||||
return 0;
|
||||
|
||||
id = (struct burn_scsi_inquiry_data *)d->idata;
|
||||
id = (struct scsi_inquiry_data *)d->idata;
|
||||
|
||||
memcpy(out->vendor, id->vendor, sizeof(id->vendor));
|
||||
strip_spaces(out->vendor);
|
||||
@ -562,27 +435,31 @@ int burn_drive_scan_sync(struct burn_drive_info *drives[],
|
||||
static int scanning = 0, scanned = 0, found = 0;
|
||||
static unsigned num_scanned = 0, count = 0;
|
||||
unsigned int i;
|
||||
#ifdef Libburn_ticket_62_enable_redundant_asserT
|
||||
struct burn_drive *d;
|
||||
#endif
|
||||
|
||||
/* ts A61007 : moved up to burn_drive_scan() */
|
||||
/* a ssert(burn_running); */
|
||||
assert(burn_running);
|
||||
|
||||
if (!scanning) {
|
||||
scanning = 1;
|
||||
|
||||
/* ts A61007 : test moved up to burn_drive_scan()
|
||||
burn_wait_all() is obsoleted */
|
||||
#if 0
|
||||
/* make sure the drives aren't in use */
|
||||
burn_wait_all(); /* make sure the queue cleans up
|
||||
before checking for the released
|
||||
state */
|
||||
#endif /* 0 */
|
||||
|
||||
/* ts A60904 : ticket 62, contribution by elmom */
|
||||
/* this is redundant with what is done in burn_wait_all() */
|
||||
#ifdef Libburn_ticket_62_enable_redundant_asserT
|
||||
d = drive_array;
|
||||
count = burn_drive_count();
|
||||
for (i = 0; i < count; ++i, ++d)
|
||||
assert(d->released == 1);
|
||||
#endif /* Libburn_ticket_62_enable_redundant_asserT */
|
||||
|
||||
/* refresh the lib's drives */
|
||||
|
||||
/* ts A61115 : formerly sg_enumerate(); ata_enumerate(); */
|
||||
scsi_enumerate_drives();
|
||||
|
||||
sg_enumerate();
|
||||
ata_enumerate();
|
||||
count = burn_drive_count();
|
||||
if (count)
|
||||
*drives =
|
||||
@ -624,17 +501,19 @@ void burn_drive_info_free(struct burn_drive_info drive_infos[])
|
||||
burn_drive_info is not the manager of burn_drive but only its
|
||||
spokesperson. To my knowlege drive_infos from burn_drive_scan()
|
||||
are not memorized globally. */
|
||||
if(drive_infos != NULL)
|
||||
if(drive_infos != NULL) /* and this NULL test is deadly necessary */
|
||||
free((void *) drive_infos);
|
||||
|
||||
burn_drive_free_all();
|
||||
}
|
||||
|
||||
/* ts A61001 : internal call */
|
||||
int burn_drive_forget(struct burn_drive *d, int force)
|
||||
/* Experimental API call */
|
||||
int burn_drive_info_forget(struct burn_drive_info *info, int force)
|
||||
{
|
||||
int occup;
|
||||
struct burn_drive *d;
|
||||
|
||||
d = info->drive;
|
||||
occup = burn_drive_is_occupied(d);
|
||||
/*
|
||||
fprintf(stderr, "libburn: experimental: occup == %d\n",occup);
|
||||
@ -657,18 +536,8 @@ int burn_drive_forget(struct burn_drive *d, int force)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* API call */
|
||||
int burn_drive_info_forget(struct burn_drive_info *info, int force)
|
||||
{
|
||||
return burn_drive_forget(info->drive, force);
|
||||
}
|
||||
|
||||
struct burn_disc *burn_drive_get_disc(struct burn_drive *d)
|
||||
{
|
||||
/* ts A61022: SIGSEGV on calling this function with blank media */
|
||||
if(d->disc == NULL)
|
||||
return NULL;
|
||||
|
||||
d->disc->refcnt++;
|
||||
return d->disc;
|
||||
}
|
||||
@ -700,12 +569,6 @@ int burn_drive_get_write_speed(struct burn_drive *d)
|
||||
return d->mdata->max_write_speed;
|
||||
}
|
||||
|
||||
/* ts A61021 : New API function */
|
||||
int burn_drive_get_min_write_speed(struct burn_drive *d)
|
||||
{
|
||||
return d->mdata->min_write_speed;
|
||||
}
|
||||
|
||||
|
||||
/* ts A51221 */
|
||||
static char *enumeration_whitelist[BURN_DRIVE_WHITELIST_LEN];
|
||||
@ -764,472 +627,39 @@ int burn_drive_scan_and_grab(struct burn_drive_info *drive_infos[], char* adr,
|
||||
fprintf(stderr,"libburn: experimental: burn_drive_scan_and_grab(%s)\n",
|
||||
adr);
|
||||
*/
|
||||
while (1) {
|
||||
ret = burn_drive_scan(drive_infos, &n_drives);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
if (ret > 0)
|
||||
break;
|
||||
while (!burn_drive_scan(drive_infos, &n_drives))
|
||||
usleep(1002);
|
||||
}
|
||||
if (n_drives <= 0)
|
||||
return 0;
|
||||
/*
|
||||
fprintf(stderr, "libburn: experimental: n_drives == %d\n",n_drives);
|
||||
*/
|
||||
|
||||
/* ts A60908 : seems we get rid of this :) */
|
||||
#ifdef Libburn_grab_release_and_grab_agaiN
|
||||
if (load) {
|
||||
/* RIP-14.5 + LITE-ON 48125S produce a false status
|
||||
if tray was unloaded */
|
||||
/* Therefore the first grab is just for loading */
|
||||
ret= burn_drive_grab(drive_infos[0]->drive, 1);
|
||||
if (ret != 1)
|
||||
return -1;
|
||||
burn_drive_release(drive_infos[0]->drive,0);
|
||||
}
|
||||
#endif /* Libburn_grab_release_and_grab_agaiN */
|
||||
|
||||
ret = burn_drive_grab(drive_infos[0]->drive, load);
|
||||
if (ret != 1)
|
||||
return -1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ts A60925 */
|
||||
/** Simple debug message frontend to libdax_msgs_submit().
|
||||
If arg is not NULL, then fmt MUST contain exactly one %s and no
|
||||
other sprintf() %-formatters.
|
||||
*/
|
||||
int burn_drive_adr_debug_msg(char *fmt, char *arg)
|
||||
{
|
||||
int ret;
|
||||
char msg[4096], *msgpt;
|
||||
|
||||
msgpt= msg;
|
||||
if(arg != NULL)
|
||||
sprintf(msg, fmt, arg);
|
||||
else
|
||||
msgpt= fmt;
|
||||
if(libdax_messenger == NULL)
|
||||
return 0;
|
||||
ret = libdax_msgs_submit(libdax_messenger, -1, 0x00000002,
|
||||
LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO,
|
||||
msgpt, 0, 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* ts A60923 */
|
||||
/** Inquire the persistent address of the given drive. */
|
||||
int burn_drive_raw_get_adr(struct burn_drive *d, char adr[])
|
||||
{
|
||||
if (strlen(d->devname) >= BURN_DRIVE_ADR_LEN) {
|
||||
libdax_msgs_submit(libdax_messenger, d->global_index,
|
||||
0x00020110,
|
||||
LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH,
|
||||
"Persistent drive address too long", 0, 0);
|
||||
return -1;
|
||||
}
|
||||
strcpy(adr,d->devname);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ts A60823 - A60923 */
|
||||
/* ts A60823 */
|
||||
/** Inquire the persistent address of the given drive. */
|
||||
int burn_drive_get_adr(struct burn_drive_info *drive_info, char adr[])
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = burn_drive_raw_get_adr(drive_info->drive, adr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* ts A60922 ticket 33 */
|
||||
/** Evaluate wether the given address would be enumerated by libburn */
|
||||
int burn_drive_is_enumerable_adr(char *adr)
|
||||
{
|
||||
return sg_is_enumerable_adr(adr);
|
||||
}
|
||||
|
||||
#define BURN_DRIVE_MAX_LINK_DEPTH 20
|
||||
|
||||
/* ts A60922 ticket 33 */
|
||||
/* Try to find an enumerated address with the given stat.st_rdev number */
|
||||
int burn_drive_resolve_link(char *path, char adr[], int *recursion_count)
|
||||
{
|
||||
int ret;
|
||||
char link_target[4096], msg[4096+100], link_adr[4096], *adrpt;
|
||||
|
||||
burn_drive_adr_debug_msg("burn_drive_resolve_link( %s )", path);
|
||||
if (*recursion_count >= BURN_DRIVE_MAX_LINK_DEPTH) {
|
||||
burn_drive_adr_debug_msg(
|
||||
"burn_drive_resolve_link aborts because link too deep",
|
||||
NULL);
|
||||
return 0;
|
||||
}
|
||||
(*recursion_count)++;
|
||||
ret = readlink(path, link_target, sizeof(link_target));
|
||||
if (ret == -1) {
|
||||
burn_drive_adr_debug_msg("readlink( %s ) returns -1", path);
|
||||
return 0;
|
||||
}
|
||||
if (ret >= sizeof(link_target) - 1) {
|
||||
sprintf(msg,"readlink( %s ) returns %d (too much)", path, ret);
|
||||
burn_drive_adr_debug_msg(msg, NULL);
|
||||
return -1;
|
||||
}
|
||||
link_target[ret] = 0;
|
||||
adrpt= link_target;
|
||||
if (link_target[0] != '/') {
|
||||
strcpy(link_adr, path);
|
||||
if ((adrpt = strrchr(link_adr, '/')) != NULL) {
|
||||
strcpy(adrpt + 1, link_target);
|
||||
adrpt = link_adr;
|
||||
} else
|
||||
adrpt = link_target;
|
||||
}
|
||||
ret = burn_drive_convert_fs_adr_sub(adrpt, adr, recursion_count);
|
||||
sprintf(msg,"burn_drive_convert_fs_adr( %s ) returns %d",
|
||||
link_target, ret);
|
||||
burn_drive_adr_debug_msg(msg, NULL);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* ts A60922 - A61014 ticket 33 */
|
||||
/* Try to find an enumerated address with the given stat.st_rdev number */
|
||||
int burn_drive_find_devno(dev_t devno, char adr[])
|
||||
{
|
||||
char fname[4096], msg[4096+100];
|
||||
int ret = 0, first = 1;
|
||||
struct stat stbuf;
|
||||
burn_drive_enumerator_t enm;
|
||||
|
||||
while (1) {
|
||||
ret = sg_give_next_adr(&enm, fname, sizeof(fname), first);
|
||||
if(ret <= 0)
|
||||
break;
|
||||
first = 0;
|
||||
ret = stat(fname, &stbuf);
|
||||
if(ret == -1)
|
||||
continue;
|
||||
if(devno != stbuf.st_rdev)
|
||||
continue;
|
||||
if(strlen(fname) >= BURN_DRIVE_ADR_LEN)
|
||||
return -1;
|
||||
|
||||
sprintf(msg, "burn_drive_find_devno( 0x%lX ) found %s",
|
||||
(long) devno, fname);
|
||||
burn_drive_adr_debug_msg(msg, NULL);
|
||||
strcpy(adr, fname);
|
||||
{ ret = 1; goto ex;}
|
||||
}
|
||||
ret = 0;
|
||||
ex:;
|
||||
if (first == 0)
|
||||
sg_give_next_adr(&enm, fname, sizeof(fname), -1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* ts A60923 */
|
||||
/** Try to obtain host,channel,target,lun from path.
|
||||
@return 1 = success , 0 = failure , -1 = severe error
|
||||
*/
|
||||
int burn_drive_obtain_scsi_adr(char *path,
|
||||
int *bus_no, int *host_no, int *channel_no,
|
||||
int *target_no, int *lun_no)
|
||||
{
|
||||
int ret, i;
|
||||
char adr[BURN_DRIVE_ADR_LEN];
|
||||
|
||||
/* open drives cannot be inquired by sg_obtain_scsi_adr() */
|
||||
for (i = 0; i < drivetop + 1; i++) {
|
||||
if (drive_array[i].global_index < 0)
|
||||
continue;
|
||||
ret = burn_drive_raw_get_adr(&(drive_array[i]),adr);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
if (ret == 0)
|
||||
continue;
|
||||
if (strcmp(adr, path) == 0) {
|
||||
*host_no = drive_array[i].host;
|
||||
*channel_no = drive_array[i].channel;
|
||||
*target_no = drive_array[i].id;
|
||||
*lun_no = drive_array[i].lun;
|
||||
*bus_no = drive_array[i].bus_no;
|
||||
if (*host_no < 0 || *channel_no < 0 ||
|
||||
*target_no < 0 || *lun_no < 0)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
ret = sg_obtain_scsi_adr(path, bus_no, host_no, channel_no,
|
||||
target_no, lun_no);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* ts A60923 */
|
||||
int burn_drive_convert_scsi_adr(int bus_no, int host_no, int channel_no,
|
||||
int target_no, int lun_no, char adr[])
|
||||
{
|
||||
char fname[4096],msg[4096+100];
|
||||
int ret = 0, first = 1, i_bus_no = -1;
|
||||
int i_host_no = -1, i_channel_no = -1, i_target_no = -1, i_lun_no = -1;
|
||||
burn_drive_enumerator_t enm;
|
||||
|
||||
sprintf(msg,"burn_drive_convert_scsi_adr( %d,%d,%d,%d,%d )",
|
||||
bus_no, host_no, channel_no, target_no, lun_no);
|
||||
burn_drive_adr_debug_msg(msg, NULL);
|
||||
|
||||
while (1) {
|
||||
ret= sg_give_next_adr(&enm, fname, sizeof(fname), first);
|
||||
if(ret <= 0)
|
||||
break;
|
||||
first = 0;
|
||||
ret = burn_drive_obtain_scsi_adr(fname, &i_bus_no, &i_host_no,
|
||||
&i_channel_no, &i_target_no, &i_lun_no);
|
||||
if(ret <= 0)
|
||||
continue;
|
||||
if(bus_no >=0 && i_bus_no != bus_no)
|
||||
continue;
|
||||
if(host_no >=0 && i_host_no != host_no)
|
||||
continue;
|
||||
if(channel_no >= 0 && i_channel_no != channel_no)
|
||||
continue;
|
||||
if(target_no >= 0 && i_target_no != target_no)
|
||||
continue;
|
||||
if(lun_no >= 0 && i_lun_no != lun_no)
|
||||
continue;
|
||||
if(strlen(fname) >= BURN_DRIVE_ADR_LEN)
|
||||
{ ret = -1; goto ex;}
|
||||
burn_drive_adr_debug_msg(
|
||||
"burn_drive_convert_scsi_adr() found %s", fname);
|
||||
strcpy(adr, fname);
|
||||
{ ret = 1; goto ex;}
|
||||
}
|
||||
ret = 0;
|
||||
ex:;
|
||||
if (first == 0)
|
||||
sg_give_next_adr(&enm, fname, sizeof(fname), -1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* ts A60922 ticket 33 */
|
||||
/* Try to find an enumerated address with the same host,channel,target,lun
|
||||
as path */
|
||||
int burn_drive_find_scsi_equiv(char *path, char adr[])
|
||||
{
|
||||
int ret = 0;
|
||||
int bus_no, host_no, channel_no, target_no, lun_no;
|
||||
char msg[4096];
|
||||
|
||||
ret = burn_drive_obtain_scsi_adr(path, &bus_no, &host_no, &channel_no,
|
||||
&target_no, &lun_no);
|
||||
if(ret <= 0) {
|
||||
sprintf(msg,"burn_drive_obtain_scsi_adr( %s ) returns %d\n",
|
||||
path, ret);
|
||||
burn_drive_adr_debug_msg(msg, NULL);
|
||||
return 0;
|
||||
}
|
||||
sprintf(msg, "burn_drive_find_scsi_equiv( %s ) : (%d),%d,%d,%d,%d",
|
||||
path, bus_no, host_no, channel_no, target_no, lun_no);
|
||||
burn_drive_adr_debug_msg(msg, NULL);
|
||||
|
||||
ret= burn_drive_convert_scsi_adr(-1, host_no, channel_no, target_no,
|
||||
lun_no, adr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* ts A60922 ticket 33 */
|
||||
/** Try to convert a given existing filesystem address into a persistent drive
|
||||
address. */
|
||||
int burn_drive_convert_fs_adr_sub(char *path, char adr[], int *rec_count)
|
||||
{
|
||||
int ret;
|
||||
struct stat stbuf;
|
||||
|
||||
burn_drive_adr_debug_msg("burn_drive_convert_fs_adr( %s )", path);
|
||||
if(burn_drive_is_enumerable_adr(path)) {
|
||||
if(strlen(path) >= BURN_DRIVE_ADR_LEN)
|
||||
return -1;
|
||||
burn_drive_adr_debug_msg(
|
||||
"burn_drive_is_enumerable_adr( %s ) is true", path);
|
||||
strcpy(adr, path);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(lstat(path, &stbuf) == -1) {
|
||||
burn_drive_adr_debug_msg("lstat( %s ) returns -1", path);
|
||||
return 0;
|
||||
}
|
||||
if((stbuf.st_mode & S_IFMT) == S_IFLNK) {
|
||||
ret = burn_drive_resolve_link(path, adr, rec_count);
|
||||
if(ret > 0)
|
||||
return 1;
|
||||
burn_drive_adr_debug_msg("link fallback via stat( %s )", path);
|
||||
if(stat(path, &stbuf) == -1) {
|
||||
burn_drive_adr_debug_msg("stat( %s ) returns -1",path);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if((stbuf.st_mode&S_IFMT) == S_IFBLK ||
|
||||
(stbuf.st_mode&S_IFMT) == S_IFCHR) {
|
||||
ret = burn_drive_find_devno(stbuf.st_rdev, adr);
|
||||
if(ret > 0)
|
||||
return 1;
|
||||
ret = burn_drive_find_scsi_equiv(path, adr);
|
||||
if(ret > 0)
|
||||
return 1;
|
||||
}
|
||||
burn_drive_adr_debug_msg("Nothing found for %s", path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Try to convert a given existing filesystem address into a persistent drive
|
||||
address. */
|
||||
int burn_drive_convert_fs_adr(char *path, char adr[])
|
||||
{
|
||||
int ret, rec_count = 0;
|
||||
|
||||
ret = burn_drive_convert_fs_adr_sub(path, adr, &rec_count);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/** A pacifier function suitable for burn_abort.
|
||||
@param handle If not NULL, a pointer to a text suitable for printf("%s")
|
||||
*/
|
||||
int burn_abort_pacifier(void *handle, int patience, int elapsed)
|
||||
{
|
||||
char *prefix= "libburn : ";
|
||||
|
||||
if(handle!=NULL)
|
||||
prefix= handle;
|
||||
fprintf(stderr,
|
||||
"\r%sABORT : Waiting for drive to finish ( %d s, %d max)",
|
||||
(char *) prefix, elapsed, patience);
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
/** Abort any running drive operation and finis libburn.
|
||||
@param patience Maximum number of seconds to wait for drives to finish
|
||||
@param pacifier_func Function to produce appeasing messages. See
|
||||
burn_abort_pacifier() for an example.
|
||||
@return 1 ok, all went well
|
||||
0 had to leave a drive in unclean state
|
||||
<0 severe error, do no use libburn again
|
||||
*/
|
||||
int burn_abort(int patience,
|
||||
int (*pacifier_func)(void *handle, int patience, int elapsed),
|
||||
void *handle)
|
||||
{
|
||||
int ret, i, occup, still_not_done= 1, pacifier_off= 0, first_round= 1;
|
||||
unsigned long wait_grain= 100000;
|
||||
time_t start_time, current_time, pacifier_time, end_time;
|
||||
|
||||
current_time = start_time = pacifier_time = time(0);
|
||||
end_time = start_time + patience;
|
||||
while(current_time-end_time < patience) {
|
||||
still_not_done = 0;
|
||||
|
||||
for(i = 0; i < drivetop + 1; i++) {
|
||||
occup = burn_drive_is_occupied(&(drive_array[i]));
|
||||
if(occup == -2)
|
||||
continue;
|
||||
if(occup <= 10) {
|
||||
burn_drive_forget(&(drive_array[i]), 1);
|
||||
} else if(occup <= 100) {
|
||||
if(first_round)
|
||||
burn_drive_cancel(&(drive_array[i]));
|
||||
still_not_done++;
|
||||
} else if(occup <= 1000) {
|
||||
still_not_done++;
|
||||
}
|
||||
}
|
||||
first_round = 0;
|
||||
|
||||
if(still_not_done == 0)
|
||||
break;
|
||||
usleep(wait_grain);
|
||||
current_time = time(0);
|
||||
if(current_time>pacifier_time) {
|
||||
if(pacifier_func != NULL && !pacifier_off) {
|
||||
ret = (*pacifier_func)(handle, patience,
|
||||
current_time-start_time);
|
||||
pacifier_off = (ret <= 0);
|
||||
}
|
||||
pacifier_time = current_time;
|
||||
}
|
||||
}
|
||||
burn_finish();
|
||||
return(still_not_done == 0);
|
||||
}
|
||||
|
||||
|
||||
/* ts A61020 API function */
|
||||
int burn_drive_get_start_end_lba(struct burn_drive *d,
|
||||
int *start_lba, int *end_lba, int flag)
|
||||
{
|
||||
if (d->start_lba == -2000000000 || d->end_lba == -2000000000)
|
||||
return 0;
|
||||
*start_lba = d->start_lba;
|
||||
*end_lba= d->end_lba;
|
||||
assert(strlen(drive_info->location) < BURN_DRIVE_ADR_LEN);
|
||||
strcpy(adr,drive_info->location);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* ts A61020 API function */
|
||||
int burn_disc_pretend_blank(struct burn_drive *d)
|
||||
{
|
||||
if (d->status != BURN_DISC_UNREADY &&
|
||||
d->status != BURN_DISC_UNSUITABLE)
|
||||
return 0;
|
||||
d->status = BURN_DISC_BLANK;
|
||||
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)
|
||||
{
|
||||
if (burn_drive_is_released(d)) {
|
||||
libdax_msgs_submit(libdax_messenger,
|
||||
d->global_index, 0x0002010e,
|
||||
LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH,
|
||||
"Attempt to read ATIP from ungrabbed drive",
|
||||
0, 0);
|
||||
return -1;
|
||||
}
|
||||
d->read_atip(d);
|
||||
/* >>> some control of success would be nice :) */
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -35,14 +35,9 @@ struct mempage;
|
||||
SESSION_LEADOUT_ENTRY((d), (d)->toc->sessions-1))
|
||||
|
||||
struct burn_drive *burn_drive_register(struct burn_drive *);
|
||||
int burn_drive_unregister(struct burn_drive *d);
|
||||
|
||||
unsigned int burn_drive_count(void);
|
||||
|
||||
/* ts A61007 */
|
||||
/* void burn_wait_all(void); */
|
||||
int burn_drives_are_clear(void);
|
||||
|
||||
void burn_wait_all(void);
|
||||
int burn_sector_length_write(struct burn_drive *d);
|
||||
int burn_track_control(struct burn_drive *d, int);
|
||||
void burn_write_empty_sector(int fd);
|
||||
@ -56,18 +51,7 @@ void burn_disc_erase_sync(struct burn_drive *d, int fast);
|
||||
int burn_drive_get_block_types(struct burn_drive *d,
|
||||
enum burn_write_types write_type);
|
||||
|
||||
/* ts A60822 */
|
||||
int burn_drive_is_open(struct burn_drive *d);
|
||||
int burn_drive_is_occupied(struct burn_drive *d);
|
||||
int burn_drive_forget(struct burn_drive *d, int force);
|
||||
int burn_drive_convert_fs_adr_sub(char *path, char adr[], int *rec_count);
|
||||
|
||||
/* ts A61021 : the unspecific part of sg.c:enumerate_common()
|
||||
*/
|
||||
int burn_setup_drive(struct burn_drive *d, char *fname);
|
||||
|
||||
/* ts A61021 : after-setup activities from sg.c:enumerate_common()
|
||||
*/
|
||||
struct burn_drive *burn_drive_finish_enum(struct burn_drive *d);
|
||||
|
||||
|
||||
#endif /* __DRIVE */
|
||||
|
208
libburn/init.c
208
libburn/init.c
@ -1,14 +1,9 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
/* ts A61007 */
|
||||
/* #include <a ssert.h> */
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "init.h"
|
||||
#include "sg.h"
|
||||
#include "error.h"
|
||||
@ -19,11 +14,6 @@
|
||||
#define BURN_BACK_HACKS_INIT 1
|
||||
#include "back_hacks.h"
|
||||
|
||||
/* ts A60924 : a new message handling facility */
|
||||
#include "libdax_msgs.h"
|
||||
struct libdax_msgs *libdax_messenger= NULL;
|
||||
|
||||
|
||||
int burn_running = 0;
|
||||
|
||||
/* ts A60813 : wether to use O_EXCL and/or O_NONBLOCK in libburn/sg.c */
|
||||
@ -41,206 +31,50 @@ int burn_sg_open_o_nonblock = 1;
|
||||
int burn_sg_open_abort_busy = 0;
|
||||
|
||||
|
||||
/* ts A61002 */
|
||||
|
||||
#include "cleanup.h"
|
||||
|
||||
/* Parameters for builtin abort handler */
|
||||
static char abort_message_prefix[81] = {"libburn : "};
|
||||
static pid_t abort_control_pid= 0;
|
||||
|
||||
|
||||
/* ts A60925 : ticket 74 */
|
||||
/** Create the messenger object for libburn. */
|
||||
int burn_msgs_initialize(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if(libdax_messenger == NULL) {
|
||||
ret = libdax_msgs_new(&libdax_messenger,0);
|
||||
if (ret <= 0)
|
||||
return 0;
|
||||
}
|
||||
libdax_msgs_set_severities(libdax_messenger, LIBDAX_MSGS_SEV_NEVER,
|
||||
LIBDAX_MSGS_SEV_FATAL, "libburn: ", 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ts A60924 : ticket 74 : Added use of global libdax_messenger */
|
||||
int burn_initialize(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (burn_running)
|
||||
return 1;
|
||||
ret = burn_msgs_initialize();
|
||||
if (ret <= 0)
|
||||
return 0;
|
||||
|
||||
burn_running = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void burn_finish(void)
|
||||
{
|
||||
/* ts A61007 : assume no messageing system */
|
||||
/* a ssert(burn_running); */
|
||||
if (!burn_running)
|
||||
return;
|
||||
assert(burn_running);
|
||||
|
||||
/* ts A61007 */
|
||||
/* burn_wait_all(); */
|
||||
if (!burn_drives_are_clear()) {
|
||||
libdax_msgs_submit(libdax_messenger, -1, 0x00020107,
|
||||
LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
|
||||
"Drive is busy on attempt to shut down library", 0, 0);
|
||||
return;
|
||||
}
|
||||
burn_wait_all();
|
||||
|
||||
/* ts A60904 : ticket 62, contribution by elmom : name addon "_all" */
|
||||
burn_drive_free_all();
|
||||
|
||||
/* ts A60924 : ticket 74 */
|
||||
libdax_msgs_destroy(&libdax_messenger,0);
|
||||
|
||||
burn_running = 0;
|
||||
}
|
||||
|
||||
|
||||
/* ts A60813 */
|
||||
/** API function. See libburn.h */
|
||||
/** Set parameters for behavior on opening device files. To be called early
|
||||
after burn_initialize() and before any bus scan. But not mandatory at all.
|
||||
@param exclusive Try to open only devices which are not marked as busy
|
||||
and try to mark them busy if opened sucessfully. (O_EXCL)
|
||||
There are kernels which simply don't care about O_EXCL.
|
||||
Some have it off, some have it on, some are switchable.
|
||||
@param blocking Try to wait for drives which do not open immediately but
|
||||
also do not return an error as well. (O_NONBLOCK)
|
||||
This might stall indefinitely with /dev/hdX hard disks.
|
||||
@param abort_on_busy Unconditionally abort process when a non blocking
|
||||
exclusive opening attempt indicates a busy drive.
|
||||
Use this only after thorough tests with your app.
|
||||
Parameter value 1 enables a feature, 0 disables.
|
||||
Default is (1,0,0). Have a good reason before you change it.
|
||||
*/
|
||||
void burn_preset_device_open(int exclusive, int blocking, int abort_on_busy)
|
||||
{
|
||||
/* ts A61007 */
|
||||
/* a ssert(burn_running); */
|
||||
if (!burn_running)
|
||||
return;
|
||||
assert(burn_running);
|
||||
|
||||
burn_sg_open_o_excl= exclusive;
|
||||
burn_sg_open_o_excl= !!exclusive;
|
||||
burn_sg_open_o_nonblock= !blocking;
|
||||
burn_sg_open_abort_busy= !!abort_on_busy;
|
||||
}
|
||||
|
||||
|
||||
/* ts A60924 : ticket 74 */
|
||||
/** Control queueing and stderr printing of messages from libburn.
|
||||
Severity may be one of "NEVER", "FATAL", "SORRY", "WARNING", "HINT",
|
||||
"NOTE", "UPDATE", "DEBUG", "ALL".
|
||||
@param queue_severity Gives the minimum limit for messages to be queued.
|
||||
Default: "NEVER". If you queue messages then you
|
||||
must consume them by burn_msgs_obtain().
|
||||
@param print_severity Does the same for messages to be printed directly
|
||||
to stderr.
|
||||
@param print_id A text prefix to be printed before the message.
|
||||
@return >0 for success, <=0 for error
|
||||
|
||||
*/
|
||||
int burn_msgs_set_severities(char *queue_severity,
|
||||
char *print_severity, char *print_id)
|
||||
{
|
||||
int ret, queue_sevno, print_sevno;
|
||||
|
||||
ret = libdax_msgs__text_to_sev(queue_severity, &queue_sevno, 0);
|
||||
if (ret <= 0)
|
||||
return 0;
|
||||
ret = libdax_msgs__text_to_sev(print_severity, &print_sevno, 0);
|
||||
if (ret <= 0)
|
||||
return 0;
|
||||
ret = libdax_msgs_set_severities(libdax_messenger, queue_sevno,
|
||||
print_sevno, print_id, 0);
|
||||
if (ret <= 0)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* ts A60924 : ticket 74 */
|
||||
#define BURM_MSGS_MESSAGE_LEN 4096
|
||||
|
||||
/** Obtain the oldest pending libburn message from the queue which has at
|
||||
least the given minimum_severity. This message and any older message of
|
||||
lower severity will get discarded from the queue and is then lost forever.
|
||||
Severity may be one of "NEVER", "FATAL", "SORRY", "WARNING", "HINT",
|
||||
"NOTE", "UPDATE", "DEBUG", "ALL". To call with minimum_severity "NEVER"
|
||||
will discard the whole queue.
|
||||
@param error_code Will become a unique error code as liste in
|
||||
libburn/libdax_msgs.h
|
||||
@param msg_text Must provide at least BURM_MSGS_MESSAGE_LEN bytes.
|
||||
@param os_errno Will become the eventual errno related to the message
|
||||
@param severity Will become the severity related to the message and
|
||||
should provide at least 80 bytes.
|
||||
@return 1 if a matching item was found, 0 if not, <0 for severe errors
|
||||
*/
|
||||
int burn_msgs_obtain(char *minimum_severity,
|
||||
int *error_code, char msg_text[], int *os_errno,
|
||||
char severity[])
|
||||
{
|
||||
int ret, minimum_sevno, sevno, priority;
|
||||
char *textpt, *sev_name;
|
||||
struct libdax_msgs_item *item = NULL;
|
||||
|
||||
ret = libdax_msgs__text_to_sev(minimum_severity, &minimum_sevno, 0);
|
||||
if (ret <= 0)
|
||||
return 0;
|
||||
ret = libdax_msgs_obtain(libdax_messenger, &item, minimum_sevno,
|
||||
LIBDAX_MSGS_PRIO_ZERO, 0);
|
||||
if (ret <= 0)
|
||||
goto ex;
|
||||
ret = libdax_msgs_item_get_msg(item, error_code, &textpt, os_errno, 0);
|
||||
if (ret <= 0)
|
||||
goto ex;
|
||||
strncpy(msg_text, textpt, BURM_MSGS_MESSAGE_LEN-1);
|
||||
if(strlen(textpt) >= BURM_MSGS_MESSAGE_LEN)
|
||||
msg_text[BURM_MSGS_MESSAGE_LEN-1] = 0;
|
||||
|
||||
severity[0]= 0;
|
||||
ret = libdax_msgs_item_get_rank(item, &sevno, &priority, 0);
|
||||
if(ret <= 0)
|
||||
goto ex;
|
||||
ret = libdax_msgs__sev_to_text(sevno, &sev_name, 0);
|
||||
if(ret <= 0)
|
||||
goto ex;
|
||||
strcpy(severity,sev_name);
|
||||
|
||||
ret = 1;
|
||||
ex:
|
||||
libdax_msgs_destroy_item(libdax_messenger, &item, 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int burn_builtin_abort_handler(void *handle, int signum, int flag)
|
||||
{
|
||||
if(getpid() != abort_control_pid)
|
||||
return -2;
|
||||
Cleanup_set_handlers(NULL, NULL, 2);
|
||||
fprintf(stderr,"%sABORT : Trying to shut down drive and library\n",
|
||||
abort_message_prefix);
|
||||
fprintf(stderr,
|
||||
"%sABORT : Wait the normal burning time before any kill -9\n",
|
||||
abort_message_prefix);
|
||||
close(0); /* somehow stdin as input blocks abort until EOF */
|
||||
burn_abort(4440, burn_abort_pacifier, abort_message_prefix);
|
||||
fprintf(stderr,
|
||||
"\n%sABORT : Program done. Even if you do not see a shell prompt.\n\n",
|
||||
abort_message_prefix);
|
||||
return(1);
|
||||
}
|
||||
|
||||
void burn_set_signal_handling(void *handle, burn_abort_handler_t handler,
|
||||
int mode)
|
||||
{
|
||||
if(handler == NULL && mode == 0) {
|
||||
handler = burn_builtin_abort_handler;
|
||||
/*
|
||||
fprintf(stderr, "libburn_experimental: activated burn_builtin_abort_handler() with handle '%s'\n",(handle==NULL ? "libburn : " : (char *) handle));
|
||||
*/
|
||||
|
||||
}
|
||||
strcpy(abort_message_prefix, "libburn : ");
|
||||
if(handle != NULL)
|
||||
strncpy(abort_message_prefix, (char *) handle,
|
||||
sizeof(abort_message_prefix)-1);
|
||||
abort_message_prefix[sizeof(abort_message_prefix)-1] = 0;
|
||||
abort_control_pid= getpid();
|
||||
Cleanup_set_handlers(handle, (Cleanup_app_handler_T) handler, mode|4);
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,6 @@
|
||||
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>
|
||||
|
||||
@ -41,10 +40,6 @@ 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 */
|
||||
@ -177,17 +172,39 @@ enum burn_disc_status
|
||||
/** There is an incomplete disc in the drive */
|
||||
BURN_DISC_APPENDABLE,
|
||||
/** There is a disc with data on it in the drive */
|
||||
BURN_DISC_FULL,
|
||||
|
||||
/* ts A61007 */
|
||||
/** The drive was not grabbed when the status was inquired */
|
||||
BURN_DISC_UNGRABBED,
|
||||
|
||||
/* ts A61020 */
|
||||
/** The media seems not to be suitable for burning */
|
||||
BURN_DISC_UNSUITABLE
|
||||
BURN_DISC_FULL
|
||||
};
|
||||
|
||||
/** Possible types of messages form the library. */
|
||||
enum burn_message_type
|
||||
{
|
||||
/** Diagnostic/Process information. For the curious user. */
|
||||
BURN_MESSAGE_INFO,
|
||||
/** A warning regarding a possible problem. The user should probably
|
||||
be notified, but its not fatal. */
|
||||
BURN_MESSAGE_WARNING,
|
||||
/** An error message. This usually means the current process will be
|
||||
aborted, and the user should definately see these. */
|
||||
BURN_MESSAGE_ERROR
|
||||
};
|
||||
|
||||
/** Possible information messages */
|
||||
enum burn_message_info
|
||||
{
|
||||
BURN_INFO_FOO
|
||||
};
|
||||
|
||||
/** Possible warning messages */
|
||||
enum burn_message_warning
|
||||
{
|
||||
BURN_WARNING_FOO
|
||||
};
|
||||
|
||||
/** Possible error messages */
|
||||
enum burn_message_error
|
||||
{
|
||||
BURN_ERROR_CANCELLED
|
||||
};
|
||||
|
||||
/** Possible data source return values */
|
||||
enum burn_source_status
|
||||
@ -221,16 +238,7 @@ enum burn_drive_status
|
||||
/** The drive is erasing a disc */
|
||||
BURN_DRIVE_ERASING,
|
||||
/** The drive is being grabbed */
|
||||
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
|
||||
|
||||
BURN_DRIVE_GRABBING
|
||||
};
|
||||
|
||||
/** Information about a track on a disc - this is from the q sub channel of the
|
||||
@ -309,7 +317,7 @@ struct burn_drive_info
|
||||
/** This is currently the string which is used as persistent
|
||||
drive address. But be warned: there is NO GUARANTEE that this
|
||||
will stay so. Always use function burn_drive_get_adr() to
|
||||
inquire a persistent address. ^^^^^^ ALWAYS ^^^^^^ */
|
||||
inquire a persisten address. ^^^^^^ ALWAYS ^^^^^^ */
|
||||
|
||||
/** Can the drive read DVD-RAM discs */
|
||||
unsigned int read_dvdram:1;
|
||||
@ -370,6 +378,29 @@ struct burn_drive_info
|
||||
struct burn_drive *drive;
|
||||
};
|
||||
|
||||
/** Messages from the library */
|
||||
struct burn_message
|
||||
{
|
||||
/** The drive associated with the message. NULL if the error is not
|
||||
related to a specific drive. */
|
||||
struct burn_drive *drive;
|
||||
|
||||
/** The type of message this is. See message_type for details. */
|
||||
enum burn_message_type type;
|
||||
|
||||
/** The actual message */
|
||||
union detail {
|
||||
struct {
|
||||
enum burn_message_info message;
|
||||
} info;
|
||||
struct {
|
||||
enum burn_message_warning message;
|
||||
} warning;
|
||||
struct {
|
||||
enum burn_message_error message;
|
||||
} error;
|
||||
} detail;
|
||||
};
|
||||
|
||||
/** Operation progress report. All values are 0 based indices.
|
||||
* */
|
||||
@ -388,26 +419,10 @@ struct burn_progress {
|
||||
int index;
|
||||
/** The starting logical block address */
|
||||
int start_sector;
|
||||
/** On write: The number of sectors.
|
||||
On blank: 0x10000 as upper limit for relative progress steps */
|
||||
/** The number of sector */
|
||||
int sectors;
|
||||
/** On write: The current sector being processed.
|
||||
On blank: Relative progress steps 0 to 0x10000 */
|
||||
/** The current sector being processed */
|
||||
int sector;
|
||||
|
||||
/* ts A61023 */
|
||||
/** The capacity of the drive buffer */
|
||||
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.
|
||||
@ -429,36 +444,7 @@ int burn_initialize(void);
|
||||
*/
|
||||
void burn_finish(void);
|
||||
|
||||
|
||||
/* ts A61002 */
|
||||
/** Abort any running drive operation and finally call burn_finish().
|
||||
You MUST calm down the busy drive if an aborting event occurs during a
|
||||
burn run. For that you may call this function either from your own signal
|
||||
handling code or indirectly by activating the builtin signal handling:
|
||||
burn_set_signal_handling("my_app_name : ", NULL, 0);
|
||||
Else you may eventually call burn_drive_cancel() on the active drive and
|
||||
wait for it to assume state BURN_DRIVE_IDLE.
|
||||
@param patience Maximum number of seconds to wait for drives to finish
|
||||
@param pacifier_func If not NULL: a function to produce appeasing messages.
|
||||
See burn_abort_pacifier() for an example.
|
||||
@return 1 ok, all went well
|
||||
0 had to leave a drive in unclean state
|
||||
<0 severe error, do no use libburn again
|
||||
*/
|
||||
int burn_abort(int patience,
|
||||
int (*pacifier_func)(void *handle, int patience, int elapsed),
|
||||
void *handle);
|
||||
|
||||
/** A pacifier function suitable for burn_abort.
|
||||
@param handle If not NULL, a pointer to a text suitable for printf("%s")
|
||||
@param patience Maximum number of seconds to wait
|
||||
@param elapsed Elapsed number of seconds
|
||||
*/
|
||||
int burn_abort_pacifier(void *handle, int patience, int elapsed);
|
||||
|
||||
|
||||
/** ts A61006 : This is for development only. Not suitable for applications.
|
||||
Set the verbosity level of the library. The default value is 0, which means
|
||||
/** Set the verbosity level of the library. The default value is 0, which means
|
||||
that nothing is output on stderr. The more you increase this, the more
|
||||
debug output should be displayed on stderr for you.
|
||||
@param level The verbosity level desired. 0 for nothing, higher positive
|
||||
@ -471,13 +457,10 @@ void burn_set_verbosity(int level);
|
||||
after burn_initialize() and before any bus scan. But not mandatory at all.
|
||||
Parameter value 1 enables a feature, 0 disables.
|
||||
Default is (1,0,0). Have a good reason before you change it.
|
||||
@param exclusive 1 = Try to open only devices which are not marked as busy
|
||||
@param exclusive Try to open only devices which are not marked as busy
|
||||
and try to mark them busy if opened sucessfully. (O_EXCL)
|
||||
There are kernels which simply don't care about O_EXCL.
|
||||
Some have it off, some have it on, some are switchable.
|
||||
2 = in case of a SCSI device, also try to open exclusively
|
||||
the matching /dev/sr, /dev/scd and /dev/st .
|
||||
0 = no attempt to make drive access exclusive.
|
||||
@param blocking Try to wait for drives which do not open immediately but
|
||||
also do not return an error as well. (O_NONBLOCK)
|
||||
This might stall indefinitely with /dev/hdX hard disks.
|
||||
@ -487,6 +470,15 @@ void burn_set_verbosity(int level);
|
||||
*/
|
||||
void burn_preset_device_open(int exclusive, int blocking, int abort_on_busy);
|
||||
|
||||
/** Returns a newly allocated burn_message structure. This message should be
|
||||
freed with burn_message_free() when you are finished with it.
|
||||
@return A message or NULL when there are no more messages to retrieve.
|
||||
*/
|
||||
struct burn_message* burn_get_message(void);
|
||||
|
||||
/** Frees a burn_message structure */
|
||||
void burn_message_free(struct burn_message *msg);
|
||||
|
||||
|
||||
/* ts A60823 */
|
||||
/** Aquire a drive with known persistent address.This is the sysadmin friendly
|
||||
@ -536,7 +528,7 @@ void burn_drive_clear_whitelist(void);
|
||||
|
||||
|
||||
/** Scan for drives. This function MUST be called until it returns nonzero.
|
||||
No drives may be in use when this is called.
|
||||
No drives can be in use when this is called or it will assert.
|
||||
All drive pointers are invalidated by using this function. Do NOT store
|
||||
drive pointers across calls to this function or death AND pain will ensue.
|
||||
After this call all drives depicted by the returned array are subject
|
||||
@ -549,9 +541,7 @@ void burn_drive_clear_whitelist(void);
|
||||
before burn_finish(), and also before calling this function
|
||||
burn_drive_scan() again.
|
||||
@param n_drives Returns the number of drive items in drive_infos.
|
||||
@return 0 while scanning is not complete
|
||||
>0 when it is finished sucessfully,
|
||||
<0 when finished but failed.
|
||||
@return Zero while scanning is not complete; non-zero when it is finished.
|
||||
*/
|
||||
int burn_drive_scan(struct burn_drive_info *drive_infos[],
|
||||
unsigned int *n_drives);
|
||||
@ -586,51 +576,9 @@ void burn_drive_info_free(struct burn_drive_info drive_infos[]);
|
||||
@param drive_info The drive to inquire. Usually some &(drive_infos[driveno])
|
||||
@param adr An application provided array of at least BURN_DRIVE_ADR_LEN
|
||||
characters size. The persistent address gets copied to it.
|
||||
@return >0 success , <=0 error (due to libburn internal problem)
|
||||
*/
|
||||
int burn_drive_get_adr(struct burn_drive_info *drive_info, char adr[]);
|
||||
|
||||
/* ts A60922 ticket 33 */
|
||||
/** Evaluate wether the given address would be a possible persistent drive
|
||||
address of libburn.
|
||||
@return 1 means yes, 0 means no
|
||||
*/
|
||||
int burn_drive_is_enumerable_adr(char *adr);
|
||||
|
||||
/* ts A60922 ticket 33 */
|
||||
/** Try to convert a given existing filesystem address into a persistent drive
|
||||
address. This succeeds with symbolic links or if a hint about the drive's
|
||||
system address can be read from the filesystem object and a matching drive
|
||||
is found.
|
||||
@param path The address of an existing file system object
|
||||
@param adr An application provided array of at least BURN_DRIVE_ADR_LEN
|
||||
characters size. The persistent address gets copied to it.
|
||||
@return 1 = success , 0 = failure , -1 = severe error
|
||||
*/
|
||||
int burn_drive_convert_fs_adr(char *path, char adr[]);
|
||||
|
||||
/* ts A60923 */
|
||||
/** Try to convert a given SCSI address of bus,host,channel,target,lun into
|
||||
a persistent drive address. If a SCSI address component parameter is < 0
|
||||
then it is not decisive and the first enumerated address which matches
|
||||
the >= 0 parameters is taken as result.
|
||||
Note: bus and (host,channel) are supposed to be redundant.
|
||||
@param adr An application provided array of at least BURN_DRIVE_ADR_LEN
|
||||
characters size. The persistent address gets copied to it.
|
||||
@return 1 = success , 0 = failure , -1 = severe error
|
||||
*/
|
||||
int burn_drive_convert_scsi_adr(int bus_no, int host_no, int channel_no,
|
||||
int target_no, int lun_no, char adr[]);
|
||||
|
||||
/* ts A60923 - A61005 */
|
||||
/** Try to obtain bus,host,channel,target,lun from path. If there is an SCSI
|
||||
address at all, then this call should succeed with a persistent
|
||||
drive address obtained via burn_drive_get_adr(). It is also supposed to
|
||||
succeed with any device file of a (possibly emulated) SCSI device.
|
||||
@return 1 = success , 0 = failure , -1 = severe error
|
||||
*/
|
||||
int burn_drive_obtain_scsi_adr(char *path, int *bus_no, int *host_no,
|
||||
int *channel_no, int *target_no, int *lun_no);
|
||||
|
||||
/** Grab a drive. This must be done before the drive can be used (for reading,
|
||||
writing, etc).
|
||||
@ -653,74 +601,13 @@ 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_disc_status
|
||||
called more than once to get a proper status from it. See burn_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.
|
||||
Note: BURN_DISC_UNGRABBED indicates wrong API usage
|
||||
*/
|
||||
enum burn_disc_status burn_disc_get_status(struct burn_drive *drive);
|
||||
|
||||
|
||||
/* ts A61020 */
|
||||
/** WARNING: This revives an old bug-like behavior that might be dangerous.
|
||||
Sets the drive status to BURN_DISC_BLANK if it is BURN_DISC_UNREADY
|
||||
or BURN_DISC_UNSUITABLE. Thus marking media as writable which actually
|
||||
failed to declare themselves either blank or (partially) filled.
|
||||
@return 1 drive status has been set , 0 = unsuitable drive status
|
||||
*/
|
||||
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(),
|
||||
burn_drive_get_start_end_lba(). The drive must be grabbed for this call.
|
||||
@param drive The drive to query.
|
||||
@return 1=sucess, 0=no valid ATIP info read, -1 severe error
|
||||
*/
|
||||
int burn_disc_read_atip(struct burn_drive *drive);
|
||||
|
||||
|
||||
/* ts A61020 */
|
||||
/** Returns start and end lba of the media which is currently inserted
|
||||
in the given drive. The drive has to be grabbed to have hope for reply.
|
||||
Shortcomming (not a feature): unless burn_disc_read_atip() was called
|
||||
only blank media will return valid info.
|
||||
@param drive The drive to query.
|
||||
@param start_lba Returns the start lba value
|
||||
@param end_lba Returns the end lba value
|
||||
@param flag Bitfield for control purposes (unused yet, submit 0)
|
||||
@return 1 if lba values are valid , 0 if invalid
|
||||
*/
|
||||
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
|
||||
*/
|
||||
@ -737,7 +624,7 @@ enum burn_drive_status burn_drive_get_status(struct burn_drive *drive,
|
||||
/** Creates a write_opts struct for burning to the specified drive
|
||||
must be freed with burn_write_opts_free
|
||||
@param drive The drive to write with
|
||||
@return The write_opts, NULL on error
|
||||
@return The write_opts
|
||||
*/
|
||||
struct burn_write_opts *burn_write_opts_new(struct burn_drive *drive);
|
||||
|
||||
@ -746,7 +633,7 @@ struct burn_write_opts *burn_write_opts_new(struct burn_drive *drive);
|
||||
*/
|
||||
void burn_write_opts_free(struct burn_write_opts *opts);
|
||||
|
||||
/** Creates a read_opts struct for reading from the specified drive
|
||||
/** Creates a write_opts struct for reading from the specified drive
|
||||
must be freed with burn_write_opts_free
|
||||
@param drive The drive to read from
|
||||
@return The read_opts
|
||||
@ -769,7 +656,6 @@ 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.
|
||||
@ -778,9 +664,10 @@ 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 before calling this function.
|
||||
BURN_DISC_BLANK or BURN_STATUS_FULL (to append a new session to the
|
||||
disc) 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
|
||||
*/
|
||||
@ -897,16 +784,6 @@ int burn_session_remove_track(struct burn_session *s, struct burn_track *t);
|
||||
void burn_track_define_data(struct burn_track *t, int offset, int tail,
|
||||
int pad, int mode);
|
||||
|
||||
|
||||
/* ts A61024 */
|
||||
/** Define wether a track shall swap bytes of its input stream.
|
||||
@param t The track to change
|
||||
@param swap_source_bytes 0=do not swap, 1=swap byte pairs
|
||||
@return 1=success , 0=unacceptable value
|
||||
*/
|
||||
int burn_track_set_byte_swap(struct burn_track *t, int swap_source_bytes);
|
||||
|
||||
|
||||
/** Set the ISRC details for a track
|
||||
@param t The track to change
|
||||
@param country the 2 char country code. Each character must be
|
||||
@ -932,7 +809,7 @@ void burn_session_hide_first_track(struct burn_session *s, int onoff);
|
||||
|
||||
/** Get the drive's disc struct - free when done
|
||||
@param d drive to query
|
||||
@return the disc struct or NULL on failure
|
||||
@return the disc struct
|
||||
*/
|
||||
struct burn_disc *burn_drive_get_disc(struct burn_drive *d);
|
||||
|
||||
@ -965,19 +842,10 @@ 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
|
||||
>>> NOTE: Not reliable with tracks of undefined length
|
||||
*/
|
||||
/** Tells how long a track will be on disc */
|
||||
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)
|
||||
@ -1042,15 +910,6 @@ 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
|
||||
@ -1112,16 +971,6 @@ void burn_read_opts_set_hardware_error_retries(struct burn_read_opts *opts,
|
||||
*/
|
||||
int burn_drive_get_write_speed(struct burn_drive *d);
|
||||
|
||||
|
||||
/* ts A61021 */
|
||||
/** Gets the minimum write speed for a drive. This might differ from
|
||||
burn_drive_get_write_speed() only after burn_disc_read_atip()
|
||||
@param d Drive to query
|
||||
@return Minimum write speed in K/s
|
||||
*/
|
||||
int burn_drive_get_min_write_speed(struct burn_drive *d);
|
||||
|
||||
|
||||
/** Gets the maximum read speed for a drive
|
||||
@param d Drive to query
|
||||
@return Maximum read speed in K/s
|
||||
@ -1182,68 +1031,6 @@ int burn_session_get_hidefirst(struct burn_session *session);
|
||||
*/
|
||||
void burn_version(int *major, int *minor, int *micro);
|
||||
|
||||
|
||||
/* ts A60924 : ticket 74 */
|
||||
/** Control queueing and stderr printing of messages from libburn.
|
||||
Severity may be one of "NEVER", "FATAL", "SORRY", "WARNING", "HINT",
|
||||
"NOTE", "UPDATE", "DEBUG", "ALL".
|
||||
@param queue_severity Gives the minimum limit for messages to be queued.
|
||||
Default: "NEVER". If you queue messages then you
|
||||
must consume them by burn_msgs_obtain().
|
||||
@param print_severity Does the same for messages to be printed directly
|
||||
to stderr. Default: "FATAL".
|
||||
@param print_id A text prefix to be printed before the message.
|
||||
@return >0 for success, <=0 for error
|
||||
|
||||
*/
|
||||
int burn_msgs_set_severities(char *queue_severity,
|
||||
char *print_severity, char *print_id);
|
||||
|
||||
/* ts A60924 : ticket 74 */
|
||||
#define BURN_MSGS_MESSAGE_LEN 4096
|
||||
|
||||
/** Obtain the oldest pending libburn message from the queue which has at
|
||||
least the given minimum_severity. This message and any older message of
|
||||
lower severity will get discarded from the queue and is then lost forever.
|
||||
Severity may be one of "NEVER", "FATAL", "SORRY", "WARNING", "HINT",
|
||||
"NOTE", "UPDATE", "DEBUG", "ALL". To call with minimum_severity "NEVER"
|
||||
will discard the whole queue.
|
||||
@param error_code Will become a unique error code as liste in
|
||||
libburn/libdax_msgs.h
|
||||
@param msg_text Must provide at least BURN_MSGS_MESSAGE_LEN bytes.
|
||||
@param os_errno Will become the eventual errno related to the message
|
||||
@param severity Will become the severity related to the message and
|
||||
should provide at least 80 bytes.
|
||||
@return 1 if a matching item was found, 0 if not, <0 for severe errors
|
||||
*/
|
||||
int burn_msgs_obtain(char *minimum_severity,
|
||||
int *error_code, char msg_text[], int *os_errno,
|
||||
char severity[]);
|
||||
|
||||
|
||||
/* ts A61002 */
|
||||
/* The prototype of a handler function suitable for burn_set_abort_handling().
|
||||
Such a function has to return -2 if it does not want the process to
|
||||
exit with value 1.
|
||||
*/
|
||||
typedef int (*burn_abort_handler_t)(void *handle, int signum, int flag);
|
||||
|
||||
/** Control builtin signal handling. See also burn_abort().
|
||||
@param handle Opaque handle eventually pointing to an application
|
||||
provided memory object
|
||||
@param handler A function to be called on signals. It will get handle as
|
||||
argument. It should finally call burn_abort(). See there.
|
||||
@param mode : 0 call handler(handle, signum, 0) on nearly all signals
|
||||
1 enable system default reaction on all signals
|
||||
2 try to ignore nearly all signals
|
||||
10 like mode 2 but handle SIGABRT like with mode 0
|
||||
Arguments (text, NULL, 0) activate the builtin abort handler. It will
|
||||
eventually call burn_abort() and then perform exit(1). If text is not NULL
|
||||
then it is used as prefix for pacifier messages of burn_abort_pacifier().
|
||||
*/
|
||||
void burn_set_signal_handling(void *handle, burn_abort_handler_t handler,
|
||||
int mode);
|
||||
|
||||
#ifndef DOXYGEN
|
||||
|
||||
BURN_END_DECLS
|
||||
|
@ -1,326 +0,0 @@
|
||||
|
||||
/* libdax_audioxtr
|
||||
Audio track data extraction facility of libdax and libburn.
|
||||
Copyright (C) 2006 Thomas Schmitt <scdbackup@gmx.net>, provided under GPL
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
|
||||
#include "libdax_msgs.h"
|
||||
extern struct libdax_msgs *libdax_messenger;
|
||||
|
||||
|
||||
/* Only this single source module is entitled to do this */
|
||||
#define LIBDAX_AUDIOXTR_H_INTERNAL 1
|
||||
|
||||
/* All clients of the extraction facility must do this */
|
||||
#include "libdax_audioxtr.h"
|
||||
|
||||
|
||||
int libdax_audioxtr_new(struct libdax_audioxtr **xtr, char *path, int flag)
|
||||
{
|
||||
int ret= -1;
|
||||
struct libdax_audioxtr *o;
|
||||
|
||||
o= *xtr= (struct libdax_audioxtr *) malloc(sizeof(struct libdax_audioxtr));
|
||||
if(o==NULL)
|
||||
return(-1);
|
||||
strncpy(o->path,path,LIBDAX_AUDIOXTR_STRLEN-1);
|
||||
o->path[LIBDAX_AUDIOXTR_STRLEN]= 0;
|
||||
o->fd= -1;
|
||||
strcpy(o->fmt,"unidentified");
|
||||
o->fmt_info[0]= 0;
|
||||
o->data_size= 0;
|
||||
o->extract_count= 0;
|
||||
|
||||
o->num_channels= 0;
|
||||
o->sample_rate= 0;
|
||||
o->bits_per_sample= 0;
|
||||
o->msb_first= 0;
|
||||
|
||||
o->wav_subchunk2_size= 0;
|
||||
|
||||
o->au_data_location= 0;
|
||||
o->au_data_size= 0xffffffff;
|
||||
|
||||
ret= libdax_audioxtr_open(o,0);
|
||||
if(ret<=0)
|
||||
{ret= -2*(ret<0); goto failure;}
|
||||
|
||||
return(1);
|
||||
failure:
|
||||
libdax_audioxtr_destroy(xtr,0);
|
||||
return(ret);
|
||||
}
|
||||
|
||||
|
||||
int libdax_audioxtr_destroy(struct libdax_audioxtr **xtr, int flag)
|
||||
{
|
||||
struct libdax_audioxtr *o;
|
||||
|
||||
o= *xtr;
|
||||
if(o==NULL)
|
||||
return(0);
|
||||
if(o->fd>=0 && strcmp(o->path,"-")!=0)
|
||||
close(o->fd);
|
||||
free((char *) o);
|
||||
*xtr= NULL;
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
static int libdax_audioxtr_open(struct libdax_audioxtr *o, int flag)
|
||||
{
|
||||
int ret;
|
||||
char msg[LIBDAX_AUDIOXTR_STRLEN+80];
|
||||
|
||||
if(strcmp(o->path,"-")==0)
|
||||
o->fd= 0;
|
||||
else
|
||||
o->fd= open(o->path, O_RDONLY);
|
||||
if(o->fd<0) {
|
||||
sprintf(msg,"Cannot open audio source file : %s",o->path);
|
||||
libdax_msgs_submit(libdax_messenger,-1,0x00020200,
|
||||
LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
|
||||
msg, errno, 0);
|
||||
return(-1);
|
||||
}
|
||||
ret= libdax_audioxtr_identify(o,0);
|
||||
if(ret<=0) {
|
||||
sprintf(msg,"Audio source file has unsuitable format : %s",o->path);
|
||||
libdax_msgs_submit(libdax_messenger,-1,0x00020201,
|
||||
LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
|
||||
msg, 0, 0);
|
||||
return(0);
|
||||
}
|
||||
ret= libdax_audioxtr_init_reading(o,0);
|
||||
if(ret<=0) {
|
||||
sprintf(msg,"Failed to prepare reading of audio data : %s",o->path);
|
||||
libdax_msgs_submit(libdax_messenger,-1,0x00020202,
|
||||
LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
|
||||
msg, 0, 0);
|
||||
return(0);
|
||||
}
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
static int libdax_audioxtr_identify_wav(struct libdax_audioxtr *o, int flag)
|
||||
{
|
||||
int ret;
|
||||
char buf[45];
|
||||
|
||||
/* check wether this is a MS WAVE file .wav */
|
||||
/* info used: http://ccrma.stanford.edu/courses/422/projects/WaveFormat/ */
|
||||
|
||||
if(o->fd!=0) {
|
||||
ret= lseek(o->fd,0,SEEK_SET);
|
||||
if(ret==-1)
|
||||
return(0);
|
||||
}
|
||||
ret= read(o->fd, buf, 44);
|
||||
if(ret<44)
|
||||
return(0);
|
||||
buf[44]= 0; /* as stopper for any string operations */
|
||||
|
||||
if(strncmp(buf,"RIFF",4)!=0) /* ChunkID */
|
||||
return(0);
|
||||
if(strncmp(buf+8,"WAVE",4)!=0) /* Format */
|
||||
return(0);
|
||||
if(strncmp(buf+12,"fmt ",4)!=0) /* Subchunk1ID */
|
||||
return(0);
|
||||
if(buf[16]!=16 || buf[17]!=0 || buf[18]!=0 || buf[19]!=0) /* Subchunk1Size */
|
||||
return(0);
|
||||
if(buf[20]!=1 || buf[21]!=0) /* AudioFormat must be 1 (Linear quantization) */
|
||||
return(0);
|
||||
|
||||
strcpy(o->fmt,".wav");
|
||||
o->msb_first= 0;
|
||||
o->num_channels= libdax_audioxtr_to_int(o,(unsigned char *) buf+22,2,0);
|
||||
o->sample_rate= libdax_audioxtr_to_int(o,(unsigned char *) buf+24,4,0);
|
||||
o->bits_per_sample= libdax_audioxtr_to_int(o,(unsigned char *)buf+34,2,0);
|
||||
sprintf(o->fmt_info,
|
||||
".wav , num_channels=%d , sample_rate=%d , bits_per_sample=%d",
|
||||
o->num_channels,o->sample_rate,o->bits_per_sample);
|
||||
o->wav_subchunk2_size= libdax_audioxtr_to_int(o,(unsigned char *)buf+40,4,0);
|
||||
o->data_size= o->wav_subchunk2_size;
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
static int libdax_audioxtr_identify_au(struct libdax_audioxtr *o, int flag)
|
||||
{
|
||||
int ret,encoding;
|
||||
char buf[24];
|
||||
|
||||
/* Check wether this is a Sun Audio, .au file */
|
||||
/* info used: http://ccrma.stanford.edu/courses/422/projects/WaveFormat/ */
|
||||
|
||||
if(o->fd!=0) {
|
||||
ret= lseek(o->fd,0,SEEK_SET);
|
||||
if(ret==-1)
|
||||
return(0);
|
||||
}
|
||||
ret= read(o->fd, buf, 24);
|
||||
if(ret<24)
|
||||
return(0);
|
||||
|
||||
if(strncmp(buf,".snd",4)!=0)
|
||||
return(0);
|
||||
strcpy(o->fmt,".au");
|
||||
o->msb_first= 1;
|
||||
o->au_data_location= libdax_audioxtr_to_int(o,(unsigned char *)buf+4,4,1);
|
||||
o->au_data_size= libdax_audioxtr_to_int(o,(unsigned char *)buf+8,4,1);
|
||||
encoding= libdax_audioxtr_to_int(o,(unsigned char *)buf+12,4,1);
|
||||
if(encoding==2)
|
||||
o->bits_per_sample= 8;
|
||||
else if(encoding==3)
|
||||
o->bits_per_sample= 16;
|
||||
else if(encoding==4)
|
||||
o->bits_per_sample= 24;
|
||||
else if(encoding==5)
|
||||
o->bits_per_sample= 32;
|
||||
else
|
||||
o->bits_per_sample= -encoding;
|
||||
o->sample_rate= libdax_audioxtr_to_int(o,(unsigned char *)buf+16,4,1);
|
||||
o->num_channels= libdax_audioxtr_to_int(o,(unsigned char *)buf+20,4,1);
|
||||
if(o->au_data_size!=0xffffffff)
|
||||
o->data_size= o->au_data_size;
|
||||
else
|
||||
o->data_size= 0;
|
||||
sprintf(o->fmt_info,
|
||||
".au , num_channels=%d , sample_rate=%d , bits_per_sample=%d",
|
||||
o->num_channels,o->sample_rate,o->bits_per_sample);
|
||||
|
||||
/* <<< for testing only */;
|
||||
return(1);
|
||||
|
||||
return(o->bits_per_sample>0); /* Audio format must be linear PCM */
|
||||
}
|
||||
|
||||
|
||||
static int libdax_audioxtr_identify(struct libdax_audioxtr *o, int flag)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret= libdax_audioxtr_identify_wav(o, 0);
|
||||
if(ret!=0)
|
||||
return(ret);
|
||||
if(o->fd==0) /* cannot rewind stdin */
|
||||
return(0);
|
||||
ret= libdax_audioxtr_identify_au(o, 0);
|
||||
if(ret!=0)
|
||||
return(ret);
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
/* @param flag bit0=msb_first */
|
||||
static unsigned libdax_audioxtr_to_int(struct libdax_audioxtr *o,
|
||||
unsigned char *bytes, int len, int flag)
|
||||
{
|
||||
unsigned int ret= 0;
|
||||
int i;
|
||||
|
||||
if(flag&1)
|
||||
for(i= 0; i<len; i++)
|
||||
ret= ret*256+bytes[i];
|
||||
else
|
||||
for(i= len-1; i>=0; i--)
|
||||
ret= ret*256+bytes[i];
|
||||
return(ret);
|
||||
}
|
||||
|
||||
|
||||
static int libdax_audioxtr_init_reading(struct libdax_audioxtr *o, int flag)
|
||||
{
|
||||
int ret;
|
||||
|
||||
|
||||
/* currently this only works for MS WAVE files .wav and Sun .au*/;
|
||||
if(o->fd==0) /* stdin: hope no read came after libdax_audioxtr_identify() */
|
||||
return(1);
|
||||
|
||||
o->extract_count= 0;
|
||||
if(strcmp(o->fmt,".wav")==0)
|
||||
ret= lseek(o->fd,44,SEEK_SET);
|
||||
else if(strcmp(o->fmt,".au")==0)
|
||||
ret= lseek(o->fd,o->au_data_location,SEEK_SET);
|
||||
else
|
||||
ret= -1;
|
||||
if(ret==-1)
|
||||
return(0);
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
int libdax_audioxtr_get_id(struct libdax_audioxtr *o,
|
||||
char **fmt, char **fmt_info,
|
||||
int *num_channels, int *sample_rate, int *bits_per_sample,
|
||||
int *msb_first, int flag)
|
||||
{
|
||||
*fmt= o->fmt;
|
||||
*fmt_info= o->fmt_info;
|
||||
*num_channels= o->num_channels;
|
||||
*sample_rate= o->sample_rate;
|
||||
*bits_per_sample= o->bits_per_sample;
|
||||
*msb_first= o->msb_first;
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
int libdax_audioxtr_get_size(struct libdax_audioxtr *o, off_t *size, int flag)
|
||||
{
|
||||
*size= o->data_size;
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
int libdax_audioxtr_read(struct libdax_audioxtr *o,
|
||||
char buffer[], int buffer_size, int flag)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if(buffer_size<=0 || o->fd<0)
|
||||
return(-2);
|
||||
if(o->data_size>0 && !(flag&1))
|
||||
if(buffer_size > o->data_size - o->extract_count)
|
||||
buffer_size= o->data_size - o->extract_count;
|
||||
if(buffer_size<=0)
|
||||
return(0);
|
||||
ret= read(o->fd,buffer,buffer_size);
|
||||
if(ret>0)
|
||||
o->extract_count+= ret;
|
||||
return(ret);
|
||||
}
|
||||
|
||||
|
||||
int libdax_audioxtr_detach_fd(struct libdax_audioxtr *o, int *fd, int flag)
|
||||
{
|
||||
if(o->fd<0)
|
||||
return(-1);
|
||||
if(strcmp(o->fmt,".wav")!=0 && strcmp(o->fmt,".au")!=0)
|
||||
return(0);
|
||||
if(flag&1) {
|
||||
*fd= o->fd;
|
||||
} else {
|
||||
*fd= dup(o->fd);
|
||||
if(*fd>=0 && strcmp(o->path,"-")!=0)
|
||||
close(o->fd);
|
||||
}
|
||||
if(*fd>=0) {
|
||||
o->fd= -1;
|
||||
return(1);
|
||||
}
|
||||
return(-1);
|
||||
}
|
||||
|
@ -1,229 +0,0 @@
|
||||
|
||||
/* libdax_audioxtr
|
||||
Audio track data extraction facility of libdax and libburn.
|
||||
Copyright (C) 2006 Thomas Schmitt <scdbackup@gmx.net>, provided under GPL
|
||||
*/
|
||||
|
||||
#ifndef LIBDAX_AUDIOXTR_H_INCLUDED
|
||||
#define LIBDAX_AUDIOXTR_H_INCLUDED 1
|
||||
|
||||
/* Public Macros */
|
||||
|
||||
/* Maximum size for address paths and fmt_info strings */
|
||||
#define LIBDAX_AUDIOXTR_STRLEN 4096
|
||||
|
||||
|
||||
/* Public Opaque Handles */
|
||||
|
||||
/** Extractor object encapsulating intermediate states of extraction.
|
||||
The clients of libdax_audioxtr shall only allocate pointers to this
|
||||
struct and get a storage object via libdax_audioxtr_new().
|
||||
Appropriate initial value for the pointer is NULL.
|
||||
*/
|
||||
struct libdax_audioxtr;
|
||||
|
||||
|
||||
/* Public Functions */
|
||||
|
||||
/* Calls initiated from inside libdax/libburn */
|
||||
|
||||
|
||||
/* Calls from applications (to be forwarded by libdax/libburn) */
|
||||
|
||||
|
||||
/** Open an audio file, check wether suitable, create extractor object.
|
||||
@param xtr Opaque handle to extractor. Gets attached extractor object.
|
||||
@param path Address of the audio file to extract. "-" is stdin (but might
|
||||
be not suitable for all futurely supported formats).
|
||||
@param flag Bitfield for control purposes (unused yet, submit 0)
|
||||
@return >0 success
|
||||
0 unsuitable format
|
||||
-1 severe error
|
||||
-2 path not found
|
||||
*/
|
||||
int libdax_audioxtr_new(struct libdax_audioxtr **xtr, char *path, int flag);
|
||||
|
||||
|
||||
/** Obtain identification parameters of opened audio source.
|
||||
@param xtr Opaque handle to extractor
|
||||
@param fmt Gets pointed to the audio file format id text: ".wav" , ".au"
|
||||
@param fmt_info Gets pointed to a format info text telling parameters
|
||||
@param num_channels e.g. 1=mono, 2=stereo, etc
|
||||
@param sample_rate e.g. 11025, 44100
|
||||
@param bits_per_sample e.g. 8= 8 bits per sample, 16= 16 bits ...
|
||||
@param msb_first Byte order of samples: 0=Intel 1=Motorola
|
||||
@param flag Bitfield for control purposes (unused yet, submit 0)
|
||||
@return >0 success, <=0 failure
|
||||
*/
|
||||
int libdax_audioxtr_get_id(struct libdax_audioxtr *xtr,
|
||||
char **fmt, char **fmt_info,
|
||||
int *num_channels, int *sample_rate,
|
||||
int *bits_per_sample, int *msb_first, int flag);
|
||||
|
||||
|
||||
/** Obtain a prediction about the extracted size based on internal information
|
||||
of the formatted file.
|
||||
@param xtr Opaque handle to extractor
|
||||
@param size Gets filled with the predicted size
|
||||
@param flag Bitfield for control purposes (unused yet, submit 0)
|
||||
@return 1 prediction was possible , 0 no prediction could be made
|
||||
*/
|
||||
int libdax_audioxtr_get_size(struct libdax_audioxtr *o, off_t *size, int flag);
|
||||
|
||||
|
||||
/** Obtain next buffer full of extracted data in desired format (only raw audio
|
||||
for now).
|
||||
@param xtr Opaque handle to extractor
|
||||
@param buffer Gets filled with extracted data
|
||||
@param buffer_size Maximum number of bytes to be filled into buffer
|
||||
@param flag Bitfield for control purposes
|
||||
bit0= do not stop at predicted end of data
|
||||
@return >0 number of valid buffer bytes,
|
||||
0 End of file
|
||||
-1 operating system reports error
|
||||
-2 usage error by application
|
||||
*/
|
||||
int libdax_audioxtr_read(struct libdax_audioxtr *xtr,
|
||||
char buffer[], int buffer_size, int flag);
|
||||
|
||||
|
||||
/** Try to obtain a file descriptor which will deliver extracted data
|
||||
to normal calls of read(2). This may fail because the format is
|
||||
unsuitable for that, but ".wav" is ok. If this call succeeds the xtr
|
||||
object will have forgotten its file descriptor and libdax_audioxtr_read()
|
||||
will return a usage error. One may use *fd after libdax_audioxtr_destroy()
|
||||
and will have to close it via close(2) when done with it.
|
||||
@param xtr Opaque handle to extractor
|
||||
@param fd Eventually returns the file descriptor number
|
||||
@param flag Bitfield for control purposes
|
||||
bit0= do not dup(2) and close(2) but hand out original fd
|
||||
@return 1 success, 0 cannot hand out fd , -1 severe error
|
||||
*/
|
||||
int libdax_audioxtr_detach_fd(struct libdax_audioxtr *o, int *fd, int flag);
|
||||
|
||||
|
||||
/** Clean up after extraction and destroy extractor object.
|
||||
@param xtr Opaque handle to extractor, *xtr is allowed to be NULL,
|
||||
*xtr is set to NULL by this function
|
||||
@param flag Bitfield for control purposes (unused yet, submit 0)
|
||||
@return 1 = destroyed object, 0 = was already destroyed
|
||||
*/
|
||||
int libdax_audioxtr_destroy(struct libdax_audioxtr **xtr, int flag);
|
||||
|
||||
|
||||
|
||||
#ifdef LIDBAX_AUDIOXTR________________
|
||||
|
||||
|
||||
-- place documentation text here ---
|
||||
|
||||
|
||||
#endif /* LIDBAX_AUDIOXTR_________________ */
|
||||
|
||||
|
||||
|
||||
/*
|
||||
*Never* set this macro outside libdax_audioxtr.c !
|
||||
The entrails of this facility are not to be seen by
|
||||
the other library components or the applications.
|
||||
*/
|
||||
#ifdef LIBDAX_AUDIOXTR_H_INTERNAL
|
||||
|
||||
/* Internal Structures */
|
||||
|
||||
/** Extractor object encapsulating intermediate states of extraction */
|
||||
struct libdax_audioxtr {
|
||||
|
||||
/* Source of the encoded audio data */
|
||||
char path[LIBDAX_AUDIOXTR_STRLEN];
|
||||
|
||||
/* File descriptor to path. Anything else than 0 must be lseek-able */
|
||||
int fd;
|
||||
|
||||
/* Format identifier. E.g. ".wav" */
|
||||
char fmt[80];
|
||||
|
||||
/* Format parameter info text */
|
||||
char fmt_info[LIBDAX_AUDIOXTR_STRLEN];
|
||||
|
||||
/* 1= mono, 2= stereo, etc. */
|
||||
int num_channels;
|
||||
|
||||
/* 8000, 44100, etc. */
|
||||
int sample_rate;
|
||||
|
||||
/* 8 bits = 8, 16 bits = 16, etc. */
|
||||
int bits_per_sample;
|
||||
|
||||
/* Byte order of samples: 0=Intel 1=Motorola */
|
||||
int msb_first;
|
||||
|
||||
/* Number of bytes to extract (0= unknown/unlimited) */
|
||||
off_t data_size;
|
||||
|
||||
/* Number of extracted data bytes */
|
||||
off_t extract_count;
|
||||
|
||||
|
||||
/* Format dependent parameters */
|
||||
|
||||
/* MS WAVE Format */
|
||||
/* info used: http://ccrma.stanford.edu/courses/422/projects/WaveFormat/ */
|
||||
|
||||
/* == NumSamples * NumChannels * BitsPerSample/8
|
||||
This is the number of bytes in the data. */
|
||||
unsigned wav_subchunk2_size;
|
||||
|
||||
|
||||
/* Sun Audio, .au */
|
||||
/* info used: http://www.opengroup.org/public/pubs/external/auformat.html */
|
||||
|
||||
/* Number of bytes in non-payload header part */
|
||||
unsigned au_data_location;
|
||||
|
||||
/* Number of payload bytes or 0xffffffff */
|
||||
unsigned au_data_size;
|
||||
|
||||
};
|
||||
|
||||
|
||||
/* Internal Functions */
|
||||
|
||||
/** Open the audio source pointed to by .path and evaluate suitability.
|
||||
@return -1 failure to open, 0 unsuitable format, 1 success
|
||||
*/
|
||||
static int libdax_audioxtr_open(struct libdax_audioxtr *o, int flag);
|
||||
|
||||
|
||||
/** Identify format and evaluate suitability.
|
||||
@return 0 unsuitable format, 1 format is suitable
|
||||
*/
|
||||
static int libdax_audioxtr_identify(struct libdax_audioxtr *o, int flag);
|
||||
|
||||
/** Specialized identifier for .wav */
|
||||
static int libdax_audioxtr_identify_wav(struct libdax_audioxtr *o, int flag);
|
||||
/** Specialized identifier for .au */
|
||||
static int libdax_audioxtr_identify_au(struct libdax_audioxtr *o, int flag);
|
||||
|
||||
|
||||
/** Convert a byte string into a number (currently only little endian)
|
||||
@param flag Bitfield for control purposes
|
||||
bit0=msb_first
|
||||
@return The resulting number
|
||||
*/
|
||||
static unsigned libdax_audioxtr_to_int(struct libdax_audioxtr *o,
|
||||
unsigned char *bytes, int len, int flag);
|
||||
|
||||
|
||||
/** Prepare for reading of first buffer.
|
||||
@return 0 error, 1 success
|
||||
*/
|
||||
static int libdax_audioxtr_init_reading(struct libdax_audioxtr *o, int flag);
|
||||
|
||||
|
||||
|
||||
#endif /* LIBDAX_AUDIOXTR_H_INTERNAL */
|
||||
|
||||
|
||||
#endif /* ! LIBDAX_AUDIOXTR_H_INCLUDED */
|
||||
|
@ -1,404 +0,0 @@
|
||||
|
||||
/* libdax_msgs
|
||||
Message handling facility of libdax.
|
||||
Copyright (C) 2006 Thomas Schmitt <scdbackup@gmx.net>, provided under GPL
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
/* Only this single source module is entitled to do this */
|
||||
#define LIBDAX_MSGS_H_INTERNAL 1
|
||||
|
||||
/* All participants in the messaging system must do this */
|
||||
#include "libdax_msgs.h"
|
||||
|
||||
|
||||
/* ----------------------------- libdax_msgs_item ------------------------- */
|
||||
|
||||
|
||||
static int libdax_msgs_item_new(struct libdax_msgs_item **item,
|
||||
struct libdax_msgs_item *link, int flag)
|
||||
{
|
||||
int ret;
|
||||
struct libdax_msgs_item *o;
|
||||
struct timeval tv;
|
||||
struct timezone tz;
|
||||
|
||||
(*item)= o=
|
||||
(struct libdax_msgs_item *) malloc(sizeof(struct libdax_msgs_item));
|
||||
if(o==NULL)
|
||||
return(-1);
|
||||
o->timestamp= 0.0;
|
||||
ret= gettimeofday(&tv,&tz);
|
||||
if(ret==0)
|
||||
o->timestamp= tv.tv_sec+0.000001*tv.tv_usec;
|
||||
o->process_id= getpid();
|
||||
o->driveno= -1;
|
||||
o->severity= LIBDAX_MSGS_SEV_ALL;
|
||||
o->priority= LIBDAX_MSGS_PRIO_ZERO;
|
||||
o->error_code= 0;
|
||||
o->msg_text= NULL;
|
||||
o->os_errno= 0;
|
||||
o->prev= link;
|
||||
o->next= NULL;
|
||||
if(link!=NULL) {
|
||||
if(link->next!=NULL) {
|
||||
link->next->prev= o;
|
||||
o->next= link->next;
|
||||
}
|
||||
link->next= o;
|
||||
}
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
/** Detaches item from its queue and eventually readjusts start, end pointers
|
||||
of the queue */
|
||||
int libdax_msgs_item_unlink(struct libdax_msgs_item *o,
|
||||
struct libdax_msgs_item **chain_start,
|
||||
struct libdax_msgs_item **chain_end, int flag)
|
||||
{
|
||||
if(o->prev!=NULL)
|
||||
o->prev->next= o->next;
|
||||
if(o->next!=NULL)
|
||||
o->next->prev= o->prev;
|
||||
if(chain_start!=NULL)
|
||||
if(*chain_start == o)
|
||||
*chain_start= o->next;
|
||||
if(chain_end!=NULL)
|
||||
if(*chain_end == o)
|
||||
*chain_end= o->prev;
|
||||
o->next= o->prev= NULL;
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
int libdax_msgs_item_destroy(struct libdax_msgs_item **item,
|
||||
int flag)
|
||||
{
|
||||
struct libdax_msgs_item *o;
|
||||
|
||||
o= *item;
|
||||
if(o==NULL)
|
||||
return(0);
|
||||
libdax_msgs_item_unlink(o,NULL,NULL,0);
|
||||
if(o->msg_text!=NULL)
|
||||
free((char *) o->msg_text);
|
||||
free((char *) o);
|
||||
*item= NULL;
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
int libdax_msgs_item_get_msg(struct libdax_msgs_item *item,
|
||||
int *error_code, char **msg_text, int *os_errno,
|
||||
int flag)
|
||||
{
|
||||
*error_code= item->error_code;
|
||||
*msg_text= item->msg_text;
|
||||
*os_errno= item->os_errno;
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
int libdax_msgs_item_get_origin(struct libdax_msgs_item *item,
|
||||
double *timestamp, pid_t *process_id, int *driveno,
|
||||
int flag)
|
||||
{
|
||||
*timestamp= item->timestamp;
|
||||
*process_id= item->process_id;
|
||||
*driveno= item->driveno;
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
int libdax_msgs_item_get_rank(struct libdax_msgs_item *item,
|
||||
int *severity, int *priority, int flag)
|
||||
{
|
||||
*severity= item->severity;
|
||||
*priority= item->priority;
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------- libdax_msgs ---------------------------- */
|
||||
|
||||
|
||||
int libdax_msgs_new(struct libdax_msgs **m, int flag)
|
||||
{
|
||||
struct libdax_msgs *o;
|
||||
|
||||
(*m)= o= (struct libdax_msgs *) malloc(sizeof(struct libdax_msgs));
|
||||
if(o==NULL)
|
||||
return(-1);
|
||||
o->oldest= NULL;
|
||||
o->youngest= NULL;
|
||||
o->count= 0;
|
||||
o->queue_severity= LIBDAX_MSGS_SEV_ALL;
|
||||
o->print_severity= LIBDAX_MSGS_SEV_NEVER;
|
||||
strcpy(o->print_id,"libdax: ");
|
||||
|
||||
#ifndef LIBDAX_MSGS_SINGLE_THREADED
|
||||
pthread_mutex_init(&(o->lock_mutex),NULL);
|
||||
#endif
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
int libdax_msgs_destroy(struct libdax_msgs **m, int flag)
|
||||
{
|
||||
struct libdax_msgs *o;
|
||||
struct libdax_msgs_item *item, *next_item;
|
||||
|
||||
o= *m;
|
||||
if(o==NULL)
|
||||
return(0);
|
||||
|
||||
#ifndef LIBDAX_MSGS_SINGLE_THREADED
|
||||
if(pthread_mutex_destroy(&(o->lock_mutex))!=0) {
|
||||
pthread_mutex_unlock(&(o->lock_mutex));
|
||||
pthread_mutex_destroy(&(o->lock_mutex));
|
||||
}
|
||||
#endif
|
||||
|
||||
for(item= o->oldest; item!=NULL; item= next_item) {
|
||||
next_item= item->next;
|
||||
libdax_msgs_item_destroy(&item,0);
|
||||
}
|
||||
free((char *) o);
|
||||
*m= NULL;
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
int libdax_msgs_set_severities(struct libdax_msgs *m, int queue_severity,
|
||||
int print_severity, char *print_id, int flag)
|
||||
{
|
||||
m->queue_severity= queue_severity;
|
||||
m->print_severity= print_severity;
|
||||
strncpy(m->print_id,print_id,80);
|
||||
m->print_id[80]= 0;
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
static int libdax_msgs_lock(struct libdax_msgs *m, int flag)
|
||||
{
|
||||
|
||||
#ifndef LIBDAX_MSGS_SINGLE_THREADED
|
||||
int ret;
|
||||
|
||||
ret= pthread_mutex_lock(&(m->lock_mutex));
|
||||
if(ret!=0)
|
||||
return(0);
|
||||
#endif
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
static int libdax_msgs_unlock(struct libdax_msgs *m, int flag)
|
||||
{
|
||||
|
||||
#ifndef LIBDAX_MSGS_SINGLE_THREADED
|
||||
int ret;
|
||||
|
||||
ret= pthread_mutex_unlock(&(m->lock_mutex));
|
||||
if(ret!=0)
|
||||
return(0);
|
||||
#endif
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
int libdax_msgs__text_to_sev(char *severity_name, int *severity,
|
||||
int flag)
|
||||
{
|
||||
if(strncmp(severity_name,"NEVER",5)==0)
|
||||
*severity= LIBDAX_MSGS_SEV_NEVER;
|
||||
else if(strncmp(severity_name,"ABORT",5)==0)
|
||||
*severity= LIBDAX_MSGS_SEV_ABORT;
|
||||
else if(strncmp(severity_name,"FATAL",5)==0)
|
||||
*severity= LIBDAX_MSGS_SEV_FATAL;
|
||||
else if(strncmp(severity_name,"SORRY",5)==0)
|
||||
*severity= LIBDAX_MSGS_SEV_SORRY;
|
||||
else if(strncmp(severity_name,"WARNING",7)==0)
|
||||
*severity= LIBDAX_MSGS_SEV_WARNING;
|
||||
else if(strncmp(severity_name,"HINT",4)==0)
|
||||
*severity= LIBDAX_MSGS_SEV_HINT;
|
||||
else if(strncmp(severity_name,"NOTE",4)==0)
|
||||
*severity= LIBDAX_MSGS_SEV_NOTE;
|
||||
else if(strncmp(severity_name,"UPDATE",6)==0)
|
||||
*severity= LIBDAX_MSGS_SEV_UPDATE;
|
||||
else if(strncmp(severity_name,"DEBUG",5)==0)
|
||||
*severity= LIBDAX_MSGS_SEV_DEBUG;
|
||||
else if(strncmp(severity_name,"ALL",3)==0)
|
||||
*severity= LIBDAX_MSGS_SEV_ALL;
|
||||
else {
|
||||
*severity= LIBDAX_MSGS_SEV_NEVER;
|
||||
return(0);
|
||||
}
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
int libdax_msgs__sev_to_text(int severity, char **severity_name,
|
||||
int flag)
|
||||
{
|
||||
if(flag&1) {
|
||||
*severity_name=
|
||||
"NEVER\nABORT\nFATAL\nSORRY\nWARNING\nHINT\nNOTE\nUPDATE\nDEBUG\nALL";
|
||||
return(1);
|
||||
}
|
||||
*severity_name= "";
|
||||
if(severity>=LIBDAX_MSGS_SEV_NEVER)
|
||||
*severity_name= "NEVER";
|
||||
else if(severity>=LIBDAX_MSGS_SEV_ABORT)
|
||||
*severity_name= "ABORT";
|
||||
else if(severity>=LIBDAX_MSGS_SEV_FATAL)
|
||||
*severity_name= "FATAL";
|
||||
else if(severity>=LIBDAX_MSGS_SEV_SORRY)
|
||||
*severity_name= "SORRY";
|
||||
else if(severity>=LIBDAX_MSGS_SEV_WARNING)
|
||||
*severity_name= "WARNING";
|
||||
else if(severity>=LIBDAX_MSGS_SEV_HINT)
|
||||
*severity_name= "HINT";
|
||||
else if(severity>=LIBDAX_MSGS_SEV_NOTE)
|
||||
*severity_name= "NOTE";
|
||||
else if(severity>=LIBDAX_MSGS_SEV_UPDATE)
|
||||
*severity_name= "UPDATE";
|
||||
else if(severity>=LIBDAX_MSGS_SEV_DEBUG)
|
||||
*severity_name= "DEBUG";
|
||||
else if(severity>=LIBDAX_MSGS_SEV_ALL)
|
||||
*severity_name= "ALL";
|
||||
else {
|
||||
*severity_name= "";
|
||||
return(0);
|
||||
}
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
int libdax_msgs_submit(struct libdax_msgs *m, int driveno, int error_code,
|
||||
int severity, int priority, char *msg_text,
|
||||
int os_errno, int flag)
|
||||
{
|
||||
int ret;
|
||||
char *textpt,*sev_name,sev_text[81];
|
||||
struct libdax_msgs_item *item= NULL;
|
||||
|
||||
if(severity >= m->print_severity) {
|
||||
if(msg_text==NULL)
|
||||
textpt= "";
|
||||
else
|
||||
textpt= msg_text;
|
||||
sev_text[0]= 0;
|
||||
ret= libdax_msgs__sev_to_text(severity,&sev_name,0);
|
||||
if(ret>0)
|
||||
sprintf(sev_text,"%s : ",sev_name);
|
||||
|
||||
fprintf(stderr,"%s%s%s\n",m->print_id,sev_text,textpt);
|
||||
if(os_errno!=0) {
|
||||
ret= libdax_msgs_lock(m,0);
|
||||
if(ret<=0)
|
||||
return(-1);
|
||||
fprintf(stderr,"%s( Most recent system error: %d '%s' )\n",
|
||||
m->print_id,os_errno,strerror(os_errno));
|
||||
libdax_msgs_unlock(m,0);
|
||||
}
|
||||
|
||||
}
|
||||
if(severity < m->queue_severity)
|
||||
return(0);
|
||||
|
||||
ret= libdax_msgs_lock(m,0);
|
||||
if(ret<=0)
|
||||
return(-1);
|
||||
ret= libdax_msgs_item_new(&item,m->youngest,0);
|
||||
if(ret<=0)
|
||||
goto failed;
|
||||
item->driveno= driveno;
|
||||
item->error_code= error_code;
|
||||
item->severity= severity;
|
||||
item->priority= priority;
|
||||
if(msg_text!=NULL) {
|
||||
item->msg_text= malloc(strlen(msg_text)+1);
|
||||
if(item->msg_text==NULL)
|
||||
goto failed;
|
||||
strcpy(item->msg_text,msg_text);
|
||||
}
|
||||
item->os_errno= os_errno;
|
||||
if(m->oldest==NULL)
|
||||
m->oldest= item;
|
||||
m->youngest= item;
|
||||
m->count++;
|
||||
libdax_msgs_unlock(m,0);
|
||||
|
||||
/*
|
||||
fprintf(stderr,"libdax_experimental: message submitted to queue (now %d)\n",
|
||||
m->count);
|
||||
*/
|
||||
|
||||
return(1);
|
||||
failed:;
|
||||
libdax_msgs_item_destroy(&item,0);
|
||||
libdax_msgs_unlock(m,0);
|
||||
return(-1);
|
||||
}
|
||||
|
||||
|
||||
int libdax_msgs_obtain(struct libdax_msgs *m, struct libdax_msgs_item **item,
|
||||
int severity, int priority, int flag)
|
||||
{
|
||||
int ret;
|
||||
struct libdax_msgs_item *im, *next_im= NULL;
|
||||
|
||||
*item= NULL;
|
||||
ret= libdax_msgs_lock(m,0);
|
||||
if(ret<=0)
|
||||
return(-1);
|
||||
for(im= m->oldest; im!=NULL; im= next_im) {
|
||||
for(; im!=NULL; im= next_im) {
|
||||
next_im= im->next;
|
||||
if(im->severity>=severity)
|
||||
break;
|
||||
libdax_msgs_item_unlink(im,&(m->oldest),&(m->youngest),0);
|
||||
libdax_msgs_item_destroy(&im,0); /* severity too low: delete */
|
||||
}
|
||||
if(im==NULL)
|
||||
break;
|
||||
if(im->priority>=priority)
|
||||
break;
|
||||
}
|
||||
if(im==NULL)
|
||||
{ret= 0; goto ex;}
|
||||
libdax_msgs_item_unlink(im,&(m->oldest),&(m->youngest),0);
|
||||
*item= im;
|
||||
ret= 1;
|
||||
ex:;
|
||||
libdax_msgs_unlock(m,0);
|
||||
return(ret);
|
||||
}
|
||||
|
||||
|
||||
int libdax_msgs_destroy_item(struct libdax_msgs *m,
|
||||
struct libdax_msgs_item **item, int flag)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret= libdax_msgs_lock(m,0);
|
||||
if(ret<=0)
|
||||
return(-1);
|
||||
ret= libdax_msgs_item_destroy(item,0);
|
||||
libdax_msgs_unlock(m,0);
|
||||
return(ret);
|
||||
}
|
||||
|
@ -1,382 +0,0 @@
|
||||
|
||||
/* libdax_msgs
|
||||
Message handling facility of libdax.
|
||||
Copyright (C) 2006 Thomas Schmitt <scdbackup@gmx.net>, provided under GPL
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
*Never* set this macro outside libdax_msgs.c !
|
||||
The entrails of the message handling facility are not to be seen by
|
||||
the other library components or the applications.
|
||||
*/
|
||||
#ifdef LIBDAX_MSGS_H_INTERNAL
|
||||
|
||||
|
||||
#ifndef LIBDAX_MSGS_SINGLE_THREADED
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
|
||||
struct libdax_msgs_item {
|
||||
|
||||
double timestamp;
|
||||
pid_t process_id;
|
||||
int driveno;
|
||||
|
||||
int severity;
|
||||
int priority;
|
||||
|
||||
/* Apply for your developer's error code range at
|
||||
libburn-hackers@pykix.org
|
||||
Report introduced codes in the list below. */
|
||||
int error_code;
|
||||
|
||||
char *msg_text;
|
||||
int os_errno;
|
||||
|
||||
struct libdax_msgs_item *prev,*next;
|
||||
|
||||
};
|
||||
|
||||
|
||||
struct libdax_msgs {
|
||||
|
||||
struct libdax_msgs_item *oldest;
|
||||
struct libdax_msgs_item *youngest;
|
||||
int count;
|
||||
|
||||
int queue_severity;
|
||||
int print_severity;
|
||||
char print_id[81];
|
||||
|
||||
#ifndef LIBDAX_MSGS_SINGLE_THREADED
|
||||
pthread_mutex_t lock_mutex;
|
||||
#endif
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif /* LIBDAX_MSGS_H_INTERNAL */
|
||||
|
||||
|
||||
#ifndef LIBDAX_MSGS_H_INCLUDED
|
||||
#define LIBDAX_MSGS_H_INCLUDED 1
|
||||
|
||||
|
||||
#ifndef LIBDAX_MSGS_H_INTERNAL
|
||||
|
||||
|
||||
/* Public Opaque Handles */
|
||||
|
||||
/** A pointer to this is a opaque handle to a message handling facility */
|
||||
struct libdax_msgs;
|
||||
|
||||
/** A pointer to this is a opaque handle to a single message item */
|
||||
struct libdax_msgs_item;
|
||||
|
||||
#endif /* ! LIBDAX_MSGS_H_INTERNAL */
|
||||
|
||||
|
||||
/* Public Macros */
|
||||
|
||||
|
||||
/* Registered Severities */
|
||||
|
||||
/* It is well advisable to let applications select severities via strings and
|
||||
forwarded functions libdax_msgs__text_to_sev(), libdax_msgs__sev_to_text().
|
||||
These macros are for use by libdax/libburn only.
|
||||
*/
|
||||
|
||||
/** Use this to get messages of any severity. Do not use for submitting.
|
||||
*/
|
||||
#define LIBDAX_MSGS_SEV_ALL 0x00000000
|
||||
|
||||
/** Debugging messages not to be visible to normal users by default
|
||||
*/
|
||||
#define LIBDAX_MSGS_SEV_DEBUG 0x10000000
|
||||
|
||||
/** Update of a progress report about long running actions
|
||||
*/
|
||||
#define LIBDAX_MSGS_SEV_UPDATE 0x20000000
|
||||
|
||||
/** Not so usual events which were gracefully handled
|
||||
*/
|
||||
#define LIBDAX_MSGS_SEV_NOTE 0x30000000
|
||||
|
||||
/** Possibilities to achieve a better result
|
||||
*/
|
||||
#define LIBDAX_MSGS_SEV_HINT 0x40000000
|
||||
|
||||
/** Warnings about problems which could not be handled optimally
|
||||
*/
|
||||
#define LIBDAX_MSGS_SEV_WARNING 0x50000000
|
||||
|
||||
/** Non-fatal error messages indicating that parts of the action failed
|
||||
but processing will/should go on
|
||||
*/
|
||||
#define LIBDAX_MSGS_SEV_SORRY 0x60000000
|
||||
|
||||
/** An error message which puts the whole operation of libdax in question
|
||||
*/
|
||||
#define LIBDAX_MSGS_SEV_FATAL 0x70000000
|
||||
|
||||
/** A message from an abort handler which will finally finish libburn
|
||||
*/
|
||||
#define LIBDAX_MSGS_SEV_ABORT 0x71000000
|
||||
|
||||
/** A severity to exclude resp. discard any possible message.
|
||||
Do not use this severity for submitting.
|
||||
*/
|
||||
#define LIBDAX_MSGS_SEV_NEVER 0x7fffffff
|
||||
|
||||
|
||||
/* Registered Priorities */
|
||||
|
||||
/* Priorities are to be used by libburn/libdax only. */
|
||||
|
||||
#define LIBDAX_MSGS_PRIO_ZERO 0x00000000
|
||||
#define LIBDAX_MSGS_PRIO_LOW 0x10000000
|
||||
#define LIBDAX_MSGS_PRIO_MEDIUM 0x20000000
|
||||
#define LIBDAX_MSGS_PRIO_HIGH 0x30000000
|
||||
#define LIBDAX_MSGS_PRIO_TOP 0x7ffffffe
|
||||
|
||||
/* Do not use this priority for submitting */
|
||||
#define LIBDAX_MSGS_PRIO_NEVER 0x7fffffff
|
||||
|
||||
|
||||
/* Public Functions */
|
||||
|
||||
/* Calls initiated from inside libdax/libburn */
|
||||
|
||||
|
||||
/** Create new empty message handling facility with queue.
|
||||
@param flag Bitfield for control purposes (unused yet, submit 0)
|
||||
@return >0 success, <=0 failure
|
||||
*/
|
||||
int libdax_msgs_new(struct libdax_msgs **m, int flag);
|
||||
|
||||
|
||||
/** Destroy a message handling facility and all its eventual messages.
|
||||
The submitted pointer gets set to NULL.
|
||||
@param flag Bitfield for control purposes (unused yet, submit 0)
|
||||
@return 1 for success, 0 for pointer to NULL
|
||||
*/
|
||||
int libdax_msgs_destroy(struct libdax_msgs **m, int flag);
|
||||
|
||||
|
||||
/** Submit a message to a message handling facility.
|
||||
@param driveno libdax drive number. Use -1 if no number is known.
|
||||
@param error_code Unique error code. Use only registered codes. See below.
|
||||
The same unique error_code may be issued at different
|
||||
occasions but those should be equivalent out of the view
|
||||
of a libdax application. (E.g. "cannot open ATA drive"
|
||||
versus "cannot open SCSI drive" would be equivalent.)
|
||||
@param severity The LIBDAX_MSGS_SEV_* of the event.
|
||||
@param priority The LIBDAX_MSGS_PRIO_* number of the event.
|
||||
@param msg_text Printable and human readable message text.
|
||||
@param os_errno Eventual error code from operating system (0 if none)
|
||||
@param flag Bitfield for control purposes (unused yet, submit 0)
|
||||
@return 1 on success, 0 on rejection, <0 for severe errors
|
||||
*/
|
||||
int libdax_msgs_submit(struct libdax_msgs *m, int driveno, int error_code,
|
||||
int severity, int priority, char *msg_text,
|
||||
int os_errno, int flag);
|
||||
|
||||
|
||||
/* Calls from applications (to be forwarded by libdax/libburn) */
|
||||
|
||||
|
||||
/** Convert a registered severity number into a severity name
|
||||
@param flag Bitfield for control purposes:
|
||||
bit0= list all severity names in a newline separated string
|
||||
@return >0 success, <=0 failure
|
||||
*/
|
||||
int libdax_msgs__sev_to_text(int severity, char **severity_name,
|
||||
int flag);
|
||||
|
||||
|
||||
/** Convert a severity name into a severity number,
|
||||
@param flag Bitfield for control purposes (unused yet, submit 0)
|
||||
@return >0 success, <=0 failure
|
||||
*/
|
||||
int libdax_msgs__text_to_sev(char *severity_name, int *severity,
|
||||
int flag);
|
||||
|
||||
|
||||
/** Set minimum severity for messages to be queued (default
|
||||
LIBDAX_MSGS_SEV_ALL) and for messages to be printed directly to stderr
|
||||
(default LIBDAX_MSGS_SEV_NEVER).
|
||||
@param print_id A text of at most 80 characters to be printed before
|
||||
any eventually printed message (default is "libdax: ").
|
||||
@param flag Bitfield for control purposes (unused yet, submit 0)
|
||||
@return always 1 for now
|
||||
*/
|
||||
int libdax_msgs_set_severities(struct libdax_msgs *m, int queue_severity,
|
||||
int print_severity, char *print_id, int flag);
|
||||
|
||||
|
||||
/** Obtain a message item that has at least the given severity and priority.
|
||||
Usually all older messages of lower severity are discarded then. If no
|
||||
item of sufficient severity was found, all others are discarded from the
|
||||
queue.
|
||||
@param flag Bitfield for control purposes (unused yet, submit 0)
|
||||
@return 1 if a matching item was found, 0 if not, <0 for severe errors
|
||||
*/
|
||||
int libdax_msgs_obtain(struct libdax_msgs *m, struct libdax_msgs_item **item,
|
||||
int severity, int priority, int flag);
|
||||
|
||||
|
||||
/** Destroy a message item obtained by libdax_msgs_obtain(). The submitted
|
||||
pointer gets set to NULL.
|
||||
Caution: Copy eventually obtained msg_text before destroying the item,
|
||||
if you want to use it further.
|
||||
@param flag Bitfield for control purposes (unused yet, submit 0)
|
||||
@return 1 for success, 0 for pointer to NULL, <0 for severe errors
|
||||
*/
|
||||
int libdax_msgs_destroy_item(struct libdax_msgs *m,
|
||||
struct libdax_msgs_item **item, int flag);
|
||||
|
||||
|
||||
/** Obtain from a message item the three application oriented components as
|
||||
submitted with the originating call of libdax_msgs_submit().
|
||||
Caution: msg_text becomes a pointer into item, not a copy.
|
||||
@param flag Bitfield for control purposes (unused yet, submit 0)
|
||||
@return 1 on success, 0 on invalid item, <0 for servere errors
|
||||
*/
|
||||
int libdax_msgs_item_get_msg(struct libdax_msgs_item *item,
|
||||
int *error_code, char **msg_text, int *os_errno,
|
||||
int flag);
|
||||
|
||||
|
||||
/** Obtain from a message item the submitter identification submitted
|
||||
with the originating call of libdax_msgs_submit().
|
||||
@param flag Bitfield for control purposes (unused yet, submit 0)
|
||||
@return 1 on success, 0 on invalid item, <0 for servere errors
|
||||
*/
|
||||
int libdax_msgs_item_get_origin(struct libdax_msgs_item *item,
|
||||
double *timestamp, pid_t *process_id, int *driveno,
|
||||
int flag);
|
||||
|
||||
|
||||
/** Obtain from a message item severity and priority as submitted
|
||||
with the originating call of libdax_msgs_submit().
|
||||
@param flag Bitfield for control purposes (unused yet, submit 0)
|
||||
@return 1 on success, 0 on invalid item, <0 for servere errors
|
||||
*/
|
||||
int libdax_msgs_item_get_rank(struct libdax_msgs_item *item,
|
||||
int *severity, int *priority, int flag);
|
||||
|
||||
|
||||
#ifdef LIDBAX_MSGS_________________
|
||||
|
||||
|
||||
/* Registered Error Codes */
|
||||
|
||||
|
||||
Format: error_code (LIBDAX_MSGS_SEV_*,LIBDAX_MSGS_PRIO_*) = explanation
|
||||
If no severity or priority are fixely associates, use "(,)".
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
Range "libdax_msgs" : 0x00000000 to 0x0000ffff
|
||||
|
||||
0x00000000 (ALL,ZERO) = Initial setting in new libdax_msgs_item
|
||||
0x00000001 (DEBUG,ZERO) = Test error message
|
||||
0x00000002 (DEBUG,ZERO) = Debugging message
|
||||
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
Range "elmom" : 0x00010000 to 0x0001ffff
|
||||
|
||||
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
Range "scdbackup" : 0x00020000 to 0x0002ffff
|
||||
|
||||
Acessing and defending drives:
|
||||
|
||||
0x00020001 (SORRY,LOW) = Cannot open busy device
|
||||
0x00020002 (SORRY,HIGH) = Encountered error when closing drive
|
||||
0x00020003 (SORRY,HIGH) = Could not grab drive
|
||||
0x00020004 (NOTE,HIGH) = Opened O_EXCL scsi sibling
|
||||
0x00020005 (FATAL,HIGH) = Failed to open device
|
||||
0x00020006 (FATAL,HIGH) = Too many scsi siblings
|
||||
0x00020007 (NOTE,HIGH) = Closed O_EXCL scsi siblings
|
||||
|
||||
General library operations:
|
||||
|
||||
0x00020101 (WARNING,HIGH) = Cannot find given worker item
|
||||
0x00020102 (SORRY,HIGH) = A drive operation is still going on
|
||||
0x00020103 (WARNING,HIGH) = After scan a drive operation is still going on
|
||||
0x00020104 (SORRY,HIGH) = NULL pointer caught
|
||||
0x00020105 (SORRY,HIGH) = Drive is already released
|
||||
0x00020106 (SORRY,HIGH) = Drive is busy on attempt to close
|
||||
0x00020107 (SORRY,HIGH) = Drive is busy on attempt to shut down library
|
||||
0x00020108 (SORRY,HIGH) = Drive is not grabbed on disc status inquiry
|
||||
0x00020108 (FATAL,HIGH) = Could not allocate new drive object
|
||||
0x00020109 (FATAL,HIGH) = Library not running
|
||||
0x0002010a (FATAL,HIGH) = Unsuitable track mode
|
||||
0x0002010b (FATAL,HIGH) = Burn run failed
|
||||
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 (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
|
||||
0x00020113 (FATAL,HIGH) = Drive capabilities not inquired yet
|
||||
0x00020114 (SORRY,HIGH) = Attempt to set ISRC with bad data
|
||||
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
|
||||
0x00020201 (SORRY,HIGH) = Audio source file has unsuitable format
|
||||
0x00020202 (SORRY,HIGH) = Failed to prepare reading of audio data
|
||||
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
#endif /* LIDBAX_MSGS_________________ */
|
||||
|
||||
|
||||
|
||||
#ifdef LIBDAX_MSGS_H_INTERNAL
|
||||
|
||||
/* Internal Functions */
|
||||
|
||||
|
||||
/** Lock before doing side effect operations on m */
|
||||
static int libdax_msgs_lock(struct libdax_msgs *m, int flag);
|
||||
|
||||
/** Unlock after effect operations on m are done */
|
||||
static int libdax_msgs_unlock(struct libdax_msgs *m, int flag);
|
||||
|
||||
|
||||
/** Create new empty message item.
|
||||
@param link Previous item in queue
|
||||
@param flag Bitfield for control purposes (unused yet, submit 0)
|
||||
@return >0 success, <=0 failure
|
||||
*/
|
||||
static int libdax_msgs_item_new(struct libdax_msgs_item **item,
|
||||
struct libdax_msgs_item *link, int flag);
|
||||
|
||||
/** Destroy a message item obtained by libdax_msgs_obtain(). The submitted
|
||||
pointer gets set to NULL.
|
||||
@param flag Bitfield for control purposes (unused yet, submit 0)
|
||||
@return 1 for success, 0 for pointer to NULL
|
||||
*/
|
||||
static int libdax_msgs_item_destroy(struct libdax_msgs_item **item, int flag);
|
||||
|
||||
|
||||
#endif /* LIBDAX_MSGS_H_INTERNAL */
|
||||
|
||||
|
||||
#endif /* ! LIBDAX_MSGS_H_INCLUDED */
|
108
libburn/message.c
Normal file
108
libburn/message.c
Normal file
@ -0,0 +1,108 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
|
||||
#include "message.h"
|
||||
#include "libburn.h"
|
||||
#include "debug.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
struct message_list
|
||||
{
|
||||
struct message_list *next;
|
||||
|
||||
struct burn_message *msg;
|
||||
};
|
||||
|
||||
static struct message_list *queue;
|
||||
|
||||
void burn_message_free(struct burn_message *msg)
|
||||
{
|
||||
free(msg);
|
||||
}
|
||||
|
||||
void burn_message_clear_queue(void)
|
||||
{
|
||||
struct burn_message *msg;
|
||||
|
||||
if ((msg = burn_get_message())) {
|
||||
burn_print(0,
|
||||
"YOU HAVE MESSAGES QUEUED FROM THE LAST OPERATION. "
|
||||
"YOU SHOULD BE GRABBING THEM ALL!\n");
|
||||
do {
|
||||
burn_message_free(msg);
|
||||
} while ((msg = burn_get_message()));
|
||||
}
|
||||
}
|
||||
|
||||
struct burn_message *burn_get_message()
|
||||
{
|
||||
struct burn_message *msg = NULL;
|
||||
|
||||
if (queue) {
|
||||
struct message_list *next;
|
||||
|
||||
next = queue->next;
|
||||
msg = queue->msg;
|
||||
free(queue);
|
||||
queue = next;
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
static void queue_push_tail(struct burn_message *msg)
|
||||
{
|
||||
struct message_list *node;
|
||||
|
||||
node = malloc(sizeof(struct message_list));
|
||||
node->next = NULL;
|
||||
node->msg = msg;
|
||||
|
||||
if (!queue)
|
||||
queue = node;
|
||||
else {
|
||||
struct message_list *it;
|
||||
|
||||
for (it = queue; it->next; it = it->next) ;
|
||||
it->next = node;
|
||||
}
|
||||
}
|
||||
|
||||
void burn_message_info_new(struct burn_drive *drive,
|
||||
enum burn_message_info message)
|
||||
{
|
||||
struct burn_message *msg;
|
||||
|
||||
msg = malloc(sizeof(struct burn_message));
|
||||
msg->drive = drive;
|
||||
msg->type = BURN_MESSAGE_INFO;
|
||||
msg->detail.info.message = message;
|
||||
|
||||
queue_push_tail(msg);
|
||||
}
|
||||
|
||||
void burn_message_warning_new(struct burn_drive *drive,
|
||||
enum burn_message_info message)
|
||||
{
|
||||
struct burn_message *msg;
|
||||
|
||||
msg = malloc(sizeof(struct burn_message));
|
||||
msg->drive = drive;
|
||||
msg->type = BURN_MESSAGE_WARNING;
|
||||
msg->detail.warning.message = message;
|
||||
|
||||
queue_push_tail(msg);
|
||||
}
|
||||
|
||||
void burn_message_error_new(struct burn_drive *drive,
|
||||
enum burn_message_info message)
|
||||
{
|
||||
struct burn_message *msg;
|
||||
|
||||
msg = malloc(sizeof(struct burn_message));
|
||||
msg->drive = drive;
|
||||
msg->type = BURN_MESSAGE_ERROR;
|
||||
msg->detail.error.message = message;
|
||||
|
||||
queue_push_tail(msg);
|
||||
}
|
19
libburn/message.h
Normal file
19
libburn/message.h
Normal file
@ -0,0 +1,19 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
|
||||
#ifndef __MESSAGE
|
||||
#define __MESSAGE
|
||||
|
||||
#include "libburn.h"
|
||||
|
||||
void burn_message_clear_queue(void);
|
||||
|
||||
void burn_message_info_new(struct burn_drive *drive,
|
||||
enum burn_message_info message);
|
||||
|
||||
void burn_message_warning_new(struct burn_drive *drive,
|
||||
enum burn_message_info message);
|
||||
|
||||
void burn_message_error_new(struct burn_drive *drive,
|
||||
enum burn_message_info message);
|
||||
|
||||
#endif
|
414
libburn/mmc.c
414
libburn/mmc.c
@ -1,8 +1,6 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
|
||||
/* ts A61009 */
|
||||
/* #include <a ssert.h> */
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
@ -20,21 +18,6 @@
|
||||
#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;
|
||||
|
||||
|
||||
|
||||
static unsigned char MMC_GET_TOC[] = { 0x43, 2, 2, 0, 0, 0, 0, 16, 0, 0 };
|
||||
static unsigned char MMC_GET_ATIP[] = { 0x43, 2, 4, 0, 0, 0, 0, 16, 0, 0 };
|
||||
static unsigned char MMC_GET_DISC_INFO[] =
|
||||
@ -56,9 +39,6 @@ static unsigned char MMC_TRACK_INFO[] = { 0x52, 0, 0, 0, 0, 0, 0, 16, 0, 0 };
|
||||
static unsigned char MMC_SEND_CUE_SHEET[] =
|
||||
{ 0x5D, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
|
||||
/* ts A61023 : get size and free space of drive buffer */
|
||||
static unsigned char MMC_READ_BUFFER_CAPACITY[] = { 0x5C, 0, 0, 0, 0, 0, 0, 16, 0, 0 };
|
||||
|
||||
|
||||
static int mmc_function_spy_do_tell = 0;
|
||||
|
||||
@ -99,9 +79,7 @@ void mmc_send_cue_sheet(struct burn_drive *d, struct cue_sheet *s)
|
||||
d->issue_command(d, &c);
|
||||
}
|
||||
|
||||
/* 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)
|
||||
int mmc_get_nwa(struct burn_drive *d)
|
||||
{
|
||||
struct buffer buf;
|
||||
struct command c;
|
||||
@ -112,66 +90,28 @@ int mmc_get_nwa(struct burn_drive *d, int trackno, int *lba, int *nwa)
|
||||
c.oplen = sizeof(MMC_TRACK_INFO);
|
||||
memcpy(c.opcode, MMC_TRACK_INFO, sizeof(MMC_TRACK_INFO));
|
||||
c.opcode[1] = 1;
|
||||
if(trackno<=0)
|
||||
c.opcode[5] = 0xFF;
|
||||
else
|
||||
c.opcode[5] = trackno;
|
||||
c.opcode[5] = 0xFF;
|
||||
c.page = &buf;
|
||||
c.dir = FROM_DRIVE;
|
||||
d->issue_command(d, &c);
|
||||
data = c.page->data;
|
||||
|
||||
*lba = (data[8] << 24) + (data[9] << 16)
|
||||
+ (data[10] << 8) + data[11];
|
||||
*nwa = (data[12] << 24) + (data[13] << 16)
|
||||
return (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. */
|
||||
/* void mmc_close_disc(struct burn_drive *d, struct burn_write_opts *o) */
|
||||
void mmc_close_disc(struct burn_write_opts *o)
|
||||
void mmc_close_disc(struct burn_drive *d, struct burn_write_opts *o)
|
||||
{
|
||||
struct burn_drive *d;
|
||||
|
||||
mmc_function_spy("mmc_close_disc");
|
||||
|
||||
libdax_msgs_submit(libdax_messenger, -1, 0x00000002,
|
||||
LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO,
|
||||
"HOW THAT ? mmc_close_disc() was called", 0, 0);
|
||||
|
||||
/* ts A61009 : made impossible by removing redundant parameter d */
|
||||
/* a ssert(o->drive == d); */
|
||||
d = o->drive;
|
||||
|
||||
assert(o->drive == d);
|
||||
o->multi = 0;
|
||||
spc_select_write_params(d, o);
|
||||
mmc_close(d, 1, 0);
|
||||
}
|
||||
|
||||
/* ts A61009 : function is obviously unused. */
|
||||
/* void mmc_close_session(struct burn_drive *d, struct burn_write_opts *o) */
|
||||
void mmc_close_session(struct burn_write_opts *o)
|
||||
void mmc_close_session(struct burn_drive *d, struct burn_write_opts *o)
|
||||
{
|
||||
struct burn_drive *d;
|
||||
|
||||
mmc_function_spy("mmc_close_session");
|
||||
|
||||
libdax_msgs_submit(libdax_messenger, -1, 0x00000002,
|
||||
LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO,
|
||||
"HOW THAT ? mmc_close_session() was called", 0, 0);
|
||||
|
||||
/* ts A61009 : made impossible by removing redundant parameter d */
|
||||
/* a ssert(o->drive == d); */
|
||||
d = o->drive;
|
||||
|
||||
assert(o->drive == d);
|
||||
o->multi = 3;
|
||||
spc_select_write_params(d, o);
|
||||
mmc_close(d, 1, 0);
|
||||
@ -181,15 +121,11 @@ void mmc_close(struct burn_drive *d, int session, int track)
|
||||
{
|
||||
struct command c;
|
||||
|
||||
mmc_function_spy("mmc_close");
|
||||
|
||||
mmc_function_spy("mmc_close_session");
|
||||
c.retry = 1;
|
||||
c.oplen = sizeof(MMC_CLOSE);
|
||||
memcpy(c.opcode, MMC_CLOSE, sizeof(MMC_CLOSE));
|
||||
|
||||
/* ts A61030 : shifted !!session rather than or-ing plain session */
|
||||
c.opcode[2] = ((!!session)<<1) | !!track;
|
||||
|
||||
c.opcode[2] = session | !!track;
|
||||
c.opcode[4] = track >> 8;
|
||||
c.opcode[5] = track & 0xFF;
|
||||
c.page = NULL;
|
||||
@ -218,7 +154,6 @@ void mmc_get_event(struct burn_drive *d)
|
||||
c.page->data[5], c.page->data[6], c.page->data[7]);
|
||||
}
|
||||
|
||||
|
||||
void mmc_write_12(struct burn_drive *d, int start, struct buffer *buf)
|
||||
{
|
||||
struct command c;
|
||||
@ -226,10 +161,7 @@ void mmc_write_12(struct burn_drive *d, int start, struct buffer *buf)
|
||||
|
||||
mmc_function_spy("mmc_write_12");
|
||||
len = buf->sectors;
|
||||
|
||||
/* ts A61009 */
|
||||
/* a ssert(buf->bytes >= buf->sectors);*/ /* can be == at 0... */
|
||||
|
||||
assert(buf->bytes >= buf->sectors); /* can be == at 0... */
|
||||
burn_print(100, "trying to write %d at %d\n", len, start);
|
||||
memcpy(c.opcode, MMC_WRITE_12, sizeof(MMC_WRITE_12));
|
||||
c.retry = 1;
|
||||
@ -254,14 +186,6 @@ 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;
|
||||
@ -271,10 +195,7 @@ int mmc_write(struct burn_drive *d, int start, struct buffer *buf)
|
||||
return BE_CANCELLED;
|
||||
|
||||
len = buf->sectors;
|
||||
|
||||
/* ts A61009 : buffer fill problems are to be handled by caller */
|
||||
/* a ssert(buf->bytes >= buf->sectors);*/ /* can be == at 0... */
|
||||
|
||||
assert(buf->bytes >= buf->sectors); /* can be == at 0... */
|
||||
burn_print(100, "trying to write %d at %d\n", len, start);
|
||||
memcpy(c.opcode, MMC_WRITE_10, sizeof(MMC_WRITE_10));
|
||||
c.retry = 1;
|
||||
@ -293,35 +214,9 @@ 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]);
|
||||
*/
|
||||
|
||||
#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 */
|
||||
/* write(fileno(stderr), c.page->data, c.page->bytes);*/
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -333,7 +228,7 @@ void mmc_read_toc(struct burn_drive *d)
|
||||
struct buffer buf;
|
||||
struct command c;
|
||||
int dlen;
|
||||
int i, bpl= 12;
|
||||
int i;
|
||||
unsigned char *tdata;
|
||||
|
||||
mmc_function_spy("mmc_read_toc");
|
||||
@ -347,24 +242,7 @@ void mmc_read_toc(struct burn_drive *d)
|
||||
d->issue_command(d, &c);
|
||||
|
||||
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.
|
||||
|
||||
d->busy = BURN_DRIVE_IDLE;
|
||||
*/
|
||||
libdax_msgs_submit(libdax_messenger, d->global_index,
|
||||
0x0002010d,
|
||||
LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH,
|
||||
"Could not inquire TOC (non-blank DVD media ?)", 0,0);
|
||||
d->status = BURN_DISC_UNSUITABLE;
|
||||
d->toc_entries = 0;
|
||||
/* Prefering memory leaks over fandangos */
|
||||
d->toc_entry = malloc(sizeof(struct burn_toc_entry));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -373,8 +251,7 @@ void mmc_read_toc(struct burn_drive *d)
|
||||
/*
|
||||
some drives fail this check.
|
||||
|
||||
ts A61007 : if re-enabled then not via Assert.
|
||||
a ssert(((dlen - 2) % 11) == 0);
|
||||
assert(((dlen - 2) % 11) == 0);
|
||||
*/
|
||||
d->toc_entry = malloc(d->toc_entries * sizeof(struct burn_toc_entry));
|
||||
tdata = c.page->data + 4;
|
||||
@ -388,27 +265,14 @@ void mmc_read_toc(struct burn_drive *d)
|
||||
burn_disc_add_session(d->disc, session, BURN_POS_END);
|
||||
burn_session_free(session);
|
||||
}
|
||||
|
||||
/* ts A61022 */
|
||||
burn_print(bpl, "-----------------------------------\n");
|
||||
|
||||
for (i = 0; i < d->toc_entries; i++, tdata += 11) {
|
||||
|
||||
/* ts A61022: was burn_print level 12 */
|
||||
burn_print(bpl, "S %d, PT %2.2Xh, TNO %d :", tdata[0],tdata[3],
|
||||
burn_print(12, "S %d, PT %d, TNO %d : ", tdata[0], tdata[3],
|
||||
tdata[2]);
|
||||
burn_print(bpl, " MSF(%d:%d:%d)", tdata[4],tdata[5],tdata[6]);
|
||||
burn_print(bpl, " PMSF(%d:%d:%d %d)",
|
||||
tdata[8], tdata[9], tdata[10],
|
||||
burn_msf_to_lba(tdata[8], tdata[9], tdata[10])
|
||||
);
|
||||
burn_print(bpl, " - control %d, adr %d\n", tdata[1] & 0xF,
|
||||
burn_print(12, "(%d:%d:%d)", tdata[8], tdata[9], tdata[10]);
|
||||
burn_print(12, "A(%d:%d:%d)", tdata[4], tdata[5], tdata[6]);
|
||||
burn_print(12, " - control %d, adr %d\n", tdata[1] & 0xF,
|
||||
tdata[1] >> 4);
|
||||
|
||||
/*
|
||||
fprintf(stderr, "libburn_experimental: toc entry #%d : %d %d %d\n",i,tdata[8], tdata[9], tdata[10]);
|
||||
*/
|
||||
|
||||
if (tdata[3] == 1) {
|
||||
if (burn_msf_to_lba(tdata[8], tdata[9], tdata[10])) {
|
||||
d->disc->session[0]->hidefirst = 1;
|
||||
@ -447,10 +311,6 @@ void mmc_read_toc(struct burn_drive *d)
|
||||
d->disc->session[tdata[0] - 1]->leadout_entry =
|
||||
&d->toc_entry[i];
|
||||
}
|
||||
|
||||
/* ts A61022 */
|
||||
burn_print(bpl, "-----------------------------------\n");
|
||||
|
||||
if (d->status != BURN_DISC_APPENDABLE)
|
||||
d->status = BURN_DISC_FULL;
|
||||
toc_find_modes(d);
|
||||
@ -479,26 +339,11 @@ void mmc_read_disc_info(struct burn_drive *d)
|
||||
|
||||
data = c.page->data;
|
||||
d->erasable = !!(data[2] & 16);
|
||||
|
||||
/* ts A61020 */
|
||||
d->start_lba = d->end_lba = -2000000000;
|
||||
|
||||
/*
|
||||
fprintf(stderr, "libburn_experimental: data[2]= %d 0x%x\n",
|
||||
(unsigned) data[2], (unsigned) data[2]);
|
||||
*/
|
||||
switch (data[2] & 3) {
|
||||
case 0:
|
||||
d->toc_entries = 0;
|
||||
d->start_lba = burn_msf_to_lba(data[17], data[18], data[19]);
|
||||
d->end_lba = burn_msf_to_lba(data[21], data[22], data[23]);
|
||||
|
||||
/*
|
||||
fprintf(stderr, "libburn_experimental: start_lba = %d (%d %d %d) , end_lba = %d (%d %d %d)\n",
|
||||
d->start_lba, data[17], data[18], data[19],
|
||||
d->end_lba, data[21], data[22], data[23]);
|
||||
*/
|
||||
|
||||
d->status = BURN_DISC_BLANK;
|
||||
break;
|
||||
case 1:
|
||||
@ -514,17 +359,6 @@ void mmc_read_atip(struct burn_drive *d)
|
||||
struct buffer buf;
|
||||
struct command c;
|
||||
|
||||
/* ts A61021 */
|
||||
unsigned char *data;
|
||||
/* Speed values from A1:
|
||||
With 4 cdrecord tells "10" or "8" where MMC-1 says "8".
|
||||
cdrecord "8" appear on 4xCD-RW and thus seem to be quite invalid.
|
||||
My CD-R (>=24 speed) tell no A1.
|
||||
The higher non-MMC-1 values are hearsay.
|
||||
*/
|
||||
static int speed_value[16]= { 0, 2, 4, 6, 10, -5, 16, -7,
|
||||
24, 32, 40, 48, -12, -13, -14, -15};
|
||||
|
||||
mmc_function_spy("mmc_read_atip");
|
||||
memcpy(c.opcode, MMC_GET_ATIP, sizeof(MMC_GET_ATIP));
|
||||
c.retry = 1;
|
||||
@ -536,125 +370,6 @@ void mmc_read_atip(struct burn_drive *d)
|
||||
c.dir = FROM_DRIVE;
|
||||
d->issue_command(d, &c);
|
||||
burn_print(1, "atip shit for you\n");
|
||||
|
||||
|
||||
/* ts A61021 */
|
||||
data = c.page->data;
|
||||
d->erasable= !!(data[6]&64);
|
||||
d->start_lba= burn_msf_to_lba(data[8],data[9],data[10]);
|
||||
d->end_lba= burn_msf_to_lba(data[12],data[13],data[14]);
|
||||
if (data[6]&4) {
|
||||
if (speed_value[(data[16]>>4)&7] > 0)
|
||||
d->mdata->min_write_speed =
|
||||
speed_value[(data[16]>>4)&7]*176;
|
||||
if (speed_value[(data[16])&15] > 0)
|
||||
d->mdata->max_write_speed =
|
||||
speed_value[(data[16])&15]*176;
|
||||
}
|
||||
|
||||
#ifdef Burn_mmc_be_verbous_about_atiP
|
||||
{ int i;
|
||||
fprintf(stderr,"libburn_experimental: Returned ATIP Data\n");
|
||||
for(i= 0; i<28; i++)
|
||||
fprintf(stderr,"%3.3d (0x%2.2x)%s",
|
||||
data[i],data[i],((i+1)%5 ? " ":"\n"));
|
||||
fprintf(stderr,"\n");
|
||||
|
||||
fprintf(stderr,
|
||||
"libburn_experimental: Indicative Target Writing Power= %d\n",
|
||||
(data[4]>>4)&7);
|
||||
fprintf(stderr,
|
||||
"libburn_experimental: Reference speed= %d ->%d\n",
|
||||
data[4]&7, speed_value[data[4]&7]);
|
||||
fprintf(stderr,
|
||||
"libburn_experimental: Is %sunrestricted\n",
|
||||
(data[5]&64?"":"not "));
|
||||
fprintf(stderr,
|
||||
"libburn_experimental: Is %serasable, sub-type %d\n",
|
||||
(data[6]&64?"":"not "),(data[6]>>3)&3);
|
||||
fprintf(stderr,
|
||||
"libburn_experimental: lead in: %d (%-2.2d:%-2.2d/%-2.2d)\n",
|
||||
burn_msf_to_lba(data[8],data[9],data[10]),
|
||||
data[8],data[9],data[10]);
|
||||
fprintf(stderr,
|
||||
"libburn_experimental: lead out: %d (%-2.2d:%-2.2d/%-2.2d)\n",
|
||||
burn_msf_to_lba(data[12],data[13],data[14]),
|
||||
data[12],data[13],data[14]);
|
||||
if(data[6]&4)
|
||||
fprintf(stderr,
|
||||
"libburn_experimental: A1 speed low %d speed high %d\n",
|
||||
speed_value[(data[16]>>4)&7], speed_value[(data[16])&7]);
|
||||
if(data[6]&2)
|
||||
fprintf(stderr,
|
||||
"libburn_experimental: A2 speed low %d speed high %d\n",
|
||||
speed_value[(data[20]>>4)&7], speed_value[(data[20])&7]);
|
||||
if(data[6]&1)
|
||||
fprintf(stderr,
|
||||
"libburn_experimental: A3 speed low %d speed high %d\n",
|
||||
speed_value[(data[24]>>4)&7], speed_value[(data[24])&7]);
|
||||
}
|
||||
|
||||
#endif /* Burn_mmc_be_verbous_about_atiP */
|
||||
|
||||
/* ts A61020
|
||||
http://www.t10.org/ftp/t10/drafts/mmc/mmc-r10a.pdf , table 77 :
|
||||
|
||||
0 ATIP Data Length MSB
|
||||
1 ATIP Data Length LSB
|
||||
2 Reserved
|
||||
3 Reserved
|
||||
4 bit7=1, bit4-6="Indicative Target Writing Power", bit3=reserved ,
|
||||
bit0-2="Reference speed"
|
||||
5 bit7=0, bit6="URU" , bit0-5=reserved
|
||||
6 bit7=1, bit6="Disc Type", bit3-4="Disc Sub-Type",
|
||||
bit2="A1", bit1="A2", bit0="A3"
|
||||
7 reserved
|
||||
8 ATIP Start Time of lead-in (Min)
|
||||
9 ATIP Start Time of lead-in (Sec)
|
||||
10 ATIP Start Time of lead-in (Frame)
|
||||
11 reserved
|
||||
12 ATIP Last Possible Start Time of lead-out (Min)
|
||||
13 ATIP Last Possible Start Time of lead-out (Sec)
|
||||
14 ATIP Last Possible Start Time of lead-out (Frame)
|
||||
15 reserved
|
||||
16 bit7=0, bit4-6="Lowest Usable CLV Recording speed"
|
||||
bit0-3="Highest Usable CLV Recording speed"
|
||||
17 bit7=0, bit4-6="Power Multiplication Factor p",
|
||||
bit1-3="Target y value of the Modulation/Power function", bit0=reserved
|
||||
18 bit7=1, bit4-6="Recommended Erase/Write Power Ratio (P(inf)/W(inf))"
|
||||
bit0-3=reserved
|
||||
19 reserved
|
||||
20-22 A2 Values
|
||||
23 reserved
|
||||
24-26 A3 Values
|
||||
27 reserved
|
||||
|
||||
Disc Type - zero indicates CD-R media; one indicates CD-RW media.
|
||||
|
||||
Disc Sub-Type - shall be set to zero.
|
||||
|
||||
A1 - when set to one, indicates that bytes 16-18 are valid.
|
||||
|
||||
Lowest Usable CLV Recording Speed
|
||||
000b Reserved
|
||||
001b 2X
|
||||
010b - 111b Reserved
|
||||
|
||||
Highest CLV Recording Speeds
|
||||
000b Reserved
|
||||
001b 2X
|
||||
010b 4X
|
||||
011b 6X
|
||||
100b 8X
|
||||
101b - 111b Reserved
|
||||
|
||||
MMC-3 seems to recommend MODE SENSE (5Ah) page 2Ah rather than A1, A2, A3.
|
||||
This page is loaded in libburn function spc_sense_caps() .
|
||||
Speed is given in kbytes/sec there. But i suspect this to be independent
|
||||
of media. So one would habe to associate the speed descriptor blocks with
|
||||
the ATIP media characteristics ? How ?
|
||||
|
||||
*/
|
||||
}
|
||||
|
||||
void mmc_read_sectors(struct burn_drive *d,
|
||||
@ -667,14 +382,9 @@ void mmc_read_sectors(struct burn_drive *d,
|
||||
struct command c;
|
||||
|
||||
mmc_function_spy("mmc_read_sectors");
|
||||
|
||||
/* ts A61009 : to be ensured by callers */
|
||||
/* a ssert(len >= 0); */
|
||||
|
||||
/* if the drive isn't busy, why the hell are we here? */
|
||||
/* ts A61006 : i second that question */
|
||||
/* a ssert(d->busy); */
|
||||
|
||||
assert(len >= 0);
|
||||
/* if the drive isn't busy, why the hell are we here? */
|
||||
assert(d->busy);
|
||||
burn_print(12, "reading %d from %d\n", len, start);
|
||||
memcpy(c.opcode, MMC_READ_CD, sizeof(MMC_READ_CD));
|
||||
c.retry = 1;
|
||||
@ -693,12 +403,8 @@ 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
|
||||
@ -784,13 +490,6 @@ 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;
|
||||
@ -843,72 +542,3 @@ void mmc_sync_cache(struct burn_drive *d)
|
||||
c.dir = NO_TRANSFER;
|
||||
d->issue_command(d, &c);
|
||||
}
|
||||
|
||||
|
||||
/* ts A61023 : http://libburn.pykix.org/ticket/14
|
||||
get size and free space of drive buffer
|
||||
*/
|
||||
int mmc_read_buffer_capacity(struct burn_drive *d)
|
||||
{
|
||||
struct buffer buf;
|
||||
struct command c;
|
||||
unsigned char *data;
|
||||
|
||||
mmc_function_spy("mmc_read_buffer_capacity");
|
||||
memcpy(c.opcode, MMC_READ_BUFFER_CAPACITY,
|
||||
sizeof(MMC_READ_BUFFER_CAPACITY));
|
||||
c.retry = 1;
|
||||
c.oplen = sizeof(MMC_READ_BUFFER_CAPACITY);
|
||||
c.page = &buf;
|
||||
c.page->bytes = 0;
|
||||
c.page->sectors = 0;
|
||||
|
||||
c.dir = FROM_DRIVE;
|
||||
d->issue_command(d, &c);
|
||||
|
||||
/* >>> ??? error diagnostics */
|
||||
|
||||
data = c.page->data;
|
||||
|
||||
d->progress.buffer_capacity =
|
||||
(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;
|
||||
}
|
||||
|
||||
|
||||
/* ts A61021 : the mmc specific part of sg.c:enumerate_common()
|
||||
*/
|
||||
int mmc_setup_drive(struct burn_drive *d)
|
||||
{
|
||||
d->read_atip = mmc_read_atip;
|
||||
d->read_toc = mmc_read_toc;
|
||||
d->write = mmc_write;
|
||||
d->erase = mmc_erase;
|
||||
d->read_sectors = mmc_read_sectors;
|
||||
d->perform_opc = mmc_perform_opc;
|
||||
d->set_speed = mmc_set_speed;
|
||||
d->send_cue_sheet = mmc_send_cue_sheet;
|
||||
d->sync_cache = mmc_sync_cache;
|
||||
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 */
|
||||
d->start_lba= -2000000000;
|
||||
d->end_lba= -2000000000;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -12,13 +12,8 @@ struct cue_sheet;
|
||||
/* MMC commands */
|
||||
|
||||
void mmc_read(struct burn_drive *);
|
||||
|
||||
/* ts A61009 : removed redundant parameter d in favor of o->drive */
|
||||
/* void mmc_close_session(struct burn_drive *, struct burn_write_opts *); */
|
||||
/* void mmc_close_disc(struct burn_drive *, struct burn_write_opts *); */
|
||||
void mmc_close_session(struct burn_write_opts *o);
|
||||
void mmc_close_disc(struct burn_write_opts *o);
|
||||
|
||||
void mmc_close_session(struct burn_drive *, struct burn_write_opts *);
|
||||
void mmc_close_disc(struct burn_drive *, struct burn_write_opts *);
|
||||
void mmc_close(struct burn_drive *, int session, int track);
|
||||
void mmc_get_event(struct burn_drive *);
|
||||
int mmc_write(struct burn_drive *, int start, struct buffer *buf);
|
||||
@ -37,18 +32,6 @@ 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 *);
|
||||
|
||||
/* 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);
|
||||
|
||||
int mmc_get_nwa(struct burn_drive *);
|
||||
void mmc_send_cue_sheet(struct burn_drive *, struct cue_sheet *);
|
||||
|
||||
/* ts A61023 : get size and free space of drive buffer */
|
||||
int mmc_read_buffer_capacity(struct burn_drive *d);
|
||||
|
||||
/* ts A61021 : the mmc specific part of sg.c:enumerate_common()
|
||||
*/
|
||||
int mmc_setup_drive(struct burn_drive *d);
|
||||
|
||||
#endif /*__MMC*/
|
||||
|
@ -2,27 +2,14 @@
|
||||
#include "options.h"
|
||||
#include "transport.h"
|
||||
|
||||
/* ts A61007 */
|
||||
/* #include <a ssert.h> */
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "libdax_msgs.h"
|
||||
extern struct libdax_msgs *libdax_messenger;
|
||||
|
||||
|
||||
struct burn_write_opts *burn_write_opts_new(struct burn_drive *drive)
|
||||
{
|
||||
struct burn_write_opts *opts;
|
||||
|
||||
opts = malloc(sizeof(struct burn_write_opts));
|
||||
if (opts == NULL) {
|
||||
libdax_msgs_submit(libdax_messenger, -1, 0x00020111,
|
||||
LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH,
|
||||
"Could not allocate new auxiliary object", 0, 0);
|
||||
return NULL;
|
||||
}
|
||||
opts->drive = drive;
|
||||
opts->refcount = 1;
|
||||
opts->write_type = BURN_WRITE_TAO;
|
||||
@ -74,32 +61,14 @@ int burn_write_opts_set_write_type(struct burn_write_opts *opts,
|
||||
enum burn_write_types write_type,
|
||||
int block_type)
|
||||
{
|
||||
int sector_get_outmode(enum burn_write_types write_type,
|
||||
enum burn_block_types block_type);
|
||||
int spc_block_type(enum burn_block_types b);
|
||||
|
||||
/* ts A61007 */
|
||||
if (! ( (write_type == BURN_WRITE_SAO && block_type == BURN_BLOCK_SAO)
|
||||
|| (opts->drive->block_types[write_type] & block_type) ) ) {
|
||||
bad_combination:;
|
||||
libdax_msgs_submit(libdax_messenger, -1, 0x00020112,
|
||||
LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
|
||||
"Bad combination of write_type and block_type", 0, 0);
|
||||
return 0;
|
||||
if ((write_type == BURN_WRITE_SAO && block_type == BURN_BLOCK_SAO) ||
|
||||
(opts->drive->block_types[write_type] & block_type)) {
|
||||
opts->write_type = write_type;
|
||||
opts->block_type = block_type;
|
||||
return 1;
|
||||
}
|
||||
/* ts A61007 : obsoleting Assert in sector.c:get_outmode() */
|
||||
if (sector_get_outmode(write_type, (enum burn_block_types) block_type)
|
||||
== -1)
|
||||
goto bad_combination;
|
||||
/* ts A61007 : obsoleting Assert in spc.c:spc_block_type() */
|
||||
if (spc_block_type((enum burn_block_types) block_type) == -1)
|
||||
goto bad_combination;
|
||||
|
||||
opts->write_type = write_type;
|
||||
opts->block_type = block_type;
|
||||
return 1;
|
||||
|
||||
/* a ssert(0); */
|
||||
assert(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void burn_write_opts_set_toc_entries(struct burn_write_opts *opts, int count,
|
||||
@ -152,14 +121,6 @@ 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;
|
||||
@ -206,4 +167,3 @@ void burn_read_opts_set_hardware_error_retries(struct burn_read_opts *opts,
|
||||
{
|
||||
opts->hardware_error_retries = hardware_error_retries;
|
||||
}
|
||||
|
||||
|
@ -1,72 +0,0 @@
|
||||
|
||||
/* 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 */
|
||||
|
@ -1,63 +0,0 @@
|
||||
|
||||
/* 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
34
libburn/os.h
@ -1,34 +0,0 @@
|
||||
|
||||
/* 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 */
|
||||
|
@ -1,14 +1,9 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
|
||||
/* #include <m alloc.h> ts A61013 : not in Linux man 3 malloc */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <malloc.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
|
||||
/* ts A61007 */
|
||||
/* #include <a ssert.h> */
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
@ -16,11 +11,7 @@
|
||||
#include "libburn.h"
|
||||
#include "drive.h"
|
||||
#include "transport.h"
|
||||
|
||||
/* ts A60925 : obsoleted by libdax_msgs.h
|
||||
#include "message.h"
|
||||
*/
|
||||
|
||||
#include "crc.h"
|
||||
#include "debug.h"
|
||||
#include "init.h"
|
||||
@ -42,16 +33,11 @@ void burn_disc_read(struct burn_drive *d, const struct burn_read_opts *o)
|
||||
struct buffer page;
|
||||
int speed;
|
||||
|
||||
/* ts A61007 : if this function gets revived, then these
|
||||
tests have to be done more graceful */
|
||||
a ssert((o->version & 0xfffff000) == (OPTIONS_VERSION & 0xfffff000));
|
||||
a ssert(!d->busy);
|
||||
a ssert(d->toc->valid);
|
||||
a ssert(o->datafd != -1);
|
||||
|
||||
/* moved up from spc_select_error_params alias d->send_parameters() */
|
||||
a ssert(d->mdata->valid);
|
||||
assert((o->version & 0xfffff000) == (OPTIONS_VERSION & 0xfffff000));
|
||||
|
||||
assert(!d->busy);
|
||||
assert(d->toc->valid);
|
||||
assert(o->datafd != -1);
|
||||
/* XXX not sure this is a good idea. copy it? */
|
||||
/* XXX also, we have duplicated data now, do we remove the fds from struct
|
||||
drive, or only store a subset of the _opts structs in drives */
|
||||
@ -61,7 +47,6 @@ drive, or only store a subset of the _opts structs in drives */
|
||||
d->set_speed(d, speed, 0);
|
||||
|
||||
d->params.retries = o->hardware_error_retries;
|
||||
|
||||
d->send_parameters(d, o);
|
||||
|
||||
d->cancel = 0;
|
||||
@ -139,10 +124,7 @@ drive, or only store a subset of the _opts structs in drives */
|
||||
page.sectors = (finish < maxsects) ? finish : maxsects;
|
||||
printf("reading %d sectors from %d\n", page.sectors,
|
||||
drive_lba);
|
||||
|
||||
/* >>> ts A61009 : ensure page.sectors >= 0 before calling */
|
||||
d->r ead_sectors(d, drive_lba, page.sectors, o, &page);
|
||||
|
||||
d->read_sectors(d, drive_lba, page.sectors, o, &page);
|
||||
printf("Read %d\n", page.sectors);
|
||||
}
|
||||
#endif
|
||||
|
@ -2,7 +2,9 @@
|
||||
|
||||
/* scsi block commands */
|
||||
|
||||
#include <scsi/scsi.h>
|
||||
#include <string.h>
|
||||
#include <scsi/sg.h>
|
||||
|
||||
#include "transport.h"
|
||||
#include "sbc.h"
|
||||
@ -11,7 +13,6 @@
|
||||
/* 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,29 +38,3 @@ void sbc_eject(struct burn_drive *d)
|
||||
c.dir = NO_TRANSFER;
|
||||
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,11 +8,4 @@ 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);
|
||||
|
||||
#endif /* __SBC */
|
||||
|
263
libburn/sector.c
263
libburn/sector.c
@ -1,10 +1,7 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
/* ts A61010 */
|
||||
/* #include <a ssert.h> */
|
||||
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include "error.h"
|
||||
@ -19,15 +16,6 @@
|
||||
#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;
|
||||
@ -49,15 +37,13 @@ static void uncook_subs(unsigned char *dest, unsigned char *source)
|
||||
}
|
||||
}
|
||||
|
||||
/* @return >=0 : valid , <0 invalid */
|
||||
int sector_get_outmode(enum burn_write_types write_type,
|
||||
enum burn_block_types block_type)
|
||||
/* 0 means "same as inmode" */
|
||||
static int get_outmode(struct burn_write_opts *o)
|
||||
{
|
||||
/* ts A61103 : extended SAO condition to TAO */
|
||||
if (write_type == BURN_WRITE_SAO || write_type == BURN_WRITE_TAO)
|
||||
if (o->write_type == BURN_WRITE_SAO)
|
||||
return 0;
|
||||
else
|
||||
switch (block_type) {
|
||||
switch (o->block_type) {
|
||||
case BURN_BLOCK_RAW0:
|
||||
return BURN_MODE_RAW;
|
||||
case BURN_BLOCK_RAW16:
|
||||
@ -68,37 +54,13 @@ int sector_get_outmode(enum burn_write_types write_type,
|
||||
return BURN_MODE_RAW | BURN_SUBCODE_R96;
|
||||
case BURN_BLOCK_MODE1:
|
||||
return BURN_MODE1;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* ts A61007 : now handled in burn_write_opts_set_write_type() */
|
||||
/* a ssert(0); */ /* return BURN_MODE_UNIMPLEMENTED :) */
|
||||
assert(0); /* return BURN_MODE_UNIMPLEMENTED :) */
|
||||
}
|
||||
|
||||
/* 0 means "same as inmode" */
|
||||
static int get_outmode(struct burn_write_opts *o)
|
||||
{
|
||||
/* ts A61007 */
|
||||
return sector_get_outmode(o->write_type, o->block_type);
|
||||
|
||||
/* -1 is prevented by check in burn_write_opts_set_write_type() */
|
||||
/* a ssert(0); */ /* return BURN_MODE_UNIMPLEMENTED :) */
|
||||
}
|
||||
|
||||
|
||||
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 */
|
||||
|
||||
int valid, shortage, curr;
|
||||
|
||||
/* no track pointer means we're just generating 0s */
|
||||
if (!track) {
|
||||
@ -118,7 +80,7 @@ static void get_bytes(struct burn_track *track, int count, unsigned char *data)
|
||||
shortage = count - valid;
|
||||
|
||||
if (!shortage)
|
||||
goto ex;
|
||||
return;
|
||||
|
||||
/* Next we use source data */
|
||||
curr = valid;
|
||||
@ -126,24 +88,16 @@ 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 <= 0) { /* ts A61031 : extended from (valid == -1) */
|
||||
if (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;
|
||||
|
||||
if (!shortage)
|
||||
goto ex;
|
||||
return;
|
||||
|
||||
/* Before going to the next track, we run through any tail */
|
||||
|
||||
@ -159,13 +113,7 @@ static void get_bytes(struct burn_track *track, int count, unsigned char *data)
|
||||
shortage -= valid;
|
||||
|
||||
if (!shortage)
|
||||
goto ex;
|
||||
|
||||
/* ts A61031 */
|
||||
if (shortage >= count)
|
||||
track->track_data_done = 1;
|
||||
if (track->open_ended)
|
||||
goto ex;
|
||||
return;
|
||||
|
||||
/* If we're still short, and there's a "next" pointer, we pull from that.
|
||||
if that depletes, we'll just fill with 0s.
|
||||
@ -180,24 +128,11 @@ static void get_bytes(struct burn_track *track, int count, unsigned char *data)
|
||||
curr += valid;
|
||||
}
|
||||
}
|
||||
ex:;
|
||||
/* ts A61024 : general finalizing processing */
|
||||
if(shortage)
|
||||
memset(data + curr, 0, shortage); /* this is old icculus.org */
|
||||
if (track->swap_source_bytes == 1) {
|
||||
for (i = 1; i < count; i += 2) {
|
||||
tr = data[i];
|
||||
data[i] = data[i-1];
|
||||
data[i-1] = tr;
|
||||
}
|
||||
}
|
||||
if (!shortage)
|
||||
return;
|
||||
memset(data + curr, 0, shortage);
|
||||
}
|
||||
|
||||
/* ts A61009 : seems to hand out sector start pointer in opts->drive->buffer
|
||||
and to count hand outs as well as reserved bytes */
|
||||
/* 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)
|
||||
static unsigned char *get_sector(struct burn_write_opts *opts, int inmode)
|
||||
{
|
||||
struct burn_drive *d = opts->drive;
|
||||
struct buffer *out = d->buffer;
|
||||
@ -209,28 +144,13 @@ static unsigned char *get_sector(struct burn_write_opts *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 NULL;
|
||||
seclen += burn_subcode_length(outmode);
|
||||
seclen = burn_sector_length(outmode) + burn_subcode_length(outmode);
|
||||
|
||||
if (out->bytes + (seclen) >= BUFFER_SIZE) {
|
||||
int err;
|
||||
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;
|
||||
@ -243,36 +163,8 @@ static unsigned char *get_sector(struct burn_write_opts *opts,
|
||||
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 */
|
||||
static int convert_data(struct burn_write_opts *o, struct burn_track *track,
|
||||
static void convert_data(struct burn_write_opts *o, struct burn_track *track,
|
||||
int inmode, unsigned char *data)
|
||||
{
|
||||
int outlen, inlen;
|
||||
@ -285,21 +177,14 @@ static int convert_data(struct burn_write_opts *o, struct burn_track *track,
|
||||
|
||||
outlen = burn_sector_length(outmode);
|
||||
inlen = burn_sector_length(inmode);
|
||||
|
||||
/* ts A61010 */
|
||||
/* a ssert(outlen >= inlen); */
|
||||
if (outlen < inlen)
|
||||
return 0;
|
||||
assert(outlen >= inlen);
|
||||
|
||||
if ((outmode & BURN_MODE_BITS) == (inmode & BURN_MODE_BITS)) {
|
||||
get_bytes(track, inlen, data);
|
||||
return 1;
|
||||
return;
|
||||
}
|
||||
|
||||
/* ts A61010 */
|
||||
/* a ssert(outmode & BURN_MODE_RAW); */
|
||||
if (!(outmode & BURN_MODE_RAW))
|
||||
return 0;
|
||||
assert(outmode & BURN_MODE_RAW);
|
||||
|
||||
if (inmode & BURN_MODE1)
|
||||
offset = 16;
|
||||
@ -307,16 +192,9 @@ static int convert_data(struct burn_write_opts *o, struct burn_track *track,
|
||||
offset = 0;
|
||||
if (inmode & BURN_AUDIO)
|
||||
offset = 0;
|
||||
|
||||
/* ts A61010 */
|
||||
/* a ssert(offset != -1); */
|
||||
if (offset == -1)
|
||||
return 0;
|
||||
|
||||
assert(offset != -1);
|
||||
get_bytes(track, inlen, data + offset);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void convert_subs(struct burn_write_opts *o, int inmode,
|
||||
unsigned char *subs, unsigned char *sector)
|
||||
{
|
||||
@ -350,8 +228,6 @@ 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:;
|
||||
}
|
||||
}
|
||||
|
||||
@ -398,12 +274,10 @@ int sector_toc(struct burn_write_opts *o, int mode)
|
||||
unsigned char *data;
|
||||
unsigned char subs[96];
|
||||
|
||||
data = get_sector(o, NULL, mode);
|
||||
if (data == NULL)
|
||||
return 0;
|
||||
/* ts A61010 */
|
||||
if (convert_data(o, NULL, mode, data) <= 0)
|
||||
data = get_sector(o, mode);
|
||||
if (!data)
|
||||
return 0;
|
||||
convert_data(o, NULL, mode, data);
|
||||
subcode_toc(d, mode, subs);
|
||||
convert_subs(o, mode, subs, data);
|
||||
sector_headers(o, data, mode, 1);
|
||||
@ -418,12 +292,10 @@ int sector_pregap(struct burn_write_opts *o,
|
||||
unsigned char *data;
|
||||
unsigned char subs[96];
|
||||
|
||||
data = get_sector(o, NULL, mode);
|
||||
if (data == NULL)
|
||||
return 0;
|
||||
/* ts A61010 */
|
||||
if (convert_data(o, NULL, mode, data) <= 0)
|
||||
data = get_sector(o, mode);
|
||||
if (!data)
|
||||
return 0;
|
||||
convert_data(o, NULL, mode, data);
|
||||
subcode_user(o, subs, tno, control, 0, NULL, 1);
|
||||
convert_subs(o, mode, subs, data);
|
||||
sector_headers(o, data, mode, 0);
|
||||
@ -438,12 +310,10 @@ int sector_postgap(struct burn_write_opts *o,
|
||||
unsigned char subs[96];
|
||||
unsigned char *data;
|
||||
|
||||
data = get_sector(o, NULL, mode);
|
||||
if (data == NULL)
|
||||
data = get_sector(o, mode);
|
||||
if (!data)
|
||||
return 0;
|
||||
/* ts A61010 */
|
||||
if (convert_data(o, NULL, mode, data) <= 0)
|
||||
return 0;;
|
||||
convert_data(o, NULL, mode, data);
|
||||
/* use last index in track */
|
||||
subcode_user(o, subs, tno, control, 1, NULL, 1);
|
||||
convert_subs(o, mode, subs, data);
|
||||
@ -492,9 +362,7 @@ static char char_to_isrc(char c)
|
||||
return 0x11 + (c - 'A');
|
||||
if (c >= 'a' && c <= 'z')
|
||||
return 0x11 + (c - 'a');
|
||||
|
||||
/* ts A61008 : obsoleted by test in burn_track_set_isrc() */
|
||||
/* a ssert(0); */
|
||||
assert(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -530,8 +398,7 @@ void subcode_user(struct burn_write_opts *o, unsigned char *subcodes,
|
||||
}
|
||||
}
|
||||
|
||||
/* ts A61010 : this cannot happen. Assert for fun ? */
|
||||
/* a ssert(qmode == 1 || qmode == 2 || qmode == 3); */
|
||||
assert(qmode == 1 || qmode == 2 || qmode == 3);
|
||||
|
||||
switch (qmode) {
|
||||
case 1:
|
||||
@ -610,12 +477,10 @@ int sector_lout(struct burn_write_opts *o, unsigned char control, int mode)
|
||||
unsigned char subs[96];
|
||||
unsigned char *data;
|
||||
|
||||
data = get_sector(o, NULL, mode);
|
||||
data = get_sector(o, mode);
|
||||
if (!data)
|
||||
return 0;
|
||||
/* ts A61010 */
|
||||
if (convert_data(o, NULL, mode, data) <= 0)
|
||||
return 0;
|
||||
convert_data(o, NULL, mode, data);
|
||||
subcode_lout(o, control, subs);
|
||||
convert_subs(o, mode, subs, data);
|
||||
sector_headers(o, data, mode, 0);
|
||||
@ -629,18 +494,10 @@ 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, t->mode);
|
||||
data = get_sector(o, 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;
|
||||
}
|
||||
convert_data(o, t, t->mode, data);
|
||||
|
||||
if (!t->source->read_sub)
|
||||
subcode_user(o, subs, t->entry->point,
|
||||
@ -685,22 +542,6 @@ int dec_to_bcd(int d)
|
||||
return (top << 4) + bottom;
|
||||
}
|
||||
|
||||
int sector_headers_is_ok(struct burn_write_opts *o, int mode)
|
||||
{
|
||||
if (mode & BURN_AUDIO) /* no headers for "audio" */
|
||||
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;
|
||||
}
|
||||
|
||||
void sector_headers(struct burn_write_opts *o, unsigned char *out,
|
||||
int mode, int leadin)
|
||||
{
|
||||
@ -709,33 +550,14 @@ void sector_headers(struct burn_write_opts *o, unsigned char *out,
|
||||
int min, sec, frame;
|
||||
int modebyte = -1;
|
||||
|
||||
/* ts A61009 */
|
||||
#if 1
|
||||
int ret;
|
||||
|
||||
ret = sector_headers_is_ok(o, mode);
|
||||
if (ret != 2)
|
||||
return;
|
||||
modebyte = 1;
|
||||
|
||||
#else
|
||||
|
||||
if (mode & BURN_AUDIO) /* no headers for "audio" */
|
||||
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;
|
||||
|
||||
#endif
|
||||
|
||||
/* ts A61009 : now ensured by burn_disc_write_is_ok() */
|
||||
/* a ssert(modebyte == 1); */
|
||||
assert(modebyte == 1);
|
||||
|
||||
out[0] = 0;
|
||||
memset(out + 1, 0xFF, 10); /* sync */
|
||||
@ -807,22 +629,13 @@ void process_q(struct burn_drive *d, unsigned char *q)
|
||||
*/
|
||||
break;
|
||||
default:
|
||||
|
||||
/* ts A61009 : if reactivated then witout Assert */
|
||||
a ssert(0);
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
#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);
|
||||
|
@ -18,10 +18,6 @@ int sector_postgap(struct burn_write_opts *, unsigned char tno,
|
||||
unsigned char control, int mode);
|
||||
int sector_lout(struct burn_write_opts *, unsigned char control, int mode);
|
||||
int sector_data(struct burn_write_opts *, struct burn_track *t, int psub);
|
||||
|
||||
/* ts A61009 */
|
||||
int sector_headers_is_ok(struct burn_write_opts *o, int mode);
|
||||
|
||||
void sector_headers(struct burn_write_opts *, unsigned char *,
|
||||
int mode, int leadin);
|
||||
void subcode_user(struct burn_write_opts *, unsigned char *s,
|
||||
|
@ -1,559 +0,0 @@
|
||||
/* -*- 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,697 +0,0 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
|
||||
|
||||
|
||||
/* Revives old enumerate_common(). New version delegates much work
|
||||
to methods in drive, mmc, spc, and sbc .
|
||||
*/
|
||||
#define Scsi_freebsd_make_own_enumeratE 1
|
||||
|
||||
|
||||
/* Revives old scsi_enumerate_drives(). New version delegates most work to
|
||||
sg_give_next_adr().
|
||||
*/
|
||||
#define Scsi_freebsd_old_scsi_enumeratE 1
|
||||
|
||||
|
||||
#include <assert.h>
|
||||
#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 */
|
||||
|
||||
|
||||
#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;
|
||||
|
||||
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);
|
||||
|
||||
|
||||
/* ts A60821
|
||||
<<< debug: for tracing calls which might use open drive fds */
|
||||
int mmc_function_spy(char * text);
|
||||
|
||||
|
||||
#ifdef Scsi_freebsd_old_scsi_enumeratE
|
||||
|
||||
int sg_give_next_adr(burn_drive_enumerator_t *idx,
|
||||
char adr[], int adr_size, int initialize)
|
||||
{
|
||||
return (0);
|
||||
}
|
||||
|
||||
int sg_is_enumerable_adr(char* adr)
|
||||
{
|
||||
return (0);
|
||||
}
|
||||
|
||||
int sg_obtain_scsi_adr(char *path, int *bus_no, int *host_no, int *channel_no,
|
||||
int *target_no, int *lun_no)
|
||||
{
|
||||
return (0);
|
||||
}
|
||||
|
||||
#else /* Scsi_freebsd_old_scsi_enumeratE */
|
||||
|
||||
/* 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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/** 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)
|
||||
{
|
||||
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 */
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
/** Try 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);
|
||||
}
|
||||
|
||||
#endif /* ! Scsi_freebsd_old_scsi_enumeratE */
|
||||
|
||||
|
||||
int sg_close_drive(struct burn_drive * d)
|
||||
{
|
||||
if (d->cam != NULL) {
|
||||
cam_close_device(d->cam);
|
||||
d->cam = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sg_drive_is_open(struct burn_drive * d)
|
||||
{
|
||||
return (d->cam != NULL);
|
||||
}
|
||||
|
||||
int scsi_enumerate_drives(void)
|
||||
{
|
||||
|
||||
#ifdef Scsi_freebsd_old_scsi_enumeratE
|
||||
|
||||
union ccb ccb;
|
||||
int bufsize, fd;
|
||||
unsigned int i;
|
||||
int skip_device = 0;
|
||||
|
||||
if ((fd = open(XPT_DEVICE, O_RDWR)) == -1) {
|
||||
warn("couldn't open %s", XPT_DEVICE);
|
||||
return;
|
||||
}
|
||||
|
||||
bzero(&ccb, sizeof(union ccb));
|
||||
|
||||
ccb.ccb_h.path_id = CAM_XPT_PATH_ID;
|
||||
ccb.ccb_h.target_id = CAM_TARGET_WILDCARD;
|
||||
ccb.ccb_h.target_lun = CAM_LUN_WILDCARD;
|
||||
|
||||
ccb.ccb_h.func_code = XPT_DEV_MATCH;
|
||||
bufsize = sizeof(struct dev_match_result) * 100;
|
||||
ccb.cdm.match_buf_len = bufsize;
|
||||
ccb.cdm.matches = (struct dev_match_result *)malloc(bufsize);
|
||||
if (ccb.cdm.matches == NULL) {
|
||||
warnx("can't malloc memory for matches");
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
ccb.cdm.num_matches = 0;
|
||||
|
||||
/*
|
||||
* We fetch all nodes, since we display most of them in the default
|
||||
* case, and all in the verbose case.
|
||||
*/
|
||||
ccb.cdm.num_patterns = 0;
|
||||
ccb.cdm.pattern_buf_len = 0;
|
||||
|
||||
/*
|
||||
* We do the ioctl multiple times if necessary, in case there are
|
||||
* more than 100 nodes in the EDT.
|
||||
*/
|
||||
do {
|
||||
if (ioctl(fd, CAMIOCOMMAND, &ccb) == -1) {
|
||||
warn("error sending CAMIOCOMMAND ioctl");
|
||||
break;
|
||||
}
|
||||
|
||||
if ((ccb.ccb_h.status != CAM_REQ_CMP)
|
||||
|| ((ccb.cdm.status != CAM_DEV_MATCH_LAST)
|
||||
&& (ccb.cdm.status != CAM_DEV_MATCH_MORE))) {
|
||||
warnx("got CAM error %#x, CDM error %d\n",
|
||||
ccb.ccb_h.status, ccb.cdm.status);
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < ccb.cdm.num_matches; i++) {
|
||||
switch (ccb.cdm.matches[i].type) {
|
||||
case DEV_MATCH_BUS:
|
||||
break;
|
||||
case DEV_MATCH_DEVICE: {
|
||||
struct device_match_result* result;
|
||||
|
||||
result = &ccb.cdm.matches[i].result.device_result;
|
||||
|
||||
if (result->flags & DEV_RESULT_UNCONFIGURED)
|
||||
skip_device = 1;
|
||||
else
|
||||
skip_device = 0;
|
||||
|
||||
break;
|
||||
}
|
||||
case DEV_MATCH_PERIPH: {
|
||||
struct periph_match_result* result;
|
||||
char buf[64];
|
||||
|
||||
result = &ccb.cdm.matches[i].result.periph_result;
|
||||
if (skip_device || strcmp(result->periph_name, "pass") == 0)
|
||||
break;
|
||||
snprintf(buf, sizeof (buf), "/dev/%s%d", result->periph_name, result->unit_number);
|
||||
/* ts A51221 */
|
||||
if (burn_drive_is_banned(buf))
|
||||
break;
|
||||
|
||||
enumerate_common(buf, result->path_id, result->path_id, 0,
|
||||
result->target_id, result->target_lun);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
fprintf(stdout, "unknown match type\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} while ((ccb.ccb_h.status == CAM_REQ_CMP)
|
||||
&& (ccb.cdm.status == CAM_DEV_MATCH_MORE));
|
||||
|
||||
close(fd);
|
||||
|
||||
#else /* Scsi_freebsd_old_scsi_enumeratE */
|
||||
|
||||
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);
|
||||
|
||||
#endif /* ! Scsi_freebsd_old_scsi_enumeratE */
|
||||
|
||||
}
|
||||
|
||||
|
||||
#ifdef Scsi_freebsd_make_own_enumeratE
|
||||
|
||||
/* ts A61021: The old version which mixes SCSI and operating system adapter
|
||||
*/
|
||||
static void enumerate_common(char *fname, int bus_no, int host_no,
|
||||
int channel_no, int target_no, int lun_no)
|
||||
{
|
||||
struct burn_drive *t;
|
||||
struct burn_drive out;
|
||||
|
||||
/* ts A60923 */
|
||||
out.bus_no = bus_no;
|
||||
out.host = host_no;
|
||||
out.id = target_no;
|
||||
out.channel = channel_no;
|
||||
out.lun = lun_no;
|
||||
|
||||
out.devname = burn_strdup(fname);
|
||||
out.cam = NULL;
|
||||
|
||||
out.start_lba= -2000000000;
|
||||
out.end_lba= -2000000000;
|
||||
out.read_atip = mmc_read_atip;
|
||||
|
||||
out.grab = sg_grab;
|
||||
out.release = sg_release;
|
||||
out.drive_is_open= sg_drive_is_open;
|
||||
out.issue_command = sg_issue_command;
|
||||
out.getcaps = spc_getcaps;
|
||||
out.released = 1;
|
||||
out.status = BURN_DISC_UNREADY;
|
||||
|
||||
out.eject = sbc_eject;
|
||||
out.load = sbc_load;
|
||||
out.lock = spc_prevent;
|
||||
out.unlock = spc_allow;
|
||||
out.read_disc_info = spc_sense_write_params;
|
||||
out.get_erase_progress = spc_get_erase_progress;
|
||||
out.test_unit_ready = spc_test_unit_ready;
|
||||
out.probe_write_modes = spc_probe_write_modes;
|
||||
out.read_toc = mmc_read_toc;
|
||||
out.write = mmc_write;
|
||||
out.erase = mmc_erase;
|
||||
out.read_sectors = mmc_read_sectors;
|
||||
out.perform_opc = mmc_perform_opc;
|
||||
out.set_speed = mmc_set_speed;
|
||||
out.send_parameters = spc_select_error_params;
|
||||
out.send_write_parameters = spc_select_write_params;
|
||||
out.send_cue_sheet = mmc_send_cue_sheet;
|
||||
out.sync_cache = mmc_sync_cache;
|
||||
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;
|
||||
out.mdata = malloc(sizeof(struct scsi_mode_data));
|
||||
out.mdata->valid = 0;
|
||||
if (out.idata == NULL || out.mdata == NULL) {
|
||||
libdax_msgs_submit(libdax_messenger, -1, 0x00020108,
|
||||
LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH,
|
||||
"Could not allocate new drive object", 0, 0);
|
||||
return;
|
||||
}
|
||||
memset(&out.params, 0, sizeof(struct params));
|
||||
t = burn_drive_register(&out);
|
||||
|
||||
/* ts A60821
|
||||
<<< debug: for tracing calls which might use open drive fds */
|
||||
mmc_function_spy("enumerate_common : -------- doing grab");
|
||||
|
||||
/* try to get the drive info */
|
||||
if (t->grab(t)) {
|
||||
burn_print(2, "getting drive info\n");
|
||||
t->getcaps(t);
|
||||
t->unlock(t);
|
||||
t->released = 1;
|
||||
} else {
|
||||
burn_print(2, "unable to grab new located drive\n");
|
||||
}
|
||||
|
||||
/* ts A60821
|
||||
<<< debug: for tracing calls which might use open drive fds */
|
||||
mmc_function_spy("enumerate_common : ----- would release ");
|
||||
|
||||
}
|
||||
|
||||
#else /* Scsi_freebsd_make_own_enumeratE */
|
||||
|
||||
/* The new, more concise version of enumerate_common */
|
||||
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;
|
||||
|
||||
/* Operating system adapter is CAM */
|
||||
/* Adapter specific handles and data */
|
||||
out.cam = NULL;
|
||||
/* Adapter specific functions */
|
||||
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);
|
||||
}
|
||||
|
||||
#endif /* ! Scsi_freebsd_make_own_enumeratE */
|
||||
|
||||
/* ts A61021: do not believe this:
|
||||
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.
|
||||
*/
|
||||
int sg_grab(struct burn_drive *d)
|
||||
{
|
||||
int count;
|
||||
struct cam_device *cam;
|
||||
|
||||
mmc_function_spy("sg_grab");
|
||||
|
||||
assert(d->cam == NULL);
|
||||
|
||||
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;
|
||||
}
|
||||
/* er = ioctl(fd, SG_GET_ACCESS_COUNT, &count);*/
|
||||
count = 1;
|
||||
if (1 == count) {
|
||||
d->cam = cam;
|
||||
fcntl(cam->fd, F_SETOWN, getpid());
|
||||
d->released = 0;
|
||||
return 1;
|
||||
}
|
||||
burn_print(1, "could not acquire drive - already open\n");
|
||||
sg_close_drive(d);
|
||||
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?)
|
||||
*/
|
||||
|
||||
int sg_release(struct burn_drive *d)
|
||||
{
|
||||
mmc_function_spy("sg_release");
|
||||
|
||||
if (d->cam == NULL) {
|
||||
burn_print(1, "release an ungrabbed drive. die\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
mmc_function_spy("sg_release ----------- closing.");
|
||||
|
||||
sg_close_drive(d);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sg_issue_command(struct burn_drive *d, struct command *c)
|
||||
{
|
||||
int done = 0;
|
||||
int err;
|
||||
union ccb *ccb;
|
||||
|
||||
char buf[161];
|
||||
snprintf(buf, sizeof (buf), "sg_issue_command d->cam=%p d->released=%d",
|
||||
(void*)d->cam, d->released);
|
||||
mmc_function_spy(buf);
|
||||
|
||||
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 {
|
||||
assert(c->page->bytes > 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;
|
||||
}
|
||||
|
@ -1,920 +0,0 @@
|
||||
/* -*- 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: 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>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <string.h>
|
||||
#include <sys/poll.h>
|
||||
#include <linux/hdreg.h>
|
||||
#include <stdlib.h>
|
||||
#include <scsi/sg.h>
|
||||
#include <scsi/scsi.h>
|
||||
|
||||
|
||||
/** 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;
|
||||
|
||||
/* 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
|
||||
wether to take O_EXCL rejection as fatal error */
|
||||
extern int burn_sg_open_o_excl;
|
||||
extern int burn_sg_open_o_nonblock;
|
||||
extern int burn_sg_open_abort_busy;
|
||||
|
||||
|
||||
/* ts A60821
|
||||
<<< debug: for tracing calls which might use open drive fds */
|
||||
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 };
|
||||
sg_io_hdr_t s;
|
||||
|
||||
memset(&s, 0, sizeof(sg_io_hdr_t));
|
||||
s.interface_id = 'S';
|
||||
s.dxfer_direction = SG_DXFER_NONE;
|
||||
s.cmd_len = 6;
|
||||
s.cmdp = test_ops;
|
||||
s.timeout = 12345;
|
||||
return ioctl(fd, SG_IO, &s);
|
||||
}
|
||||
|
||||
|
||||
/* ts A60924 */
|
||||
static int sg_handle_busy_device(char *fname, int os_errno)
|
||||
{
|
||||
char msg[4096];
|
||||
|
||||
/* ts A60814 : i saw no way to do this more nicely */
|
||||
if (burn_sg_open_abort_busy) {
|
||||
fprintf(stderr,
|
||||
"\nlibburn: FATAL : Application triggered abort on busy device '%s'\n",
|
||||
fname);
|
||||
|
||||
/* ts A61007 */
|
||||
abort();
|
||||
/* a ssert("drive busy" == "non fatal"); */
|
||||
}
|
||||
|
||||
/* ts A60924 : now reporting to libdax_msgs */
|
||||
sprintf(msg, "Cannot open busy device '%s'", fname);
|
||||
libdax_msgs_submit(libdax_messenger, -1, 0x00020001,
|
||||
LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_LOW,
|
||||
msg, os_errno, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* ts A60926 */
|
||||
static int sg_open_drive_fd(char *fname, int scan_mode)
|
||||
{
|
||||
int open_mode = O_RDWR, fd;
|
||||
char msg[81];
|
||||
|
||||
/* ts A60813 - A60927
|
||||
O_EXCL with devices is a non-POSIX feature
|
||||
of Linux kernels. Possibly introduced 2002.
|
||||
Mentioned in "The Linux SCSI Generic (sg) HOWTO" */
|
||||
if(burn_sg_open_o_excl)
|
||||
open_mode |= O_EXCL;
|
||||
/* ts A60813
|
||||
O_NONBLOCK was already hardcoded in ata_ but not in sg_.
|
||||
There must be some reason for this. So O_NONBLOCK is
|
||||
default mode for both now. Disable on own risk. */
|
||||
if(burn_sg_open_o_nonblock)
|
||||
open_mode |= O_NONBLOCK;
|
||||
|
||||
/* <<< debugging
|
||||
fprintf(stderr,
|
||||
"\nlibburn: experimental: o_excl= %d , o_nonblock= %d, abort_on_busy= %d\n",
|
||||
burn_sg_open_o_excl,burn_sg_open_o_nonblock,burn_sg_open_abort_busy);
|
||||
fprintf(stderr,
|
||||
"libburn: experimental: O_EXCL= %d , O_NONBLOCK= %d\n",
|
||||
!!(open_mode&O_EXCL),!!(open_mode&O_NONBLOCK));
|
||||
*/
|
||||
|
||||
fd = open(fname, open_mode);
|
||||
if (fd == -1) {
|
||||
/* <<< debugging
|
||||
fprintf(stderr,
|
||||
"\nlibburn: experimental: fname= %s , errno= %d\n",
|
||||
fname,errno);
|
||||
*/
|
||||
if (errno == EBUSY) {
|
||||
sg_handle_busy_device(fname, errno);
|
||||
return -1;
|
||||
|
||||
}
|
||||
if (scan_mode)
|
||||
return -1;
|
||||
sprintf(msg, "Failed to open device '%s'",fname);
|
||||
libdax_msgs_submit(libdax_messenger, -1, 0x00020005,
|
||||
LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH,
|
||||
msg, errno, 0);
|
||||
return -1;
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
|
||||
/* 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 */
|
||||
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)
|
||||
{
|
||||
int tld, i, ret, fd, i_bus_no = -1;
|
||||
int i_host_no = -1, i_channel_no = -1, i_target_no = -1, i_lun_no = -1;
|
||||
char msg[161], fname[81];
|
||||
|
||||
static char tldev[][81]= {"/dev/sr%d", "/dev/scd%d", "/dev/st%d", ""};
|
||||
|
||||
if(host_no < 0 || id_no < 0 || channel_no < 0 || lun_no < 0)
|
||||
return(2);
|
||||
if(*sibling_count > 0)
|
||||
sg_release_siblings(sibling_fds, sibling_count);
|
||||
|
||||
for (tld = 0; tldev[tld][0] != 0; tld++) {
|
||||
for (i = 0; i < 32; i++) {
|
||||
sprintf(fname, tldev[tld], i);
|
||||
ret = sg_obtain_scsi_adr(fname, &i_bus_no, &i_host_no,
|
||||
&i_channel_no, &i_target_no, &i_lun_no);
|
||||
if (ret <= 0)
|
||||
continue;
|
||||
if (i_host_no != host_no || i_channel_no != channel_no)
|
||||
continue;
|
||||
if (i_target_no != id_no || i_lun_no != lun_no)
|
||||
continue;
|
||||
|
||||
fd = sg_open_drive_fd(fname, 0);
|
||||
if (fd < 0)
|
||||
goto failed;
|
||||
|
||||
if (*sibling_count>=LIBBURN_SG_MAX_SIBLINGS) {
|
||||
sprintf(msg, "Too many scsi siblings of '%s'",
|
||||
path);
|
||||
libdax_msgs_submit(libdax_messenger,
|
||||
driveno, 0x00020006,
|
||||
LIBDAX_MSGS_SEV_FATAL,
|
||||
LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0);
|
||||
goto failed;
|
||||
}
|
||||
sprintf(msg, "Opened O_EXCL scsi sibling '%s' of '%s'",
|
||||
fname, path);
|
||||
libdax_msgs_submit(libdax_messenger, driveno,
|
||||
0x00020004,
|
||||
LIBDAX_MSGS_SEV_NOTE, LIBDAX_MSGS_PRIO_HIGH,
|
||||
msg, 0, 0);
|
||||
sibling_fds[*sibling_count] = fd;
|
||||
(*sibling_count)++;
|
||||
}
|
||||
}
|
||||
return(1);
|
||||
failed:;
|
||||
sg_release_siblings(sibling_fds, sibling_count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/** 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;
|
||||
char fname[10];
|
||||
|
||||
for (i = 0; i < 26; i++) {
|
||||
sprintf(fname, "/dev/hd%c", 'a' + i);
|
||||
/* ts A51221 */
|
||||
if (burn_drive_is_banned(fname))
|
||||
continue;
|
||||
fd = sg_open_drive_fd(fname, 1);
|
||||
if (fd == -1)
|
||||
continue;
|
||||
|
||||
/* found a drive */
|
||||
ioctl(fd, HDIO_GET_IDENTITY, &tm);
|
||||
|
||||
/* not atapi */
|
||||
if (!(tm.config & 0x8000) || (tm.config & 0x4000)) {
|
||||
sg_close_drive_fd(fname, -1, &fd, 0);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* if SG_IO fails on an atapi device, we should stop trying to
|
||||
use hd* devices */
|
||||
if (sgio_test(fd) == -1) {
|
||||
sg_close_drive_fd(fname, -1, &fd, 0);
|
||||
return;
|
||||
}
|
||||
if (sg_close_drive_fd(fname, -1, &fd, 1) <= 0)
|
||||
continue;
|
||||
enumerate_common(fname, -1, -1, -1, -1, -1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** 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;
|
||||
int bus_no = -1;
|
||||
char fname[10];
|
||||
|
||||
for (i = 0; i < 32; i++) {
|
||||
sprintf(fname, "/dev/sg%d", i);
|
||||
/* ts A51221 */
|
||||
if (burn_drive_is_banned(fname))
|
||||
continue;
|
||||
/* ts A60927 */
|
||||
fd = sg_open_drive_fd(fname, 1);
|
||||
if (fd == -1)
|
||||
continue;
|
||||
|
||||
/* found a drive */
|
||||
ioctl(fd, SG_GET_SCSI_ID, &sid);
|
||||
|
||||
#ifdef SCSI_IOCTL_GET_BUS_NUMBER
|
||||
/* Hearsay A61005 */
|
||||
if (ioctl(fd, SCSI_IOCTL_GET_BUS_NUMBER, &bus_no) == -1)
|
||||
bus_no = -1;
|
||||
#endif
|
||||
|
||||
if (sg_close_drive_fd(fname, -1, &fd,
|
||||
sid.scsi_type == TYPE_ROM ) <= 0)
|
||||
continue;
|
||||
if (sid.scsi_type != TYPE_ROM)
|
||||
continue;
|
||||
|
||||
/* ts A60927 : trying to do locking with growisofs */
|
||||
if(burn_sg_open_o_excl>1) {
|
||||
ret = sg_open_scsi_siblings(
|
||||
fname, -1, sibling_fds, &sibling_count,
|
||||
sid.host_no, sid.channel,
|
||||
sid.scsi_id, sid.lun);
|
||||
if (ret<=0) {
|
||||
sg_handle_busy_device(fname, 0);
|
||||
continue;
|
||||
}
|
||||
/* the final occupation will be done in sg_grab() */
|
||||
sg_release_siblings(sibling_fds, &sibling_count);
|
||||
}
|
||||
#ifdef SCSI_IOCTL_GET_BUS_NUMBER
|
||||
if(bus_no == -1)
|
||||
bus_no = 1000 * (sid.host_no + 1) + sid.channel;
|
||||
#else
|
||||
bus_no = sid.host_no;
|
||||
#endif
|
||||
enumerate_common(fname, bus_no, sid.host_no, sid.channel,
|
||||
sid.scsi_id, sid.lun);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* 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,
|
||||
int channel_no, int target_no, int lun_no)
|
||||
{
|
||||
int ret, i;
|
||||
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 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;
|
||||
|
||||
/* 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. */
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
|
||||
/** 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, os_errno= 0, ret;
|
||||
|
||||
/* ts A60813 */
|
||||
int open_mode = O_RDWR;
|
||||
|
||||
/* ts A60821
|
||||
<<< debug: for tracing calls which might use open drive fds */
|
||||
mmc_function_spy("sg_grab");
|
||||
|
||||
|
||||
/* ts A60813 - A60927
|
||||
O_EXCL with devices is a non-POSIX feature
|
||||
of Linux kernels. Possibly introduced 2002.
|
||||
Mentioned in "The Linux SCSI Generic (sg) HOWTO".
|
||||
*/
|
||||
if(burn_sg_open_o_excl)
|
||||
open_mode |= O_EXCL;
|
||||
|
||||
/* ts A60813
|
||||
O_NONBLOCK was hardcoded here. So it should stay default mode. */
|
||||
if(burn_sg_open_o_nonblock)
|
||||
open_mode |= O_NONBLOCK;
|
||||
|
||||
/* 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. */
|
||||
if(! burn_drive_is_open(d)) {
|
||||
|
||||
/* ts A60821
|
||||
<<< debug: for tracing calls which might use open drive fds */
|
||||
mmc_function_spy("sg_grab ----------- opening");
|
||||
|
||||
/* ts A60926 */
|
||||
if(burn_sg_open_o_excl>1) {
|
||||
fd = -1;
|
||||
ret = sg_open_scsi_siblings(d->devname,
|
||||
d->global_index,d->sibling_fds,
|
||||
&(d->sibling_count),
|
||||
d->host, d->channel, d->id, d->lun);
|
||||
if(ret <= 0)
|
||||
goto drive_is_in_use;
|
||||
}
|
||||
|
||||
fd = open(d->devname, open_mode);
|
||||
os_errno = errno;
|
||||
} else
|
||||
fd= d->fd;
|
||||
|
||||
if (fd >= 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;
|
||||
}
|
||||
|
||||
|
||||
/** 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
|
||||
<<< debug: for tracing calls which might use open drive fds */
|
||||
mmc_function_spy("sg_release");
|
||||
|
||||
if (d->fd < 1) {
|
||||
burn_print(1, "release an ungrabbed drive. die\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ts A60821
|
||||
<<< debug: for tracing calls which might use open drive fds */
|
||||
mmc_function_spy("sg_release ----------- closing");
|
||||
|
||||
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, no_c_page = 0;
|
||||
int err;
|
||||
sg_io_hdr_t s;
|
||||
/*
|
||||
#define Libburn_log_sg_commandS 1
|
||||
*/
|
||||
|
||||
#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;
|
||||
return 0;
|
||||
}
|
||||
|
||||
c->error = 0;
|
||||
memset(&s, 0, sizeof(sg_io_hdr_t));
|
||||
|
||||
s.interface_id = 'S';
|
||||
|
||||
if (c->dir == TO_DRIVE)
|
||||
s.dxfer_direction = SG_DXFER_TO_DEV;
|
||||
else if (c->dir == FROM_DRIVE)
|
||||
s.dxfer_direction = SG_DXFER_FROM_DEV;
|
||||
else if (c->dir == NO_TRANSFER) {
|
||||
s.dxfer_direction = SG_DXFER_NONE;
|
||||
|
||||
/* ts A61007 */
|
||||
/* a ssert(!c->page); */
|
||||
no_c_page = 1;
|
||||
}
|
||||
s.cmd_len = c->oplen;
|
||||
s.cmdp = c->opcode;
|
||||
s.mx_sb_len = 32;
|
||||
s.sbp = c->sense;
|
||||
memset(c->sense, 0, sizeof(c->sense));
|
||||
s.timeout = 200000;
|
||||
if (c->page && !no_c_page) {
|
||||
s.dxferp = c->page->data;
|
||||
if (c->dir == FROM_DRIVE) {
|
||||
s.dxfer_len = BUFFER_SIZE;
|
||||
/* touch page so we can use valgrind */
|
||||
memset(c->page->data, 0, BUFFER_SIZE);
|
||||
} else {
|
||||
|
||||
/* ts A61010 */
|
||||
/* a ssert(c->page->bytes > 0); */
|
||||
if (c->page->bytes <= 0) {
|
||||
c->error = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
s.dxfer_len = c->page->bytes;
|
||||
}
|
||||
} else {
|
||||
s.dxferp = NULL;
|
||||
s.dxfer_len = 0;
|
||||
}
|
||||
s.usr_ptr = c;
|
||||
|
||||
do {
|
||||
err = ioctl(d->fd, SG_IO, &s);
|
||||
|
||||
/* ts A61010 */
|
||||
/* a ssert(err != -1); */
|
||||
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);
|
||||
sg_close_drive(d);
|
||||
d->released = 1;
|
||||
d->busy = BURN_DRIVE_IDLE;
|
||||
c->error = 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (s.sb_len_wr) {
|
||||
if (!c->retry) {
|
||||
c->error = 1;
|
||||
|
||||
/* A61106: rather than : return 1 */
|
||||
goto ex;
|
||||
}
|
||||
switch (scsi_error(d, s.sbp, s.sb_len_wr)) {
|
||||
case RETRY:
|
||||
done = 0;
|
||||
break;
|
||||
case FAIL:
|
||||
done = 1;
|
||||
c->error = 1;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/* ts A60922 */
|
||||
/** 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)
|
||||
{
|
||||
int fd, ret;
|
||||
struct my_scsi_idlun {
|
||||
int x;
|
||||
int host_unique_id;
|
||||
};
|
||||
struct my_scsi_idlun idlun;
|
||||
|
||||
if (strncmp(path, "/dev/hd", 7) == 0
|
||||
&& path[7] >= 'a' && path[7] <= 'z' && path[8] == 0)
|
||||
return 0; /* on RIP 14 all hdx return SCSI adr 0,0,0,0 */
|
||||
|
||||
fd = open(path, O_RDONLY | O_NONBLOCK);
|
||||
if(fd < 0)
|
||||
return 0;
|
||||
|
||||
#ifdef SCSI_IOCTL_GET_BUS_NUMBER
|
||||
/* Hearsay A61005 */
|
||||
if (ioctl(fd, SCSI_IOCTL_GET_BUS_NUMBER, bus_no) == -1)
|
||||
*bus_no = -1;
|
||||
#endif
|
||||
|
||||
/* http://www.tldp.org/HOWTO/SCSI-Generic-HOWTO/scsi_g_idlun.html */
|
||||
ret = ioctl(fd, SCSI_IOCTL_GET_IDLUN, &idlun);
|
||||
|
||||
sg_close_drive_fd(path, -1, &fd, 0);
|
||||
if (ret == -1)
|
||||
return(0);
|
||||
*host_no= (idlun.x>>24)&255;
|
||||
*channel_no= (idlun.x>>16)&255;
|
||||
*target_no= (idlun.x)&255;
|
||||
*lun_no= (idlun.x>>8)&255;
|
||||
#ifdef SCSI_IOCTL_GET_BUS_NUMBER
|
||||
if(*bus_no == -1)
|
||||
*bus_no = 1000 * (*host_no + 1) + *channel_no;
|
||||
#else
|
||||
*bus_no= *host_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);
|
||||
}
|
||||
|
||||
|
499
libburn/sg.c
499
libburn/sg.c
@ -1,17 +1,500 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
|
||||
/* sg.c
|
||||
Switcher for operating system dependent transport level modules of libburn.
|
||||
Copyright (C) 2006 Thomas Schmitt <scdbackup@gmx.net>, provided under GPL
|
||||
#include <assert.h>
|
||||
#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 <malloc.h>
|
||||
#include <string.h>
|
||||
#include <sys/poll.h>
|
||||
#include <linux/hdreg.h>
|
||||
|
||||
#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"
|
||||
|
||||
static void enumerate_common(char *fname);
|
||||
|
||||
/* ts A51221 */
|
||||
int burn_drive_is_banned(char *device_address);
|
||||
|
||||
/* ts A60813 : storage objects are in libburn/init.c
|
||||
wether to use O_EXCL
|
||||
wether to use O_NOBLOCK with open(2) on devices
|
||||
wether to take O_EXCL rejection as fatal error */
|
||||
extern int burn_sg_open_o_excl;
|
||||
extern int burn_sg_open_o_nonblock;
|
||||
extern int burn_sg_open_abort_busy;
|
||||
|
||||
|
||||
/* ts A60821
|
||||
<<< debug: for tracing calls which might use open drive fds */
|
||||
int mmc_function_spy(char * text);
|
||||
|
||||
|
||||
static int sgio_test(int fd)
|
||||
{
|
||||
unsigned char test_ops[] = { 0, 0, 0, 0, 0, 0 };
|
||||
sg_io_hdr_t s;
|
||||
|
||||
memset(&s, 0, sizeof(sg_io_hdr_t));
|
||||
s.interface_id = 'S';
|
||||
s.dxfer_direction = SG_DXFER_NONE;
|
||||
s.cmd_len = 6;
|
||||
s.cmdp = test_ops;
|
||||
s.timeout = 12345;
|
||||
return ioctl(fd, SG_IO, &s);
|
||||
}
|
||||
|
||||
void ata_enumerate(void)
|
||||
{
|
||||
struct hd_driveid tm;
|
||||
int i, fd;
|
||||
char fname[10];
|
||||
|
||||
/* ts A60813 */
|
||||
int open_mode = O_RDWR;
|
||||
|
||||
/* ts A60813
|
||||
O_EXCL with block devices is an unpublished feature
|
||||
of Linux kernels. Possibly introduced 2002.
|
||||
It can only be used if libburn stops opening several
|
||||
file descriptor on the same block device.
|
||||
See comment in sg_grab() */
|
||||
if(burn_sg_open_o_excl)
|
||||
open_mode |= O_EXCL;
|
||||
/* ts A60813
|
||||
O_NONBLOCK was already hardcoded in ata_ but not in sg_.
|
||||
There must be some reason for this. So O_NONBLOCK is
|
||||
default mode for both now. Disable on own risk. */
|
||||
if(burn_sg_open_o_nonblock)
|
||||
open_mode |= O_NONBLOCK;
|
||||
|
||||
for (i = 0; i < 26; i++) {
|
||||
sprintf(fname, "/dev/hd%c", 'a' + i);
|
||||
/* open O_RDWR so we don't think read only drives are
|
||||
in some way useful
|
||||
*/
|
||||
/* ts A51221 */
|
||||
if (burn_drive_is_banned(fname))
|
||||
continue;
|
||||
fd = open(fname, open_mode);
|
||||
if (fd == -1) {
|
||||
/* <<< debugging
|
||||
fprintf(stderr,
|
||||
"\nlibburn: experimental: fname= %s , errno= %d\n",
|
||||
fname,errno);
|
||||
*/
|
||||
/* ts A60814 : i see no way to do this more nicely */
|
||||
if (errno == EBUSY && burn_sg_open_abort_busy) {
|
||||
fprintf(stderr,
|
||||
"\nlibburn: FATAL : Application triggered abort on busy drive '%s'\n",
|
||||
fname);
|
||||
/* <<< maybe one should plainly exit here */
|
||||
assert("drive busy" == "non fatal");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
/* found a drive */
|
||||
ioctl(fd, HDIO_GET_IDENTITY, &tm);
|
||||
|
||||
/* not atapi */
|
||||
if (!(tm.config & 0x8000) || (tm.config & 0x4000)) {
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* if SG_IO fails on an atapi device, we should stop trying to
|
||||
use hd* devices */
|
||||
if (sgio_test(fd) == -1) {
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
close(fd);
|
||||
enumerate_common(fname);
|
||||
}
|
||||
}
|
||||
|
||||
void sg_enumerate(void)
|
||||
{
|
||||
struct sg_scsi_id sid;
|
||||
int i, fd;
|
||||
char fname[10];
|
||||
|
||||
/* ts A60813 */
|
||||
int open_mode = O_RDWR;
|
||||
|
||||
/* ts A60813
|
||||
O_EXCL with block devices is an unpublished feature
|
||||
of Linux kernels. Possibly introduced 2002.
|
||||
It can only be used if libburn stops opening several
|
||||
file descriptor on the same block device.
|
||||
See comment in sg_grab() */
|
||||
if(burn_sg_open_o_excl)
|
||||
open_mode |= O_EXCL;
|
||||
/* ts A60813
|
||||
O_NONBLOCK was not hardcoded in sg_ but was in ata_.
|
||||
I myself test mainly sg_ and it seems to be ok with
|
||||
O_NONBLOCK. So it should stay default mode. */
|
||||
if(burn_sg_open_o_nonblock)
|
||||
open_mode |= O_NONBLOCK;
|
||||
|
||||
/* <<< debugging
|
||||
fprintf(stderr,
|
||||
"\nlibburn: experimental: o_excl= %d , o_nonblock= %d, abort_on_busy= %d\n",
|
||||
burn_sg_open_o_excl,burn_sg_open_o_nonblock,burn_sg_open_abort_busy);
|
||||
fprintf(stderr,
|
||||
"libburn: experimental: O_EXCL= %d , O_NONBLOCK= %d\n",
|
||||
!!(open_mode&O_EXCL),!!(open_mode&O_NONBLOCK));
|
||||
|
||||
*/
|
||||
|
||||
for (i = 0; i < 32; i++) {
|
||||
sprintf(fname, "/dev/sg%d", i);
|
||||
/* open RDWR so we don't accidentally think read only drives
|
||||
are in some way useful
|
||||
*/
|
||||
/* ts A51221 */
|
||||
if (burn_drive_is_banned(fname))
|
||||
continue;
|
||||
fd = open(fname, open_mode);
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
if (fd == -1) {
|
||||
/* <<< debugging
|
||||
fprintf(stderr,
|
||||
"\n cdrskin: experimental: fname= %s , errno= %d\n",
|
||||
fname,errno);
|
||||
*/
|
||||
/* ts A60814 : i see no way to do this more nicely */
|
||||
if (errno == EBUSY && burn_sg_open_abort_busy) {
|
||||
fprintf(stderr,
|
||||
"\nlibburn: FATAL : Application triggered abort on busy drive '%s'\n",
|
||||
fname);
|
||||
/* <<< maybe one should plainly exit here */
|
||||
assert("drive busy" == "non fatal");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
/* found a drive */
|
||||
ioctl(fd, SG_GET_SCSI_ID, &sid);
|
||||
close(fd);
|
||||
if (sid.scsi_type != TYPE_ROM)
|
||||
continue;
|
||||
|
||||
#include "sg-freebsd.c"
|
||||
enumerate_common(fname);
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
static void enumerate_common(char *fname)
|
||||
{
|
||||
struct burn_drive *t;
|
||||
struct burn_drive out;
|
||||
|
||||
#include "sg-linux.c"
|
||||
out.devname = burn_strdup(fname);
|
||||
out.fd = -1337;
|
||||
|
||||
#endif
|
||||
out.grab = sg_grab;
|
||||
out.release = sg_release;
|
||||
out.issue_command = sg_issue_command;
|
||||
out.getcaps = spc_getcaps;
|
||||
out.released = 1;
|
||||
out.status = BURN_DISC_UNREADY;
|
||||
|
||||
out.eject = sbc_eject;
|
||||
out.load = sbc_load;
|
||||
out.lock = spc_prevent;
|
||||
out.unlock = spc_allow;
|
||||
out.read_disc_info = spc_sense_write_params;
|
||||
out.get_erase_progress = spc_get_erase_progress;
|
||||
out.test_unit_ready = spc_test_unit_ready;
|
||||
out.probe_write_modes = spc_probe_write_modes;
|
||||
out.read_toc = mmc_read_toc;
|
||||
out.write = mmc_write;
|
||||
out.erase = mmc_erase;
|
||||
out.read_sectors = mmc_read_sectors;
|
||||
out.perform_opc = mmc_perform_opc;
|
||||
out.set_speed = mmc_set_speed;
|
||||
out.send_parameters = spc_select_error_params;
|
||||
out.send_write_parameters = spc_select_write_params;
|
||||
out.send_cue_sheet = mmc_send_cue_sheet;
|
||||
out.sync_cache = mmc_sync_cache;
|
||||
out.get_nwa = mmc_get_nwa;
|
||||
out.close_disc = mmc_close_disc;
|
||||
out.close_session = mmc_close_session;
|
||||
out.idata = malloc(sizeof(struct scsi_inquiry_data));
|
||||
out.idata->valid = 0;
|
||||
out.mdata = malloc(sizeof(struct scsi_mode_data));
|
||||
out.mdata->valid = 0;
|
||||
memset(&out.params, 0, sizeof(struct params));
|
||||
t = burn_drive_register(&out);
|
||||
|
||||
/* ts A60821
|
||||
<<< debug: for tracing calls which might use open drive fds */
|
||||
mmc_function_spy("enumerate_common : -------- doing grab");
|
||||
|
||||
/* try to get the drive info */
|
||||
if (sg_grab(t)) {
|
||||
burn_print(2, "getting drive info\n");
|
||||
t->getcaps(t);
|
||||
t->unlock(t);
|
||||
t->released = 1;
|
||||
} else {
|
||||
burn_print(2, "unable to grab new located drive\n");
|
||||
}
|
||||
|
||||
/* ts A60821
|
||||
<<< debug: for tracing calls which might use open drive fds */
|
||||
mmc_function_spy("enumerate_common : ----- would release ");
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
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 diabled already in icculus.org/burn CVS.
|
||||
*/
|
||||
int sg_grab(struct burn_drive *d)
|
||||
{
|
||||
int fd, count;
|
||||
|
||||
/* ts A60813 */
|
||||
int open_mode = O_RDWR;
|
||||
|
||||
/* ts A60821
|
||||
<<< debug: for tracing calls which might use open drive fds */
|
||||
mmc_function_spy("sg_grab");
|
||||
|
||||
|
||||
/* ts A60813
|
||||
O_EXCL with block devices is an unpublished feature
|
||||
of Linux kernels. Possibly introduced 2002.
|
||||
It can only be used if libburn stops opening several
|
||||
file descriptor on the same block device.
|
||||
See comment below */
|
||||
if(burn_sg_open_o_excl)
|
||||
open_mode |= O_EXCL;
|
||||
|
||||
/* ts A60813
|
||||
O_NONBLOCK was hardcoded here. So it should stay default mode. */
|
||||
if(burn_sg_open_o_nonblock)
|
||||
open_mode |= O_NONBLOCK;
|
||||
|
||||
/* ts A60813
|
||||
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
|
||||
<<< debug: for tracing calls which might use open drive fds */
|
||||
mmc_function_spy("sg_grab ----------- opening");
|
||||
|
||||
fd = open(d->devname, open_mode);
|
||||
} else
|
||||
fd= d->fd;
|
||||
|
||||
assert(fd != -1337);
|
||||
if (-1 != fd) {
|
||||
|
||||
/* ts A60814:
|
||||
according to my experiments this test would work now ! */
|
||||
|
||||
/* 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;
|
||||
}
|
||||
burn_print(1, "could not acquire drive - already open\n");
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
burn_print(1, "could not acquire drive\n");
|
||||
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?)
|
||||
*/
|
||||
|
||||
int sg_release(struct burn_drive *d)
|
||||
{
|
||||
/* ts A60821
|
||||
<<< debug: for tracing calls which might use open drive fds */
|
||||
mmc_function_spy("sg_release");
|
||||
|
||||
if (d->fd < 1) {
|
||||
burn_print(1, "release an ungrabbed drive. die\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ts A60821
|
||||
<<< debug: for tracing calls which might use open drive fds */
|
||||
mmc_function_spy("sg_release ----------- closing");
|
||||
|
||||
close(d->fd);
|
||||
d->fd = -1337;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sg_issue_command(struct burn_drive *d, struct command *c)
|
||||
{
|
||||
int done = 0;
|
||||
int err;
|
||||
sg_io_hdr_t s;
|
||||
|
||||
|
||||
/* 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);
|
||||
|
||||
|
||||
c->error = 0;
|
||||
/*
|
||||
this is valid during the mode probe in scan
|
||||
if (d->fd < 1 || d->released) {
|
||||
burn_print(1,
|
||||
"command issued on ungrabbed drive, chaos.\n");
|
||||
burn_print(1, "fd = %d, released = %d\n", d->fd,
|
||||
d->released);
|
||||
}
|
||||
*/
|
||||
memset(&s, 0, sizeof(sg_io_hdr_t));
|
||||
|
||||
s.interface_id = 'S';
|
||||
|
||||
if (c->dir == TO_DRIVE)
|
||||
s.dxfer_direction = SG_DXFER_TO_DEV;
|
||||
else if (c->dir == FROM_DRIVE)
|
||||
s.dxfer_direction = SG_DXFER_FROM_DEV;
|
||||
else if (c->dir == NO_TRANSFER) {
|
||||
s.dxfer_direction = SG_DXFER_NONE;
|
||||
assert(!c->page);
|
||||
}
|
||||
s.cmd_len = c->oplen;
|
||||
s.cmdp = c->opcode;
|
||||
s.mx_sb_len = 32;
|
||||
s.sbp = c->sense;
|
||||
memset(c->sense, 0, sizeof(c->sense));
|
||||
s.timeout = 200000;
|
||||
if (c->page) {
|
||||
s.dxferp = c->page->data;
|
||||
if (c->dir == FROM_DRIVE) {
|
||||
s.dxfer_len = BUFFER_SIZE;
|
||||
/* touch page so we can use valgrind */
|
||||
memset(c->page->data, 0, BUFFER_SIZE);
|
||||
} else {
|
||||
assert(c->page->bytes > 0);
|
||||
s.dxfer_len = c->page->bytes;
|
||||
}
|
||||
} else {
|
||||
s.dxferp = NULL;
|
||||
s.dxfer_len = 0;
|
||||
}
|
||||
s.usr_ptr = c;
|
||||
|
||||
do {
|
||||
err = ioctl(d->fd, SG_IO, &s);
|
||||
assert(err != -1);
|
||||
if (s.sb_len_wr) {
|
||||
if (!c->retry) {
|
||||
c->error = 1;
|
||||
return 1;
|
||||
}
|
||||
switch (scsi_error(d, s.sbp, s.sb_len_wr)) {
|
||||
case RETRY:
|
||||
done = 0;
|
||||
break;
|
||||
case FAIL:
|
||||
done = 1;
|
||||
c->error = 1;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
done = 1;
|
||||
}
|
||||
} while (!done);
|
||||
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;
|
||||
}
|
||||
|
25
libburn/sg.h
25
libburn/sg.h
@ -3,32 +3,17 @@
|
||||
#ifndef __SG
|
||||
#define __SG
|
||||
|
||||
|
||||
#include "os.h"
|
||||
|
||||
|
||||
/* see os.h for name of particular os-*.h where this is defined */
|
||||
BURN_OS_DEFINE_DRIVE_ENUMERATOR_T
|
||||
|
||||
|
||||
struct burn_drive;
|
||||
struct command;
|
||||
|
||||
enum response
|
||||
{ RETRY, FAIL };
|
||||
|
||||
/* ts A60922 ticket 33 */
|
||||
int sg_give_next_adr(burn_drive_enumerator_t *enm_context,
|
||||
char adr[], int adr_size, int initialize);
|
||||
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);
|
||||
|
||||
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 *);
|
||||
|
||||
/* ts A61115 : formerly sg_enumerate();ata_enumerate() */
|
||||
int scsi_enumerate_drives(void);
|
||||
|
||||
int sg_drive_is_open(struct burn_drive * d);
|
||||
enum response scsi_error(struct burn_drive *, unsigned char *, int);
|
||||
|
||||
#endif /* __SG */
|
||||
|
@ -22,10 +22,6 @@ 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;
|
||||
}
|
||||
|
||||
@ -33,7 +29,8 @@ struct burn_source *burn_source_new(void)
|
||||
{
|
||||
struct burn_source *out;
|
||||
|
||||
out = calloc(1, sizeof(struct burn_source));
|
||||
out = malloc(sizeof(struct burn_source));
|
||||
memset(out, 0, sizeof(struct burn_source));
|
||||
out->refcount = 1;
|
||||
return out;
|
||||
}
|
||||
|
268
libburn/spc.c
268
libburn/spc.c
@ -9,10 +9,7 @@
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <string.h>
|
||||
|
||||
/* ts A61008 */
|
||||
/* #include <a ssert.h> */
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "transport.h"
|
||||
@ -23,10 +20,6 @@
|
||||
#include "debug.h"
|
||||
#include "options.h"
|
||||
|
||||
#include "libdax_msgs.h"
|
||||
extern struct libdax_msgs *libdax_messenger;
|
||||
|
||||
|
||||
/* spc command set */
|
||||
static unsigned char SPC_INQUIRY[] = { 0x12, 0, 0, 0, 255, 0 };
|
||||
|
||||
@ -79,7 +72,7 @@ int spc_get_erase_progress(struct burn_drive *d)
|
||||
void spc_inquiry(struct burn_drive *d)
|
||||
{
|
||||
struct buffer buf;
|
||||
struct burn_scsi_inquiry_data *id;
|
||||
struct scsi_inquiry_data *id;
|
||||
struct command c;
|
||||
|
||||
memcpy(c.opcode, SPC_INQUIRY, sizeof(SPC_INQUIRY));
|
||||
@ -91,7 +84,7 @@ void spc_inquiry(struct burn_drive *d)
|
||||
c.dir = FROM_DRIVE;
|
||||
d->issue_command(d, &c);
|
||||
|
||||
id = (struct burn_scsi_inquiry_data *)d->idata;
|
||||
id = (struct scsi_inquiry_data *)d->idata;
|
||||
id->vendor[8] = 0;
|
||||
id->product[16] = 0;
|
||||
id->revision[4] = 0;
|
||||
@ -161,24 +154,10 @@ void spc_sense_caps(struct burn_drive *d)
|
||||
m->cdrw_write = page[3] & 2;
|
||||
m->cdr_read = page[2] & 1;
|
||||
m->cdr_write = page[3] & 1;
|
||||
|
||||
/* ts A61021 : these fields are marked obsolete in MMC 3 */
|
||||
m->max_read_speed = page[8] * 256 + page[9];
|
||||
m->cur_read_speed = page[14] * 256 + page[15];
|
||||
|
||||
/* in MMC-3 : see [30-31] and blocks beginning at [32] */
|
||||
m->max_write_speed = page[18] * 256 + page[19];
|
||||
/* New field to be set by atip */
|
||||
m->min_write_speed = m->max_write_speed;
|
||||
|
||||
/* in MMC-3 : [28-29] */
|
||||
m->cur_write_speed = page[20] * 256 + page[21];
|
||||
|
||||
/* >>> ts A61021 : iterate over all speeds :
|
||||
data[30-31]: number of speed performance descriptor blocks
|
||||
data[32-35]: block 0 : [+2-3] speed in kbytes/sec
|
||||
*/
|
||||
|
||||
m->c2_pointers = page[5] & 16;
|
||||
m->valid = 1;
|
||||
m->underrun_proof = page[4] & 128;
|
||||
@ -223,10 +202,7 @@ void spc_select_error_params(struct burn_drive *d,
|
||||
c.page = &buf;
|
||||
c.page->bytes = 0;
|
||||
c.page->sectors = 0;
|
||||
|
||||
/* ts A61007 : moved up to only caller burn_disc_read() */
|
||||
/* a ssert(d->mdata->valid); */
|
||||
|
||||
assert(d->mdata->valid);
|
||||
memset(c.page->data, 0, 8 + 2 + d->mdata->retry_page_length);
|
||||
c.page->bytes = 8 + 2 + d->mdata->retry_page_length;
|
||||
c.page->data[8] = 1;
|
||||
@ -251,9 +227,8 @@ void spc_sense_write_params(struct burn_drive *d)
|
||||
unsigned char *page;
|
||||
struct command c;
|
||||
|
||||
/* ts A61007 : Done in soft at only caller burn_drive_grab() */
|
||||
/* a ssert(d->mdata->cdr_write || d->mdata->cdrw_write ||
|
||||
d->mdata->dvdr_write || d->mdata->dvdram_write); */
|
||||
assert(d->mdata->cdr_write || d->mdata->cdrw_write ||
|
||||
d->mdata->dvdr_write || d->mdata->dvdram_write);
|
||||
|
||||
memcpy(c.opcode, SPC_MODE_SENSE, sizeof(SPC_MODE_SENSE));
|
||||
c.retry = 1;
|
||||
@ -274,11 +249,6 @@ 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)
|
||||
{
|
||||
@ -286,16 +256,7 @@ void spc_select_write_params(struct burn_drive *d,
|
||||
struct command c;
|
||||
int bufe, sim;
|
||||
|
||||
/* 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));
|
||||
*/
|
||||
|
||||
assert(o->drive == d);
|
||||
memcpy(c.opcode, SPC_MODE_SELECT, sizeof(SPC_MODE_SELECT));
|
||||
c.retry = 1;
|
||||
c.oplen = sizeof(SPC_MODE_SELECT);
|
||||
@ -303,10 +264,7 @@ void spc_select_write_params(struct burn_drive *d,
|
||||
c.page = &buf;
|
||||
c.page->bytes = 0;
|
||||
c.page->sectors = 0;
|
||||
|
||||
/* ts A61007 : moved up to burn_disc_write() */
|
||||
/* a ssert(d->mdata->valid); */
|
||||
|
||||
assert(d->mdata->valid);
|
||||
memset(c.page->data, 0, 8 + 2 + d->mdata->write_page_length);
|
||||
c.page->bytes = 8 + 2 + d->mdata->write_page_length;
|
||||
c.page->data[8] = 5;
|
||||
@ -319,17 +277,8 @@ void spc_select_write_params(struct burn_drive *d,
|
||||
c.page->data[10] = (bufe << 6)
|
||||
+ (sim << 4)
|
||||
+ o->write_type;
|
||||
|
||||
/* ts A61106 : MMC-1 table 110 : multi==0 or multi==3 */
|
||||
c.page->data[11] = ((3 * !!o->multi) << 6) | o->control;
|
||||
|
||||
c.page->data[11] = (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! */
|
||||
@ -382,10 +331,7 @@ 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];
|
||||
@ -429,7 +375,6 @@ void spc_probe_write_modes(struct burn_drive *d)
|
||||
}
|
||||
}
|
||||
|
||||
/** @return -1 = error */
|
||||
int spc_block_type(enum burn_block_types b)
|
||||
{
|
||||
switch (b) {
|
||||
@ -455,199 +400,6 @@ int spc_block_type(enum burn_block_types b)
|
||||
return 12;
|
||||
case BURN_BLOCK_MODE2_OK:
|
||||
return 13;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
/* ts A61007 : already prevented in burn_write_opts_set_write_type() */
|
||||
/* a ssert(0); */;
|
||||
assert(0);
|
||||
}
|
||||
|
||||
/* ts A61021 : the spc specific part of sg.c:enumerate_common()
|
||||
*/
|
||||
int spc_setup_drive(struct burn_drive *d)
|
||||
{
|
||||
d->getcaps = spc_getcaps;
|
||||
d->lock = spc_prevent;
|
||||
d->unlock = spc_allow;
|
||||
d->read_disc_info = spc_sense_write_params;
|
||||
d->get_erase_progress = spc_get_erase_progress;
|
||||
d->test_unit_ready = spc_test_unit_ready;
|
||||
d->probe_write_modes = spc_probe_write_modes;
|
||||
d->send_parameters = spc_select_error_params;
|
||||
d->send_write_parameters = spc_select_write_params;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ts A61021 : the general SCSI specific part of sg.c:enumerate_common()
|
||||
@param flag Bitfiled for control purposes
|
||||
bit0= do not setup spc/sbc/mmc
|
||||
*/
|
||||
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)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* ts A60923 */
|
||||
d->bus_no = bus_no;
|
||||
d->host = host_no;
|
||||
d->id = target_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));
|
||||
d->mdata->valid = 0;
|
||||
|
||||
/* ts A61007 : obsolete Assert in drive_getcaps() */
|
||||
if(d->idata == NULL || d->mdata == NULL) {
|
||||
libdax_msgs_submit(libdax_messenger, -1, 0x00020108,
|
||||
LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH,
|
||||
"Could not allocate new drive object", 0, 0);
|
||||
return -1;
|
||||
}
|
||||
if(!(flag & 1)) {
|
||||
ret = spc_setup_drive(d);
|
||||
if (ret<=0)
|
||||
return ret;
|
||||
ret = sbc_setup_drive(d);
|
||||
if (ret<=0)
|
||||
return ret;
|
||||
ret = mmc_setup_drive(d);
|
||||
if (ret<=0)
|
||||
return ret;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -22,29 +22,4 @@ int spc_block_type(enum burn_block_types b);
|
||||
int spc_get_erase_progress(struct burn_drive *d);
|
||||
int spc_test_unit_ready(struct burn_drive *d);
|
||||
|
||||
/* ts A61021 : the spc specific part of sg.c:enumerate_common()
|
||||
*/
|
||||
int spc_setup_drive(struct burn_drive *d);
|
||||
|
||||
/* ts A61021 : the general SCSI specific part of sg.c:enumerate_common()
|
||||
@param flag Bitfield for control purposes
|
||||
bit0= do not setup spc/sbc/mmc
|
||||
*/
|
||||
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*/
|
||||
|
@ -1,7 +1,4 @@
|
||||
|
||||
/* ts A61008 */
|
||||
/* #include <a ssert.h> */
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@ -10,18 +7,10 @@
|
||||
#include "write.h"
|
||||
#include "debug.h"
|
||||
|
||||
#include "libdax_msgs.h"
|
||||
extern struct libdax_msgs *libdax_messenger;
|
||||
|
||||
|
||||
/* ts A61008 : replaced Assert by if and return 0 */
|
||||
/* a ssert(!(pos > BURN_POS_END)); */
|
||||
|
||||
#define RESIZE(TO, NEW, pos) {\
|
||||
void *tmp;\
|
||||
\
|
||||
if (pos > BURN_POS_END)\
|
||||
return 0;\
|
||||
assert(!(pos > BURN_POS_END));\
|
||||
if (pos == BURN_POS_END)\
|
||||
pos = TO->NEW##s;\
|
||||
if (pos > TO->NEW##s)\
|
||||
@ -39,7 +28,8 @@ extern struct libdax_msgs *libdax_messenger;
|
||||
struct burn_disc *burn_disc_create(void)
|
||||
{
|
||||
struct burn_disc *d;
|
||||
d = calloc(1, sizeof(struct burn_disc));
|
||||
d = malloc(sizeof(struct burn_disc));
|
||||
memset(d, 0, sizeof(struct burn_disc));
|
||||
d->refcnt = 1;
|
||||
d->sessions = 0;
|
||||
d->session = NULL;
|
||||
@ -63,7 +53,8 @@ void burn_disc_free(struct burn_disc *d)
|
||||
struct burn_session *burn_session_create(void)
|
||||
{
|
||||
struct burn_session *s;
|
||||
s = calloc(1, sizeof(struct burn_session));
|
||||
s = malloc(sizeof(struct burn_session));
|
||||
memset(s, 0, sizeof(struct burn_session));
|
||||
s->refcnt = 1;
|
||||
s->tracks = 0;
|
||||
s->track = NULL;
|
||||
@ -103,7 +94,8 @@ int burn_disc_add_session(struct burn_disc *d, struct burn_session *s,
|
||||
struct burn_track *burn_track_create(void)
|
||||
{
|
||||
struct burn_track *t;
|
||||
t = calloc(1, sizeof(struct burn_track));
|
||||
t = malloc(sizeof(struct burn_track));
|
||||
memset(t, 0, sizeof(struct burn_track));
|
||||
t->refcnt = 1;
|
||||
t->indices = 0;
|
||||
t->offset = 0;
|
||||
@ -115,23 +107,9 @@ 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;
|
||||
|
||||
/* ts A61024 */
|
||||
t->swap_source_bytes = 0;
|
||||
return t;
|
||||
}
|
||||
|
||||
@ -160,34 +138,30 @@ int burn_session_remove_track(struct burn_session *s, struct burn_track *t)
|
||||
struct burn_track **tmp;
|
||||
int i, pos = -1;
|
||||
|
||||
/* ts A61008 */
|
||||
/* a ssert(s->track != NULL); */
|
||||
if (s->track == NULL)
|
||||
return 0;
|
||||
assert(s->track != NULL);
|
||||
|
||||
burn_track_free(t);
|
||||
|
||||
/* Find the position */
|
||||
for (i = 0; i < s->tracks; i++) {
|
||||
if (t == s->track[i]) {
|
||||
if (t == s->track[i])
|
||||
pos = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (pos == -1)
|
||||
return 0;
|
||||
|
||||
/* Is it the last track? */
|
||||
if (pos != s->tracks - 1) {
|
||||
memmove(&s->track[pos], &s->track[pos + 1],
|
||||
if (pos != s->tracks) {
|
||||
memmove(s->track[pos], s->track[pos + 1],
|
||||
sizeof(struct burn_track *) * (s->tracks - (pos + 1)));
|
||||
}
|
||||
|
||||
s->tracks--;
|
||||
tmp = realloc(s->track, sizeof(struct burn_track *) * s->tracks);
|
||||
if (tmp)
|
||||
s->track = tmp;
|
||||
if (!tmp)
|
||||
return 0;
|
||||
s->track = tmp;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -218,96 +192,34 @@ void burn_structure_print_track(struct burn_track *t)
|
||||
void burn_track_define_data(struct burn_track *t, int offset, int tail,
|
||||
int pad, int mode)
|
||||
{
|
||||
int type_to_form(int mode, unsigned char *ctladr, int *form);
|
||||
int burn_sector_length(int tracktype);
|
||||
unsigned char ctladr;
|
||||
int form = -1; /* unchanged form will be considered an error too */
|
||||
|
||||
type_to_form(mode, &ctladr, &form);
|
||||
if (form == -1 || burn_sector_length(mode) <= 0) {
|
||||
char msg[160];
|
||||
|
||||
sprintf(msg, "Attempt to set track mode to unusable value %d",
|
||||
mode);
|
||||
libdax_msgs_submit(libdax_messenger, -1, 0x00020115,
|
||||
LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
|
||||
msg, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
t->offset = offset;
|
||||
t->pad = pad;
|
||||
t->mode = mode;
|
||||
t->tail = tail;
|
||||
}
|
||||
|
||||
|
||||
/* ts A61024 */
|
||||
int burn_track_set_byte_swap(struct burn_track *t, int swap_source_bytes)
|
||||
{
|
||||
if(swap_source_bytes!=0 && swap_source_bytes!=1)
|
||||
return 0;
|
||||
t->swap_source_bytes = swap_source_bytes;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
void burn_track_set_isrc(struct burn_track *t, char *country, char *owner,
|
||||
unsigned char year, unsigned int serial)
|
||||
{
|
||||
int i;
|
||||
|
||||
t->isrc.has_isrc = 1;
|
||||
for (i = 0; i < 2; ++i) {
|
||||
|
||||
/* ts A61008 : This is always true */
|
||||
/* a ssert((country[i] >= '0' || country[i] < '9') &&
|
||||
assert((country[i] >= '0' || country[i] < '9') &&
|
||||
(country[i] >= 'a' || country[i] < 'z') &&
|
||||
(country[i] >= 'A' || country[i] < 'Z')); */
|
||||
/* ts A61008 : now coordinated with sector.c: char_to_isrc() */
|
||||
if (! ((country[i] >= '0' && country[i] <= '9') ||
|
||||
(country[i] >= 'a' && country[i] <= 'z') ||
|
||||
(country[i] >= 'A' && country[i] <= 'Z') ) )
|
||||
goto is_not_allowed;
|
||||
|
||||
(country[i] >= 'A' || country[i] < 'Z'));
|
||||
t->isrc.country[i] = country[i];
|
||||
}
|
||||
for (i = 0; i < 3; ++i) {
|
||||
|
||||
/* ts A61008 : This is always true */
|
||||
/* a ssert((owner[i] >= '0' || owner[i] < '9') &&
|
||||
assert((owner[i] >= '0' || owner[i] < '9') &&
|
||||
(owner[i] >= 'a' || owner[i] < 'z') &&
|
||||
(owner[i] >= 'A' || owner[i] < 'Z')); */
|
||||
/* ts A61008 : now coordinated with sector.c: char_to_isrc() */
|
||||
if (! ((owner[i] >= '0' && owner[i] <= '9') ||
|
||||
(owner[i] >= 'a' && owner[i] <= 'z') ||
|
||||
(owner[i] >= 'A' && owner[i] <= 'Z') ) )
|
||||
goto is_not_allowed;
|
||||
|
||||
(owner[i] >= 'A' || owner[i] < 'Z'));
|
||||
t->isrc.owner[i] = owner[i];
|
||||
}
|
||||
|
||||
/* ts A61008 */
|
||||
/* a ssert(year <= 99); */
|
||||
if (year > 99)
|
||||
goto is_not_allowed;
|
||||
|
||||
assert(year <= 99);
|
||||
t->isrc.year = year;
|
||||
|
||||
/* ts A61008 */
|
||||
/* a ssert(serial <= 99999); */
|
||||
if (serial > 99999)
|
||||
goto is_not_allowed;
|
||||
|
||||
assert(serial <= 99999);
|
||||
t->isrc.serial = serial;
|
||||
|
||||
/* ts A61008 */
|
||||
t->isrc.has_isrc = 1;
|
||||
return;
|
||||
is_not_allowed:;
|
||||
libdax_msgs_submit(libdax_messenger, -1, 0x00020114,
|
||||
LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
|
||||
"Attempt to set ISRC with bad data", 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
void burn_track_clear_isrc(struct burn_track *t)
|
||||
@ -329,31 +241,6 @@ 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,18 +31,6 @@ 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;
|
||||
@ -53,10 +41,6 @@ struct burn_track
|
||||
/** The track contains a postgap */
|
||||
int postgap;
|
||||
struct isrc isrc;
|
||||
|
||||
/* ts A61024 */
|
||||
/** Byte swapping on source data stream : 0=none , 1=pairwise */
|
||||
int swap_source_bytes;
|
||||
};
|
||||
|
||||
struct burn_session
|
||||
@ -83,10 +67,4 @@ 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 */
|
||||
|
@ -1,8 +1,6 @@
|
||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||
|
||||
/* ts A61008 */
|
||||
/* #include <a ssert.h> */
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@ -53,10 +51,7 @@ static void write_clonecd2(volatile struct toc *toc, int f)
|
||||
case BURN_MODE2_FORM1:
|
||||
case BURN_MODE2_FORM2:
|
||||
case BURN_MODE_UNINITIALIZED:
|
||||
|
||||
/* ts A61008 : do this softly without Assert */
|
||||
|
||||
a ssert(0); /* unhandled! find out ccd's
|
||||
assert(0); /* unhandled! find out ccd's
|
||||
value for these modes! */
|
||||
}
|
||||
dprintf(f, "PreGapMode=%d\r\n", m);
|
||||
@ -100,8 +95,7 @@ void toc_find_modes(struct burn_drive *d)
|
||||
struct buffer mem;
|
||||
struct burn_toc_entry *e;
|
||||
|
||||
/* ts A61008 : to be prevented on the higher levels */
|
||||
/* a ssert(d->busy); */
|
||||
assert(d->busy);
|
||||
|
||||
mem.bytes = 0;
|
||||
mem.sectors = 1;
|
||||
|
@ -4,16 +4,19 @@
|
||||
#define __TRANSPORT
|
||||
|
||||
#include "libburn.h"
|
||||
#include "os.h"
|
||||
|
||||
#include <pthread.h>
|
||||
/* sg data structures */
|
||||
#include <sys/types.h>
|
||||
#include <scsi/sg.h>
|
||||
#include <scsi/scsi.h>
|
||||
|
||||
/* kludge! glibc headers don't define all the SCSI shit that we use! */
|
||||
#ifndef SG_GET_ACCESS_COUNT
|
||||
# define SG_GET_ACCESS_COUNT 0x2289
|
||||
#endif
|
||||
|
||||
/* see os.h for name of particular os-*.h where this is defined */
|
||||
#define BUFFER_SIZE BURN_OS_TRANSPORT_BUFFER_SIZE
|
||||
|
||||
#define BUFFER_SIZE 65536
|
||||
|
||||
enum transfer_direction
|
||||
{ TO_DRIVE, FROM_DRIVE, NO_TRANSFER };
|
||||
@ -52,7 +55,7 @@ struct command
|
||||
struct buffer *page;
|
||||
};
|
||||
|
||||
struct burn_scsi_inquiry_data
|
||||
struct scsi_inquiry_data
|
||||
{
|
||||
char vendor[9];
|
||||
char product[17];
|
||||
@ -75,10 +78,6 @@ struct scsi_mode_data
|
||||
int simulate;
|
||||
int max_read_speed;
|
||||
int max_write_speed;
|
||||
|
||||
/* ts A61021 */
|
||||
int min_write_speed;
|
||||
|
||||
int cur_read_speed;
|
||||
int cur_write_speed;
|
||||
int retry_page_length;
|
||||
@ -91,22 +90,15 @@ struct scsi_mode_data
|
||||
};
|
||||
|
||||
|
||||
#define LIBBURN_SG_MAX_SIBLINGS 16
|
||||
|
||||
/** Gets initialized in enumerate_common() and burn_drive_register() */
|
||||
struct burn_drive
|
||||
{
|
||||
int bus_no;
|
||||
int host;
|
||||
int id;
|
||||
int channel;
|
||||
int lun;
|
||||
char *devname;
|
||||
|
||||
|
||||
/* see os.h for name of particular os-*.h where this is defined */
|
||||
BURN_OS_TRANSPORT_DRIVE_ELEMENTS
|
||||
|
||||
int fd;
|
||||
|
||||
/* ts A60904 : ticket 62, contribution by elmom */
|
||||
/**
|
||||
@ -120,10 +112,6 @@ 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 */
|
||||
@ -140,26 +128,17 @@ struct burn_drive
|
||||
/* transport functions */
|
||||
int (*grab) (struct burn_drive *);
|
||||
int (*release) (struct burn_drive *);
|
||||
|
||||
/* ts A61021 */
|
||||
int (*drive_is_open) (struct burn_drive *);
|
||||
|
||||
int (*issue_command) (struct burn_drive *, struct command *);
|
||||
|
||||
/* lower level functions */
|
||||
void (*erase) (struct burn_drive *, int);
|
||||
void (*getcaps) (struct burn_drive *);
|
||||
|
||||
/* ts A61021 */
|
||||
void (*read_atip) (struct burn_drive *);
|
||||
|
||||
int (*write) (struct burn_drive *, int, struct buffer *);
|
||||
void (*read_toc) (struct burn_drive *);
|
||||
void (*lock) (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,
|
||||
@ -174,32 +153,17 @@ 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 trackno, int *lba, int *nwa);
|
||||
|
||||
/* ts A61009 : removed d in favor of o->drive */
|
||||
/* void (*close_disc) (struct burn_drive * d,
|
||||
struct burn_write_opts * o);
|
||||
void (*close_session) (struct burn_drive * d,
|
||||
int (*get_nwa) (struct burn_drive *);
|
||||
void (*close_disc) (struct burn_drive * d, struct burn_write_opts * o);
|
||||
void (*close_session) (struct burn_drive * d,
|
||||
struct burn_write_opts * o);
|
||||
*/
|
||||
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;
|
||||
struct burn_scsi_inquiry_data *idata;
|
||||
struct scsi_inquiry_data *idata;
|
||||
struct scsi_mode_data *mdata;
|
||||
int toc_entries;
|
||||
struct burn_toc_entry *toc_entry;
|
||||
|
||||
/* ts A61023 : get size and free space of drive buffer */
|
||||
int (*read_buffer_capacity) (struct burn_drive *d);
|
||||
|
||||
};
|
||||
|
||||
/* end of generic 'drive' data structures */
|
||||
|
@ -1,8 +1,5 @@
|
||||
#include <string.h>
|
||||
|
||||
/* ts A61008 */
|
||||
/* #include <a ssert.h> */
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include "../version.h"
|
||||
#include "util.h"
|
||||
@ -13,10 +10,7 @@ char *burn_strdup(char *s)
|
||||
char *ret;
|
||||
int l;
|
||||
|
||||
/* ts A61008 */
|
||||
/* a ssert(s); */
|
||||
if (s == NULL)
|
||||
return NULL;
|
||||
assert(s);
|
||||
|
||||
l = strlen(s) + 1;
|
||||
ret = malloc(l);
|
||||
@ -30,11 +24,8 @@ char *burn_strndup(char *s, int n)
|
||||
char *ret;
|
||||
int l;
|
||||
|
||||
/* ts A61008 */
|
||||
/* a ssert(s); */
|
||||
/* a ssert(n > 0); */
|
||||
if (s == NULL || n <= 0)
|
||||
return NULL;
|
||||
assert(s);
|
||||
assert(n > 0);
|
||||
|
||||
l = strlen(s);
|
||||
ret = malloc(l < n ? l : n);
|
||||
|
501
libburn/write.c
501
libburn/write.c
@ -2,23 +2,7 @@
|
||||
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
|
||||
/* ts A61009 */
|
||||
/* #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 <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
@ -28,6 +12,7 @@
|
||||
#include "libburn.h"
|
||||
#include "drive.h"
|
||||
#include "transport.h"
|
||||
#include "message.h"
|
||||
#include "crc.h"
|
||||
#include "debug.h"
|
||||
#include "init.h"
|
||||
@ -38,10 +23,6 @@
|
||||
#include "write.h"
|
||||
#include "options.h"
|
||||
|
||||
#include "libdax_msgs.h"
|
||||
extern struct libdax_msgs *libdax_messenger;
|
||||
|
||||
|
||||
static int type_to_ctrl(int mode)
|
||||
{
|
||||
int ctrl = 0;
|
||||
@ -56,9 +37,7 @@ static int type_to_ctrl(int mode)
|
||||
if (mode & BURN_PREEMPHASIS)
|
||||
ctrl |= 1;
|
||||
} else
|
||||
/* ts A61008 */
|
||||
/* a ssert(0); */
|
||||
return -1;
|
||||
assert(0);
|
||||
|
||||
if (mode & BURN_COPY)
|
||||
ctrl |= 2;
|
||||
@ -67,40 +46,18 @@ static int type_to_ctrl(int mode)
|
||||
}
|
||||
|
||||
/* only the ctrl nibble is set here (not adr) */
|
||||
/* ts A61009 : removed "static" , reacted on type_to_ctrl() == -1
|
||||
preserved ignorance towards unknown modes (for now) */
|
||||
void type_to_form(int mode, unsigned char *ctladr, int *form)
|
||||
static void type_to_form(int mode, unsigned char *ctladr, int *form)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = type_to_ctrl(mode) << 4;
|
||||
if (ret == -1) {
|
||||
*ctladr = 0xff;
|
||||
*form = -1;
|
||||
return;
|
||||
}
|
||||
*ctladr = ret;
|
||||
*ctladr = type_to_ctrl(mode) << 4;
|
||||
|
||||
if (mode & BURN_AUDIO)
|
||||
*form = 0;
|
||||
if (mode & BURN_MODE0) {
|
||||
|
||||
/* ts A61009 */
|
||||
/* a ssert(0); */
|
||||
*form = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (mode & BURN_MODE0)
|
||||
assert(0);
|
||||
if (mode & BURN_MODE1)
|
||||
*form = 0x10;
|
||||
if (mode & BURN_MODE2) {
|
||||
|
||||
/* ts A61009 */
|
||||
/* a ssert(0); */ /* XXX someone's gonna want this sometime */
|
||||
*form = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (mode & BURN_MODE2)
|
||||
assert(0); /* XXX someone's gonna want this sometime */
|
||||
if (mode & BURN_MODE_RAW)
|
||||
*form = 0;
|
||||
if (mode & BURN_SUBCODE_P16) /* must be expanded to R96 */
|
||||
@ -111,7 +68,7 @@ void type_to_form(int mode, unsigned char *ctladr, int *form)
|
||||
*form |= 0x40;
|
||||
}
|
||||
|
||||
int burn_write_flush(struct burn_write_opts *o, struct burn_track *track)
|
||||
int burn_write_flush(struct burn_write_opts *o)
|
||||
{
|
||||
struct burn_drive *d = o->drive;
|
||||
|
||||
@ -120,115 +77,13 @@ int burn_write_flush(struct burn_write_opts *o, struct burn_track *track)
|
||||
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.
|
||||
@ -255,9 +110,7 @@ static void print_cue(struct cue_sheet *sheet)
|
||||
#endif /* Libburn_write_with_print_cuE */
|
||||
|
||||
|
||||
/* ts A61009 : changed type from void to int */
|
||||
/** @return 1 = success , <=0 failure */
|
||||
static int add_cue(struct cue_sheet *sheet, unsigned char ctladr,
|
||||
static void add_cue(struct cue_sheet *sheet, unsigned char ctladr,
|
||||
unsigned char tno, unsigned char indx,
|
||||
unsigned char form, unsigned char scms, int lba)
|
||||
{
|
||||
@ -269,17 +122,7 @@ static int add_cue(struct cue_sheet *sheet, unsigned char ctladr,
|
||||
|
||||
sheet->count++;
|
||||
ptr = realloc(sheet->data, sheet->count * 8);
|
||||
|
||||
/* ts A61009 */
|
||||
/* a ssert(ptr); */
|
||||
if (ptr == NULL) {
|
||||
libdax_msgs_submit(libdax_messenger, -1, 0x00020111,
|
||||
LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH,
|
||||
"Could not allocate new auxiliary object (cue_sheet->data)",
|
||||
0, 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
assert(ptr);
|
||||
sheet->data = ptr;
|
||||
unit = sheet->data + (sheet->count - 1) * 8;
|
||||
unit[0] = ctladr;
|
||||
@ -290,15 +133,12 @@ static int add_cue(struct cue_sheet *sheet, unsigned char ctladr,
|
||||
unit[5] = m;
|
||||
unit[6] = s;
|
||||
unit[7] = f;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ts A61114: added parameter nwa */
|
||||
struct cue_sheet *burn_create_toc_entries(struct burn_write_opts *o,
|
||||
struct burn_session *session,
|
||||
int nwa)
|
||||
struct burn_session *session)
|
||||
{
|
||||
int i, m, s, f, form, pform, runtime = -150, ret;
|
||||
int i, m, s, f, form, pform, runtime = -150;
|
||||
unsigned char ctladr;
|
||||
struct burn_drive *d;
|
||||
struct burn_toc_entry *e;
|
||||
@ -309,64 +149,21 @@ 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()
|
||||
type_to_form() */
|
||||
if (sheet == NULL) {
|
||||
libdax_msgs_submit(libdax_messenger, -1, 0x00020111,
|
||||
LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH,
|
||||
"Could not allocate new auxiliary object (cue_sheet)",
|
||||
0, 0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sheet->data = NULL;
|
||||
sheet->count = 0;
|
||||
|
||||
type_to_form(tar[0]->mode, &ctladr, &form);
|
||||
if (form == -1) {
|
||||
libdax_msgs_submit(libdax_messenger, -1, 0x00020116,
|
||||
LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH,
|
||||
"Track mode has unusable value", 0, 0);
|
||||
goto failed;
|
||||
}
|
||||
ret = add_cue(sheet, ctladr | 1, 0, 0, 1, 0, runtime);
|
||||
if (ret <= 0)
|
||||
goto failed;
|
||||
ret = add_cue(sheet, ctladr | 1, 1, 0, form, 0, runtime);
|
||||
if (ret <= 0)
|
||||
goto failed;
|
||||
add_cue(sheet, ctladr | 1, 0, 0, 1, 0, runtime);
|
||||
add_cue(sheet, ctladr | 1, 1, 0, form, 0, runtime);
|
||||
runtime += 150;
|
||||
|
||||
burn_print(1, "toc for %d tracks:\n", ntr);
|
||||
d->toc_entries = ntr + 3;
|
||||
|
||||
/* 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));
|
||||
assert(d->toc_entry == NULL);
|
||||
d->toc_entry = malloc(d->toc_entries * sizeof(struct burn_toc_entry));
|
||||
e = d->toc_entry;
|
||||
memset((void *)e, 0, d->toc_entries * sizeof(struct burn_toc_entry));
|
||||
e[0].point = 0xA0;
|
||||
if (tar[0]->mode & BURN_AUDIO)
|
||||
e[0].control = TOC_CONTROL_AUDIO;
|
||||
@ -391,12 +188,7 @@ struct cue_sheet *burn_create_toc_entries(struct burn_write_opts *o,
|
||||
for (i = 0; i < ntr; i++) {
|
||||
type_to_form(tar[i]->mode, &ctladr, &form);
|
||||
if (pform != form) {
|
||||
|
||||
ret = add_cue(sheet, ctladr | 1, i + 1, 0, form, 0,
|
||||
runtime);
|
||||
if (ret <= 0)
|
||||
goto failed;
|
||||
|
||||
add_cue(sheet, ctladr | 1, i + 1, 0, form, 0, runtime);
|
||||
runtime += 150;
|
||||
/* XXX fix pregap interval 1 for data tracks */
|
||||
/* ts A60813 silence righteous compiler warning about C++ style comments
|
||||
@ -419,11 +211,7 @@ struct cue_sheet *burn_create_toc_entries(struct burn_write_opts *o,
|
||||
e[3 + i].control = type_to_ctrl(tar[i]->mode);
|
||||
burn_print(1, "track %d control %d\n", tar[i]->mode,
|
||||
e[3 + i].control);
|
||||
|
||||
ret = add_cue(sheet, ctladr | 1, i + 1, 1, form, 0, runtime);
|
||||
if (ret <= 0)
|
||||
goto failed;
|
||||
|
||||
add_cue(sheet, ctladr | 1, i + 1, 1, form, 0, runtime);
|
||||
runtime += burn_track_get_sectors(tar[i]);
|
||||
/* if we're padding, we'll clear any current shortage.
|
||||
if we're not, we'll slip toc entries by a sector every time our
|
||||
@ -432,17 +220,8 @@ XXX this is untested :)
|
||||
*/
|
||||
if (!tar[i]->pad) {
|
||||
rem += burn_track_get_shortage(tar[i]);
|
||||
|
||||
/* 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;
|
||||
if (i +1 != ntr)
|
||||
tar[i]->source->next = tar[i+1]->source;
|
||||
} else if (rem) {
|
||||
rem = 0;
|
||||
runtime++;
|
||||
@ -462,15 +241,8 @@ XXX this is untested :)
|
||||
burn_print(1, "point %d (%02d:%02d:%02d)\n",
|
||||
d->toc_entry[i].point, d->toc_entry[i].pmin,
|
||||
d->toc_entry[i].psec, d->toc_entry[i].pframe);
|
||||
ret = add_cue(sheet, ctladr | 1, 0xAA, 1, 1, 0, runtime);
|
||||
if (ret <= 0)
|
||||
goto failed;
|
||||
add_cue(sheet, ctladr | 1, 0xAA, 1, 1, 0, runtime);
|
||||
return sheet;
|
||||
|
||||
failed:;
|
||||
if (sheet != NULL)
|
||||
free((char *) sheet);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int burn_sector_length(int tracktype)
|
||||
@ -481,9 +253,8 @@ int burn_sector_length(int tracktype)
|
||||
return 2352;
|
||||
if (tracktype & BURN_MODE1)
|
||||
return 2048;
|
||||
/* ts A61009 */
|
||||
/* a ssert(0); */
|
||||
return -1;
|
||||
assert(0);
|
||||
return 12345;
|
||||
}
|
||||
|
||||
int burn_subcode_length(int tracktype)
|
||||
@ -556,7 +327,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, ret;
|
||||
int i;
|
||||
|
||||
d->rlba = 0;
|
||||
burn_print(1, " writing a session\n");
|
||||
@ -569,15 +340,9 @@ int burn_write_session(struct burn_write_opts *o, struct burn_session *s)
|
||||
next = NULL;
|
||||
|
||||
if (!burn_write_track(o, s, i))
|
||||
{ ret = 0; goto ex; }
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ts A61103 */
|
||||
ret = 1;
|
||||
ex:;
|
||||
if (o->write_type == BURN_WRITE_TAO)
|
||||
burn_write_close_session(o, s);
|
||||
return ret;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int burn_write_track(struct burn_write_opts *o, struct burn_session *s,
|
||||
@ -585,18 +350,13 @@ 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, open_ended = 0, ret= 0, nwa, lba;
|
||||
int i, tmp = 0;
|
||||
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)
|
||||
@ -612,36 +372,20 @@ 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))
|
||||
{ ret = 0; goto ex; }
|
||||
return 0;
|
||||
}
|
||||
if (t->pregap2)
|
||||
for (i = 0; i < 150; i++)
|
||||
if (!sector_pregap(o, t->entry->point,
|
||||
t->entry->control, t->mode))
|
||||
{ ret = 0; goto ex; }
|
||||
return 0;
|
||||
} 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;
|
||||
@ -651,47 +395,22 @@ int burn_write_track(struct burn_write_opts *o, struct burn_session *s,
|
||||
/* ts A60831: added tnum-line, extended print message on proposal
|
||||
by bonfire-app@wanadoo.fr in http://libburn.pykix.org/ticket/58 */
|
||||
d->progress.track = tnum;
|
||||
|
||||
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; open_ended || i < sectors - tmp; i++) {
|
||||
|
||||
/* ts A61023 : http://libburn.pykix.org/ticket/14
|
||||
From time to time inquire drive buffer */
|
||||
if ((i%64)==0)
|
||||
d->read_buffer_capacity(d);
|
||||
|
||||
for (i = 0; i < sectors - tmp; i++) {
|
||||
if (!sector_data(o, t, 0))
|
||||
{ ret = 0; goto ex; }
|
||||
|
||||
/* ts A61031 */
|
||||
if (open_ended) {
|
||||
d->progress.sectors = sectors = i;
|
||||
if (burn_track_is_data_done(t))
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
|
||||
/* 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 */
|
||||
if ((i%64)==0)
|
||||
d->read_buffer_capacity(d);
|
||||
|
||||
if (!sector_data(o, t, 1))
|
||||
{ ret = 0; goto ex; }
|
||||
return 0;
|
||||
|
||||
/* update progress */
|
||||
d->progress.sector++;
|
||||
@ -701,64 +420,23 @@ 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))
|
||||
{ ret = 0; goto ex; }
|
||||
return 0;
|
||||
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)
|
||||
{ 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;
|
||||
|
||||
return 0;
|
||||
d->nwa += d->buffer->sectors;
|
||||
d->buffer->bytes = 0;
|
||||
d->buffer->sectors = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
int burn_disc_write_is_ok(struct burn_write_opts *o, struct burn_disc *disc)
|
||||
{
|
||||
int i, t;
|
||||
char msg[80];
|
||||
|
||||
for (i = 0; i < disc->sessions; i++)
|
||||
for (t = 0; t < disc->session[i]->tracks; t++)
|
||||
if (!sector_headers_is_ok(
|
||||
o, disc->session[i]->track[t]->mode))
|
||||
goto bad_track_mode_found;
|
||||
if (o->write_type == BURN_WRITE_TAO)
|
||||
if (!burn_write_flush(o))
|
||||
return 0;
|
||||
return 1;
|
||||
bad_track_mode_found:;
|
||||
sprintf(msg, "Unsuitable track mode 0x%x in track %d of session %d",
|
||||
disc->session[i]->track[t]->mode, i+1, t+1);
|
||||
libdax_msgs_submit(libdax_messenger, -1, 0x0002010a,
|
||||
LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH,
|
||||
msg, 0, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void burn_disc_write_sync(struct burn_write_opts *o, struct burn_disc *disc)
|
||||
@ -767,12 +445,10 @@ 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, ret, lba, nwa = 0;
|
||||
char msg[80];
|
||||
int first = 1, i;
|
||||
int res;
|
||||
|
||||
/* ts A60924 : libburn/message.c gets obsoleted
|
||||
burn_message_clear_queue();
|
||||
*/
|
||||
|
||||
burn_print(1, "sync write of %d sessions\n", disc->sessions);
|
||||
d->buffer = &buf;
|
||||
@ -785,24 +461,15 @@ 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.
|
||||
*/
|
||||
/* 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 */
|
||||
res = d->get_nwa(d);
|
||||
/* printf("ignored nwa: %d\n", res);*/
|
||||
|
||||
d->alba = d->start_lba;
|
||||
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;
|
||||
@ -817,12 +484,6 @@ return crap. so we send the command, then ignore the result.
|
||||
d->progress.sectors = 0;
|
||||
d->progress.sector = 0;
|
||||
|
||||
/* 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;
|
||||
|
||||
for (i = 0; i < disc->sessions; i++) {
|
||||
@ -830,13 +491,7 @@ return crap. so we send the command, then ignore the result.
|
||||
d->progress.session = i;
|
||||
d->progress.tracks = disc->session[i]->tracks;
|
||||
|
||||
/* ts A61114: added parameter nwa */
|
||||
sheet = burn_create_toc_entries(o, disc->session[i], nwa);
|
||||
|
||||
/* ts A61009 */
|
||||
if (sheet == NULL)
|
||||
goto fail;
|
||||
|
||||
sheet = burn_create_toc_entries(o, disc->session[i]);
|
||||
/* print_cue(sheet);*/
|
||||
if (o->write_type == BURN_WRITE_SAO)
|
||||
d->send_cue_sheet(d, sheet);
|
||||
@ -847,39 +502,8 @@ return crap. so we send the command, then ignore the result.
|
||||
goto fail;
|
||||
} else {
|
||||
if (first) {
|
||||
|
||||
/* 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 */
|
||||
|
||||
|
||||
}
|
||||
|
||||
d->nwa = -150;
|
||||
d->alba = -150;
|
||||
} else {
|
||||
d->nwa += 4500;
|
||||
d->alba += 4500;
|
||||
@ -894,13 +518,8 @@ return crap. so we send the command, then ignore the result.
|
||||
lt->mode))
|
||||
goto fail;
|
||||
} else {
|
||||
|
||||
/* ts A61030 */
|
||||
if (o->write_type != BURN_WRITE_TAO)
|
||||
|
||||
if (!burn_write_flush(o, NULL))
|
||||
goto fail;
|
||||
|
||||
if (!burn_write_flush(o))
|
||||
goto fail;
|
||||
d->nwa += first ? 6750 : 2250;
|
||||
d->alba += first ? 6750 : 2250;
|
||||
}
|
||||
@ -912,26 +531,16 @@ return crap. so we send the command, then ignore the result.
|
||||
d->progress.start_sector = 0;
|
||||
d->progress.sectors = 0;
|
||||
}
|
||||
|
||||
/* 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))
|
||||
if (o->write_type != BURN_WRITE_SAO)
|
||||
if (!burn_write_flush(o))
|
||||
goto fail;
|
||||
|
||||
sleep(1);
|
||||
|
||||
burn_print(1, "done\n");
|
||||
d->busy = BURN_DRIVE_IDLE;
|
||||
|
||||
/* ts A61012 : This return was traditionally missing. I suspect this
|
||||
to have caused Cdrskin_eject() failures */
|
||||
return;
|
||||
|
||||
fail:
|
||||
d->sync_cache(d);
|
||||
burn_print(1, "done - failed\n");
|
||||
libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002010b,
|
||||
LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH,
|
||||
"Burn run failed", 0, 0);
|
||||
d->busy = BURN_DRIVE_IDLE;
|
||||
}
|
||||
|
@ -9,14 +9,9 @@ struct burn_write_opts;
|
||||
struct burn_disc;
|
||||
|
||||
struct cue_sheet *burn_create_toc_entries(struct burn_write_opts *o,
|
||||
struct burn_session *session,
|
||||
int nwa);
|
||||
struct burn_session *session);
|
||||
int burn_sector_length(int trackmode);
|
||||
int burn_subcode_length(int trackmode);
|
||||
|
||||
/* ts A61009 */
|
||||
int burn_disc_write_is_ok(struct burn_write_opts *o, struct burn_disc *disc);
|
||||
|
||||
void burn_disc_write_sync(struct burn_write_opts *o, struct burn_disc *disc);
|
||||
int burn_write_leadin(struct burn_write_opts *o,
|
||||
struct burn_session *s, int first);
|
||||
@ -25,11 +20,6 @@ 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, 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);
|
||||
int burn_write_flush(struct burn_write_opts *o);
|
||||
|
||||
#endif /* BURN__WRITE_H */
|
||||
|
11
libisofs-1.pc.in
Normal file
11
libisofs-1.pc.in
Normal file
@ -0,0 +1,11 @@
|
||||
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
|
4
libisofs/Makefile
Executable file
4
libisofs/Makefile
Executable file
@ -0,0 +1,4 @@
|
||||
all clean:
|
||||
$(MAKE) -C .. -$(MAKEFLAGS) $@
|
||||
|
||||
.PHONY: all clean
|
49
libisofs/Makefile.am
Executable file
49
libisofs/Makefile.am
Executable file
@ -0,0 +1,49 @@
|
||||
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
|
||||
|
||||
## ========================================================================= ##
|
694
libisofs/ecma119.c
Executable file
694
libisofs/ecma119.c
Executable file
@ -0,0 +1,694 @@
|
||||
/* -*- 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;
|
||||
}
|
267
libisofs/ecma119.h
Executable file
267
libisofs/ecma119.h
Executable file
@ -0,0 +1,267 @@
|
||||
/* -*- 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 */
|
312
libisofs/ecma119_tree.c
Normal file
312
libisofs/ecma119_tree.c
Normal file
@ -0,0 +1,312 @@
|
||||
/* 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);
|
||||
}
|
95
libisofs/ecma119_tree.h
Normal file
95
libisofs/ecma119_tree.h
Normal file
@ -0,0 +1,95 @@
|
||||
/* 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 */
|
42
libisofs/exclude.c
Normal file
42
libisofs/exclude.c
Normal file
@ -0,0 +1,42 @@
|
||||
#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);
|
||||
}
|
12
libisofs/exclude.h
Normal file
12
libisofs/exclude.h
Normal file
@ -0,0 +1,12 @@
|
||||
#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
Normal file
158
libisofs/hash.c
Normal file
@ -0,0 +1,158 @@
|
||||
#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);
|
||||
}
|
||||
}
|
||||
|
46
libisofs/hash.h
Normal file
46
libisofs/hash.h
Normal file
@ -0,0 +1,46 @@
|
||||
#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 */
|
379
libisofs/joliet.c
Normal file
379
libisofs/joliet.c
Normal file
@ -0,0 +1,379 @@
|
||||
/* -*- 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);
|
||||
}
|
||||
|
84
libisofs/joliet.h
Normal file
84
libisofs/joliet.h
Normal file
@ -0,0 +1,84 @@
|
||||
/* -*- 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 */
|
225
libisofs/libisofs.h
Executable file
225
libisofs/libisofs.h
Executable file
@ -0,0 +1,225 @@
|
||||
/* -*- 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 */
|
300
libisofs/rockridge.c
Executable file
300
libisofs/rockridge.c
Executable file
@ -0,0 +1,300 @@
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
}
|
26
libisofs/rockridge.h
Executable file
26
libisofs/rockridge.h
Executable file
@ -0,0 +1,26 @@
|
||||
/* 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
Executable file
280
libisofs/susp.c
Executable file
@ -0,0 +1,280 @@
|
||||
/* 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));
|
||||
}
|
62
libisofs/susp.h
Executable file
62
libisofs/susp.h
Executable file
@ -0,0 +1,62 @@
|
||||
/* 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
Executable file
223
libisofs/tree.c
Executable file
@ -0,0 +1,223 @@
|
||||
/* 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
Executable file
159
libisofs/tree.h
Executable file
@ -0,0 +1,159 @@
|
||||
/* 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 */
|
577
libisofs/util.c
Executable file
577
libisofs/util.c
Executable file
@ -0,0 +1,577 @@
|
||||
/* -*- 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.
|
||||
*/
|
||||
|
||||
#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;
|
||||
static int tzoffset;
|
||||
struct tm tm;
|
||||
|
||||
if (!tzsetup) {
|
||||
tzset();
|
||||
|
||||
tzoffset = -timezone / 60 / 15;
|
||||
if (tzoffset < -48)
|
||||
tzoffset += 101;
|
||||
|
||||
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;
|
||||
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();
|
||||
|
||||
tzoffset = -timezone / 60 / 15;
|
||||
if (tzoffset < -48)
|
||||
tzoffset += 101;
|
||||
|
||||
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);
|
||||
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
Executable file
121
libisofs/util.h
Executable file
@ -0,0 +1,121 @@
|
||||
/* -*- 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 */
|
189
libisofs/volume.c
Executable file
189
libisofs/volume.c
Executable file
@ -0,0 +1,189 @@
|
||||
/* -*- 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;
|
||||
}
|
45
libisofs/volume.h
Executable file
45
libisofs/volume.h
Executable file
@ -0,0 +1,45 @@
|
||||
/* -*- 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 */
|
215
test/dewav.c
215
test/dewav.c
@ -1,215 +0,0 @@
|
||||
|
||||
/* dewav
|
||||
Demo of libburn extension libdax_audioxtr
|
||||
Audio track data extraction facility of libdax and libburn.
|
||||
Copyright (C) 2006 Thomas Schmitt <scdbackup@gmx.net>, provided under GPL
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
|
||||
/* libdax_audioxtr is quite independent of libburn. It only needs
|
||||
the messaging facility libdax_msgs. So we got two build variations:
|
||||
*/
|
||||
#ifdef Dewav_without_libburN
|
||||
|
||||
/* This build environment is standalone relying only on libdax components */
|
||||
#include "../libburn/libdax_msgs.h"
|
||||
struct libdax_msgs *libdax_messenger= NULL;
|
||||
|
||||
#else /* Dewav_without_libburN */
|
||||
|
||||
/* This build environment uses libdax_msgs via libburn */
|
||||
/* Thus the API header of libburn */
|
||||
#include "../libburn/libburn.h"
|
||||
|
||||
#endif /* ! Dewav_without_libburN */
|
||||
|
||||
|
||||
/* The API for .wav extraction */
|
||||
#include "../libburn/libdax_audioxtr.h"
|
||||
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
/* This program acts as filter from in_path to out_path */
|
||||
char *in_path= "", *out_path= "-";
|
||||
|
||||
/* The read-and-extract object for use with in_path */
|
||||
struct libdax_audioxtr *xtr= NULL;
|
||||
/* The file descriptor eventually detached from xtr */
|
||||
int xtr_fd= -2;
|
||||
|
||||
/* Default output is stdout */
|
||||
int out_fd= 1;
|
||||
|
||||
/* Inquired source parameters */
|
||||
char *fmt, *fmt_info;
|
||||
int num_channels, sample_rate, bits_per_sample, msb_first;
|
||||
off_t data_size;
|
||||
|
||||
/* Auxiliary variables */
|
||||
int ret, i, be_strict= 1, buf_count, detach_fd= 0, extract_all= 0;
|
||||
char buf[2048];
|
||||
|
||||
if(argc < 2)
|
||||
goto help;
|
||||
for(i= 1; i<argc; i++) {
|
||||
if(strcmp(argv[i],"-o")==0) {
|
||||
if(i>=argc-1) {
|
||||
fprintf(stderr,"%s: option -o needs a file address as argument.\n",
|
||||
argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
i++;
|
||||
out_path= argv[i];
|
||||
} else if(strcmp(argv[i],"--lax")==0) {
|
||||
be_strict= 0;
|
||||
} else if(strcmp(argv[i],"--strict")==0) {
|
||||
be_strict= 1;
|
||||
} else if(strcmp(argv[i],"--detach_fd")==0) {
|
||||
/* Test the dirty detach method. Always --extract_all */
|
||||
detach_fd= 1;
|
||||
} else if(strcmp(argv[i],"--extract_all")==0) {
|
||||
/* Dirty : read all available bytes regardless of data_size */
|
||||
extract_all= 1;
|
||||
} else if(strcmp(argv[i],"--help")==0) {
|
||||
help:;
|
||||
fprintf(stderr,
|
||||
"usage: %s [-o output_path|\"-\"] [--lax|--strict] [source_path|\"-\"]\n",
|
||||
argv[0]);
|
||||
exit(0);
|
||||
} else {
|
||||
if(in_path[0]!=0) {
|
||||
fprintf(stderr,"%s: only one input file is allowed.\n", argv[0]);
|
||||
exit(2);
|
||||
}
|
||||
in_path= argv[i];
|
||||
}
|
||||
}
|
||||
if(in_path[0] == 0)
|
||||
in_path= "-";
|
||||
|
||||
|
||||
/* Depending on wether this was built standalone or with full libburn :
|
||||
*/
|
||||
#ifdef Dewav_without_libburN
|
||||
|
||||
/* Initialize and set up libdax messaging system */
|
||||
ret= libdax_msgs_new(&libdax_messenger,0);
|
||||
if(ret<=0) {
|
||||
fprintf(stderr,"Failed to create libdax_messenger object.\n");
|
||||
exit(3);
|
||||
}
|
||||
libdax_msgs_set_severities(libdax_messenger, LIBDAX_MSGS_SEV_NEVER,
|
||||
LIBDAX_MSGS_SEV_NOTE, "", 0);
|
||||
fprintf(stderr, "dewav on libdax\n");
|
||||
|
||||
#else /* Dewav_without_libburN */
|
||||
|
||||
/* Initialize libburn and set up its messaging system */
|
||||
if(burn_initialize() == 0) {
|
||||
fprintf(stderr,"Failed to initialize libburn.\n");
|
||||
exit(3);
|
||||
}
|
||||
/* Print messages of severity NOTE or more directly to stderr */
|
||||
burn_msgs_set_severities("NEVER", "NOTE", "");
|
||||
fprintf(stderr, "dewav on libburn\n");
|
||||
|
||||
#endif /* ! Dewav_without_libburN */
|
||||
|
||||
|
||||
/* Open audio source and create extractor object */
|
||||
ret= libdax_audioxtr_new(&xtr, in_path, 0);
|
||||
if(ret<=0)
|
||||
exit(4);
|
||||
if(strcmp(out_path,"-")!=0) {
|
||||
out_fd= open(out_path, O_WRONLY | O_CREAT | O_TRUNC,
|
||||
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
|
||||
if(out_fd == -1) {
|
||||
fprintf(stderr, "Cannot open file: %s\n", out_path);
|
||||
fprintf(stderr, "Error reported: '%s' (%d)\n",strerror(errno), errno);
|
||||
exit(5);
|
||||
}
|
||||
}
|
||||
/* Obtain and print parameters of audio source */
|
||||
libdax_audioxtr_get_id(xtr, &fmt, &fmt_info,
|
||||
&num_channels, &sample_rate, &bits_per_sample, &msb_first, 0);
|
||||
fprintf(stderr, "Detected format: %s\n", fmt_info);
|
||||
libdax_audioxtr_get_size(xtr, &data_size, 0);
|
||||
fprintf(stderr, "Data size : %.f bytes\n", (double) data_size);
|
||||
if((strcmp(fmt,".wav")!=0 && strcmp(fmt,".au")!=0) ||
|
||||
num_channels!=2 || sample_rate!=44100 || bits_per_sample!=16) {
|
||||
fprintf(stderr,
|
||||
"%sAudio source parameters do not comply to cdrskin/README specs\n",
|
||||
(be_strict ? "" : "WARNING: "));
|
||||
if(be_strict)
|
||||
exit(6);
|
||||
}
|
||||
if(msb_first==0)
|
||||
fprintf(stderr,
|
||||
"NOTE: Extracted data to be written with cdrskin option -swab\n");
|
||||
|
||||
if(detach_fd) {
|
||||
/* Take over fd from xtr */;
|
||||
ret= libdax_audioxtr_detach_fd(xtr, &xtr_fd, 0);
|
||||
if(ret<=0) {
|
||||
fprintf(stderr, "Cannot detach file descriptor from extractor\n");
|
||||
exit(8);
|
||||
}
|
||||
/* not needed any more */
|
||||
libdax_audioxtr_destroy(&xtr, 0);
|
||||
fprintf(stderr, "Note: detached fd and freed extractor object.\n");
|
||||
}
|
||||
|
||||
/* Extract and put out raw audio data */;
|
||||
while(1) {
|
||||
if(detach_fd) {
|
||||
buf_count= read(xtr_fd, buf, sizeof(buf));
|
||||
if(buf_count==-1)
|
||||
fprintf(stderr,"Error while reading from detached fd\n(%d) '%s'\n",
|
||||
errno, strerror(errno));
|
||||
} else {
|
||||
buf_count= libdax_audioxtr_read(xtr, buf, sizeof(buf), !!extract_all);
|
||||
}
|
||||
if(buf_count < 0)
|
||||
exit(7);
|
||||
if(buf_count == 0)
|
||||
break;
|
||||
|
||||
ret= write(out_fd, buf, buf_count);
|
||||
if(ret == -1) {
|
||||
fprintf(stderr, "Failed to write buffer of %d bytes to: %s\n",
|
||||
buf_count, out_path);
|
||||
fprintf(stderr, "Error reported: '%s' (%d)\n", strerror(errno), errno);
|
||||
exit(5);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Shutdown */
|
||||
if(out_fd>2)
|
||||
close(out_fd);
|
||||
/* ( It is permissible to do this with xtr==NULL ) */
|
||||
libdax_audioxtr_destroy(&xtr, 0);
|
||||
|
||||
#ifdef Dewav_without_libburN
|
||||
|
||||
libdax_msgs_destroy(&libdax_messenger,0);
|
||||
|
||||
#else /* Dewav_without_libburN */
|
||||
|
||||
burn_finish();
|
||||
|
||||
#endif /* ! Dewav_without_libburN */
|
||||
|
||||
exit(0);
|
||||
}
|
164
test/fake_au.c
164
test/fake_au.c
@ -1,164 +0,0 @@
|
||||
|
||||
/* fake_au
|
||||
Fakes a file in SUN .au format from a raw little-endian PCM audio file
|
||||
(e.g. a file extracted from .wav by test/dewav). The input data are assumed
|
||||
to be 16 bit, stereo, 44100 Hz.
|
||||
Copyright (C) 2006 Thomas Schmitt <scdbackup@gmx.net>, provided under GPL
|
||||
|
||||
Info used: http://www.opengroup.org/public/pubs/external/auformat.html
|
||||
*/
|
||||
|
||||
|
||||
#include <ctype.h>
|
||||
#include <sys/types.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
int fake_write(unsigned char *buf, size_t size, FILE *fp)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret= fwrite(buf,size,1,fp);
|
||||
if(ret==1)
|
||||
return(1);
|
||||
fprintf(stderr,"Error %d while writing: '%s'\n",errno,strerror(errno));
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int ret, i;
|
||||
unsigned data_size= 0,byte_count,exit_value= 0;
|
||||
FILE *fp_out= stdout,*fp_in= stdin;
|
||||
unsigned char buf[4];
|
||||
char out_path[4096],in_path[4096];
|
||||
struct stat stbuf;
|
||||
|
||||
strcpy(out_path,"-");
|
||||
strcpy(in_path,"");
|
||||
if(argc < 2) {
|
||||
exit_value= 1;
|
||||
goto help;
|
||||
}
|
||||
for(i= 1; i<argc; i++) {
|
||||
if(strcmp(argv[i],"-o")==0) {
|
||||
if(i>=argc-1) {
|
||||
fprintf(stderr,"%s: option -o needs a file address as argument.\n",
|
||||
argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
i++;
|
||||
strcpy(out_path, argv[i]);
|
||||
} else if(strcmp(argv[i],"--stdin_size")==0) {
|
||||
if(i>=argc-1) {
|
||||
fprintf(stderr,"%s: option --stdin_size needs a number as argument.\n",
|
||||
argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
i++;
|
||||
sscanf(argv[i],"%u",&data_size);
|
||||
} else if(strcmp(argv[i],"--help")==0) {
|
||||
exit_value= 0;
|
||||
help:;
|
||||
fprintf(stderr,"usage: %s \\\n", argv[0]);
|
||||
fprintf(stderr," [-o output_path|\"-\"] [source_path | --stdin_size size]\n");
|
||||
fprintf(stderr,
|
||||
"Disguises an extracted .wav stream as .au stereo, 16bit, 44100Hz\n");
|
||||
fprintf(stderr,
|
||||
"stdin gets byte-swapped and appended up to the announced data_size.\n");
|
||||
exit(exit_value);
|
||||
} else {
|
||||
if(in_path[0]!=0) {
|
||||
fprintf(stderr,"%s: only one input file is allowed.\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
strcpy(in_path, argv[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if(strcmp(in_path,"-")==0 || in_path[0]==0) {
|
||||
if(data_size==0) {
|
||||
fprintf(stderr,"%s: input from stdin needs option --stdin_size.\n",
|
||||
argv[0]);
|
||||
exit(6);
|
||||
}
|
||||
fp_in= stdin;
|
||||
} else {
|
||||
fp_in= fopen(in_path,"r");
|
||||
if(stat(in_path,&stbuf)!=-1)
|
||||
data_size= stbuf.st_size;
|
||||
}
|
||||
if(fp_in==NULL) {
|
||||
fprintf(stderr,"Error %d while fopen(\"%s\",\"r\") : '%s'\n",
|
||||
errno,in_path,strerror(errno));
|
||||
exit(2);
|
||||
}
|
||||
|
||||
if(strcmp(out_path,"-")==0) {
|
||||
fp_out= stdout;
|
||||
} else {
|
||||
if(stat(out_path,&stbuf)!=-1) {
|
||||
fprintf(stderr,"%s: file '%s' already existing\n",argv[0],out_path);
|
||||
exit(4);
|
||||
}
|
||||
fp_out= fopen(out_path,"w");
|
||||
}
|
||||
if(fp_out==NULL) {
|
||||
fprintf(stderr,"Error %d while fopen(\"%s\",\"w\") : '%s'\n",
|
||||
errno,out_path,strerror(errno));
|
||||
exit(2);
|
||||
}
|
||||
|
||||
fake_write((unsigned char *) ".snd",4,fp_out); /* magic number */
|
||||
buf[0]= buf[1]= buf[2]= 0;
|
||||
buf[3]= 32;
|
||||
fake_write(buf,4,fp_out); /* data_location */
|
||||
buf[0]= (data_size>>24)&0xff;
|
||||
buf[1]= (data_size>>16)&0xff;
|
||||
buf[2]= (data_size>>8)&0xff;
|
||||
buf[3]= (data_size)&0xff;
|
||||
fake_write(buf,4,fp_out); /* data_size */
|
||||
buf[0]= buf[1]= buf[2]= 0;
|
||||
buf[3]= 3;
|
||||
fake_write(buf,4,fp_out); /* encoding 16 Bit PCM */
|
||||
buf[0]= buf[1]= 0;
|
||||
buf[2]= 172;
|
||||
buf[3]= 68;
|
||||
fake_write(buf,4,fp_out); /* sample rate 44100 Hz */
|
||||
buf[0]= buf[1]= buf[2]= 0;
|
||||
buf[3]= 2;
|
||||
fake_write(buf,4,fp_out); /* number of channels */
|
||||
buf[0]= buf[1]= buf[2]= buf[3]= 0;
|
||||
fake_write(buf,4,fp_out); /* padding */
|
||||
fake_write(buf,4,fp_out); /* padding */
|
||||
|
||||
for(byte_count= 0; byte_count<data_size; byte_count+=2) {
|
||||
ret= fread(buf,2,1,fp_in);
|
||||
if(ret<=0) {
|
||||
fprintf(stderr,"Premature end end of input\n");
|
||||
exit_value= 5;
|
||||
break;
|
||||
}
|
||||
buf[3]= buf[0];
|
||||
buf[2]= buf[1];
|
||||
ret= fake_write(buf+2,2,fp_out);
|
||||
if(ret<=0) {
|
||||
exit_value= 3;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(fp_out!=stdout)
|
||||
fclose(fp_out);
|
||||
if(fp_in!=stdin)
|
||||
fclose(fp_in);
|
||||
fprintf(stderr, "Swapped and appended: %u stdin bytes\n",byte_count);
|
||||
exit(exit_value);
|
||||
}
|
||||
|
107
test/iso.c
Normal file
107
test/iso.c
Normal file
@ -0,0 +1,107 @@
|
||||
/* -*- 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
Normal file
297
test/iso.py
Normal file
@ -0,0 +1,297 @@
|
||||
|
||||
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
|
334
test/libburner.c
334
test/libburner.c
@ -1,7 +1,7 @@
|
||||
|
||||
/* 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 */
|
||||
/* 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 */
|
||||
|
||||
|
||||
/** Overview
|
||||
@ -16,8 +16,6 @@
|
||||
|
||||
Before you can do anything, you have to initialize libburn by
|
||||
burn_initialize()
|
||||
and provide some signal and abort handling, e.g. by the builtin handler, by
|
||||
burn_set_signal_handling()
|
||||
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:
|
||||
@ -35,10 +33,17 @@
|
||||
|
||||
*/
|
||||
|
||||
/* 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 */
|
||||
/* For using the installed header file : #include <libburn/libburn.h> */
|
||||
/* This program insists in the own headerfile. */
|
||||
#include "../libburn/libburn.h"
|
||||
#include <libburn/libburn.h>
|
||||
|
||||
/* libburn is intended for Linux systems with kernel 2.4 or 2.6 for now */
|
||||
#include <stdio.h>
|
||||
@ -49,8 +54,6 @@
|
||||
#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.
|
||||
@ -71,6 +74,13 @@ static unsigned int drive_count;
|
||||
static int drive_is_grabbed = 0;
|
||||
|
||||
|
||||
/** Here you may enable simulated burn by default. This does not apply to
|
||||
blanking. Anyway, some CD recorders obey the request to simulate, some do
|
||||
not. Explicit options are: --burn_for_real and --try_to_simulate
|
||||
*/
|
||||
static int simulate_burn = 0;
|
||||
|
||||
|
||||
/* Some in-advance definitions to allow a more comprehensive ordering
|
||||
of the functions and their explanations in here */
|
||||
int libburner_aquire_by_adr(char *drive_adr);
|
||||
@ -115,6 +125,9 @@ 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;
|
||||
@ -174,6 +187,7 @@ int libburner_aquire_by_driveno(int *driveno)
|
||||
}
|
||||
printf("-----------------------------------------------------------------------------\n\n");
|
||||
|
||||
|
||||
/*
|
||||
On multi-drive systems save yourself from sysadmins' revenge.
|
||||
|
||||
@ -234,16 +248,17 @@ int libburner_aquire_by_driveno(int *driveno)
|
||||
|
||||
To our knowledge it is hardly possible to abort an ongoing blank operation
|
||||
because after start it is entirely handled by the drive.
|
||||
So expect signal handling to wait the normal blanking timespan until it
|
||||
can allow the process to end. External kill -9 will not help the drive.
|
||||
So expect a blank run to survive the end of the blanking process and be
|
||||
patient for the usual timespan of a normal blank run. Only after that
|
||||
time has surely elapsed, only then you should start any rescue attempts
|
||||
with the drive. If necessary at all.
|
||||
*/
|
||||
int libburner_blank_disc(struct burn_drive *drive, int blank_fast)
|
||||
{
|
||||
enum burn_disc_status disc_state;
|
||||
struct burn_progress p;
|
||||
int percent = 1;
|
||||
struct burn_progress progress;
|
||||
|
||||
while (burn_drive_get_status(drive, NULL) != BURN_DRIVE_IDLE)
|
||||
while (burn_drive_get_status(drive, NULL))
|
||||
usleep(1001);
|
||||
|
||||
while ((disc_state = burn_disc_get_status(drive)) == BURN_DISC_UNREADY)
|
||||
@ -273,13 +288,11 @@ 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);
|
||||
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);
|
||||
while (burn_drive_get_status(drive, &progress)) {
|
||||
printf("Blanking sector %d\n", progress.sector);
|
||||
sleep(1);
|
||||
}
|
||||
printf("Done\n");
|
||||
@ -308,109 +321,109 @@ int libburner_regrab(struct burn_drive *drive) {
|
||||
}
|
||||
|
||||
|
||||
/** Brings preformatted track images (ISO 9660, audio, ...) onto media.
|
||||
To make sure a data image is fully readable on any Linux machine, this
|
||||
function adds 300 kB of padding to the (usualy single) track.
|
||||
Audio tracks get padded to complete their last sector.
|
||||
/** Brings the preformatted image (ISO 9660, afio, ext2, whatever) onto media.
|
||||
|
||||
In case of external signals expect abort handling of an ongoing burn to
|
||||
last up to a minute. Wait the normal burning timespan before any kill -9.
|
||||
To make sure your image is fully readable on any Linux machine, this
|
||||
function adds 300 kB of padding to the track.
|
||||
|
||||
Without a signal handler it is quite dangerous to abort the process
|
||||
while this function is active. See cdrskin/cdrskin.c and its usage
|
||||
of cdrskin/cleanup.[ch] for an example of application provided
|
||||
abort handling. It must cope with 2 of 3 threads reporting for
|
||||
being handled.
|
||||
|
||||
Without signal handler have ready a command line
|
||||
cdrecord dev=... -reset
|
||||
with a dev= previously inquired by cdrecord [dev=ATA] -scanbus
|
||||
in order to get your drive out of shock state after raw abort.
|
||||
Thanks to Joerg Schilling for helping out unquestioned. :)
|
||||
|
||||
In general, libburn is less prone to system problems than cdrecord,
|
||||
i believe. But cdrecord had long years of time to complete itself.
|
||||
We are still practicing. Help us with that. :))
|
||||
*/
|
||||
int libburner_payload(struct burn_drive *drive,
|
||||
char source_adr[][4096], int source_adr_count,
|
||||
int multi, int simulate_burn, int all_tracks_type)
|
||||
int libburner_payload(struct burn_drive *drive, const char *source_adr,
|
||||
off_t size)
|
||||
{
|
||||
struct burn_source *data_src;
|
||||
struct burn_disc *target_disc;
|
||||
struct burn_session *session;
|
||||
struct burn_write_opts *burn_options;
|
||||
enum burn_disc_status disc_state;
|
||||
struct burn_track *track, *tracklist[99];
|
||||
struct burn_track *track;
|
||||
struct burn_progress progress;
|
||||
time_t start_time;
|
||||
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;
|
||||
/* a padding of 300 kB helps to avoid the read-ahead bug */
|
||||
padding = 300*1024;
|
||||
}
|
||||
int last_sector = 0;
|
||||
|
||||
target_disc = burn_disc_create();
|
||||
session = burn_session_create();
|
||||
burn_disc_add_session(target_disc, session, BURN_POS_END);
|
||||
track = burn_track_create();
|
||||
|
||||
for (trackno = 0 ; trackno < source_adr_count; trackno++) {
|
||||
tracklist[trackno] = track = burn_track_create();
|
||||
burn_track_define_data(track, 0, padding, 1, all_tracks_type);
|
||||
/* a padding of 300 kB is helpful to avoid the read-ahead bug */
|
||||
burn_track_define_data(track, 0, 300*1024, 1, BURN_MODE1);
|
||||
|
||||
adr = source_adr[trackno];
|
||||
fixed_size = 0;
|
||||
if (adr[0] == '-' && adr[1] == 0) {
|
||||
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) {
|
||||
if (source_adr[0] == '-' && source_adr[1] == 0) {
|
||||
data_src = burn_fd_source_new(0, -1, size);
|
||||
printf("Note: using standard input as source with %.f bytes\n",
|
||||
(double) size);
|
||||
} else
|
||||
data_src = burn_file_source_new(source_adr, NULL);
|
||||
if (data_src == NULL) {
|
||||
fprintf(stderr,
|
||||
"FATAL: Could not open data source '%s'.\n",adr);
|
||||
"FATAL: Could not open data source '%s'.\n",source_adr);
|
||||
if(errno!=0)
|
||||
fprintf(stderr,"(Most recent system error: %s )\n",
|
||||
strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
if (burn_track_set_source(track, data_src) != BURN_SOURCE_OK) {
|
||||
}
|
||||
|
||||
if (burn_track_set_source(track, data_src) != BURN_SOURCE_OK) {
|
||||
printf("FATAL: Cannot attach source object to track object\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
burn_session_add_track(session, track, BURN_POS_END);
|
||||
burn_source_free(data_src);
|
||||
|
||||
burn_session_add_track(session, track, BURN_POS_END);
|
||||
printf("Track %d : source is '%s'\n", trackno, adr);
|
||||
burn_source_free(data_src);
|
||||
} /* trackno loop end */
|
||||
|
||||
while (burn_drive_get_status(drive, NULL) != BURN_DRIVE_IDLE)
|
||||
usleep(100001);
|
||||
while (burn_drive_get_status(drive, NULL))
|
||||
usleep(1001);
|
||||
|
||||
/* Evaluate drive and media */
|
||||
while ((disc_state = burn_disc_get_status(drive)) == BURN_DISC_UNREADY)
|
||||
usleep(100001);
|
||||
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");
|
||||
usleep(1001);
|
||||
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 (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 state of drive and media\n");
|
||||
"FATAL: Cannot recognize drive and media state\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
burn_options = burn_write_opts_new(drive);
|
||||
burn_write_opts_set_perform_opc(burn_options, 0);
|
||||
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);
|
||||
|
||||
#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
|
||||
if(simulate_burn)
|
||||
printf("\n*** Will TRY to SIMULATE burning ***\n\n");
|
||||
burn_write_opts_set_simulate(burn_options, simulate_burn);
|
||||
@ -425,62 +438,49 @@ int libburner_payload(struct burn_drive *drive,
|
||||
burn_write_opts_free(burn_options);
|
||||
while (burn_drive_get_status(drive, NULL) == BURN_DRIVE_SPAWNING)
|
||||
usleep(1002);
|
||||
while (burn_drive_get_status(drive, &progress) != BURN_DRIVE_IDLE) {
|
||||
while (burn_drive_get_status(drive, &progress)) {
|
||||
if( progress.sectors <= 0 || progress.sector == last_sector)
|
||||
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+1,
|
||||
printf("Burning sector %d of %d\n",
|
||||
progress.sector, progress.sectors);
|
||||
last_sector = progress.sector;
|
||||
sleep(1);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
for (trackno = 0 ; trackno < source_adr_count; trackno++)
|
||||
burn_track_free(tracklist[trackno]);
|
||||
burn_track_free(track);
|
||||
burn_session_free(session);
|
||||
burn_disc_free(target_disc);
|
||||
if (multi)
|
||||
printf("NOTE: Media left appendable.\n");
|
||||
if (simulate_burn)
|
||||
if(simulate_burn)
|
||||
printf("\n*** Did TRY to SIMULATE burning ***\n\n");
|
||||
return 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 int do_multi = 0;
|
||||
static int simulate_burn = 0;
|
||||
static int all_tracks_type = BURN_MODE1;
|
||||
|
||||
|
||||
/** Converts command line arguments into above setup parameters.
|
||||
/** Converts command line arguments into a few program parameters.
|
||||
drive_adr[] must provide at least BURN_DRIVE_ADR_LEN bytes.
|
||||
source_adr[] must provide at least 4096 bytes.
|
||||
*/
|
||||
int libburner_setup(int argc, char **argv)
|
||||
int libburner_setup(int argc, char **argv, char drive_adr[], int *driveno,
|
||||
int *do_blank, char source_adr[], off_t *size)
|
||||
{
|
||||
int i, insuffient_parameters = 0, print_help = 0;
|
||||
int i, insuffient_parameters = 0;
|
||||
int print_help = 0;
|
||||
|
||||
drive_adr[0] = 0;
|
||||
*driveno = 0;
|
||||
*do_blank = 0;
|
||||
source_adr[0] = 0;
|
||||
*size = 650*1024*1024;
|
||||
|
||||
for (i = 1; i < argc; ++i) {
|
||||
if (!strcmp(argv[i], "--audio")) {
|
||||
all_tracks_type = BURN_AUDIO;
|
||||
|
||||
} else if (!strcmp(argv[i], "--blank_fast")) {
|
||||
do_blank = 1;
|
||||
if (!strcmp(argv[i], "--blank_fast")) {
|
||||
*do_blank = 1;
|
||||
|
||||
} else if (!strcmp(argv[i], "--blank_full")) {
|
||||
do_blank = 2;
|
||||
*do_blank = 2;
|
||||
|
||||
} else if (!strcmp(argv[i], "--burn_for_real")) {
|
||||
simulate_burn = 0;
|
||||
@ -492,10 +492,10 @@ int libburner_setup(int argc, char **argv)
|
||||
return 1;
|
||||
} else if (strcmp(argv[i], "-") == 0) {
|
||||
drive_adr[0] = 0;
|
||||
driveno = -1;
|
||||
*driveno = -1;
|
||||
} else if (isdigit(argv[i][0])) {
|
||||
drive_adr[0] = 0;
|
||||
driveno = atoi(argv[i]);
|
||||
*driveno = atoi(argv[i]);
|
||||
} else {
|
||||
if(strlen(argv[i]) >= BURN_DRIVE_ADR_LEN) {
|
||||
fprintf(stderr,"--drive address too long (max. %d)\n",
|
||||
@ -504,63 +504,64 @@ int libburner_setup(int argc, char **argv)
|
||||
}
|
||||
strcpy(drive_adr, argv[i]);
|
||||
}
|
||||
} else if (!strcmp(argv[i], "--multi")) {
|
||||
do_multi = 1;
|
||||
|
||||
} else if (!strcmp(argv[i], "--stdin_size")) { /* obsoleted */
|
||||
i++;
|
||||
|
||||
} else if (!strcmp(argv[i], "--stdin_size")) {
|
||||
++i;
|
||||
if (i >= argc) {
|
||||
fprintf(stderr,"--stdin_size requires an argument\n");
|
||||
return 3;
|
||||
} else
|
||||
*size = atoi(argv[i]);
|
||||
if (*size < 600*1024) /* seems to be minimum readable track size */
|
||||
*size = 600*1024;
|
||||
} else if (!strcmp(argv[i], "--try_to_simulate")) {
|
||||
simulate_burn = 1;
|
||||
|
||||
} else if (!strcmp(argv[i], "--verbose")) {
|
||||
++i;
|
||||
if (i >= argc) {
|
||||
fprintf(stderr,"--verbose requires an argument\n");
|
||||
return 4;
|
||||
} else
|
||||
burn_set_verbosity(atoi(argv[i]));
|
||||
} else if (!strcmp(argv[i], "--help")) {
|
||||
print_help = 1;
|
||||
|
||||
} else if (!strncmp(argv[i], "--",2)) {
|
||||
fprintf(stderr, "Unidentified option: %s\n", argv[i]);
|
||||
return 7;
|
||||
} else {
|
||||
if(strlen(argv[i]) >= 4096) {
|
||||
fprintf(stderr, "Source address too long (max. %d)\n", 4096-1);
|
||||
return 5;
|
||||
}
|
||||
if(source_adr_count >= 99) {
|
||||
fprintf(stderr, "Too many tracks (max. 99)\n");
|
||||
return 6;
|
||||
}
|
||||
strcpy(source_adr[source_adr_count], argv[i]);
|
||||
source_adr_count++;
|
||||
strcpy(source_adr, argv[i]);
|
||||
}
|
||||
}
|
||||
insuffient_parameters = 1;
|
||||
if (driveno < 0)
|
||||
if (*driveno < 0)
|
||||
insuffient_parameters = 0;
|
||||
if (source_adr_count > 0)
|
||||
if (source_adr[0] != 0)
|
||||
insuffient_parameters = 0;
|
||||
if (do_blank)
|
||||
if (*do_blank)
|
||||
insuffient_parameters = 0;
|
||||
if (print_help || insuffient_parameters ) {
|
||||
printf("Usage: %s\n", argv[0]);
|
||||
printf(" [--drive <address>|<driveno>|\"-\"] [--audio]\n");
|
||||
printf(" [--blank_fast|--blank_full] [--try_to_simulate]\n");
|
||||
printf(" [--multi] [<one or more imagefiles>|\"-\"]\n");
|
||||
printf(" [--drive <address>|<driveno>|\"-\"]\n");
|
||||
printf(" [--verbose <level>] [--blank_fast|--blank_full]\n");
|
||||
printf(" [--burn_for_real|--try_to_simulate] [--stdin_size <bytes>]\n");
|
||||
printf(" [<imagefile>|\"-\"]\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, 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("Burn a file to drive chosen by number:\n");
|
||||
printf(" %s --drive 0 --burn_for_real my_image_file\n",
|
||||
argv[0]);
|
||||
printf("Burn a file to drive chosen by persistent address:\n");
|
||||
printf(" %s --drive /dev/hdc --burn_for_real 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]);
|
||||
printf("Burn two audio tracks\n");
|
||||
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:\n");
|
||||
printf(" %s --drive 0 --blank_fast\n",argv[0]);
|
||||
printf("Burn a compressed afio archive on-the-fly, pad up to 700 MB:\n");
|
||||
printf(" ( cd my_directory ; find . -print | afio -oZ - ) | \\\n");
|
||||
printf(" %s --drive /dev/hdc -\n", argv[0]);
|
||||
printf("To be read from *not mounted* CD via: afio -tvZ /dev/hdc\n");
|
||||
printf(" %s --drive /dev/hdc --burn_for_real --stdin_size 734003200 -\n", argv[0]);
|
||||
printf("To be read from *not mounted* CD via:\n");
|
||||
printf(" afio -tvZ /dev/hdc\n");
|
||||
printf("Program tar would need a clean EOF which our padded CD cannot deliver.\n");
|
||||
if (insuffient_parameters)
|
||||
return 6;
|
||||
@ -571,28 +572,24 @@ int libburner_setup(int argc, char **argv)
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int ret;
|
||||
int driveno, ret, do_blank;
|
||||
char source_adr[4096], drive_adr[BURN_DRIVE_ADR_LEN];
|
||||
off_t stdin_size;
|
||||
|
||||
ret = libburner_setup(argc, argv);
|
||||
ret = libburner_setup(argc, argv, drive_adr, &driveno, &do_blank,
|
||||
source_adr, &stdin_size);
|
||||
if (ret)
|
||||
exit(ret);
|
||||
|
||||
printf("Initializing libburn.pykix.org ...\n");
|
||||
printf("Initializing library ...\n");
|
||||
if (burn_initialize())
|
||||
printf("Done\n");
|
||||
else {
|
||||
printf("FAILED\n");
|
||||
fprintf(stderr,"\nFATAL: Failed to initialize.\n");
|
||||
fprintf(stderr,"\nFATAL: Failed to initialize libburn.\n");
|
||||
exit(33);
|
||||
}
|
||||
|
||||
/* Print messages of severity SORRY or more directly to stderr */
|
||||
burn_msgs_set_severities("NEVER", "SORRY", "libburner : ");
|
||||
|
||||
/* Activate the default signal handler which eventually will try to
|
||||
properly shutdown drive and library on aborting events. */
|
||||
burn_set_signal_handling("libburner : ", NULL, 0);
|
||||
|
||||
/** Note: driveno might change its value in this call */
|
||||
ret = libburner_aquire_drive(drive_adr, &driveno);
|
||||
if (ret<=0) {
|
||||
@ -606,7 +603,7 @@ int main(int argc, char **argv)
|
||||
do_blank == 1);
|
||||
if (ret<=0)
|
||||
{ ret = 36; goto release_drive; }
|
||||
if (ret != 2 && source_adr_count > 0)
|
||||
if (ret != 2 && source_adr[0] != 0)
|
||||
ret = libburner_regrab(drive_list[driveno].drive);
|
||||
if (ret<=0) {
|
||||
fprintf(stderr,
|
||||
@ -614,10 +611,9 @@ int main(int argc, char **argv)
|
||||
{ ret = 37; goto finish_libburn; }
|
||||
}
|
||||
}
|
||||
if (source_adr_count > 0) {
|
||||
ret = libburner_payload(drive_list[driveno].drive,
|
||||
source_adr, source_adr_count,
|
||||
do_multi, simulate_burn, all_tracks_type);
|
||||
if (source_adr[0] != 0) {
|
||||
ret = libburner_payload(drive_list[driveno].drive, source_adr,
|
||||
stdin_size);
|
||||
if (ret<=0)
|
||||
{ ret = 38; goto release_drive; }
|
||||
}
|
||||
@ -632,7 +628,7 @@ finish_libburn:;
|
||||
/* burn_drive_info_free(drive_list); */
|
||||
|
||||
burn_finish();
|
||||
exit(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
574
test/telltoc.c
574
test/telltoc.c
@ -1,574 +0,0 @@
|
||||
|
||||
/* 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
Normal file
103
test/toc.c
Normal file
@ -0,0 +1,103 @@
|
||||
/* -*- 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
Normal file
77
test/tree.py
Normal file
@ -0,0 +1,77 @@
|
||||
# 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()
|
Reference in New Issue
Block a user