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 |
@ -1,3 +1,2 @@
|
|||||||
Joe Neeman
|
Joe Neeman
|
||||||
Philippe Rouquier
|
Philippe Rouquier
|
||||||
Jean-Francois Wauthy
|
|
||||||
|
87
Makefile.am
87
Makefile.am
@ -1,20 +1,17 @@
|
|||||||
pkgconfigdir=$(libdir)/pkgconfig
|
pkgconfigdir=$(libdir)/pkgconfig
|
||||||
libincludedir=$(includedir)/libburn
|
libincludedir=$(includedir)/libburn
|
||||||
|
|
||||||
lib_LTLIBRARIES = libburn/libburn.la
|
lib_LTLIBRARIES = libburn/libburn.la libisofs/libisofs.la
|
||||||
|
|
||||||
## ========================================================================= ##
|
## ========================================================================= ##
|
||||||
|
|
||||||
# Build libraries
|
# Build libraries
|
||||||
libburn_libburn_la_LDFLAGS = \
|
libburn_libburn_la_LDFLAGS = \
|
||||||
-version-info $(LT_VERSION_INFO)
|
-version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE)
|
||||||
libburn_libburn_la_LIBADD = $(LIBBURN_ARCH_LIBS) $(THREAD_LIBS)
|
|
||||||
libburn_libburn_la_SOURCES = \
|
libburn_libburn_la_SOURCES = \
|
||||||
libburn/async.c \
|
libburn/async.c \
|
||||||
libburn/async.h \
|
libburn/async.h \
|
||||||
libburn/back_hacks.h \
|
libburn/back_hacks.h \
|
||||||
libburn/cleanup.c \
|
|
||||||
libburn/cleanup.h \
|
|
||||||
libburn/crc.c \
|
libburn/crc.c \
|
||||||
libburn/crc.h \
|
libburn/crc.h \
|
||||||
libburn/debug.c \
|
libburn/debug.c \
|
||||||
@ -29,17 +26,14 @@ libburn_libburn_la_SOURCES = \
|
|||||||
libburn/lec.c \
|
libburn/lec.c \
|
||||||
libburn/lec.h \
|
libburn/lec.h \
|
||||||
libburn/libburn.h \
|
libburn/libburn.h \
|
||||||
libburn/libdax_audioxtr.h \
|
libburn/message.c \
|
||||||
libburn/libdax_audioxtr.c \
|
libburn/message.h \
|
||||||
libburn/libdax_msgs.h \
|
|
||||||
libburn/libdax_msgs.c \
|
|
||||||
libburn/mmc.c \
|
libburn/mmc.c \
|
||||||
libburn/mmc.h \
|
libburn/mmc.h \
|
||||||
libburn/null.c \
|
libburn/null.c \
|
||||||
libburn/null.h \
|
libburn/null.h \
|
||||||
libburn/options.c \
|
libburn/options.c \
|
||||||
libburn/options.h \
|
libburn/options.h \
|
||||||
libburn/os.h \
|
|
||||||
libburn/read.c \
|
libburn/read.c \
|
||||||
libburn/read.h \
|
libburn/read.h \
|
||||||
libburn/sbc.c \
|
libburn/sbc.c \
|
||||||
@ -63,20 +57,42 @@ libburn_libburn_la_SOURCES = \
|
|||||||
libburn/write.h \
|
libburn/write.h \
|
||||||
version.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 = \
|
libinclude_HEADERS = \
|
||||||
libburn/libburn.h
|
libburn/libburn.h \
|
||||||
|
libisofs/libisofs.h
|
||||||
|
|
||||||
## ========================================================================= ##
|
## ========================================================================= ##
|
||||||
|
|
||||||
## Build test applications
|
## Build test applications
|
||||||
noinst_PROGRAMS = \
|
noinst_PROGRAMS = \
|
||||||
test/libburner \
|
test/libburner \
|
||||||
test/telltoc \
|
test/iso \
|
||||||
test/dewav \
|
|
||||||
test/fake_au \
|
|
||||||
test/poll \
|
test/poll \
|
||||||
|
test/toc \
|
||||||
test/structest
|
test/structest
|
||||||
|
|
||||||
bin_PROGRAMS = \
|
bin_PROGRAMS = \
|
||||||
@ -85,27 +101,24 @@ bin_PROGRAMS = \
|
|||||||
test_libburner_CPPFLAGS = -Ilibburn
|
test_libburner_CPPFLAGS = -Ilibburn
|
||||||
test_libburner_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS)
|
test_libburner_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS)
|
||||||
test_libburner_SOURCES = test/libburner.c
|
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_CPPFLAGS = -Ilibburn
|
||||||
test_poll_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS)
|
test_poll_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS)
|
||||||
test_poll_SOURCES = test/poll.c
|
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_CPPFLAGS = -Ilibburn
|
||||||
test_structest_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS)
|
test_structest_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS)
|
||||||
test_structest_SOURCES = test/structest.c
|
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 construction site - ts A60816
|
||||||
cdrskin_cdrskin_CPPFLAGS = -Ilibburn
|
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_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"
|
## Open questions: how to compute $timestamp and express -DX="$timestamp"
|
||||||
##
|
##
|
||||||
@ -145,9 +158,13 @@ uninstall-local:
|
|||||||
|
|
||||||
# Indent source files
|
# Indent source files
|
||||||
indent_files = \
|
indent_files = \
|
||||||
|
$(libisofs_libisofs_la_SOURCES) \
|
||||||
$(libburn_libburn_la_SOURCES) \
|
$(libburn_libburn_la_SOURCES) \
|
||||||
|
$(test_libburner_SOURCES) \
|
||||||
$(test_poll_SOURCES) \
|
$(test_poll_SOURCES) \
|
||||||
$(test_structest_SOURCES)
|
$(test_toc_SOURCES) \
|
||||||
|
$(test_structest_SOURCES) \
|
||||||
|
$(test_iso_SOURCES)
|
||||||
|
|
||||||
|
|
||||||
indent: $(indent_files)
|
indent: $(indent_files)
|
||||||
@ -163,13 +180,12 @@ indent: $(indent_files)
|
|||||||
|
|
||||||
# Extra things
|
# Extra things
|
||||||
nodist_pkgconfig_DATA = \
|
nodist_pkgconfig_DATA = \
|
||||||
libburn-1.pc
|
libburn-1.pc \
|
||||||
|
libisofs-1.pc
|
||||||
# http://www.nada.kth.se/cgi-bin/info?(automake.info)Man%20pages
|
|
||||||
man_MANS = cdrskin/cdrskin.1
|
|
||||||
|
|
||||||
EXTRA_DIST = \
|
EXTRA_DIST = \
|
||||||
libburn-1.pc.in \
|
libburn-1.pc.in \
|
||||||
|
libisofs-1.pc.in \
|
||||||
version.h.in \
|
version.h.in \
|
||||||
doc/comments \
|
doc/comments \
|
||||||
doc/doxygen.conf.in \
|
doc/doxygen.conf.in \
|
||||||
@ -183,12 +199,5 @@ EXTRA_DIST = \
|
|||||||
cdrskin/changelog.txt \
|
cdrskin/changelog.txt \
|
||||||
cdrskin/cdrskin_eng.html \
|
cdrskin/cdrskin_eng.html \
|
||||||
cdrskin/wiki_plain.txt \
|
cdrskin/wiki_plain.txt \
|
||||||
cdrskin/cleanup.h \
|
COPYING
|
||||||
cdrskin/cleanup.c \
|
|
||||||
libburn/os-freebsd.h \
|
|
||||||
libburn/os-linux.h \
|
|
||||||
libburn/sg-freebsd.c \
|
|
||||||
libburn/sg-linux.c \
|
|
||||||
COPYING \
|
|
||||||
$(man_MANS)
|
|
||||||
|
|
||||||
|
43
README
43
README
@ -24,47 +24,34 @@ This libburn.pykix.org toplevel README (C) 2006 Thomas Schmitt
|
|||||||
|
|
||||||
Build and Installation
|
Build and Installation
|
||||||
|
|
||||||
Our build system is based on autotools. For preparing the build of a SVN
|
Our build system is based on autotools.
|
||||||
snapshot you will need autotools of at least version 1.7.
|
User experience tells us that you will need at least autotools 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.
|
|
||||||
|
|
||||||
To build libburn.pykix.org and its subprojects it should be sufficient to go
|
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
|
./configure
|
||||||
make
|
make
|
||||||
|
|
||||||
To make the libraries accessible for running resp. developing applications
|
To make the libraries accessible for running resp. developing applications
|
||||||
make install
|
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
|
Overview of libburn.pykix.org
|
||||||
|
|
||||||
libburn.pykix.org is an open-source software project for reading, mastering and
|
libburn.pykix.org is an open-source library for reading, mastering and writing
|
||||||
writing optical discs. For now this means only CD-R and CD-RW.
|
optical discs. For now this means only CD-R and CD-RW.
|
||||||
|
|
||||||
The project comprises of several more or less interdependent parts which
|
The project comprises of several more or less interdependent parts which
|
||||||
together strive to be a usable foundation for application development.
|
together strive to be a usable foundation for application development.
|
||||||
These are libraries, language bindings, and middleware binaries which emulate
|
These are libraries, language bindings, and middleware binaries which emulate
|
||||||
classical (and valuable) Linux tools.
|
classical (and valuable) Linux tools.
|
||||||
|
|
||||||
Our scope is currently Linux 2.4 and 2.6 only. For ports to other systems
|
Our scope is currently Linux 2.4 and 2.6 and we will have a hard time to widen
|
||||||
we would need : login on a development machine resp. a live OS on CD or DVD,
|
this for now, because of our history. The project could need advise from or
|
||||||
advise from a system person about the equivalent of Linux sg or FreeBSD CAM,
|
membership of skilled kernel people and people who know how to talk CD/DVD
|
||||||
volunteers for testing of realistic use cases.
|
drives into doing things.
|
||||||
|
|
||||||
We do have a workable code base for burning data CDs, though. The burn API is
|
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
|
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 .
|
- 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
|
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],
|
AC_DEFUN([TARGET_SHIZZLE],
|
||||||
[
|
[
|
||||||
ARCH=""
|
ARCH=""
|
||||||
@ -7,11 +22,6 @@ AC_DEFUN([TARGET_SHIZZLE],
|
|||||||
case $target in
|
case $target in
|
||||||
*-*-linux*)
|
*-*-linux*)
|
||||||
ARCH=linux
|
ARCH=linux
|
||||||
LIBBURN_ARCH_LIBS=
|
|
||||||
;;
|
|
||||||
*-*-freebsd*)
|
|
||||||
ARCH=freebsd
|
|
||||||
LIBBURN_ARCH_LIBS=-lcam
|
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
AC_ERROR([You are attempting to compile for an unsupported platform])
|
AC_ERROR([You are attempting to compile for an unsupported platform])
|
||||||
@ -19,4 +29,9 @@ AC_DEFUN([TARGET_SHIZZLE],
|
|||||||
esac
|
esac
|
||||||
|
|
||||||
AC_MSG_RESULT([$ARCH])
|
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
|
aclocal
|
||||||
libtoolize --copy --force
|
libtoolize --copy --force
|
||||||
autoconf
|
autoconf
|
||||||
|
|
||||||
# ts A61101 : libburn is not prepared for config.h
|
|
||||||
# autoheader
|
|
||||||
|
|
||||||
automake --foreign --add-missing --copy --include-deps
|
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>
|
cdrskin. By Thomas Schmitt <scdbackup@gmx.net>
|
||||||
Integrated sub project of libburn.pykix.org but also published via:
|
Integrated sub project of libburn.pykix.org but also published via:
|
||||||
http://scdbackup.sourceforge.net/cdrskin_eng.html
|
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
|
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
|
compatibility wrapper which allows to use some libburn features from
|
||||||
the command line.
|
the command line.
|
||||||
Interested users of cdrecord are invited to participate in the development
|
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.
|
Don't blame me or other authors of libburn if anything goes wrong.
|
||||||
|
|
||||||
I used it on my own risk with :
|
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 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, 2004
|
SuSE 9.0, kernel 2.4.21, ide-scsi emulation, LG GSA-4082B CD/DVD burner
|
||||||
NEC ND-4570A CD/DVD burner, 2006
|
RIP-14.4, kernel 2.6.14, no ide-scsi, with both above burners
|
||||||
RIP-14.4, kernel 2.6.14, no ide-scsi, with all above burners
|
|
||||||
|
|
||||||
It fails to compile or run on SuSE 6.4 (kernel 2.2.14).
|
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.
|
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
|
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
|
tar xzf cdrskin-0.2.2.tar.gz
|
||||||
cd cdrskin-0.2.6
|
cd cdrskin-0.2.2
|
||||||
|
|
||||||
Or obtain a libburn.pykix.org SVN snapshot,
|
Or obtain a libburn.pykix.org SVN snapshot,
|
||||||
go into the toplevel directory of the snapshot (e.g. cd libburn_pykix ),
|
go into the toplevel directory of the snapshot (e.g. cd libburn_pykix ),
|
||||||
and execute the autotools script ./bootstrap . Use autools version >= 1.7 .
|
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
|
./configure
|
||||||
make
|
make
|
||||||
@ -86,11 +85,10 @@ In order to surely get a standalone binary, execute
|
|||||||
|
|
||||||
cdrskin/compile_cdrskin.sh
|
cdrskin/compile_cdrskin.sh
|
||||||
|
|
||||||
Version identification and help texts available afterwards:
|
Version identification an help texts available afterwards:
|
||||||
cdrskin/cdrskin -version
|
cdrskin/cdrskin -version
|
||||||
cdrskin/cdrskin --help
|
cdrskin/cdrskin --help
|
||||||
cdrskin/cdrskin -help
|
cdrskin/cdrskin -help
|
||||||
man cdrskin/cdrskin.1
|
|
||||||
|
|
||||||
Install (eventually as superuser) cdrskin to a directory where it can be found:
|
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"
|
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
|
But libpthread must be installed on the system and glibc has to match. (See
|
||||||
below for a way to create a statically linked binary.)
|
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
|
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
|
The output of cdrskin --devices might look like
|
||||||
|
|
||||||
0 dev='/dev/sg0' rwrwr- : '_NEC' 'DVD_RW ND-4570A'
|
0 dev='/dev/sg0' rwrwr- : 'TEAC' 'CD-ROM CD-532S'
|
||||||
1 dev='/dev/sg1' rwrw-- : 'HL-DT-ST' 'DVDRAM GSA-4082B'
|
1 dev='/dev/sg1' rwrw-- : 'LITE-ON' 'LTR-48125S'
|
||||||
|
|
||||||
So full and insecure enabling of both for everybody would look like
|
So full and insecure enabling of both for everybody would look like
|
||||||
|
|
||||||
chmod a+rw /dev/sg0 /dev/sg1
|
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 !
|
I strongly discourage to run cdrskin with setuid root or via sudo !
|
||||||
It is not checked for the necessary degree of hacker safety.
|
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
|
Get an overview of cdrecord style addresses of available devices
|
||||||
cdrskin -scanbus
|
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
|
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
|
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
|
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
|
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
|
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
|
-eject my_image.iso
|
||||||
|
|
||||||
Burn a compressed afio archive to CD on-the-fly
|
Burn a compressed afio archive to CD on-the-fly
|
||||||
find . | afio -oZ - | cdrskin -v dev=0,1,0 fs=32m speed=8 -tao \
|
find . | afio -oZ - | cdrskin -v dev=1,1,0 fs=32m speed=8 -sao \
|
||||||
driveropts=burnfree padsize=300k -
|
driveropts=burnfree padsize=300k tsize=650m -
|
||||||
|
|
||||||
Burn 6 audio tracks from files with different formats to CD.
|
Burn 5 audio tracks to CD
|
||||||
Anything except .wav or .au files has to be converted into raw format first.
|
|
||||||
See below "Audio CD" for specifications.
|
|
||||||
ogg123 -d raw -f track01.cd /path/to/track1.ogg
|
ogg123 -d raw -f track01.cd /path/to/track1.ogg
|
||||||
oggdec -R -o track02.cd /path/to/track2.ogg
|
oggdec -R -o track02.cd /path/to/track2.ogg
|
||||||
lame --decode -t /path/to/track3.mp3 track03.cd
|
lame --decode -t /path/to/track3.mp3 track03.cd
|
||||||
madplay -o raw:track04.cd /path/to/track4.mp3
|
madplay -o raw:track04.cd /path/to/track4.mp3
|
||||||
mppdec --raw-le /path/to/track5.mpc track05.cd
|
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
|
Usage example with http://scdbackup.sourceforge.net
|
||||||
|
|
||||||
Address may be a cdrecord-style "scsibus,target,lun" as listed with
|
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 :
|
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 :
|
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 :
|
Run a backup :
|
||||||
|
|
||||||
@ -212,11 +196,17 @@ Run a backup :
|
|||||||
|
|
||||||
Restrictions
|
Restrictions
|
||||||
|
|
||||||
The major restrictions are lifted now: audio, TAO, multi-session do work.
|
The convenient burn mode TAO is not available with libburn yet.
|
||||||
Many cdrecord options are still unsupported, though.
|
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
|
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.
|
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
|
Pseudo-SCSI Adresses
|
||||||
|
|
||||||
cdrecord and cdrskin share the syntax of SCSI addresses but not necessarily
|
cdrecord and cdrskin share only some syntax of addresses but not the meaning
|
||||||
the meaning of the components. A cdrecord-style address for cdrskin
|
of the components. A cdrecord-style address for cdrskin
|
||||||
[prefix:]scsibus,target,lun
|
[[prefix:]scsibus,]target,lun
|
||||||
can be interpreted in two different modes.
|
corresponds either to a device file address or to a libburn drive number.
|
||||||
|
|
||||||
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.
|
|
||||||
Component "scsibus" indicates the translation method. Defined busses are:
|
Component "scsibus" indicates the translation method. Defined busses are:
|
||||||
0 target is the libburn drivenumber as listed with --devices
|
0 target is the libburn drivenumber as listed with --devices
|
||||||
1 associated to device file /dev/sgN , target chooses N
|
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
|
Further busses may emerge as libburn evolves. "prefix" and "lun" may get
|
||||||
a meaning. To stay upward compatible, use addresses as printed by -scanbus.
|
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.
|
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
|
K3b 0.10 for example derives cdrecord addresses by own examination of the
|
||||||
devices and not by calling cdrecord -scanbus.
|
devices and not by calling cdrecord -scanbus.
|
||||||
Standard mode will hopefully be fully compatible with their ideas.
|
To direct such callers to the appropriate drives, cdrskin allows to define
|
||||||
|
device address aliases. Like
|
||||||
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
|
|
||||||
cdrskin dev_translation=+1,0,0+/dev/sg1 \
|
cdrskin dev_translation=+1,0,0+/dev/sg1 \
|
||||||
dev_translation=+ATA: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
|
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
|
The first character after "dev_translation=" defines the character which
|
||||||
separates the two parts of the translation pair. (Above: "+" and "-".)
|
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
|
If not --no_rc is the first argument then cdrskin attempts on startup to read
|
||||||
arguments from the following three files:
|
arguments from the following three files:
|
||||||
/etc/default/cdrskin
|
/etc/defaults/cdrskin
|
||||||
/etc/opt/cdrskin/rc
|
/etc/opt/cdrskin/rc
|
||||||
$HOME/.cdrskinrc
|
$HOME/.cdrskinrc
|
||||||
The files are read in the sequence given above.
|
The files are read in the sequence given above.
|
||||||
Each readable line is treated as one single argument. No extra blanks.
|
Each readable line is treated as one single argument. No extra blanks,
|
||||||
A first character '#' marks a comment, empty lines are ignored.
|
no comments, no empty lines are permitted.
|
||||||
|
|
||||||
Example content of a startup file:
|
Example content of a startup file:
|
||||||
# This is the default device
|
dev=1,1,0
|
||||||
dev=0,1,0
|
dev_translation=+1,0,0+1,1,0
|
||||||
# To accomodate to eventual remnant cdrskin-0.2.2 addresses
|
|
||||||
dev_translation=+1,0,0+0,1,0
|
|
||||||
|
|
||||||
# Some more options
|
|
||||||
--fifo_start_empty
|
--fifo_start_empty
|
||||||
fs=16m
|
fs=16m
|
||||||
|
|
||||||
|
|
||||||
Audio CD
|
Audio CD
|
||||||
|
|
||||||
Lorenzo Taylor enabled option -audio in cdrskin (thanks !) and reports neat
|
Builtin extraction of raw audio data from filetypes .au and .wav is not
|
||||||
results with audio data files which are :
|
implemented yet. Lorenzo Taylor enabled option -audio in cdrskin (thanks !)
|
||||||
headerless PCM (i.e. uncompressed)
|
and reports neat results with audio data files which are :
|
||||||
44100 Hz sampling rate
|
headerless
|
||||||
16 bits per sample
|
44100Hz
|
||||||
stereo (2 channels)
|
16bit, stereo (or 4-channel if the 4-channel bit is set),
|
||||||
little-endian byte order with option -swab, or big-endian without -swab
|
little-endian byte order
|
||||||
|
He proposes to extract them from usual audio formats by commands like
|
||||||
Files with name extension .wav get examined wether they are in Microsoft WAVE
|
given above under "Usage examples".
|
||||||
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.
|
|
||||||
|
|
||||||
|
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
|
I myself am not into audio. So libburn-hackers@pykix.org might be the
|
||||||
best address for suggestions, requests and bug reports.
|
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
|
# The script is to be run in the directory above the toplevel
|
||||||
# directory of libburn resp. cdrskin development.
|
# directory of libburn resp. cdrskin development.
|
||||||
#
|
#
|
||||||
# libburn version used: http://libburn.pykix.org
|
# libburn version used: http://libburn.pykix.org SVN of Aug 15 2006
|
||||||
# Downloaded by:
|
# packed up in a tarball just to save it from inadverted changes.
|
||||||
# $ 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:
|
|
||||||
# original="./libburn_svn_A60815.tgz"
|
# original="./libburn_svn_A60815.tgz"
|
||||||
# original="./libburn_cdrskin_A60819.tgz"
|
original="./libburn_cdrskin_A60819.tgz"
|
||||||
|
|
||||||
# The top level directory in that snapshot is named
|
# The top level directory in that snapshot is named
|
||||||
intermediate="./libburn_pykix"
|
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"
|
changes="./libburn-0.2.1.ts.develop"
|
||||||
skin_rev="0.2.7"
|
skin_rev="0.1.5"
|
||||||
|
|
||||||
# The result directory and the name of the result tarballs
|
# The result directory and the name of the result tarballs
|
||||||
target="./cdrskin-${skin_rev}"
|
target="./cdrskin-${skin_rev}"
|
||||||
@ -51,7 +46,9 @@ compile_cmd="./cdrskin/compile_cdrskin.sh"
|
|||||||
compile_static_opts="-static"
|
compile_static_opts="-static"
|
||||||
compile_result="cdrskin/cdrskin"
|
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
|
bintarget_static="$bintarget_dynamic"-static
|
||||||
|
|
||||||
if test -d "$changes"
|
if test -d "$changes"
|
||||||
@ -108,24 +105,9 @@ fi
|
|||||||
cp -a "$cdrskin_dir" "$cdrskin_target"
|
cp -a "$cdrskin_dir" "$cdrskin_target"
|
||||||
|
|
||||||
# Remove copied binaries
|
# Remove copied binaries
|
||||||
rm "$cdrskin_target"/*.o
|
|
||||||
rm "$cdrskin_target"/cdrfifo
|
rm "$cdrskin_target"/cdrfifo
|
||||||
rm "$cdrskin_target"/cdrskin
|
rm "$cdrskin_target"/cdrskin
|
||||||
rm "$cdrskin_target"/cleanup
|
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)
|
# Remove unwanted SVN stuff (TODO: avoid downloading it)
|
||||||
for i in "$target"/.svn "$target"/*/.svn
|
for i in "$target"/.svn "$target"/*/.svn
|
||||||
@ -141,14 +123,15 @@ do
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
## No more : Add own libburn-README in toplevel
|
# For now: Add own libburn-README in toplevel
|
||||||
# cp -a "$changes"/README "$target"
|
cp -a "$changes"/README "$target"
|
||||||
|
|
||||||
## No more : Add modified Makefile.am
|
# Add modified Makefile.am
|
||||||
# cp -a "$changes"/Makefile.am "$target"
|
cp -a "$changes"/Makefile.am "$target"
|
||||||
|
|
||||||
|
|
||||||
# Make SVN state tarball for the libburn team
|
# Make SVN state tarball for the libburn team
|
||||||
|
# TODO: will probably be obsoleted after sucessful merge
|
||||||
tar czf "$cdrskin_tarball_svn" "$target"
|
tar czf "$cdrskin_tarball_svn" "$target"
|
||||||
|
|
||||||
|
|
||||||
@ -158,20 +141,6 @@ tar czf "$cdrskin_tarball_svn" "$target"
|
|||||||
#
|
#
|
||||||
( cd "$target" ; ./bootstrap )
|
( 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
|
# Pack it up to the new libburn+cdrskin-tarball
|
||||||
tar czf "$cdrskin_tarball" "$target"
|
tar czf "$cdrskin_tarball" "$target"
|
||||||
@ -182,22 +151,15 @@ tar czf "$cdrskin_tarball" "$target"
|
|||||||
./configure
|
./configure
|
||||||
make
|
make
|
||||||
$compile_cmd -do_strip
|
$compile_cmd -do_strip
|
||||||
cp "$compile_result" "../$bintarget_dynamic"
|
cp "$compile_result" "$bintarget_dynamic"
|
||||||
if test -n "$compile_static_opts"
|
if test -n "$compile_static_opts"
|
||||||
then
|
then
|
||||||
$compile_cmd $compile_static_opts -do_strip
|
$compile_cmd $compile_static_opts -do_strip
|
||||||
cp "$compile_result" "../$bintarget_static"
|
cp "$compile_result" "$bintarget_static"
|
||||||
fi
|
fi
|
||||||
)
|
)
|
||||||
|
|
||||||
# Remove the build area
|
|
||||||
# Disable this for debugging the merge process
|
# Disable this for debugging the merge process
|
||||||
rm -rf "$target"
|
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;
|
int source_fd;
|
||||||
double in_counter;
|
double in_counter;
|
||||||
|
|
||||||
double fd_in_counter;
|
|
||||||
double fd_in_limit;
|
|
||||||
|
|
||||||
char *buffer;
|
char *buffer;
|
||||||
int buffer_size;
|
int buffer_size;
|
||||||
int buffer_is_full;
|
int buffer_is_full;
|
||||||
@ -75,27 +72,17 @@ struct CdrfifO {
|
|||||||
double empty_counter;
|
double empty_counter;
|
||||||
double full_counter;
|
double full_counter;
|
||||||
|
|
||||||
|
|
||||||
/* (sequential) fd chaining */
|
/* (sequential) fd chaining */
|
||||||
/* fds: 0=source, 1=dest */
|
|
||||||
int follow_up_fds[Cdrfifo_ffd_maX][2];
|
int follow_up_fds[Cdrfifo_ffd_maX][2];
|
||||||
|
|
||||||
/* index of first byte in buffer which does not belong to predecessor fd */
|
/* index of first byte in buffer which does not belong to predecessor fd */
|
||||||
int follow_up_eop[Cdrfifo_ffd_maX];
|
int follow_up_eop[Cdrfifo_ffd_maX];
|
||||||
|
|
||||||
/* index of first byte in buffer which belongs to [this] fd pair */
|
/* index of first byte in buffer which belongs to [this] fd pair */
|
||||||
int follow_up_sod[Cdrfifo_ffd_maX];
|
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 */
|
/* number of defined follow-ups */
|
||||||
int follow_up_fd_counter;
|
int follow_up_fd_counter;
|
||||||
|
|
||||||
/* index of currently active (i.e. reading) follow-up */
|
/* index of currently active (i.e. reading) follow-up */
|
||||||
int follow_up_fd_idx;
|
int follow_up_fd_idx;
|
||||||
|
|
||||||
|
|
||||||
/* (simultaneous) peer chaining */
|
/* (simultaneous) peer chaining */
|
||||||
struct CdrfifO *next;
|
struct CdrfifO *next;
|
||||||
struct CdrfifO *prev;
|
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);
|
buffer_size+= chunk_size-(buffer_size%chunk_size);
|
||||||
o->source_fd= source_fd;
|
o->source_fd= source_fd;
|
||||||
o->in_counter= 0.0;
|
o->in_counter= 0.0;
|
||||||
o->fd_in_counter= 0;
|
|
||||||
o->fd_in_limit= -1.0;
|
|
||||||
o->buffer= NULL;
|
o->buffer= NULL;
|
||||||
o->buffer_is_full= 0;
|
o->buffer_is_full= 0;
|
||||||
o->buffer_size= buffer_size;
|
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++) {
|
for(i= 0; i<Cdrfifo_ffd_maX; i++) {
|
||||||
o->follow_up_fds[i][0]= o->follow_up_fds[i][1]= -1;
|
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_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_counter= 0;
|
||||||
o->follow_up_fd_idx= -1;
|
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)
|
int Cdrfifo_set_fds(struct CdrfifO *o, int source_fd, int dest_fd, int flag)
|
||||||
{
|
{
|
||||||
o->source_fd= source_fd;
|
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
|
fifo buffer when its predecessors are exhausted. Reading will start as
|
||||||
soon as reading of the predecessor encounters EOF. Writing will start
|
soon as reading of the predecessor encounters EOF. Writing will start
|
||||||
as soon as all pending predecessor data are written.
|
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 Cdrfifo_attach_follow_up_fds(struct CdrfifO *o, int source_fd, int dest_fd,
|
||||||
int flag)
|
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][0]= source_fd;
|
||||||
o->follow_up_fds[o->follow_up_fd_counter][1]= dest_fd;
|
o->follow_up_fds[o->follow_up_fd_counter][1]= dest_fd;
|
||||||
o->follow_up_fd_counter++;
|
o->follow_up_fd_counter++;
|
||||||
return(o->follow_up_fd_counter);
|
return(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -604,12 +567,7 @@ after_write:;
|
|||||||
can_read= o->chunk_size;
|
can_read= o->chunk_size;
|
||||||
if(o->write_idx<o->read_idx && o->write_idx+can_read > o->read_idx)
|
if(o->write_idx<o->read_idx && o->write_idx+can_read > o->read_idx)
|
||||||
can_read= o->read_idx - o->write_idx;
|
can_read= o->read_idx - o->write_idx;
|
||||||
if(o->fd_in_limit>=0.0)
|
ret= read(o->source_fd,o->buffer+o->write_idx,can_read);
|
||||||
if(can_read > o->fd_in_limit - o->fd_in_counter)
|
|
||||||
can_read= o->fd_in_limit - o->fd_in_counter;
|
|
||||||
ret= 0;
|
|
||||||
if(can_read>0)
|
|
||||||
ret= read(o->source_fd,o->buffer+o->write_idx,can_read);
|
|
||||||
if(ret==-1) {
|
if(ret==-1) {
|
||||||
|
|
||||||
/* >>> handle input error */;
|
/* >>> handle input error */;
|
||||||
@ -641,8 +599,6 @@ after_write:;
|
|||||||
sod= 0;
|
sod= 0;
|
||||||
o->follow_up_sod[idx]= sod;
|
o->follow_up_sod[idx]= sod;
|
||||||
o->write_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))
|
if(Cdrfifo_debuG || (flag&1))
|
||||||
fprintf(stderr,"\ncdrfio: new fifo source fd : %d\n",o->source_fd);
|
fprintf(stderr,"\ncdrfio: new fifo source fd : %d\n",o->source_fd);
|
||||||
} else {
|
} else {
|
||||||
@ -652,7 +608,6 @@ after_write:;
|
|||||||
did_work= 1;
|
did_work= 1;
|
||||||
o->put_counter++;
|
o->put_counter++;
|
||||||
o->in_counter+= ret;
|
o->in_counter+= ret;
|
||||||
o->fd_in_counter+= ret;
|
|
||||||
o->write_idx+= ret;
|
o->write_idx+= ret;
|
||||||
if(o->write_idx>=o->buffer_size)
|
if(o->write_idx>=o->buffer_size)
|
||||||
o->write_idx= 0;
|
o->write_idx= 0;
|
||||||
@ -796,7 +751,7 @@ ex:;
|
|||||||
|
|
||||||
|
|
||||||
/** Fill the fifo as far as possible without writing to destination fd */
|
/** 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;
|
int ret,fill= 0,space,state;
|
||||||
|
|
||||||
@ -810,8 +765,6 @@ int Cdrfifo_fill(struct CdrfifO *o, int size, int flag)
|
|||||||
} else if(state!=1)
|
} else if(state!=1)
|
||||||
break;
|
break;
|
||||||
if(space<=0)
|
if(space<=0)
|
||||||
break;
|
|
||||||
if(size>=0 && fill>=size)
|
|
||||||
break;
|
break;
|
||||||
ret= Cdrfifo_try_to_work(o,100000,NULL,NULL,2);
|
ret= Cdrfifo_try_to_work(o,100000,NULL,NULL,2);
|
||||||
if(ret<0) {
|
if(ret<0) {
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
|
|
||||||
/** The fifo buffer which will smoothen the data stream from data provider
|
/** 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
|
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
|
with varying bandwidths (e.g. compressed archives created on the fly
|
||||||
or mkisofs running at its speed limit.).
|
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 Cdrfifo_set_speed_limit(struct CdrfifO *o, double bytes_per_second,
|
||||||
int flag);
|
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_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);
|
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
|
fifo buffer when its predecessors are exhausted. Reading will start as
|
||||||
soon as reading of the predecessor encounters EOF. Writing will start
|
soon as reading of the predecessor encounters EOF. Writing will start
|
||||||
as soon as all pending predecessor data are written.
|
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 Cdrfifo_attach_follow_up_fds(struct CdrfifO *o, int source_fd, int dest_fd,
|
||||||
int flag);
|
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,
|
int Cdrfifo_try_to_work(struct CdrfifO *o, int wait_usec,
|
||||||
char *reply_buffer, int *reply_count, int flag);
|
char *reply_buffer, int *reply_count, int flag);
|
||||||
|
|
||||||
/** Fill the fifo as far as possible without writing to destination fd.
|
/** Fill the fifo as far as possible without writing to destination fd
|
||||||
@param size if >=0 : end filling after the given number of bytes
|
|
||||||
@return 1 on success, <=0 on failure
|
@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 */
|
#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>
|
GPL software included:<BR>
|
||||||
</H2>
|
</H2>
|
||||||
<DL>
|
<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>(by Derek Foreman, Ben Jansens, and team of libburn.pykix.org)</DD>
|
||||||
<DD>transfers data to CD</DD>
|
<DD>transfers data to CD</DD>
|
||||||
</DL>
|
</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
|
This has been achieved quite sufficiently for the needs of backup tool
|
||||||
<A HREF="http://scdbackup.sourceforge.net/main_eng.html">scdbackup</A>
|
<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>
|
and for data CD projects of <A HREF="http://www.k3b.org">K3b</A>
|
||||||
(see <A HREF="#examples">examples</A>).
|
(see <A HREF="#examples">examples</A>).<BR>
|
||||||
Suitability for audio CD frontends has been improved much and is now being
|
|
||||||
evaluated.<BR>
|
|
||||||
Further enhancements depend on people who can describe and discuss their
|
Further enhancements depend on people who can describe and discuss their
|
||||||
wishes as well as on the development of libburn.</DT>
|
wishes as well as on the development of libburn.</DT>
|
||||||
<BR><BR>
|
<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>
|
<DT><A HREF="cdrskin_help">cdrskin -help</A></DT>
|
||||||
<DD>reports the cdrecord compatible options</DD>
|
<DD>reports the cdrecord compatible options</DD>
|
||||||
<DT><A HREF="cdrskin__help">cdrskin --help</A></DT>
|
<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
|
(Be cursed if you install cdrskin as "cdrecord" without clearly forwarding
|
||||||
this "don't bother Joerg" demand.)
|
this "don't bother Joerg" demand.)
|
||||||
</DD>
|
</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>
|
</DL>
|
||||||
</P>
|
</P>
|
||||||
|
|
||||||
@ -123,7 +111,15 @@ wishes as well as on the development of libburn.</DT>
|
|||||||
<UL>
|
<UL>
|
||||||
<DT></DT>
|
<DT></DT>
|
||||||
<LI>
|
<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>
|
||||||
<LI>
|
<LI>
|
||||||
cdrskin -scanbus or --devices hangs for quite a while if there is
|
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
|
/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
|
ill peers. Alternatively one can guess the address of the ill device, remove
|
||||||
rw-permissions and retry the bus scan as non-superuser.
|
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>
|
</UL>
|
||||||
</P>
|
</P>
|
||||||
|
|
||||||
@ -145,30 +147,30 @@ rw-permissions and retry the bus scan as non-superuser.
|
|||||||
<P>
|
<P>
|
||||||
<DL>
|
<DL>
|
||||||
<DT>Download as source code (see README):</DT>
|
<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>
|
<DD><A HREF="cdrskin-0.1.4.tar.gz">cdrskin-0.1.4.tar.gz</A>
|
||||||
(510 KB).
|
(580 KB).
|
||||||
</DD>
|
</DD>
|
||||||
<DD>(Most recent patch: backported man page from cdrskin-0.2.7)</DD>
|
|
||||||
<DD>
|
<DD>
|
||||||
The "stable" cdrskin tarballs are source code identical with "stable"
|
The "stable" cdrskin tarballs are tested and eventually slightly modified
|
||||||
libburn releases or with "stabilized" libburn SVN snapshots. They get
|
SVN snapshots from libburn.pykix.org . All modifications are to be re-merged
|
||||||
produced via a different procedure, though.<BR>
|
into that SVN repository.<BR>
|
||||||
cdrskin is part of libburn - full libburn is provided with cdrskin releases.
|
cdrskin is part of libburn - full libburn is provided with cdrskin releases.
|
||||||
</DD>
|
</DD>
|
||||||
<DD> </DD>
|
<DD> </DD>
|
||||||
<DT>Download as single x86 binaries (untar and move to /usr/bin/cdrskin):</DT>
|
<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">
|
<DD><A HREF="cdrskin_0.1.4-x86-suse9_0.tar.gz">
|
||||||
cdrskin_0.2.6.pl01-x86-suse9_0.tar.gz</A>, (60 KB),
|
cdrskin_0.1.4-x86-suse9_0.tar.gz</A>, (50 KB),
|
||||||
<DL>
|
<DL>
|
||||||
<DD>runs on SuSE 9.0 (2.4.21) , RIP-14.4 (2.6.14) ,
|
<DD>runs on SuSE 9.0 (2.4.21) , RIP-14.4 (2.6.14) ,
|
||||||
Gentoo (2.6.15 x86_64 Athlon).</DD>
|
Gentoo (2.6.15 x86_64 Athlon).</DD>
|
||||||
</DL>
|
</DL>
|
||||||
<DD><A HREF="cdrskin_0.2.6.pl01-x86-suse9_0-static.tar.gz">
|
<DD><A HREF="cdrskin_0.1.4-x86-suse9_0-static.tar.gz">
|
||||||
cdrskin_0.2.6.pl01-x86-suse9_0-static.tar.gz</A>, (260 KB), -static compiled,
|
cdrskin_0.1.4-x86-suse9_0-static.tar.gz</A>, (250 KB), -static compiled,
|
||||||
<DL>
|
<DL>
|
||||||
<DD>runs on SuSE 7.2 (2.4.4), and on the systems above.</DD>
|
<DD>runs on SuSE 7.2 (2.4.4), and on the systems above.</DD>
|
||||||
</DL>
|
</DL>
|
||||||
</DD>
|
</DD>
|
||||||
|
<DD> </DD>
|
||||||
</DL>
|
</DL>
|
||||||
<DL><DT>Documentation:</DT>
|
<DL><DT>Documentation:</DT>
|
||||||
<DD><A HREF="README_cdrskin">README</A> a short introduction</DD>
|
<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>
|
<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>
|
<P>
|
||||||
<DL>
|
<DL>
|
||||||
<DT><H3>Development snapshot, version 0.2.7 :</H3></DT>
|
<DT>Development snapshot:</DT>
|
||||||
<DD>Enhancements towards stable version 0.2.6:
|
|
||||||
(none yet)
|
|
||||||
</DD>
|
|
||||||
<DD> </DD>
|
<DD> </DD>
|
||||||
<DD><A HREF="README_cdrskin_devel">README 0.2.7</A>
|
<DD><A HREF="README_cdrskin_devel">README 0.1.5</A>
|
||||||
<DD><A HREF="cdrskin__help_devel">cdrskin_0.2.7 --help</A></DD>
|
<DD><A HREF="cdrskin__help_devel">cdrskin_0.1.5 --help</A></DD>
|
||||||
<DD><A HREF="cdrskin_help_devel">cdrskin_0.2.7 -help</A></DD>
|
<DD><A HREF="cdrskin_help_devel">cdrskin_0.1.5 -help</A></DD>
|
||||||
<DD> </DD>
|
<DD> </DD>
|
||||||
<DT>Maintainers of cdrskin unstable packages please use SVN of
|
<DD>Maintainers of cdrskin unstable packages please use SVN of
|
||||||
<A HREF="http://libburn.pykix.org"> libburn.pykix.org</A></DT>
|
<A HREF="http://libburn.pykix.org"> libburn.pykix.org</A></DD>
|
||||||
<DD>Download: <KBD><B>svn co http://libburn-svn.pykix.org/libburn/trunk libburn_pykix</B>
|
<DD>Download: <KBD><B>svn co http://libburn-svn.pykix.org/trunk libburn_pykix</B>
|
||||||
</KBD></DD>
|
</KBD></DD>
|
||||||
<DD>Build: <KBD><B>cd libburn_pykix ; ./bootstrap ; ./configure ; make</B>
|
<DD>Build: <KBD><B>cd libburn_pykix ; ./bootstrap ; ./configure ; make</B>
|
||||||
</KBD></DD>
|
</KBD></DD>
|
||||||
@ -235,19 +211,19 @@ vanilla tools like make and gcc are needed.</DD>
|
|||||||
<DD> </DD>
|
<DD> </DD>
|
||||||
<DT>The following downloads are intended for adventurous end users or
|
<DT>The following downloads are intended for adventurous end users or
|
||||||
admins with full system souvereignty.</DT>
|
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> ):
|
<A HREF="README_cdrskin_devel">upcoming README</A> ):
|
||||||
</DD>
|
</DT>
|
||||||
<DD>
|
<DD>
|
||||||
<A HREF="cdrskin-0.2.7.tar.gz">cdrskin-0.2.7.tar.gz</A>
|
<A HREF="cdrskin-0.1.5.tar.gz">cdrskin-0.1.5.tar.gz</A>
|
||||||
(500 KB).
|
(580 KB).
|
||||||
</DD>
|
</DD>
|
||||||
<DD>Binary (untar and move to /usr/bin/cdrskin):</DD>
|
<DT>Binary (untar and move to /usr/bin/cdrskin):</DT>
|
||||||
<DD><A HREF="cdrskin_0.2.7-x86-suse9_0.tar.gz">
|
<DD><A HREF="cdrskin_0.1.5-x86-suse9_0.tar.gz">
|
||||||
cdrskin_0.2.7-x86-suse9_0.tar.gz</A>, (60 KB).
|
cdrskin_0.1.5-x86-suse9_0.tar.gz</A>, (50 KB).
|
||||||
</DD>
|
</DD>
|
||||||
<DD><A HREF="cdrskin_0.2.7-x86-suse9_0-static.tar.gz">
|
<DD><A HREF="cdrskin_0.1.5-x86-suse9_0-static.tar.gz">
|
||||||
cdrskin_0.2.7-x86-suse9_0-static.tar.gz</A>, (260 KB)
|
cdrskin_0.1.5-x86-suse9_0-static.tar.gz</A>, (250 KB)
|
||||||
</DD>
|
</DD>
|
||||||
</DL>
|
</DL>
|
||||||
</P>
|
</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>
|
<DT>Example for a test session with a cdrecord based scdbackup installation:</DT>
|
||||||
<DD>$ <KBD><B>cdrskin -scanbus</B></KBD></DD>
|
<DD>$ <KBD><B>cdrskin -scanbus</B></KBD></DD>
|
||||||
<DD><KBD>...</KBD></DD>
|
<DD><KBD>...</KBD></DD>
|
||||||
<DD><KBD> 2,0,0 0) 'TEAC' 'CD-ROM CD-532S' '?' Removable CD-ROM</KBD></DD>
|
<DD><KBD>scsibus1:</KBD></DD>
|
||||||
<DD>$ <KBD><B>cdrskin -scanbus dev=ATA</B></KBD></DD>
|
<DD><KBD> 1,0,0 0) 'TEAC' 'CD-ROM CD-532S' '?' Removable CD-ROM</KBD></DD>
|
||||||
<DD><KBD>...</KBD></DD>
|
<DD><KBD>scsibus2:</KBD></DD>
|
||||||
<DD><KBD> 1,0,0 1) 'LITE-ON' 'LTR-48125S' '?' Removable CD-ROM</KBD></DD>
|
<DD><KBD> 2,2,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_SCSI_ADR="2,2,0"</B></KBD></DD>
|
||||||
<DD>$ <KBD><B>export SCDBACKUP_CDRECORD="cdrskin -v -v tao_to_sao_tsize=650m"</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>
|
<DD>$ <KBD><B>scdbackup_home</B></KBD></DD>
|
||||||
</DL>
|
</DL>
|
||||||
<DL>
|
<DL>
|
||||||
<DT>Example for a permanent configuration of cdrskin based scdbackup</DT>
|
<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>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_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><B>./CONFIGURE_CD</B></KBD></DD>
|
||||||
<DD><KBD>...</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>
|
</DL>
|
||||||
If your system is stricken with some ill CD device then this can stall
|
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.
|
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.
|
and try again.
|
||||||
<DL>
|
<DL>
|
||||||
<DT></DT>
|
<DT></DT>
|
||||||
<DD><KBD> ------------------- SCSI devices. To be used like 0,0,0</KBD></DD>
|
<DD><KBD> 1,0,0 0) 'TEAC' 'CD-ROM CD-532S' '?' Removable CD-ROM</KBD></DD>
|
||||||
<DD><KBD> 2,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>------------------- end of SCSI device list</KBD></DD>
|
||||||
<DD><KBD> ------------------- ATA devices. To be used like ATA:0,0,0
|
|
||||||
<DD><KBD> 1,0,0 1) 'LITE-ON' 'LTR-48125S' '?' Removable CD-ROM</KBD></DD>
|
|
||||||
<DD><KBD>...</KBD></DD>
|
<DD><KBD>...</KBD></DD>
|
||||||
<DD><KBD> * Your cdrecord offers -driveropts=burnfree with your recorder.</KBD></DD>
|
<DD><KBD> * Your cdrecord offers -driveropts=burnfree with your recorder.</KBD></DD>
|
||||||
<DD><KBD>...</KBD></DD>
|
<DD><KBD>...</KBD></DD>
|
||||||
<DD><KBD>scdbackup for CD 0.8.6 : First stage of installation done.</KBD></DD>
|
<DD><KBD>scdbackup for CD 0.8.6 : First stage of installation done.</KBD></DD>
|
||||||
<DD><KBD>...</KBD></DD>
|
<DD><KBD>...</KBD></DD>
|
||||||
<DD><KBD>Now give it a try. Run : scdbackup_home</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>
|
||||||
<DL>
|
<DL>
|
||||||
<DT>To get back to using cdrecord :</DT>
|
<DT>To get back to using cdrecord :</DT>
|
||||||
|
@ -1 +1 @@
|
|||||||
#define Cdrskin_timestamP "2006.12.23.220001"
|
#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.
|
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"
|
#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 */
|
/* Signals to be caught */
|
||||||
static int signal_list[]= {
|
static int signal_list[]= {
|
||||||
SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT,
|
SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT,
|
||||||
SIGFPE, SIGSEGV, SIGPIPE, SIGALRM, SIGTERM,
|
SIGFPE, SIGSEGV, SIGPIPE, SIGALRM, SIGTERM,
|
||||||
SIGUSR1, SIGUSR2, SIGXCPU, SIGTSTP, SIGTTIN,
|
SIGUSR1, SIGUSR2, SIGXCPU, SIGTSTP, SIGTTIN,
|
||||||
@ -51,7 +32,7 @@ static int signal_list[]= {
|
|||||||
SIGBUS, SIGPOLL, SIGPROF, SIGSYS, SIGTRAP,
|
SIGBUS, SIGPOLL, SIGPROF, SIGSYS, SIGTRAP,
|
||||||
SIGVTALRM, SIGXCPU, SIGXFSZ, -1
|
SIGVTALRM, SIGXCPU, SIGXFSZ, -1
|
||||||
};
|
};
|
||||||
static char *signal_name_list[]= {
|
static char *signal_name_list[]= {
|
||||||
"SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGABRT",
|
"SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGABRT",
|
||||||
"SIGFPE", "SIGSEGV", "SIGPIPE", "SIGALRM", "SIGTERM",
|
"SIGFPE", "SIGSEGV", "SIGPIPE", "SIGALRM", "SIGTERM",
|
||||||
"SIGUSR1", "SIGUSR2", "SIGXCPU", "SIGTSTP", "SIGTTIN",
|
"SIGUSR1", "SIGUSR2", "SIGXCPU", "SIGTSTP", "SIGTTIN",
|
||||||
@ -64,14 +45,10 @@ static int signal_list_count= 24;
|
|||||||
/* Signals not to be caught */
|
/* Signals not to be caught */
|
||||||
static int non_signal_list[]= {
|
static int non_signal_list[]= {
|
||||||
SIGKILL, SIGCHLD, SIGSTOP, SIGURG, -1
|
SIGKILL, SIGCHLD, SIGSTOP, SIGURG, -1
|
||||||
};
|
};
|
||||||
static int non_signal_list_count= 4;
|
static int non_signal_list_count= 4;
|
||||||
|
|
||||||
|
|
||||||
#endif /* Cleanup_has_no_libburn_os_H */
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* run time dynamic part */
|
/* run time dynamic part */
|
||||||
static char cleanup_msg[4096]= {""};
|
static char cleanup_msg[4096]= {""};
|
||||||
static int cleanup_exiting= 0;
|
static int cleanup_exiting= 0;
|
||||||
@ -85,27 +62,26 @@ static int Cleanup_handler_exit(int exit_value, int signum, int flag)
|
|||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if(cleanup_msg[0]!=0)
|
|
||||||
fprintf(stderr,"\n%s\n",cleanup_msg);
|
|
||||||
if(cleanup_perform_app_handler_first)
|
if(cleanup_perform_app_handler_first)
|
||||||
if(cleanup_app_handler!=NULL) {
|
if(cleanup_app_handler!=NULL) {
|
||||||
ret= (*cleanup_app_handler)(cleanup_app_handle,signum,0);
|
ret= (*cleanup_app_handler)(cleanup_app_handle,signum,0);
|
||||||
if(ret==2 || ret==-2)
|
if(ret==2)
|
||||||
return(2);
|
return(2);
|
||||||
}
|
}
|
||||||
if(cleanup_exiting) {
|
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",
|
fprintf(stderr,"cleanup: ABORT : repeat by pid=%d, signum=%d\n",
|
||||||
getpid(),signum);
|
getpid(),signum);
|
||||||
return(0);
|
return(0);
|
||||||
}
|
}
|
||||||
cleanup_exiting= 1;
|
cleanup_exiting= 1;
|
||||||
|
if(cleanup_msg[0]!=0)
|
||||||
|
fprintf(stderr,"%s\n",cleanup_msg);
|
||||||
alarm(0);
|
alarm(0);
|
||||||
if(!cleanup_perform_app_handler_first)
|
if(!cleanup_perform_app_handler_first)
|
||||||
if(cleanup_app_handler!=NULL) {
|
if(cleanup_app_handler!=NULL)
|
||||||
ret= (*cleanup_app_handler)(cleanup_app_handle,signum,0);
|
(*cleanup_app_handler)(cleanup_app_handle,signum,0);
|
||||||
if(ret==2 || ret==-2)
|
|
||||||
return(2);
|
|
||||||
}
|
|
||||||
exit(exit_value);
|
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_msg[0]= 0;
|
||||||
cleanup_app_handle= handle;
|
cleanup_app_handle= handle;
|
||||||
cleanup_app_handler= handler;
|
cleanup_app_handler= handler;
|
||||||
|
|
||||||
/* <<< make cleanup_exiting thread safe to get rid of this */
|
|
||||||
if(flag&4)
|
if(flag&4)
|
||||||
cleanup_perform_app_handler_first= 1;
|
cleanup_perform_app_handler_first= 1;
|
||||||
|
|
||||||
|
|
||||||
if(flag&1)
|
if(flag&1)
|
||||||
sig_handler= SIG_DFL;
|
sig_handler= SIG_DFL;
|
||||||
else if(flag&2)
|
else if(flag&2)
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
A signal handler which cleans up an application and exits.
|
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
|
#ifndef Cleanup_includeD
|
||||||
@ -13,7 +13,7 @@
|
|||||||
/** Layout of an application provided cleanup function using an application
|
/** Layout of an application provided cleanup function using an application
|
||||||
provided handle as first argument and the signal number as second
|
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.
|
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.
|
instance and the Cleanup handler shall return rather than exit.
|
||||||
*/
|
*/
|
||||||
typedef int (*Cleanup_app_handler_T)(void *, int, int);
|
typedef int (*Cleanup_app_handler_T)(void *, int, int);
|
||||||
|
@ -6,55 +6,37 @@
|
|||||||
|
|
||||||
debug_opts=
|
debug_opts=
|
||||||
def_opts=
|
def_opts=
|
||||||
largefile_opts="-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE=1"
|
libvers="-DCdrskin_libburn_0_2_2"
|
||||||
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"
|
|
||||||
do_strip=0
|
do_strip=0
|
||||||
static_opts=
|
static_opts=
|
||||||
warn_opts="-Wall"
|
warn_opts="-Wall"
|
||||||
fifo_source="cdrskin/cdrfifo.c"
|
fifo_source="cdrskin/cdrfifo.c"
|
||||||
compile_cdrskin=1
|
compile_cdrskin=1
|
||||||
compile_cdrfifo=0
|
compile_cdrfifo=0
|
||||||
compile_dewav=0
|
|
||||||
|
|
||||||
for i in "$@"
|
for i in "$@"
|
||||||
do
|
do
|
||||||
if test "$i" = "-compile_cdrfifo"
|
if test "$i" = "-compile_cdrfifo"
|
||||||
then
|
then
|
||||||
compile_cdrfifo=1
|
compile_cdrfifo=1
|
||||||
elif test "$i" = "-compile_dewav"
|
elif test "$i" = "-tarball_0_2"
|
||||||
then
|
then
|
||||||
compile_dewav=1
|
libvers=
|
||||||
|
elif test "$i" = "-cvs_A51208"
|
||||||
|
then
|
||||||
|
libvers="-DCdrskin_libburn_cvs_A51208_tS"
|
||||||
elif test "$i" = "-cvs_A60220"
|
elif test "$i" = "-cvs_A60220"
|
||||||
then
|
then
|
||||||
libvers="-DCdrskin_libburn_cvs_A60220_tS"
|
libvers="-DCdrskin_libburn_cvs_A60220_tS"
|
||||||
libdax_audioxtr_o=
|
elif test "$i" = "-libburn_0_2_1"
|
||||||
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"
|
|
||||||
then
|
then
|
||||||
libvers="-DCdrskin_libburn_0_2_6"
|
libvers="-DCdrskin_libburn_0_2_1"
|
||||||
libdax_audioxtr_o="libburn/libdax_audioxtr.o"
|
elif test "$i" = "-libburn_0_2_2"
|
||||||
libdax_msgs_o="libburn/libdax_msgs.o"
|
|
||||||
cleanup_src_or_obj="libburn/cleanup.o"
|
|
||||||
elif test "$i" = "-libburn_svn"
|
|
||||||
then
|
then
|
||||||
libvers="-DCdrskin_libburn_0_2_6"
|
libvers="-DCdrskin_libburn_0_2_2"
|
||||||
libdax_audioxtr_o="libburn/libdax_audioxtr.o"
|
|
||||||
libdax_msgs_o="libburn/libdax_msgs.o"
|
|
||||||
cleanup_src_or_obj="libburn/cleanup.o"
|
|
||||||
elif test "$i" = "-newapi" -o "$i" = "-experimental"
|
elif test "$i" = "-newapi" -o "$i" = "-experimental"
|
||||||
then
|
then
|
||||||
def_opts="$def_opts -DCdrskin_new_api_tesT"
|
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"
|
elif test "$i" = "-do_not_compile_cdrskin"
|
||||||
then
|
then
|
||||||
compile_cdrskin=0
|
compile_cdrskin=0
|
||||||
@ -74,17 +56,16 @@ do
|
|||||||
debug_opts="$debug_opts -O2"
|
debug_opts="$debug_opts -O2"
|
||||||
elif test "$i" = "-help" -o "$i" = "--help" -o "$i" = "-h"
|
elif test "$i" = "-help" -o "$i" = "--help" -o "$i" = "-h"
|
||||||
then
|
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 "Options:"
|
||||||
echo " -compile_cdrfifo compile program cdrskin/cdrfifo."
|
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 " -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_0_2_1 set macro to match libburn-SVN of 15 Sep 2006."
|
||||||
echo " -libburn_svn set macro to match current libburn-SVN."
|
echo " -libburn_0_2_2 set macro to match libburn-0.2.2"
|
||||||
echo " -no_largefile do not use 64 bit off_t (must match libburn)."
|
|
||||||
echo " -do_not_compile_cdrskin omit compilation of cdrskin/cdrskin."
|
echo " -do_not_compile_cdrskin omit compilation of cdrskin/cdrskin."
|
||||||
echo " -experimental use newly introduced libburn features."
|
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_diet produce capability reduced lean version."
|
||||||
echo " -do_strip apply program strip to compiled programs."
|
echo " -do_strip apply program strip to compiled programs."
|
||||||
echo " -g compile with cc option -g."
|
echo " -g compile with cc option -g."
|
||||||
@ -104,23 +85,15 @@ echo "Build timestamp : $timestamp"
|
|||||||
|
|
||||||
if test "$compile_cdrskin"
|
if test "$compile_cdrskin"
|
||||||
then
|
then
|
||||||
echo "compiling program cdrskin/cdrskin.c $static_opts $debug_opts $libvers $def_opts $cleanup_src_or_obj"
|
echo "compiling program cdrskin/cdrskin.c $static_opts $debug_opts $libvers $def_opts"
|
||||||
cc -I. \
|
cc $warn_opts -I. $static_opts $debug_opts $libvers $def_opts \
|
||||||
$warn_opts \
|
|
||||||
$static_opts \
|
|
||||||
$debug_opts \
|
|
||||||
$libvers \
|
|
||||||
$largefile_opts \
|
|
||||||
$def_opts \
|
|
||||||
\
|
|
||||||
-DCdrskin_build_timestamP='"'"$timestamp"'"' \
|
-DCdrskin_build_timestamP='"'"$timestamp"'"' \
|
||||||
\
|
\
|
||||||
-o cdrskin/cdrskin \
|
-o cdrskin/cdrskin \
|
||||||
\
|
\
|
||||||
cdrskin/cdrskin.c \
|
cdrskin/cdrskin.c \
|
||||||
$fifo_source \
|
$fifo_source \
|
||||||
\
|
cdrskin/cleanup.c \
|
||||||
$cleanup_src_or_obj \
|
|
||||||
\
|
\
|
||||||
libburn/async.o \
|
libburn/async.o \
|
||||||
libburn/debug.o \
|
libburn/debug.o \
|
||||||
@ -131,10 +104,9 @@ then
|
|||||||
libburn/source.o \
|
libburn/source.o \
|
||||||
libburn/structure.o \
|
libburn/structure.o \
|
||||||
\
|
\
|
||||||
|
libburn/message.o \
|
||||||
libburn/sg.o \
|
libburn/sg.o \
|
||||||
libburn/write.o \
|
libburn/write.o \
|
||||||
$libdax_audioxtr_o \
|
|
||||||
$libdax_msgs_o \
|
|
||||||
\
|
\
|
||||||
libburn/mmc.o \
|
libburn/mmc.o \
|
||||||
libburn/sbc.o \
|
libburn/sbc.o \
|
||||||
@ -148,17 +120,6 @@ then
|
|||||||
libburn/lec.o \
|
libburn/lec.o \
|
||||||
\
|
\
|
||||||
-lpthread
|
-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
|
fi
|
||||||
|
|
||||||
if test "$compile_cdrfifo" = 1
|
if test "$compile_cdrfifo" = 1
|
||||||
@ -168,43 +129,8 @@ then
|
|||||||
-DCdrfifo_standalonE \
|
-DCdrfifo_standalonE \
|
||||||
-o cdrskin/cdrfifo \
|
-o cdrskin/cdrfifo \
|
||||||
cdrskin/cdrfifo.c
|
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
|
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
|
if test "$do_strip" = 1
|
||||||
then
|
then
|
||||||
echo "stripping result cdrskin/cdrskin"
|
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.
|
cdrskin does not contain any bytes copied from cdrecord's sources.
|
||||||
Many bytes have been copied from the message output of cdrecord
|
Many bytes have been copied from the message output of cdrecord
|
||||||
runs, though. The most comprehensive technical overview of cdrskin
|
runs, though. The most comprehensive technical overview of cdrskin
|
||||||
can be found in cdrskin/README . Online available as :
|
can be found in cdrskin/README .
|
||||||
http://libburn.pykix.org/browser/trunk/cdrskin/README?format=raw
|
|
||||||
|
|
||||||
About libburn API for burning CD: http://libburn-api.pykix.org
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------
|
--------------------------------------------------------------------------
|
||||||
|
|
||||||
Appending sessions to an unclosed CD is restricted to write mode TAO.
|
cdrskin with CD media fails to match its paragon cdrecord on three major
|
||||||
(Users who have a burner which succeeds with a follow-up session via
|
fields: convenient TAO burn mode, multi session, audio features.
|
||||||
cdrecord -sao : please contact us.)
|
|
||||||
|
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
|
cdrskin does not provide DVD burning yet. See advise to use dvd+rw-tools
|
||||||
at the end of this text.
|
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.
|
which provide rather exotic unique features of cdrskin.
|
||||||
|
|
||||||
The cdrecord-compatible options are listed in the output of
|
The cdrecord-compatible options are listed in the output of
|
||||||
{{{
|
|
||||||
cdrskin -help
|
cdrskin -help
|
||||||
}}}
|
|
||||||
where the option "help" has *one* dash.
|
where the option "help" has *one* dash.
|
||||||
For these options you may expect program behavior that is roughly the
|
For these options you may expect program behavior that is roughly the
|
||||||
same as described in original man 1 cdrecord .
|
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
|
Online: http://cdrecord.berlios.de/old/private/man/cdrecord-2.0.html
|
||||||
|
|
||||||
The cdrskin-specific options are listed by
|
The cdrskin-specific options are listed by
|
||||||
{{{
|
|
||||||
cdrskin --help
|
cdrskin --help
|
||||||
}}}
|
|
||||||
where the option "help" has *two* dashes.
|
where the option "help" has *two* dashes.
|
||||||
|
|
||||||
Those have no man page yet. Some are very experimental and should only be
|
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
|
--devices allows the sysadmin to scan the system for possible drives
|
||||||
and displays their detected properties.
|
and displays their detected properties.
|
||||||
The drives are listed one per line, with fields:
|
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'
|
0 dev='/dev/sg0' rwrw-- : 'HL-DT-ST' 'DVDRAM GSA-4082B'
|
||||||
}}}
|
|
||||||
This feature is valuable since cdrskin -scanbus will not give you
|
This feature is valuable since cdrskin -scanbus will not give you
|
||||||
the device file name and its current permissions.
|
the device file name and its current permissions.
|
||||||
cdrskin will accept of course the proposed dev= option as address
|
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
|
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
|
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.
|
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
|
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.
|
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
|
--fifo_start_empty makes cdrskin start burning without waiting for the
|
||||||
is read rather than waiting for the FIFO to be completely full resp. the data
|
FIFO to be full resp. the data stream to end. It can make use of the
|
||||||
stream to end. It risks a few drive buffer underruns at the beginning of burn
|
seconds spend with drive preparation and lead-in, it risks a few drive
|
||||||
- but modern drives stand this.
|
buffer underruns at the beginning of burn - but modern drives stand this.
|
||||||
|
|
||||||
Usage examples:
|
|
||||||
{{{
|
|
||||||
cdrskin ... fs=128m fifo_start_at=20m ...
|
|
||||||
cdrskin ... fifo_start_at=0 ...
|
|
||||||
}}}
|
|
||||||
|
|
||||||
Note: no FIFO can give you better average throughput than the average
|
Note: no FIFO can give you better average throughput than the average
|
||||||
throughput of the data source and the throughput of the burner.
|
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 -
|
-tao and defines a default track size to be used if - as custom with -tao -
|
||||||
no option tsize=# is given.
|
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
|
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.
|
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
|
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:
|
DVD advise:
|
||||||
|
|
||||||
For burning of DVD media the cdrskin project currently advises to use
|
For burning of DVD media the cdrskin project currently advises to use
|
||||||
|
87
configure.ac
87
configure.ac
@ -1,30 +1,12 @@
|
|||||||
|
AC_INIT([libburn], [0.2.2], [http://libburn.pykix.org])
|
||||||
dnl library version info
|
|
||||||
m4_define([burn_interface_age], [0])
|
|
||||||
m4_define([burn_binary_age], [0])
|
|
||||||
|
|
||||||
dnl version info
|
|
||||||
m4_define([burn_major_version], [0])
|
|
||||||
m4_define([burn_minor_version], [2])
|
|
||||||
m4_define([burn_micro_version], [6])
|
|
||||||
m4_define([burn_nano_version], [3])
|
|
||||||
m4_define([burn_version], [burn_major_version().burn_minor_version().burn_micro_version()ifelse(burn_nano_version(), [], [], [.burn_nano_version()])])
|
|
||||||
|
|
||||||
AC_INIT([libburn], [burn_version], [http://libburnia.pykix.org])
|
|
||||||
AC_PREREQ([2.50])
|
AC_PREREQ([2.50])
|
||||||
dnl AC_CONFIG_HEADER([config.h])
|
dnl AC_CONFIG_HEADER([config.h])
|
||||||
|
|
||||||
AC_CANONICAL_HOST()
|
AC_CANONICAL_HOST
|
||||||
AC_CANONICAL_TARGET()
|
AC_CANONICAL_TARGET
|
||||||
|
|
||||||
AM_INIT_AUTOMAKE([subdir-objects])
|
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 Making releases:
|
||||||
dnl BURN_MICRO_VERSION += 1;
|
dnl BURN_MICRO_VERSION += 1;
|
||||||
dnl BURN_INTERFACE_AGE += 1;
|
dnl BURN_INTERFACE_AGE += 1;
|
||||||
@ -35,62 +17,62 @@ dnl set BURN_BINARY_AGE and BURN_INTERFACE_AGE to 0.
|
|||||||
dnl
|
dnl
|
||||||
dnl if MAJOR or MINOR version changes, be sure to change AC_INIT above to match
|
dnl if MAJOR or MINOR version changes, be sure to change AC_INIT above to match
|
||||||
dnl
|
dnl
|
||||||
BURN_MAJOR_VERSION=burn_major_version()
|
BURN_MAJOR_VERSION=0
|
||||||
BURN_MINOR_VERSION=burn_minor_version()
|
BURN_MINOR_VERSION=2
|
||||||
BURN_MICRO_VERSION=burn_micro_version()
|
BURN_MICRO_VERSION=2
|
||||||
BURN_INTERFACE_AGE=burn_interface_age()
|
BURN_INTERFACE_AGE=0
|
||||||
BURN_BINARY_AGE=burn_binary_age()
|
BURN_BINARY_AGE=0
|
||||||
|
BURN_VERSION=$BURN_MAJOR_VERSION.$BURN_MINOR_VERSION.$BURN_MICRO_VERSION
|
||||||
|
|
||||||
AC_SUBST([BURN_MAJOR_VERSION])
|
AC_SUBST(BURN_MAJOR_VERSION)
|
||||||
AC_SUBST([BURN_MINOR_VERSION])
|
AC_SUBST(BURN_MINOR_VERSION)
|
||||||
AC_SUBST([BURN_MICRO_VERSION])
|
AC_SUBST(BURN_MICRO_VERSION)
|
||||||
|
AC_SUBST(BURN_INTERFACE_AGE)
|
||||||
|
AC_SUBST(BURN_BINARY_AGE)
|
||||||
|
AC_SUBST(BURN_VERSION)
|
||||||
|
|
||||||
dnl Libtool versioning
|
dnl Libtool versioning
|
||||||
|
LT_RELEASE=$BURN_MAJOR_VERSION.$BURN_MINOR_VERSION
|
||||||
LT_CURRENT=`expr $BURN_MICRO_VERSION - $BURN_INTERFACE_AGE`
|
LT_CURRENT=`expr $BURN_MICRO_VERSION - $BURN_INTERFACE_AGE`
|
||||||
LT_REVISION=$BURN_INTERFACE_AGE
|
LT_REVISION=$BURN_INTERFACE_AGE
|
||||||
LT_AGE=`expr $BURN_BINARY_AGE - $BURN_INTERFACE_AGE`
|
LT_AGE=`expr $BURN_BINARY_AGE - $BURN_INTERFACE_AGE`
|
||||||
|
LT_CURRENT_MINUS_AGE=`expr $LT_CURRENT - $LT_AGE`
|
||||||
|
|
||||||
LT_VERSION_INFO=$LT_CURRENT:$LT_REVISION:$LT_AGE
|
AC_SUBST(LT_RELEASE)
|
||||||
AC_SUBST([LT_VERSION_INFO])
|
AC_SUBST(LT_CURRENT)
|
||||||
|
AC_SUBST(LT_REVISION)
|
||||||
|
AC_SUBST(LT_AGE)
|
||||||
|
AC_SUBST(LT_CURRENT_MINUS_AGE)
|
||||||
|
|
||||||
AC_PREFIX_DEFAULT([/usr/local])
|
AC_PREFIX_DEFAULT([/usr/local])
|
||||||
test "$prefix" = "NONE" && prefix=$ac_default_prefix
|
test "$prefix" = "NONE" && prefix=$ac_default_prefix
|
||||||
|
|
||||||
AM_MAINTAINER_MODE()
|
AM_MAINTAINER_MODE
|
||||||
|
|
||||||
AM_PROG_CC_C_O()
|
AM_PROG_CC_C_O
|
||||||
AC_C_CONST()
|
AC_C_CONST
|
||||||
AC_C_INLINE()
|
AC_C_INLINE
|
||||||
AC_C_BIGENDIAN()
|
AC_C_BIGENDIAN
|
||||||
|
|
||||||
dnl Large file support
|
dnl Large file support
|
||||||
AC_SYS_LARGEFILE()
|
AC_SYS_LARGEFILE
|
||||||
dnl AC_FUNC_FSEEKO()
|
AC_FUNC_FSEEKO
|
||||||
AC_CHECK_FUNC([fseeko])
|
|
||||||
if test ! $ac_cv_func_fseeko; then
|
if test ! $ac_cv_func_fseeko; then
|
||||||
AC_ERROR([Libburn requires largefile support.])
|
AC_ERROR([Libburn requires largefile support.])
|
||||||
fi
|
fi
|
||||||
|
|
||||||
AC_PROG_LIBTOOL()
|
AC_PROG_LIBTOOL
|
||||||
AC_SUBST([LIBTOOL_DEPS])
|
AC_SUBST(LIBTOOL_DEPS)
|
||||||
LIBTOOL="$LIBTOOL --silent"
|
LIBTOOL="$LIBTOOL --silent"
|
||||||
|
|
||||||
AC_PROG_INSTALL()
|
AC_PROG_INSTALL
|
||||||
|
|
||||||
AC_CHECK_HEADERS()
|
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
|
THREAD_LIBS=-lpthread
|
||||||
AC_SUBST([THREAD_LIBS])
|
AC_SUBST(THREAD_LIBS)
|
||||||
|
|
||||||
TARGET_SHIZZLE
|
TARGET_SHIZZLE
|
||||||
AC_SUBST([ARCH])
|
|
||||||
AC_SUBST([LIBBURN_ARCH_LIBS])
|
|
||||||
|
|
||||||
dnl Add compiler-specific flags
|
dnl Add compiler-specific flags
|
||||||
|
|
||||||
@ -116,5 +98,6 @@ AC_CONFIG_FILES([
|
|||||||
doc/doxygen.conf
|
doc/doxygen.conf
|
||||||
version.h
|
version.h
|
||||||
libburn-1.pc
|
libburn-1.pc
|
||||||
|
libisofs-1.pc
|
||||||
])
|
])
|
||||||
AC_OUTPUT()
|
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
|
These are libraries, language bindings, and middleware binaries which emulate
|
||||||
classical (and valuable) Linux tools.
|
classical (and valuable) Linux tools.
|
||||||
|
|
||||||
Our scope is currently Linux 2.4 and 2.6 only. For ports to other systems
|
Our scope is currently Linux 2.4 and 2.6 and we will have a hard time to widen
|
||||||
we would need : login on a development machine resp. a live OS on CD or DVD,
|
this for now, because of our history. The project could need advise from or
|
||||||
advise from a system person about the equivalent of Linux sg or FreeBSD CAM,
|
membership of skilled kernel people and people who know how to talk CD/DVD
|
||||||
volunteers for testing of realistic use cases.
|
drives into doing things.
|
||||||
|
|
||||||
We do have a workable code base for burning data CDs, though. The burn API is
|
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
|
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
|
@subsection libburner-help Libburner --help
|
||||||
<pre>
|
<pre>
|
||||||
Usage: test/libburner
|
Usage: test/libburner
|
||||||
[--drive <address>|<driveno>|"-"] [--audio]
|
[--drive <address>|<driveno>|"-"]
|
||||||
[--blank_fast|--blank_full] [--try_to_simulate]
|
[--verbose <level>] [--blank_fast|--blank_full]
|
||||||
[<one or more imagefiles>|"-"]
|
[--burn_for_real|--try_to_simulate] [--stdin_size <bytes>]
|
||||||
|
[<imagefile>|"-"]
|
||||||
Examples
|
Examples
|
||||||
A bus scan (needs rw-permissions to see a drive):
|
A bus scan (needs rw-permissions to see a drive):
|
||||||
test/libburner --drive -
|
test/libburner --drive -
|
||||||
Burn a file to drive chosen by number:
|
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:
|
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):
|
Blank a used CD-RW (is combinable with burning in one run):
|
||||||
test/libburner --drive /dev/hdc --blank_fast
|
test/libburner --drive 0 --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
|
|
||||||
Burn a compressed afio archive on-the-fly, pad up to 700 MB:
|
Burn a compressed afio archive on-the-fly, pad up to 700 MB:
|
||||||
( cd my_directory ; find . -print | afio -oZ - ) | \
|
( cd my_directory ; find . -print | afio -oZ - ) | \
|
||||||
test/libburner --drive /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
|
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.
|
Program tar would need a clean EOF which our padded CD cannot deliver.
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
@ -56,8 +56,8 @@ WARN_LOGFILE =
|
|||||||
#---------------------------------------------------------------------------
|
#---------------------------------------------------------------------------
|
||||||
# configuration options related to the input files
|
# configuration options related to the input files
|
||||||
#---------------------------------------------------------------------------
|
#---------------------------------------------------------------------------
|
||||||
INPUT = libburn doc test
|
INPUT = libburn libisofs doc test
|
||||||
FILE_PATTERNS = libburn.h comments libburner.c
|
FILE_PATTERNS = libburn.h libisofs.h comments libburner.c
|
||||||
RECURSIVE = NO
|
RECURSIVE = NO
|
||||||
EXCLUDE =
|
EXCLUDE =
|
||||||
EXCLUDE_SYMLINKS = NO
|
EXCLUDE_SYMLINKS = NO
|
||||||
|
@ -7,6 +7,5 @@ Name: libburn
|
|||||||
Description: Disc reading/writing library
|
Description: Disc reading/writing library
|
||||||
Version: @VERSION@
|
Version: @VERSION@
|
||||||
Requires:
|
Requires:
|
||||||
Libs: -L${libdir} -lburn
|
Libs: -L${libdir} -lburn @THREAD_LIBS@
|
||||||
Libs.private: @THREAD_LIBS@ @LIBBURN_ARCH_LIBS@
|
|
||||||
Cflags: -I${includedir}/libburn
|
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 "write.h"
|
||||||
#include "options.h"
|
#include "options.h"
|
||||||
#include "async.h"
|
#include "async.h"
|
||||||
#include "init.h"
|
|
||||||
|
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
|
#include <assert.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
/*
|
|
||||||
#include <a ssert.h>
|
|
||||||
*/
|
|
||||||
#include "libdax_msgs.h"
|
|
||||||
extern struct libdax_msgs *libdax_messenger;
|
|
||||||
|
|
||||||
#define SCAN_GOING() (workers && !workers->drive)
|
#define SCAN_GOING() (workers && !workers->drive)
|
||||||
|
|
||||||
typedef void *(*WorkerFunc) (void *);
|
typedef void *(*WorkerFunc) (void *);
|
||||||
@ -106,13 +100,7 @@ static void remove_worker(pthread_t th)
|
|||||||
free(a);
|
free(a);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
assert(a != NULL); /* wasn't found.. this should not be possible */
|
||||||
/* 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *scan_worker_func(struct w_list *w)
|
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;
|
struct scan_opts o;
|
||||||
int ret = 0;
|
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! */
|
/* cant be anything working! */
|
||||||
|
assert(!(workers && workers->drive));
|
||||||
/* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!workers) {
|
if (!workers) {
|
||||||
/* start it */
|
/* 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.drives = drives;
|
||||||
o.n_drives = n_drives;
|
o.n_drives = n_drives;
|
||||||
o.done = 0;
|
o.done = 0;
|
||||||
@ -170,17 +128,7 @@ drive_is_active:;
|
|||||||
/* its done */
|
/* its done */
|
||||||
ret = workers->u.scan.done;
|
ret = workers->u.scan.done;
|
||||||
remove_worker(workers->thread);
|
remove_worker(workers->thread);
|
||||||
|
assert(workers == NULL);
|
||||||
/* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
/* still going */
|
/* still going */
|
||||||
}
|
}
|
||||||
@ -198,25 +146,9 @@ void burn_disc_erase(struct burn_drive *drive, int fast)
|
|||||||
{
|
{
|
||||||
struct erase_opts o;
|
struct erase_opts o;
|
||||||
|
|
||||||
/* ts A61006 */
|
assert(drive);
|
||||||
/* a ssert(drive); */
|
assert(!SCAN_GOING());
|
||||||
/* a ssert(!SCAN_GOING()); */
|
assert(!find_worker(drive));
|
||||||
/* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
o.drive = drive;
|
o.drive = drive;
|
||||||
o.fast = fast;
|
o.fast = fast;
|
||||||
@ -239,29 +171,8 @@ void burn_disc_write(struct burn_write_opts *opts, struct burn_disc *disc)
|
|||||||
{
|
{
|
||||||
struct write_opts o;
|
struct write_opts o;
|
||||||
|
|
||||||
/* ts A61006 */
|
assert(!SCAN_GOING());
|
||||||
/* a ssert(!SCAN_GOING()); */
|
assert(!find_worker(opts->drive));
|
||||||
/* 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;
|
|
||||||
|
|
||||||
o.drive = opts->drive;
|
o.drive = opts->drive;
|
||||||
o.opts = opts;
|
o.opts = opts;
|
||||||
o.disc = disc;
|
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; -*- */
|
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <malloc.h>
|
||||||
#include <sys/stat.h>
|
|
||||||
|
|
||||||
/* #include <m alloc.h> ts A61013 : not in Linux man 3 malloc */
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
#include <assert.h>
|
||||||
/* ts A61007 */
|
|
||||||
/* #include <a ssert.h> */
|
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
@ -19,6 +11,7 @@
|
|||||||
#include "libburn.h"
|
#include "libburn.h"
|
||||||
#include "drive.h"
|
#include "drive.h"
|
||||||
#include "transport.h"
|
#include "transport.h"
|
||||||
|
#include "message.h"
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
#include "init.h"
|
#include "init.h"
|
||||||
#include "toc.h"
|
#include "toc.h"
|
||||||
@ -27,22 +20,10 @@
|
|||||||
#include "structure.h"
|
#include "structure.h"
|
||||||
#include "back_hacks.h"
|
#include "back_hacks.h"
|
||||||
|
|
||||||
#include "libdax_msgs.h"
|
|
||||||
extern struct libdax_msgs *libdax_messenger;
|
|
||||||
|
|
||||||
static struct burn_drive drive_array[255];
|
static struct burn_drive drive_array[255];
|
||||||
static int drivetop = -1;
|
static int drivetop = -1;
|
||||||
|
|
||||||
/* ts A61021 : the unspecific part of sg.c:enumerate_common()
|
int burn_drive_is_open(struct burn_drive *d);
|
||||||
*/
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ts A60904 : ticket 62, contribution by elmom */
|
/* ts A60904 : ticket 62, contribution by elmom */
|
||||||
/* splitting former burn_drive_free() (which freed all, into two calls) */
|
/* splitting former burn_drive_free() (which freed all, into two calls) */
|
||||||
@ -52,11 +33,10 @@ void burn_drive_free(struct burn_drive *d)
|
|||||||
return;
|
return;
|
||||||
/* ts A60822 : close open fds before forgetting them */
|
/* ts A60822 : close open fds before forgetting them */
|
||||||
if (burn_drive_is_open(d))
|
if (burn_drive_is_open(d))
|
||||||
d->release(d);
|
close(d->fd);
|
||||||
free((void *) d->idata);
|
free((void *) d->idata);
|
||||||
free((void *) d->mdata);
|
free((void *) d->mdata);
|
||||||
if(d->toc_entry != NULL)
|
free((void *) d->toc_entry);
|
||||||
free((void *) d->toc_entry);
|
|
||||||
free(d->devname);
|
free(d->devname);
|
||||||
d->global_index = -1;
|
d->global_index = -1;
|
||||||
}
|
}
|
||||||
@ -75,8 +55,12 @@ void burn_drive_free_all(void)
|
|||||||
/* ts A60822 */
|
/* ts A60822 */
|
||||||
int burn_drive_is_open(struct burn_drive *d)
|
int burn_drive_is_open(struct burn_drive *d)
|
||||||
{
|
{
|
||||||
/* ts A61021 : moved decision to sg.c */
|
/* a bit more detailed case distinction than needed */
|
||||||
return d->drive_is_open(d);
|
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->load(d);
|
||||||
|
|
||||||
d->lock(d);
|
d->lock(d);
|
||||||
d->start_unit(d);
|
d->status = BURN_DISC_BLANK;
|
||||||
|
|
||||||
/* ts A61020 : this was BURN_DISC_BLANK as pure guess */
|
|
||||||
d->status = BURN_DISC_UNREADY;
|
|
||||||
|
|
||||||
if (d->mdata->cdr_write || d->mdata->cdrw_write ||
|
if (d->mdata->cdr_write || d->mdata->cdrw_write ||
|
||||||
d->mdata->dvdr_write || d->mdata->dvdram_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;
|
old_erasable = new_erasable;
|
||||||
|
|
||||||
d->read_disc_info(d);
|
d->read_disc_info(d);
|
||||||
if(d->status == BURN_DISC_UNSUITABLE)
|
|
||||||
break;
|
|
||||||
|
|
||||||
new_speed = burn_drive_get_write_speed(d);
|
new_speed = burn_drive_get_write_speed(d);
|
||||||
new_erasable = burn_disc_erasable(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)
|
void burn_drive_release(struct burn_drive *d, int le)
|
||||||
{
|
{
|
||||||
if (d->released) {
|
if (d->released)
|
||||||
/* ts A61007 */
|
burn_print(1, "second release on drive!\n");
|
||||||
/* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ts A61007 */
|
|
||||||
/* ts A60906: one should not assume BURN_DRIVE_IDLE == 0 */
|
/* ts A60906: one should not assume BURN_DRIVE_IDLE == 0 */
|
||||||
/* a ssert(d->busy == BURN_DRIVE_IDLE); */
|
assert(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;
|
|
||||||
|
|
||||||
d->unlock(d);
|
d->unlock(d);
|
||||||
if (le)
|
if (le)
|
||||||
@ -346,7 +260,7 @@ void burn_drive_release(struct burn_drive *d, int le)
|
|||||||
|
|
||||||
d->status = BURN_DISC_UNREADY;
|
d->status = BURN_DISC_UNREADY;
|
||||||
d->released = 1;
|
d->released = 1;
|
||||||
if (d->toc_entry != NULL)
|
if (d->toc_entry)
|
||||||
free(d->toc_entry);
|
free(d->toc_entry);
|
||||||
d->toc_entry = NULL;
|
d->toc_entry = NULL;
|
||||||
d->toc_entries = 0;
|
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)
|
void burn_wait_all(void)
|
||||||
{
|
{
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
@ -391,20 +285,16 @@ void burn_wait_all(void)
|
|||||||
if (d->global_index==-1)
|
if (d->global_index==-1)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
a ssert(d->released);
|
assert(d->released);
|
||||||
}
|
}
|
||||||
if (!finished)
|
if (!finished)
|
||||||
sleep(1);
|
sleep(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
void burn_disc_erase_sync(struct burn_drive *d, int fast)
|
void burn_disc_erase_sync(struct burn_drive *d, int fast)
|
||||||
{
|
{
|
||||||
/* ts A60924 : libburn/message.c gets obsoleted
|
|
||||||
burn_message_clear_queue();
|
burn_message_clear_queue();
|
||||||
*/
|
|
||||||
|
|
||||||
burn_print(1, "erasing drive %s %s\n", d->idata->vendor,
|
burn_print(1, "erasing drive %s %s\n", d->idata->vendor,
|
||||||
d->idata->product);
|
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)
|
enum burn_disc_status burn_disc_get_status(struct burn_drive *d)
|
||||||
{
|
{
|
||||||
/* ts A61007 */
|
assert(!d->released);
|
||||||
/* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
return d->status;
|
return d->status;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -474,17 +354,14 @@ void burn_drive_cancel(struct burn_drive *d)
|
|||||||
pthread_mutex_unlock(&d->access_lock);
|
pthread_mutex_unlock(&d->access_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ts A61007 : defunct because unused */
|
|
||||||
#if 0
|
|
||||||
int burn_drive_get_block_types(struct burn_drive *d,
|
int burn_drive_get_block_types(struct burn_drive *d,
|
||||||
enum burn_write_types write_type)
|
enum burn_write_types write_type)
|
||||||
{
|
{
|
||||||
burn_print(12, "write type: %d\n", 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));
|
(write_type <= BURN_WRITE_RAW));
|
||||||
return d->block_types[write_type];
|
return d->block_types[write_type];
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
static void strip_spaces(char *str)
|
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)
|
static int drive_getcaps(struct burn_drive *d, struct burn_drive_info *out)
|
||||||
{
|
{
|
||||||
struct burn_scsi_inquiry_data *id;
|
struct scsi_inquiry_data *id;
|
||||||
|
|
||||||
/* ts A61007 : now prevented in enumerate_common() */
|
|
||||||
#if 0
|
|
||||||
a ssert(d->idata);
|
|
||||||
a ssert(d->mdata);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
assert(d->idata);
|
||||||
|
assert(d->mdata);
|
||||||
if (!d->idata->valid || !d->mdata->valid)
|
if (!d->idata->valid || !d->mdata->valid)
|
||||||
return 0;
|
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));
|
memcpy(out->vendor, id->vendor, sizeof(id->vendor));
|
||||||
strip_spaces(out->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 int scanning = 0, scanned = 0, found = 0;
|
||||||
static unsigned num_scanned = 0, count = 0;
|
static unsigned num_scanned = 0, count = 0;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
#ifdef Libburn_ticket_62_enable_redundant_asserT
|
||||||
|
struct burn_drive *d;
|
||||||
|
#endif
|
||||||
|
|
||||||
/* ts A61007 : moved up to burn_drive_scan() */
|
assert(burn_running);
|
||||||
/* a ssert(burn_running); */
|
|
||||||
|
|
||||||
if (!scanning) {
|
if (!scanning) {
|
||||||
scanning = 1;
|
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 */
|
/* make sure the drives aren't in use */
|
||||||
burn_wait_all(); /* make sure the queue cleans up
|
burn_wait_all(); /* make sure the queue cleans up
|
||||||
before checking for the released
|
before checking for the released
|
||||||
state */
|
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 */
|
/* refresh the lib's drives */
|
||||||
|
sg_enumerate();
|
||||||
/* ts A61115 : formerly sg_enumerate(); ata_enumerate(); */
|
ata_enumerate();
|
||||||
scsi_enumerate_drives();
|
|
||||||
|
|
||||||
count = burn_drive_count();
|
count = burn_drive_count();
|
||||||
if (count)
|
if (count)
|
||||||
*drives =
|
*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
|
burn_drive_info is not the manager of burn_drive but only its
|
||||||
spokesperson. To my knowlege drive_infos from burn_drive_scan()
|
spokesperson. To my knowlege drive_infos from burn_drive_scan()
|
||||||
are not memorized globally. */
|
are not memorized globally. */
|
||||||
if(drive_infos != NULL)
|
if(drive_infos != NULL) /* and this NULL test is deadly necessary */
|
||||||
free((void *) drive_infos);
|
free((void *) drive_infos);
|
||||||
|
|
||||||
burn_drive_free_all();
|
burn_drive_free_all();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ts A61001 : internal call */
|
/* Experimental API call */
|
||||||
int burn_drive_forget(struct burn_drive *d, int force)
|
int burn_drive_info_forget(struct burn_drive_info *info, int force)
|
||||||
{
|
{
|
||||||
int occup;
|
int occup;
|
||||||
|
struct burn_drive *d;
|
||||||
|
|
||||||
|
d = info->drive;
|
||||||
occup = burn_drive_is_occupied(d);
|
occup = burn_drive_is_occupied(d);
|
||||||
/*
|
/*
|
||||||
fprintf(stderr, "libburn: experimental: occup == %d\n",occup);
|
fprintf(stderr, "libburn: experimental: occup == %d\n",occup);
|
||||||
@ -657,18 +536,8 @@ int burn_drive_forget(struct burn_drive *d, int force)
|
|||||||
return 1;
|
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)
|
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++;
|
d->disc->refcnt++;
|
||||||
return d->disc;
|
return d->disc;
|
||||||
}
|
}
|
||||||
@ -700,12 +569,6 @@ int burn_drive_get_write_speed(struct burn_drive *d)
|
|||||||
return d->mdata->max_write_speed;
|
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 */
|
/* ts A51221 */
|
||||||
static char *enumeration_whitelist[BURN_DRIVE_WHITELIST_LEN];
|
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",
|
fprintf(stderr,"libburn: experimental: burn_drive_scan_and_grab(%s)\n",
|
||||||
adr);
|
adr);
|
||||||
*/
|
*/
|
||||||
while (1) {
|
while (!burn_drive_scan(drive_infos, &n_drives))
|
||||||
ret = burn_drive_scan(drive_infos, &n_drives);
|
|
||||||
if (ret < 0)
|
|
||||||
return -1;
|
|
||||||
if (ret > 0)
|
|
||||||
break;
|
|
||||||
usleep(1002);
|
usleep(1002);
|
||||||
}
|
|
||||||
if (n_drives <= 0)
|
if (n_drives <= 0)
|
||||||
return 0;
|
return 0;
|
||||||
/*
|
/*
|
||||||
fprintf(stderr, "libburn: experimental: n_drives == %d\n",n_drives);
|
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);
|
ret = burn_drive_grab(drive_infos[0]->drive, load);
|
||||||
if (ret != 1)
|
if (ret != 1)
|
||||||
return -1;
|
return -1;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ts A60925 */
|
/* ts A60823 */
|
||||||
/** 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 */
|
|
||||||
/** Inquire the persistent address of the given drive. */
|
/** Inquire the persistent address of the given drive. */
|
||||||
int burn_drive_get_adr(struct burn_drive_info *drive_info, char adr[])
|
int burn_drive_get_adr(struct burn_drive_info *drive_info, char adr[])
|
||||||
{
|
{
|
||||||
int ret;
|
assert(strlen(drive_info->location) < BURN_DRIVE_ADR_LEN);
|
||||||
|
strcpy(adr,drive_info->location);
|
||||||
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;
|
|
||||||
return 1;
|
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))
|
SESSION_LEADOUT_ENTRY((d), (d)->toc->sessions-1))
|
||||||
|
|
||||||
struct burn_drive *burn_drive_register(struct burn_drive *);
|
struct burn_drive *burn_drive_register(struct burn_drive *);
|
||||||
int burn_drive_unregister(struct burn_drive *d);
|
|
||||||
|
|
||||||
unsigned int burn_drive_count(void);
|
unsigned int burn_drive_count(void);
|
||||||
|
void burn_wait_all(void);
|
||||||
/* ts A61007 */
|
|
||||||
/* void burn_wait_all(void); */
|
|
||||||
int burn_drives_are_clear(void);
|
|
||||||
|
|
||||||
int burn_sector_length_write(struct burn_drive *d);
|
int burn_sector_length_write(struct burn_drive *d);
|
||||||
int burn_track_control(struct burn_drive *d, int);
|
int burn_track_control(struct burn_drive *d, int);
|
||||||
void burn_write_empty_sector(int fd);
|
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,
|
int burn_drive_get_block_types(struct burn_drive *d,
|
||||||
enum burn_write_types write_type);
|
enum burn_write_types write_type);
|
||||||
|
|
||||||
|
/* ts A60822 */
|
||||||
int burn_drive_is_open(struct burn_drive *d);
|
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 */
|
#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; -*- */
|
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <assert.h>
|
||||||
/* ts A61007 */
|
|
||||||
/* #include <a ssert.h> */
|
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "init.h"
|
#include "init.h"
|
||||||
#include "sg.h"
|
#include "sg.h"
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
@ -19,11 +14,6 @@
|
|||||||
#define BURN_BACK_HACKS_INIT 1
|
#define BURN_BACK_HACKS_INIT 1
|
||||||
#include "back_hacks.h"
|
#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;
|
int burn_running = 0;
|
||||||
|
|
||||||
/* ts A60813 : wether to use O_EXCL and/or O_NONBLOCK in libburn/sg.c */
|
/* 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;
|
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 burn_initialize(void)
|
||||||
{
|
{
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (burn_running)
|
if (burn_running)
|
||||||
return 1;
|
return 1;
|
||||||
ret = burn_msgs_initialize();
|
|
||||||
if (ret <= 0)
|
|
||||||
return 0;
|
|
||||||
burn_running = 1;
|
burn_running = 1;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void burn_finish(void)
|
void burn_finish(void)
|
||||||
{
|
{
|
||||||
/* ts A61007 : assume no messageing system */
|
assert(burn_running);
|
||||||
/* a ssert(burn_running); */
|
|
||||||
if (!burn_running)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* ts A61007 */
|
burn_wait_all();
|
||||||
/* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ts A60904 : ticket 62, contribution by elmom : name addon "_all" */
|
/* ts A60904 : ticket 62, contribution by elmom : name addon "_all" */
|
||||||
burn_drive_free_all();
|
burn_drive_free_all();
|
||||||
|
|
||||||
/* ts A60924 : ticket 74 */
|
|
||||||
libdax_msgs_destroy(&libdax_messenger,0);
|
|
||||||
|
|
||||||
burn_running = 0;
|
burn_running = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ts A60813 */
|
/* 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)
|
void burn_preset_device_open(int exclusive, int blocking, int abort_on_busy)
|
||||||
{
|
{
|
||||||
/* ts A61007 */
|
assert(burn_running);
|
||||||
/* a ssert(burn_running); */
|
|
||||||
if (!burn_running)
|
|
||||||
return;
|
|
||||||
|
|
||||||
burn_sg_open_o_excl= exclusive;
|
burn_sg_open_o_excl= !!exclusive;
|
||||||
burn_sg_open_o_nonblock= !blocking;
|
burn_sg_open_o_nonblock= !blocking;
|
||||||
burn_sg_open_abort_busy= !!abort_on_busy;
|
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.
|
expressing a file or stream size.
|
||||||
|
|
||||||
XXX we should enforce 64-bitness for off_t
|
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>
|
#include <sys/types.h>
|
||||||
|
|
||||||
@ -41,10 +40,6 @@ struct burn_session;
|
|||||||
/** References a single track on a disc */
|
/** References a single track on a disc */
|
||||||
struct burn_track;
|
struct burn_track;
|
||||||
|
|
||||||
/* ts A61111 */
|
|
||||||
/** References a set of write parameters */
|
|
||||||
struct burn_write_opts;
|
|
||||||
|
|
||||||
/** Session format for normal audio or data discs */
|
/** Session format for normal audio or data discs */
|
||||||
#define BURN_CDROM 0
|
#define BURN_CDROM 0
|
||||||
/** Session format for obsolete CD-I discs */
|
/** Session format for obsolete CD-I discs */
|
||||||
@ -177,17 +172,39 @@ enum burn_disc_status
|
|||||||
/** There is an incomplete disc in the drive */
|
/** There is an incomplete disc in the drive */
|
||||||
BURN_DISC_APPENDABLE,
|
BURN_DISC_APPENDABLE,
|
||||||
/** There is a disc with data on it in the drive */
|
/** There is a disc with data on it in the drive */
|
||||||
BURN_DISC_FULL,
|
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
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** 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 */
|
/** Possible data source return values */
|
||||||
enum burn_source_status
|
enum burn_source_status
|
||||||
@ -221,16 +238,7 @@ enum burn_drive_status
|
|||||||
/** The drive is erasing a disc */
|
/** The drive is erasing a disc */
|
||||||
BURN_DRIVE_ERASING,
|
BURN_DRIVE_ERASING,
|
||||||
/** The drive is being grabbed */
|
/** The drive is being grabbed */
|
||||||
BURN_DRIVE_GRABBING,
|
BURN_DRIVE_GRABBING
|
||||||
|
|
||||||
/* ts A61102 */
|
|
||||||
/** The drive gets written zeroes before the track payload data */
|
|
||||||
BURN_DRIVE_WRITING_PREGAP,
|
|
||||||
/** The drive is told to close a track (TAO only) */
|
|
||||||
BURN_DRIVE_CLOSING_TRACK,
|
|
||||||
/** The drive is told to close a session (TAO only) */
|
|
||||||
BURN_DRIVE_CLOSING_SESSION
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Information about a track on a disc - this is from the q sub channel of the
|
/** 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
|
/** This is currently the string which is used as persistent
|
||||||
drive address. But be warned: there is NO GUARANTEE that this
|
drive address. But be warned: there is NO GUARANTEE that this
|
||||||
will stay so. Always use function burn_drive_get_adr() to
|
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 */
|
/** Can the drive read DVD-RAM discs */
|
||||||
unsigned int read_dvdram:1;
|
unsigned int read_dvdram:1;
|
||||||
@ -370,6 +378,29 @@ struct burn_drive_info
|
|||||||
struct burn_drive *drive;
|
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.
|
/** Operation progress report. All values are 0 based indices.
|
||||||
* */
|
* */
|
||||||
@ -388,26 +419,10 @@ struct burn_progress {
|
|||||||
int index;
|
int index;
|
||||||
/** The starting logical block address */
|
/** The starting logical block address */
|
||||||
int start_sector;
|
int start_sector;
|
||||||
/** On write: The number of sectors.
|
/** The number of sector */
|
||||||
On blank: 0x10000 as upper limit for relative progress steps */
|
|
||||||
int sectors;
|
int sectors;
|
||||||
/** On write: The current sector being processed.
|
/** The current sector being processed */
|
||||||
On blank: Relative progress steps 0 to 0x10000 */
|
|
||||||
int sector;
|
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.
|
/** Initialize the library.
|
||||||
@ -429,36 +444,7 @@ int burn_initialize(void);
|
|||||||
*/
|
*/
|
||||||
void burn_finish(void);
|
void burn_finish(void);
|
||||||
|
|
||||||
|
/** Set the verbosity level of the library. The default value is 0, which means
|
||||||
/* 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
|
|
||||||
that nothing is output on stderr. The more you increase this, the more
|
that nothing is output on stderr. The more you increase this, the more
|
||||||
debug output should be displayed on stderr for you.
|
debug output should be displayed on stderr for you.
|
||||||
@param level The verbosity level desired. 0 for nothing, higher positive
|
@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.
|
after burn_initialize() and before any bus scan. But not mandatory at all.
|
||||||
Parameter value 1 enables a feature, 0 disables.
|
Parameter value 1 enables a feature, 0 disables.
|
||||||
Default is (1,0,0). Have a good reason before you change it.
|
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)
|
and try to mark them busy if opened sucessfully. (O_EXCL)
|
||||||
There are kernels which simply don't care about O_EXCL.
|
There are kernels which simply don't care about O_EXCL.
|
||||||
Some have it off, some have it on, some are switchable.
|
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
|
@param blocking Try to wait for drives which do not open immediately but
|
||||||
also do not return an error as well. (O_NONBLOCK)
|
also do not return an error as well. (O_NONBLOCK)
|
||||||
This might stall indefinitely with /dev/hdX hard disks.
|
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);
|
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 */
|
/* ts A60823 */
|
||||||
/** Aquire a drive with known persistent address.This is the sysadmin friendly
|
/** 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.
|
/** 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
|
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.
|
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
|
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
|
before burn_finish(), and also before calling this function
|
||||||
burn_drive_scan() again.
|
burn_drive_scan() again.
|
||||||
@param n_drives Returns the number of drive items in drive_infos.
|
@param n_drives Returns the number of drive items in drive_infos.
|
||||||
@return 0 while scanning is not complete
|
@return Zero while scanning is not complete; non-zero when it is finished.
|
||||||
>0 when it is finished sucessfully,
|
|
||||||
<0 when finished but failed.
|
|
||||||
*/
|
*/
|
||||||
int burn_drive_scan(struct burn_drive_info *drive_infos[],
|
int burn_drive_scan(struct burn_drive_info *drive_infos[],
|
||||||
unsigned int *n_drives);
|
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 drive_info The drive to inquire. Usually some &(drive_infos[driveno])
|
||||||
@param adr An application provided array of at least BURN_DRIVE_ADR_LEN
|
@param adr An application provided array of at least BURN_DRIVE_ADR_LEN
|
||||||
characters size. The persistent address gets copied to it.
|
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[]);
|
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,
|
/** Grab a drive. This must be done before the drive can be used (for reading,
|
||||||
writing, etc).
|
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
|
/** 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.
|
for details.
|
||||||
@param drive The drive to query for a disc.
|
@param drive The drive to query for a disc.
|
||||||
@return The status of the drive, or what kind of disc is in it.
|
@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);
|
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
|
/** Tells whether a disc can be erased or not
|
||||||
@return Non-zero means erasable
|
@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
|
/** Creates a write_opts struct for burning to the specified drive
|
||||||
must be freed with burn_write_opts_free
|
must be freed with burn_write_opts_free
|
||||||
@param drive The drive to write with
|
@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);
|
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);
|
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
|
must be freed with burn_write_opts_free
|
||||||
@param drive The drive to read from
|
@param drive The drive to read from
|
||||||
@return The read_opts
|
@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);
|
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
|
/** 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
|
grabbed successfully BEFORE calling this function. Always ensure that the
|
||||||
drive reports a status of BURN_DISC_FULL before calling this function.
|
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);
|
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
|
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 o The options for the writing operation.
|
||||||
@param disc The struct burn_disc * that described the disc to be created
|
@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,
|
void burn_track_define_data(struct burn_track *t, int offset, int tail,
|
||||||
int pad, int mode);
|
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
|
/** Set the ISRC details for a track
|
||||||
@param t The track to change
|
@param t The track to change
|
||||||
@param country the 2 char country code. Each character must be
|
@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
|
/** Get the drive's disc struct - free when done
|
||||||
@param d drive to query
|
@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);
|
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);
|
struct burn_source *burn_fd_source_new(int datafd, int subfd, off_t size);
|
||||||
|
|
||||||
/** Tells how long a track will be on disc
|
/** Tells how long a track will be on disc */
|
||||||
>>> NOTE: Not reliable with tracks of undefined length
|
|
||||||
*/
|
|
||||||
int burn_track_get_sectors(struct burn_track *);
|
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
|
/** Sets drive read and write speed
|
||||||
@param d The drive to set speed for
|
@param d The drive to set speed for
|
||||||
@param read Read speed in k/s (0 is max)
|
@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]);
|
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
|
/** Sets whether to read in raw mode or not
|
||||||
@param opts The read opts to change
|
@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
|
@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);
|
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
|
/** Gets the maximum read speed for a drive
|
||||||
@param d Drive to query
|
@param d Drive to query
|
||||||
@return Maximum read speed in K/s
|
@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);
|
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
|
#ifndef DOXYGEN
|
||||||
|
|
||||||
BURN_END_DECLS
|
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; -*- */
|
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||||
|
|
||||||
/* ts A61009 */
|
#include <assert.h>
|
||||||
/* #include <a ssert.h> */
|
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
@ -20,21 +18,6 @@
|
|||||||
#include "structure.h"
|
#include "structure.h"
|
||||||
#include "options.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_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_ATIP[] = { 0x43, 2, 4, 0, 0, 0, 0, 16, 0, 0 };
|
||||||
static unsigned char MMC_GET_DISC_INFO[] =
|
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[] =
|
static unsigned char MMC_SEND_CUE_SHEET[] =
|
||||||
{ 0x5D, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
{ 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;
|
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);
|
d->issue_command(d, &c);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ts A61110 : added parameters trackno, lba, nwa. Redefined return value.
|
int mmc_get_nwa(struct burn_drive *d)
|
||||||
@return 1=nwa is valid , 0=nwa is not valid , -1=error */
|
|
||||||
int mmc_get_nwa(struct burn_drive *d, int trackno, int *lba, int *nwa)
|
|
||||||
{
|
{
|
||||||
struct buffer buf;
|
struct buffer buf;
|
||||||
struct command c;
|
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);
|
c.oplen = sizeof(MMC_TRACK_INFO);
|
||||||
memcpy(c.opcode, MMC_TRACK_INFO, sizeof(MMC_TRACK_INFO));
|
memcpy(c.opcode, MMC_TRACK_INFO, sizeof(MMC_TRACK_INFO));
|
||||||
c.opcode[1] = 1;
|
c.opcode[1] = 1;
|
||||||
if(trackno<=0)
|
c.opcode[5] = 0xFF;
|
||||||
c.opcode[5] = 0xFF;
|
|
||||||
else
|
|
||||||
c.opcode[5] = trackno;
|
|
||||||
c.page = &buf;
|
c.page = &buf;
|
||||||
c.dir = FROM_DRIVE;
|
c.dir = FROM_DRIVE;
|
||||||
d->issue_command(d, &c);
|
d->issue_command(d, &c);
|
||||||
data = c.page->data;
|
data = c.page->data;
|
||||||
|
return (data[12] << 24) + (data[13] << 16)
|
||||||
*lba = (data[8] << 24) + (data[9] << 16)
|
|
||||||
+ (data[10] << 8) + data[11];
|
|
||||||
*nwa = (data[12] << 24) + (data[13] << 16)
|
|
||||||
+ (data[14] << 8) + data[15];
|
+ (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_drive *d, struct burn_write_opts *o) */
|
|
||||||
void mmc_close_disc(struct burn_write_opts *o)
|
|
||||||
{
|
{
|
||||||
struct burn_drive *d;
|
|
||||||
|
|
||||||
mmc_function_spy("mmc_close_disc");
|
mmc_function_spy("mmc_close_disc");
|
||||||
|
assert(o->drive == d);
|
||||||
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;
|
|
||||||
|
|
||||||
o->multi = 0;
|
o->multi = 0;
|
||||||
spc_select_write_params(d, o);
|
spc_select_write_params(d, o);
|
||||||
mmc_close(d, 1, 0);
|
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_drive *d, struct burn_write_opts *o) */
|
|
||||||
void mmc_close_session(struct burn_write_opts *o)
|
|
||||||
{
|
{
|
||||||
struct burn_drive *d;
|
|
||||||
|
|
||||||
mmc_function_spy("mmc_close_session");
|
mmc_function_spy("mmc_close_session");
|
||||||
|
assert(o->drive == d);
|
||||||
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;
|
|
||||||
|
|
||||||
o->multi = 3;
|
o->multi = 3;
|
||||||
spc_select_write_params(d, o);
|
spc_select_write_params(d, o);
|
||||||
mmc_close(d, 1, 0);
|
mmc_close(d, 1, 0);
|
||||||
@ -181,15 +121,11 @@ void mmc_close(struct burn_drive *d, int session, int track)
|
|||||||
{
|
{
|
||||||
struct command c;
|
struct command c;
|
||||||
|
|
||||||
mmc_function_spy("mmc_close");
|
mmc_function_spy("mmc_close_session");
|
||||||
|
|
||||||
c.retry = 1;
|
c.retry = 1;
|
||||||
c.oplen = sizeof(MMC_CLOSE);
|
c.oplen = sizeof(MMC_CLOSE);
|
||||||
memcpy(c.opcode, MMC_CLOSE, sizeof(MMC_CLOSE));
|
memcpy(c.opcode, MMC_CLOSE, sizeof(MMC_CLOSE));
|
||||||
|
c.opcode[2] = session | !!track;
|
||||||
/* ts A61030 : shifted !!session rather than or-ing plain session */
|
|
||||||
c.opcode[2] = ((!!session)<<1) | !!track;
|
|
||||||
|
|
||||||
c.opcode[4] = track >> 8;
|
c.opcode[4] = track >> 8;
|
||||||
c.opcode[5] = track & 0xFF;
|
c.opcode[5] = track & 0xFF;
|
||||||
c.page = NULL;
|
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]);
|
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)
|
void mmc_write_12(struct burn_drive *d, int start, struct buffer *buf)
|
||||||
{
|
{
|
||||||
struct command c;
|
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");
|
mmc_function_spy("mmc_write_12");
|
||||||
len = buf->sectors;
|
len = buf->sectors;
|
||||||
|
assert(buf->bytes >= buf->sectors); /* can be == at 0... */
|
||||||
/* ts A61009 */
|
|
||||||
/* a ssert(buf->bytes >= buf->sectors);*/ /* can be == at 0... */
|
|
||||||
|
|
||||||
burn_print(100, "trying to write %d at %d\n", len, start);
|
burn_print(100, "trying to write %d at %d\n", len, start);
|
||||||
memcpy(c.opcode, MMC_WRITE_12, sizeof(MMC_WRITE_12));
|
memcpy(c.opcode, MMC_WRITE_12, sizeof(MMC_WRITE_12));
|
||||||
c.retry = 1;
|
c.retry = 1;
|
||||||
@ -254,14 +186,6 @@ int mmc_write(struct burn_drive *d, int start, struct buffer *buf)
|
|||||||
struct command c;
|
struct command c;
|
||||||
int len;
|
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");
|
mmc_function_spy("mmc_write");
|
||||||
pthread_mutex_lock(&d->access_lock);
|
pthread_mutex_lock(&d->access_lock);
|
||||||
cancelled = d->cancel;
|
cancelled = d->cancel;
|
||||||
@ -271,10 +195,7 @@ int mmc_write(struct burn_drive *d, int start, struct buffer *buf)
|
|||||||
return BE_CANCELLED;
|
return BE_CANCELLED;
|
||||||
|
|
||||||
len = buf->sectors;
|
len = buf->sectors;
|
||||||
|
assert(buf->bytes >= buf->sectors); /* can be == at 0... */
|
||||||
/* ts A61009 : buffer fill problems are to be handled by caller */
|
|
||||||
/* a ssert(buf->bytes >= buf->sectors);*/ /* can be == at 0... */
|
|
||||||
|
|
||||||
burn_print(100, "trying to write %d at %d\n", len, start);
|
burn_print(100, "trying to write %d at %d\n", len, start);
|
||||||
memcpy(c.opcode, MMC_WRITE_10, sizeof(MMC_WRITE_10));
|
memcpy(c.opcode, MMC_WRITE_10, sizeof(MMC_WRITE_10));
|
||||||
c.retry = 1;
|
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]);
|
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
|
/* write(fileno(stderr), c.page->data, c.page->bytes);*/
|
||||||
/* <<< ts A61031 */
|
|
||||||
if(tee_fd!=-1) {
|
|
||||||
write(tee_fd,c.page->data,len*2048);
|
|
||||||
}
|
|
||||||
#endif /* Libburn_log_in_and_out_streaM */
|
|
||||||
|
|
||||||
d->issue_command(d, &c);
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -333,7 +228,7 @@ void mmc_read_toc(struct burn_drive *d)
|
|||||||
struct buffer buf;
|
struct buffer buf;
|
||||||
struct command c;
|
struct command c;
|
||||||
int dlen;
|
int dlen;
|
||||||
int i, bpl= 12;
|
int i;
|
||||||
unsigned char *tdata;
|
unsigned char *tdata;
|
||||||
|
|
||||||
mmc_function_spy("mmc_read_toc");
|
mmc_function_spy("mmc_read_toc");
|
||||||
@ -347,24 +242,7 @@ void mmc_read_toc(struct burn_drive *d)
|
|||||||
d->issue_command(d, &c);
|
d->issue_command(d, &c);
|
||||||
|
|
||||||
if (c.error) {
|
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;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -373,8 +251,7 @@ void mmc_read_toc(struct burn_drive *d)
|
|||||||
/*
|
/*
|
||||||
some drives fail this check.
|
some drives fail this check.
|
||||||
|
|
||||||
ts A61007 : if re-enabled then not via Assert.
|
assert(((dlen - 2) % 11) == 0);
|
||||||
a ssert(((dlen - 2) % 11) == 0);
|
|
||||||
*/
|
*/
|
||||||
d->toc_entry = malloc(d->toc_entries * sizeof(struct burn_toc_entry));
|
d->toc_entry = malloc(d->toc_entries * sizeof(struct burn_toc_entry));
|
||||||
tdata = c.page->data + 4;
|
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_disc_add_session(d->disc, session, BURN_POS_END);
|
||||||
burn_session_free(session);
|
burn_session_free(session);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ts A61022 */
|
|
||||||
burn_print(bpl, "-----------------------------------\n");
|
|
||||||
|
|
||||||
for (i = 0; i < d->toc_entries; i++, tdata += 11) {
|
for (i = 0; i < d->toc_entries; i++, tdata += 11) {
|
||||||
|
burn_print(12, "S %d, PT %d, TNO %d : ", tdata[0], tdata[3],
|
||||||
/* ts A61022: was burn_print level 12 */
|
|
||||||
burn_print(bpl, "S %d, PT %2.2Xh, TNO %d :", tdata[0],tdata[3],
|
|
||||||
tdata[2]);
|
tdata[2]);
|
||||||
burn_print(bpl, " MSF(%d:%d:%d)", tdata[4],tdata[5],tdata[6]);
|
burn_print(12, "(%d:%d:%d)", tdata[8], tdata[9], tdata[10]);
|
||||||
burn_print(bpl, " PMSF(%d:%d:%d %d)",
|
burn_print(12, "A(%d:%d:%d)", tdata[4], tdata[5], tdata[6]);
|
||||||
tdata[8], tdata[9], tdata[10],
|
burn_print(12, " - control %d, adr %d\n", tdata[1] & 0xF,
|
||||||
burn_msf_to_lba(tdata[8], tdata[9], tdata[10])
|
|
||||||
);
|
|
||||||
burn_print(bpl, " - control %d, adr %d\n", tdata[1] & 0xF,
|
|
||||||
tdata[1] >> 4);
|
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 (tdata[3] == 1) {
|
||||||
if (burn_msf_to_lba(tdata[8], tdata[9], tdata[10])) {
|
if (burn_msf_to_lba(tdata[8], tdata[9], tdata[10])) {
|
||||||
d->disc->session[0]->hidefirst = 1;
|
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->disc->session[tdata[0] - 1]->leadout_entry =
|
||||||
&d->toc_entry[i];
|
&d->toc_entry[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ts A61022 */
|
|
||||||
burn_print(bpl, "-----------------------------------\n");
|
|
||||||
|
|
||||||
if (d->status != BURN_DISC_APPENDABLE)
|
if (d->status != BURN_DISC_APPENDABLE)
|
||||||
d->status = BURN_DISC_FULL;
|
d->status = BURN_DISC_FULL;
|
||||||
toc_find_modes(d);
|
toc_find_modes(d);
|
||||||
@ -479,26 +339,11 @@ void mmc_read_disc_info(struct burn_drive *d)
|
|||||||
|
|
||||||
data = c.page->data;
|
data = c.page->data;
|
||||||
d->erasable = !!(data[2] & 16);
|
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) {
|
switch (data[2] & 3) {
|
||||||
case 0:
|
case 0:
|
||||||
d->toc_entries = 0;
|
d->toc_entries = 0;
|
||||||
d->start_lba = burn_msf_to_lba(data[17], data[18], data[19]);
|
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]);
|
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;
|
d->status = BURN_DISC_BLANK;
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
@ -514,17 +359,6 @@ void mmc_read_atip(struct burn_drive *d)
|
|||||||
struct buffer buf;
|
struct buffer buf;
|
||||||
struct command c;
|
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");
|
mmc_function_spy("mmc_read_atip");
|
||||||
memcpy(c.opcode, MMC_GET_ATIP, sizeof(MMC_GET_ATIP));
|
memcpy(c.opcode, MMC_GET_ATIP, sizeof(MMC_GET_ATIP));
|
||||||
c.retry = 1;
|
c.retry = 1;
|
||||||
@ -536,125 +370,6 @@ void mmc_read_atip(struct burn_drive *d)
|
|||||||
c.dir = FROM_DRIVE;
|
c.dir = FROM_DRIVE;
|
||||||
d->issue_command(d, &c);
|
d->issue_command(d, &c);
|
||||||
burn_print(1, "atip shit for you\n");
|
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,
|
void mmc_read_sectors(struct burn_drive *d,
|
||||||
@ -667,14 +382,9 @@ void mmc_read_sectors(struct burn_drive *d,
|
|||||||
struct command c;
|
struct command c;
|
||||||
|
|
||||||
mmc_function_spy("mmc_read_sectors");
|
mmc_function_spy("mmc_read_sectors");
|
||||||
|
assert(len >= 0);
|
||||||
/* ts A61009 : to be ensured by callers */
|
/* if the drive isn't busy, why the hell are we here? */
|
||||||
/* a ssert(len >= 0); */
|
assert(d->busy);
|
||||||
|
|
||||||
/* if the drive isn't busy, why the hell are we here? */
|
|
||||||
/* ts A61006 : i second that question */
|
|
||||||
/* a ssert(d->busy); */
|
|
||||||
|
|
||||||
burn_print(12, "reading %d from %d\n", len, start);
|
burn_print(12, "reading %d from %d\n", len, start);
|
||||||
memcpy(c.opcode, MMC_READ_CD, sizeof(MMC_READ_CD));
|
memcpy(c.opcode, MMC_READ_CD, sizeof(MMC_READ_CD));
|
||||||
c.retry = 1;
|
c.retry = 1;
|
||||||
@ -693,12 +403,8 @@ void mmc_read_sectors(struct burn_drive *d,
|
|||||||
len >>= 8;
|
len >>= 8;
|
||||||
c.opcode[6] = len & 0xFF;
|
c.opcode[6] = len & 0xFF;
|
||||||
req = 0xF8;
|
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)
|
if (d->busy == BURN_DRIVE_GRABBING || o->report_recovered_errors)
|
||||||
req |= 2;
|
req |= 2;
|
||||||
*/
|
|
||||||
|
|
||||||
c.opcode[10] = 0;
|
c.opcode[10] = 0;
|
||||||
/* always read the subcode, throw it away later, since we don't know
|
/* 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;
|
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");
|
mmc_function_spy("mmc_set_speed");
|
||||||
memcpy(c.opcode, MMC_SET_SPEED, sizeof(MMC_SET_SPEED));
|
memcpy(c.opcode, MMC_SET_SPEED, sizeof(MMC_SET_SPEED));
|
||||||
c.retry = 1;
|
c.retry = 1;
|
||||||
@ -843,72 +542,3 @@ void mmc_sync_cache(struct burn_drive *d)
|
|||||||
c.dir = NO_TRANSFER;
|
c.dir = NO_TRANSFER;
|
||||||
d->issue_command(d, &c);
|
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 */
|
/* MMC commands */
|
||||||
|
|
||||||
void mmc_read(struct burn_drive *);
|
void mmc_read(struct burn_drive *);
|
||||||
|
void mmc_close_session(struct burn_drive *, struct burn_write_opts *);
|
||||||
/* ts A61009 : removed redundant parameter d in favor of o->drive */
|
void mmc_close_disc(struct burn_drive *, struct burn_write_opts *);
|
||||||
/* 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(struct burn_drive *, int session, int track);
|
void mmc_close(struct burn_drive *, int session, int track);
|
||||||
void mmc_get_event(struct burn_drive *);
|
void mmc_get_event(struct burn_drive *);
|
||||||
int mmc_write(struct burn_drive *, int start, struct buffer *buf);
|
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_read_lead_in(struct burn_drive *, struct buffer *);
|
||||||
void mmc_perform_opc(struct burn_drive *);
|
void mmc_perform_opc(struct burn_drive *);
|
||||||
void mmc_get_configuration(struct burn_drive *);
|
void mmc_get_configuration(struct burn_drive *);
|
||||||
|
int mmc_get_nwa(struct burn_drive *);
|
||||||
/* ts A61110 : added parameters trackno, lba, nwa. Redefined return value.
|
|
||||||
@return 1=nwa is valid , 0=nwa is not valid , -1=error */
|
|
||||||
int mmc_get_nwa(struct burn_drive *d, int trackno, int *lba, int *nwa);
|
|
||||||
|
|
||||||
void mmc_send_cue_sheet(struct burn_drive *, struct cue_sheet *);
|
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*/
|
#endif /*__MMC*/
|
||||||
|
@ -2,27 +2,14 @@
|
|||||||
#include "options.h"
|
#include "options.h"
|
||||||
#include "transport.h"
|
#include "transport.h"
|
||||||
|
|
||||||
/* ts A61007 */
|
#include <assert.h>
|
||||||
/* #include <a ssert.h> */
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.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 *burn_write_opts_new(struct burn_drive *drive)
|
||||||
{
|
{
|
||||||
struct burn_write_opts *opts;
|
struct burn_write_opts *opts;
|
||||||
|
|
||||||
opts = malloc(sizeof(struct burn_write_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->drive = drive;
|
||||||
opts->refcount = 1;
|
opts->refcount = 1;
|
||||||
opts->write_type = BURN_WRITE_TAO;
|
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,
|
enum burn_write_types write_type,
|
||||||
int block_type)
|
int block_type)
|
||||||
{
|
{
|
||||||
int sector_get_outmode(enum burn_write_types write_type,
|
if ((write_type == BURN_WRITE_SAO && block_type == BURN_BLOCK_SAO) ||
|
||||||
enum burn_block_types block_type);
|
(opts->drive->block_types[write_type] & block_type)) {
|
||||||
int spc_block_type(enum burn_block_types b);
|
opts->write_type = write_type;
|
||||||
|
opts->block_type = block_type;
|
||||||
/* ts A61007 */
|
return 1;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
/* ts A61007 : obsoleting Assert in sector.c:get_outmode() */
|
assert(0);
|
||||||
if (sector_get_outmode(write_type, (enum burn_block_types) block_type)
|
return 0;
|
||||||
== -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); */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void burn_write_opts_set_toc_entries(struct burn_write_opts *opts, int count,
|
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);
|
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)
|
void burn_read_opts_set_raw(struct burn_read_opts *opts, int raw)
|
||||||
{
|
{
|
||||||
opts->raw = 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;
|
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; -*- */
|
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||||
|
|
||||||
/* #include <m alloc.h> ts A61013 : not in Linux man 3 malloc */
|
#include <malloc.h>
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
#include <assert.h>
|
||||||
/* ts A61007 */
|
|
||||||
/* #include <a ssert.h> */
|
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
@ -16,11 +11,7 @@
|
|||||||
#include "libburn.h"
|
#include "libburn.h"
|
||||||
#include "drive.h"
|
#include "drive.h"
|
||||||
#include "transport.h"
|
#include "transport.h"
|
||||||
|
|
||||||
/* ts A60925 : obsoleted by libdax_msgs.h
|
|
||||||
#include "message.h"
|
#include "message.h"
|
||||||
*/
|
|
||||||
|
|
||||||
#include "crc.h"
|
#include "crc.h"
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
#include "init.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;
|
struct buffer page;
|
||||||
int speed;
|
int speed;
|
||||||
|
|
||||||
/* ts A61007 : if this function gets revived, then these
|
assert((o->version & 0xfffff000) == (OPTIONS_VERSION & 0xfffff000));
|
||||||
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(!d->busy);
|
||||||
|
assert(d->toc->valid);
|
||||||
|
assert(o->datafd != -1);
|
||||||
/* XXX not sure this is a good idea. copy it? */
|
/* XXX not sure this is a good idea. copy it? */
|
||||||
/* XXX also, we have duplicated data now, do we remove the fds from struct
|
/* 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 */
|
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->set_speed(d, speed, 0);
|
||||||
|
|
||||||
d->params.retries = o->hardware_error_retries;
|
d->params.retries = o->hardware_error_retries;
|
||||||
|
|
||||||
d->send_parameters(d, o);
|
d->send_parameters(d, o);
|
||||||
|
|
||||||
d->cancel = 0;
|
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;
|
page.sectors = (finish < maxsects) ? finish : maxsects;
|
||||||
printf("reading %d sectors from %d\n", page.sectors,
|
printf("reading %d sectors from %d\n", page.sectors,
|
||||||
drive_lba);
|
drive_lba);
|
||||||
|
d->read_sectors(d, drive_lba, page.sectors, o, &page);
|
||||||
/* >>> ts A61009 : ensure page.sectors >= 0 before calling */
|
|
||||||
d->r ead_sectors(d, drive_lba, page.sectors, o, &page);
|
|
||||||
|
|
||||||
printf("Read %d\n", page.sectors);
|
printf("Read %d\n", page.sectors);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -2,7 +2,9 @@
|
|||||||
|
|
||||||
/* scsi block commands */
|
/* scsi block commands */
|
||||||
|
|
||||||
|
#include <scsi/scsi.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <scsi/sg.h>
|
||||||
|
|
||||||
#include "transport.h"
|
#include "transport.h"
|
||||||
#include "sbc.h"
|
#include "sbc.h"
|
||||||
@ -11,7 +13,6 @@
|
|||||||
/* spc command set */
|
/* spc command set */
|
||||||
static char SBC_LOAD[] = { 0x1b, 0, 0, 0, 3, 0 };
|
static char SBC_LOAD[] = { 0x1b, 0, 0, 0, 3, 0 };
|
||||||
static char SBC_UNLOAD[] = { 0x1b, 0, 0, 0, 2, 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)
|
void sbc_load(struct burn_drive *d)
|
||||||
{
|
{
|
||||||
@ -37,29 +38,3 @@ void sbc_eject(struct burn_drive *d)
|
|||||||
c.dir = NO_TRANSFER;
|
c.dir = NO_TRANSFER;
|
||||||
d->issue_command(d, &c);
|
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_load(struct burn_drive *);
|
||||||
void sbc_eject(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 */
|
#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; -*- */
|
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <assert.h>
|
||||||
/* ts A61010 */
|
|
||||||
/* #include <a ssert.h> */
|
|
||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
@ -19,15 +16,6 @@
|
|||||||
#include "toc.h"
|
#include "toc.h"
|
||||||
#include "write.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";*/
|
/*static unsigned char isrc[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";*/
|
||||||
|
|
||||||
#define sector_common(X) d->alba++; d->rlba X;
|
#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 */
|
/* 0 means "same as inmode" */
|
||||||
int sector_get_outmode(enum burn_write_types write_type,
|
static int get_outmode(struct burn_write_opts *o)
|
||||||
enum burn_block_types block_type)
|
|
||||||
{
|
{
|
||||||
/* ts A61103 : extended SAO condition to TAO */
|
if (o->write_type == BURN_WRITE_SAO)
|
||||||
if (write_type == BURN_WRITE_SAO || write_type == BURN_WRITE_TAO)
|
|
||||||
return 0;
|
return 0;
|
||||||
else
|
else
|
||||||
switch (block_type) {
|
switch (o->block_type) {
|
||||||
case BURN_BLOCK_RAW0:
|
case BURN_BLOCK_RAW0:
|
||||||
return BURN_MODE_RAW;
|
return BURN_MODE_RAW;
|
||||||
case BURN_BLOCK_RAW16:
|
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;
|
return BURN_MODE_RAW | BURN_SUBCODE_R96;
|
||||||
case BURN_BLOCK_MODE1:
|
case BURN_BLOCK_MODE1:
|
||||||
return BURN_MODE1;
|
return BURN_MODE1;
|
||||||
default:
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
assert(0); /* return BURN_MODE_UNIMPLEMENTED :) */
|
||||||
/* ts A61007 : now handled in burn_write_opts_set_write_type() */
|
|
||||||
/* a ssert(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)
|
static void get_bytes(struct burn_track *track, int count, unsigned char *data)
|
||||||
{
|
{
|
||||||
int valid, shortage, curr, i, tr;
|
int valid, shortage, curr;
|
||||||
|
|
||||||
#ifdef Libburn_log_in_and_out_streaM
|
|
||||||
/* <<< ts A61031 */
|
|
||||||
static int tee_fd= -1;
|
|
||||||
if(tee_fd==-1)
|
|
||||||
tee_fd= open("/tmp/libburn_sg_readin",
|
|
||||||
O_WRONLY|O_CREAT|O_TRUNC,S_IRUSR|S_IWUSR);
|
|
||||||
#endif /* Libburn_log_in_and_out_streaM */
|
|
||||||
|
|
||||||
|
|
||||||
/* no track pointer means we're just generating 0s */
|
/* no track pointer means we're just generating 0s */
|
||||||
if (!track) {
|
if (!track) {
|
||||||
@ -118,7 +80,7 @@ static void get_bytes(struct burn_track *track, int count, unsigned char *data)
|
|||||||
shortage = count - valid;
|
shortage = count - valid;
|
||||||
|
|
||||||
if (!shortage)
|
if (!shortage)
|
||||||
goto ex;
|
return;
|
||||||
|
|
||||||
/* Next we use source data */
|
/* Next we use source data */
|
||||||
curr = valid;
|
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);
|
valid = track->source->read(track->source, data + curr, count - curr);
|
||||||
} else valid = 0;
|
} else valid = 0;
|
||||||
|
|
||||||
if (valid <= 0) { /* ts A61031 : extended from (valid == -1) */
|
if (valid == -1) {
|
||||||
track->eos = 1;
|
track->eos = 1;
|
||||||
valid = 0;
|
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;
|
curr += valid;
|
||||||
shortage = count - curr;
|
shortage = count - curr;
|
||||||
|
|
||||||
if (!shortage)
|
if (!shortage)
|
||||||
goto ex;
|
return;
|
||||||
|
|
||||||
/* Before going to the next track, we run through any tail */
|
/* 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;
|
shortage -= valid;
|
||||||
|
|
||||||
if (!shortage)
|
if (!shortage)
|
||||||
goto ex;
|
return;
|
||||||
|
|
||||||
/* ts A61031 */
|
|
||||||
if (shortage >= count)
|
|
||||||
track->track_data_done = 1;
|
|
||||||
if (track->open_ended)
|
|
||||||
goto ex;
|
|
||||||
|
|
||||||
/* If we're still short, and there's a "next" pointer, we pull from that.
|
/* If we're still short, and there's a "next" pointer, we pull from that.
|
||||||
if that depletes, we'll just fill with 0s.
|
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;
|
curr += valid;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ex:;
|
if (!shortage)
|
||||||
/* ts A61024 : general finalizing processing */
|
return;
|
||||||
if(shortage)
|
memset(data + curr, 0, 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
static unsigned char *get_sector(struct burn_write_opts *opts, int inmode)
|
||||||
/* 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)
|
|
||||||
{
|
{
|
||||||
struct burn_drive *d = opts->drive;
|
struct burn_drive *d = opts->drive;
|
||||||
struct buffer *out = d->buffer;
|
struct buffer *out = d->buffer;
|
||||||
@ -209,28 +144,13 @@ static unsigned char *get_sector(struct burn_write_opts *opts,
|
|||||||
if (outmode == 0)
|
if (outmode == 0)
|
||||||
outmode = inmode;
|
outmode = inmode;
|
||||||
|
|
||||||
/* ts A61009 : react on eventual failure of burn_sector_length()
|
seclen = burn_sector_length(outmode) + burn_subcode_length(outmode);
|
||||||
(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);
|
|
||||||
|
|
||||||
if (out->bytes + (seclen) >= BUFFER_SIZE) {
|
if (out->bytes + (seclen) >= BUFFER_SIZE) {
|
||||||
int err;
|
int err;
|
||||||
err = d->write(d, d->nwa, out);
|
err = d->write(d, d->nwa, out);
|
||||||
if (err == BE_CANCELLED)
|
if (err == BE_CANCELLED)
|
||||||
return NULL;
|
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;
|
d->nwa += out->sectors;
|
||||||
out->bytes = 0;
|
out->bytes = 0;
|
||||||
out->sectors = 0;
|
out->sectors = 0;
|
||||||
@ -243,36 +163,8 @@ static unsigned char *get_sector(struct burn_write_opts *opts,
|
|||||||
return ret;
|
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 */
|
/* either inmode == outmode, or outmode == raw. anything else is bad news */
|
||||||
/* ts A61010 : changed type to int in order to propagate said bad news */
|
static void convert_data(struct burn_write_opts *o, struct burn_track *track,
|
||||||
/** @return 1 is ok, <= 0 is failure */
|
|
||||||
static int convert_data(struct burn_write_opts *o, struct burn_track *track,
|
|
||||||
int inmode, unsigned char *data)
|
int inmode, unsigned char *data)
|
||||||
{
|
{
|
||||||
int outlen, inlen;
|
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);
|
outlen = burn_sector_length(outmode);
|
||||||
inlen = burn_sector_length(inmode);
|
inlen = burn_sector_length(inmode);
|
||||||
|
assert(outlen >= inlen);
|
||||||
/* ts A61010 */
|
|
||||||
/* a ssert(outlen >= inlen); */
|
|
||||||
if (outlen < inlen)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if ((outmode & BURN_MODE_BITS) == (inmode & BURN_MODE_BITS)) {
|
if ((outmode & BURN_MODE_BITS) == (inmode & BURN_MODE_BITS)) {
|
||||||
get_bytes(track, inlen, data);
|
get_bytes(track, inlen, data);
|
||||||
return 1;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ts A61010 */
|
assert(outmode & BURN_MODE_RAW);
|
||||||
/* a ssert(outmode & BURN_MODE_RAW); */
|
|
||||||
if (!(outmode & BURN_MODE_RAW))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (inmode & BURN_MODE1)
|
if (inmode & BURN_MODE1)
|
||||||
offset = 16;
|
offset = 16;
|
||||||
@ -307,16 +192,9 @@ static int convert_data(struct burn_write_opts *o, struct burn_track *track,
|
|||||||
offset = 0;
|
offset = 0;
|
||||||
if (inmode & BURN_AUDIO)
|
if (inmode & BURN_AUDIO)
|
||||||
offset = 0;
|
offset = 0;
|
||||||
|
assert(offset != -1);
|
||||||
/* ts A61010 */
|
|
||||||
/* a ssert(offset != -1); */
|
|
||||||
if (offset == -1)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
get_bytes(track, inlen, data + offset);
|
get_bytes(track, inlen, data + offset);
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void convert_subs(struct burn_write_opts *o, int inmode,
|
static void convert_subs(struct burn_write_opts *o, int inmode,
|
||||||
unsigned char *subs, unsigned char *sector)
|
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[0] = ~out[0];
|
||||||
out[1] = ~out[1];
|
out[1] = ~out[1];
|
||||||
break;
|
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 *data;
|
||||||
unsigned char subs[96];
|
unsigned char subs[96];
|
||||||
|
|
||||||
data = get_sector(o, NULL, mode);
|
data = get_sector(o, mode);
|
||||||
if (data == NULL)
|
if (!data)
|
||||||
return 0;
|
|
||||||
/* ts A61010 */
|
|
||||||
if (convert_data(o, NULL, mode, data) <= 0)
|
|
||||||
return 0;
|
return 0;
|
||||||
|
convert_data(o, NULL, mode, data);
|
||||||
subcode_toc(d, mode, subs);
|
subcode_toc(d, mode, subs);
|
||||||
convert_subs(o, mode, subs, data);
|
convert_subs(o, mode, subs, data);
|
||||||
sector_headers(o, data, mode, 1);
|
sector_headers(o, data, mode, 1);
|
||||||
@ -418,12 +292,10 @@ int sector_pregap(struct burn_write_opts *o,
|
|||||||
unsigned char *data;
|
unsigned char *data;
|
||||||
unsigned char subs[96];
|
unsigned char subs[96];
|
||||||
|
|
||||||
data = get_sector(o, NULL, mode);
|
data = get_sector(o, mode);
|
||||||
if (data == NULL)
|
if (!data)
|
||||||
return 0;
|
|
||||||
/* ts A61010 */
|
|
||||||
if (convert_data(o, NULL, mode, data) <= 0)
|
|
||||||
return 0;
|
return 0;
|
||||||
|
convert_data(o, NULL, mode, data);
|
||||||
subcode_user(o, subs, tno, control, 0, NULL, 1);
|
subcode_user(o, subs, tno, control, 0, NULL, 1);
|
||||||
convert_subs(o, mode, subs, data);
|
convert_subs(o, mode, subs, data);
|
||||||
sector_headers(o, data, mode, 0);
|
sector_headers(o, data, mode, 0);
|
||||||
@ -438,12 +310,10 @@ int sector_postgap(struct burn_write_opts *o,
|
|||||||
unsigned char subs[96];
|
unsigned char subs[96];
|
||||||
unsigned char *data;
|
unsigned char *data;
|
||||||
|
|
||||||
data = get_sector(o, NULL, mode);
|
data = get_sector(o, mode);
|
||||||
if (data == NULL)
|
if (!data)
|
||||||
return 0;
|
return 0;
|
||||||
/* ts A61010 */
|
convert_data(o, NULL, mode, data);
|
||||||
if (convert_data(o, NULL, mode, data) <= 0)
|
|
||||||
return 0;;
|
|
||||||
/* use last index in track */
|
/* use last index in track */
|
||||||
subcode_user(o, subs, tno, control, 1, NULL, 1);
|
subcode_user(o, subs, tno, control, 1, NULL, 1);
|
||||||
convert_subs(o, mode, subs, data);
|
convert_subs(o, mode, subs, data);
|
||||||
@ -492,9 +362,7 @@ static char char_to_isrc(char c)
|
|||||||
return 0x11 + (c - 'A');
|
return 0x11 + (c - 'A');
|
||||||
if (c >= 'a' && c <= 'z')
|
if (c >= 'a' && c <= 'z')
|
||||||
return 0x11 + (c - 'a');
|
return 0x11 + (c - 'a');
|
||||||
|
assert(0);
|
||||||
/* ts A61008 : obsoleted by test in burn_track_set_isrc() */
|
|
||||||
/* a ssert(0); */
|
|
||||||
return 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 ? */
|
assert(qmode == 1 || qmode == 2 || qmode == 3);
|
||||||
/* a ssert(qmode == 1 || qmode == 2 || qmode == 3); */
|
|
||||||
|
|
||||||
switch (qmode) {
|
switch (qmode) {
|
||||||
case 1:
|
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 subs[96];
|
||||||
unsigned char *data;
|
unsigned char *data;
|
||||||
|
|
||||||
data = get_sector(o, NULL, mode);
|
data = get_sector(o, mode);
|
||||||
if (!data)
|
if (!data)
|
||||||
return 0;
|
return 0;
|
||||||
/* ts A61010 */
|
convert_data(o, NULL, mode, data);
|
||||||
if (convert_data(o, NULL, mode, data) <= 0)
|
|
||||||
return 0;
|
|
||||||
subcode_lout(o, control, subs);
|
subcode_lout(o, control, subs);
|
||||||
convert_subs(o, mode, subs, data);
|
convert_subs(o, mode, subs, data);
|
||||||
sector_headers(o, data, mode, 0);
|
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 subs[96];
|
||||||
unsigned char *data;
|
unsigned char *data;
|
||||||
|
|
||||||
data = get_sector(o, t, t->mode);
|
data = get_sector(o, t->mode);
|
||||||
if (!data)
|
if (!data)
|
||||||
return 0;
|
return 0;
|
||||||
/* ts A61010 */
|
convert_data(o, t, t->mode, data);
|
||||||
if (convert_data(o, t, t->mode, data) <= 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* ts A61031 */
|
|
||||||
if (t->open_ended && t->track_data_done) {
|
|
||||||
unget_sector(o, t->mode);
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!t->source->read_sub)
|
if (!t->source->read_sub)
|
||||||
subcode_user(o, subs, t->entry->point,
|
subcode_user(o, subs, t->entry->point,
|
||||||
@ -685,22 +542,6 @@ int dec_to_bcd(int d)
|
|||||||
return (top << 4) + bottom;
|
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,
|
void sector_headers(struct burn_write_opts *o, unsigned char *out,
|
||||||
int mode, int leadin)
|
int mode, int leadin)
|
||||||
{
|
{
|
||||||
@ -709,33 +550,14 @@ void sector_headers(struct burn_write_opts *o, unsigned char *out,
|
|||||||
int min, sec, frame;
|
int min, sec, frame;
|
||||||
int modebyte = -1;
|
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" */
|
if (mode & BURN_AUDIO) /* no headers for "audio" */
|
||||||
return;
|
return;
|
||||||
if (o->write_type == BURN_WRITE_SAO)
|
if (o->write_type == BURN_WRITE_SAO)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* ts A61031 */
|
|
||||||
if (o->write_type == BURN_WRITE_TAO)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (mode & BURN_MODE1)
|
if (mode & BURN_MODE1)
|
||||||
modebyte = 1;
|
modebyte = 1;
|
||||||
|
|
||||||
#endif
|
assert(modebyte == 1);
|
||||||
|
|
||||||
/* ts A61009 : now ensured by burn_disc_write_is_ok() */
|
|
||||||
/* a ssert(modebyte == 1); */
|
|
||||||
|
|
||||||
out[0] = 0;
|
out[0] = 0;
|
||||||
memset(out + 1, 0xFF, 10); /* sync */
|
memset(out + 1, 0xFF, 10); /* sync */
|
||||||
@ -807,22 +629,13 @@ void process_q(struct burn_drive *d, unsigned char *q)
|
|||||||
*/
|
*/
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
assert(0);
|
||||||
/* ts A61009 : if reactivated then witout Assert */
|
|
||||||
a ssert(0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* this needs more info. subs in the data? control/adr? */
|
/* 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
|
#warning sector_identify needs to be written
|
||||||
*/
|
|
||||||
int sector_identify(unsigned char *data)
|
int sector_identify(unsigned char *data)
|
||||||
{
|
{
|
||||||
scramble(data);
|
scramble(data);
|
||||||
|
@ -18,10 +18,6 @@ int sector_postgap(struct burn_write_opts *, unsigned char tno,
|
|||||||
unsigned char control, int mode);
|
unsigned char control, int mode);
|
||||||
int sector_lout(struct burn_write_opts *, 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);
|
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 *,
|
void sector_headers(struct burn_write_opts *, unsigned char *,
|
||||||
int mode, int leadin);
|
int mode, int leadin);
|
||||||
void subcode_user(struct burn_write_opts *, unsigned char *s,
|
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
|
#include <assert.h>
|
||||||
Switcher for operating system dependent transport level modules of libburn.
|
#include <errno.h>
|
||||||
Copyright (C) 2006 Thomas Schmitt <scdbackup@gmx.net>, provided under GPL
|
#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
|
#ifndef __SG
|
||||||
#define __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 burn_drive;
|
||||||
struct command;
|
struct command;
|
||||||
|
|
||||||
|
enum response
|
||||||
|
{ RETRY, FAIL };
|
||||||
|
|
||||||
/* ts A60922 ticket 33 */
|
void sg_enumerate(void);
|
||||||
int sg_give_next_adr(burn_drive_enumerator_t *enm_context,
|
void ata_enumerate(void);
|
||||||
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);
|
|
||||||
|
|
||||||
int sg_grab(struct burn_drive *);
|
int sg_grab(struct burn_drive *);
|
||||||
int sg_release(struct burn_drive *);
|
int sg_release(struct burn_drive *);
|
||||||
int sg_issue_command(struct burn_drive *, struct command *);
|
int sg_issue_command(struct burn_drive *, struct command *);
|
||||||
|
enum response scsi_error(struct burn_drive *, unsigned char *, int);
|
||||||
/* ts A61115 : formerly sg_enumerate();ata_enumerate() */
|
|
||||||
int scsi_enumerate_drives(void);
|
|
||||||
|
|
||||||
int sg_drive_is_open(struct burn_drive * d);
|
|
||||||
|
|
||||||
#endif /* __SG */
|
#endif /* __SG */
|
||||||
|
@ -22,10 +22,6 @@ enum burn_source_status burn_track_set_source(struct burn_track *t,
|
|||||||
return BURN_SOURCE_FAILED;
|
return BURN_SOURCE_FAILED;
|
||||||
s->refcount++;
|
s->refcount++;
|
||||||
t->source = s;
|
t->source = s;
|
||||||
|
|
||||||
/* ts A61031 */
|
|
||||||
t->open_ended = (s->get_size(s) <= 0);
|
|
||||||
|
|
||||||
return BURN_SOURCE_OK;
|
return BURN_SOURCE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,7 +29,8 @@ struct burn_source *burn_source_new(void)
|
|||||||
{
|
{
|
||||||
struct burn_source *out;
|
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;
|
out->refcount = 1;
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
268
libburn/spc.c
268
libburn/spc.c
@ -9,10 +9,7 @@
|
|||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
/* ts A61008 */
|
|
||||||
/* #include <a ssert.h> */
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "transport.h"
|
#include "transport.h"
|
||||||
@ -23,10 +20,6 @@
|
|||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
#include "options.h"
|
#include "options.h"
|
||||||
|
|
||||||
#include "libdax_msgs.h"
|
|
||||||
extern struct libdax_msgs *libdax_messenger;
|
|
||||||
|
|
||||||
|
|
||||||
/* spc command set */
|
/* spc command set */
|
||||||
static unsigned char SPC_INQUIRY[] = { 0x12, 0, 0, 0, 255, 0 };
|
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)
|
void spc_inquiry(struct burn_drive *d)
|
||||||
{
|
{
|
||||||
struct buffer buf;
|
struct buffer buf;
|
||||||
struct burn_scsi_inquiry_data *id;
|
struct scsi_inquiry_data *id;
|
||||||
struct command c;
|
struct command c;
|
||||||
|
|
||||||
memcpy(c.opcode, SPC_INQUIRY, sizeof(SPC_INQUIRY));
|
memcpy(c.opcode, SPC_INQUIRY, sizeof(SPC_INQUIRY));
|
||||||
@ -91,7 +84,7 @@ void spc_inquiry(struct burn_drive *d)
|
|||||||
c.dir = FROM_DRIVE;
|
c.dir = FROM_DRIVE;
|
||||||
d->issue_command(d, &c);
|
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->vendor[8] = 0;
|
||||||
id->product[16] = 0;
|
id->product[16] = 0;
|
||||||
id->revision[4] = 0;
|
id->revision[4] = 0;
|
||||||
@ -161,24 +154,10 @@ void spc_sense_caps(struct burn_drive *d)
|
|||||||
m->cdrw_write = page[3] & 2;
|
m->cdrw_write = page[3] & 2;
|
||||||
m->cdr_read = page[2] & 1;
|
m->cdr_read = page[2] & 1;
|
||||||
m->cdr_write = page[3] & 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->max_read_speed = page[8] * 256 + page[9];
|
||||||
m->cur_read_speed = page[14] * 256 + page[15];
|
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];
|
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];
|
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->c2_pointers = page[5] & 16;
|
||||||
m->valid = 1;
|
m->valid = 1;
|
||||||
m->underrun_proof = page[4] & 128;
|
m->underrun_proof = page[4] & 128;
|
||||||
@ -223,10 +202,7 @@ void spc_select_error_params(struct burn_drive *d,
|
|||||||
c.page = &buf;
|
c.page = &buf;
|
||||||
c.page->bytes = 0;
|
c.page->bytes = 0;
|
||||||
c.page->sectors = 0;
|
c.page->sectors = 0;
|
||||||
|
assert(d->mdata->valid);
|
||||||
/* ts A61007 : moved up to only caller burn_disc_read() */
|
|
||||||
/* a ssert(d->mdata->valid); */
|
|
||||||
|
|
||||||
memset(c.page->data, 0, 8 + 2 + d->mdata->retry_page_length);
|
memset(c.page->data, 0, 8 + 2 + d->mdata->retry_page_length);
|
||||||
c.page->bytes = 8 + 2 + d->mdata->retry_page_length;
|
c.page->bytes = 8 + 2 + d->mdata->retry_page_length;
|
||||||
c.page->data[8] = 1;
|
c.page->data[8] = 1;
|
||||||
@ -251,9 +227,8 @@ void spc_sense_write_params(struct burn_drive *d)
|
|||||||
unsigned char *page;
|
unsigned char *page;
|
||||||
struct command c;
|
struct command c;
|
||||||
|
|
||||||
/* ts A61007 : Done in soft at only caller burn_drive_grab() */
|
assert(d->mdata->cdr_write || d->mdata->cdrw_write ||
|
||||||
/* a ssert(d->mdata->cdr_write || d->mdata->cdrw_write ||
|
d->mdata->dvdr_write || d->mdata->dvdram_write);
|
||||||
d->mdata->dvdr_write || d->mdata->dvdram_write); */
|
|
||||||
|
|
||||||
memcpy(c.opcode, SPC_MODE_SENSE, sizeof(SPC_MODE_SENSE));
|
memcpy(c.opcode, SPC_MODE_SENSE, sizeof(SPC_MODE_SENSE));
|
||||||
c.retry = 1;
|
c.retry = 1;
|
||||||
@ -274,11 +249,6 @@ void spc_sense_write_params(struct burn_drive *d)
|
|||||||
mmc_read_disc_info(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,
|
void spc_select_write_params(struct burn_drive *d,
|
||||||
const struct burn_write_opts *o)
|
const struct burn_write_opts *o)
|
||||||
{
|
{
|
||||||
@ -286,16 +256,7 @@ void spc_select_write_params(struct burn_drive *d,
|
|||||||
struct command c;
|
struct command c;
|
||||||
int bufe, sim;
|
int bufe, sim;
|
||||||
|
|
||||||
/* ts A61007 : All current callers are safe. */
|
assert(o->drive == d);
|
||||||
/* a ssert(o->drive == d); */
|
|
||||||
|
|
||||||
/* <<< A61030
|
|
||||||
fprintf(stderr,"libburn_debug: write_type=%d multi=%d control=%d\n",
|
|
||||||
o->write_type,o->multi,o->control);
|
|
||||||
fprintf(stderr,"libburn_debug: block_type=%d spc_block_type=%d\n",
|
|
||||||
o->block_type,spc_block_type(o->block_type));
|
|
||||||
*/
|
|
||||||
|
|
||||||
memcpy(c.opcode, SPC_MODE_SELECT, sizeof(SPC_MODE_SELECT));
|
memcpy(c.opcode, SPC_MODE_SELECT, sizeof(SPC_MODE_SELECT));
|
||||||
c.retry = 1;
|
c.retry = 1;
|
||||||
c.oplen = sizeof(SPC_MODE_SELECT);
|
c.oplen = sizeof(SPC_MODE_SELECT);
|
||||||
@ -303,10 +264,7 @@ void spc_select_write_params(struct burn_drive *d,
|
|||||||
c.page = &buf;
|
c.page = &buf;
|
||||||
c.page->bytes = 0;
|
c.page->bytes = 0;
|
||||||
c.page->sectors = 0;
|
c.page->sectors = 0;
|
||||||
|
assert(d->mdata->valid);
|
||||||
/* ts A61007 : moved up to burn_disc_write() */
|
|
||||||
/* a ssert(d->mdata->valid); */
|
|
||||||
|
|
||||||
memset(c.page->data, 0, 8 + 2 + d->mdata->write_page_length);
|
memset(c.page->data, 0, 8 + 2 + d->mdata->write_page_length);
|
||||||
c.page->bytes = 8 + 2 + d->mdata->write_page_length;
|
c.page->bytes = 8 + 2 + d->mdata->write_page_length;
|
||||||
c.page->data[8] = 5;
|
c.page->data[8] = 5;
|
||||||
@ -319,17 +277,8 @@ void spc_select_write_params(struct burn_drive *d,
|
|||||||
c.page->data[10] = (bufe << 6)
|
c.page->data[10] = (bufe << 6)
|
||||||
+ (sim << 4)
|
+ (sim << 4)
|
||||||
+ o->write_type;
|
+ o->write_type;
|
||||||
|
c.page->data[11] = (o->multi << 6) | o->control;
|
||||||
/* ts A61106 : MMC-1 table 110 : multi==0 or multi==3 */
|
|
||||||
c.page->data[11] = ((3 * !!o->multi) << 6) | o->control;
|
|
||||||
|
|
||||||
c.page->data[12] = spc_block_type(o->block_type);
|
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[22] = 0;
|
||||||
c.page->data[23] = 150; /* audio pause length */
|
c.page->data[23] = 150; /* audio pause length */
|
||||||
/*XXX need session format! */
|
/*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[12] = try_block_type;
|
||||||
c.page->data[23] = 150;
|
c.page->data[23] = 150;
|
||||||
c.dir = TO_DRIVE;
|
c.dir = TO_DRIVE;
|
||||||
|
|
||||||
d->silent_on_scsi_error = 1;
|
|
||||||
d->issue_command(d, &c);
|
d->issue_command(d, &c);
|
||||||
d->silent_on_scsi_error = 0;
|
|
||||||
|
|
||||||
key = c.sense[2];
|
key = c.sense[2];
|
||||||
asc = c.sense[12];
|
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)
|
int spc_block_type(enum burn_block_types b)
|
||||||
{
|
{
|
||||||
switch (b) {
|
switch (b) {
|
||||||
@ -455,199 +400,6 @@ int spc_block_type(enum burn_block_types b)
|
|||||||
return 12;
|
return 12;
|
||||||
case BURN_BLOCK_MODE2_OK:
|
case BURN_BLOCK_MODE2_OK:
|
||||||
return 13;
|
return 13;
|
||||||
default:
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
/* ts A61007 : already prevented in burn_write_opts_set_write_type() */
|
assert(0);
|
||||||
/* a ssert(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_get_erase_progress(struct burn_drive *d);
|
||||||
int spc_test_unit_ready(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*/
|
#endif /*__SPC*/
|
||||||
|
@ -1,7 +1,4 @@
|
|||||||
|
#include <assert.h>
|
||||||
/* ts A61008 */
|
|
||||||
/* #include <a ssert.h> */
|
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@ -10,18 +7,10 @@
|
|||||||
#include "write.h"
|
#include "write.h"
|
||||||
#include "debug.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) {\
|
#define RESIZE(TO, NEW, pos) {\
|
||||||
void *tmp;\
|
void *tmp;\
|
||||||
\
|
\
|
||||||
if (pos > BURN_POS_END)\
|
assert(!(pos > BURN_POS_END));\
|
||||||
return 0;\
|
|
||||||
if (pos == BURN_POS_END)\
|
if (pos == BURN_POS_END)\
|
||||||
pos = TO->NEW##s;\
|
pos = TO->NEW##s;\
|
||||||
if (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 *burn_disc_create(void)
|
||||||
{
|
{
|
||||||
struct burn_disc *d;
|
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->refcnt = 1;
|
||||||
d->sessions = 0;
|
d->sessions = 0;
|
||||||
d->session = NULL;
|
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 *burn_session_create(void)
|
||||||
{
|
{
|
||||||
struct burn_session *s;
|
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->refcnt = 1;
|
||||||
s->tracks = 0;
|
s->tracks = 0;
|
||||||
s->track = NULL;
|
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 *burn_track_create(void)
|
||||||
{
|
{
|
||||||
struct burn_track *t;
|
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->refcnt = 1;
|
||||||
t->indices = 0;
|
t->indices = 0;
|
||||||
t->offset = 0;
|
t->offset = 0;
|
||||||
@ -115,23 +107,9 @@ struct burn_track *burn_track_create(void)
|
|||||||
t->pad = 1;
|
t->pad = 1;
|
||||||
t->entry = NULL;
|
t->entry = NULL;
|
||||||
t->source = 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->postgap = 0;
|
||||||
t->pregap1 = 0;
|
t->pregap1 = 0;
|
||||||
t->pregap2 = 0;
|
t->pregap2 = 0;
|
||||||
|
|
||||||
/* ts A61024 */
|
|
||||||
t->swap_source_bytes = 0;
|
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,34 +138,30 @@ int burn_session_remove_track(struct burn_session *s, struct burn_track *t)
|
|||||||
struct burn_track **tmp;
|
struct burn_track **tmp;
|
||||||
int i, pos = -1;
|
int i, pos = -1;
|
||||||
|
|
||||||
/* ts A61008 */
|
assert(s->track != NULL);
|
||||||
/* a ssert(s->track != NULL); */
|
|
||||||
if (s->track == NULL)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
burn_track_free(t);
|
burn_track_free(t);
|
||||||
|
|
||||||
/* Find the position */
|
/* Find the position */
|
||||||
for (i = 0; i < s->tracks; i++) {
|
for (i = 0; i < s->tracks; i++) {
|
||||||
if (t == s->track[i]) {
|
if (t == s->track[i])
|
||||||
pos = i;
|
pos = i;
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pos == -1)
|
if (pos == -1)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* Is it the last track? */
|
/* Is it the last track? */
|
||||||
if (pos != s->tracks - 1) {
|
if (pos != s->tracks) {
|
||||||
memmove(&s->track[pos], &s->track[pos + 1],
|
memmove(s->track[pos], s->track[pos + 1],
|
||||||
sizeof(struct burn_track *) * (s->tracks - (pos + 1)));
|
sizeof(struct burn_track *) * (s->tracks - (pos + 1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
s->tracks--;
|
s->tracks--;
|
||||||
tmp = realloc(s->track, sizeof(struct burn_track *) * s->tracks);
|
tmp = realloc(s->track, sizeof(struct burn_track *) * s->tracks);
|
||||||
if (tmp)
|
if (!tmp)
|
||||||
s->track = tmp;
|
return 0;
|
||||||
|
s->track = tmp;
|
||||||
return 1;
|
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,
|
void burn_track_define_data(struct burn_track *t, int offset, int tail,
|
||||||
int pad, int mode)
|
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->offset = offset;
|
||||||
t->pad = pad;
|
t->pad = pad;
|
||||||
t->mode = mode;
|
t->mode = mode;
|
||||||
t->tail = tail;
|
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,
|
void burn_track_set_isrc(struct burn_track *t, char *country, char *owner,
|
||||||
unsigned char year, unsigned int serial)
|
unsigned char year, unsigned int serial)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
t->isrc.has_isrc = 1;
|
||||||
for (i = 0; i < 2; ++i) {
|
for (i = 0; i < 2; ++i) {
|
||||||
|
assert((country[i] >= '0' || country[i] < '9') &&
|
||||||
/* ts A61008 : This is always true */
|
|
||||||
/* a ssert((country[i] >= '0' || country[i] < '9') &&
|
|
||||||
(country[i] >= 'a' || country[i] < 'z') &&
|
(country[i] >= 'a' || country[i] < 'z') &&
|
||||||
(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;
|
|
||||||
|
|
||||||
t->isrc.country[i] = country[i];
|
t->isrc.country[i] = country[i];
|
||||||
}
|
}
|
||||||
for (i = 0; i < 3; ++i) {
|
for (i = 0; i < 3; ++i) {
|
||||||
|
assert((owner[i] >= '0' || owner[i] < '9') &&
|
||||||
/* ts A61008 : This is always true */
|
|
||||||
/* a ssert((owner[i] >= '0' || owner[i] < '9') &&
|
|
||||||
(owner[i] >= 'a' || owner[i] < 'z') &&
|
(owner[i] >= 'a' || owner[i] < 'z') &&
|
||||||
(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;
|
|
||||||
|
|
||||||
t->isrc.owner[i] = owner[i];
|
t->isrc.owner[i] = owner[i];
|
||||||
}
|
}
|
||||||
|
assert(year <= 99);
|
||||||
/* ts A61008 */
|
|
||||||
/* a ssert(year <= 99); */
|
|
||||||
if (year > 99)
|
|
||||||
goto is_not_allowed;
|
|
||||||
|
|
||||||
t->isrc.year = year;
|
t->isrc.year = year;
|
||||||
|
assert(serial <= 99999);
|
||||||
/* ts A61008 */
|
|
||||||
/* a ssert(serial <= 99999); */
|
|
||||||
if (serial > 99999)
|
|
||||||
goto is_not_allowed;
|
|
||||||
|
|
||||||
t->isrc.serial = serial;
|
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)
|
void burn_track_clear_isrc(struct burn_track *t)
|
||||||
@ -329,31 +241,6 @@ int burn_track_get_sectors(struct burn_track *t)
|
|||||||
return sectors;
|
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 burn_track_get_shortage(struct burn_track *t)
|
||||||
{
|
{
|
||||||
int size;
|
int size;
|
||||||
|
@ -31,18 +31,6 @@ struct burn_track
|
|||||||
struct burn_source *source;
|
struct burn_source *source;
|
||||||
/** End of Source flag */
|
/** End of Source flag */
|
||||||
int eos;
|
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
|
/** The audio/data mode for the entry. Derived from control and
|
||||||
possibly from reading the track's first sector. */
|
possibly from reading the track's first sector. */
|
||||||
int mode;
|
int mode;
|
||||||
@ -53,10 +41,6 @@ struct burn_track
|
|||||||
/** The track contains a postgap */
|
/** The track contains a postgap */
|
||||||
int postgap;
|
int postgap;
|
||||||
struct isrc isrc;
|
struct isrc isrc;
|
||||||
|
|
||||||
/* ts A61024 */
|
|
||||||
/** Byte swapping on source data stream : 0=none , 1=pairwise */
|
|
||||||
int swap_source_bytes;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct burn_session
|
struct burn_session
|
||||||
@ -83,10 +67,4 @@ struct burn_disc
|
|||||||
|
|
||||||
int burn_track_get_shortage(struct burn_track *t);
|
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 */
|
#endif /* BURN__STRUCTURE_H */
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||||
|
|
||||||
/* ts A61008 */
|
#include <assert.h>
|
||||||
/* #include <a ssert.h> */
|
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.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_FORM1:
|
||||||
case BURN_MODE2_FORM2:
|
case BURN_MODE2_FORM2:
|
||||||
case BURN_MODE_UNINITIALIZED:
|
case BURN_MODE_UNINITIALIZED:
|
||||||
|
assert(0); /* unhandled! find out ccd's
|
||||||
/* ts A61008 : do this softly without Assert */
|
|
||||||
|
|
||||||
a ssert(0); /* unhandled! find out ccd's
|
|
||||||
value for these modes! */
|
value for these modes! */
|
||||||
}
|
}
|
||||||
dprintf(f, "PreGapMode=%d\r\n", m);
|
dprintf(f, "PreGapMode=%d\r\n", m);
|
||||||
@ -100,8 +95,7 @@ void toc_find_modes(struct burn_drive *d)
|
|||||||
struct buffer mem;
|
struct buffer mem;
|
||||||
struct burn_toc_entry *e;
|
struct burn_toc_entry *e;
|
||||||
|
|
||||||
/* ts A61008 : to be prevented on the higher levels */
|
assert(d->busy);
|
||||||
/* a ssert(d->busy); */
|
|
||||||
|
|
||||||
mem.bytes = 0;
|
mem.bytes = 0;
|
||||||
mem.sectors = 1;
|
mem.sectors = 1;
|
||||||
|
@ -4,16 +4,19 @@
|
|||||||
#define __TRANSPORT
|
#define __TRANSPORT
|
||||||
|
|
||||||
#include "libburn.h"
|
#include "libburn.h"
|
||||||
#include "os.h"
|
|
||||||
|
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
/* sg data structures */
|
/* sg data structures */
|
||||||
#include <sys/types.h>
|
#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 65536
|
||||||
#define BUFFER_SIZE BURN_OS_TRANSPORT_BUFFER_SIZE
|
|
||||||
|
|
||||||
|
|
||||||
enum transfer_direction
|
enum transfer_direction
|
||||||
{ TO_DRIVE, FROM_DRIVE, NO_TRANSFER };
|
{ TO_DRIVE, FROM_DRIVE, NO_TRANSFER };
|
||||||
@ -52,7 +55,7 @@ struct command
|
|||||||
struct buffer *page;
|
struct buffer *page;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct burn_scsi_inquiry_data
|
struct scsi_inquiry_data
|
||||||
{
|
{
|
||||||
char vendor[9];
|
char vendor[9];
|
||||||
char product[17];
|
char product[17];
|
||||||
@ -75,10 +78,6 @@ struct scsi_mode_data
|
|||||||
int simulate;
|
int simulate;
|
||||||
int max_read_speed;
|
int max_read_speed;
|
||||||
int max_write_speed;
|
int max_write_speed;
|
||||||
|
|
||||||
/* ts A61021 */
|
|
||||||
int min_write_speed;
|
|
||||||
|
|
||||||
int cur_read_speed;
|
int cur_read_speed;
|
||||||
int cur_write_speed;
|
int cur_write_speed;
|
||||||
int retry_page_length;
|
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() */
|
/** Gets initialized in enumerate_common() and burn_drive_register() */
|
||||||
struct burn_drive
|
struct burn_drive
|
||||||
{
|
{
|
||||||
int bus_no;
|
|
||||||
int host;
|
int host;
|
||||||
int id;
|
int id;
|
||||||
int channel;
|
int channel;
|
||||||
int lun;
|
int lun;
|
||||||
char *devname;
|
char *devname;
|
||||||
|
int fd;
|
||||||
|
|
||||||
/* see os.h for name of particular os-*.h where this is defined */
|
|
||||||
BURN_OS_TRANSPORT_DRIVE_ELEMENTS
|
|
||||||
|
|
||||||
|
|
||||||
/* ts A60904 : ticket 62, contribution by elmom */
|
/* ts A60904 : ticket 62, contribution by elmom */
|
||||||
/**
|
/**
|
||||||
@ -120,10 +112,6 @@ struct burn_drive
|
|||||||
enum burn_disc_status status;
|
enum burn_disc_status status;
|
||||||
int erasable;
|
int erasable;
|
||||||
volatile int released;
|
volatile int released;
|
||||||
|
|
||||||
/* ts A61106 */
|
|
||||||
int silent_on_scsi_error;
|
|
||||||
|
|
||||||
int nwa; /* next writeable address */
|
int nwa; /* next writeable address */
|
||||||
int alba; /* absolute lba */
|
int alba; /* absolute lba */
|
||||||
int rlba; /* relative lba in section */
|
int rlba; /* relative lba in section */
|
||||||
@ -140,26 +128,17 @@ struct burn_drive
|
|||||||
/* transport functions */
|
/* transport functions */
|
||||||
int (*grab) (struct burn_drive *);
|
int (*grab) (struct burn_drive *);
|
||||||
int (*release) (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 *);
|
int (*issue_command) (struct burn_drive *, struct command *);
|
||||||
|
|
||||||
/* lower level functions */
|
/* lower level functions */
|
||||||
void (*erase) (struct burn_drive *, int);
|
void (*erase) (struct burn_drive *, int);
|
||||||
void (*getcaps) (struct burn_drive *);
|
void (*getcaps) (struct burn_drive *);
|
||||||
|
|
||||||
/* ts A61021 */
|
|
||||||
void (*read_atip) (struct burn_drive *);
|
|
||||||
|
|
||||||
int (*write) (struct burn_drive *, int, struct buffer *);
|
int (*write) (struct burn_drive *, int, struct buffer *);
|
||||||
void (*read_toc) (struct burn_drive *);
|
void (*read_toc) (struct burn_drive *);
|
||||||
void (*lock) (struct burn_drive *);
|
void (*lock) (struct burn_drive *);
|
||||||
void (*unlock) (struct burn_drive *);
|
void (*unlock) (struct burn_drive *);
|
||||||
void (*eject) (struct burn_drive *);
|
void (*eject) (struct burn_drive *);
|
||||||
void (*load) (struct burn_drive *);
|
void (*load) (struct burn_drive *);
|
||||||
int (*start_unit) (struct burn_drive *);
|
|
||||||
void (*read_disc_info) (struct burn_drive *);
|
void (*read_disc_info) (struct burn_drive *);
|
||||||
void (*read_sectors) (struct burn_drive *,
|
void (*read_sectors) (struct burn_drive *,
|
||||||
int start,
|
int start,
|
||||||
@ -174,32 +153,17 @@ struct burn_drive
|
|||||||
void (*send_cue_sheet) (struct burn_drive *, struct cue_sheet *);
|
void (*send_cue_sheet) (struct burn_drive *, struct cue_sheet *);
|
||||||
void (*sync_cache) (struct burn_drive *);
|
void (*sync_cache) (struct burn_drive *);
|
||||||
int (*get_erase_progress) (struct burn_drive *);
|
int (*get_erase_progress) (struct burn_drive *);
|
||||||
int (*get_nwa) (struct burn_drive *, int trackno, int *lba, int *nwa);
|
int (*get_nwa) (struct burn_drive *);
|
||||||
|
void (*close_disc) (struct burn_drive * d, struct burn_write_opts * o);
|
||||||
/* ts A61009 : removed d in favor of o->drive */
|
void (*close_session) (struct burn_drive * d,
|
||||||
/* void (*close_disc) (struct burn_drive * d,
|
|
||||||
struct burn_write_opts * o);
|
|
||||||
void (*close_session) (struct burn_drive * d,
|
|
||||||
struct burn_write_opts * o);
|
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);
|
int (*test_unit_ready) (struct burn_drive * d);
|
||||||
void (*probe_write_modes) (struct burn_drive * d);
|
void (*probe_write_modes) (struct burn_drive * d);
|
||||||
struct params params;
|
struct params params;
|
||||||
struct burn_scsi_inquiry_data *idata;
|
struct scsi_inquiry_data *idata;
|
||||||
struct scsi_mode_data *mdata;
|
struct scsi_mode_data *mdata;
|
||||||
int toc_entries;
|
int toc_entries;
|
||||||
struct burn_toc_entry *toc_entry;
|
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 */
|
/* end of generic 'drive' data structures */
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
/* ts A61008 */
|
|
||||||
/* #include <a ssert.h> */
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include "../version.h"
|
#include "../version.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
@ -13,10 +10,7 @@ char *burn_strdup(char *s)
|
|||||||
char *ret;
|
char *ret;
|
||||||
int l;
|
int l;
|
||||||
|
|
||||||
/* ts A61008 */
|
assert(s);
|
||||||
/* a ssert(s); */
|
|
||||||
if (s == NULL)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
l = strlen(s) + 1;
|
l = strlen(s) + 1;
|
||||||
ret = malloc(l);
|
ret = malloc(l);
|
||||||
@ -30,11 +24,8 @@ char *burn_strndup(char *s, int n)
|
|||||||
char *ret;
|
char *ret;
|
||||||
int l;
|
int l;
|
||||||
|
|
||||||
/* ts A61008 */
|
assert(s);
|
||||||
/* a ssert(s); */
|
assert(n > 0);
|
||||||
/* a ssert(n > 0); */
|
|
||||||
if (s == NULL || n <= 0)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
l = strlen(s);
|
l = strlen(s);
|
||||||
ret = malloc(l < n ? l : n);
|
ret = malloc(l < n ? l : n);
|
||||||
|
501
libburn/write.c
501
libburn/write.c
@ -2,23 +2,7 @@
|
|||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
#include <assert.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 <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
@ -28,6 +12,7 @@
|
|||||||
#include "libburn.h"
|
#include "libburn.h"
|
||||||
#include "drive.h"
|
#include "drive.h"
|
||||||
#include "transport.h"
|
#include "transport.h"
|
||||||
|
#include "message.h"
|
||||||
#include "crc.h"
|
#include "crc.h"
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
#include "init.h"
|
#include "init.h"
|
||||||
@ -38,10 +23,6 @@
|
|||||||
#include "write.h"
|
#include "write.h"
|
||||||
#include "options.h"
|
#include "options.h"
|
||||||
|
|
||||||
#include "libdax_msgs.h"
|
|
||||||
extern struct libdax_msgs *libdax_messenger;
|
|
||||||
|
|
||||||
|
|
||||||
static int type_to_ctrl(int mode)
|
static int type_to_ctrl(int mode)
|
||||||
{
|
{
|
||||||
int ctrl = 0;
|
int ctrl = 0;
|
||||||
@ -56,9 +37,7 @@ static int type_to_ctrl(int mode)
|
|||||||
if (mode & BURN_PREEMPHASIS)
|
if (mode & BURN_PREEMPHASIS)
|
||||||
ctrl |= 1;
|
ctrl |= 1;
|
||||||
} else
|
} else
|
||||||
/* ts A61008 */
|
assert(0);
|
||||||
/* a ssert(0); */
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
if (mode & BURN_COPY)
|
if (mode & BURN_COPY)
|
||||||
ctrl |= 2;
|
ctrl |= 2;
|
||||||
@ -67,40 +46,18 @@ static int type_to_ctrl(int mode)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* only the ctrl nibble is set here (not adr) */
|
/* only the ctrl nibble is set here (not adr) */
|
||||||
/* ts A61009 : removed "static" , reacted on type_to_ctrl() == -1
|
static void type_to_form(int mode, unsigned char *ctladr, int *form)
|
||||||
preserved ignorance towards unknown modes (for now) */
|
|
||||||
void type_to_form(int mode, unsigned char *ctladr, int *form)
|
|
||||||
{
|
{
|
||||||
int ret;
|
*ctladr = type_to_ctrl(mode) << 4;
|
||||||
|
|
||||||
ret = type_to_ctrl(mode) << 4;
|
|
||||||
if (ret == -1) {
|
|
||||||
*ctladr = 0xff;
|
|
||||||
*form = -1;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
*ctladr = ret;
|
|
||||||
|
|
||||||
if (mode & BURN_AUDIO)
|
if (mode & BURN_AUDIO)
|
||||||
*form = 0;
|
*form = 0;
|
||||||
if (mode & BURN_MODE0) {
|
if (mode & BURN_MODE0)
|
||||||
|
assert(0);
|
||||||
/* ts A61009 */
|
|
||||||
/* a ssert(0); */
|
|
||||||
*form = -1;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mode & BURN_MODE1)
|
if (mode & BURN_MODE1)
|
||||||
*form = 0x10;
|
*form = 0x10;
|
||||||
if (mode & BURN_MODE2) {
|
if (mode & BURN_MODE2)
|
||||||
|
assert(0); /* XXX someone's gonna want this sometime */
|
||||||
/* ts A61009 */
|
|
||||||
/* a ssert(0); */ /* XXX someone's gonna want this sometime */
|
|
||||||
*form = -1;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mode & BURN_MODE_RAW)
|
if (mode & BURN_MODE_RAW)
|
||||||
*form = 0;
|
*form = 0;
|
||||||
if (mode & BURN_SUBCODE_P16) /* must be expanded to R96 */
|
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;
|
*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;
|
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);
|
err = d->write(d, d->nwa, d->buffer);
|
||||||
if (err == BE_CANCELLED)
|
if (err == BE_CANCELLED)
|
||||||
return 0;
|
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->nwa += d->buffer->sectors;
|
||||||
d->buffer->bytes = 0;
|
|
||||||
d->buffer->sectors = 0;
|
|
||||||
}
|
}
|
||||||
d->sync_cache(d);
|
d->sync_cache(d);
|
||||||
return 1;
|
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:
|
/* ts A60819:
|
||||||
This is unused since about Feb 2006, icculus.org/burn CVS.
|
This is unused since about Feb 2006, icculus.org/burn CVS.
|
||||||
The compiler complains. We shall please our compiler.
|
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 */
|
#endif /* Libburn_write_with_print_cuE */
|
||||||
|
|
||||||
|
|
||||||
/* ts A61009 : changed type from void to int */
|
static void add_cue(struct cue_sheet *sheet, unsigned char ctladr,
|
||||||
/** @return 1 = success , <=0 failure */
|
|
||||||
static int add_cue(struct cue_sheet *sheet, unsigned char ctladr,
|
|
||||||
unsigned char tno, unsigned char indx,
|
unsigned char tno, unsigned char indx,
|
||||||
unsigned char form, unsigned char scms, int lba)
|
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++;
|
sheet->count++;
|
||||||
ptr = realloc(sheet->data, sheet->count * 8);
|
ptr = realloc(sheet->data, sheet->count * 8);
|
||||||
|
assert(ptr);
|
||||||
/* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
sheet->data = ptr;
|
sheet->data = ptr;
|
||||||
unit = sheet->data + (sheet->count - 1) * 8;
|
unit = sheet->data + (sheet->count - 1) * 8;
|
||||||
unit[0] = ctladr;
|
unit[0] = ctladr;
|
||||||
@ -290,15 +133,12 @@ static int add_cue(struct cue_sheet *sheet, unsigned char ctladr,
|
|||||||
unit[5] = m;
|
unit[5] = m;
|
||||||
unit[6] = s;
|
unit[6] = s;
|
||||||
unit[7] = f;
|
unit[7] = f;
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ts A61114: added parameter nwa */
|
|
||||||
struct cue_sheet *burn_create_toc_entries(struct burn_write_opts *o,
|
struct cue_sheet *burn_create_toc_entries(struct burn_write_opts *o,
|
||||||
struct burn_session *session,
|
struct burn_session *session)
|
||||||
int nwa)
|
|
||||||
{
|
{
|
||||||
int i, m, s, f, form, pform, runtime = -150, ret;
|
int i, m, s, f, form, pform, runtime = -150;
|
||||||
unsigned char ctladr;
|
unsigned char ctladr;
|
||||||
struct burn_drive *d;
|
struct burn_drive *d;
|
||||||
struct burn_toc_entry *e;
|
struct burn_toc_entry *e;
|
||||||
@ -309,64 +149,21 @@ struct cue_sheet *burn_create_toc_entries(struct burn_write_opts *o,
|
|||||||
|
|
||||||
d = o->drive;
|
d = o->drive;
|
||||||
|
|
||||||
#ifdef Libburn_sao_can_appenD
|
|
||||||
if (d->status == BURN_DISC_APPENDABLE)
|
|
||||||
runtime = nwa-150;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
sheet = malloc(sizeof(struct cue_sheet));
|
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->data = NULL;
|
||||||
sheet->count = 0;
|
sheet->count = 0;
|
||||||
|
|
||||||
type_to_form(tar[0]->mode, &ctladr, &form);
|
type_to_form(tar[0]->mode, &ctladr, &form);
|
||||||
if (form == -1) {
|
add_cue(sheet, ctladr | 1, 0, 0, 1, 0, runtime);
|
||||||
libdax_msgs_submit(libdax_messenger, -1, 0x00020116,
|
add_cue(sheet, ctladr | 1, 1, 0, form, 0, runtime);
|
||||||
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;
|
|
||||||
runtime += 150;
|
runtime += 150;
|
||||||
|
|
||||||
burn_print(1, "toc for %d tracks:\n", ntr);
|
burn_print(1, "toc for %d tracks:\n", ntr);
|
||||||
d->toc_entries = ntr + 3;
|
d->toc_entries = ntr + 3;
|
||||||
|
assert(d->toc_entry == NULL);
|
||||||
/* ts A61009 */
|
d->toc_entry = malloc(d->toc_entries * sizeof(struct burn_toc_entry));
|
||||||
/* 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));
|
|
||||||
e = d->toc_entry;
|
e = d->toc_entry;
|
||||||
|
memset((void *)e, 0, d->toc_entries * sizeof(struct burn_toc_entry));
|
||||||
e[0].point = 0xA0;
|
e[0].point = 0xA0;
|
||||||
if (tar[0]->mode & BURN_AUDIO)
|
if (tar[0]->mode & BURN_AUDIO)
|
||||||
e[0].control = TOC_CONTROL_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++) {
|
for (i = 0; i < ntr; i++) {
|
||||||
type_to_form(tar[i]->mode, &ctladr, &form);
|
type_to_form(tar[i]->mode, &ctladr, &form);
|
||||||
if (pform != form) {
|
if (pform != form) {
|
||||||
|
add_cue(sheet, ctladr | 1, i + 1, 0, form, 0, runtime);
|
||||||
ret = add_cue(sheet, ctladr | 1, i + 1, 0, form, 0,
|
|
||||||
runtime);
|
|
||||||
if (ret <= 0)
|
|
||||||
goto failed;
|
|
||||||
|
|
||||||
runtime += 150;
|
runtime += 150;
|
||||||
/* XXX fix pregap interval 1 for data tracks */
|
/* XXX fix pregap interval 1 for data tracks */
|
||||||
/* ts A60813 silence righteous compiler warning about C++ style comments
|
/* 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);
|
e[3 + i].control = type_to_ctrl(tar[i]->mode);
|
||||||
burn_print(1, "track %d control %d\n", tar[i]->mode,
|
burn_print(1, "track %d control %d\n", tar[i]->mode,
|
||||||
e[3 + i].control);
|
e[3 + i].control);
|
||||||
|
add_cue(sheet, ctladr | 1, i + 1, 1, form, 0, runtime);
|
||||||
ret = add_cue(sheet, ctladr | 1, i + 1, 1, form, 0, runtime);
|
|
||||||
if (ret <= 0)
|
|
||||||
goto failed;
|
|
||||||
|
|
||||||
runtime += burn_track_get_sectors(tar[i]);
|
runtime += burn_track_get_sectors(tar[i]);
|
||||||
/* if we're padding, we'll clear any current shortage.
|
/* 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
|
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) {
|
if (!tar[i]->pad) {
|
||||||
rem += burn_track_get_shortage(tar[i]);
|
rem += burn_track_get_shortage(tar[i]);
|
||||||
|
if (i +1 != ntr)
|
||||||
/* ts A61101 : I doubt that linking would yield a
|
tar[i]->source->next = tar[i+1]->source;
|
||||||
desireable effect. With TAO it is
|
|
||||||
counterproductive in any way.
|
|
||||||
*/
|
|
||||||
if (o->write_type == BURN_WRITE_TAO)
|
|
||||||
tar[i]->source->next = NULL;
|
|
||||||
else
|
|
||||||
|
|
||||||
if (i +1 != ntr)
|
|
||||||
tar[i]->source->next = tar[i+1]->source;
|
|
||||||
} else if (rem) {
|
} else if (rem) {
|
||||||
rem = 0;
|
rem = 0;
|
||||||
runtime++;
|
runtime++;
|
||||||
@ -462,15 +241,8 @@ XXX this is untested :)
|
|||||||
burn_print(1, "point %d (%02d:%02d:%02d)\n",
|
burn_print(1, "point %d (%02d:%02d:%02d)\n",
|
||||||
d->toc_entry[i].point, d->toc_entry[i].pmin,
|
d->toc_entry[i].point, d->toc_entry[i].pmin,
|
||||||
d->toc_entry[i].psec, d->toc_entry[i].pframe);
|
d->toc_entry[i].psec, d->toc_entry[i].pframe);
|
||||||
ret = add_cue(sheet, ctladr | 1, 0xAA, 1, 1, 0, runtime);
|
add_cue(sheet, ctladr | 1, 0xAA, 1, 1, 0, runtime);
|
||||||
if (ret <= 0)
|
|
||||||
goto failed;
|
|
||||||
return sheet;
|
return sheet;
|
||||||
|
|
||||||
failed:;
|
|
||||||
if (sheet != NULL)
|
|
||||||
free((char *) sheet);
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int burn_sector_length(int tracktype)
|
int burn_sector_length(int tracktype)
|
||||||
@ -481,9 +253,8 @@ int burn_sector_length(int tracktype)
|
|||||||
return 2352;
|
return 2352;
|
||||||
if (tracktype & BURN_MODE1)
|
if (tracktype & BURN_MODE1)
|
||||||
return 2048;
|
return 2048;
|
||||||
/* ts A61009 */
|
assert(0);
|
||||||
/* a ssert(0); */
|
return 12345;
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int burn_subcode_length(int tracktype)
|
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_drive *d = o->drive;
|
||||||
struct burn_track *prev = NULL, *next = NULL;
|
struct burn_track *prev = NULL, *next = NULL;
|
||||||
int i, ret;
|
int i;
|
||||||
|
|
||||||
d->rlba = 0;
|
d->rlba = 0;
|
||||||
burn_print(1, " writing a session\n");
|
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;
|
next = NULL;
|
||||||
|
|
||||||
if (!burn_write_track(o, s, i))
|
if (!burn_write_track(o, s, i))
|
||||||
{ ret = 0; goto ex; }
|
return 0;
|
||||||
}
|
}
|
||||||
|
return 1;
|
||||||
/* ts A61103 */
|
|
||||||
ret = 1;
|
|
||||||
ex:;
|
|
||||||
if (o->write_type == BURN_WRITE_TAO)
|
|
||||||
burn_write_close_session(o, s);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int burn_write_track(struct burn_write_opts *o, struct burn_session *s,
|
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_track *t = s->track[tnum];
|
||||||
struct burn_drive *d = o->drive;
|
struct burn_drive *d = o->drive;
|
||||||
int i, tmp = 0, open_ended = 0, ret= 0, nwa, lba;
|
int i, tmp = 0;
|
||||||
int sectors;
|
int sectors;
|
||||||
char msg[80];
|
|
||||||
|
|
||||||
d->rlba = -150;
|
d->rlba = -150;
|
||||||
|
|
||||||
/* XXX for tao, we don't want the pregaps but still want post? */
|
/* XXX for tao, we don't want the pregaps but still want post? */
|
||||||
if (o->write_type != BURN_WRITE_TAO) {
|
if (o->write_type != BURN_WRITE_TAO) {
|
||||||
|
|
||||||
/* ts A61102 */
|
|
||||||
d->busy = BURN_DRIVE_WRITING_PREGAP;
|
|
||||||
|
|
||||||
if (t->pregap1)
|
if (t->pregap1)
|
||||||
d->rlba += 75;
|
d->rlba += 75;
|
||||||
if (t->pregap2)
|
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++)
|
for (i = 0; i < 75; i++)
|
||||||
if (!sector_pregap(o, t->entry->point,
|
if (!sector_pregap(o, t->entry->point,
|
||||||
pt->entry->control, pt->mode))
|
pt->entry->control, pt->mode))
|
||||||
{ ret = 0; goto ex; }
|
return 0;
|
||||||
}
|
}
|
||||||
if (t->pregap2)
|
if (t->pregap2)
|
||||||
for (i = 0; i < 150; i++)
|
for (i = 0; i < 150; i++)
|
||||||
if (!sector_pregap(o, t->entry->point,
|
if (!sector_pregap(o, t->entry->point,
|
||||||
t->entry->control, t->mode))
|
t->entry->control, t->mode))
|
||||||
{ ret = 0; goto ex; }
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
o->control = t->entry->control;
|
o->control = t->entry->control;
|
||||||
d->send_write_parameters(d, o);
|
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 */
|
/* user data */
|
||||||
|
|
||||||
/* ts A61102 */
|
|
||||||
d->busy = BURN_DRIVE_WRITING;
|
|
||||||
|
|
||||||
sectors = burn_track_get_sectors(t);
|
sectors = burn_track_get_sectors(t);
|
||||||
open_ended = burn_track_is_open_ended(t);
|
|
||||||
|
|
||||||
/* Update progress */
|
/* Update progress */
|
||||||
d->progress.start_sector = d->nwa;
|
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
|
/* ts A60831: added tnum-line, extended print message on proposal
|
||||||
by bonfire-app@wanadoo.fr in http://libburn.pykix.org/ticket/58 */
|
by bonfire-app@wanadoo.fr in http://libburn.pykix.org/ticket/58 */
|
||||||
d->progress.track = tnum;
|
d->progress.track = tnum;
|
||||||
|
|
||||||
burn_print(12, "track %d is %d sectors long\n", tnum, sectors);
|
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)
|
if (tnum == s->tracks)
|
||||||
tmp = sectors > 150 ? 150 : sectors;
|
tmp = sectors > 150 ? 150 : sectors;
|
||||||
|
|
||||||
for (i = 0; open_ended || i < sectors - tmp; i++) {
|
for (i = 0; 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);
|
|
||||||
|
|
||||||
if (!sector_data(o, t, 0))
|
if (!sector_data(o, t, 0))
|
||||||
{ ret = 0; goto ex; }
|
return 0;
|
||||||
|
|
||||||
/* ts A61031 */
|
|
||||||
if (open_ended) {
|
|
||||||
d->progress.sectors = sectors = i;
|
|
||||||
if (burn_track_is_data_done(t))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* update current progress */
|
/* update current progress */
|
||||||
d->progress.sector++;
|
d->progress.sector++;
|
||||||
}
|
}
|
||||||
for (; i < sectors; i++) {
|
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");
|
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))
|
if (!sector_data(o, t, 1))
|
||||||
{ ret = 0; goto ex; }
|
return 0;
|
||||||
|
|
||||||
/* update progress */
|
/* update progress */
|
||||||
d->progress.sector++;
|
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++)
|
for (i = 0; i < 150; i++)
|
||||||
if (!sector_postgap(o, t->entry->point, t->entry->control,
|
if (!sector_postgap(o, t->entry->point, t->entry->control,
|
||||||
t->mode))
|
t->mode))
|
||||||
{ ret = 0; goto ex; }
|
return 0;
|
||||||
i = t->offset;
|
i = t->offset;
|
||||||
if (o->write_type == BURN_WRITE_SAO) {
|
if (o->write_type == BURN_WRITE_SAO) {
|
||||||
if (d->buffer->bytes) {
|
if (d->buffer->bytes) {
|
||||||
int err;
|
int err;
|
||||||
err = d->write(d, d->nwa, d->buffer);
|
err = d->write(d, d->nwa, d->buffer);
|
||||||
if (err == BE_CANCELLED)
|
if (err == BE_CANCELLED)
|
||||||
{ ret = 0; goto ex; }
|
return 0;
|
||||||
|
|
||||||
/* A61101 : probably this is not all payload data */
|
|
||||||
/* A61108 : but audio count is short without this */
|
|
||||||
t->writecount += d->buffer->bytes;
|
|
||||||
t->written_sectors += d->buffer->sectors;
|
|
||||||
d->progress.buffered_bytes += d->buffer->bytes;
|
|
||||||
|
|
||||||
d->nwa += d->buffer->sectors;
|
d->nwa += d->buffer->sectors;
|
||||||
d->buffer->bytes = 0;
|
d->buffer->bytes = 0;
|
||||||
d->buffer->sectors = 0;
|
d->buffer->sectors = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (o->write_type == BURN_WRITE_TAO)
|
||||||
/* ts A61103 */
|
if (!burn_write_flush(o))
|
||||||
ret = 1;
|
return 0;
|
||||||
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;
|
|
||||||
return 1;
|
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)
|
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 burn_drive *d = o->drive;
|
||||||
struct buffer buf;
|
struct buffer buf;
|
||||||
struct burn_track *lt;
|
struct burn_track *lt;
|
||||||
int first = 1, i, ret, lba, nwa = 0;
|
int first = 1, i;
|
||||||
char msg[80];
|
int res;
|
||||||
|
|
||||||
/* ts A60924 : libburn/message.c gets obsoleted
|
|
||||||
burn_message_clear_queue();
|
burn_message_clear_queue();
|
||||||
*/
|
|
||||||
|
|
||||||
burn_print(1, "sync write of %d sessions\n", disc->sessions);
|
burn_print(1, "sync write of %d sessions\n", disc->sessions);
|
||||||
d->buffer = &buf;
|
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
|
/* Apparently some drives require this command to be sent, and a few drives
|
||||||
return crap. so we send the command, then ignore the result.
|
return crap. so we send the command, then ignore the result.
|
||||||
*/
|
*/
|
||||||
/* ts A61107 : moved up send_write_parameters because LG GSA-4082B
|
res = d->get_nwa(d);
|
||||||
seems to dislike get_nwa() in advance */
|
/* printf("ignored nwa: %d\n", res);*/
|
||||||
d->alba = d->start_lba; /* ts A61114: this looks senseless */
|
|
||||||
|
d->alba = d->start_lba;
|
||||||
d->nwa = d->alba;
|
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);
|
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 */
|
/* init progress before showing the state */
|
||||||
d->progress.session = 0;
|
d->progress.session = 0;
|
||||||
d->progress.sessions = disc->sessions;
|
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.sectors = 0;
|
||||||
d->progress.sector = 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;
|
d->busy = BURN_DRIVE_WRITING;
|
||||||
|
|
||||||
for (i = 0; i < disc->sessions; i++) {
|
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.session = i;
|
||||||
d->progress.tracks = disc->session[i]->tracks;
|
d->progress.tracks = disc->session[i]->tracks;
|
||||||
|
|
||||||
/* ts A61114: added parameter nwa */
|
sheet = burn_create_toc_entries(o, disc->session[i]);
|
||||||
sheet = burn_create_toc_entries(o, disc->session[i], nwa);
|
|
||||||
|
|
||||||
/* ts A61009 */
|
|
||||||
if (sheet == NULL)
|
|
||||||
goto fail;
|
|
||||||
|
|
||||||
/* print_cue(sheet);*/
|
/* print_cue(sheet);*/
|
||||||
if (o->write_type == BURN_WRITE_SAO)
|
if (o->write_type == BURN_WRITE_SAO)
|
||||||
d->send_cue_sheet(d, sheet);
|
d->send_cue_sheet(d, sheet);
|
||||||
@ -847,39 +502,8 @@ return crap. so we send the command, then ignore the result.
|
|||||||
goto fail;
|
goto fail;
|
||||||
} else {
|
} else {
|
||||||
if (first) {
|
if (first) {
|
||||||
|
d->nwa = -150;
|
||||||
/* ts A61030 : 0 made the burner take data. */
|
d->alba = -150;
|
||||||
/* ts A61103 : Meanwhile d->nwa is updated in
|
|
||||||
burn_write_track() */
|
|
||||||
if(o->write_type == BURN_WRITE_TAO) {
|
|
||||||
d->nwa= d->alba = 0;
|
|
||||||
} else {
|
|
||||||
|
|
||||||
#ifdef Libburn_sao_can_appenD
|
|
||||||
/* ts A61114: address for d->write() */
|
|
||||||
if (d->status == BURN_DISC_APPENDABLE
|
|
||||||
&& o->write_type == BURN_WRITE_SAO) {
|
|
||||||
d->nwa = d->alba = nwa-150;
|
|
||||||
|
|
||||||
sprintf(msg,
|
|
||||||
"SAO appendable d->nwa= %d\n", d->nwa);
|
|
||||||
libdax_msgs_submit(
|
|
||||||
libdax_messenger, d->global_index, 0x000002,
|
|
||||||
LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO,
|
|
||||||
msg,0,0);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
d->nwa = -150;
|
|
||||||
d->alba = -150;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
d->nwa = -150;
|
|
||||||
d->alba = -150;
|
|
||||||
#endif /* ! Libburn_sao_can_appenD */
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
d->nwa += 4500;
|
d->nwa += 4500;
|
||||||
d->alba += 4500;
|
d->alba += 4500;
|
||||||
@ -894,13 +518,8 @@ return crap. so we send the command, then ignore the result.
|
|||||||
lt->mode))
|
lt->mode))
|
||||||
goto fail;
|
goto fail;
|
||||||
} else {
|
} else {
|
||||||
|
if (!burn_write_flush(o))
|
||||||
/* ts A61030 */
|
goto fail;
|
||||||
if (o->write_type != BURN_WRITE_TAO)
|
|
||||||
|
|
||||||
if (!burn_write_flush(o, NULL))
|
|
||||||
goto fail;
|
|
||||||
|
|
||||||
d->nwa += first ? 6750 : 2250;
|
d->nwa += first ? 6750 : 2250;
|
||||||
d->alba += 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.start_sector = 0;
|
||||||
d->progress.sectors = 0;
|
d->progress.sectors = 0;
|
||||||
}
|
}
|
||||||
|
if (o->write_type != BURN_WRITE_SAO)
|
||||||
/* ts A61030: extended skipping of flush to TAO: session is closed */
|
if (!burn_write_flush(o))
|
||||||
if (o->write_type != BURN_WRITE_SAO && o->write_type != BURN_WRITE_TAO)
|
|
||||||
if (!burn_write_flush(o, NULL))
|
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
sleep(1);
|
sleep(1);
|
||||||
|
|
||||||
burn_print(1, "done\n");
|
burn_print(1, "done\n");
|
||||||
d->busy = BURN_DRIVE_IDLE;
|
d->busy = BURN_DRIVE_IDLE;
|
||||||
|
|
||||||
/* ts A61012 : This return was traditionally missing. I suspect this
|
|
||||||
to have caused Cdrskin_eject() failures */
|
|
||||||
return;
|
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
d->sync_cache(d);
|
d->sync_cache(d);
|
||||||
burn_print(1, "done - failed\n");
|
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;
|
d->busy = BURN_DRIVE_IDLE;
|
||||||
}
|
}
|
||||||
|
@ -9,14 +9,9 @@ struct burn_write_opts;
|
|||||||
struct burn_disc;
|
struct burn_disc;
|
||||||
|
|
||||||
struct cue_sheet *burn_create_toc_entries(struct burn_write_opts *o,
|
struct cue_sheet *burn_create_toc_entries(struct burn_write_opts *o,
|
||||||
struct burn_session *session,
|
struct burn_session *session);
|
||||||
int nwa);
|
|
||||||
int burn_sector_length(int trackmode);
|
int burn_sector_length(int trackmode);
|
||||||
int burn_subcode_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);
|
void burn_disc_write_sync(struct burn_write_opts *o, struct burn_disc *disc);
|
||||||
int burn_write_leadin(struct burn_write_opts *o,
|
int burn_write_leadin(struct burn_write_opts *o,
|
||||||
struct burn_session *s, int first);
|
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_session(struct burn_write_opts *o, struct burn_session *s);
|
||||||
int burn_write_track(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 tnum);
|
||||||
int burn_write_flush(struct burn_write_opts *o, struct burn_track *track);
|
int burn_write_flush(struct burn_write_opts *o);
|
||||||
|
|
||||||
/* ts A61030 : necessary for TAO */
|
|
||||||
int burn_write_close_track(struct burn_write_opts *o, struct burn_session *s,
|
|
||||||
int tnum);
|
|
||||||
int burn_write_close_session(struct burn_write_opts *o,struct burn_session *s);
|
|
||||||
|
|
||||||
#endif /* BURN__WRITE_H */
|
#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 */
|
/* test/libburner.c , API illustration of burning a single data track to CD */
|
||||||
/* Copyright (C) 2005 - 2006 Thomas Schmitt <scdbackup@gmx.net> */
|
/* Copyright (C) 2005 - 2006 Thomas Schmitt <scdbackup@gmx.net> */
|
||||||
/* Provided under GPL, see also "License and copyright aspects" at file end */
|
/* Provided under GPL, see also "License and copyright aspects" at file end */
|
||||||
|
|
||||||
|
|
||||||
/** Overview
|
/** Overview
|
||||||
@ -16,8 +16,6 @@
|
|||||||
|
|
||||||
Before you can do anything, you have to initialize libburn by
|
Before you can do anything, you have to initialize libburn by
|
||||||
burn_initialize()
|
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
|
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
|
drive in an appropriate way conforming to the API. The two main
|
||||||
approaches are shown here in application functions:
|
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 */
|
/** See this for the decisive API specs . libburn.h is The Original */
|
||||||
/* For using the installed header file : #include <libburn/libburn.h> */
|
#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 */
|
/* libburn is intended for Linux systems with kernel 2.4 or 2.6 for now */
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@ -49,8 +54,6 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
|
|
||||||
|
|
||||||
/** For simplicity i use global variables to represent the drives.
|
/** 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;
|
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
|
/* Some in-advance definitions to allow a more comprehensive ordering
|
||||||
of the functions and their explanations in here */
|
of the functions and their explanations in here */
|
||||||
int libburner_aquire_by_adr(char *drive_adr);
|
int libburner_aquire_by_adr(char *drive_adr);
|
||||||
@ -115,6 +125,9 @@ int libburner_aquire_by_adr(char *drive_adr)
|
|||||||
if (ret <= 0) {
|
if (ret <= 0) {
|
||||||
fprintf(stderr,"FAILURE with persistent drive address '%s'\n",
|
fprintf(stderr,"FAILURE with persistent drive address '%s'\n",
|
||||||
drive_adr);
|
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 {
|
} else {
|
||||||
printf("Done\n");
|
printf("Done\n");
|
||||||
drive_is_grabbed = 1;
|
drive_is_grabbed = 1;
|
||||||
@ -174,6 +187,7 @@ int libburner_aquire_by_driveno(int *driveno)
|
|||||||
}
|
}
|
||||||
printf("-----------------------------------------------------------------------------\n\n");
|
printf("-----------------------------------------------------------------------------\n\n");
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
On multi-drive systems save yourself from sysadmins' revenge.
|
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
|
To our knowledge it is hardly possible to abort an ongoing blank operation
|
||||||
because after start it is entirely handled by the drive.
|
because after start it is entirely handled by the drive.
|
||||||
So expect signal handling to wait the normal blanking timespan until it
|
So expect a blank run to survive the end of the blanking process and be
|
||||||
can allow the process to end. External kill -9 will not help the drive.
|
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)
|
int libburner_blank_disc(struct burn_drive *drive, int blank_fast)
|
||||||
{
|
{
|
||||||
enum burn_disc_status disc_state;
|
enum burn_disc_status disc_state;
|
||||||
struct burn_progress p;
|
struct burn_progress progress;
|
||||||
int percent = 1;
|
|
||||||
|
|
||||||
while (burn_drive_get_status(drive, NULL) != BURN_DRIVE_IDLE)
|
while (burn_drive_get_status(drive, NULL))
|
||||||
usleep(1001);
|
usleep(1001);
|
||||||
|
|
||||||
while ((disc_state = burn_disc_get_status(drive)) == BURN_DISC_UNREADY)
|
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(
|
printf(
|
||||||
"Beginning to %s-blank CD media.\n", (blank_fast?"fast":"full"));
|
"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);
|
burn_disc_erase(drive, blank_fast);
|
||||||
sleep(1);
|
while (burn_drive_get_status(drive, &progress)) {
|
||||||
while (burn_drive_get_status(drive, &p) != BURN_DRIVE_IDLE) {
|
printf("Blanking sector %d\n", progress.sector);
|
||||||
if(p.sectors>0 && p.sector>=0) /* display 1 to 99 percent */
|
|
||||||
percent = 1.0 + ((double) p.sector+1.0)
|
|
||||||
/ ((double) p.sectors) * 98.0;
|
|
||||||
printf("Blanking ( %d%% done )\n", percent);
|
|
||||||
sleep(1);
|
sleep(1);
|
||||||
}
|
}
|
||||||
printf("Done\n");
|
printf("Done\n");
|
||||||
@ -308,109 +321,109 @@ int libburner_regrab(struct burn_drive *drive) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** Brings preformatted track images (ISO 9660, audio, ...) onto media.
|
/** Brings the preformatted image (ISO 9660, afio, ext2, whatever) 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.
|
|
||||||
|
|
||||||
In case of external signals expect abort handling of an ongoing burn to
|
To make sure your image is fully readable on any Linux machine, this
|
||||||
last up to a minute. Wait the normal burning timespan before any kill -9.
|
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,
|
int libburner_payload(struct burn_drive *drive, const char *source_adr,
|
||||||
char source_adr[][4096], int source_adr_count,
|
off_t size)
|
||||||
int multi, int simulate_burn, int all_tracks_type)
|
|
||||||
{
|
{
|
||||||
struct burn_source *data_src;
|
struct burn_source *data_src;
|
||||||
struct burn_disc *target_disc;
|
struct burn_disc *target_disc;
|
||||||
struct burn_session *session;
|
struct burn_session *session;
|
||||||
struct burn_write_opts *burn_options;
|
struct burn_write_opts *burn_options;
|
||||||
enum burn_disc_status disc_state;
|
enum burn_disc_status disc_state;
|
||||||
struct burn_track *track, *tracklist[99];
|
struct burn_track *track;
|
||||||
struct burn_progress progress;
|
struct burn_progress progress;
|
||||||
time_t start_time;
|
time_t start_time;
|
||||||
int last_sector = 0, padding = 0, trackno, write_mode_tao = 0, fd;
|
int last_sector = 0;
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
target_disc = burn_disc_create();
|
target_disc = burn_disc_create();
|
||||||
session = burn_session_create();
|
session = burn_session_create();
|
||||||
burn_disc_add_session(target_disc, session, BURN_POS_END);
|
burn_disc_add_session(target_disc, session, BURN_POS_END);
|
||||||
|
track = burn_track_create();
|
||||||
|
|
||||||
for (trackno = 0 ; trackno < source_adr_count; trackno++) {
|
/* a padding of 300 kB is helpful to avoid the read-ahead bug */
|
||||||
tracklist[trackno] = track = burn_track_create();
|
burn_track_define_data(track, 0, 300*1024, 1, BURN_MODE1);
|
||||||
burn_track_define_data(track, 0, padding, 1, all_tracks_type);
|
|
||||||
|
|
||||||
adr = source_adr[trackno];
|
if (source_adr[0] == '-' && source_adr[1] == 0) {
|
||||||
fixed_size = 0;
|
data_src = burn_fd_source_new(0, -1, size);
|
||||||
if (adr[0] == '-' && adr[1] == 0) {
|
printf("Note: using standard input as source with %.f bytes\n",
|
||||||
fd = 0;
|
(double) size);
|
||||||
} else {
|
} else
|
||||||
fd = open(adr, O_RDONLY);
|
data_src = burn_file_source_new(source_adr, NULL);
|
||||||
if (fd>=0)
|
if (data_src == NULL) {
|
||||||
if (fstat(fd,&stbuf)!=-1)
|
|
||||||
if((stbuf.st_mode&S_IFMT)==S_IFREG)
|
|
||||||
fixed_size = stbuf.st_size;
|
|
||||||
}
|
|
||||||
if (fixed_size==0)
|
|
||||||
write_mode_tao = 1;
|
|
||||||
data_src = NULL;
|
|
||||||
if (fd>=0)
|
|
||||||
data_src = burn_fd_source_new(fd, -1, fixed_size);
|
|
||||||
if (data_src == NULL) {
|
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"FATAL: Could not open data source '%s'.\n",adr);
|
"FATAL: Could not open data source '%s'.\n",source_adr);
|
||||||
if(errno!=0)
|
if(errno!=0)
|
||||||
fprintf(stderr,"(Most recent system error: %s )\n",
|
fprintf(stderr,"(Most recent system error: %s )\n",
|
||||||
strerror(errno));
|
strerror(errno));
|
||||||
return 0;
|
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");
|
printf("FATAL: Cannot attach source object to track object\n");
|
||||||
return 0;
|
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);
|
while (burn_drive_get_status(drive, NULL))
|
||||||
printf("Track %d : source is '%s'\n", trackno, adr);
|
usleep(1001);
|
||||||
burn_source_free(data_src);
|
|
||||||
} /* trackno loop end */
|
|
||||||
|
|
||||||
while (burn_drive_get_status(drive, NULL) != BURN_DRIVE_IDLE)
|
|
||||||
usleep(100001);
|
|
||||||
|
|
||||||
/* Evaluate drive and media */
|
/* Evaluate drive and media */
|
||||||
while ((disc_state = burn_disc_get_status(drive)) == BURN_DISC_UNREADY)
|
while ((disc_state = burn_disc_get_status(drive)) == BURN_DISC_UNREADY)
|
||||||
usleep(100001);
|
usleep(1001);
|
||||||
if (disc_state == BURN_DISC_APPENDABLE) {
|
if (disc_state != BURN_DISC_BLANK) {
|
||||||
write_mode_tao = 1;
|
if (disc_state == BURN_DISC_FULL ||
|
||||||
} else if (disc_state != BURN_DISC_BLANK) {
|
disc_state == BURN_DISC_APPENDABLE) {
|
||||||
if (disc_state == BURN_DISC_FULL) {
|
fprintf(stderr,
|
||||||
fprintf(stderr, "FATAL: Closed media with data detected. Need blank or appendable media.\n");
|
"FATAL: Media with data detected. Need blank media.\n");
|
||||||
if (burn_disc_erasable(drive))
|
if (burn_disc_erasable(drive))
|
||||||
fprintf(stderr, "HINT: Try --blank_fast\n\n");
|
fprintf(stderr, "HINT: Try --blank_fast\n\n");
|
||||||
} else if (disc_state == BURN_DISC_EMPTY)
|
} else if (disc_state == BURN_DISC_EMPTY)
|
||||||
fprintf(stderr,"FATAL: No media detected in drive\n");
|
fprintf(stderr,"FATAL: No media detected in drive\n");
|
||||||
else
|
else
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"FATAL: Cannot recognize state of drive and media\n");
|
"FATAL: Cannot recognize drive and media state\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
burn_options = burn_write_opts_new(drive);
|
burn_options = burn_write_opts_new(drive);
|
||||||
burn_write_opts_set_perform_opc(burn_options, 0);
|
burn_write_opts_set_perform_opc(burn_options, 0);
|
||||||
burn_write_opts_set_multi(burn_options, !!multi);
|
|
||||||
if (write_mode_tao)
|
#ifdef Libburner_raw_mode_which_i_do_not_likE
|
||||||
burn_write_opts_set_write_type(burn_options,
|
/* This yields higher CD capacity but hampers my IDE controller
|
||||||
BURN_WRITE_TAO, BURN_BLOCK_MODE1);
|
with burning on one drive and reading on another simultaneously.
|
||||||
else
|
My burner does not obey the order --try_to_simulate in this mode.
|
||||||
burn_write_opts_set_write_type(burn_options,
|
*/
|
||||||
BURN_WRITE_SAO, BURN_BLOCK_SAO);
|
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)
|
if(simulate_burn)
|
||||||
printf("\n*** Will TRY to SIMULATE burning ***\n\n");
|
printf("\n*** Will TRY to SIMULATE burning ***\n\n");
|
||||||
burn_write_opts_set_simulate(burn_options, simulate_burn);
|
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);
|
burn_write_opts_free(burn_options);
|
||||||
while (burn_drive_get_status(drive, NULL) == BURN_DRIVE_SPAWNING)
|
while (burn_drive_get_status(drive, NULL) == BURN_DRIVE_SPAWNING)
|
||||||
usleep(1002);
|
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)
|
if( progress.sectors <= 0 || progress.sector == last_sector)
|
||||||
printf(
|
printf(
|
||||||
"Thank you for being patient since %d seconds.\n",
|
"Thank you for being patient since %d seconds.\n",
|
||||||
(int) (time(0) - start_time));
|
(int) (time(0) - start_time));
|
||||||
else if(write_mode_tao)
|
|
||||||
printf("Track %d : sector %d\n", progress.track+1,
|
|
||||||
progress.sector);
|
|
||||||
else
|
else
|
||||||
printf("Track %d : sector %d of %d\n",progress.track+1,
|
printf("Burning sector %d of %d\n",
|
||||||
progress.sector, progress.sectors);
|
progress.sector, progress.sectors);
|
||||||
last_sector = progress.sector;
|
last_sector = progress.sector;
|
||||||
sleep(1);
|
sleep(1);
|
||||||
}
|
}
|
||||||
printf("\n");
|
printf("\n");
|
||||||
|
burn_track_free(track);
|
||||||
for (trackno = 0 ; trackno < source_adr_count; trackno++)
|
|
||||||
burn_track_free(tracklist[trackno]);
|
|
||||||
burn_session_free(session);
|
burn_session_free(session);
|
||||||
burn_disc_free(target_disc);
|
burn_disc_free(target_disc);
|
||||||
if (multi)
|
if(simulate_burn)
|
||||||
printf("NOTE: Media left appendable.\n");
|
|
||||||
if (simulate_burn)
|
|
||||||
printf("\n*** Did TRY to SIMULATE burning ***\n\n");
|
printf("\n*** Did TRY to SIMULATE burning ***\n\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** The setup parameters of libburner */
|
/** Converts command line arguments into a few program parameters.
|
||||||
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.
|
|
||||||
drive_adr[] must provide at least BURN_DRIVE_ADR_LEN bytes.
|
drive_adr[] must provide at least BURN_DRIVE_ADR_LEN bytes.
|
||||||
source_adr[] must provide at least 4096 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) {
|
for (i = 1; i < argc; ++i) {
|
||||||
if (!strcmp(argv[i], "--audio")) {
|
if (!strcmp(argv[i], "--blank_fast")) {
|
||||||
all_tracks_type = BURN_AUDIO;
|
*do_blank = 1;
|
||||||
|
|
||||||
} else if (!strcmp(argv[i], "--blank_fast")) {
|
|
||||||
do_blank = 1;
|
|
||||||
|
|
||||||
} else if (!strcmp(argv[i], "--blank_full")) {
|
} else if (!strcmp(argv[i], "--blank_full")) {
|
||||||
do_blank = 2;
|
*do_blank = 2;
|
||||||
|
|
||||||
} else if (!strcmp(argv[i], "--burn_for_real")) {
|
} else if (!strcmp(argv[i], "--burn_for_real")) {
|
||||||
simulate_burn = 0;
|
simulate_burn = 0;
|
||||||
@ -492,10 +492,10 @@ int libburner_setup(int argc, char **argv)
|
|||||||
return 1;
|
return 1;
|
||||||
} else if (strcmp(argv[i], "-") == 0) {
|
} else if (strcmp(argv[i], "-") == 0) {
|
||||||
drive_adr[0] = 0;
|
drive_adr[0] = 0;
|
||||||
driveno = -1;
|
*driveno = -1;
|
||||||
} else if (isdigit(argv[i][0])) {
|
} else if (isdigit(argv[i][0])) {
|
||||||
drive_adr[0] = 0;
|
drive_adr[0] = 0;
|
||||||
driveno = atoi(argv[i]);
|
*driveno = atoi(argv[i]);
|
||||||
} else {
|
} else {
|
||||||
if(strlen(argv[i]) >= BURN_DRIVE_ADR_LEN) {
|
if(strlen(argv[i]) >= BURN_DRIVE_ADR_LEN) {
|
||||||
fprintf(stderr,"--drive address too long (max. %d)\n",
|
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]);
|
strcpy(drive_adr, argv[i]);
|
||||||
}
|
}
|
||||||
} else if (!strcmp(argv[i], "--multi")) {
|
} else if (!strcmp(argv[i], "--stdin_size")) {
|
||||||
do_multi = 1;
|
++i;
|
||||||
|
if (i >= argc) {
|
||||||
} else if (!strcmp(argv[i], "--stdin_size")) { /* obsoleted */
|
fprintf(stderr,"--stdin_size requires an argument\n");
|
||||||
i++;
|
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")) {
|
} else if (!strcmp(argv[i], "--try_to_simulate")) {
|
||||||
simulate_burn = 1;
|
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")) {
|
} else if (!strcmp(argv[i], "--help")) {
|
||||||
print_help = 1;
|
print_help = 1;
|
||||||
|
|
||||||
} else if (!strncmp(argv[i], "--",2)) {
|
|
||||||
fprintf(stderr, "Unidentified option: %s\n", argv[i]);
|
|
||||||
return 7;
|
|
||||||
} else {
|
} else {
|
||||||
if(strlen(argv[i]) >= 4096) {
|
if(strlen(argv[i]) >= 4096) {
|
||||||
fprintf(stderr, "Source address too long (max. %d)\n", 4096-1);
|
fprintf(stderr, "Source address too long (max. %d)\n", 4096-1);
|
||||||
return 5;
|
return 5;
|
||||||
}
|
}
|
||||||
if(source_adr_count >= 99) {
|
strcpy(source_adr, argv[i]);
|
||||||
fprintf(stderr, "Too many tracks (max. 99)\n");
|
|
||||||
return 6;
|
|
||||||
}
|
|
||||||
strcpy(source_adr[source_adr_count], argv[i]);
|
|
||||||
source_adr_count++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
insuffient_parameters = 1;
|
insuffient_parameters = 1;
|
||||||
if (driveno < 0)
|
if (*driveno < 0)
|
||||||
insuffient_parameters = 0;
|
insuffient_parameters = 0;
|
||||||
if (source_adr_count > 0)
|
if (source_adr[0] != 0)
|
||||||
insuffient_parameters = 0;
|
insuffient_parameters = 0;
|
||||||
if (do_blank)
|
if (*do_blank)
|
||||||
insuffient_parameters = 0;
|
insuffient_parameters = 0;
|
||||||
if (print_help || insuffient_parameters ) {
|
if (print_help || insuffient_parameters ) {
|
||||||
printf("Usage: %s\n", argv[0]);
|
printf("Usage: %s\n", argv[0]);
|
||||||
printf(" [--drive <address>|<driveno>|\"-\"] [--audio]\n");
|
printf(" [--drive <address>|<driveno>|\"-\"]\n");
|
||||||
printf(" [--blank_fast|--blank_full] [--try_to_simulate]\n");
|
printf(" [--verbose <level>] [--blank_fast|--blank_full]\n");
|
||||||
printf(" [--multi] [<one or more imagefiles>|\"-\"]\n");
|
printf(" [--burn_for_real|--try_to_simulate] [--stdin_size <bytes>]\n");
|
||||||
|
printf(" [<imagefile>|\"-\"]\n");
|
||||||
printf("Examples\n");
|
printf("Examples\n");
|
||||||
printf("A bus scan (needs rw-permissions to see a drive):\n");
|
printf("A bus scan (needs rw-permissions to see a drive):\n");
|
||||||
printf(" %s --drive -\n",argv[0]);
|
printf(" %s --drive -\n",argv[0]);
|
||||||
printf("Burn a file to drive chosen by number, leave appendable:\n");
|
printf("Burn a file to drive chosen by number:\n");
|
||||||
printf(" %s --drive 0 --multi my_image_file\n", argv[0]);
|
printf(" %s --drive 0 --burn_for_real my_image_file\n",
|
||||||
printf("Burn a file to drive chosen by persistent address, close:\n");
|
argv[0]);
|
||||||
printf(" %s --drive /dev/hdc 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("Blank a used CD-RW (is combinable with burning in one run):\n");
|
||||||
printf(" %s --drive /dev/hdc --blank_fast\n",argv[0]);
|
printf(" %s --drive 0 --blank_fast\n",argv[0]);
|
||||||
printf("Burn two audio tracks\n");
|
printf("Burn a compressed afio archive on-the-fly, pad up to 700 MB:\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(" ( cd my_directory ; find . -print | afio -oZ - ) | \\\n");
|
printf(" ( cd my_directory ; find . -print | afio -oZ - ) | \\\n");
|
||||||
printf(" %s --drive /dev/hdc -\n", argv[0]);
|
printf(" %s --drive /dev/hdc --burn_for_real --stdin_size 734003200 -\n", argv[0]);
|
||||||
printf("To be read from *not mounted* CD via: afio -tvZ /dev/hdc\n");
|
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");
|
printf("Program tar would need a clean EOF which our padded CD cannot deliver.\n");
|
||||||
if (insuffient_parameters)
|
if (insuffient_parameters)
|
||||||
return 6;
|
return 6;
|
||||||
@ -571,28 +572,24 @@ int libburner_setup(int argc, char **argv)
|
|||||||
|
|
||||||
int main(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)
|
if (ret)
|
||||||
exit(ret);
|
exit(ret);
|
||||||
|
|
||||||
printf("Initializing libburn.pykix.org ...\n");
|
printf("Initializing library ...\n");
|
||||||
if (burn_initialize())
|
if (burn_initialize())
|
||||||
printf("Done\n");
|
printf("Done\n");
|
||||||
else {
|
else {
|
||||||
printf("FAILED\n");
|
printf("FAILED\n");
|
||||||
fprintf(stderr,"\nFATAL: Failed to initialize.\n");
|
fprintf(stderr,"\nFATAL: Failed to initialize libburn.\n");
|
||||||
exit(33);
|
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 */
|
/** Note: driveno might change its value in this call */
|
||||||
ret = libburner_aquire_drive(drive_adr, &driveno);
|
ret = libburner_aquire_drive(drive_adr, &driveno);
|
||||||
if (ret<=0) {
|
if (ret<=0) {
|
||||||
@ -606,7 +603,7 @@ int main(int argc, char **argv)
|
|||||||
do_blank == 1);
|
do_blank == 1);
|
||||||
if (ret<=0)
|
if (ret<=0)
|
||||||
{ ret = 36; goto release_drive; }
|
{ 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);
|
ret = libburner_regrab(drive_list[driveno].drive);
|
||||||
if (ret<=0) {
|
if (ret<=0) {
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
@ -614,10 +611,9 @@ int main(int argc, char **argv)
|
|||||||
{ ret = 37; goto finish_libburn; }
|
{ ret = 37; goto finish_libburn; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (source_adr_count > 0) {
|
if (source_adr[0] != 0) {
|
||||||
ret = libburner_payload(drive_list[driveno].drive,
|
ret = libburner_payload(drive_list[driveno].drive, source_adr,
|
||||||
source_adr, source_adr_count,
|
stdin_size);
|
||||||
do_multi, simulate_burn, all_tracks_type);
|
|
||||||
if (ret<=0)
|
if (ret<=0)
|
||||||
{ ret = 38; goto release_drive; }
|
{ ret = 38; goto release_drive; }
|
||||||
}
|
}
|
||||||
@ -632,7 +628,7 @@ finish_libburn:;
|
|||||||
/* burn_drive_info_free(drive_list); */
|
/* burn_drive_info_free(drive_list); */
|
||||||
|
|
||||||
burn_finish();
|
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