dvd+rw-tools-7.1/Makefile0000644000000100000010000000244410761260577013451 0ustar binbinall: @(m4 -DOS=`uname -s` Makefile.m4 | $(MAKE) -f - dvd+rw-tools) install:# BSD make doesn't support wild-card targets:-( @(m4 -DOS=`uname -s` Makefile.m4 | $(MAKE) -f - $@) nothing:# dumb target .%: # don't mess with Solaris .INIT/.DONE, ... %: # ... but the rest just passes through @(m4 -DOS=`uname -s` Makefile.m4 | $(MAKE) -f - $@) CHAIN=growisofs dvd+rw-format dvd+rw-booktype dvd+rw-mediainfo dvd-ram-control clean: -(rm *.o $(CHAIN) rpl8 btcflash panaflash; exit 0) < /dev/null > /dev/null 2>&1 VER=7.1 DIST=dvd+rw-tools-$(VER) pkg: [ -h $(DIST) ] || ln -s . $(DIST) tar chf dvd+rw-tools-$(VER).tar \ --owner=bin --group=bin \ $(DIST)/Makefile \ $(DIST)/Makefile.m4 \ $(DIST)/dvd+rw-tools.spec \ $(DIST)/growisofs.1 \ $(DIST)/transport.hxx \ $(DIST)/mp.h \ $(DIST)/win32err.h \ $(DIST)/keys.txt \ $(DIST)/genasctable.pl \ $(DIST)/asctable.h \ $(DIST)/growisofs.c \ $(DIST)/growisofs_mmc.cpp \ $(DIST)/dvd+rw-format.cpp \ $(DIST)/dvd+rw-mediainfo.cpp \ $(DIST)/dvd+rw-booktype.cpp \ $(DIST)/dvd-ram-control.cpp \ $(DIST)/rpl8.cpp \ $(DIST)/btcflash.cpp \ $(DIST)/LICENSE if [ -f index.html ]; then \ tar rhf dvd+rw-tools-$(VER).tar \ --owner=bin --group=bin \ $(DIST)/index.html; \ fi gzip -f dvd+rw-tools-$(VER).tar rm $(DIST) dvd+rw-tools-7.1/Makefile.m40000644000000100000010000001375210762560425013770 0ustar binbin# OBS! M4 processed! changequote([, ]) [ CHAIN=growisofs dvd+rw-format dvd+rw-booktype dvd+rw-mediainfo dvd-ram-control dvd+rw-tools: $(CHAIN) WARN=#-Wall # developers are welcomed to build with `make WARN=-Wall' ] # fix-up OS macro ifelse(substr(OS,0,7),[CYGWIN_],[define([OS],[MINGW32])]) ifelse(substr(OS,0,7),[MINGW32],[define([OS],[MINGW32])]) ifelse(OS,NetBSD,[define([OS],[BSD])CXXFLAGS+=-D__unix]) ifelse(OS,OpenBSD,[define([OS],[BSD])]) ifelse(OS,FreeBSD,[define([OS],[BSD])LDLIBS=-lcam]) ifelse(OS,IRIX64,[define([OS],[IRIX])]) ifelse(OS,Darwin,[ # # Mac OS X section # CC =gcc CFLAGS +=$(WARN) -D__unix -O2 CXX =g++ CXXFLAGS+=$(WARN) -D__unix -O2 -fno-exceptions LDLIBS =-framework CoreFoundation -framework IOKit LINK.o =$(LINK.cc) # to install set-root-uid, `make BIN_MODE=04755 install'... BIN_MODE?=0755 install: dvd+rw-tools install -o root -m $(BIN_MODE) $(CHAIN) /usr/bin install -o root -m 0644 growisofs.1 /usr/share/man/man1 ]) ifelse(OS,MINGW32,[ # # MINGW section # CC =gcc CFLAGS +=$(WARN) -mno-cygwin -O2 CXX =g++ CXXFLAGS+=$(WARN) -mno-cygwin -O2 -fno-exceptions LINK.o =$(LINK.cc) ]) ifelse(OS,BSD,[ # # OpenBSD/NetBSD/FreeBSD section # CC ?=gcc CFLAGS +=$(WARN) -O2 -pthread -D_THREAD_SAFE -D_REENTRANT CXX ?=g++ CXXFLAGS+=$(WARN) -O2 -fno-exceptions -pthread -D_THREAS_SAFE -D_REENTRANT .SUFFIXES: .c .cpp .o .c.o: $(CC) $(CFLAGS) -c -o $@ $< .cpp.o: $(CXX) $(CXXFLAGS) -c -o $@ $< .o: # try to please both BSD vv&vv GNU make at the same time... $(CXX) $(CXXFLAGS) -o $@ $> $^ $(LDFLAGS) $(LDLIBS) # yes, default is set-root-uid, `make BIN_MODE=0755 install' to override... BIN_MODE?=04755 install: dvd+rw-tools install -o root -m $(BIN_MODE) $(CHAIN) /usr/local/bin install -o root -m 0644 growisofs.1 /usr/local/man/man1 ]) ifelse(OS,SunOS,[ # # Solaris section # .SUFFIXES: .c .cpp .o # check for WorkShop C++ syscmd([(CC -flags) > /dev/null 2>&1]) ifelse(sysval,0,[ CC =cc CFLAGS =-O -xstrconst -w -D_REENTRANT -D__`uname -s`=`uname -r | tr -d .` CXX =CC CXXFLAGS=-O -features=no%except,conststrings -w -D_REENTRANT LDFLAGS =-staticlib=%all ],[ CC =gcc CFLAGS =$(WARN) -O2 -D_REENTRANT -D__`uname -s`=`uname -r | tr -d .` CXX =g++ CXXFLAGS=$(WARN) -O2 -fno-exceptions -D_REENTRANT ]) LDLIBS=-lvolmgt -lrt -lpthread -ldl .c.o: $(CC) $(CFLAGS) -c -o $@ $< .cpp.o: $(CXX) $(CXXFLAGS) -c -o $@ $< %: %.o $(CXX) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) $(LDLIBS) install: dvd+rw-tools /usr/ucb/install -o root -m 04755 $(CHAIN) /usr/local/bin /usr/ucb/install -o root -m 0644 growisofs.1 /usr/local/man/man1 ]) ifelse(OS,HP-UX,[ # # HP-UX section # .SUFFIXES: .c .cpp .o # check for HP C++ syscmd([(aCC -E /dev/null) > /dev/null 2>&1]) ifelse(sysval,0,[ # run `make TARGET_ARCH=+DD64' for 64-bit build... # ... or should we check for `getconf KERNEL_BITS' and/or CPU_VERSION? # syscmd([([ `getconf KERNEL_BITS` -eq 64 ]) > /dev/null 2>&1]) CC =cc CFLAGS =$(TARGET_ARCH) -O -D_REENTRANT CXX =aCC CXXFLAGS=$(TARGET_ARCH) -O +noeh -D_REENTRANT ],[ CC =gcc CFLAGS =$(WARN) -O2 -D_REENTRANT CXX =g++ CXXFLAGS=$(WARN) -O2 -fno-exceptions -D_REENTRANT ]) LDLIBS=-lrt -lpthread .c.o: $(CC) $(CFLAGS) \ -DSCTL_MAJOR=`/usr/sbin/lsdev -h -d sctl | awk '{print$$1}'` \ -c -o $@ $< .cpp.o: $(CXX) $(CXXFLAGS) -c -o $@ $< .o: # try to please both BSD vv&vv GNU make at the same time... $(CXX) $(CXXFLAGS) -o $@ $> $^ $(LDFLAGS) $(LDLIBS) install: dvd+rw-tools /usr/sbin/install -o -f /usr/local/bin $(CHAIN) /usr/sbin/install -o -f /usr/local/man/man1 growisofs.1 ]) ifelse(OS,IRIX,[ # # IRIX section # .SUFFIXES: .c .cpp .o # check for MIPSpro compiler syscmd([(CC -version) > /dev/null 2>&1]) ifelse(sysval,0,[ CC =cc CFLAGS =$(WARN) -O -use_readonly_const -D_SGI_MP_SOURCE CXX =CC CXXFLAGS=$(WARN) -O -OPT:Olimit=0 -use_readonly_const -LANG:exceptions=OFF -D_SGI_MP_SOURCE ],[ CC =gcc CFLAGS =$(WARN) -O2 -D_SGI_MP_SOURCE CXX =g++ CXXFLAGS=$(WARN) -O2 -fno-exceptions -D_SGI_MP_SOURCE ]) LDLIBS=-lmediad -lpthread LDFLAGS=-dont_warn_unused .c.o: $(CC) $(CFLAGS) -c -o $@ $< .cpp.o: $(CXX) $(CXXFLAGS) -c -o $@ $< .o: $(CXX) $(CXXFLAGS) -o $@ $> $^ $(LDFLAGS) $(LDLIBS) BIN_MODE=04755 # set-root-uid install: dvd+rw-tools /sbin/install -u root -m $(BIN_MODE) $(CHAIN) /usr/local/bin /sbin/install -u root -m 0644 growisofs.1 /usr/local/man/man1 ]) ifelse(OS,Linux,[ # # Linux section # CC =gcc CFLAGS +=$(WARN) -O2 -D_REENTRANT CXX =g++ CXXFLAGS+=$(WARN) -O2 -fno-exceptions -D_REENTRANT LDLIBS =-lpthread LINK.o =$(LINK.cc) prefix?=/usr/local manprefix?=$(shell case $(prefix) in (*/usr/?*) echo $(prefix)/man ;; (*) echo $(prefix)/share/man ;; esac) bin_mode?=0755 # yes, default is *no* set-uid minus_o:=$(shell [[ `id -u` == 0 ]] && echo "-o root") install: dvd+rw-tools [[ -d $(prefix)/bin ]] || mkdir -p $(prefix)/bin install $(minus_o) -m $(bin_mode) $(CHAIN) $(prefix)/bin [[ -d $(manprefix)/man1 ]] || mkdir -p $(manprefix)/man1 install $(minus_o) -m 0644 growisofs.1 $(manprefix)/man1 -[[ -f rpl8 ]] && install $(minus_o) -m $(bin_mode) rpl8 $(prefix)/bin; : -[[ -f btcflash ]] && install $(minus_o) -m $(bin_mode) btcflash $(prefix)/bin; : ]) # common section [ growisofs: growisofs_mmc.o growisofs.o growisofs.o: growisofs.c mp.h growisofs_mmc.o: growisofs_mmc.cpp transport.hxx asctable.h asctable.h: keys.txt perl genasctable.pl < keys.txt > $@ dvd+rw-format: dvd+rw-format.o dvd+rw-format.o: dvd+rw-format.cpp transport.hxx asctable.h dvd+rw-mediainfo: dvd+rw-mediainfo.o dvd+rw-mediainfo.o: dvd+rw-mediainfo.cpp transport.hxx asctable.h dvd+rw-booktype: dvd+rw-booktype.o dvd+rw-booktype.o: dvd+rw-booktype.cpp transport.hxx asctable.h dvd-ram-control: dvd-ram-control.o dvd-ram-control.o: dvd-ram-control.cpp transport.hxx asctable.h rpl8: rpl8.o rpl8.o: rpl8.cpp transport.hxx asctable.h +rpl8: rpl8 #so that I can invoke `make +rpl8' to build rpl8... btcflash: btcflash.o btcflash.o: btcflash.cpp transport.hxx asctable.h +btcflash: btcflash #so that I can invoke `make +btcflash' to build btcflash... panaflash: panaflash.o panaflash.o: panaflash.cpp transport.hxx asctable.h +panaflash: panaflash ] dvd+rw-tools-7.1/dvd+rw-tools.spec0000644000000100000010000005420010762744704015221 0ustar binbinSummary: Toolchain for mastering recordable BD and DVD media Name: dvd+rw-tools Version: 7.1 Release: 1 License: GPL Group: Applications/Multimedia Source: http://fy.chalmers.se/~appro/linux/DVD+RW/tools/dvd+rw-tools-%{version}.tar.gz URL: http://fy.chalmers.se/~appro/linux/DVD+RW/ Requires: mkisofs >= 1.10 BuildRoot: %{_tmppath}/%{name}-root BuildRequires: kernel-headers wget %description Collection of tools to master Blu-ray Disc and DVD+RW/+R/-R/-RW media. For further information see http://fy.chalmers.se/~appro/linux/DVD+RW/. %prep %setup -q %build make [ -f index.html ] || wget -nd http://fy.chalmers.se/~appro/linux/DVD+RW/ %install [ %{buildroot} == / ] || rm -rf %{buildroot} cd %{_builddir}/%{name}-%{version} make prefix=%{buildroot}%{_prefix} manprefix=%{buildroot}%{_mandir} install mkdir -p %{buildroot}%{_docdir}/%{name}-%{version}-%{release} cp -a index.html %{buildroot}%{_docdir}/%{name}-%{version}-%{release} %clean [ %{buildroot} == / ] || rm -rf %{buildroot} %files %defattr(-,root,root) %{_prefix}/bin/* %doc %{_docdir}/%{name}-%{version}-%{release} %doc %{_mandir}/man1/* %changelog * Mon Mar 3 2008 Andy Polyakov - 7.1 release; - Linux: allow compilation with non-substitution 2.6 kernel headers; - Linux: not all 2.4 filesystem types support direct I/O; - Linux: lock corresponding /dev/srN; - use PTHREAD_STACK_MIN when creating threads; - use _SC_PHYS_PAGES instead of _SC_AVPHYS_PAGES when calculating ring buffer size limit; - Mac OS X: allow compilation on 10.5; - human-readable sense code transcription; - Solaris: privileges, authorization and hald awareness; - in order to minimize swap reservations mmap ring buffer with MAP_SHARED; - add -F/-free-space option displaying next session offset and media capacity, which facilitates free space calculation [by suggestion from Bacula project]; - allow session to cross 4GB boundary even on single-layer media [by request from Bacula project]; - HP-UX: fix compilation warnings; - refine x73xx error treatment; - handle deferred errno from reader thread; - return non-zero exit status more aggressively; * Sun Sep 24 2006 Andy Polyakov - 7.0 release; - Blu-ray Disc support [upon release tested with Panasonic SW-5582]; - Mac OS X 10>=2 support [upon release tested on 10.4 only]; - Linux: copy definitions directly into application code in order to secure backward compatibility; - Linux: overcome 16MB O_DIRECT limitaton for NFS client; - limit ring buffer size to 1/4 of RAM; - copy volume descriptors if -C was specified with -M /dev/dvd=image [by request from K3b]; - -use-the-force-luke=spare[:none|min] to control blank BD pre-format [for finer control use dvd+rw-format]; - some units, e.g. Lite-on SOHW-1693S, seem to fire off OPC already upon Layer Break command, therefore longer timeout is required; - Linux: deploy BLKFLSBUF to avoid media reloads when possible; - add unit buffer utilization indicator [by request from K3b]; * Sun Jan 29 2006 Andy Polyakov - 6.1 release; * Thu Jan 26 2006 Andy Polyakov - permit tracksize to be not divisible by 32KB in DAO mode; * Wed Jan 25 2006 Andy Polyakov - Linux: utilize O_EXCL flag [but do see commentary in source code!]; - Treat only x73xx OPC errors as fatal; - Fix typo in -speed scaling code; * Fri Jan 20 2006 Andy Polyakov - ± localization; * Thu Jan 19 2006 Andy Polyakov - Linux: default rlimit for locked pages is way too small [note that in order to allocate ring buffer larger than default 32MB through command line option, you have to increase memorylocked limit accordingly prior application start]; - make -use-the-force-luke=noload, which leaves tray open, work; - DEFAULT_BUF_SIZE_MB is a macro, which can be redefined at compile time with 'make WARN=-DDEFAULT_BUF_SIZE_MB=[16|64]' to change the default ring buffer size; * Tue Jan 17 2006 Andy Polyakov - FreeBSD: improve backward binary compatibility; * Mon Jan 16 2006 Andy Polyakov - 6.0 release; * Fri Jan 13 2006 Andy Polyakov - -use-the-force-luke=noopc to skip OPC altogether; - implement 50% check for DVD-R DL; - demote failure to change speed to warning; * Sun Jan 8 2006 Andy Polyakov - refine Pioneer strategies; - write procedure to recognize "IN PROCESS OF BECOMING READY" [observed on newer Lite-On 1693S firmware and NEC]; - allow for more intuitive interpretation of -speed factor with units returning minimal velocity other than 1x; * Fri Jan 6 2006 Andy Polyakov - multi-threaded design; - asynchronous ring buffer implementation; - Win32/Mingw32 port; * Mon Dec 5 2005 Andy Polyakov - version harmonization in preparation for 6.0 release; * Sun Nov 28 2004 Andy Polyakov - DVD-R Dual Layer DAO and Incremental support; * Sun Sep 26 2004 Andy Polyakov - fix for DVD+R recordings in Samsung TS-H542A; * Sat Aug 28 2004 Andy Polyakov - 5.21.4.10.8 release; * Wed Aug 25 2004 Andy Polyakov - Linux: fix for kernel version 2.6>=8, 2.6.8 itself is deficient, but the problem can be worked around by installing this version set-root-uid; * Thu Jul 15 2004 Andy Polyakov - growisofs 5.20.4.10.8 release; * Tue Jul 13 2004 Andy Polyakov - Layer Break position sanity check with respect to dataset size; - #if directive to get rid of sudo check at compile time with 'make WARN=-DI_KNOW_ALL_ABOUT_SUDO'; * Mon Jul 12 2004 Andy Polyakov - speed verification issue with 8x AccessTek derivatives addressed; - -use-the-force-luke=noload to leave tray ejected at the end; - allow to resume incomplete sessions recorded with -M option; * Sat Jul 3 2004 Andy Polyakov - -use-the-force-luke=break:size to set Layer Break position for Double Layer recordings; * Fri Jun 25 2004 Andy Polyakov - handle non-fatal OPC errors; - DVD+R Double Layer support; - -use-the-force-luke=4gms to allow ISO9660 directory structures to cross 4GB boundary, the option is effective only with DVD+R DL and for data to be accessible under Linux isofs kernel patch is required; * Wed May 26 2004 Andy Polyakov - HP-UX: inconsistency between /dev/rdsk and /dev/rscsi names; * Wed Apr 21 2004 Andy Polyakov - growisofs 5.19-1 hotfix to address "flushing cache takes forever; * Sat Apr 18 2004 Andy Polyakov - DVD-RAM reload if recorded with -poor-man; - -use-the-force-luke=wrvfy for WRITE AND VERIFY(10); - "flushing cache" takes forever; - dvd+rw-format 4.10: add support for DVD-RAM; * Sat Apr 10 2004 Andy Polyakov - 5.19.4.9.7 release; * Sun Apr 4 2004 Andy Polyakov - LG GSA-4081B fails to "SET STREAMING" with "LBA OUT OF RANGE" for DVD+RW media, but not e.g. DVD-R; - dvd+rw-booktype: BTC support; * Sat Apr 3 2004 Andy Polyakov - make DVD-RAM work in "poor-man" mode; - average write speed report at the end of recording; * Fri Apr 2 2004 Andy Polyakov - dvd+rw-format 4.9: permit for DVD-RW blank even if format descriptors are not present; * Thu Apr 1 2004 Andy Polyakov - Solaris: get rid of media reload, which made it possible to improve volume manager experience as well; - address speed verification issues with NEC ND-2500 and Plextor PX-708A; * Mon Mar 22 2004 Andy Polyakov - dvd+rw-tools-5.18.4.8.6: www.software.hp.com release; * Sat Mar 20 2004 Andy Polyakov - IRIX: IRIX 6.x port is added; * Wed Feb 11 2004 Andy Polyakov - minimize amount of compiler warnings on 64-bit platforms; - skip count-down if no_tty_check is set; - -use-the-force-luke=tracksize:size option by suggestion from K3b; - Linux: fix for "Bad file descriptor" with DVD+RW kernel patch; * Tue Jan 20 2004 Andy Polyakov - refuse to run if ${SUDO_COMMAND} is set; * Wed Jan 14 2004 Andy Polyakov - growisofs 5.17: fix for COMMAND SEQUENCE ERROR in the beginning of DVD-recording; - drop privileges prior mkisofs -version; * Tue Jan 13 2004 Andy Polyakov - the last speed change required adaptations for Pioneer and LG units, which don't/fail to provide current write speed through GET PERFORMANCE despite the fact that the command is mandatory; - HP-UX: retain root privileges in setup_fds, SIOC_IO requires them; * Sun Jan 4 2004 Andy Polyakov - switch to GET PERFORMANCE even for current write speed (most notably required for NEC and derivatives); * Tue Dec 30 2003 Andy Polyakov - Linux: fix for /proc/sys/dev/cdrom/check_media set to 1; - HP-UX: INQUIRY buffer is required to be 128 bytes, well, "required" is wrong word in this context, as it's apparently a kernel bug addressed in PHKL_30038 (HPUX 11.11) and PHKL_30039 (HPUX 11.23); * Fri Dec 26 2003 Andy Polyakov - growisofs 5.16: brown-bag bug in "LONG WRITE IN PROGRESS" code path; * Mon Dec 22 2003 Andy Polyakov - growisofs 5.15: confusing output when DAO mode is manually engaged and DVD-RW media is minimally blanked; - complement -use-the-force-luke=dao[:size] to arrange for piping non-iso images in DAO mode (size is to be expressed in 2KB chunks); - Pioneer DVR-x06 apparently needs larger timeout to avoid "the LUN appears to be stuck" message in the beginning of DAO recording; - HP-UX: fix-up umount code; - HP-UX: make sure user doesn't pass /dev/rscsi/cXtYlZ, they should stick to /dev/rdsk/cXtYdZ; - implement -use-the-force-luke=seek:N -Z /dev/dvd=image to arrange for 'builtin_dd if=image of=/dev/dvd obs=32k seek=N/16' (note that N is expected to be expressed in 2KB chunks); - skip overwrite check for blank media to avoid read errors at start, which reportedly may cause bus reset in some configurations; - make get_mmc_profile load media, explicit media load used to be on per platform basis, while it doesn't have to; - postpone handle_events till after dry-run checkpoint; - error reporting revised; - Optorite seems to insist on resuming suspended DVD+RW format, at least it's apparently the only way to get *reliable* results (formally this contradicts specification, which explicitly states that format is to be resumed automatically and transparently); - FreeBSD: FreeBSD 5-CURRENT since 2003-08-24, including 5.2 fails to pull sense data automatically, at least for ATAPI transport, so I reach for it myself (it's apparently a kernel bug, which eventually will be fixed, but I keep the workaround code just in case); - -speed option in DVD+ context is enabled, upon release tested with Plextor PX-708A; - make builtin_dd print amount of transferred data, together with -use-the-force-luke=seek:N it's easier to maintain "tar-formatted" rewritable media; - dvd+rw-format 4.8: DVD-RW format fails if preceeded by dummy recording; - make sure we talk to MMC unit, be less intrusive; - unify error reporting; - permit for -lead-out even for blank DVD+RW, needed(?) for SANYO derivatives; - dvd+rw-booktype 5: support for Benq derivatives; * Tue Dec 2 2003 Andy Polyakov - growisofs 5.14: TEAC DV-W50D and Lite-On LDW-811S failed to set recording velocity, deploy GET PERFORMANCE/SET STREAMING commands; - Lite-On LDW-811S returns 0s in Write Speed descriptors in page 2A, this would cause a problem if DVD+ speed control was implemented; * Thu Nov 20 2003 Andy Polyakov - Solaris: use large-file API in setup_fds; - HP-UX: HP-UX support is contributed by HP; - block signals in the beginning of recording, formally it shouldn't be necessary, but is apparently needed for some units (is it?); - prepare code for -speed even in DVD+ context, need a test-case... * Sun Nov 2 2003 Andy Polyakov - progress indicator process was orphaned if -Z /dev/cdrom=file.iso terminated prematurely; - -overburn -Z /dev/cdrom=file.iso printed two "ignored" messages; * Thu Oct 23 2003 Andy Polyakov - '| growisofs -Z /dev/cdrom=/dev/fd/0' failed with "already carries isofs" even when running interactively, so I check on /dev/tty instead of isatty(0); - error output was confusing when overburn condition was raised in dry-run mode; - more sane default drain buffer size to minimize system load when unit fails to return usable buffer utilization statistics under "LONG WRITE IN PROGRESS" condition; * Wed Oct 1 2003 Andy Polyakov - LG GSA-4040B fails to auto-format DVD+RW blanks; * Mon Sep 15 2003 Andy Polyakov - growisofs 5.13: support for Panasonic/MATSUSHITA DVD-RAM LF-D310; * Sat Sep 6 2003 Andy Polyakov - RPM build fix-ups, no version change; * Fri Aug 31 2003 Andy Polyakov - growisofs 5.12: [major] issue with MODE SENSE/SELECT on SANYO derivatives, such as Optorite, is addressed; - Linux can't open(2) a socket by /dev/fd/N, replace it with dup(2); - more relaxed command line option parsing and simultaneously a zealous check to make sure that no mkisofs options are passed along with -[ZM] /dev/cdrom=image; - report I/O error if input stream was less than 64K; - -M /dev/cdrom=/dev/zero didn't relocate the lead-out in DVD-RW Restricted Overwrite; * Fri Aug 15 2003 Andy Polyakov - single Pioneer DVR-x06 user reported that very small fraction of his recordings get terminted with "LONG WRITE IN PROGRESS," even though growisofs explicitly reserves for this condition... It turned out that at those rare occasions unit reported a lot of free buffer space, which growisofs treated as error condition. It's not clear if it's firmware deficiency, but growisofs reserves even for this apparently rare condition now. - dvd+rw-format 4.7: when formatting DVD+RW, Pioneer DVR-x06 remained unaccessible for over 2 minutes after dvd+rw-format exited and user was frustrated to poll the unit himself, now dvd+rw-format does it for user; * Sun Aug 3 2003 Andy Polyakov - growisofs 5.11: authorship statement in -version output; - make speed_factor floating point and print "Current Write Speed" factor for informational purposes; - Pioneer DVR-x06 exhibited degraded performance when recording DVD+; - Pioneer DVR-x06 failed to complete DVD+ recording gracefully; - alter set-root-uid behaviour under Linux from "PAM-junky" to more conservative one; - dvd+rw-format 4.6: -force to ignore error from READ DISC INFORMATION; - -force was failing under FreeBSD with 'unable to unmount'; - undocumented -gui flag to ease progress indicator parsing for GUI front-ends; * Fri Jul 14 2003 Andy Polyakov - dvd+rw-format 4.5: increase timeout for OPC, NEC multi-format derivatives might require more time to fulfill the OPC procedure; - growisofs 5.10: increase timeout for OPC, NEC multi-format derivatives might require more time to fulfill the OPC procedure; - extended syntax for -use-the-force-luke option, it's now possible to engage DVD-R[W] dummy mode by -use-the-force-luke=[tty,]dummy for example, where "tty" substitutes for the original non-extended option meaning, see the source for more easter eggs; - FreeBSD: compile-time option to pass -M /dev/fd/0 to mkisofs to make life easier for those who mount devfs, but not fdescfs; - eliminate potential race conditions; - avoid end-less loop if no media was in upon tray load; - interpret value of MKISOFS environment variable as absolute path to mkisofs binary; - to facilitate for GUI front-ends return different exit codes, most notably exit value of 128|errno denotes a fatal error upon program startup [messages worth popping up in a separate modal dialog perhaps?], errno - fatal error during recording and 1 - warnings at exit; - to facilitate for GUI front-ends auto-format blank DVD+RW media; - Linux: fix for failure to copy volume descriptors when DVD-RW Restricted Overwrite procedure is applied to patched kernel; - FreeBSD: growisofs didn't close tray upon startup nor did the rest of the tools work with open tray; - bark at -o option and terminate execution, the "problem" was that users seem to misspell -overburn once in a while, in which case it was passed down to mkisofs and an iso-image was dumped to current working directory instead of media; - generalize -M /dev/cdrom=file.iso option, but if file.iso is not /dev/zero, insist on sane -C argument to be passed prior -M and double-verify the track starting address; * Tue Jun 20 2003 Andy Polyakov - growisofs 5.9: some [SONY] firmwares make it impossible to tell apart minimally and fully blanked media, so we need a way to engage DAO manually [in DVD-RW]... let's treat multiple -dvd-compat options as "cry" for DAO; - refuse to finalize even DVD-R media with -M flag (advise to fill it up with -M /dev/cdrom=/dev/zero too), apparently DVD-units [or is it just SONY?] also "misplace" legacy lead-out in the same manner as DVD+units; - oops! DAO hung at >4MB buffer because of sign overflow; - couple of human-readable error messages in poor_mans_pwrite64; - work around Plextor firmware deficiency which [also] manifests as end-less loop upon startup; * Wed Jun 14 2003 Andy Polyakov - growisofs 5.8: elder Ricoh firmwares seem to report events differently, which triggered growisofs and dvd+rw-format to end-less loop at startup [event handling was introduced in 5.6 for debugging purposes]; - int ioctl_fd is transformed to void *ioctl_handle to facilitate port to FreeBSD; - FreeBSD support contributed by Matthew Dillon; - volume descriptors from second session were discarded in Restricted Overwrite since 5.6; * Sun Jun 8 2003 Andy Polyakov - growisofs 5.7: Solaris 2.x USB workaround; - 15 min timeout for FLUSH CACHE in DVD-RW DAO; - revalidate recording speed; - load media upon start-up (Linux used to auto-close tray upon open, but not the others, which is why this functionality is added so late); - dvd+rw-mediainfo: DVD-R[W] MediaID should be printed now; * Sat May 31 2003 Andy Polyakov - Solaris support is merged; * Mon May 26 2003 Andy Polyakov - growisofs 5.6: unconditional exit in set-root-uid assistant, mostly for aesthetic reasons; - support for DVD-RW DAO recordings (whenever Pioneer-ish Quick Format is not an option, DAO should fill in for it, as it's the only recording strategy applicable after *minimal* blanking procedure); - support for SG_IO pass-through interface, or in other words support for Linux 2>=5; - 'growisofs -M /dev/cdrom=/dev/zero', this is basically a counter- intuitive kludge assigned to fill up multi-session write-once media for better compatibility with DVD-ROM/-Video units, to keep it mountable [in the burner unit] volume descriptors from previous session are copied to the new session; - disable -dvd-compat with -M option and DVD+R, advice to fill up the media as above instead; - postpone Write Page setup all the way till after dry_run check; - if recording to write-once media is terminated by external event, leave the session opened, so that the recording can be resumed (though no promises about final results are made, it's just that leaving it open makes more sense than to close the session); - ask unit to perform OPC if READ DISC INFORMATION doesn't return any OPC descriptors; - get rid of redundant Quick Grow in Restricted Overwrite; - dvd+rw-formwat 4.4: support for -force=full in DVD-RW context; - ask unit to perform OPC if READ DISC INFORMATION doesn't return any OPC descriptors; - new dvd+rw-mediainfo utility for debugging purposes; * Thu May 1 2003 Andy Polyakov - growisofs 5.5: fix for ENOENT at unmount, I should have called myself with execlp, not execl; - security: chdir to / in set-root-uid assistant; - use /proc/mounts instead of MOUNTED (a.k.a. /etc/mtab) in Linux umount code; - changed to 'all' target in Makefile to keep NetBSD people happy; * Sun Apr 20 2003 Andy Polyakov - growisofs 5.4: setup_fds is introduced to assist ports to another platforms; - set-root-uid assistant code directly at entry point (see main()); - OpenBSD/NetBSD port added; * Thu Mar 27 2003 Andy Polyakov - growisofs 5.4: split first write to avoid "empty DMA table?" in kernel log; - dvd+rw-format 4.3: natural command-line restrictions; * Thu Mar 20 2003 Andy Polyakov - growisofs 5.3: refuse to burn if session starts close to or beyond the 4GB limit (due to limitation of Linux isofs implementation). - media reload is moved to growisofs from dvd+rw-format. - dry_run check is postponed all the way till the first write. * Sat Mar 15 2003 Andy Polyakov - growisofs 5.3/dvd+rw-format 4.2: support for DVD-RW Quick Format, upon release tested with Pioneer DVR-x05. - bug in DVD+RW overburn protection code fixed. * Thu Feb 27 2003 Andy Polyakov - growisofs 5.2: brown-bag bug in "LONG WRITE IN PROGRESS" handling code fixed. * Mon Feb 1 2003 Andy Polyakov - code to protect against overburns. - progress indicator to display recording velocity. - re-make it work under Linux 2.2 kernel. * Tue Jan 14 2003 Andy Polyakov - growisofs 5.1: support for DVD-R[W] writing speed control. - dvd+rw-booktype 4: see the source. * Tue Nov 26 2002 Andy Polyakov - growisofs 5.0: support for DVD-R[W]. - dvd+rw-format 4.0: support for DVD-RW. - growisofs 4.2: workaround for broken DVD+R firmwares (didn't make public by itself). * Thu Nov 4 2002 Andy Polyakov - Minor growisofs update. Uninitialized errno at exit when -Z /dev/scd0=image.iso is used. * Thu Nov 3 2002 Andy Polyakov - Initial packaging. Package version is derived from growisofs, dvd+rw-format and dvd+rw-booktype version. 4.0.3.0.3 means growisofs 4.0, dvd+rw-format 3.0 dvd+rw-booktype 3. dvd+rw-tools-7.1/growisofs.10000644000000100000010000002103710762231206014100 0ustar binbin.TH GROWISOFS 1m "1 Mar 2008" "growisofs 7.1" .SH NAME growisofs \- combined mkisofs frontend/DVD recording program. .SH SYNOPSIS .B growisofs [\fB\-dry\-run\fP] [\fB\-dvd\-compat\fP] [\fB\-overburn\fP] [\fB\-speed=1\fP] \-[\fBZ|M\fP] .I /dev/dvd .I .SH DESCRIPTION \fBgrowisofs\fP was originally designed as a frontend to \fBmkisofs\fP to facilitate appending of data to ISO9660 volumes residing on random-access media such as DVD+RW, DVD\-RAM, plain files, hard disk partitions. In the course of development general purpose DVD recording support was implemented, and as of now \fBgrowisofs\fP supports not only random-access media, but even mastering of multisession DVD media such as DVD+R and DVD\-R/\-RW, as well as Blu\-ray Disc. In addition \fBgrowisofs\fP supports first-/single-session recording of \fIarbitrary pre-mastered image\fP (formatted as UDF, ISO9660 or any other file system, if formatted at all) to all supported DVD media types. .SH OPTIONS .TP .BI \-Z\ /dev/dvd Burn an initial session to the selected device. A special form of this option is recognized to support burning of pre-mastered images. See EXAMPLES section for further details. .TP .BI \-M\ /dev/dvd Merge a new session to an existing one. .TP .BI \-version Print version information and invoke \fBmkisofs\fP, also with \-version option. .TP .BI \-dvd\-compat Provide maximum media compatibility with DVD\-ROM/\-Video. In write-once DVD+R or DVD\-R context this results in unappendable recording (closed disk). In DVD+RW context it instructs the logical unit to explicitly burn [otherwise optional] lead\-out. .TP .BI \-dry\-run At dry\-run \fBgrowisofs\fP performs all the steps till, but not including the first write operation. Most notably check for "overburn" condition is performed, which implies that mkisofs is invoked and terminated prematurely. .TP .BI \-overburn Normally single layer DVD media can accommodate up to approximately 4.700.000.000 bytes (in marketing speech 4.7GB). In other words a DVD can contain about 4.377 GiB or 4482 MiB. Same kind of arithmetics applies to Blu\-ray Disc capacity of 25.000.000.000 bytes. Anyway, growisofs won't start without this option, if "overburn" condition appears to be unavoidable. .TP .BI \-speed=N An option to control recording velocity. Most commonly you'll use \fB\-speed=1\fP with "no\-name" media, if default speed setting messes up the media. Keep in mind that \fBN\fP essentially denotes speed \fIclosest\fP to N*1385KBps in DVD or N*4496KBps in Blu\-ray Disc case among those offered by unit for currently mounted media. The list can be found in \fBdvd+rw\-mediainfo\fP output. Note that Blu\-ray Disc recordings are commonly performed at ~1/2 of advertised speed, because of defect management being in effect. .TP .BI More options can be found in the manpage for \fBmkisofs\fP. .P There are several undocumented options commonly denoted with \fB\-use\-the\-force\-luke\fP prefix. Some of them serve debugging purposes. Some require certain knowledge about recording process or even OS kernel internals and as being such can induce confusing behaviour. Some are to be used in very specific situations better recognized by front-ends or automated scripts. Rationale behind leaving these options undocumented is that those few users who would actually need to use them directly can as well consult the source code or obtain specific instructions elsewhere. .SH DIFFERENCES WITH RUNNING MKISOFS DIRECTLY .P When using growisofs you may not use the .B \-o option for an output file. .B growisofs dumps the image directly to the media; .P You don't have to specify the .B \-C option to create a higher level session on a multisession disk, .B growisofs will construct one for you; .P Otherwise everything that applies to .I [multisession] mastering with .B mkisofs applies to .B growisofs as well. .B growisofs needs at least .B mkisofs version 1.14, version 2.0 is required for multisession write-once recordings. .SH EXAMPLES Actual device names vary from one operating system to another. We use \fI/dev/dvd\fP as a collective name or as symbolic link to the actual device if you wish. Under Linux it will most likely be an ide\-scsi device such as "/dev/scd0." Under NetBSD/OpenBSD it has to be a \fIcharacter\fP SCSI CD\-ROM device such as "/dev/rcd0c." Under Solaris it also has to be a \fIcharacter\fP SCSI/ATAPI CD\-ROM device, e.g. "/dev/rdsk/c0t1d0s2" or "/vol/dev/aliases/cdrom0." And likewise in HP\-UX, IRIX and Mac OS X... To master and burn an ISO9660 volume with Joliet and Rock\-Ridge extensions on a DVD or Blu\-ray Disc: growisofs \fB\-Z\fP /dev/dvd \fB\-R \-J\fP /some/files To append more data to same media: growisofs \fB\-M\fP /dev/dvd \fB\-R \-J\fP /more/files Make sure to use \fIthe same options\fP for both initial burning and when appending data. To finalize the multisession DVD maintaining maximum compatibility: growisofs \fB\-M\fP /dev/dvd\fB=\fP/dev/zero To use \fBgrowisofs\fP to write a pre-mastered ISO-image to a DVD: growisofs \-dvd\-compat \-Z /dev/dvd\fB=\fPimage.iso where image.iso represents an arbitrary object in the filesystem, such as file, named pipe or device entry. Nothing is growing here and command name is not intuitive in this context. .SH NOTES If executed under sudo(8) growisofs refuses to start. This is done for the following reason. Naturally growisofs has to access the data set to be recorded to optical media, either indirectly by letting mkisofs generate ISO9660 layout on-the-fly or directly if a pre-mastered image is to be recorded. Being executed under sudo(8), growisofs effectively grants sudoers read access to \fIany\fP file in the file system. The situation is intensified by the fact that growisofs parses MKISOFS environment variable in order to determine alternative path to mkisofs executable image. This means that being executed under sudo(8), growisofs effectively grants sudoers right to execute program of their choice with elevated privileges. If you for any reason still find the above acceptable and are willing to take the consequences, then consider running following wrapper script under sudo(8) in place for real growisofs binary. .nf #!/bin/ksh unset SUDO_COMMAND export MKISOFS=/path/to/trusted/mkisofs exec growisofs "$@" .fi But note that the recommended alternative to the above "workaround" is actually to install growisofs set\-root\-uid, in which case it will drop privileges prior accessing data or executing mkisofs in order to preclude unauthorized access to the data. If the media already carries isofs and \fBgrowisofs\fP is invoked with \fB\-Z\fP option non-interactively, e.g. through cron, it shall fail with "FATAL: /dev/dvd already carries isofs!" Note that only ISO9660 is recognized, you can perfectly zap e.g. an UDF filesystem non-interactively. Recommendation is to prepare media for unattended usage by re-formatting or nullifying first 64KB in advance. "Overburn" protection in pre-mastered image context works only with plain files and ISO9660 formatted volumes. E.g. [given that /dev/root is an ext2 formatted file system larger than 4.7GB] /dev/dvd=/dev/root is bound to produce corrupted recording. Note that DVD+RW re-formatting procedure does not substitute for blanking. If you want to nullify the media, e.g. for privacy reasons, do it explicitly with 'growisofs \-Z /dev/dvd\fB=/dev/zero\fP'. Playback of re-writable DVD media, both DVD+RW and DVD\-RW, might be limited in legacy DVD\-ROM/\-Video units. In most cases this is due to lower reflectivity of such media. Even though growisofs supports it, playback of multisession write-once DVD might be limited to the first session for two reasons: .TP \(bu not all DVD\-ROM players are capable of multi-border DVD\-R playback, even less are aware of DVD+R multisessioning, burner unit therefore might be the only one in your vicinity capable of accessing files written at different occasions; .TP \(bu OS might fail to mount multisession DVD for various reasons; .PP The above is not applicable to DVD+RW, DVD\-RW Restricted Overwrite, DVD\-RAM or Blu\-ray Disc, as volumes are grown within a single session. When growisofs "runs into" blank Blu\-ray Disc media, BD\-RE or BD\-R, it gets pre-formatted with minimal spare area size of 256MB. .SH SEE ALSO Most up-to-date information on dvd+rw\-tools is available at http://fy.chalmers.se/~appro/linux/DVD+RW/. .PP The manpage for \fBmkisofs\fP. .SH AUTHORS Andy Polyakov stands for programming and on-line information. This manpage is currently maintained by Huub Reuver . .SH LICENSE \fBgrowisofs\fP is distributed under GNU GPL. dvd+rw-tools-7.1/transport.hxx0000644000000100000010000016275210762230503014572 0ustar binbin// // This is part of dvd+rw-tools by Andy Polyakov // // Use-it-on-your-own-risk, GPL bless... // // For further details see http://fy.chalmers.se/~appro/linux/DVD+RW/ // #if defined(__unix) || defined(__unix__) #include #include #include #include #include #include #include #include #include inline long getmsecs() { struct timeval tv; gettimeofday (&tv,NULL); return tv.tv_sec*1000+tv.tv_usec/1000; } #include #ifndef EMEDIUMTYPE #define EMEDIUMTYPE EINVAL #endif #ifndef ENOMEDIUM #define ENOMEDIUM ENODEV #endif #include #define ENV_LOCALE "" #elif defined(_WIN32) #include #include #include #include #define ssize_t LONG_PTR #define off64_t __int64 #include "win32err.h" #define poll(a,b,t) Sleep(t) #define getmsecs() GetTickCount() #include #define ENV_LOCALE ".OCP" #endif #include "asctable.h" #define CREAM_ON_ERRNO_NAKED(s) \ switch ((s)[12]) \ { case 0x04: errno=EAGAIN; break; \ case 0x20: errno=ENODEV; break; \ case 0x21: if ((s)[13]==0) errno=ENOSPC; \ else errno=EINVAL; \ break; \ case 0x30: errno=EMEDIUMTYPE; break; \ case 0x3A: errno=ENOMEDIUM; break; \ } #define CREAM_ON_ERRNO(s) do { CREAM_ON_ERRNO_NAKED(s) } while(0) #ifndef FATAL_START #define FATAL_START(er) (0x80|(er)) #endif #define ERRCODE(s) ((((s)[2]&0x0F)<<16)|((s)[12]<<8)|((s)[13])) #define SK(errcode) (((errcode)>>16)&0xF) #define ASC(errcode) (((errcode)>>8)&0xFF) #define ASCQ(errcode) ((errcode)&0xFF) static void sperror (const char *cmd,int err) { int saved_errno=errno; const char *msg; if (err==-1) fprintf (stderr,":-( unable to %s: ",cmd); else if ((msg=ASC_lookup(err))!=NULL) fprintf (stderr,":-[ %s failed with SK=%Xh/%s]: ", cmd,SK(err),msg); else fprintf (stderr,":-[ %s failed with SK=%Xh/ASC=%02Xh/ACQ=%02Xh]: ", cmd,SK(err),ASC(err),ASCQ(err)); errno=saved_errno, perror (NULL); } static void sperror (const char *cmd,unsigned char *sense) { int saved_errno=errno; int err=ERRCODE(sense); if (err==0) fprintf (stderr,":-( unable to %s: ",cmd); else { if ((err==0x20407 || err==0x20408) && sense[15]&0x80) fprintf (stderr,":-[ %s failed with SK=%Xh/ASC=%02Xh/ACQ=%02Xh@%.1f%%]: ", cmd,SK(err),ASC(err),ASCQ(err), 100.0*(sense[16]<<8|sense[17])/65536.0); else { errno=saved_errno; sperror (cmd,err); return; } } errno=saved_errno, perror (NULL); } class autofree { private: unsigned char *ptr; public: autofree() { ptr=NULL; } ~autofree() { if (ptr) free(ptr); } unsigned char *operator=(unsigned char *str) { return ptr=str; } operator unsigned char *() { return ptr; } }; extern "C" char *plusminus_locale() { static class __plusminus { private: char str[4]; public: __plusminus() { setlocale(LC_CTYPE,ENV_LOCALE); int l = wctomb(str,(wchar_t)(unsigned char)'±'); if (l>0) str[l]='\0'; else str[0]='±',str[1]='\0'; } ~__plusminus() { } operator char*(){ return str; } } plusminus; return plusminus; } #if defined(__linux) #include #include #include #include #include #include #if !defined(SG_FLAG_LUN_INHIBIT) # if defined(SG_FLAG_UNUSED_LUN_INHIBIT) # define SG_FLAG_LUN_INHIBIT SG_FLAG_UNUSED_LUN_INHIBIT # else # define SG_FLAG_LUN_INHIBIT 0 # endif #endif #ifndef CHECK_CONDITION #define CHECK_CONDITION 0x01 #endif typedef enum { NONE=CGC_DATA_NONE, // 3 READ=CGC_DATA_READ, // 2 WRITE=CGC_DATA_WRITE // 1 } Direction; #ifdef SG_IO static const int Dir_xlate [4] = { // should have been defined // private in USE_SG_IO scope, // but it appears to be too 0, // implementation-dependent... SG_DXFER_TO_DEV, // 1,CGC_DATA_WRITE SG_DXFER_FROM_DEV, // 2,CGC_DATA_READ SG_DXFER_NONE }; // 3,CGC_DATA_NONE static const class USE_SG_IO { private: int yes_or_no; public: USE_SG_IO() { struct utsname buf; uname (&buf); // was CDROM_SEND_PACKET declared dead in 2.5? yes_or_no=(strcmp(buf.release,"2.5.43")>=0); } ~USE_SG_IO(){} operator int() const { return yes_or_no; } int operator[] (Direction dir) const { return Dir_xlate[dir]; } } use_sg_io; #endif #if 0 #include static union dl_rsm_open_device { void *p; int (*f)(const char *,int,...); dl_rsm_open_device(){ void *h; if ((h=dlopen("libresmgr.so.1",RTLD_LAZY))==NULL || (p=dlsym(h,"rsm_open_device"))==NULL) f = open; } ~dl_rsm_open_device(){} } rsm_open_device; extern "C" int dev_open(const char *pathname,int flags) { return rsm_open_device.f(pathname,flags); } extern "C" int dev_open_patched() { return rsm_open_device.f!=open; } #endif class Scsi_Command { private: int fd,autoclose; char *filename; struct cdrom_generic_command cgc; union { struct request_sense s; unsigned char u[18]; } _sense; #ifdef SG_IO struct sg_io_hdr sg_io; #else struct { int cmd_len,timeout; } sg_io; #endif public: Scsi_Command() { fd=-1, autoclose=1; filename=NULL; } Scsi_Command(int f) { fd=f, autoclose=0; filename=NULL; } Scsi_Command(void*f){ fd=(long)f, autoclose=0; filename=NULL; } ~Scsi_Command() { if (fd>=0 && autoclose) close(fd),fd=-1; if (filename) free(filename),filename=NULL; } int associate (const char *file,const struct stat *ref=NULL) { struct stat sb; /* * O_RDWR is expected to provide for none set-root-uid * execution under Linux kernel 2.6[.8]. Under 2.4 it * falls down to O_RDONLY... */ if ((fd=open (file,O_RDWR|O_NONBLOCK)) < 0 && (fd=open (file,O_RDONLY|O_NONBLOCK)) < 0) return 0; if (fstat(fd,&sb) < 0) return 0; if (!S_ISBLK(sb.st_mode)) { errno=ENOTBLK;return 0; } if (ref && (!S_ISBLK(ref->st_mode) || ref->st_rdev!=sb.st_rdev)) { errno=ENXIO; return 0; } filename=strdup(file); return 1; } unsigned char &operator[] (size_t i) { if (i==0) { memset(&cgc,0,sizeof(cgc)), memset(&_sense,0,sizeof(_sense)); cgc.quiet = 1; cgc.sense = &_sense.s; #ifdef SG_IO if (use_sg_io) { memset(&sg_io,0,sizeof(sg_io)); sg_io.interface_id= 'S'; sg_io.mx_sb_len = sizeof(_sense); sg_io.cmdp = cgc.cmd; sg_io.sbp = _sense.u; sg_io.flags = SG_FLAG_LUN_INHIBIT|SG_FLAG_DIRECT_IO; } #endif } sg_io.cmd_len = i+1; return cgc.cmd[i]; } unsigned char &operator()(size_t i) { return _sense.u[i]; } unsigned char *sense() { return _sense.u; } void timeout(int i) { cgc.timeout=sg_io.timeout=i*1000; } #ifdef SG_IO size_t residue() { return use_sg_io?sg_io.resid:0; } #else size_t residue() { return 0; } #endif int transport(Direction dir=NONE,void *buf=NULL,size_t sz=0) { int ret = 0; #ifdef SG_IO #define KERNEL_BROKEN 0 if (use_sg_io) { sg_io.dxferp = buf; sg_io.dxfer_len = sz; sg_io.dxfer_direction = use_sg_io[dir]; if (ioctl (fd,SG_IO,&sg_io)) return -1; #if !KERNEL_BROKEN if ((sg_io.info&SG_INFO_OK_MASK) != SG_INFO_OK) #else if (sg_io.status) #endif { errno=EIO; ret=-1; #if !KERNEL_BROKEN if (sg_io.masked_status&CHECK_CONDITION) #endif { ret = ERRCODE(_sense.u); if (ret==0) ret=-1; else CREAM_ON_ERRNO(_sense.u); } } return ret; } else #undef KERNEL_BROKEN #endif { cgc.buffer = (unsigned char *)buf; cgc.buflen = sz; cgc.data_direction = dir; if (ioctl (fd,CDROM_SEND_PACKET,&cgc)) { ret = ERRCODE(_sense.u); if (ret==0) ret=-1; } } return ret; } int umount(int f=-1) { struct stat fsb,msb; struct mntent *mb; FILE *fp; pid_t pid,rpid; int ret=0,rval; if (f==-1) f=fd; if (fstat (f,&fsb) < 0) return -1; if ((fp=setmntent ("/proc/mounts","r"))==NULL) return -1; while ((mb=getmntent (fp))!=NULL) { if (stat (mb->mnt_fsname,&msb) < 0) continue; // corrupted line? #ifdef I_HAVE_PATCHED_SUBMOUNTD // see O_EXCL commentary in growisofs.c if (!strcmp (mb->mnt_type,"subfs")) continue; #endif if (msb.st_rdev == fsb.st_rdev) { ret = -1; if ((pid = fork()) == (pid_t)-1) break; if (pid == 0) execl ("/bin/umount","umount",mb->mnt_dir,(void*)NULL); while (1) { rpid = waitpid (pid,&rval,0); if (rpid == (pid_t)-1) { if (errno==EINTR) continue; else break; } else if (rpid != pid) { errno = ECHILD; break; } if (WIFEXITED(rval)) { if (WEXITSTATUS(rval) == 0) ret=0; else errno=EBUSY; // most likely break; } else { errno = ENOLINK; // some phony errno break; } } break; } } endmntent (fp); return ret; } int is_reload_needed (int same_capacity) { if (same_capacity && ioctl (fd,0x1261,0) == 0) // try BLKFLSBUF return 0; else return ioctl (fd,CDROM_MEDIA_CHANGED,CDSL_CURRENT) == 0; } }; #elif defined(__OpenBSD__) || defined(__NetBSD__) #include #include #include #include #include typedef off_t off64_t; #define stat64 stat #define fstat64 fstat #define open64 open #define pread64 pread #define pwrite64 pwrite #define lseek64 lseek typedef enum { NONE=0, READ=SCCMD_READ, WRITE=SCCMD_WRITE } Direction; class Scsi_Command { private: int fd,autoclose; char *filename; scsireq_t req; public: Scsi_Command() { fd=-1, autoclose=1; filename=NULL; } Scsi_Command(int f) { fd=f, autoclose=0; filename=NULL; } Scsi_Command(void*f){ fd=(long)f, autoclose=0; filename=NULL; } ~Scsi_Command() { if (fd>=0 && autoclose) close(fd),fd=-1; if (filename) free(filename),filename=NULL; } int associate (const char *file,const struct stat *ref=NULL) { struct stat sb; fd=open(file,O_RDWR|O_NONBLOCK); // this is --^^^^^^-- why we have to run set-root-uid... if (fd < 0) return 0; if (fstat(fd,&sb) < 0) return 0; if (!S_ISCHR(sb.st_mode)) { errno=EINVAL; return 0; } if (ref && (!S_ISCHR(ref->st_mode) || ref->st_rdev!=sb.st_rdev)) { errno=ENXIO; return 0; } filename=strdup(file); return 1; } unsigned char &operator[] (size_t i) { if (i==0) { memset(&req,0,sizeof(req)); req.flags = SCCMD_ESCAPE; req.timeout = 30000; req.senselen = 18; //sizeof(req.sense); } req.cmdlen = i+1; return req.cmd[i]; } unsigned char &operator()(size_t i) { return req.sense[i]; } unsigned char *sense() { return req.sense; } void timeout(int i) { req.timeout=i*1000; } size_t residue() { return req.datalen-req.datalen_used; } int transport(Direction dir=NONE,void *buf=NULL,size_t sz=0) { int ret=0; req.databuf = (caddr_t)buf; req.datalen = sz; req.flags |= dir; if (ioctl (fd,SCIOCCOMMAND,&req) < 0) return -1; if (req.retsts==SCCMD_OK) return 0; errno=EIO; ret=-1; if (req.retsts==SCCMD_SENSE) { ret = ERRCODE(req.sense); if (ret==0) ret=-1; else CREAM_ON_ERRNO(req.sense); } return ret; } // this code is basically redundant... indeed, we normally want to // open device O_RDWR, but we can't do that as long as it's mounted. // in other words, whenever this routine is invoked, device is not // mounted, so that it could as well just return 0; int umount(int f=-1) { struct stat fsb,msb; struct statfs *mntbuf; int ret=0,mntsize,i; if (f==-1) f=fd; if (fstat (f,&fsb) < 0) return -1; if ((mntsize=getmntinfo(&mntbuf,MNT_NOWAIT))==0)return -1; for (i=0;i #include #include #include #include #include #include #include typedef off_t off64_t; #define stat64 stat #define fstat64 fstat #define open64 open #define pread64 pread #define pwrite64 pwrite #define lseek64 lseek #define ioctl_fd (((struct cam_device *)ioctl_handle)->fd) typedef enum { NONE=CAM_DIR_NONE, READ=CAM_DIR_IN, WRITE=CAM_DIR_OUT } Direction; class Scsi_Command { private: int fd,autoclose; char *filename; struct cam_device *cam; union ccb ccb; public: Scsi_Command() { cam=NULL, fd=-1, autoclose=1; filename=NULL; } Scsi_Command(int f) { char pass[32]; // periph_name is 16 chars long cam=NULL, fd=-1, autoclose=1, filename=NULL; memset (&ccb,0,sizeof(ccb)); ccb.ccb_h.func_code = XPT_GDEVLIST; if (ioctl (f,CAMGETPASSTHRU,&ccb) < 0) return; sprintf (pass,"/dev/%.15s%u",ccb.cgdl.periph_name,ccb.cgdl.unit_number); cam=cam_open_pass (pass,O_RDWR,NULL); } Scsi_Command(void *f) { cam=(struct cam_device *)f, autoclose=0; fd=-1; filename=NULL; } ~Scsi_Command() { if (cam && autoclose) cam_close_device(cam), cam=NULL; if (fd>=0) close(fd); if (filename) free(filename), filename=NULL; } int associate (const char *file,const struct stat *ref=NULL) { struct stat sb; char pass[32]; // periph_name is 16 chars long fd=open(file,O_RDONLY|O_NONBLOCK); // all if (ref) code is actually redundant, it never runs // as long as RELOAD_NEVER_NEEDED... if (ref && fd<0 && errno==EPERM) { // expectedly we would get here if file is /dev/passN if (stat(file,&sb) < 0) return 0; if (!S_ISCHR(ref->st_mode) || ref->st_rdev!=sb.st_rdev) return (errno=ENXIO,0); fd=open(file,O_RDWR); } if (fd < 0) return 0; if (fstat(fd,&sb) < 0) return 0; if (!S_ISCHR(sb.st_mode)) return (errno=EINVAL,0); if (ref && (!S_ISCHR(ref->st_mode) || ref->st_rdev!=sb.st_rdev)) return (errno=ENXIO,0); memset (&ccb,0,sizeof(ccb)); ccb.ccb_h.func_code = XPT_GDEVLIST; if (ioctl(fd,CAMGETPASSTHRU,&ccb)<0) return (close(fd),fd=-1,0); sprintf (pass,"/dev/%.15s%u",ccb.cgdl.periph_name,ccb.cgdl.unit_number); cam=cam_open_pass (pass,O_RDWR,NULL); if (cam==NULL) return (close(fd),fd=-1,0); filename=strdup(file); return 1; } unsigned char &operator[] (size_t i) { if (i==0) { memset(&ccb,0,sizeof(ccb)); ccb.ccb_h.path_id = cam->path_id; ccb.ccb_h.target_id = cam->target_id; ccb.ccb_h.target_lun = cam->target_lun; 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 } ccb.csio.cdb_len = i+1; return ccb.csio.cdb_io.cdb_bytes[i]; } unsigned char &operator()(size_t i) { return ((unsigned char *)&ccb.csio.sense_data)[i]; } unsigned char *sense() { return (unsigned char*)&ccb.csio.sense_data; } void timeout(int i) { ccb.ccb_h.timeout=i*1000; } size_t residue() { return ccb.csio.resid; } int transport(Direction dir=NONE,void *buf=NULL,size_t sz=0) { int ret=0; ccb.csio.ccb_h.flags |= dir; ccb.csio.data_ptr = (u_int8_t *)buf; ccb.csio.dxfer_len = sz; if ((ret = cam_send_ccb(cam, &ccb)) < 0) return -1; if ((ccb.ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) return 0; unsigned char *sense=(unsigned char *)&ccb.csio.sense_data; errno = EIO; // FreeBSD 5-CURRENT since 2003-08-24, including 5.2 fails to // pull sense data automatically, at least for ATAPI transport, // so I reach for it myself... if ((ccb.csio.scsi_status==SCSI_STATUS_CHECK_COND) && !(ccb.ccb_h.status&CAM_AUTOSNS_VALID)) { u_int8_t _sense[18]; u_int32_t resid=ccb.csio.resid; memset(_sense,0,sizeof(_sense)); operator[](0) = 0x03; // REQUEST SENSE ccb.csio.cdb_io.cdb_bytes[4] = sizeof(_sense); ccb.csio.cdb_len = 6; ccb.csio.ccb_h.flags |= CAM_DIR_IN|CAM_DIS_AUTOSENSE; ccb.csio.data_ptr = _sense; ccb.csio.dxfer_len = sizeof(_sense); ccb.csio.sense_len = 0; ret = cam_send_ccb(cam, &ccb); ccb.csio.resid = resid; if (ret<0) return -1; if ((ccb.ccb_h.status&CAM_STATUS_MASK) != CAM_REQ_CMP) return errno=EIO,-1; memcpy(sense,_sense,sizeof(_sense)); } ret = ERRCODE(sense); if (ret == 0) ret = -1; else CREAM_ON_ERRNO(sense); return ret; } int umount(int f=-1) { struct stat fsb,msb; struct statfs *mntbuf; int ret=0,mntsize,i; if (f==-1) f=fd; if (fstat (f,&fsb) < 0) return -1; if ((mntsize=getmntinfo(&mntbuf,MNT_NOWAIT))==0)return -1; for (i=0;i extern "C" int _dev_unmount(char *); // VolMgt ON Consolidation Private API #include #include #include #include #include #include #include #include #include #include #include #include typedef enum { NONE=0, READ=USCSI_READ, WRITE=USCSI_WRITE } Direction; class Scsi_Command { private: int fd,autoclose; char *filename; struct uscsi_cmd ucmd; unsigned char cdb[16], _sense[18]; public: Scsi_Command() { fd=-1, autoclose=1; filename=NULL; } Scsi_Command(int f) { fd=f, autoclose=0; filename=NULL; } Scsi_Command(void*f){ fd=(long)f, autoclose=0; filename=NULL; } ~Scsi_Command() { if (fd>=0 && autoclose) close(fd),fd=-1; if (filename) free(filename),filename=NULL; } int associate (const char *file,const struct stat *ref=NULL) { class autofree { private: char *ptr; public: autofree() { ptr=NULL; } ~autofree() { if (ptr) free(ptr); } char *operator=(char *str) { return ptr=str; } operator char *() { return ptr; } } volname,device; struct stat sb; int v; uid_t uid; if ((uid=getuid())!=0) { void *secdb = dlopen("libsecdb.so.1",RTLD_LAZY); union { void *p; int (*f)(const char *,const char *); } chkauthattr; if (secdb && (chkauthattr.p=dlsym(secdb,"chkauthattr"))) { struct passwd *pwd = getpwuid(uid); if (pwd==NULL || !chkauthattr.f("solaris.device.cdrw",pwd->pw_name)) return (errno=EACCES),0; } if (secdb) dlclose(secdb); } if ((v=volmgt_running())) { if ((volname=volmgt_symname ((char *)file))) { if ((device=media_findname (volname)) == NULL) return 0; } else if ((device=media_findname ((char *)file))==NULL) return 0; } else device=strdup(file); fd=open (device,O_RDONLY|O_NONBLOCK); if (fd<0) return 0; if (fstat(fd,&sb) < 0) return 0; if (!S_ISCHR(sb.st_mode)) { errno=ENOTTY; return 0; } if (ref && (!S_ISCHR(ref->st_mode) || ref->st_rdev!=sb.st_rdev)) { errno=ENXIO; return 0; } filename=strdup(device); return 1; } unsigned char &operator[] (size_t i) { if (i==0) { memset (&ucmd,0,sizeof(ucmd)); memset (cdb,0,sizeof(cdb)); memset (_sense,0,sizeof(_sense)); ucmd.uscsi_cdb = (caddr_t)cdb; ucmd.uscsi_rqbuf = (caddr_t)_sense; ucmd.uscsi_rqlen = sizeof(_sense); ucmd.uscsi_flags = USCSI_SILENT | USCSI_DIAGNOSE | USCSI_ISOLATE | USCSI_RQENABLE; ucmd.uscsi_timeout= 60; } ucmd.uscsi_cdblen = i+1; return cdb[i]; } unsigned char &operator()(size_t i) { return _sense[i]; } unsigned char *sense() { return _sense; } void timeout(int i) { ucmd.uscsi_timeout=i; } size_t residue() { return ucmd.uscsi_resid; } int transport(Direction dir=NONE,void *buf=NULL,size_t sz=0) { int ret=0; ucmd.uscsi_bufaddr = (caddr_t)buf; ucmd.uscsi_buflen = sz; ucmd.uscsi_flags |= dir; if (ioctl(fd,USCSICMD,&ucmd)) { if (errno==EIO && _sense[0]==0) // USB seems to be broken... { size_t residue=ucmd.uscsi_resid; memset(cdb,0,sizeof(cdb)); cdb[0]=0x03; // REQUEST SENSE cdb[4]=sizeof(_sense); ucmd.uscsi_cdblen = 6; ucmd.uscsi_bufaddr = (caddr_t)_sense; ucmd.uscsi_buflen = sizeof(_sense); ucmd.uscsi_flags = USCSI_SILENT | USCSI_DIAGNOSE | USCSI_ISOLATE | USCSI_READ; ucmd.uscsi_timeout = 1; ret = ioctl(fd,USCSICMD,&ucmd); ucmd.uscsi_resid = residue; if (ret) return -1; } ret = ERRCODE(_sense); if (ret==0) ret=-1; //else CREAM_ON_ERRNO(_sense); } return ret; } // mimics umount(2), therefore inconsistent return values int umount(int f=-1) { struct stat fsb,msb; struct mnttab mb; FILE *fp; pid_t pid,rpid; int ret=0,i,rval; if (f==-1) f=fd; if (fstat (f,&fsb) < 0) return -1; if ((fp=fopen (MNTTAB,"r")) == NULL) return -1; while ((i=getmntent (fp,&mb)) != -1) { if (i != 0) continue; // ignore corrupted lines if (stat (mb.mnt_special,&msb) < 0) continue; // also corrupted? if (msb.st_rdev == fsb.st_rdev) { if (_dev_unmount (mb.mnt_special)) break; { struct utsname uts; if (uname (&uts)>=0 && (strcmp(uts.release,"5.8")>=0 || strlen(uts.release)>3)) { // M-m-m-m-m! Solaris 8 or later... ret = ::umount (mb.mnt_special); break; } } ret = -1; if ((pid = fork()) == (pid_t)-1) break; if (pid == 0) execl ("/usr/sbin/umount","umount",mb.mnt_mountp,(void*)NULL); while (1) { rpid = waitpid (pid,&rval,0); if (rpid == (pid_t)-1) { if (errno==EINTR) continue; else break; } else if (rpid != pid) { errno = ECHILD; break; } if (WIFEXITED(rval)) { if (WEXITSTATUS(rval) == 0) ret=0; else errno=EBUSY; // most likely break; } else if (WIFSTOPPED(rval) || WIFCONTINUED(rval)) continue; else { errno = ENOLINK; // some phony errno break; } } break; } } fclose (fp); return ret; } int is_reload_needed (int not_used) { struct dk_minfo mi; struct dk_allmap pt; if (ioctl (fd,DKIOCGMEDIAINFO,&mi) < 0) return 1; memset (&pt,0,sizeof(pt)); pt.dka_map[2].dkl_nblk = mi.dki_capacity*(mi.dki_lbsize/DEV_BSIZE); pt.dka_map[0] = pt.dka_map[2]; if (ioctl (fd,DKIOCSAPART,&pt) < 0) return 1; return 0; } }; #elif defined(__hpux) // // Copyright (C) 2003 Hewlett-Packard Development Company, L.P. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // ===================================================================== // // The code targets 11i/11.11 and later, but it might just work on // 11.0 as well. For further information contact following HP office // // Hewlett-Packard Company // 3404 E Harmony Road // Fort Collins, CO 80528 USA // #include #include #include #ifndef minor #include #endif typedef enum { NONE=0, READ=SCTL_READ, WRITE=0 } Direction; class Scsi_Command { private: int fd; int autoclose; char *filename; struct sctl_io cmd; public: Scsi_Command() { fd=-1, autoclose=1; filename=NULL; } Scsi_Command(int f) { fd=f, autoclose=0; filename=NULL; } Scsi_Command(void*f){ fd=(long)f, autoclose=0; filename=NULL; } ~Scsi_Command() { if (fd>=0 && autoclose) close(fd),fd=-1; if (filename) free(filename),filename=NULL; } int associate (const char *file,const struct stat *ref=NULL) { struct stat sb; fd=open (file,O_RDONLY|O_NONBLOCK); if (fd < 0) return 0; if (fstat(fd,&sb) < 0) return 0; if (!S_ISCHR(sb.st_mode)) { errno=EINVAL; return 0; } // shall we check for DIOC_DESCRIBE here? if (ref && (!S_ISCHR(ref->st_mode) || ref->st_rdev!=sb.st_rdev)) { errno=ENXIO; return 0; } filename=strdup(file); return 1; } unsigned char &operator[] (size_t i) { if (i==0) { bzero (&cmd,sizeof(struct sctl_io)); cmd.max_msecs=30*1000; } cmd.cdb_length = i+1; return cmd.cdb[i]; } unsigned char &sense(size_t i) { return cmd.sense[i]; } unsigned char *sense() { return cmd.sense; } void timeout(int i) { cmd.max_msecs=i*1000; } size_t residue() { return cmd.data_length-cmd.data_xfer; } int transport(Direction dir=NONE,void *buf=NULL,size_t sz=0) { const char *err = NULL; cmd.data = buf; cmd.data_length = sz; cmd.flags = dir; if (ioctl (fd,SIOC_IO,&cmd) != 0) return -1; if (cmd.cdb_status == S_GOOD) return 0; errno = EIO; switch (cmd.cdb_status) { case SCTL_INVALID_REQUEST: err = "SCTL_INVALID_REQUEST"; break; case SCTL_SELECT_TIMEOUT: err = "SCTL_SELECT_TIMEOUT"; break; case SCTL_INCOMPLETE: err = "SCTL_INCOMPLETE"; break; case SCTL_POWERFAIL: err = "SCTL_POWERFAIL"; break; default: err = NULL; break; } if (err != NULL) { fprintf (stderr,":-( FAIL: command failed with %s.\n",err); return -1; } switch (cmd.cdb_status & 0xff) { case S_CHECK_CONDITION: if (cmd.sense_status==S_GOOD && cmd.sense_xfer!=0) { CREAM_ON_ERRNO_NAKED(cmd.sense) // CREAM_ON_ERRNO // provokes PA-RISC // compiler bug... return ERRCODE(cmd.sense); } else fprintf (stderr,":-( FAIL: S_CHECK_CONDITION status, " "but no sense data?\n"); break; case S_BUSY: fprintf (stderr,":-( FAIL: S_BUSY condition?\n"); errno = EAGAIN; break; default: fprintf (stderr,":-( FAIL: unknown cdb_status=0x%x\n", cmd.cdb_status); break; } return -1; } // for now we only detect if media is mounted... int umount(int f=-1) { struct stat fsb,msb; struct mntent *mb; FILE *fp; int ret=0; char bdev[32]; dev_t m; if (f==-1) f=fd; if (fstat (f,&fsb) < 0) return -1; if ((fp=setmntent (MNT_MNTTAB,"r")) == NULL) return -1; m=minor(fsb.st_rdev); sprintf(bdev,"/dev/dsk/c%ut%ud%x",(m>>16)&0xFF,(m>>12)&0xF,(m>>8)&0xF); if (stat (bdev,&fsb) < 0) return -1; while ((mb=getmntent (fp))!=NULL) { if (stat (mb->mnt_fsname,&msb) < 0) continue; // corrupted line? if (msb.st_rdev == fsb.st_rdev) { errno=EBUSY; ret=-1; break; } } endmntent (fp); return ret; } int is_reload_needed (int not_used) { return 1; } }; #elif defined(__sgi) // // Not necessarily relevant IRIX notes. // // Information about UDF support seems to be contradictory. Some manuals // maintain that UDF writing is supported for DVD-RAM and hard disk, but // the only mention of UDF in IRIX release notes is "6.5.18 introduces // read-only support for the UDF filesystems format." If UDF writing is // supported, then it was implemented presumably in 6.5.21. DVD-RAM // writing at block device level was most likely introduced by then too. // // IRIX doesn't provide access to files larger than 2GB on ISO9660. // That's because ISO9660 is implemented as NFSv2 user-land server, // and 2GB limit is implied by NFSv2 protocol. // #ifdef PRIVATE #undef PRIVATE // conflicts with ? #endif #include #ifdef PRIVATE #undef PRIVATE // conflicts with ? #endif #include #include #include #include #include #include typedef enum { NONE=0, READ=DSRQ_READ, WRITE=DSRQ_WRITE } Direction; class Scsi_Command { private: int fd,autoclose; char *filename; dsreq_t req; unsigned char cdb[16], _sense[18]; public: Scsi_Command() { fd=-1, autoclose=1; filename=NULL; } Scsi_Command(int f) { fd=f, autoclose=0; filename=NULL; } Scsi_Command(void*f){ fd=(long)f, autoclose=0; filename=NULL; } ~Scsi_Command() { if (fd>=0 && autoclose) close(fd),fd=-1; if (filename) free(filename),filename=NULL; } int associate (const char *file,const struct stat *ref=NULL) { struct stat sb; char hw_path[MAXPATHLEN]; int hw_len=sizeof(hw_path)-1; if (attr_get(file,"_devname",hw_path,&hw_len,0)) return 0; if (hw_len>=sizeof(hw_path)) hw_len=sizeof(hw_path)-1; // paranoia hw_path[hw_len]='\0'; if (ref) { // hw_path is maintained by kernel through hwgfs and // I assume it's not subject to race conditions... if (stat(hw_path,&sb)) return 0; if (!S_ISCHR(ref->st_mode) || ref->st_rdev!=sb.st_rdev) { errno=ENXIO; return 0; } } if (strcmp(hw_path+strlen(hw_path)-5,"/scsi")) { char *s=strstr(hw_path,"/disk/"); if (s==NULL) { errno=EINVAL; return 0; } strcpy (s,"/scsi"); } fd=open (hw_path,O_RDONLY|O_NONBLOCK); if (fd<0) return 0; if (fstat(fd,&sb) < 0) return 0; if (!S_ISCHR(sb.st_mode)) { errno=ENOTTY; return 0; } filename=strdup(file); return 1; } unsigned char &operator[] (size_t i) { if (i==0) { memset (&req,0,sizeof(req)); memset (cdb,0,sizeof(cdb)); memset (_sense,0,sizeof(_sense)); req.ds_cmdbuf = (caddr_t)cdb; req.ds_sensebuf = (caddr_t)_sense; req.ds_senselen = sizeof(_sense); req.ds_flags = DSRQ_SENSE; req.ds_time = 60*1000; } req.ds_cmdlen = i+1; return cdb[i]; } unsigned char &operator()(size_t i) { return _sense[i]; } unsigned char *sense() { return _sense; } void timeout(int i) { req.ds_time=i*1000; } size_t residue() { return req.ds_datalen-req.ds_datasent; } int transport(Direction dir=NONE,void *buf=NULL,size_t sz=0) { int ret=0,retries=3; req.ds_databuf = (caddr_t)buf; req.ds_datalen = sz; req.ds_flags |= dir; // I personally don't understand why do we need to loop, but // /usr/share/src/irix/examples/scsi/lib/src/dslib.c is looping... while (retries--) { if (ioctl(fd,DS_ENTER,&req) < 0) return -1; if (req.ds_status==STA_GOOD) return 0; if (req.ds_ret==DSRT_NOSEL) continue; if (req.ds_status==STA_BUSY || req.ds_status==STA_RESERV) { poll(NULL,0,500); continue; } break; } errno=EIO; ret=-1; if (req.ds_status==STA_CHECK && req.ds_sensesent>=14) { ret = ERRCODE(_sense); if (ret==0) ret=-1; else CREAM_ON_ERRNO(_sense); } return ret; } // mimics umount(2), therefore inconsistent return values int umount(int f=-1) { struct stat fsb,msb; struct mntent *mb; FILE *fp; pid_t pid,rpid; int ret=0,rval; char hw_path[MAXPATHLEN]; int hw_len=sizeof(hw_path)-1; if (f==-1) f=fd; if (fstat (f,&fsb) < 0) return -1; if (!getenv("MEDIAD_GOT_EXCLUSIVEUSE")) { if (attr_getf (f,"_devname",hw_path,&hw_len,0)) return -1; if (hw_len>=sizeof(hw_path)) hw_len=sizeof(hw_path)-1;// paranoia hw_path[hw_len]='\0'; // mediad even unmounts removable volumes. However! The // locks are "granted" even for unmanaged devices, so // it's not possible to tell if device is ignored through // /etc/config/mediad.config or actually managed. Therefore // I have to pass through own unmount code in either case... mediad_get_exclusiveuse(hw_path,"dvd+rw-tools"); switch (mediad_last_error()) { case RMED_NOERROR: break; case RMED_EACCESS: case RMED_ECANTUMOUNT: errno=EBUSY; return -1; case RMED_ENOMEDIAD: break; case -1: if(errno==ECONNREFUSED) break; // no mediad... else return -1; default: errno=ENOTTY; return -1; } } if ((fp=setmntent (MOUNTED,"r"))==NULL) return -1; while ((mb=getmntent (fp))!=NULL) { if (stat (mb->mnt_fsname,&msb) < 0) continue; // corrupted line? // Following effectively catches only /dev/rdsk/dksXdYvol, // which is sufficient for iso9660 volumes, but not for e.g. // EFS formatted media. I mean code might have to be more // versatile... Wait for feedback... if (msb.st_rdev == fsb.st_rdev) { ret = -1; if ((pid = fork()) == (pid_t)-1) break; if (pid == 0) execl ("/sbin/umount","umount",mb->mnt_dir,(void*)NULL); while (1) { rpid = waitpid (pid,&rval,0); if (rpid == (pid_t)-1) { if (errno==EINTR) continue; else break; } else if (rpid != pid) { errno = ECHILD; break; } if (WIFEXITED(rval)) { if (WEXITSTATUS(rval) == 0) ret=0; else errno=EBUSY; // most likely break; } else if (WIFSTOPPED(rval) || WIFCONTINUED(rval)) continue; else { errno = ENOLINK; // some phony errno break; } } break; } } endmntent (fp); return ret; } #if 0 // for now just an idea to test... #define RELOAD_NEVER_NEEDED int is_reload_needed (int not_used) { return 0; } #else int is_reload_needed (int not_used) { return 1; } #endif }; #elif defined(_WIN32) #if defined(__MINGW32__) #include #define FSCTL_LOCK_VOLUME CTL_CODE(FILE_DEVICE_FILE_SYSTEM,6,METHOD_BUFFERED,FILE_ANY_ACCESS) #define FSCTL_UNLOCK_VOLUME CTL_CODE(FILE_DEVICE_FILE_SYSTEM,7,METHOD_BUFFERED,FILE_ANY_ACCESS) #define FSCTL_DISMOUNT_VOLUME CTL_CODE(FILE_DEVICE_FILE_SYSTEM,8,METHOD_BUFFERED,FILE_ANY_ACCESS) #else #include #include #endif typedef enum { NONE=SCSI_IOCTL_DATA_UNSPECIFIED, READ=SCSI_IOCTL_DATA_IN, WRITE=SCSI_IOCTL_DATA_OUT } Direction; typedef struct { SCSI_PASS_THROUGH_DIRECT spt; unsigned char sense[18]; } SPKG; class Scsi_Command { private: HANDLE fd; int autoclose; char *filename; SPKG p; public: Scsi_Command() { fd=INVALID_HANDLE_VALUE; autoclose=1; filename=NULL; } Scsi_Command(void*f){ fd=f, autoclose=0; filename=NULL; } ~Scsi_Command() { DWORD junk; if (fd!=INVALID_HANDLE_VALUE && autoclose) { if (autoclose>1) DeviceIoControl(fd,FSCTL_UNLOCK_VOLUME, NULL,0,NULL,0,&junk,NULL); CloseHandle (fd),fd=INVALID_HANDLE_VALUE; } if (filename) free(filename),filename=NULL; } int associate (const char *file,const struct stat *ref=NULL) { char dev[32]; sprintf(dev,"%.*s\\",sizeof(dev)-2,file); if (GetDriveType(dev)!=DRIVE_CDROM) return errno=EINVAL,0; sprintf(dev,"\\\\.\\%.*s",sizeof(dev)-5,file); fd=CreateFile (dev,GENERIC_WRITE|GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,OPEN_EXISTING,0,NULL); if (fd!=INVALID_HANDLE_VALUE) filename=strdup(dev); return fd!=INVALID_HANDLE_VALUE; } unsigned char &operator[] (size_t i) { if (i==0) { memset(&p,0,sizeof(p)); p.spt.Length = sizeof(p.spt); p.spt.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED; p.spt.TimeOutValue = 30; p.spt.SenseInfoLength = sizeof(p.sense); p.spt.SenseInfoOffset = offsetof(SPKG,sense); } p.spt.CdbLength = i+1; return p.spt.Cdb[i]; } unsigned char &operator()(size_t i) { return p.sense[i]; } unsigned char *sense() { return p.sense; } void timeout(int i) { p.spt.TimeOutValue=i; } size_t residue() { return 0; } // bogus int transport(Direction dir=NONE,void *buf=NULL,size_t sz=0) { DWORD bytes; int ret=0; p.spt.DataBuffer = buf; p.spt.DataTransferLength = sz; p.spt.DataIn = dir; if (DeviceIoControl (fd,IOCTL_SCSI_PASS_THROUGH_DIRECT, &p,sizeof(p.spt), &p,sizeof(p), &bytes,FALSE) == 0) return -1; if (p.sense[0]&0x70) { SetLastError (ERROR_GEN_FAILURE); ret = ERRCODE(p.sense); if (ret==0) ret=-1; else CREAM_ON_ERRNO(p.sense); } #if 0 else if (p.spt.Cdb[0] == 0x00) // TEST UNIT READY { unsigned char _sense[18]; operator[](0) = 0x03; // REQUEST SENSE p.spt.Cdb[4] = sizeof(_sense); p.spt.CdbLength = 6; p.spt.DataBuffer = _sense; p.spt.DataTransferLength = sizeof(_sense); p.spt.DataIn = READ; if (DeviceIoControl (fd,IOCTL_SCSI_PASS_THROUGH_DIRECT, &p,sizeof(p.spt), &p,sizeof(p), &bytes,FALSE) == 0) return -1; if ((ret = ERRCODE(_sense))) CREAM_ON_ERRNO(_sense); } #endif return ret; } int umount (int f=-1) { DWORD junk; HANDLE h = (f==-1) ? fd : (HANDLE)f; if (DeviceIoControl(h,FSCTL_LOCK_VOLUME,NULL,0,NULL,0,&junk,NULL) && DeviceIoControl(h,FSCTL_DISMOUNT_VOLUME,NULL,0,NULL,0,&junk,NULL)) { if (h==fd) autoclose++; return 0; } return -1; } #define RELOAD_NEVER_NEEDED int is_reload_needed (int not_used) { return 0; } }; #elif defined(__APPLE__) && defined(__MACH__) // // This code targets Darwin Kernel Version 6.x, a.k.a. Mac OS X v10.2, // or later, but upon initial release was tested only on PowerPC under // Darwin Kernel Version 8.7.0, a.k.a. Mac OS X v10.4.7 (Tiger). // typedef off_t off64_t; #define stat64 stat #define fstat64 fstat #define open64 open #define pread64 pread #define pwrite64 pwrite #define lseek64 lseek #include #include #include #include #include static int iokit_err (IOReturn ioret,SCSITaskStatus stat, const unsigned char *sense) { int ret=-1; if (ioret==kIOReturnSuccess) ret = 0; else if (ioret==kIOReturnNoDevice) errno = ENXIO; else if (ioret==kIOReturnNoMemory) errno = ENOMEM; else if (ioret==kIOReturnExclusiveAccess) errno = EBUSY; else errno = EIO; if (ret) return ret; if (stat==kSCSITaskStatus_CHECK_CONDITION) { ret = ERRCODE(sense); if (ret==0) errno=EIO, ret=-1; else CREAM_ON_ERRNO(sense); } else if (stat!=kSCSITaskStatus_GOOD) errno = EIO, ret = -1; return ret; } // NB! ellipsis is GCC-ism, but conveniently Apple ships only gcc:-) #define MMCIO(h,func,...) ({ \ MMCDeviceInterface **di = (MMCDeviceInterface **)h;\ SCSITaskStatus stat; \ union { \ SCSI_Sense_Data s; \ unsigned char u[18]; \ } sense; \ IOReturn ioret; \ memset (&sense,0,sizeof(sense)); \ ioret = (*di)->func(di,__VA_ARGS__,&stat,&sense.s); \ iokit_err (ioret,stat,sense.u); }) typedef enum { NONE=kSCSIDataTransfer_NoDataTransfer, READ=kSCSIDataTransfer_FromTargetToInitiator, WRITE=kSCSIDataTransfer_FromInitiatorToTarget } Direction; class Scsi_Command { private: int autoclose,_timeout; char *filename; io_object_t scsiob; IOCFPlugInInterface **plugin; MMCDeviceInterface **mmcdif; SCSITaskDeviceInterface **taskif; unsigned char cdb[16]; union { SCSI_Sense_Data s; unsigned char u[18]; } _sense; size_t cdblen,resid; public: Scsi_Command() { scsiob=IO_OBJECT_NULL, plugin=NULL, mmcdif=NULL, taskif=NULL; autoclose=1; filename=NULL; } Scsi_Command(void *f) { taskif = (SCSITaskDeviceInterface **)f, autoclose=0; filename=NULL; } ~Scsi_Command() { if (autoclose) { if (taskif) (*taskif)->ReleaseExclusiveAccess(taskif), (*taskif)->Release(taskif), taskif=NULL; if (mmcdif) (*mmcdif)->Release(mmcdif), mmcdif=NULL; if (plugin) IODestroyPlugInInterface(plugin), plugin=NULL; if (scsiob) IOObjectRelease(scsiob), scsiob=IO_OBJECT_NULL; } if (filename) free(filename), filename=NULL; } int associate (const char *file,const struct stat *ref=NULL) { struct stat sb; io_object_t scsiob=IO_OBJECT_NULL,parent; CFMutableDictionaryRef match,bsddev; CFNumberRef num; int i; if (ref) sb = *ref; else if (stat(file,&sb)) return 0; if (!(S_ISBLK(sb.st_mode) || S_ISCHR(sb.st_mode))) return !(errno=ENOTBLK); if ((match = CFDictionaryCreateMutable(kCFAllocatorDefault,0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)) == NULL) return !(errno=ENOMEM); if ((bsddev = CFDictionaryCreateMutable(kCFAllocatorDefault,0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)) == NULL) return CFRelease(match),!(errno=ENOMEM); i = major(sb.st_rdev); num = CFNumberCreate(kCFAllocatorDefault,kCFNumberIntType,&i); CFDictionarySetValue(bsddev,CFSTR("BSD Major"),num); CFRelease(num); i = minor(sb.st_rdev); num = CFNumberCreate(kCFAllocatorDefault,kCFNumberIntType,&i); CFDictionarySetValue(bsddev,CFSTR("BSD Minor"),num); CFRelease(num); CFDictionarySetValue(match,CFSTR(kIOPropertyMatchKey),bsddev); CFRelease(bsddev); if ((scsiob = IOServiceGetMatchingService(kIOMasterPortDefault,match)) == IO_OBJECT_NULL) return !(errno=ENXIO); // traverse up to "SCSITaskAuthoringDevice" kern_return_t kret; while ((kret=IORegistryEntryGetParentEntry(scsiob,kIOServicePlane, &parent)) == kIOReturnSuccess) { CFStringRef uclient; const char *s; int cmp; IOObjectRelease(scsiob); scsiob = parent; uclient = (CFStringRef)IORegistryEntryCreateCFProperty(scsiob, CFSTR(kIOPropertySCSITaskDeviceCategory), kCFAllocatorDefault,0); if (uclient) { s = CFStringGetCStringPtr(uclient,kCFStringEncodingMacRoman); cmp = strcmp(s,kIOPropertySCSITaskAuthoringDevice); CFRelease(uclient); if (cmp==0) break; } } if (kret!=kIOReturnSuccess) { if (scsiob!=IO_OBJECT_NULL) IOObjectRelease(scsiob); return !(errno=ENXIO); } SInt32 score=0; if (IOCreatePlugInInterfaceForService(scsiob, kIOMMCDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &plugin,&score) != kIOReturnSuccess) { IOObjectRelease(scsiob); return !(errno=ENXIO); } if ((*plugin)->QueryInterface(plugin, CFUUIDGetUUIDBytes(kIOMMCDeviceInterfaceID), (void**)&mmcdif) != S_OK) { IODestroyPlugInInterface(plugin), plugin=NULL; IOObjectRelease(scsiob); return !(errno=ENXIO); } if ((taskif = (*mmcdif)->GetSCSITaskDeviceInterface(mmcdif)) == NULL) { (*mmcdif)->Release(mmcdif), mmcdif=NULL; IODestroyPlugInInterface(plugin), plugin=NULL; IOObjectRelease(scsiob); return !(errno=ENXIO); } // // Note that in order to ObtainExclusiveAccess no corresponding // /dev/[r]diskN may remain open by that time. For reference, // acquiring exclusive access temporarily removes BSD block // storage device from I/O registry as well as corresponding // /dev entries. // if ((*taskif)->ObtainExclusiveAccess(taskif) != kIOReturnSuccess) { (*taskif)->Release(taskif), taskif=NULL; (*mmcdif)->Release(mmcdif), mmcdif=NULL; IODestroyPlugInInterface(plugin), plugin=NULL; IOObjectRelease(scsiob), scsiob=IO_OBJECT_NULL; return !(errno=EBUSY); } filename=strdup(file); return 1; } unsigned char &operator[] (size_t i) { if (i==0) { memset (cdb,0,sizeof(cdb)); memset (&_sense,0,sizeof(_sense)); _timeout = 30; } cdblen = i+1; return cdb[i]; } unsigned char &operator()(size_t i) { return _sense.u[i]; } unsigned char *sense() { return _sense.u; } void timeout(int i) { _timeout=i; } size_t residue() { return resid; } int transport(Direction dir=NONE,void *buf=NULL,size_t sz=0) { int ret=0; SCSITaskInterface **cmd; SCSITaskStatus stat; UInt64 bytes; IOVirtualRange range = { (IOVirtualAddress)buf, sz }; resid = sz; cmd = (*taskif)->CreateSCSITask(taskif); if (cmd==NULL) return (errno=ENOMEM),-1; (*cmd)->SetCommandDescriptorBlock(cmd,cdb,cdblen); (*cmd)->SetScatterGatherEntries(cmd,&range,1,sz,dir); (*cmd)->SetTimeoutDuration(cmd,_timeout*1000); if ((*cmd)->ExecuteTaskSync(cmd,&_sense.s,&stat,&bytes) != kIOReturnSuccess) errno=EIO, ret=-1; else if (stat==kSCSITaskStatus_GOOD) { resid = sz - bytes; } else if (stat==kSCSITaskStatus_CHECK_CONDITION) { ret = ERRCODE(_sense.u); if (ret==0) errno=EIO, ret=-1; else CREAM_ON_ERRNO(_sense.u); } else { //SCSIServiceResponse resp; //(*taskif)->GetSCSIServiceResponse(taskif,&resp); errno=EIO, ret=-1; } (*cmd)->Release(cmd); return ret; } int umount(int f=-1) { struct stat sb; dev_t ref; int i,n; if (f>=0) { if (fstat (f,&sb)) return -1; if (!S_ISCHR(sb.st_mode)) return errno=ENOTBLK,-1; ref = sb.st_rdev; // /dev/rdiskN and /dev/diskN have same st_rdev } else { char bsdname [16]; CFStringRef devname = (CFStringRef)IORegistryEntrySearchCFProperty ( scsiob,kIOServicePlane, CFSTR("BSD Name"), kCFAllocatorDefault, kIORegistryIterateRecursively); if (devname==NULL) return 0; // already exclusive sprintf (bsdname,"/dev/%.*s",(int)(sizeof(bsdname)-6), CFStringGetCStringPtr (devname,0)); CFRelease (devname); if (stat (bsdname,&sb)) return -1; ref = sb.st_rdev; } if ((n=getfsstat (NULL,0,MNT_NOWAIT)) < 0) return -1; n += 4, n *= sizeof(struct statfs); struct statfs *p = (struct statfs *)alloca(n); if ((n=getfsstat (p,n,MNT_NOWAIT)) < 0) return -1; for (i=0;if_mntfromname,&sb)==0 && S_ISBLK(sb.st_mode) && sb.st_rdev==ref) #if 0 // looks neat, but causes irritaing popups on console... return unmount (p->f_mntonname,0); #else { int ret=0,rval; pid_t pid,rpid; ret = -1; if ((pid = fork()) == (pid_t)-1) return -1; if (pid == 0) // if diskutil will be proven broken, // don't allow growisofs to be used as // attack vector... setuid (getuid ()), execl ("/usr/sbin/diskutil", "diskutil","unmount", p->f_mntonname,(void*)NULL), exit (errno); while (1) { rpid = waitpid (pid,&rval,0); if (rpid == (pid_t)-1) { if (errno==EINTR) continue; else break; } else if (rpid != pid) { errno = ECHILD; break; } if (WIFEXITED(rval)) { if (WEXITSTATUS(rval) == 0) ret=0; else errno=EBUSY; // most likely break; } else if (WIFSTOPPED(rval) || WIFCONTINUED(rval)) continue; else { errno = ENOLINK; // some phony errno break; } } // diskutil(8) seem to unmount only volfs-managed // media, so I check if it managed to unmount and // try the system call if it didn't... if (ret==0) { struct statfs fs; if (statfs (p->f_mntonname,&fs)==0 && !strcmp (fs.f_mntfromname,p->f_mntfromname)) return unmount (p->f_mntonname,0); } return ret; } #endif } return 0; // not mounted? } #define RELOAD_NEVER_NEEDED int is_reload_needed(int not_used) { return 0; } }; #else #error "Unsupported OS" #endif #define DUMP_EVENTS 0 static int handle_events (Scsi_Command &cmd) { unsigned char event[8]; unsigned short profile=0,started=0; int err,ret=0; unsigned int descr; static unsigned char events=0xFF; // "All events" while (events) { cmd[0] = 0x4A; // GET EVENT cmd[1] = 1; // "Polled" cmd[4] = events; cmd[8] = sizeof(event); cmd[9] = 0; if ((err=cmd.transport(READ,event,sizeof(event)))) { events=0; sperror ("GET EVENT",err); return ret; } events = event[3]; if ((event[2]&7) == 0 || (event[0]<<8|event[1]) == 2 || (event[4]&0xF) == 0 ) // No Changes return ret; descr = event[4]<<24|event[5]<<16|event[6]<<8|event[7]; #if DUMP_EVENTS fprintf(stderr,"< %d[%08x],%x >\n",event[2],descr,events); #endif switch(event[2]&7) { case 0: return ret; // No [supported] events case 1: ret |= 1<<1; // Operational Change if ((descr&0xFFFF) < 3) goto read_profile; start_unit: if (!started) { cmd[0]=0x1B; // START STOP UNIT cmd[4]=1; // "Start" cmd[5]=0; if ((err=cmd.transport()) && err!=0x62800) sperror ("START UNIT",err); started=1, profile=0; } read_profile: if (!profile) { cmd[0] = 0x46; // GET CONFIGURATION cmd[8] = sizeof(event); cmd[9] = 0; if (!cmd.transport(READ,event,sizeof(event))) profile=event[6]<<8|event[7]; } break; case 2: ret |= 1<<2; // Power Management if (event[5]>1) // State is other than Active goto start_unit; break; case 3: ret |= 1<<3; break; // External Request case 4: ret |= 1<<4; // Media if (event[5]&2) // Media in goto start_unit; break; case 5: ret |= 1<<5; break; // Multiple Initiators case 6: // Device Busy if ((event[4]&0xF)==1 && // Timeout occured (event[5]&0x3)!=0) { poll(NULL,0,(descr&0xFFFF)*100+100); cmd[0] = 0; // TEST UNIT READY cmd[5] = 0; if ((err=cmd.transport())) sperror("TEST UNIT READY",err); ret |= 1<<6; } break; case 7: ret |= 1<<7; break; // Reserved } } return ret; } #undef DUMP_EVENTS static int wait_for_unit (Scsi_Command &cmd,volatile int *progress=NULL) { unsigned char *sense=cmd.sense(),sensebuf[18]; int err; long msecs=1000; while (1) { if (msecs > 0) poll(NULL,0,msecs); msecs = getmsecs(); cmd[0] = 0; // TEST UNIT READY cmd[5] = 0; if (!(err=cmd.transport ())) break; // I wish I could test just for sense.valid, but (at least) // hp dvd100i returns 0 in valid bit at this point:-( So I // check for all bits... if ((sense[0]&0x70)==0) { perror (":-( unable to TEST UNIT READY"); return -1; } else if (sense[12]==0x3A) // doesn't get any further than "no media" return err; while (progress) { if (sense[15]&0x80) { *progress = sense[16]<<8|sense[17]; break; } // MMC-3 (draft) specification says that the unit should // return progress indicator in key specific bytes even // in reply to TEST UNIT READY. I.e. as above! But (at // least) hp dvd100i doesn't do that and I have to fetch // it separately:-( cmd[0] = 0x03; // REQUEST SENSE cmd[4] = sizeof(sensebuf); cmd[5] = 0; if ((err=cmd.transport (READ,sensebuf,sizeof(sensebuf)))) { sperror ("REQUEST SENSE",err); return err; } if (sensebuf[15]&0x80) *progress = sensebuf[16]<<8|sensebuf[17]; break; } msecs = 1000 - (getmsecs() - msecs); } return 0; } static int pioneer_stop(Scsi_Command &cmd,volatile int *progress=NULL) { int err; unsigned char *sense=cmd.sense(); // Pioneer units tend to just fall through wait_for_unit() and // report "OP IN PROGRESS" on next non-TEST UNIT READY command. // This behaviour is explicitly discouraged in MMC, but what one // can do? Well, whenever appropriate one can deploy STOP UNIT // as "barrier" command with all units... while (1) { cmd[0] = 0x1B; // START/STOP UNIT cmd[1] = 0x1; // "IMMED" cmd[4] = 0; // "Stop" cmd[5] = 0; if ((err=cmd.transport()) == 0x20407) // "OP IN PROGRESS" { if (progress && sense[15]&0x80) *progress = sense[16]<<8|sense[17]; poll (NULL,0,333); continue; } break; } return err; } #define FEATURE21_BROKEN 1 static void page05_setup (Scsi_Command &cmd, unsigned short profile=0, unsigned char p32=0xC0) // 5 least significant bits of p32 go to p[2], Test Write&Write Type // 2 most significant bits go to p[3], Multi-session field // 0xC0 means "Multi-session, no Test Write, Incremental" { unsigned int len,bdlen; unsigned char header[12],track[32],*p; #if !FEATURE21_BROKEN unsigned char feature21[24]; #endif int err; class autofree page05; if (profile==0) { unsigned char prof[8]; cmd[0] = 0x46; // GET CONFIGURATION cmd[8] = sizeof(prof); cmd[9] = 0; if ((err=cmd.transport(READ,prof,sizeof(prof)))) sperror ("GET CONFIGURATION",err), exit(FATAL_START(errno)); profile = prof[6]<<8|prof[7]; } #if !FEATURE21_BROKEN if (profile==0x11 || profile==0x14) { cmd[0] = 0x46; // GET CONFIGURATION cmd[1] = 2; // ask for the only feature... cmd[3] = 0x21; // the "Incremental Streaming Writable" one cmd[8] = 8; // read the header only to start with cmd[9] = 0; if ((err=cmd.transport(READ,feature21,8))) sperror ("GET CONFIGURATION",err), exit(FATAL_START(errno)); len = feature21[0]<<24|feature21[1]<<16|feature21[2]<<8|feature21[3]; len += 4; if (len>sizeof(feature21)) len = sizeof(feature21); else if (len<(8+8)) fprintf (stderr,":-( READ FEATURE DESCRIPTOR 0021h: insane length\n"), exit(FATAL_START(EINVAL)); cmd[0] = 0x46; // GET CONFIGURATION cmd[1] = 2; // ask for the only feature... cmd[3] = 0x21; // the "Incremental Streaming Writable" one cmd[8] = len; // this time with real length cmd[9] = 0; if ((err=cmd.transport(READ,feature21,len))) sperror ("READ FEATURE DESCRIPTOR 0021h",err), exit(FATAL_START(errno)); if ((feature21[8+2]&1)==0) fprintf (stderr,":-( FEATURE 0021h is not in effect\n"), exit(FATAL_START(EMEDIUMTYPE)); } else feature21[8+2]=0; #endif cmd[0] = 0x52; // READ TRACK INFORMATION cmd[1] = 1; // TRACK INFORMATION cmd[5] = 1; // track#1, in DVD context it's safe to assume // that all tracks are in the same mode cmd[8] = sizeof(track); cmd[9] = 0; if ((err=cmd.transport(READ,track,sizeof(track)))) sperror ("READ TRACK INFORMATION",err), exit(FATAL_START(errno)); // WRITE PAGE SETUP // cmd[0] = 0x5A; // MODE SENSE cmd[1] = 0x08; // "Disable Block Descriptors" cmd[2] = 0x05; // "Write Page" cmd[8] = sizeof(header); // header only to start with cmd[9] = 0; if ((err=cmd.transport(READ,header,sizeof(header)))) sperror ("MODE SENSE",err), exit(FATAL_START(errno)); len = (header[0]<<8|header[1])+2; bdlen = header[6]<<8|header[7]; if (bdlen) // should never happen as we set "DBD" above { if (len <= (8+bdlen+14)) fprintf (stderr,":-( LUN is impossible to bear with...\n"), exit(FATAL_START(EINVAL)); } else if (len < (8+2+(unsigned int)header[9]))// SANYO does this. len = 8+2+header[9]; page05 = (unsigned char *)malloc(len); if (page05 == NULL) fprintf (stderr,":-( memory exhausted\n"), exit(FATAL_START(ENOMEM)); cmd[0] = 0x5A; // MODE SENSE cmd[1] = 0x08; // "Disable Block Descriptors" cmd[2] = 0x05; // "Write Page" cmd[7] = len>>8; cmd[8] = len; // real length this time cmd[9] = 0; if ((err=cmd.transport(READ,page05,len))) sperror("MODE SENSE",err), exit(FATAL_START(errno)); len -= 2; if (len < ((unsigned int)page05[0]<<8|page05[1])) // paranoia:-) page05[0] = len>>8, page05[1] = len; len = (page05[0]<<8|page05[1])+2; bdlen = page05[6]<<8|page05[7]; len -= bdlen; if (len < (8+14)) fprintf (stderr,":-( LUN is impossible to bear with...\n"), exit(FATAL_START(EINVAL)); p = page05 + 8 + bdlen; memset (p-8,0,8); p[0] &= 0x7F; // copy "Test Write" and "Write Type" from p32 p[2] &= ~0x1F, p[2] |= p32&0x1F; p[2] |= 0x40; // insist on BUFE on // setup Preferred Link Size #if !FEATURE21_BROKEN if (feature21[8+2]&1) { if (feature21[8+7]) p[2] |= 0x20, p[5] = feature21[8+8]; else p[2] &= ~0x20, p[5] = 0; } #else // At least Pioneer DVR-104 returns some bogus data in // Preferred Link Size... if (profile==0x11 || profile==0x14) // Sequential recordings... p[2] |= 0x20, p[5] = 0x10; #endif else p[2] &= ~0x20, p[5] = 0; // copy Track Mode from TRACK INFORMATION // [some DVD-R units (most notably Panasonic LF-D310), insist // on Track Mode 5, even though it's effectively ignored] p[3] &= ~0x0F, p[3] |= profile==0x11?5:(track[5]&0x0F); // copy "Multi-session" bits from p32 p[3] &= ~0xC0, p[3] |= p32&0xC0; if (profile == 0x13) // DVD-RW Restricted Overwrite p[3] &= 0x3F; // always Single-session? // setup Data Block Type // Some units [e.g. Toshiba/Samsung TS-H542A] return "unknown Data // Block Type" in track[6]&0x0F field. Essentially it's a firmware // glitch, yet it makes certain sense, as track may not be written // yet... if ((track[6]&0x0F)==1 || (track[6]&0x0F)==0x0F) p[4] = 8; else fprintf (stderr,":-( none Mode 1 track\n"), exit(FATAL_START(EMEDIUMTYPE)); // setup Packet Size // [some DVD-R units (most notably Panasonic LF-D310), insist // on fixed Packet Size of 16 blocks, even though it's effectively // ignored] p[3] |= 0x20, memset (p+10,0,4), p[13] = 0x10; if (track[6]&0x10) memcpy (p+10,track+20,4); // Fixed else if (profile != 0x11) p[3] &= ~0x20, p[13] = 0; // Variable switch (profile) { case 0x13: // DVD-RW Restricted Overwrite if (!(track[6]&0x10)) fprintf (stderr,":-( track is not formatted for fixed packet size\n"), exit(FATAL_START(EMEDIUMTYPE)); break; case 0x14: // DVD-RW Sequential Recording case 0x11: // DVD-R Sequential Recording if (track[6]&0x10) fprintf (stderr,":-( track is formatted for fixed packet size\n"), exit(FATAL_START(EMEDIUMTYPE)); break; default: #if 0 fprintf (stderr,":-( invalid profile %04xh\n",profile); exit(FATAL_START(EMEDIUMTYPE)); #endif break; } p[8] = 0; // "Session Format" should be ignored, but // I reset it just in case... cmd[0] = 0x55; // MODE SELECT cmd[1] = 0x10; // conformant cmd[7] = len>>8; cmd[8] = len; cmd[9] = 0; if ((err=cmd.transport(WRITE,p-8,len))) sperror ("MODE SELECT",err), exit(FATAL_START(errno)); // END OF WRITE PAGE SETUP // } #undef FEATURE21_BROKEN #undef ERRCODE #undef CREAM_ON_ERRNO #undef CREAM_ON_ERRNO_NAKED dvd+rw-tools-7.1/mp.h0000644000000100000010000000444610760327667012605 0ustar binbin#ifdef _WIN32 #ifndef _WIN32_WINNT #define _WIN32_WINNT 0x0400 #endif #include #define THR_TYPE DWORD WINAPI static void *__thread_create(DWORD (WINAPI *func)(PVOID),void *arg) { return (void *)CreateThread(NULL,64*1024,func,arg,0,NULL); } static void __thread_yield(void) { (void)SwitchToThread(); } static int __thread_wait(void *h) { DWORD ret; if (WaitForSingleObject(h,INFINITE)==WAIT_OBJECT_0 && GetExitCodeThread(h,&ret)) { CloseHandle(h); return ret; } return -1; } static void *__semaphore_create(int max){ return CreateSemaphore(NULL,0,max,NULL); } static int __semaphore_wait(void *h) { return WaitForSingleObject(h,INFINITE)!=WAIT_FAILED; } static int __semaphore_post(void *h) { return ReleaseSemaphore(h,1,NULL); } static void __semaphore_close(void *h) { CloseHandle(h); } #else #include #include #include #include #include #define THR_TYPE int static void *__thread_create(int (*func)(void *),void *arg) { pthread_t h; pthread_attr_t attr; size_t stack_sz=16*1024; #ifdef PTHREAD_STACK_MIN stack_sz += PTHREAD_STACK_MIN; #endif if (pthread_attr_init(&attr)==0 && pthread_attr_setstacksize(&attr,stack_sz)==0 && pthread_attr_setscope(&attr,PTHREAD_SCOPE_SYSTEM)==0 && pthread_create(&h,&attr,(void *(*)(void *))func,arg)==0 ) return (void *)h; return NULL; } static void __thread_yield(void) { (void)sched_yield(); } static int __thread_wait(void *h) { void *ret; if (pthread_join((pthread_t)((size_t)h),&ret)==0) return (int)ret; return -1; } #if defined(__APPLE__) && defined(__MACH__) // Mac OS X doesn't implement unnamed semaphores, g-r-r-r-r... static void *__semaphore_create(int max) { sem_t *h = sem_open ("growisofs",O_CREAT|O_EXCL,0700,0); if (h==(sem_t*)SEM_FAILED) return NULL; sem_unlink ("growisofs"); return h; } static void __semaphore_close(void *h) { sem_close(h); } #else static void *__semaphore_create(int max) { sem_t *h = calloc(1,sizeof(sem_t)); if (h==NULL) return h; if (sem_init(h,0,0)<0) { free(h); return NULL; } return h; } static void __semaphore_close(void *h) { sem_destroy(h), free(h); } #endif static int __semaphore_wait(void *h) { return sem_wait(h)==0; } static int __semaphore_post(void *h) { return sem_post(h)==0; } #endif dvd+rw-tools-7.1/win32err.h0000644000000100000010000000245710763304122013624 0ustar binbin# define EINVAL ERROR_BAD_ARGUMENTS # define ENOMEM ERROR_OUTOFMEMORY # define EMEDIUMTYPE ERROR_MEDIA_INCOMPATIBLE # define ENOMEDIUM ERROR_MEDIA_OFFLINE # define ENODEV ERROR_BAD_COMMAND # define EAGAIN ERROR_NOT_READY # define ENOSPC ERROR_DISK_FULL # define EIO ERROR_NOT_SUPPORTED # define ENXIO ERROR_GEN_FAILURE # define EBUSY ERROR_SHARING_VIOLATION # define ENOENT ERROR_FILE_NOT_FOUND # define ENOTDIR ERROR_NO_VOLUME_LABEL # define EINTR ERROR_OPERATION_ABORTED # define FATAL_START(e) (0x10000|(e)) # define FATAL_MASK 0x0FFFF #ifdef errno # undef errno #endif #ifdef __cplusplus static class __win32_errno__ { public: operator int() { return GetLastError(); } int operator=(int e) { SetLastError(e); return e; } } win32_errno; # define errno win32_errno #else # define errno (GetLastError()) #endif #define set_errno(e) (SetLastError(e),e) #ifdef perror #undef perror #endif #define perror win32_perror static void win32_perror (const char *str) { LPSTR lpMsgBuf; FormatMessageA( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), 0, // Default language (LPSTR) &lpMsgBuf, 0, NULL ); if (str) fprintf (stderr,"%s: %s",str,lpMsgBuf); else fprintf (stderr,"%s",lpMsgBuf); LocalFree(lpMsgBuf); } dvd+rw-tools-7.1/keys.txt0000644000000100000010000002574510755143236013531 0ustar binbinSK ASC ASCQ Description 0 00 00 NO ADDITIONAL SENSE INFORMATION B 00 06 I/O PROCESS TERMINATED 5 00 11 AUDIO PLAY OPERATION IN PROGRESS 5 00 12 AUDIO PLAY OPERATION PAUSED 5 00 13 AUDIO PLAY OPERATION SUCCESSFULLY COMPLETED 5 00 14 AUDIO PLAY OPERATION STOPPED DUE TO ERROR 5 00 15 NO CURRENT AUDIO STATUS TO RETURN 5 00 16 OPERATION IN PROGRESS 4 00 17 CLEANING REQUESTED 3 02 00 NO SEEK COMPLETE 2 04 00 LOGICAL UNIT NOT READY, CAUSE NOT REPORTABLE 2 04 01 LOGICAL UNIT IS IN PROCESS OF BECOMING READY 2 04 02 LOGICAL UNIT NOT READY, INITIALIZING CMD. REQUIRED 2 04 03 LOGICAL UNIT NOT READY, MANUAL INTERVENTION REQUIRED 0/2 04 04 LOGICAL UNIT NOT READY, FORMAT IN PROGRESS 2 04 07 LOGICAL UNIT NOT READY, OPERATION IN PROGRESS 2 04 08 LOGICAL UNIT NOT READY, LONG WRITE IN PROGRESS 2 04 09 LOGICAL UNIT NOT READY, SELF-TEST IN PROGRESS 4 05 00 LOGICAL UNIT DOES NOT RESPOND TO SELECTION 3 06 00 NO REFERENCE POSITION FOUND 5 07 00 MULTIPLE PERIPHERAL DEVICES SELECTED 4 08 00 LOGICAL UNIT COMMUNICATION FAILURE 4 08 01 LOGICAL UNIT COMMUNICATION TIMEOUT 4 08 02 LOGICAL UNIT COMMUNICATION PARITY ERROR 4 08 03 LOGICAL UNIT COMMUNICATION CRC ERROR (ULTRA-DMA/32) 4 09 00 TRACK FOLLOWING ERROR 4 09 01 TRACKING SERVO FAILURE 4 09 02 FOCUS SERVO FAILURE 4 09 03 SPINDLE SERVO FAILURE 4 09 04 HEAD SELECT FAULT 6 0A 00 ERROR LOG OVERFLOW 1 0B 00 WARNING 1 0B 01 WARNING - SPECIFIED TEMPERATURE EXCEEDED 1 0B 02 WARNING - ENCLOSURE DEGRADED 1 0B 03 WARNING - BACKGROUND SELF-TEST FAILED 1 0B 04 WARNING - BACKGROUND PRE-SCAN DETECTED MEDIUM ERROR 1 0B 05 WARNING - BACKGROUND MEDIUM SCAN DETECTED MEDIUM ERROR 3 0C 00 WRITE ERROR 1 0C 01 WRITE ERROR - RECOVERED WITH AUTO-REALLOCATION 3 0C 02 WRITE ERROR - AUTO-REALLOCATION FAILED 3 0C 03 WRITE ERROR - RECOMMEND REASSIGNMENT 2/3 0C 07 WRITE ERROR - RECOVERY NEEDED 3 0C 08 WRITE ERROR - RECOVERY FAILED 3 0C 09 WRITE ERROR - LOSS OF STREAMING 3 0C 0A WRITE ERROR - PADDING BLOCKS ADDED 2 0C 0F DEFECTS IN ERROR WINDOW 3 11 00 UNRECOVERED READ ERROR 3 11 01 READ RETRIES EXHAUSTED 3 11 02 ERROR TOO LONG TO CORRECT 3 11 05 L-EC UNCORRECTABLE ERROR 3 11 06 CIRC UNRECOVERED ERROR 3 11 0F ERROR READING UPC/EAN NUMBER 3 11 10 ERROR READING ISRC NUMBER B 11 11 READ ERROR - LOSS OF STREAMING 3/4 15 00 RANDOM POSITIONING ERROR 3/4 15 01 MECHANICAL POSITIONING ERROR 3 15 02 POSITIONING ERROR DETECTED BY READ OF MEDIUM 1 17 00 RECOVERED DATA WITH NO ERROR CORRECTION APPLIED 1 17 01 RECOVERED DATA WITH RETRIES 1 17 02 RECOVERED DATA WITH POSITIVE HEAD OFFSET 1 17 03 RECOVERED DATA WITH NEGATIVE HEAD OFFSET 1 17 04 RECOVERED DATA WITH RETRIES AND/OR CIRC APPLIED 1 17 05 RECOVERED DATA USING PREVIOUS SECTOR ID 1 17 07 RECOVERED DATA WITHOUT ECC - RECOMMEND REASSIGNMENT 1 17 08 RECOVERED DATA WITHOUT ECC - RECOMMEND REWRITE 1 17 09 RECOVERED DATA WITHOUT ECC - DATA REWRITTEN 1 18 00 RECOVERED DATA WITH ERROR CORRECTION APPLIED 1 18 01 RECOVERED DATA WITH ERROR CORR. & RETRIES APPLIED 1 18 02 RECOVERED DATA - DATA AUTO-REALLOCATED 1 18 03 RECOVERED DATA WITH CIRC 1 18 04 RECOVERED DATA WITH L-EC 1 18 05 RECOVERED DATA - RECOMMEND REASSIGNMENT 1 18 06 RECOVERED DATA - RECOMMEND REWRITE 1 18 08 RECOVERED DATA WITH LINKING 5 1A 00 PARAMETER LIST LENGTH ERROR 4 1B 00 SYNCHRONOUS DATA TRANSFER ERROR A 1D 00 MISCOMPARE DURING VERIFY OPERATION 5 20 00 INVALID COMMAND OPERATION CODE 5 21 00 LOGICAL BLOCK ADDRESS OUT OF RANGE 5 21 01 INVALID ELEMENT ADDRESS 5 21 02 INVALID ADDRESS FOR WRITE 5 21 03 INVALID WRITE CROSSING LAYER JUMP 5 22 00 ILLEGAL FUNCTION 5 24 00 INVALID FIELD IN CDB 5 25 00 LOGICAL UNIT NOT SUPPORTED 5 26 00 INVALID FIELD IN PARAMETER LIST 5 26 01 PARAMETER NOT SUPPORTED 5 26 02 PARAMETER VALUE INVALID 5 26 03 THRESHOLD PARAMETERS NOT SUPPORTED 5 26 04 INVALID RELEASE OF PERSISTENT RESERVATION 7 27 00 WRITE PROTECTED 7 27 01 HARDWARE WRITE PROTECTED 7 27 02 LOGICAL UNIT SOFTWARE WRITE PROTECTED 7 27 03 ASSOCIATED WRITE PROTECT 7 27 04 PERSISTENT WRITE PROTECT 7 27 05 PERMANENT WRITE PROTECT 7 27 06 CONDITIONAL WRITE PROTECT 6 28 00 NOT READY TO READY CHANGE, MEDIUM MAY HAVE CHANGED 6 28 01 IMPORT OR EXPORT ELEMENT ACCESSED 6 28 02 FORMAT-LAYER MAY HAVE CHANGED 6 29 00 POWER ON, RESET, OR BUS DEVICE RESET OCCURRED 6 29 01 POWER ON OCCURRED 6 29 02 BUS RESET OCCURRED 6 29 03 BUS DEVICE RESET FUNCTION OCCURRED 6 29 04 DEVICE INTERNAL RESET 6 2A 00 PARAMETERS CHANGED 6 2A 01 MODE PARAMETERS CHANGED 6 2A 02 LOG PARAMETERS CHANGED 6 2A 03 RESERVATIONS PREEMPTED 5 2B 00 COPY CANNOT EXECUTE SINCE INITIATOR CANNOT DISCONNECT 5 2C 00 COMMAND SEQUENCE ERROR 5 2C 03 CURRENT PROGRAM AREA IS NOT EMPTY 5 2C 04 CURRENT PROGRAM AREA IS EMPTY 6 2E 00 INSUFFICIENT TIME FOR OPERATION 6 2F 00 COMMANDS CLEARED BY ANOTHER INITIATOR 2/5 30 00 INCOMPATIBLE MEDIUM INSTALLED 2/5 30 01 CANNOT READ MEDIUM - UNKNOWN FORMAT 2/5 30 02 CANNOT READ MEDIUM - INCOMPATIBLE FORMAT 2/5 30 03 CLEANING CARTRIDGE INSTALLED 2/5 30 04 CANNOT WRITE MEDIUM - UNKNOWN FORMAT 2/5 30 05 CANNOT WRITE MEDIUM - INCOMPATIBLE FORMAT 2/5 30 06 CANNOT FORMAT MEDIUM - INCOMPATIBLE MEDIUM 2/5 30 07 CLEANING FAILURE 5 30 08 CANNOT WRITE - APPLICATION CODE MISMATCH 5 30 09 CURRENT SESSION NOT FIXATED FOR APPEND 5 30 10 MEDIUM NOT FORMATTED 2/5 30 11 CANNOT WRITE MEDIUM - UNSUPPORTED MEDIUM VERSION 3 31 00 MEDIUM FORMAT CORRUPTED 3 31 01 FORMAT COMMAND FAILED 3 31 02 ZONED FORMATTING FAILED DUE TO SPARE LINKING 3 32 00 NO DEFECT SPARE LOCATION AVAILABLE - 34 00 ENCLOSURE FAILURE - 35 00 ENCLOSURE SERVICES FAILURE - 35 01 UNSUPPORTED ENCLOSURE FUNCTION - 35 02 ENCLOSURE SERVICES UNAVAILABLE - 35 03 ENCLOSURE SERVICES TRANSFER FAILURE - 35 04 RNCLOSURE SERVICES TRANSFER REFUSED - 35 05 ENCLOSURE SERVICES CHECKSUM ERROR 1 37 00 ROUNDED PARAMETER 5 39 00 SAVING PARAMETERS NOT SUPPORTED 2 3A 00 MEDIUM NOT PRESENT 2 3A 01 MEDIUM NOT PRESENT - TRAY CLOSED 2 3A 02 MEDIUM NOT PRESENT - TRAY OPEN 2 3A 03 MEDIUM NOT PRESENT - LOADABLE 6 3B 0D MEDIUM DESTINATION ELEMENT FULL 6 3B 0E MEDIUM SOURCE ELEMENT EMPTY 6 3B 0F END OF MEDIUM REACHED 6 3B 11 MEDIUM MAGAZINE NOT ACCESSIBLE 6 3B 12 MEDIUM MAGAZINE REMOVED 6 3B 13 MEDIUM MAGAZINE INSERTED 6 3B 14 MEDIUM MAGAZINE LOCKED 6 3B 15 MEDIUM MAGAZINE UNLOCKED 4 3B 16 MECHANICAL POSITIONING OR CHANGER ERROR 5 3D 00 INVALID BITS IN IDENTIFY MESSAGE 2 3E 00 LOGICAL UNIT HAS NOT SELF-CONFIGURED YET 4 3E 01 LOGICAL UNIT FAILURE 4 3E 02 TIMEOUT ON LOGICAL UNIT 6 3F 00 TARGET OPERATING CONDITIONS HAVE CHANGED 6 3F 01 MICROCODE HAS BEEN CHANGED 6 3F 02 CHANGED OPERATING DEFINITION 6 3F 03 INQUIRY DATA HAS CHANGED 4 40 NN DIAGNOSTIC FAILURE ON COMPONENT NN (80H-FFH) 5 43 00 MESSAGE ERROR 4 44 00 INTERNAL TARGET FAILURE B 45 00 SELECT OR RESELECT FAILURE 4 46 00 UNSUCCESSFUL SOFT RESET 4 47 00 SCSI PARITY ERROR B 48 00 INITIATOR DETECTED ERROR MESSAGE RECEIVED B 49 00 INVALID MESSAGE ERROR 4 4A 00 COMMAND PHASE ERROR 4 4B 00 DATA PHASE ERROR 4 4C 00 LOGICAL UNIT FAILED SELF-CONFIGURATION B 4D NN TAGGED OVERLAPPED COMMANDS (NN = QUEUE TAG) B 4E 00 OVERLAPPED COMMANDS ATTEMPTED 3 51 00 ERASE FAILURE 3 51 01 ERASE FAILURE - INCOMPLETE ERASE OPERATION DETECTED 4 53 00 MEDIA LOAD OR EJECT FAILED 5 53 02 MEDIUM REMOVAL PREVENTED 5 55 00 SYSTEM RESOURCE FAILURE 3 57 00 UNABLE TO RECOVER TABLE-OF-CONTENTS 6 5A 00 OPERATOR REQUEST OR STATE CHANGE INPUT 6 5A 01 OPERATOR MEDIUM REMOVAL REQUEST 6 5A 02 OPERATOR SELECTED WRITE PROTECT 6 5A 03 OPERATOR SELECTED WRITE PERMIT 6 5B 00 LOG EXCEPTION 6 5B 01 THRESHOLD CONDITION MET 6 5B 02 LOG COUNTER AT MAXIMUM 6 5B 03 LOG LIST CODES EXHAUSTED 1 5D 00 FAILURE PREDICTION THRESHOLD EXCEEDED 1 5D 01 MEDIA FAILURE PREDICTION THRESHOLD EXCEEDED 1 5D 02 LOGICAL UNIT FAILURE PREDICTION THRESHOLD EXCEEDED 1 5D 03 SPARE AREA EXHAUSTION FAILURE PREDICTION THRESHOLD EXCEEDED 1 5D FF FAILURE PREDICTION THRESHOLD EXCEEDED (FALSE) 6 5E 00 LOW POWER CONDITION ON 6 5E 01 IDLE CONDITION ACTIVATED BY TIMER 6 5E 02 STANDBY CONDITION ACTIVATED BY TIMER 6 5E 03 IDLE CONDITION ACTIVATED BY COMMAND 6 5E 04 STANDBY CONDITION ACTIVATED BY COMMAND 5 63 00 END OF USER AREA ENCOUNTERED ON THIS TRACK 5 63 01 PACKET DOES NOT FIT IN AVAILABLE SPACE 5 64 00 ILLEGAL MODE FOR THIS TRACK 5 64 01 INVALID PACKET SIZE 4 65 00 VOLTAGE FAULT 5 6F 00 COPY PROTECTION KEY EXCHANGE FAILURE - AUTHENTICATION FAILURE 5 6F 01 COPY PROTECTION KEY EXCHANGE FAILURE - KEY NOT PRESENT 5 6F 02 COPY PROTECTION KEY EXCHANGE FAILURE - KEY NOT ESTABLISHED 5 6F 03 READ OF SCRAMBLED SECTOR WITHOUT AUTHENTICATION 5 6F 04 MEDIA REGION CODE IS MISMATCHED TO LOGICAL UNIT REGION 5 6F 05 LOGICAL UNIT REGION MUST BE PERMANENT/REGION RESET COUNT ERROR 5 6F 06 INSUFFICIENT BLOCK COUNT FOR BINDING NONCE RECORDING 5 6F 07 CONFLICT IN BINDING NONCE RECORDING 3 72 00 SESSION FIXATION ERROR 3 72 01 SESSION FIXATION ERROR WRITING LEAD-IN 3 72 02 SESSION FIXATION ERROR WRITING LEAD-OUT 5 72 03 SESSION FIXATION ERROR - INCOMPLETE TRACK IN SESSION 5 72 04 EMPTY OR PARTIALLY WRITTEN RESERVED TRACK 5 72 05 NO MORE TRACK RESERVATIONS ALLOWED 5 72 06 RMZ EXTENSION IS NOT ALLOWED 5 72 07 NO MORE TEST ZONE EXTENSIONS ARE ALLOWED 3 73 00 CD CONTROL ERROR 1 73 01 POWER CALIBRATION AREA ALMOST FULL 3 73 02 POWER CALIBRATION AREA IS FULL 3 73 03 POWER CALIBRATION AREA ERROR 3 73 04 PROGRAM MEMORY AREA UPDATE FAILURE 3 73 05 PROGRAM MEMORY AREA IS FULL 1 73 06 RMA/PMA IS ALMOST FULL 3 73 10 CURRENT POWER CALIBRATION AREA IS ALMOST FULL 3 73 11 CURRENT POWER CALIBRATION AREA IS FULL 5 73 17 RDZ IS FULL 8 -- -- BLANK CHECK dvd+rw-tools-7.1/genasctable.pl0000755000000100000010000000130310755143233014602 0ustar binbin#!/bin/env perl print <<___; const char *ASC_lookup (int code) { static const struct { int code; const char *msg; } _table [] = { ___ while(<>) { chomp; if (m/([0-9A-F\-\/]+)\s+([0-9A-F]{2})\s+([0-9A-F]{2})\s+(.*)/i) { printf " { 0x%s%s, \"%s\" },\n",$2,$3,$4; } } print <<___; { 0xFFFF, NULL } }; int sz = sizeof(_table)/sizeof(_table[0])/2,i=sz; code &= 0xFFFF; while (sz) { if (_table[i].code == code) return _table[i].msg; if (_table[i].code > code) { if (sz/=2) i -= sz; else while (_table[--i].code > code); } else { if (sz/=2) i += sz; else while (_table[++i].code < code); } } return _table[i].code==code ? _table[i].msg : NULL; } ___ dvd+rw-tools-7.1/asctable.h0000644000000100000010000002773510755143237013746 0ustar binbinconst char *ASC_lookup (int code) { static const struct { int code; const char *msg; } _table [] = { { 0x0000, "NO ADDITIONAL SENSE INFORMATION" }, { 0x0006, "I/O PROCESS TERMINATED" }, { 0x0011, "AUDIO PLAY OPERATION IN PROGRESS" }, { 0x0012, "AUDIO PLAY OPERATION PAUSED" }, { 0x0013, "AUDIO PLAY OPERATION SUCCESSFULLY COMPLETED" }, { 0x0014, "AUDIO PLAY OPERATION STOPPED DUE TO ERROR" }, { 0x0015, "NO CURRENT AUDIO STATUS TO RETURN" }, { 0x0016, "OPERATION IN PROGRESS" }, { 0x0017, "CLEANING REQUESTED" }, { 0x0200, "NO SEEK COMPLETE" }, { 0x0400, "LOGICAL UNIT NOT READY, CAUSE NOT REPORTABLE" }, { 0x0401, "LOGICAL UNIT IS IN PROCESS OF BECOMING READY" }, { 0x0402, "LOGICAL UNIT NOT READY, INITIALIZING CMD. REQUIRED" }, { 0x0403, "LOGICAL UNIT NOT READY, MANUAL INTERVENTION REQUIRED" }, { 0x0404, "LOGICAL UNIT NOT READY, FORMAT IN PROGRESS" }, { 0x0407, "LOGICAL UNIT NOT READY, OPERATION IN PROGRESS" }, { 0x0408, "LOGICAL UNIT NOT READY, LONG WRITE IN PROGRESS" }, { 0x0409, "LOGICAL UNIT NOT READY, SELF-TEST IN PROGRESS" }, { 0x0500, "LOGICAL UNIT DOES NOT RESPOND TO SELECTION" }, { 0x0600, "NO REFERENCE POSITION FOUND" }, { 0x0700, "MULTIPLE PERIPHERAL DEVICES SELECTED" }, { 0x0800, "LOGICAL UNIT COMMUNICATION FAILURE" }, { 0x0801, "LOGICAL UNIT COMMUNICATION TIMEOUT" }, { 0x0802, "LOGICAL UNIT COMMUNICATION PARITY ERROR" }, { 0x0803, "LOGICAL UNIT COMMUNICATION CRC ERROR (ULTRA-DMA/32)" }, { 0x0900, "TRACK FOLLOWING ERROR" }, { 0x0901, "TRACKING SERVO FAILURE" }, { 0x0902, "FOCUS SERVO FAILURE" }, { 0x0903, "SPINDLE SERVO FAILURE" }, { 0x0904, "HEAD SELECT FAULT" }, { 0x0A00, "ERROR LOG OVERFLOW" }, { 0x0B00, "WARNING" }, { 0x0B01, "WARNING - SPECIFIED TEMPERATURE EXCEEDED" }, { 0x0B02, "WARNING - ENCLOSURE DEGRADED" }, { 0x0B03, "WARNING - BACKGROUND SELF-TEST FAILED" }, { 0x0B04, "WARNING - BACKGROUND PRE-SCAN DETECTED MEDIUM ERROR" }, { 0x0B05, "WARNING - BACKGROUND MEDIUM SCAN DETECTED MEDIUM ERROR" }, { 0x0C00, "WRITE ERROR" }, { 0x0C01, "WRITE ERROR - RECOVERED WITH AUTO-REALLOCATION" }, { 0x0C02, "WRITE ERROR - AUTO-REALLOCATION FAILED" }, { 0x0C03, "WRITE ERROR - RECOMMEND REASSIGNMENT" }, { 0x0C07, "WRITE ERROR - RECOVERY NEEDED" }, { 0x0C08, "WRITE ERROR - RECOVERY FAILED" }, { 0x0C09, "WRITE ERROR - LOSS OF STREAMING" }, { 0x0C0A, "WRITE ERROR - PADDING BLOCKS ADDED" }, { 0x0C0F, "DEFECTS IN ERROR WINDOW" }, { 0x1100, "UNRECOVERED READ ERROR" }, { 0x1101, "READ RETRIES EXHAUSTED" }, { 0x1102, "ERROR TOO LONG TO CORRECT" }, { 0x1105, "L-EC UNCORRECTABLE ERROR" }, { 0x1106, "CIRC UNRECOVERED ERROR" }, { 0x110F, "ERROR READING UPC/EAN NUMBER" }, { 0x1110, "ERROR READING ISRC NUMBER" }, { 0x1111, "READ ERROR - LOSS OF STREAMING" }, { 0x1500, "RANDOM POSITIONING ERROR" }, { 0x1501, "MECHANICAL POSITIONING ERROR" }, { 0x1502, "POSITIONING ERROR DETECTED BY READ OF MEDIUM" }, { 0x1700, "RECOVERED DATA WITH NO ERROR CORRECTION APPLIED" }, { 0x1701, "RECOVERED DATA WITH RETRIES" }, { 0x1702, "RECOVERED DATA WITH POSITIVE HEAD OFFSET" }, { 0x1703, "RECOVERED DATA WITH NEGATIVE HEAD OFFSET" }, { 0x1704, "RECOVERED DATA WITH RETRIES AND/OR CIRC APPLIED" }, { 0x1705, "RECOVERED DATA USING PREVIOUS SECTOR ID" }, { 0x1707, "RECOVERED DATA WITHOUT ECC - RECOMMEND REASSIGNMENT" }, { 0x1708, "RECOVERED DATA WITHOUT ECC - RECOMMEND REWRITE" }, { 0x1709, "RECOVERED DATA WITHOUT ECC - DATA REWRITTEN" }, { 0x1800, "RECOVERED DATA WITH ERROR CORRECTION APPLIED" }, { 0x1801, "RECOVERED DATA WITH ERROR CORR. & RETRIES APPLIED" }, { 0x1802, "RECOVERED DATA - DATA AUTO-REALLOCATED" }, { 0x1803, "RECOVERED DATA WITH CIRC" }, { 0x1804, "RECOVERED DATA WITH L-EC" }, { 0x1805, "RECOVERED DATA - RECOMMEND REASSIGNMENT" }, { 0x1806, "RECOVERED DATA - RECOMMEND REWRITE" }, { 0x1808, "RECOVERED DATA WITH LINKING" }, { 0x1A00, "PARAMETER LIST LENGTH ERROR" }, { 0x1B00, "SYNCHRONOUS DATA TRANSFER ERROR" }, { 0x1D00, "MISCOMPARE DURING VERIFY OPERATION" }, { 0x2000, "INVALID COMMAND OPERATION CODE" }, { 0x2100, "LOGICAL BLOCK ADDRESS OUT OF RANGE" }, { 0x2101, "INVALID ELEMENT ADDRESS" }, { 0x2102, "INVALID ADDRESS FOR WRITE" }, { 0x2103, "INVALID WRITE CROSSING LAYER JUMP" }, { 0x2200, "ILLEGAL FUNCTION" }, { 0x2400, "INVALID FIELD IN CDB" }, { 0x2500, "LOGICAL UNIT NOT SUPPORTED" }, { 0x2600, "INVALID FIELD IN PARAMETER LIST" }, { 0x2601, "PARAMETER NOT SUPPORTED" }, { 0x2602, "PARAMETER VALUE INVALID" }, { 0x2603, "THRESHOLD PARAMETERS NOT SUPPORTED" }, { 0x2604, "INVALID RELEASE OF PERSISTENT RESERVATION" }, { 0x2700, "WRITE PROTECTED" }, { 0x2701, "HARDWARE WRITE PROTECTED" }, { 0x2702, "LOGICAL UNIT SOFTWARE WRITE PROTECTED" }, { 0x2703, "ASSOCIATED WRITE PROTECT" }, { 0x2704, "PERSISTENT WRITE PROTECT" }, { 0x2705, "PERMANENT WRITE PROTECT" }, { 0x2706, "CONDITIONAL WRITE PROTECT" }, { 0x2800, "NOT READY TO READY CHANGE, MEDIUM MAY HAVE CHANGED" }, { 0x2801, "IMPORT OR EXPORT ELEMENT ACCESSED" }, { 0x2802, "FORMAT-LAYER MAY HAVE CHANGED" }, { 0x2900, "POWER ON, RESET, OR BUS DEVICE RESET OCCURRED" }, { 0x2901, "POWER ON OCCURRED" }, { 0x2902, "BUS RESET OCCURRED" }, { 0x2903, "BUS DEVICE RESET FUNCTION OCCURRED" }, { 0x2904, "DEVICE INTERNAL RESET" }, { 0x2A00, "PARAMETERS CHANGED" }, { 0x2A01, "MODE PARAMETERS CHANGED" }, { 0x2A02, "LOG PARAMETERS CHANGED" }, { 0x2A03, "RESERVATIONS PREEMPTED" }, { 0x2B00, "COPY CANNOT EXECUTE SINCE INITIATOR CANNOT DISCONNECT" }, { 0x2C00, "COMMAND SEQUENCE ERROR" }, { 0x2C03, "CURRENT PROGRAM AREA IS NOT EMPTY" }, { 0x2C04, "CURRENT PROGRAM AREA IS EMPTY" }, { 0x2E00, "INSUFFICIENT TIME FOR OPERATION" }, { 0x2F00, "COMMANDS CLEARED BY ANOTHER INITIATOR" }, { 0x3000, "INCOMPATIBLE MEDIUM INSTALLED" }, { 0x3001, "CANNOT READ MEDIUM - UNKNOWN FORMAT" }, { 0x3002, "CANNOT READ MEDIUM - INCOMPATIBLE FORMAT" }, { 0x3003, "CLEANING CARTRIDGE INSTALLED" }, { 0x3004, "CANNOT WRITE MEDIUM - UNKNOWN FORMAT" }, { 0x3005, "CANNOT WRITE MEDIUM - INCOMPATIBLE FORMAT" }, { 0x3006, "CANNOT FORMAT MEDIUM - INCOMPATIBLE MEDIUM" }, { 0x3007, "CLEANING FAILURE" }, { 0x3008, "CANNOT WRITE - APPLICATION CODE MISMATCH" }, { 0x3009, "CURRENT SESSION NOT FIXATED FOR APPEND" }, { 0x3010, "MEDIUM NOT FORMATTED" }, { 0x3011, "CANNOT WRITE MEDIUM - UNSUPPORTED MEDIUM VERSION" }, { 0x3100, "MEDIUM FORMAT CORRUPTED" }, { 0x3101, "FORMAT COMMAND FAILED" }, { 0x3102, "ZONED FORMATTING FAILED DUE TO SPARE LINKING" }, { 0x3200, "NO DEFECT SPARE LOCATION AVAILABLE" }, { 0x3400, "ENCLOSURE FAILURE" }, { 0x3500, "ENCLOSURE SERVICES FAILURE" }, { 0x3501, "UNSUPPORTED ENCLOSURE FUNCTION" }, { 0x3502, "ENCLOSURE SERVICES UNAVAILABLE" }, { 0x3503, "ENCLOSURE SERVICES TRANSFER FAILURE" }, { 0x3504, "RNCLOSURE SERVICES TRANSFER REFUSED" }, { 0x3505, "ENCLOSURE SERVICES CHECKSUM ERROR" }, { 0x3700, "ROUNDED PARAMETER" }, { 0x3900, "SAVING PARAMETERS NOT SUPPORTED" }, { 0x3A00, "MEDIUM NOT PRESENT" }, { 0x3A01, "MEDIUM NOT PRESENT - TRAY CLOSED" }, { 0x3A02, "MEDIUM NOT PRESENT - TRAY OPEN" }, { 0x3A03, "MEDIUM NOT PRESENT - LOADABLE" }, { 0x3B0D, "MEDIUM DESTINATION ELEMENT FULL" }, { 0x3B0E, "MEDIUM SOURCE ELEMENT EMPTY" }, { 0x3B0F, "END OF MEDIUM REACHED" }, { 0x3B11, "MEDIUM MAGAZINE NOT ACCESSIBLE" }, { 0x3B12, "MEDIUM MAGAZINE REMOVED" }, { 0x3B13, "MEDIUM MAGAZINE INSERTED" }, { 0x3B14, "MEDIUM MAGAZINE LOCKED" }, { 0x3B15, "MEDIUM MAGAZINE UNLOCKED" }, { 0x3B16, "MECHANICAL POSITIONING OR CHANGER ERROR" }, { 0x3D00, "INVALID BITS IN IDENTIFY MESSAGE" }, { 0x3E00, "LOGICAL UNIT HAS NOT SELF-CONFIGURED YET" }, { 0x3E01, "LOGICAL UNIT FAILURE" }, { 0x3E02, "TIMEOUT ON LOGICAL UNIT" }, { 0x3F00, "TARGET OPERATING CONDITIONS HAVE CHANGED" }, { 0x3F01, "MICROCODE HAS BEEN CHANGED" }, { 0x3F02, "CHANGED OPERATING DEFINITION" }, { 0x3F03, "INQUIRY DATA HAS CHANGED" }, { 0x4300, "MESSAGE ERROR" }, { 0x4400, "INTERNAL TARGET FAILURE" }, { 0x4500, "SELECT OR RESELECT FAILURE" }, { 0x4600, "UNSUCCESSFUL SOFT RESET" }, { 0x4700, "SCSI PARITY ERROR" }, { 0x4800, "INITIATOR DETECTED ERROR MESSAGE RECEIVED" }, { 0x4900, "INVALID MESSAGE ERROR" }, { 0x4A00, "COMMAND PHASE ERROR" }, { 0x4B00, "DATA PHASE ERROR" }, { 0x4C00, "LOGICAL UNIT FAILED SELF-CONFIGURATION" }, { 0x4E00, "OVERLAPPED COMMANDS ATTEMPTED" }, { 0x5100, "ERASE FAILURE" }, { 0x5101, "ERASE FAILURE - INCOMPLETE ERASE OPERATION DETECTED" }, { 0x5300, "MEDIA LOAD OR EJECT FAILED" }, { 0x5302, "MEDIUM REMOVAL PREVENTED" }, { 0x5500, "SYSTEM RESOURCE FAILURE" }, { 0x5700, "UNABLE TO RECOVER TABLE-OF-CONTENTS" }, { 0x5A00, "OPERATOR REQUEST OR STATE CHANGE INPUT" }, { 0x5A01, "OPERATOR MEDIUM REMOVAL REQUEST" }, { 0x5A02, "OPERATOR SELECTED WRITE PROTECT" }, { 0x5A03, "OPERATOR SELECTED WRITE PERMIT" }, { 0x5B00, "LOG EXCEPTION" }, { 0x5B01, "THRESHOLD CONDITION MET" }, { 0x5B02, "LOG COUNTER AT MAXIMUM" }, { 0x5B03, "LOG LIST CODES EXHAUSTED" }, { 0x5D00, "FAILURE PREDICTION THRESHOLD EXCEEDED" }, { 0x5D01, "MEDIA FAILURE PREDICTION THRESHOLD EXCEEDED" }, { 0x5D02, "LOGICAL UNIT FAILURE PREDICTION THRESHOLD EXCEEDED" }, { 0x5D03, "SPARE AREA EXHAUSTION FAILURE PREDICTION THRESHOLD EXCEEDED" }, { 0x5DFF, "FAILURE PREDICTION THRESHOLD EXCEEDED (FALSE)" }, { 0x5E00, "LOW POWER CONDITION ON" }, { 0x5E01, "IDLE CONDITION ACTIVATED BY TIMER" }, { 0x5E02, "STANDBY CONDITION ACTIVATED BY TIMER" }, { 0x5E03, "IDLE CONDITION ACTIVATED BY COMMAND" }, { 0x5E04, "STANDBY CONDITION ACTIVATED BY COMMAND" }, { 0x6300, "END OF USER AREA ENCOUNTERED ON THIS TRACK" }, { 0x6301, "PACKET DOES NOT FIT IN AVAILABLE SPACE" }, { 0x6400, "ILLEGAL MODE FOR THIS TRACK" }, { 0x6401, "INVALID PACKET SIZE" }, { 0x6500, "VOLTAGE FAULT" }, { 0x6F00, "COPY PROTECTION KEY EXCHANGE FAILURE - AUTHENTICATION FAILURE" }, { 0x6F01, "COPY PROTECTION KEY EXCHANGE FAILURE - KEY NOT PRESENT" }, { 0x6F02, "COPY PROTECTION KEY EXCHANGE FAILURE - KEY NOT ESTABLISHED" }, { 0x6F03, "READ OF SCRAMBLED SECTOR WITHOUT AUTHENTICATION" }, { 0x6F04, "MEDIA REGION CODE IS MISMATCHED TO LOGICAL UNIT REGION" }, { 0x6F05, "LOGICAL UNIT REGION MUST BE PERMANENT/REGION RESET COUNT ERROR" }, { 0x6F06, "INSUFFICIENT BLOCK COUNT FOR BINDING NONCE RECORDING" }, { 0x6F07, "CONFLICT IN BINDING NONCE RECORDING" }, { 0x7200, "SESSION FIXATION ERROR" }, { 0x7201, "SESSION FIXATION ERROR WRITING LEAD-IN" }, { 0x7202, "SESSION FIXATION ERROR WRITING LEAD-OUT" }, { 0x7203, "SESSION FIXATION ERROR - INCOMPLETE TRACK IN SESSION" }, { 0x7204, "EMPTY OR PARTIALLY WRITTEN RESERVED TRACK" }, { 0x7205, "NO MORE TRACK RESERVATIONS ALLOWED" }, { 0x7206, "RMZ EXTENSION IS NOT ALLOWED" }, { 0x7207, "NO MORE TEST ZONE EXTENSIONS ARE ALLOWED" }, { 0x7300, "CD CONTROL ERROR" }, { 0x7301, "POWER CALIBRATION AREA ALMOST FULL" }, { 0x7302, "POWER CALIBRATION AREA IS FULL" }, { 0x7303, "POWER CALIBRATION AREA ERROR" }, { 0x7304, "PROGRAM MEMORY AREA UPDATE FAILURE" }, { 0x7305, "PROGRAM MEMORY AREA IS FULL" }, { 0x7306, "RMA/PMA IS ALMOST FULL" }, { 0x7310, "CURRENT POWER CALIBRATION AREA IS ALMOST FULL" }, { 0x7311, "CURRENT POWER CALIBRATION AREA IS FULL" }, { 0x7317, "RDZ IS FULL" }, { 0xFFFF, NULL } }; int sz = sizeof(_table)/sizeof(_table[0])/2,i=sz; code &= 0xFFFF; while (sz) { if (_table[i].code == code) return _table[i].msg; if (_table[i].code > code) { if (sz/=2) i -= sz; else while (_table[--i].code > code); } else { if (sz/=2) i += sz; else while (_table[++i].code < code); } } return _table[i].code==code ? _table[i].msg : NULL; } dvd+rw-tools-7.1/growisofs.c0000644000000100000010000034221710763211427014174 0ustar binbin/* * growisofs 7.1 by Andy Polyakov . * * Version 6.0 is dedicated to all personal friends of mine! The code * was initially intended to be a 2006 New Year gift to them, but it * didn't happen:-( But it doesn't change the fact that I think of * you, guys! Cheers! * * Use-it-on-your-own-risk, GPL bless... * * This front-end to mkisofs(8) was originally developed to facilitate * appending of data to ISO9660 volumes residing on random write access * DVD media such as DVD+RW, DVD-RAM, as well as plain files/iso images. * At later stages even support for multi-session recording to DVD * write-once media such as DVD+R and DVD-R was added. * * As for growing random access volumes. The idea is very simple. The * program appends new data as it was added to a multisession media and * then copies the new volume descriptor(s) to the beginning of media * thus effectively updating the root catalog reference... * * For further details see http://fy.chalmers.se/~appro/linux/DVD+RW/. * * Revision history: * * 1.1: * - flush cache before copying volume descriptors; * 2.0: * - support for /dev/raw*; * - support for set-root-uid operation (needed for /dev/raw*); * - support for first "session" burning (needed for "poor-man"); * - "poor-man" support for those who don't want to recompile the * kernel; * 2.1: * - mkisofs_pid typo; * 2.2: * - uninitialized in_device variable; * - -help option; * 3.0: * - support for DVD+R; * 3.1: * - -Z fails if a file system is present and stdin is not a tty; * 3.2: * - support for image burning (needed for DVD+R as you can't use dd); * 3.3: * - 'growisofs -Z /dev/scdN image.iso' is too confusing, implement * 'growisofs -Z /dev/scdN=image.iso' instead; * 4.0: * - transport C++-fication for better portability; * - support for -dvd-compat option (improved DVD+R/RW compatibility); * - -dvd-video implies -dvd-compat; * - support for SONY DRU-500A; * - progress indicator for -Z /dev/scdN=image.iso; * - agressive -poor-man-ing; * 4.1: * - uninitialized errno at exit from -Z /dev/scdN=image.iso; * 4.2: * - don't print initial bogus progress indicator values; * - apparently some firmwares exhibit ambiguity in DVD+R disc * finalizing code; * 5.0: * - enforced 32K write strategy (needed for DVD-R[W]); * - support for DVD-RW Restricted Overwrite Mode; * - support for DVD-R[W] Sequential Mode; * 5.1: * - support for writing speed control; * 5.2: * - re-make it work under Linux 2.2 kernel; * - progress indicator to display recording velocity; * - code to protect against overburns; * - undocumented -use-the-force-luke flag to overwrite the media * none interactively; * - brown-bag bug in "LONG WRITE IN PROGRESS" handling code fixed; * 5.3: * - Pioneer workarounds/fix-ups, most notably DVR-x05 doesn't seem * to digest immediate "SYNC CACHE"; * - support for DVD-RW Quick Format, upon release verified to work * with Pioneer DVR-x05; * - bug in DVD+RW overburn "protection" code fixed; * - media reload is moved here from dvd+rw-format; * - refuse to burn if session starts close to or beyond 4GB limit * (limitation of Linux isofs implementation); * - dry_run check is postponed all the way till the first write; * 5.4: * - split first write to two to avoid "empty DMA table?" in kernel log; * - setup_fds is introduced to assist ports to another platforms; * - set-root-uid assistant code directly at entry point (see main()); * - OpenBSD/NetBSD support added, it's worth noting that unlike 3.3 * port by Maarten Hofman, it's /dev/rcd?c which is expected to be * passed as argument, not /dev/cd?c. * 5.5: * - fix for ENOENT at unmount, I should have called myself with execlp, * not execl; * - security: chdir("/") in set-root-uid assistant; * - use /proc/mounts instead of MOUNTED (a.k.a. /etc/mtab) in Linux * umount code; * 5.6: * - unconditional exit in set-root-uid assistant, mostly for aesthetic * reasons; * - support for DVD-RW DAO recordings (whenever Pioneer-ish Quick * Format is not an option, DAO should fill in for it, as it's the * only recording strategy applicable after *minimal* blanking * procedure); * - support for SG_IO pass-through interface, or in other words * support for Linux 2>=5; * - 'growisofs -M /dev/cdrom=/dev/zero', this is basically a counter- * intuitive kludge assigned to fill up multi-session write-once * media for better compatibility with DVD-ROM/-Video units, to keep * it mountable [in the burner unit] volume descriptors from previous * session are copied to the new session; * - disable -dvd-compat with -M option and DVD+R, advice to fill up * the media as above instead; * - postpone Write Page setup all the way till after dry_run check; * - if recording to write-once media is terminated by external event, * leave the session opened, so that the recording can be resumed * (though no promises about final results are made, it's just that * leaving it open makes more sense than to close the session); * - ask unit to perform OPC if READ DISC INFORMATION doesn't return * any OPC descriptors; * - get rid of redundant Quick Grow in Restricted Overwrite; * - Solaris 2.x support is merged, it's volume manager aware, i.e. * you can run it with or without volume manager; * 5.7: * - Solaris USB workaround; * - 15 min timeout for FLUSH CACHE in DVD-RW DAO; * - revalidate recording speed; * - load media upon start-up (Linux used to auto-close tray upon open, * but not the others, which is why this functionality is added so * late); * 5.8: * - elder Ricoh firmwares seem to report events differently, which * triggered growisofs and dvd+rw-format to end-less loop at startup * [event handling was introduced in 5.6 for debugging purposes]; * - int ioctl_fd is transformed to void *ioctl_handle to facilitate * port to FreeBSD; * - FreeBSD support contributed by Matthew Dillon; * - volume descriptors from second session were discarded in * Restricted Overwrite since 5.6; * 5.9: * - some [SONY] firmwares make it impossible to tell apart minimally * and fully blanked media, so we need a way to engage DAO manually * [in DVD-RW]... let's treat multiple -dvd-compat options as "cry" * for DAO; * - refuse to finalize even DVD-R media with -M flag (advise to fill * it up with -M /dev/cdrom=/dev/zero too), apparently DVD-units * [or is it just SONY?] also "misplace" legacy lead-out in the same * manner as DVD+units; * - oops! DAO hung at >4MB buffer because of sign overflow; * - couple of human-readable error messages in poor_mans_pwrite64; * - work around Plextor firmware deficiency which [also] manifests as * end-less loop upon startup; * 5.10: * - increase timeout for OPC, NEC multi-format derivatives might * require more time to fulfill the OPC procedure; * - extended syntax for -use-the-force-luke option, it's now possible * to engage DVD-R[W] dummy mode by -use-the-force-luke=[tty,]dummy * for example, where "tty" substitutes for the original non-extended * option meaning, see the source for more easter eggs; * - FreeBSD: compile-time option to pass -M /dev/fd/0 to mkisofs to * make life easier for those who mount devfs, but not fdescfs; * - eliminate potential race conditions; * - avoid end-less loop if no media was in upon tray load; * - interpret value of MKISOFS environment variable as absolute path * to mkisofs binary; * - to facilitate for GUI front-ends return different exit codes, most * notably exit value of 128|errno denotes a fatal error upon program * startup [messages worth popping up in a separate modal dialog * perhaps?], errno - fatal error during recording and 1 - warnings * at exit; * - to facilitate for GUI front-ends auto-format blank DVD+RW media; * - Linux: fix for failure to copy volume descriptors when DVD-RW * Restricted Overwrite procedure is applied to patched kernel; * - FreeBSD: growisofs didn't close tray upon startup nor did the rest * of the tools work with open tray; * - bark at -o option and terminate execution, the "problem" was that * users seem to misspell -overburn once in a while, in which case it * was passed down to mkisofs and an iso-image was dumped to current * working directory instead of media; * - generalize -M /dev/cdrom=file.iso option, but if file.iso is not * /dev/zero, insist on sane -C argument to be passed prior -M and * double-verify the track starting address; * 5.11: * - authorship statement in -version output; * - make speed_factor floating point and print "Current Write Speed" * factor for informational purposes; * - Pioneer DVR-x06 exhibited degraded performance when recording DVD+; * - Pioneer DVR-x06 failed to complete DVD+ recording gracefully; * - alter set-root-uid behaviour under Linux from "PAM-junky" to more * conservative one; * 5.12: * - single Pioneer DVR-x06 user reported that very small fraction of * his recordings get terminted with "LONG WRITE IN PROGRESS," even * though growisofs explicitly reserves for this condition... It * turned out that at those rare occasions unit reported a lot of free * buffer space, which growisofs treated as error condition. It's not * clear if it's firmware deficiency, but growisofs reserves even for * this apparently rare condition now. * - [major] issue with MODE SENSE/SELECT on SANYO derivatives, such as * Optorite, is addressed; * - Linux can't open(2) a socket by /dev/fd/N, replace it with dup(2); * - more relaxed command line option parsing and simultaneously a * zealous check to make sure that no mkisofs options are passed * along with -[ZM] /dev/cdrom=image; * - report I/O error if input stream was less than 64K; * - -M /dev/cdrom=/dev/zero didn't relocate the lead-out in DVD-RW * Restricted Overwrite; * 5.13: * - workarounds for Panasonic/MATSUSHITA DVD-RAM LF-D310; * - Solaris: media load upon start-up; * 5.14: * - LG GSA-4040B failed to auto-format DVD+RW blanks; * - '| growisofs -Z /dev/cdrom=/dev/fd/0' failed with "already carries * isofs" even when running interactively, so I check on /dev/tty * instead of isatty(0); * - error output was confusing when overburn condition was raised in * dry-run mode; * - more sane default drain buffer size to minimize system load when * unit fails to return usable buffer utilization statistics under * "LONG WRITE IN PROGRESS" condition; * - progress indicator process was orphaned if -Z /dev/cdrom=file.iso * terminated prematurely; * - -overburn -Z /dev/cdrom=file.iso printed two "ignored" messages; * - Solaris: use large-file API in setup_fds; * - HP-UX: HP-UX support is contributed by HP; * - block signals in the beginning of recording, formally it shouldn't * be necessary, but is apparently needed for some units (is it?); * - prepare code for -speed even in DVD+ context, need a test-case... * - TEAC DV-W50D and Lite-On LDW-811S failed to set recording velocity, * deploy GET PERFORMANCE/SET STREAMING commands; * - Lite-On LDW-811S returns 0s in Write Speed descriptors in page 2A, * this would cause a problem if DVD+ speed control was implemented; * 5.15: * - confusing output when DAO mode is manually engaged and DVD-RW media * is minimally blanked; * - complement -use-the-force-luke=dao[:size] to arrange for piping * non-iso images in DAO mode (size is to be expressed in 2KB chunks); * - Pioneer DVR-x06 apparently needs larger timeout to avoid "the LUN * appears to be stuck" message in the beginning of DAO recording; * - HP-UX: fix-up umount code; * - HP-UX: make sure user doesn't pass /dev/rscsi/cXtYlZ, they should * stick to /dev/rdsk/cXtYdZ; * - implement -use-the-force-luke=seek:N -Z /dev/dvd=image to arrange * for 'builtin_dd if=image of=/dev/dvd obs=32k seek=N/16' (note that * N is expected to be expressed in 2KB chunks); * - skip overwrite check for blank media to avoid read errors at start, * which reportedly may cause bus reset in some configurations; * - make get_mmc_profile load media, explicit media load used to be on * per platform basis, while it doesn't have to; * - postpone handle_events till after dry-run checkpoint; * - error reporting revised; * - Optorite seems to insist on resuming suspended DVD+RW format, at * least it's apparently the only way to get *reliable* results * (formally this contradicts specification, which explicitly states * that format is to be resumed automatically and transparently); * - FreeBSD: FreeBSD 5-CURRENT since 2003-08-24, including 5.2 fails * to pull sense data automatically, at least for ATAPI transport, * so I reach for it myself (it's apparently a kernel bug, which * eventually will be fixed, but I keep the workaround code just in * case); * - -speed option in DVD+ context is enabled, upon release tested with * Plextor PX-708A; * - make builtin_dd print amount of transferred data, together with * -use-the-force-luke=seek:N it's easier to maintain "tar-formatted" * rewritable media; * 5.16: * - brown-bag bug in "LONG WRITE IN PROGRESS" code path; * 5.17: * - Linux: fix for /proc/sys/dev/cdrom/check_media set to 1; * - HP-UX: INQUIRY buffer is required to be 128 bytes. Well, "required" * is wrong word in this context, as it's apparently a kernel bug * addressed in PHKL_30038 (HPUX 11.11) and PHKL_30039 (HPUX 11.23). * This "change" affects all dvd+rw-tools, but I don't bump their * version numbers for this, as it's an "ugly" workaround for an * *external* problem; * - switch to GET PERFORMANCE even for current write speed (most * notably required for NEC and derivatives); * - the above change required adaptations for Pioneer and LG units, * which don't/fail to provide current write speed through GET * PERFORMANCE despite the fact that the command is mandatory; * - HP-UX: retain root privileges in setup_fds, SIOC_IO requires them; * - fix for COMMAND SEQUENCE ERROR in the beginning of DVD-recording; * - drop privileges prior mkisofs -version; * 5.18: * - refuse to run if ${SUDO_COMMAND} is set; * - minimize amount of compiler warnings on 64-bit platforms; * - skip count-down if no_tty_check is set; * - -use-the-force-luke=tracksize:size option by suggestion from K3b; * - Linux: fix for "Bad file descriptor" with DVD+RW kernel patch; * 5.19: * - IRIX: IRIX 6.x port is added; * - Solaris: get rid of media reload, which made it possible to improve * volume manager experience as well; * - address speed verification issues with NEC ND-2500 and Plextor * PX-708A; * - make DVD-RAM work in "poor-man" mode; * - average write speed report at the end of recording; * - LG GSA-4081B fails to "SET STREAMING" with "LBA OUT OF RANGE" for * DVD+RW media, but not e.g. DVD-R; * 5.20: * - DVD-RAM reload if recorded with -poor-man; * - -use-the-force-luke=wrvfy for WRITE AND VERIFY(10); * - "flushing cache" takes forever, from 5.19-1; * - HP-UX: inconsistency between /dev/rdsk and /dev/rscsi names; * - handle non-fatal OPC errors; * - DVD+R Double Layer support; * - -use-the-force-luke=4gms to allow ISO9660 directory structures * to cross 4GB boundary, the option is effective only with DVD+R DL * and for data to be accessible under Linux isofs a kernel patch is * required; * - more sane sanity check for -use-the-force-luke=tracksize:N; * - -use-the-force-luke=break:size to set Layer Break position for * Double Layer recordings; * - speed verification issue with 8x AccessTek derivatives addressed; * - -use-the-force-luke=noload to leave tray ejected at the end; * - allow to resume incomplete sessions recorded with -M option; * - Layer Break position sanity check with respect to dataset size; * - #if directive to get rid of sudo check at compile time with * 'make WARN=-DI_KNOW_ALL_ABOUT_SUDO'; * 5.21: * - Linux: fix for kernel version 2.6>=8, 2.6.8 itself is deficient, * but the problem can be worked around by installing this version * set-root-uid; * 6.0: * - fix for DVD+R recordings in Samsung TS-H542A; * - DVD-R Dual Layer DAO and Incremental support; * - versioning harmonization; * - multi-threaded(*) design; * *) apparently strace-ing NPTL processes can disrupt synchroni- * zation between threads and cause a deadlock condition. If * you ought to strace growisofs, make sure it's started with * LD_ASSUME_KERNEL environment variable set to 2.4. * - asynchronous ring buffer implementation; * - Win32/Mingw32 port [for platform-specific notes see * http://fy.chalmers.se/~appro/linux/DVD+RW/tools/win32/]; * - refine Pioneer strategies; * - WRITE procedure to recognize "IN PROCESS OF BECOMING READY" * [observed on newer Lite-On 1693S firmware and NEC]; * - allow for more intuitive interpretation of -speed factor with * units returning minimal velocity other than 1x; * - -use-the-force-luke=noopc to skip OPC altogether; * - implement 50% check for DVD-R DL; * - demote failure to change speed to warning; * 6.1: * - FreeBSD: improve backward binary compatibility; * - Linux: default rlimit for locked pages is way too small [note * that in order to allocate ring buffer larger than default 32MB * through command line option, you have to increase memorylocked * limit accordingly prior application start]; * - make -use-the-force-luke=noload, which leaves tray open, work; * - DEFAULT_BUF_SIZE_MB is a macro, which can be redefined at * compile time with 'make WARN=-DDEFAULT_BUF_SIZE_MB=[16|64]' to * change the default ring buffer size; * - ± localization; * - Linux: utilize O_EXCL flag [but do see commentary below!]; * - Treat only x73xx OPC errors as fatal; * - Fix typo in -speed scaling code; * - permit tracksize to be not divisible by 32KB in DAO mode; * 7.0: * - Blu-ray Disc support [upon release tested with Panasonic SW-5582]; * - Mac OS X 10>=2 support [upon release tested on 10.4 only]; * - Linux: copy definitions directly into application * code in order to secure backward compatibility; * - Linux: overcome 16MB O_DIRECT limitaton for NFS client; * - limit ring buffer size to 1/4 of RAM; * - copy volume descriptors if -C was specified with -M /dev/dvd=image * [by request from K3b]; * - -use-the-force-luke=spare[:none|min] to control blank BD pre-format * [for finer control use dvd+rw-format]; * - some units, e.g. Lite-on SOHW-1693S, seem to fire off OPC already * upon Layer Break command, therefore longer timeout is required; * - Linux: deploy BLKFLSBUF to avoid media reloads when possible; * - add unit buffer utilization indicator [by request from K3b]; * 7.1: * - Linux: allow compilation with non-substitution 2.6 kernel headers; * - Linux: not all 2.4 filesystem types support direct I/O; * - Linux: lock corresponding /dev/srN; * - use PTHREAD_STACK_MIN when creating threads; * - use _SC_PHYS_PAGES instead of _SC_AVPHYS_PAGES when calculating * ring buffer size limit; * - Mac OS X: allow compilation on 10.5; * - human-readable sense code transcription; * - Solaris: privileges, authorization and hald awareness; * - in order to minimize swap reservations mmap ring buffer with * MAP_SHARED; * - add -F/-free-space option displaying next session offset and * media capacity, which facilitates free space calculation [by * suggestion from Bacula project]; * - allow session to cross 4GB boundary even on single-layer media * [by request from Bacula project]; * - HP-UX: fix compilation warnings; * - refine x73xx error treatment; * - handle deferred errno from reader thread; * - return non-zero exit status more aggressively; */ #define PRINT_VERSION(cmd) do { \ char *s=strrchr((cmd),'/'); \ s ? s++ : (s=(cmd)); \ printf ("* %.*sgrowisofs by ,"\ " version 7.1,\n",(int)(s-(cmd)),(cmd)); \ } while (0) #define _LARGEFILE_SOURCE #define _LARGEFILE64_SOURCE #define _FILE_OFFSET_BITS 64 #if defined(__linux) /* ... and "engage" glibc large file support */ # ifndef _GNU_SOURCE # define _GNU_SOURCE # endif #elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || \ (defined(__APPLE__) && defined(__MACH__)) # define off64_t off_t # if !defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) || \ __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__<1050 # define stat64 stat # define fstat64 fstat # endif # define open64 open # define pread64 pread # define pwrite64 pwrite # define lseek64 lseek # ifndef __unix # define __unix # endif #elif defined(_WIN32) # undef _WIN32_WINNT # define _WIN32_WINNT 0x0500 # define off64_t __int64 # define stat64 _stati64 # define fstat64 _fstati64 # define lseek64 _lseeki64 # define ssize_t LONG_PTR # define perror _mask_perror #endif #include #include #include #include #include #include #include #include #include "mp.h" #if defined(__unix) || defined(__unix__) # include # include # include # include # include # include # include # include # include # define set_errno(e) (errno=(e)) # define FATAL_START(e) (0x80|(e)) # define FATAL_MASK 0x7F #ifdef __FreeBSD__ # include # ifndef SYS_mlockall # define SYS_mlockall 324 # endif #endif #ifdef O_DIRECT # ifdef __linux /* * Linux 2.4 ext3 doesn't support direct I/O:-? I mean it allows to open * file for direct I/O and then fails to perform actual I/O. It appears * to be trivial omission in kernel source, but "better-safe-than-sorry" * principle makes me excuse all 2.4 kernels from attempt to open input * file for direct I/O. */ static int open_directio (const char *name,int flag) { struct utsname uts; if (uname(&uts)<0) return -1; if (strncmp(uts.release,"2.6",3)<0) return -1; return open64(name,flag|O_DIRECT); } # define OPEN_DIRECTIO(n,f) open_directio((n),(f)) # else # define OPEN_DIRECTIO(n,f) open64((n),(f)|O_DIRECT) # endif #endif #elif defined(_WIN32) #include "win32err.h" # include # include # ifndef F_OK # define F_OK 0 # endif # ifndef S_ISREG # define S_ISREG(m) (((m)&_S_IFMT)==_S_IFREG) # endif static ssize_t pread64(int,void *,size_t,off64_t); static ssize_t pwrite64(int,const void *,size_t,off64_t); # define poll(a,b,t) Sleep(t) # define LLD "I64d" #endif #ifndef EMEDIUMTYPE #define EMEDIUMTYPE EINVAL #endif #ifndef ENOMEDIUM #define ENOMEDIUM ENODEV #endif #ifndef LLD #define LLD "lld" #endif #ifndef DEFAULT_BUF_SIZE_MB /* (!) default is 32MB, which is somewhat wasteful for 4x recording * speed and somewhat close to the "edge" for 16x:-) */ #define DEFAULT_BUF_SIZE_MB 32 #endif typedef ssize_t (*pwrite64_t)(int,const void *,size_t,off64_t); /* * Symbols from growisofs_mmc.cpp */ /* * These may terminate the program if error appears fatal. * The return value is therefore is always sane and suitable * for assignment. */ int get_mmc_profile (void *fd); int plusminus_r_C_parm (void *fd,char *C_parm); pwrite64_t poor_mans_setup (void *fd,off64_t leadout); char *plusminus_locale (); int __1x (); /* * These never terminate the program. * Pay attention to the return value. */ int media_reload (char *file,struct stat *ref, unsigned int cap2k); int fumount (int fd); off64_t get_capacity (void *fd); int poor_man_rewritable (void *fd,void *buf); float get_buffer_stats (void *fd); /* simplified */ struct iso_primary_descriptor { unsigned char type [1]; unsigned char id [5]; unsigned char void1 [80-5-1]; unsigned char volume_space_size [8]; unsigned char void2 [2048-80-8]; }; #define CD_BLOCK ((off64_t)2048) #define VOLDESC_OFF 16 #define DVD_BLOCK (32*1024) #define MAX_IVDs 16 static struct iso_primary_descriptor saved_descriptors[MAX_IVDs]; static pwrite64_t pwrite64_method = pwrite64; /* * Page-aligned ring buffer... */ static char *the_buffer; static unsigned int the_buffer_size=DEFAULT_BUF_SIZE_MB*1024*1024; /* * Synchronization objects */ static volatile unsigned int highest_ecc_block,reader_exit; static void *the_ring_semaphore; static volatile struct { time_t zero; off64_t current,final; } progress; /* * in_fd is passed to mkisofs, out_fd - to pwrite and ioctl_fd - to ioctl. */ static int in_fd=-1,out_fd=-1; #ifndef INVALID_HANDLE #define INVALID_HANDLE ((void *)-1) #endif static void *ioctl_handle=INVALID_HANDLE; #define ioctl_fd ((long)ioctl_handle) static int poor_man=-1, zero_image=0, quiet=1, overburn=0, no_tty_check=0, dry_run=0, dao_size=0, no_4gb_check=0, layer_break=0,next_session=-1; static float butlz=1.00001; /* unit buffer utilization */ static char *in_device=NULL,*out_device=NULL,*argv0; int dvd_compat=0, test_write=0, no_reload=0, mmc_profile=0, wrvfy=0, no_opc=0, spare=0; double speed_factor=0.0; char *ioctl_device; int _argc; char **_argv; #if defined(__linux) #include #include #include #include #include #include #ifndef _LINUX_WAIT_H #define _LINUX_WAIT_H /* linux headers are impaired */ #endif #include #if 0 #include #else /* As they threat to remove , copy definitions here */ #define RAW_SETBIND _IO(0xac,0) #define RAW_GETBIND _IO(0xac,1) struct raw_config_request { int raw_minor; __u64 block_major,block_minor; }; #endif #if 0 int dev_open(const char *pathname,int flags); int dev_open_patched(); int open64_wrapper(const char *pathname,int flags,...) { struct stat64 sb; if (stat64(pathname,&sb)==0 && S_ISBLK(sb.st_mode) && dev_open_patched()) { poor_man = 1; return dev_open(pathname,flags|O_LARGEFILE); } return open64(pathname,flags); } #endif char *find_raw_device(struct stat64 *sb) { int i,rawctl; dev_t dev_major,dev_minor; char *ret=NULL; struct raw_config_request req; static char rawdevname[24] = ""; if (!S_ISBLK(sb->st_mode)) return NULL; dev_major = major (sb->st_rdev); dev_minor = minor (sb->st_rdev); if ((rawctl=open ("/dev/rawctl",O_RDONLY)) < 0) return NULL; for (i=1;i<256;i++) { req.raw_minor = i; if (ioctl(rawctl,RAW_GETBIND,&req) < 0) break; if (req.block_major == dev_major && req.block_minor == dev_minor) { sprintf (rawdevname,"/dev/raw/raw%d",i); ret = rawdevname; break; } } close (rawctl); return ret; } int grab_sg (int blkfd) { struct { unsigned int dev_id,host_unique_id; } idlunblk,idlunsg; FILE *fp; int host_no,channel,lun,id,type,i,sgfd=-1; char str[128]; struct stat sb; if (ioctl (blkfd,SCSI_IOCTL_GET_IDLUN,&idlunblk) < 0) return -1; if ((fp=fopen ("/proc/scsi/sg/devices","r")) == NULL) return -1; for (i=0;i>=0;i++) { if (fgets (str,sizeof(str),fp) == NULL) break; if (sscanf (str,"%d\t%d\t%d\t%d\t%d", &host_no,&channel,&id,&lun,&type) != 5) continue; if (type != 5) continue; /* skip over non-CD-ROM devices */ if (idlunblk.dev_id == ((id & 0xff) + ((lun & 0xff) << 8) + ((channel & 0xff) << 16) + ((host_no & 0xff) << 24))) { sprintf (str,"/dev/sg%d",i); if (stat (str,&sb) < 0) break; errno = ENOENT; if (minor(sb.st_rdev) != i) break; sgfd = open (str,O_RDWR|O_NONBLOCK|O_EXCL); if (sgfd>=0) { if (ioctl (sgfd,SCSI_IOCTL_GET_IDLUN,&idlunsg) < 0 || idlunblk.dev_id != idlunsg.dev_id || idlunblk.host_unique_id != idlunsg.host_unique_id) close (sgfd), sgfd = -1, errno = ENOENT; } break; } } fclose (fp); return sgfd; } char *setup_fds (char *device) { char *odevice; uid_t uid=getuid(); struct stat64 sb,sc; int fd; /* * I ignore return values from set{re}uid calls because if * they fail we have no privileges to care about and should * just proceed anyway... */ #if 0 #define GET_REAL /* * Get real, but preserve saved uid. I count that user is * logged on console and therefore owns the 'device' [this * is a PAM's resposibility to arrange]. */ setreuid ((uid_t)-1,uid); #else /* * More "traditional" set-root-uid behaviour. I assume that if * administrator has installed growisofs set-root-uid, then * [s]he consciously wanted to grant access to /dev/scdN to * everybody, not just console user. */ #endif if ((in_fd = open64 (device,O_RDONLY)) < 0) if (!(errno == ENOMEDIUM || errno == EMEDIUMTYPE) || (in_fd = open64 (device,O_RDONLY|O_NONBLOCK)) < 0) fprintf (stderr,":-( unable to open64(\"%s\",O_RDONLY): ",device), perror (NULL), exit (FATAL_START(errno)); #ifdef GET_REAL setreuid ((uid_t)-1,uid); #endif if (fstat64(in_fd,&sb) < 0) fprintf (stderr,":-( unable to stat64(\"%s\"): ",device), perror (NULL), exit (FATAL_START(errno)); if (!S_ISBLK(sb.st_mode)) { setuid(uid); /* drop all privileges */ close (in_fd); /* reopen as mortal */ if ((in_fd = open64 (device,O_RDONLY)) < 0) fprintf (stderr,":-( unable to re-open64(\"%s\",O_RDONLY): ", device), perror (NULL), exit (FATAL_START(errno)); open_rw: if ((out_fd = open64 (device,O_RDWR)) < 0) fprintf (stderr,":-( unable to open64(\"%s\",O_RDWR): ",device), perror (NULL), exit (FATAL_START(errno)); if (fstat64(out_fd,&sc) < 0) fprintf (stderr,":-( unable to stat64(\"%s\"): ",device), perror (NULL), exit (FATAL_START(errno)); if (sb.st_dev!=sc.st_dev || sb.st_ino!=sc.st_ino) fprintf (stderr,":-( %s: race condition detected!\n",device), exit(FATAL_START(EPERM)); opened_rw: poor_man = 0; if (ioctl_handle!=INVALID_HANDLE) close (ioctl_fd), ioctl_handle = INVALID_HANDLE; setuid (uid); /* drop all privileges */ return device; } /* * O_RDWR is expected to provide for none set-root-uid * execution under Linux kernel 2.6[.8]. If it fails * [as under 2.6.8], CAP_SYS_RAWIO in combination * with set-root-uid should fill in for the kernel * deficiency... */ if ((fd = open64 (device,O_RDWR|O_NONBLOCK)) >= 0) { if (fstat64 (fd,&sc) < 0) fprintf (stderr,":-( unable to stat64(\"%s\"): ",device), perror (NULL), exit (FATAL_START(errno)); if (sc.st_rdev != sb.st_rdev) fprintf (stderr,":-( %s: race condition detected!\n",device), exit(FATAL_START(EPERM)); ioctl_handle=(void *)(long)fd; } else ioctl_handle=(void *)(long)dup(in_fd); /* * get_mmc_profile terminates the program if ioctl_handle is * not an MMC unit... */ mmc_profile = get_mmc_profile (ioctl_handle); /* Consume media_changed flag */ if (ioctl (ioctl_fd,CDROM_MEDIA_CHANGED,CDSL_CURRENT) < 0) { fprintf (stderr,":-( %s: CD_ROM_MEDIA_CHANGED ioctl failed: ", device), perror (NULL), exit (FATAL_START(errno)); } switch (mmc_profile&0xFFFF) { case 0x11: /* DVD-R */ case 0x13: /* DVD-RW Restricted Overwrite */ case 0x14: /* DVD-RW Sequential */ case 0x15: /* DVD-R Dual Layer Sequential */ case 0x16: /* DVD-R Dual Layer Jump */ case 0x1B: /* DVD+R */ case 0x2B: /* DVD+R Double Layer */ case 0x41: /* BD-R SRM */ case 0x42: /* BR-R RRM */ open_poor_man: poor_man = 1; out_fd = dup(ioctl_fd); /* recording reportedly can be disrupted even by "sg scan"... */ if (!dry_run && grab_sg (ioctl_fd)<0 && errno==EBUSY) fprintf (stderr,":-( %s: failed to grab associated sg device\n", device), exit (FATAL_START(errno)); /* I consciously leak descriptor to /dev/sg, it gets released * automatically upon program exit/termination */ #if defined(PR_SET_KEEPCAPS) if (getuid()!= 0 && prctl(PR_SET_KEEPCAPS,1) == 0) do { #if defined(_LINUX_CAPABILITY_VERSION) && defined(CAP_SYS_RAWIO) && defined(SYS_capset) struct __user_cap_header_struct h; struct __user_cap_data_struct d; h.version = _LINUX_CAPABILITY_VERSION; h.pid = 0; d.effective = 0; d.permitted = 1<0) goto open_poor_man; break; default: fprintf (stderr,":-( %s: media is not recognized as " "recordable DVD: %X\n",device,mmc_profile); exit (FATAL_START(EMEDIUMTYPE)); } /* * Attempt to locate /dev/raw/raw* */ #ifdef GET_REAL #undef GET_REAL setreuid((uid_t)-1,0); /* get root for a /dev/raw sake */ #endif if ((odevice=find_raw_device (&sb))) /* /dev/raw */ { if ((out_fd=open64 (odevice,O_RDWR)) < 0) { if (errno == EROFS) /* must be unpatched kernel */ goto open_poor_man; else fprintf (stderr,":-( unable to open64(\"%s\",O_RDWR): ", odevice), perror (NULL), exit (FATAL_START(errno)); } device=odevice; goto opened_rw; } if ((mmc_profile&0xFFFF) == 0x12) goto open_rw; /* DVD-RAM */ else goto open_poor_man; } #elif defined(__OpenBSD__) || defined(__NetBSD__) char *setup_fds (char *device) { uid_t uid=getuid(); struct stat sb,sc; /* * We might be entering as euid=root! */ if ((in_fd = open (device,O_RDONLY)) < 0) fprintf (stderr,":-( unable to open(\"%s\",O_RDONLY): ",device), perror (NULL), exit (FATAL_START(errno)); if (fstat (in_fd,&sb) < 0) fprintf (stderr,":-( unable to stat(\"%s\"): ",device), perror (NULL), exit (FATAL_START(errno)); if (!S_ISCHR(sb.st_mode)) { if (S_ISBLK(sb.st_mode) && !strncmp (device,"/dev/cd",7)) { fprintf (stderr,":-) you most likely want to use /dev/r%s instead!\n", device+5); if (isatty(0) && !dry_run) poll(NULL,0,5000); } setuid(uid); /* drop all privileges */ close (in_fd); /* reopen as mortal */ if ((in_fd = open (device,O_RDONLY)) < 0) fprintf (stderr,":-( unable to reopen(\"%s\",O_RDONLY): ",device), perror (NULL), exit (FATAL_START(errno)); open_rw: if ((out_fd = open (device,O_RDWR)) < 0) fprintf (stderr,":-( unable to open(\"%s\",O_RDWR): ",device), perror (NULL), exit (FATAL_START(errno)); opened_rw: poor_man = 0; close (ioctl_fd); ioctl_handle = INVALID_HANDLE; setuid (uid); /* drop all privileges */ return device; } /* * Still as euid=root! But note that get_mmc_profile makes sure it's * an MMC device, as it terminates the program if the unit doesn't * reply to GET CONFIGURATON. In combination with following switch * this means that if installed set-root-uid, growisofs grants * access to DVD burner(s), but not to any other device. */ ioctl_handle = (void *)(long)open (device,O_RDWR); /* O_RDWR is a must for SCIOCCOMMAND */ if (ioctl_handle == INVALID_HANDLE) { fprintf (stderr,":-( unable to open(\"%s\",O_RDWR): ",device), perror (NULL); if (errno == EBUSY) fprintf (stderr," is its block counterpart mounted?\n"); exit (FATAL_START(errno)); } if (fstat(ioctl_fd,&sc) < 0) fprintf (stderr,":-( unable to stat(\"%s\"): ",device), perror (NULL), exit (FATAL_START(errno)); if (sb.st_dev!=sc.st_dev || sb.st_ino!=sc.st_ino) fprintf (stderr,":-( %s: race condition detected!\n",device), exit(FATAL_START(EPERM)); mmc_profile = get_mmc_profile (ioctl_handle); switch (mmc_profile&0xFFFF) { case 0x11: /* DVD-R */ case 0x13: /* DVD-RW Restricted Overwrite */ case 0x14: /* DVD-RW Sequential */ case 0x15: /* DVD-R Dual Layer Sequential */ case 0x16: /* DVD-R Dual Layer Jump */ case 0x1A: /* DVD+RW */ case 0x1B: /* DVD+R */ case 0x2A: /* DVD+RW Double Layer */ case 0x2B: /* DVD+R Double Layer */ case 0x41: /* BD-R SRM */ case 0x42: /* BD-R RRM */ case 0x43: /* BD-RE */ open_poor_man: poor_man = 1; out_fd = dup(ioctl_fd); setuid (uid); /* drop all privileges */ return ioctl_device=device; case 0x12: /* DVD-RAM */ if (poor_man>0) goto open_poor_man; out_fd = dup(ioctl_fd); goto opened_rw; default: fprintf (stderr,":-( %s: media is not recognized as " "recordable DVD: %X\n",device,mmc_profile); exit (FATAL_START(EMEDIUMTYPE)); } /* not actually reached */ setuid(uid); goto open_rw; } #elif defined(__FreeBSD__) #include #include #include #undef ioctl_fd #define ioctl_fd (((struct cam_device *)ioctl_handle)->fd) #define PASS_STDIN_TO_MKISOFS char *setup_fds (char *device) { uid_t uid=getuid(); struct stat sb,sc; union ccb ccb; char pass[32]; /* periph_name is 16 chars long */ struct cam_device *cam; int once=1; /* * We might be entering as euid=root! */ if ((in_fd = open (device,O_RDONLY)) < 0) fprintf (stderr,":-( unable to open(\"%s\",O_RDONLY): ",device), perror (NULL), exit (FATAL_START(errno)); if (fstat (in_fd,&sb) < 0) fprintf (stderr,":-( unable to stat(\"%s\"): ",device), perror (NULL), exit (FATAL_START(errno)); if (!S_ISCHR(sb.st_mode)) { setuid(uid); /* drop all privileges */ close (in_fd); /* reopen as mortal */ if ((in_fd = open (device,O_RDONLY)) < 0) fprintf (stderr,":-( unable to reopen(\"%s\",O_RDONLY): ",device), perror (NULL), exit (FATAL_START(errno)); open_rw: if ((out_fd = open (device,O_RDWR)) < 0) fprintf (stderr,":-( unable to open(\"%s\",O_RDWR): ",device), perror (NULL), exit (FATAL_START(errno)); if (fstat(out_fd,&sc) < 0) fprintf (stderr,":-( unable to stat(\"%s\"): ",device), perror (NULL), exit (FATAL_START(errno)); if (sb.st_dev!=sc.st_dev || sb.st_ino!=sc.st_ino) fprintf (stderr,":-( %s: race condition detected!\n",device), exit(FATAL_START(EPERM)); opened_rw: poor_man = 0; if (ioctl_handle && ioctl_handle != INVALID_HANDLE) cam_close_device (ioctl_handle); ioctl_handle = INVALID_HANDLE; setuid (uid); /* drop all privileges */ return device; } /* * Still as euid=root! But note that get_mmc_profile makes sure it's * an MMC device, as it terminates the program if the unit doesn't * reply to GET CONFIGURATON. In combination with following switch * this means that if installed set-root-uid, growisofs grants * access to DVD burner(s), but not to any other device. */ for (once=1;1;once--) { memset (&ccb,0,sizeof(ccb)); ccb.ccb_h.func_code = XPT_GDEVLIST; if (ioctl (in_fd,CAMGETPASSTHRU,&ccb) < 0) { if (errno==ENXIO && once) { if (ioctl (in_fd,CDIOCCLOSE)==0) continue; } fprintf (stderr,":-( unable to CAMGETPASSTHRU for %s: ",device), perror (NULL), exit (FATAL_START(errno)); } break; } sprintf (pass,"/dev/%.15s%u",ccb.cgdl.periph_name,ccb.cgdl.unit_number); cam = cam_open_pass (pass,O_RDWR,NULL); if (cam == NULL) { fprintf (stderr,":-( unable to cam_open_pass(\"%s\",O_RDWR): ",pass), perror (NULL); exit (FATAL_START(errno)); } ioctl_handle = (void *)cam; mmc_profile = get_mmc_profile (ioctl_handle); switch (mmc_profile&0xFFFF) { case 0x11: /* DVD-R */ case 0x13: /* DVD-RW Restricted Overwrite */ case 0x14: /* DVD-RW Sequential */ case 0x15: /* DVD-R Dual Layer Sequential */ case 0x16: /* DVD-R Dual Layer Jump */ case 0x1A: /* DVD+RW */ case 0x1B: /* DVD+R */ case 0x2A: /* DVD+RW Double Layer */ case 0x2B: /* DVD+R Double Layer */ case 0x41: /* BD-R SRM */ case 0x42: /* BD-R RRM */ case 0x43: /* BD-RE */ open_poor_man: poor_man = 1; out_fd = dup(in_fd); /* it's ignored in poor_man ... */ setuid (uid); /* drop all privileges */ return ioctl_device=cam->device_path; case 0x12: /* DVD-RAM */ if (poor_man>0) goto open_poor_man; break; default: fprintf (stderr,":-( %s: media is not recognized as " "recordable DVD: %X\n",device,mmc_profile); exit (FATAL_START(EMEDIUMTYPE)); } setuid(uid); goto open_rw; } #elif defined(__sun) || defined(sun) #include #include #include #include #include #include #if defined(__SunOS) && __SunOS >= 510 #include /* run-time linking allows for backward binary compatibility, i.e. * binary compiled on 5>=10 can still be executed on 5<10. */ #if defined(__GNUC__) && __GNUC__>=2 static void rtld(void) __attribute__((constructor)); #elif defined(__SUNPRO_C) #pragma init(rtld) #endif static int not_impl_i() { errno = ENOTSUP; return -1; } static void *not_impl_p() { errno = ENOTSUP; return NULL; } static union { void *p[8]; struct { uint_t (*getpflags)(uint_t); int (*setpglags)(uint_t,uint_t); int (*getppriv)(priv_ptype_t,priv_set_t*); int (*setppriv)(priv_op_t,priv_ptype_t,priv_set_t*); char *(*priv_set_to_str)(const priv_set_t*,char,int); priv_set_t *(*priv_str_to_set)(const char*,const char*,const char**); priv_set_t *(*priv_allocset)(void); void (*priv_freeset)(priv_set_t*); } f; } table = { { (void *)not_impl_i, (void *)not_impl_i, (void *)not_impl_i, (void *)not_impl_i, (void *)not_impl_p, (void *)not_impl_p, (void *)not_impl_p, (void *)not_impl_p } }; #define getpflags (*table.f.getpflags) #define setpglags (*table.f.setpglags) #define getppriv (*table.f.getppriv) #define setppriv (*table.f.setppriv) #define priv_set_to_str (*table.f.priv_set_to_str) #define priv_str_to_set (*table.f.priv_str_to_set) #define priv_allocset (*table.f.priv_allocset) #define priv_freeset (*table.f.priv_freeset) static priv_set_t *basic_priv,*permitted_priv; static void rtld(void) { void *g = dlopen(NULL,RTLD_LAZY),*p; if (g) { table.p[0] = (p=dlsym(g,"getpflags")) ? p : (void*)not_impl_i; table.p[1] = (p=dlsym(g,"setpflags")) ? p : (void*)not_impl_i; table.p[2] = (p=dlsym(g,"getppriv")) ? p : (void*)not_impl_i; table.p[3] = (p=dlsym(g,"setppriv")) ? p : (void*)not_impl_i; table.p[4] = (p=dlsym(g,"priv_set_to_str")) ? p : (void*)not_impl_p; table.p[5] = (p=dlsym(g,"priv_str_to_set")) ? p : (void*)not_impl_p; table.p[6] = (p=dlsym(g,"priv_allocset")) ? p : (void*)not_impl_p; table.p[7] = (p=dlsym(g,"priv_freeset")) ? p : (void*)not_impl_p; dlclose(g); basic_priv = priv_str_to_set("basic","",NULL); } } #endif /* execlp is used to re-invoke itself, so pass on privileges... */ int execlp (const char *path,const char *arg0,...) { va_list ap; int argc=0; char **argv,**p; va_start(ap,arg0); while (va_arg(ap,char*)) argc++; va_end(ap); argv = p = alloca((argc+2)*sizeof(char *)); *p++ = (char *)arg0; va_start(ap,arg0); while (argc--) *p++ = va_arg(ap,char*); va_end(ap); *p = NULL; #if defined(setppriv) if (permitted_priv && getuid()!=0) setppriv (PRIV_SET,PRIV_INHERITABLE,permitted_priv); #endif return execvp(path,argv); } char *setup_fds (char *device) { uid_t uid=getuid(); struct stat64 sb,sc; int v; if ((v=volmgt_running())) { char *file=NULL,*volname; /* * I leak some memory here, but I don't care... */ if ((volname=volmgt_symname (device))) file=media_findname (volname); else file=media_findname (device); if (file) device=file; else v=0; } /* * We might be entering as euid=root! */ #ifdef setppriv if (permitted_priv && uid!=0) setppriv(PRIV_SET,PRIV_EFFECTIVE,permitted_priv); #endif if ((in_fd = open64 (device,O_RDONLY)) < 0) if (errno != ENXIO || (in_fd = open64 (device,O_RDONLY|O_NONBLOCK)) < 0) fprintf (stderr,":-( unable to open(\"%s\",O_RDONLY): ",device), perror (NULL), exit (FATAL_START(errno)); if (fstat64 (in_fd,&sb) < 0) fprintf (stderr,":-( unable to stat(\"%s\"): ",device), perror (NULL), exit (FATAL_START(errno)); if (!S_ISCHR(sb.st_mode)) { char *s; if (S_ISBLK(sb.st_mode) && (s=strstr (device,"/dsk/"))) { fprintf (stderr,":-) you most likely want to use %.*s/r%s instead!\n", (int)(s-device),device,s+1); if (isatty(0) && !dry_run) poll(NULL,0,5000); } #ifdef setppriv /* if we were rewarded with extra privileges, drop them now */ if (basic_priv && uid!=0) setppriv(PRIV_SET,PRIV_PERMITTED,basic_priv); #endif setuid(uid); /* drop all privileges */ close (in_fd); /* reopen as mortal */ if ((in_fd = open64 (device,O_RDONLY)) < 0) fprintf (stderr,":-( unable to reopen(\"%s\",O_RDONLY): ",device), perror (NULL), exit (FATAL_START(errno)); open_rw: if ((out_fd = open64 (device,O_RDWR)) < 0) fprintf (stderr,":-( unable to open(\"%s\",O_RDWR): ",device), perror (NULL), exit (FATAL_START(errno)); if (fstat64(out_fd,&sc) < 0) fprintf (stderr,":-( unable to stat(\"%s\"): ",device), perror (NULL), exit (FATAL_START(errno)); if (sb.st_dev!=sc.st_dev || sb.st_ino!=sc.st_ino) fprintf (stderr,":-( %s: race condition detected!\n",device), exit(FATAL_START(EPERM)); poor_man = 0; close (ioctl_fd); ioctl_handle = INVALID_HANDLE; setuid (uid); /* drop all privileges */ return device; } /* * Still as euid=root! But note that get_mmc_profile makes sure it's * an MMC device, as it terminates the program if the unit doesn't * reply to GET CONFIGURATON. In combination with following switch * this means that if installed set-root-uid, growisofs grants * access to DVD burner(s), but not to any other device. */ /* * But before we proceed check for solaris.device.cdrw authorization. * In default installation it's granted to all users. See * 'man chkauthattr' for further details... */ if (uid!=0) { void *secdb = dlopen("libsecdb.so.1",RTLD_LAZY); union { void *p; int (*f)(const char *,const char *); } chkauthattr; if (secdb && (chkauthattr.p=dlsym(secdb,"chkauthattr"))) { struct passwd *pwd = getpwuid(uid); if (pwd==NULL || !chkauthattr.f("solaris.device.cdrw",pwd->pw_name)) fprintf(stderr,":-( solaris.device.cdrw is not granted to %s\n", pwd?pwd->pw_name:"(unknown)"), exit (FATAL_START(EACCES)); } if (secdb) dlclose (secdb); } ioctl_handle = (void *)(long)dup (in_fd); #if 0 if (ioctl(ioctl_handle,CDROMSTART)<0 && errno==ENXIO) media_load(ioctl_handle); #endif mmc_profile = get_mmc_profile (ioctl_handle); switch (mmc_profile&0xFFFF) { case 0x11: /* DVD-R */ case 0x12: /* DVD-RAM */ case 0x13: /* DVD-RW Restricted Overwrite */ case 0x14: /* DVD-RW Sequential */ case 0x15: /* DVD-R Dual Layer Sequential */ case 0x16: /* DVD-R Dual Layer Jump */ case 0x1A: /* DVD+RW */ case 0x1B: /* DVD+R */ case 0x2A: /* DVD+RW Double Layer */ case 0x2B: /* DVD+R Double Layer */ case 0x41: /* BD-R SRM */ case 0x42: /* BD-R RRM */ case 0x43: /* BD-RE */ poor_man = 1; out_fd = dup(ioctl_fd); #if 0 /* 'man uscsi' maintains that root privileges are required upon * issue of USCSI ioctl, we therefore can't drop them... */ setuid (uid); /* drop all privileges */ #endif return ioctl_device=device; default: fprintf (stderr,":-( %s: media is not recognized as " "recordable DVD: %X\n",device,mmc_profile); exit (FATAL_START(EMEDIUMTYPE)); } /* not reached */ setuid(uid); goto open_rw; } #elif defined(__hpux) #ifdef SCTL_MAJOR # define SCTL_SANITY_CHECK SCTL_MAJOR-1 # if SCTL_SANITY_CHECK<=0 # undef SCTL_MAJOR # endif # undef SCTL_SANITY_CHECK #endif #ifndef SCTL_MAJOR #error "SCTL_MAJOR is undefined or not sane." #endif #ifndef minor #include #endif #include #define seteuid(x) setreuid((uid_t)-1,x) #if 1 #define CANNOT_PASS_DEV_FD_N_TO_MKISOFS #elif 0 --- ./multi.c.orig Wed Dec 25 15:15:24 2002 +++ ./multi.c Tue Nov 11 17:12:27 2003 @@ -1067,3 +1067,13 @@ open_merge_image(path) char *path; { + int fd; + + if (sscanf (path,"/dev/fd/%u",&fd) == 1) { + int fdd = dup(fd); /* validate file descriptor */ + if (fdd < 0) return -1; + close (fdd); + in_image = fdopen (fd,"rb"); + return in_image ? 0 : -1; + } + #endif #ifdef CANNOT_PASS_DEV_FD_N_TO_MKISOFS char *get_M_parm (int fd, char *device) { struct stat sb; dev_t m; char *ret=device; static char ctl[16]; if (fstat (fd,&sb)==0 && S_ISCHR(sb.st_mode)) { m = minor (sb.st_rdev); sprintf (ctl,"%d,%d,%d",(m>>16)&0xFF,(m>>12)&0xF,(m>>8)&0xF); ret = ctl; } return ret; } #endif char *setup_fds (char *device) { uid_t uid=getuid(); struct stat64 sb,sc; dev_t m; static char rscsi [32]; disk_describe_type ddt; /* * We might be entering as euid=root! */ if ((in_fd = open64 (device,O_RDONLY)) < 0) fprintf (stderr,":-( unable to open(\"%s\",O_RDONLY): ",device), perror (NULL), exit (FATAL_START(errno)); if (fstat64 (in_fd,&sb) < 0) fprintf (stderr,":-( unable to stat(\"%s\"): ",device), perror (NULL), exit (FATAL_START(errno)); if (!S_ISCHR(sb.st_mode)) { char *s; if (S_ISBLK(sb.st_mode) && (s=strstr (device,"/dsk/"))) { fprintf (stderr,":-) you most likely want to use %.*s/r%s instead!\n", (int)(s-device),device,s+1); if (isatty(0) && !dry_run) poll(NULL,0,5000); } setuid(uid); /* drop all privileges */ close (in_fd); /* reopen as mortal */ if ((in_fd = open64 (device,O_RDONLY)) < 0) fprintf (stderr,":-( unable to reopen(\"%s\",O_RDONLY): ",device), perror (NULL), exit (FATAL_START(errno)); open_rw: if ((out_fd = open64 (device,O_RDWR)) < 0) fprintf (stderr,":-( unable to open(\"%s\",O_RDWR): ",device), perror (NULL), exit (FATAL_START(errno)); if (fstat64(out_fd,&sc) < 0) fprintf (stderr,":-( unable to stat(\"%s\"): ",device), perror (NULL), exit (FATAL_START(errno)); if (sb.st_dev!=sc.st_dev || sb.st_ino!=sc.st_ino) fprintf (stderr,":-( %s: race condition detected!\n",device), exit(FATAL_START(EPERM)); poor_man = 0; close (ioctl_fd); ioctl_handle = INVALID_HANDLE; setuid (uid); /* drop all privileges */ return device; } /* * Still as euid=root! But note that get_mmc_profile makes sure it's * an MMC device, as it terminates the program if the unit doesn't * reply to GET CONFIGURATON. In combination with following switch * this means that if installed set-root-uid, growisofs grants * access to DVD burner(s), but not to any other device. */ m=minor(sb.st_rdev); /* Make sure user isn't using /dev/rscsi/cXtYlZ... */ #if 1 if (ioctl (in_fd,DIOC_DESCRIBE,&ddt) != 0) #else if (major(sb.st_rdev) == SCTL_MAJOR) #endif fprintf (stderr,":-( stick to /dev/rdsk/c%ut%u%c%x!\n", (m>>16)&0xFF,(m>>12)&0xF,'d',(m>>8)&0xF), exit(FATAL_START(EINVAL)); /* * Even though /dev/rdsk/cXtYdZ accepts SIOC_IO as well, we have to * use /dev/rscsi/cXtYlZ for pass-through access in order to avoid * command replay by upper "class" driver... */ sprintf (rscsi,"/dev/rscsi/c%ut%u%c%x", (m>>16)&0xFF,(m>>12)&0xF,'l',(m>>8)&0xF); ioctl_handle = (void *)(long)open64 (rscsi,O_RDONLY); if (ioctl_handle == INVALID_HANDLE) { fprintf (stderr,":-( unable to open(\"%s\",O_RDONLY): ",rscsi), perror (NULL); if (errno == ENOENT) fprintf (stderr,":-! consider " "'mknod %s c %d 0x%06x; chmod 0600 %s'\n", rscsi,SCTL_MAJOR,(m&0xFFFF00)|2,rscsi); exit (FATAL_START(errno)); } if (fstat64 (ioctl_fd,&sc) < 0) fprintf (stderr,":-( unable to stat(\"%s\"): ",rscsi), perror (NULL), exit (FATAL_START(errno)); /* Make sure we land on same SCSI ID... */ if ((m&0xFFFF00) != (minor(sc.st_rdev)&0xFFFF00)) fprintf (stderr,":-( SCSI ID mismatch: %06x!=%06x\n", m,minor(sc.st_rdev)), exit(FATAL_START(EPERM)); mmc_profile = get_mmc_profile (ioctl_handle); switch (mmc_profile&0xFFFF) { case 0x11: /* DVD-R */ case 0x12: /* DVD-RAM */ case 0x13: /* DVD-RW Restricted Overwrite */ case 0x14: /* DVD-RW Sequential */ case 0x15: /* DVD-R Dual Layer Sequential */ case 0x16: /* DVD-R Dual Layer Jump */ case 0x1A: /* DVD+RW */ case 0x1B: /* DVD+R */ case 0x2A: /* DVD+RW Double Layer */ case 0x2B: /* DVD+R Double Layer */ case 0x41: /* BD-R SRM */ case 0x42: /* BD-R RRM */ case 0x43: /* BD-RE */ poor_man = 1; out_fd = dup(ioctl_fd); #if 0 /* HP-UX requires root privileges upon SIOC_IO ioctl */ setuid (uid); /* drop all privileges */ #endif return ioctl_device=rscsi; default: fprintf (stderr,":-( %s: media is not recognized as " "recordable DVD: %X\n",device,mmc_profile); exit (FATAL_START(EMEDIUMTYPE)); } setuid(uid); goto open_rw; } /* * PA-RISC HP-UX doesn't have /dev/zero:-( */ static fd_set _dev_zero; static int open64_zero(const char *pathname, int flags) { int fd; if (strcmp(pathname,"/dev/zero")) return open64 (pathname,flags); else if ((fd=open ("/dev/null",flags)) >= 0) FD_SET (fd,&_dev_zero); return fd; } static ssize_t read_zero (int fd, void *buf, size_t count) { if (!FD_ISSET(fd,&_dev_zero)) return read (fd,buf,count); memset (buf,0,count); return count; } static int close_zero (int fd) { int ret=close (fd); if (ret>=0 && FD_ISSET(fd,&_dev_zero)) FD_CLR (fd,&_dev_zero); return ret; } static int dup2_zero (int oldfd, int newfd) { int ret; ret = dup2 (oldfd,newfd); if (ret >= 0) { FD_CLR (newfd,&_dev_zero); if (FD_ISSET(oldfd,&_dev_zero)) FD_SET (ret,&_dev_zero); else FD_CLR (ret,&_dev_zero); } return ret; } #define open64 open64_zero #define read read_zero #define close close_zero #define dup2 dup2_zero #elif defined(__sgi) #include #include #include char *setup_fds (char *device) { uid_t uid=getuid(); struct stat64 sb,sc; char hw_path[MAXPATHLEN],*s; int hw_len=sizeof(hw_path)-1; int bus=0,tgt=4,lun=0; /* default config for O2 */ static char rscsi[64]; /* * We might be entering as euid=root! */ if ((in_fd = open64 (device,O_RDONLY)) < 0) { if (errno==EIO) goto tray_might_be_open; fprintf (stderr,":-( unable to open(\"%s\",O_RDONLY): ",device), perror (NULL), exit (FATAL_START(errno)); } if (fstat64 (in_fd,&sb) < 0) fprintf (stderr,":-( unable to stat(\"%s\"): ",device), perror (NULL), exit (FATAL_START(errno)); if (!S_ISCHR(sb.st_mode)) { if (S_ISBLK(sb.st_mode) && !strncmp (device,"/dev/dsk",7)) { fprintf (stderr,":-) you most likely want to use " "/dev/r%s instead!\n",device+5); if (isatty(0) && !dry_run) poll(NULL,0,5000); } open_rw: setuid(uid); /* drop all privileges */ close (in_fd); /* reopen as mortal */ if ((in_fd = open64 (device,O_RDONLY)) < 0) fprintf (stderr,":-( unable to reopen(\"%s\",O_RDONLY): ",device), perror (NULL), exit (FATAL_START(errno)); if ((out_fd = open64 (device,O_RDWR)) < 0) fprintf (stderr,":-( unable to open(\"%s\",O_RDWR): ",device), perror (NULL), exit (FATAL_START(errno)); opened_rw: poor_man = 0; close (ioctl_fd); ioctl_handle = INVALID_HANDLE; setuid (uid); /* drop all privileges */ return device; } /* * Still as euid=root! But note that get_mmc_profile makes sure it's * an MMC device, as it terminates the program if the unit doesn't * reply to GET CONFIGURATON. In combination with following switch * this means that if installed set-root-uid, growisofs grants * access to DVD burner(s), but not to any other device. */ tray_might_be_open: if ((in_fd<0) ? attr_get (device,"_devname",hw_path,&hw_len,0) : attr_getf (in_fd,"_devname",hw_path,&hw_len,0) ) fprintf (stderr,":-( unable to obtain hw_path for \"%s\": ",device), perror (NULL), exit (FATAL_START(errno)); if (hw_len>=sizeof(hw_path)) hw_len=sizeof(hw_path)-1; /* paranoia */ hw_path[hw_len]='\0'; if ((s=strstr(hw_path,"/scsi_ctlr/"))) sscanf (s,"/scsi_ctlr/%d/target/%d/lun/%d/",&bus,&tgt,&lun); /* Make sure user is using /dev/rdsk/dksXdYlZvol... */ if ((s=strstr(hw_path,"/disk/volume/char"))==NULL) { if (lun) fprintf (stderr,":-( stick to /dev/rdsk/dks%dd%dl%dvol!\n", bus,tgt,lun); else fprintf (stderr,":-( stick to /dev/rdsk/dks%dd%dvol!\n", bus,tgt); exit(FATAL_START(EINVAL)); } memcpy (s,"/scsi",6); ioctl_handle = (void *)(long)open64 (hw_path,O_RDWR); if (ioctl_handle == INVALID_HANDLE) fprintf (stderr,":-( unable to open(\"%s\",O_RDWR): ",hw_path), perror (NULL), exit (FATAL_START(errno)); if (fstat64 (ioctl_fd,&sc) < 0) fprintf (stderr,":-( unable to stat(\"%s\"): ",hw_path), perror (NULL), exit (FATAL_START(errno)); memcpy (s,"/disk/volume/char",6); mmc_profile = get_mmc_profile (ioctl_handle); switch (mmc_profile&0xFFFF) { case 0x11: /* DVD-R */ case 0x13: /* DVD-RW Restricted Overwrite */ case 0x14: /* DVD-RW Sequential */ case 0x15: /* DVD-R Dual Layer Sequential */ case 0x16: /* DVD-R Dual Layer Jump */ case 0x1A: /* DVD+RW */ case 0x1B: /* DVD+R */ case 0x2A: /* DVD+RW Double Layer */ case 0x2B: /* DVD+R Double Layer */ case 0x41: /* BD-R SRM */ case 0x42: /* BD-R RRM */ case 0x43: /* BD-RE */ open_poor_man: poor_man = 1; out_fd = dup (ioctl_fd); if (in_fd<0 && (in_fd=open64 (hw_path,O_RDONLY))<0) /* hope for the best? */ ; setuid (uid); /* drop all privileges */ mediad_get_exclusiveuse (hw_path,"growisofs"); if (mediad_last_error ()==RMED_NOERROR) putenv ("MEDIAD_GOT_EXCLUSIVEUSE="); /* kludge... */ sprintf (rscsi,"/dev/scsi/sc%dd%dl%d",bus,tgt,lun); return ioctl_device=rscsi; /* might be bogus... */ case 0x12: /* DVD-RAM */ /* Some of latest tentative IRIX releases seem to implement * DVD-RAM writing at dksc level, but I'm not sure which one. * So I just fall down to poor-man, at least for now... */ goto open_poor_man; break; default: fprintf (stderr,":-( %s: media is not recognized as " "recordable DVD: %X\n",device,mmc_profile); exit (FATAL_START(EMEDIUMTYPE)); } /* not actually reached */ setuid(uid); goto open_rw; } #elif defined(_WIN32) #define CANNOT_PASS_DEV_FD_N_TO_MKISOFS #define PASS_STDIN_TO_MKISOFS static ssize_t pread64 (int fd,void *p,size_t sz,off64_t off) { HANDLE h = (HANDLE)_get_osfhandle(fd); OVERLAPPED ov; DWORD br=0; /* bytes read */ if (h==INVALID_HANDLE_VALUE) return -1; memset (&ov,0,sizeof(ov)); ov.Offset = (DWORD)(off&0xFFFFFFFF); ov.OffsetHigh = (DWORD)(off>>32); return ReadFile (h,p,sz,&br,&ov) ? br : -1; } static ssize_t pwrite64 (int fd,const void *p,size_t sz,off64_t off) { HANDLE h = (HANDLE)_get_osfhandle(fd); OVERLAPPED ov; DWORD bw=0; /* bytes written */ if (h==INVALID_HANDLE_VALUE) return -1; memset (&ov,0,sizeof(ov)); ov.Offset = (DWORD)(off&0xFFFFFFFF); ov.OffsetHigh = (DWORD)(off>>32); return WriteFile (h,p,sz,&bw,&ov) ? bw : -1; } static struct { unsigned long fds_bits[512/sizeof(unsigned long)]; } _dev_zero; #define NFDBITS (sizeof(unsigned long)*8) #define _FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1UL << ((n) % NFDBITS))) #define _FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1UL << ((n) % NFDBITS))) #define _FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1UL << ((n) % NFDBITS))) /* * This subroutine is trimmed for particular growisofs needs! */ static int open64_zero(const char *pathname, int flags) { HANDLE h; int fd,zero=0; DWORD access=GENERIC_READ; if (!strcmp (pathname,"/dev/null")) pathname="NUL:", zero=0; else if (!strcmp (pathname,"/dev/zero")) pathname="NUL:", zero=1; if (flags & O_RDONLY) access=GENERIC_READ; else if (flags & O_WRONLY) access=GENERIC_WRITE; else if (flags & O_RDWR) access=GENERIC_READ|GENERIC_WRITE; h = CreateFile(pathname,access, /* always share for write as I commonly open same * file twice: with O_RDONLY and with O_WRONLY... */ FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, /* don't inherit */ OPEN_EXISTING, /* and never create any files */ FILE_FLAG_NO_BUFFERING| /* bypass file system cache */ (flags&O_WRONLY)?FILE_FLAG_WRITE_THROUGH:0, NULL); if (h == INVALID_HANDLE_VALUE) return -1; flags &= ~_O_TEXT; flags |= _O_BINARY|_O_NOINHERIT; fd = _open_osfhandle((size_t)h,flags); if (fd>=0 && zero) _FD_SET (fd,&_dev_zero); return fd; } static ssize_t read_zero (int fd, void *buf, size_t count) { if (!_FD_ISSET(fd,&_dev_zero)) return _read (fd,buf,count); memset (buf,0,count); return count; } static int close_zero (int fd) { int ret=_close (fd); if (ret>=0 && _FD_ISSET(fd,&_dev_zero)) FD_CLR (fd,&_dev_zero); return ret; } static int dup2_zero (int oldfd, int newfd) { int ret; ret = _dup2 (oldfd,newfd); if (ret >= 0) { _FD_CLR (newfd,&_dev_zero); if (_FD_ISSET(oldfd,&_dev_zero))_FD_SET (ret,&_dev_zero); else _FD_CLR (ret,&_dev_zero); } return ret; } #define open64 open64_zero #define read read_zero #define close close_zero #define dup2 dup2_zero char *setup_fds (char *device) { char dev[32]; HANDLE h; if (device[1] != ':' || device[2] != '\0') { if ((in_fd = open64 (device,O_RDONLY)) < 0) fprintf (stderr,":-( unable to open(\"%s\",O_RDONLY): ",device), perror (NULL), exit (FATAL_START(errno)); if ((out_fd = open64 (device,O_RDWR)) < 0) fprintf (stderr,":-( unable to open(\"%s\",O_RDWR): ",device), perror (NULL), exit (FATAL_START(errno)); poor_man = 0; ioctl_handle = INVALID_HANDLE; return device; } sprintf(dev,"%.*s\\",sizeof(dev)-2,device); if (GetDriveType(dev)!=DRIVE_CDROM) fprintf (stderr,":-( %s is not a CDROM device\n",dev), exit (FATAL_START(EINVAL)); sprintf(dev,"\\\\.\\%.*s",sizeof(dev)-5,device); h = CreateFile (dev,GENERIC_WRITE|GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,OPEN_EXISTING,0,NULL); if (h==INVALID_HANDLE_VALUE) fprintf (stderr,":-( unable to open %s: ",dev), perror (NULL), exit(FATAL_START(errno)); ioctl_handle = h; out_fd = _open_osfhandle((size_t)h,O_RDWR); /* * Unfortunately we're forced to pass read-write handle down to * mkisofs, because we are going to FSCTL_LOCK_VOLUME out_fd and * then we can't have other handles open. */ in_fd = out_fd; mmc_profile = get_mmc_profile (ioctl_handle); switch (mmc_profile&0xFFFF) { case 0x11: /* DVD-R */ case 0x12: /* DVD-RAM */ case 0x13: /* DVD-RW Restricted Overwrite */ case 0x14: /* DVD-RW Sequential */ case 0x15: /* DVD-R Dual Layer Sequential */ case 0x16: /* DVD-R Dual Layer Jump */ case 0x1A: /* DVD+RW */ case 0x1B: /* DVD+R */ case 0x2A: /* DVD+RW Double Layer */ case 0x2B: /* DVD+R Double Layer */ case 0x41: /* BD-R SRM */ case 0x42: /* BD-R RRM */ case 0x43: /* BD-RE */ poor_man = 1; return ioctl_device=strdup(dev); default: fprintf (stderr,":-( %s: media is not recognized as " "recordable DVD: %X\n",device,mmc_profile); exit (FATAL_START(EMEDIUMTYPE)); } /* not reached */ } #elif defined(__APPLE__) && defined(__MACH__) #if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) #define MAP_ANONYMOUS MAP_ANON #endif #define PASS_STDIN_TO_MKISOFS #define CANNOT_PASS_DEV_FD_N_TO_MKISOFS #if !defined(CANNOT_PASS_DEV_FD_N_TO_MKISOFS) #error "CANNOT_PASS_DEV_FD_N_TO_MKISOFS has to be defined" /* * Even though /dev/fd/N is present on Mac OS X we can't pass it * to mkisofs for following reason. The trouble is that in order * to ObtainExclusiveAccess, which is required for raw SCSI, no * /dev/[r]diskN descriptors may be open by that time. Now, if * I pass /dev/fd/N, mkisofs would reopen it and close only this * duplicate descriptor leaving original N open. Therefore I * pass -M - to allow mkisofs to simply take stdin and close it * when it's done with previous session directory scructure. * Needless to mention that mkisofs has to be patched to accept * dash as -M argument: --- mkisofs/multi.c.orig 2004-05-15 18:59:40.000000000 +0200 +++ mkisofs/multi.c 2006-09-11 23:50:23.000000000 +0200 @@ -1137,6 +1137,14 @@ open_merge_image(path) char *path; { + if (path[0]=='-' && path[1]=='\0') { +#ifdef NEED_O_BINARY + setmode(fileno(stdin),O_BINARY); +#endif + in_image = stdin; + return (0); + } + #ifndef USE_SCG in_image = fopen(path, "rb"); if (in_image == NULL) { --- */ #endif #include #include #include #if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && \ __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__>=1050 #define stat64 stat #define fstat64 fstat #endif static io_object_t scsiob=IO_OBJECT_NULL; static IOCFPlugInInterface **plugin=NULL; static MMCDeviceInterface **mmcdif=NULL; static SCSITaskDeviceInterface **taskif=NULL; char *setup_fds (char *device) { uid_t uid=getuid(); struct stat sb,sc; /* * We might be entering as euid=root! */ if ((in_fd = open (device,O_RDONLY)) < 0) fprintf (stderr,":-( unable to open(\"%s\",O_RDONLY): ",device), perror (NULL), exit (FATAL_START(errno)); if (fstat (in_fd,&sb) < 0) fprintf (stderr,":-( unable to stat(\"%s\"): ",device), perror (NULL), exit (FATAL_START(errno)); if (!S_ISCHR(sb.st_mode)) { setuid(uid); /* drop all privileges */ close (in_fd); /* reopen as mortal */ if ((in_fd = open (device,O_RDONLY)) < 0) fprintf (stderr,":-( unable to reopen(\"%s\",O_RDONLY): ",device), perror (NULL), exit (FATAL_START(errno)); open_rw: if ((out_fd = open (device,O_RDWR)) < 0) fprintf (stderr,":-( unable to open(\"%s\",O_RDWR): ",device), perror (NULL), exit (FATAL_START(errno)); if (fstat(out_fd,&sc) < 0) fprintf (stderr,":-( unable to stat(\"%s\"): ",device), perror (NULL), exit (FATAL_START(errno)); if (sb.st_dev!=sc.st_dev || sb.st_ino!=sc.st_ino) fprintf (stderr,":-( %s: race condition detected!\n",device), exit(FATAL_START(EPERM)); opened_rw: poor_man = 0; if (ioctl_handle && ioctl_handle != INVALID_HANDLE) { if (taskif) (*taskif)->Release(taskif), taskif=NULL; if (mmcdif) (*mmcdif)->Release(mmcdif), mmcdif=NULL; if (plugin) IODestroyPlugInInterface(plugin), plugin=NULL; } ioctl_handle = INVALID_HANDLE; setuid (uid); /* drop all privileges */ return device; } /* * Still as euid=root! But note that get_mmc_profile makes sure it's * an MMC device, as it terminates the program if the unit doesn't * reply to GET CONFIGURATON. In combination with following switch * this means that if installed set-root-uid, growisofs grants * access to DVD burner(s), but not to any other device. */ { io_object_t parent; CFMutableDictionaryRef match,bsddev; CFNumberRef num; kern_return_t kret; int i; SInt32 score=0; if ((match = CFDictionaryCreateMutable (kCFAllocatorDefault,0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)) == NULL || (bsddev = CFDictionaryCreateMutable (kCFAllocatorDefault,0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)) == NULL) { if (match) CFRelease (match); fprintf (stderr,":-( unable to CFDictionaryCreateMutable\n"); exit (FATAL_START(ENOMEM)); } i = major (sb.st_rdev); num = CFNumberCreate (kCFAllocatorDefault,kCFNumberIntType,&i); CFDictionarySetValue (bsddev,CFSTR("BSD Major"),num); CFRelease (num); i = minor (sb.st_rdev); num = CFNumberCreate (kCFAllocatorDefault,kCFNumberIntType,&i); CFDictionarySetValue (bsddev,CFSTR("BSD Minor"),num); CFRelease (num); CFDictionarySetValue (match,CFSTR(kIOPropertyMatchKey),bsddev); CFRelease (bsddev); if ((scsiob = IOServiceGetMatchingService (kIOMasterPortDefault,match)) == IO_OBJECT_NULL) { fprintf (stderr,":-( unable to IOServiceGetMatchingService\n"); exit (FATAL_START(ENXIO)); } // traverse up to "SCSITaskAuthoringDevice" while ((kret = IORegistryEntryGetParentEntry (scsiob,kIOServicePlane, &parent)) == kIOReturnSuccess) { CFStringRef uclient; const char *s; int cmp; IOObjectRelease (scsiob); scsiob = parent; uclient = (CFStringRef)IORegistryEntryCreateCFProperty (scsiob, CFSTR(kIOPropertySCSITaskDeviceCategory), kCFAllocatorDefault,0); if (uclient) { s = CFStringGetCStringPtr (uclient,kCFStringEncodingMacRoman); cmp = strcmp (s,kIOPropertySCSITaskAuthoringDevice); CFRelease (uclient); if (cmp==0) break; } } if (kret!=kIOReturnSuccess) { if (scsiob!=IO_OBJECT_NULL) IOObjectRelease (scsiob); fprintf (stderr,":-( unable to locate \"SCSITaskAuthoringDevice\"" ": %x\n",kret); exit (FATAL_START(ENXIO)); } if ((kret = IOCreatePlugInInterfaceForService (scsiob, kIOMMCDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &plugin,&score)) != kIOReturnSuccess) { IOObjectRelease (scsiob); fprintf (stderr,":-( unable to IOCreatePlugInInterface" ": 0x%x\n",kret); exit (FATAL_START(errno=ENXIO)); } if ((*plugin)->QueryInterface (plugin, CFUUIDGetUUIDBytes(kIOMMCDeviceInterfaceID), (void**)&mmcdif) != S_OK) { IODestroyPlugInInterface (plugin), plugin=NULL; IOObjectRelease (scsiob); fprintf (stderr,":-( unable to QueryInterface\n"); exit (FATAL_START(ENXIO)); } if ((taskif = (*mmcdif)->GetSCSITaskDeviceInterface (mmcdif)) == NULL) { (*mmcdif)->Release (mmcdif), mmcdif=NULL; IODestroyPlugInInterface (plugin), plugin=NULL; IOObjectRelease (scsiob); fprintf (stderr,":-( unable to GetSCSITaskDeviceInterface\n"); exit (FATAL_START(ENXIO)); } /* * ioctl_handle is reassigned to taskif after ObtainExclusiveAccess */ ioctl_handle = mmcdif; } mmc_profile = get_mmc_profile (ioctl_handle); switch (mmc_profile&0xFFFF) { case 0x11: /* DVD-R */ case 0x12: /* DVD-RAM */ case 0x13: /* DVD-RW Restricted Overwrite */ case 0x14: /* DVD-RW Sequential */ case 0x15: /* DVD-R Dual Layer Sequential */ case 0x16: /* DVD-R Dual Layer Jump */ case 0x1A: /* DVD+RW */ case 0x1B: /* DVD+R */ case 0x2A: /* DVD+RW Double Layer */ case 0x2B: /* DVD+R Double Layer */ case 0x41: /* BD-R SRM */ case 0x42: /* BD-R RRM */ case 0x43: /* BD-RE */ open_poor_man: poor_man = 1; out_fd = dup(in_fd); /* it's ignored in poor_man ... */ setuid (uid); /* drop all privileges */ return ioctl_device=device; break; default: fprintf (stderr,":-( %s: media is not recognized as " "recordable DVD: %X\n",device,mmc_profile); exit (FATAL_START(EMEDIUMTYPE)); } setuid(uid); goto open_rw; } #else #error "Unsupported OS" #endif #if defined(__unix) || defined(__unix__) static double this_very_moment() { struct timeval tv; gettimeofday (&tv,NULL); return tv.tv_usec/1e6+tv.tv_sec; } static int signals_blocked; static void (*signal_handler)(void); void sigs_mask (int mask) { sigset_t set; if (signals_blocked == mask) return; sigemptyset (&set); sigaddset (&set,SIGHUP), sigaddset (&set,SIGINT), sigaddset (&set,SIGTERM), sigaddset (&set,SIGPIPE); sigprocmask ((signals_blocked=mask)?SIG_BLOCK:SIG_UNBLOCK,&set,NULL); } static void common_handler(int sig) { if (!signals_blocked) { progress.zero = 0; (*signal_handler)(); } } #define sigit(SIG) do { \ sigaction (SIG,NULL,&sa); \ sa.sa_mask = mask; \ sa.sa_handler = common_handler; \ sa.sa_flags &= ~SA_NODEFER; \ sa.sa_flags |= SA_RESETHAND; \ sigaction (SIG,&sa,NULL); \ } while (0) void atsignals(void(*func)(void)) { sigset_t mask; struct sigaction sa; signal_handler = func; sigemptyset (&mask); sigaddset (&mask,SIGHUP), sigaddset (&mask,SIGINT), sigaddset (&mask,SIGTERM), sigaddset (&mask,SIGPIPE); sigit (SIGHUP); sigit (SIGINT); sigit (SIGTERM); sigit (SIGPIPE); } #undef sigit #elif defined(_WIN32) static double this_very_moment() { LARGE_INTEGER t; GetSystemTimeAsFileTime((FILETIME *)&t); return t.QuadPart/1e7; } static BOOL signals_blocked; static void (*signal_handler)(void); static HANDLE signal_thread; void sigs_mask (int mask) { signals_blocked = mask; } static BOOL WINAPI ctrl_handler (DWORD code) { if (!signals_blocked) { progress.zero = 0; SuspendThread (signal_thread); (*signal_handler)(); } return TRUE; } void atsignals (void(*func)(void)) { signal_handler = func; signal_thread = OpenThread (THREAD_SUSPEND_RESUME,FALSE,GetCurrentThreadId()); if (signal_thread == NULL) perror (":-( unable to OpenThread"), _exit (FATAL_START(errno)); if (!SetConsoleCtrlHandler (ctrl_handler,TRUE)) perror (":-( unable to SetConsoleCtrlHandler"), _exit (FATAL_START(errno)); } #endif static unsigned int from_733 (unsigned char *s) { unsigned int ret=0; ret |= s[0]; ret |= s[1]<<8; ret |= s[2]<<16; ret |= s[3]<<24; return ret; } static void to_733 (unsigned char *s,unsigned int val) { s[0] = (val) & 0xFF; s[1] = (val>>8) & 0xFF; s[2] = (val>>16) & 0xFF; s[3] = (val>>24) & 0xFF; s[4] = (val>>24) & 0xFF; s[5] = (val>>16) & 0xFF; s[6] = (val>>8) & 0xFF; s[7] = (val) & 0xFF; } static int setup_C_parm (char *C_parm,struct iso_primary_descriptor *descr) { int next_session=-1,profile=mmc_profile&0xFFFF; if (!poor_man || profile==0x1A || profile==0x2A || profile==0x13 || profile==0x12 || profile==0x42 || profile==0x43) { next_session = from_733(descr->volume_space_size); /* pad to closest 32K boundary */ next_session += 15; next_session /= 16; next_session *= 16; sprintf (C_parm,"16,%u",next_session); } else if ( profile==0x2B || profile==0x1B || profile==0x11 || profile==0x14 || profile==0x15 || profile==0x16 || profile==0x41) next_session=plusminus_r_C_parm (ioctl_handle,C_parm); return next_session; } /* * Threads workers */ THR_TYPE progress_print (void *arg) { double ratio,velocity,slept; int delta,rbu,nfirst=0; off64_t outoff=*(off64_t *)arg; off64_t lastcurrent=outoff,current; #if defined(__unix) || defined(__unix__) sigset_t set; sigfillset (&set); pthread_sigmask (SIG_SETMASK,&set,NULL); #endif while (1) { slept = this_very_moment(); lastcurrent = progress.current; poll (NULL,0,3333); slept -= this_very_moment(); if (progress.zero==0 || !nfirst++) continue; if ((current = progress.current) > outoff) { delta = time (NULL) - progress.zero; ratio = (double)(progress.final-outoff) / (double)(current-outoff); delta *= ratio - 1.0; velocity=(current-lastcurrent)/(-slept*1024*__1x()); rbu = (int)((current - outoff)/DVD_BLOCK); rbu = highest_ecc_block - rbu; fprintf (stdout,"%11"LLD"/%"LLD" (%4.1f%%) @%.1fx, " "remaining %d:%02d " "RBU %5.1f%% " /* ring buffer utilization */ "UBU %5.1f%%\n", /* unit buffer utilization */ current,progress.final,100.0/ratio, velocity,delta/60,delta%60, (100.0*rbu*DVD_BLOCK)/the_buffer_size, (100.0*butlz)); lastcurrent=current; butlz=1.00001; } else { rbu = (int)((current - outoff)/DVD_BLOCK); rbu = highest_ecc_block - rbu; fprintf (stdout,"%11"LLD"/%"LLD" (%4.1f%%) @0x, " "remaining ??:?? " "RBU %5.1f%% " /* ring buffer utilization */ "UBU %5.1f%%\n", /* unit buffer utilization */ current,progress.final,0.0, (100.0*rbu*DVD_BLOCK)/the_buffer_size,0.0); } fflush (stdout); } } /* * Synchronization between reader() and builtin_dd() threads is done * through a semaphore enumerating available ECC blocks in the ring * buffer and a volatile 32-bit variable denoting highest available * block. Special note about the synchronization on this variable. * Most would argue that access to this variable should be serialized * by a mutex, but is it really required? Being aligned at natural * processor word boundary [compiler does it!], both read and write * operations per se are atomic [hardware does it!] and the point is * that builtin_dd() thread only reads it, while only reader() thread * updates it. In this case of distinct and atomic reader and writer * a spin-lock upon given condition for variable in question is more * than sufficient. The spin-lock is complemented with quantum yield * in the loop body. This scheme is chosen in order to ensure maximum * responsiveness upon moment the first data block is available in * previously exhausted ring buffer, yet give the reader() thread * every opportunity to put the data there. One might argue that one * could take a nap in the spin-lock loop body, but the trouble is * that this is likely to incur undesired effects, because system * interval timer accuracy is commonly not less and sometimes even * much larger than characteristic time for recording of a single ECC * block at ever increasing burning velocity. Some OSes do offer * adequate interval timer resolution, but there're quite a few among * them which arrange it by looping in kernel mode for instrumented * amount of spins. This means that "yielding" spin-lock in user-land * is as appropriate for all practical reasons. Hibernating would be * due if a "catastrophic" event, LUN buffer underrun, is a known * fact, in which case one can afford falling asleep for coarse * interval ["coarse" refers to OS interval timer resolution in * comparison to characteristic ECC block recording time]. */ THR_TYPE reader (void *arg) { int n,infd=(size_t)arg; unsigned int off,mask=(the_buffer_size/DVD_BLOCK)-1; char *block; #if defined(__unix) || defined(__unix__) sigset_t set; sigfillset (&set); pthread_sigmask (SIG_SETMASK,&set,NULL); #endif while (1) { if (!__semaphore_wait(the_ring_semaphore)) { #if defined(__unix) || defined(__unix__) if (errno==EINTR) continue; #endif return (reader_exit = errno); } block = the_buffer + (highest_ecc_block & mask)*DVD_BLOCK; off = 0; eintr: while ((n=read (infd,block+off,DVD_BLOCK-off)) > 0) { off += n; if (off == DVD_BLOCK) { highest_ecc_block++; break; } } if (n<0) { #if defined(__unix) || defined(__unix__) if (errno==EINTR) goto eintr; #endif return (reader_exit = errno); } if (n==0) { if (off) memset (block+off,0,DVD_BLOCK-off), highest_ecc_block++; break; } } return (reader_exit = (unsigned int)-1); } /* * This is executed in main thread context */ int builtin_dd (int infd,int outfd,off64_t outoff) { char *block; int n; unsigned int off,mask; struct stat64 sb; off64_t capacity=0,tracksize=0,startoff=outoff; if (fstat64 (infd,&sb)) #ifdef _WIN32 memset (&sb,0,sizeof(sb)), sb.st_mode = (~_S_IFREG)&_S_IFMT; #else perror (":-( unable to fstat64"), exit(FATAL_START(errno)); #endif if (ioctl_handle!=INVALID_HANDLE) capacity = get_capacity (ioctl_handle); progress.zero=0; progress.current=outoff; if (dao_size || S_ISREG(sb.st_mode)) { tracksize = dao_size ? (dao_size*CD_BLOCK) : sb.st_size; progress.final=outoff+tracksize; if (capacity && progress.final > capacity) { fprintf (stderr,":-( %s: %"LLD" blocks are free, " "%"LLD" to be written!\n", ioctl_device, (capacity-outoff)/2048,tracksize/2048); if (overburn) fprintf (stderr,":-! ignoring...\n"); else close(infd), close(outfd), exit (FATAL_START(ENOSPC)); } } else progress.final=0; /* suck in first 64K and examine ISO9660 Primary Descriptor if present */ off = 0; while ((n=read (infd,the_buffer+off,2*DVD_BLOCK-off)) > 0) { off += n; if (off == 2*DVD_BLOCK) { if (!memcmp(the_buffer+DVD_BLOCK,"\1CD001",6)) { struct iso_primary_descriptor *descr = saved_descriptors; /* * Save descriptor set for use at the end of recording! */ memcpy (saved_descriptors,the_buffer+DVD_BLOCK,DVD_BLOCK); if (!zero_image) { if (tracksize==0) { tracksize=from_733(descr->volume_space_size)*CD_BLOCK; if (capacity && (outoff+tracksize) > capacity) { fprintf (stderr,":-( %s: %"LLD" blocks are free, " "%"LLD" to be written!\n", ioctl_device, (capacity-outoff)/2048, tracksize/2048); if (overburn) fprintf (stderr,":-! ignoring...\n"); else { n = -1; set_errno(FATAL_START(ENOSPC)); goto out; } } } /* else already checked for overburn condition */ /* layer_break is meaningful only for -Z recording */ if (layer_break>0 && !outoff) { if (tracksize > layer_break*CD_BLOCK*2) { fprintf (stderr,":-( insane Layer Break position " "with respect to dataset size\n"); n = -1; set_errno(FATAL_START(EINVAL)); goto out; } if (!progress.final) progress.final = tracksize; tracksize = layer_break*CD_BLOCK*2; } } else if (capacity > outoff) { int i=0; unsigned int ts = (tracksize=capacity-outoff)/2048; while (i<16 && descr->type[0] != (unsigned char)255) to_733 (descr->volume_space_size,ts), descr++, i++; } else { fprintf (stderr,":-( capacity is undefined or insane?\n"); n = -1; set_errno(FATAL_START(EINVAL));/* ... or whatever */ goto out; } } else if (outoff && zero_image) { fprintf (stderr,":-( no volume descriptors found " "in previous session?\n"); n = -1; set_errno(FATAL_START(ENOTDIR)); /* kludge! */ goto out; } break; } } if (n<=0) goto out; if (dry_run) close(infd), close(outfd), exit(0); if (quiet<=0) __thread_create(progress_print,&outoff); /* yeah, really kludgy, shuffling file descriptor like that... */ if (zero_image) close(infd), infd=open64 ("/dev/zero",O_RDONLY); highest_ecc_block = off/DVD_BLOCK; assert (highest_ecc_block==2); /* Fill the_buffer up to the_buffer_size */ while ((n=the_buffer_size-off, n%=DVD_BLOCK, n=n?n:DVD_BLOCK, n=read (infd,the_buffer+off,n)) > 0) { off += n; if (off == the_buffer_size) break; } if (n<0) goto out; highest_ecc_block = (off+DVD_BLOCK-1)/DVD_BLOCK; if (off==the_buffer_size) { the_ring_semaphore = __semaphore_create(the_buffer_size/DVD_BLOCK); if (the_ring_semaphore == NULL) perror(":-( failed to create semaphore"), exit(FATAL_START(errno)); if (__thread_create(reader,(void *)(size_t)infd) == NULL) perror(":-( failed to create thread"), exit(FATAL_START(errno)); reader_exit = 0; } else { memset (the_buffer+off,0,the_buffer_size-off); reader_exit = (unsigned int)-1; /* reader "exited" already */ } if (poor_man || next_session==0) /* unmount media */ { #if defined(__unix) || defined(__unix__) pid_t rpid,pid; int rval; if ((pid=fork()) == (pid_t)-1) perror (":-( unable to fork -umount"), exit (FATAL_START(errno)); /* pass through set-root-uid if any */ if (pid == 0) { char str[12]; if ((rval=fcntl (in_fd,F_GETFD))<0) rval=0; fcntl (in_fd,F_SETFD,rval&~FD_CLOEXEC); sprintf (str,"%d",in_fd); execlp (argv0,"-umount",str,in_device,NULL); exit (FATAL_START(errno)); } while (1) { rpid = waitpid (pid,&rval,0); if (rpid == (pid_t)-1 || (rpid != pid && (errno=ECHILD,1))) { if (errno==EINTR) continue; else perror (":-( waipid failed"), exit(FATAL_START(errno)); } if (WIFSTOPPED(rval)) continue; errno=0; if (WIFEXITED(rval)) errno=WEXITSTATUS(rval); else errno=ECHILD; break; } set_errno(errno&FATAL_MASK); if (errno) { if (errno==EBUSY) fprintf (stderr,":-( %s: unable to proceed with recording: ", in_device), #ifdef __hpux fprintf (stderr,"device is mounted\n"), #else fprintf (stderr,"unable to unmount\n"), #endif exit (FATAL_START(errno)); else fprintf (stderr,":-( unable to umount %s: ",in_device); perror (""), exit (FATAL_START(errno)); } #elif defined(_WIN32) if (fumount (ioctl_fd)) fprintf (stderr,":-( unable to umount %s: ",in_device), perror (NULL), exit (FATAL_START(errno)); #endif } if (poor_man) { #ifdef __linux int i; /* * Linux 2.6 kernel allows to claim O_EXCL on block device. * However! It does not really exclude the possibility for * another application to interfere with ongoing recording, * because kernel serializes *only* O_EXCL calls and lets * through those without. And the trouble naturally is that * there are automounting/autoplaying facilities, which * don't adhere to O_EXCL. Note that mount(2) system call * does acquire such exclusive lock at kernel level all by * itself, but most commonly it's user-land file system * detection subroutines, those determining 3rd argument * for mount(2) call, which turn to be the culprit. E.g. * among examined mount(8) and submountd(8) both were found * needing patching. Once mount(8) is patched, one can allow * autofs to handle DVD recorder [because automount deploys * mount(8)]. And once submountd(8) is patched it would be * possible to exempt subfs mount point from umount method * in transport.hxx. Keep in mind that this "list" is not * by any means complete... */ for (i=3;i>=0;i--) { /* * For reference, I can't do it earlier as exclusive lock * could have been granted to mounted file system, the one * we've tried to unmount just a moment ago... */ int fd = open64 (ioctl_device,O_RDONLY|O_NONBLOCK|O_EXCL); struct stat64 sb,sc; if (fd<0) { if (errno==EBUSY) { if (i==0) fprintf (stderr,":-( unable to O_EXCL %s: some" "one was in time to remount?\n", ioctl_device), exit (FATAL_START(errno)); poll(NULL,0,157); /* retry... */ } else break; /* running under 2.4? */ } else { if (fstat64 (ioctl_fd,&sb) || fstat64 (fd,&sc) || sb.st_rdev != sc.st_rdev) fprintf (stderr,":-( %s: race condition detected!\n", ioctl_device), exit(FATAL_START(EPERM)); /* * Note that I effectively leak this file descriptor, * but as it's meant to be closed at the very end, I * might as well let kernel clean it up automagically * upon process termination... */ break; } } #elif defined(__APPLE__) && defined(__MACH__) IOReturn rval; close (in_fd); close (out_fd); if ((rval = (*taskif)->ObtainExclusiveAccess (taskif)) != kIOReturnSuccess) { (*taskif)->Release (taskif), taskif=NULL; (*mmcdif)->Release (mmcdif), mmcdif=NULL; IODestroyPlugInInterface (plugin), plugin=NULL; IOObjectRelease (scsiob), scsiob=IO_OBJECT_NULL; fprintf (stderr,":-( unable to ObtainExclusiveAccess: 0x%x\n",rval); exit (FATAL_START(EBUSY)); } ioctl_handle = taskif; #endif /* * See commentary section in growisofs_mmc.cpp for * further details on poor_mans_setup */ pwrite64_method = poor_mans_setup (ioctl_handle, outoff+tracksize); } if (!progress.final) { if (tracksize) progress.final = outoff+tracksize; else progress.final = capacity; } if (capacity && progress.final>capacity) progress.final = capacity; progress.zero=time(NULL); off = 0; /* off is now used as written ECC block counter!!! */ mask = (the_buffer_size/DVD_BLOCK)-1; while (1) { /* "yielding" spin-lock */ while (!reader_exit && (off == highest_ecc_block)) { if (poor_man && butlz>1.0) butlz = get_buffer_stats(ioctl_handle); __thread_yield(); } if (off == highest_ecc_block) break; /* no more data */ block = the_buffer + (off & mask)*DVD_BLOCK; if ((n=(*pwrite64_method) (outfd,block,DVD_BLOCK,outoff)) != DVD_BLOCK) { if (n>0) set_errno(EIO); else if (n==0) set_errno(ENOSPC); n = -1; goto out; } if (the_ring_semaphore) __semaphore_post(the_ring_semaphore); /* collect statistics every 64th block or every second MB */ if (poor_man && (off&63)==0) { float u = get_buffer_stats(ioctl_handle); /* but show only the minimal value; might appear a bit * alarming, but it's really more informative... */ if (u32*1024) cmdsz = 32*1024; if ((arg=strrchr(mkisofs_argv[0],'\\')))arg++; else arg = mkisofs_argv[0]; cmdsz--; assert ((len0=strlen (arg)) < cmdsz); strcpy (cmd,arg), cmd[len0++] = ' ', cmdsz -= len0; for (argv=mkisofs_argv+1;*argv;argv++) { size_t len1 = strlen (*argv); int quot = 0; if (strchr (*argv,' ') && (*argv)[0]!='"' && (*argv)[len1-1]!='"') quot = 2; assert ((len1 + quot) < cmdsz); if (quot) cmd[len0++] = '"',cmdsz--; strcpy (cmd+len0,*argv), cmdsz-=len1, len0+=len1; if (quot) cmd[len0++] = '"',cmdsz--; cmd[len0++] = ' '; } if (cmd[len0-1] == ' ') cmd[len0-1] = '\0'; else cmd[len0] = '\0'; } if (!CreateProcess (mkisofs_argv[0],cmd,NULL,NULL, HANDLE_FLAG_INHERIT, 0,NULL,NULL,&si,&pi)) fprintf (stderr,":-( unable to execute %s: ", mkisofs_argv[0]), perror(NULL), exit (FATAL_START(errno)); memset (saved_descriptors,0,sizeof(saved_descriptors)); CloseHandle (si.hStdOutput); CloseHandle (pi.hThread); n=builtin_dd(_open_osfhandle((size_t)hRead,O_RDONLY),outfd,outoff); if (n==0) /* mkisofs must have finished, consume the exit code */ { DWORD ret; if (GetExitCodeProcess (pi.hProcess,&ret) && ret) fprintf (stderr,":-( mkisofs has failed: %d\n",ret), exit (1); else perror (":-( GetExitCodeProcess failed"), exit(errno); } else if (n<0) { int err = errno; set_errno(err&FATAL_MASK); /* they might be passing FATAL_START */ perror (":-( write failed"), exit (err); } } #endif int main (int argc, char *argv[]) { int imgfd=-1; char *in_image=NULL,*env; char dev_found='\0'; int i,n,warn_for_isofs=0; char **mkisofs_argv,C_parm[24],M_parm_[16],*M_parm=M_parm_; int mkisofs_argc,growisofs_argc; int alleged_next_session=-1; unsigned int new_size; argv0 = argv[0]; #if defined(__unix) || defined(__unix__) #if !defined(I_KNOW_ALL_ABOUT_SUDO) if (getenv ("SUDO_COMMAND")) { fprintf (stderr,":-( %s is being executed under sudo, " "aborting!\n",argv[0]); fprintf (stderr," See NOTES paragraph in growisofs " "manual page for further details.\n"); exit(FATAL_START(EACCES)); } #endif /* * This is a set-root-uid "entry point" for listed operations. User * can't trick this code to unmount arbitrary file system, as [s]he * has to pass opened file descriptor to the mounted device. As for * file descriptor passed by this program itself, I rely upon the * fact that it was appropriately audited at open time in platform- * specific setup_fds above... */ if (*argv[0] == '-') { int fd; struct stat fdst; unsigned int cap2k=0; chdir ("/"); if (argc < 3) exit (EINVAL); fd=atoi (argv[1]); if (!strcmp(argv[0],"-umount")) { if (fumount (fd)) exit (errno); exit (0); } else if ( (!strcmp(argv[0],"-reload") && (no_reload=0,1)) || (!strcmp(argv[0],"-eject") && (no_reload=-1,1)) ) { if (fstat (fd,&fdst) < 0) perror (":-( unable to fstat"), exit (1); if (argc > 3) cap2k = (unsigned int)strtoul (argv[3],NULL,0); close (fd); if (media_reload (argv[2],&fdst,cap2k)) perror (":-( unable to reload tray"), exit (1); exit (0); } exit(1); } /* * Ignore return values as we might be running as mortals */ nice(-20); /* I'd rather do it right after I allocate ring buffer and fire off * threads, but I'm likely to be running without extra privileges * by then:-( */ do { # ifdef RLIMIT_MEMLOCK struct rlimit rlim; if (getrlimit(RLIMIT_MEMLOCK,&rlim)) break; /* those who want to increase beyond DEFAULT_BUF_SIZE_MB have * to 'limit memorylocked unlimited' or whatever appropriate * at command prompt or in wrapper script */ if (rlim.rlim_cur < (DEFAULT_BUF_SIZE_MB+16)*1024*1024) { rlim.rlim_cur = (DEFAULT_BUF_SIZE_MB+16)*1024*1024; if (rlim.rlim_max < rlim.rlim_cur) rlim.rlim_max = rlim.rlim_cur; if (setrlimit(RLIMIT_MEMLOCK,&rlim)) break; } # endif # ifdef __FreeBSD__ syscall(SYS_mlockall,3); # else mlockall(MCL_CURRENT|MCL_FUTURE); # endif } while (0); # if (defined(__sun) || defined(sun)) && defined(setppriv) if (basic_priv && getuid() != 0) { /* this is done to allow secure -Z /dev/dvd=image.iso processing */ setppriv (PRIV_SET,PRIV_EFFECTIVE,basic_priv); /* this is done to allow secure mkisofs startup */ setppriv (PRIV_SET,PRIV_INHERITABLE,basic_priv); if ((permitted_priv=priv_allocset())) getppriv (PRIV_PERMITTED,permitted_priv); } # endif #endif mkisofs_argv = malloc ((argc+3)*sizeof(char *)); if (mkisofs_argv == NULL) fprintf (stderr,":-( unable to allocate %lu bytes: ", (unsigned long)((argc+3)*sizeof(char *))), perror (NULL), exit (FATAL_START(errno)); #if defined(__unix) || defined(__unix__) env = getenv ("MKISOFS"); mkisofs_argv[0] = (env?env:"mkisofs"); #elif defined(_WIN32) /* * On Windows I insist on mkisofs.exe to reside in very same * directory as growisofs.exe. Unlike Unix that is... */ { char *backslash,*borrow = (char *)saved_descriptors; GetModuleFileName (NULL,borrow,sizeof(saved_descriptors)); backslash = strrchr(borrow,'\\'); if (backslash) backslash++; else backslash = borrow; strcpy (backslash,"mkisofs.exe"); mkisofs_argv[0] = strdup(borrow); memset (saved_descriptors,0,sizeof(saved_descriptors)); } #endif mkisofs_argc = 1; growisofs_argc=0; _argc=argc, _argv=argv; for (i=1;i 2) in_device = argv[i]+2; else in_device = argv[++i]; dev_found = 'M'; } else if (!strncmp(opt,"-prev-session",13)) { if (len > 13) in_device = opt+13; else in_device = argv[++i]; dev_found = 'M'; } else if (argv[i][1] == 'Z') { if (len > 2) in_device = argv[i]+2; else in_device = argv[++i]; dev_found = 'Z'; } else if (!strncmp(opt,"-zero-session",13)) { if (len > 13) in_device = opt+13; else in_device = argv[++i]; dev_found = 'Z'; } else if (argv[i][1] == 'F') { if (len > 2) in_device = argv[i]+2; else in_device = argv[++i]; dev_found = 'F'; dry_run = 1; } else if (!strncmp(opt,"-free-space",11)) { if (len > 11) in_device = opt+11; else in_device = argv[++i]; dev_found = 'F'; dry_run = 1; } else if (!strcmp(opt,"-poor-man")) { if (poor_man<0) poor_man = 1; continue; } else if (!strncmp(opt,"-speed",6)) { char *s; if (len>6) (s=strchr(opt,'='))?s++:s; else s=argv[++i]; if (s) speed_factor=atof(s); if (speed_factor<=0) fprintf (stderr,"-speed=%.1f: insane speed factor.\n", speed_factor), exit(FATAL_START(EINVAL)); continue; } else if (!strcmp(opt,"-dvd-compat")) { if (poor_man<0) poor_man = 1; dvd_compat++; continue; } else if (!strcmp(opt,"-overburn")) { overburn = 1; continue; } else if (argv[i][1] == 'o') { if (!strchr(argv[i]+2,'-')) /* somewhat opportunistic... */ fprintf (stderr,"%s: -o[utput] option " "is not permitted.\n",argv[0]), exit(FATAL_START(EINVAL)); } else if (!strncmp(opt,"-use-the-force-luke",19)) { char *s=strchr (opt,'='),*o; if (s == NULL) /* backward compatibility */ no_tty_check = 1; else { s++; if (strstr(s,"tty")) no_tty_check = 1; if (strstr(s,"dummy")) test_write = 1; if (strstr(s,"notray")) no_reload = 1; if (strstr(s,"noload")) no_reload = -1; if (strstr(s,"wrvfy")) wrvfy = 1; if (strstr(s,"4gms")) no_4gb_check = 1; if (strstr(s,"noopc")) no_opc = 1; if (strstr(s,"moi")) { quiet=-1; mkisofs_argv[mkisofs_argc++] = "-quiet"; } if ((o=strstr(s,"dao"))) { dvd_compat += 256; /* vvvvvvvvvvv tracksize option takes precedence! */ if (dao_size==0 && (o[3]==':' || o[3]=='=')) { dao_size=strtol(o+4,0,0); if (dao_size<=0) fprintf (stderr,":-( insane dao%c%d option\n", o[3],dao_size), exit(FATAL_START(EINVAL)); } } if ((o=strstr(s,"tracksize"))) { if (o[9]==':' || o[9]=='=') { dao_size=strtol(o+10,0,0); if (dao_size<=0) fprintf (stderr,":-( insane tracksize%c%d option\n", o[9],dao_size), exit(FATAL_START(EINVAL)); } } if ((o=strstr(s,"break"))) { if (o[5]==':' || o[5]=='=') { layer_break=strtol(o+6,0,0); if (layer_break<=0 || layer_break%16) fprintf (stderr,":-( insane break%c%d option\n", o[5],layer_break), exit(FATAL_START(EINVAL)); } } if ((o=strstr(s,"seek")) && next_session<0) { if (o[4]==':' || o[4]=='=') { next_session=strtol(o+5,0,0); if (next_session<0 || next_session%16) fprintf (stderr,":-( insane seek%c%d option\n", o[4],next_session), exit(FATAL_START(EINVAL)); } } if ((o=strstr(s,"bufsize"))) { if (o[7]==':' || o[7]=='=') { the_buffer_size=strtol(o+8,&o,0); if (*o=='M' || *o=='m') the_buffer_size*=1024*1024; else if (*o=='K' || *o=='k') the_buffer_size*=1024; } } if ((o=strstr(s,"spare"))) { spare=1; if (o[5]==':' || o[5]=='=') { if (!strncmp(o+6,"none",4)) spare=-1; else if (!strncmp(o+6,"min",3)) spare=0; } } } continue; } else if (!strcmp(opt,"-dvd-video")) { if (poor_man<0) poor_man = 1; dvd_compat++, growisofs_argc++; } else if (!strcmp(opt,"-quiet")) quiet++, growisofs_argc++; else if (argv[i][1] == 'C' || !strncmp(opt,"-cdrecord-params",16)) { char *s=argv[i+1]; int i1,i2; if (argv[i][1]=='C' && len>2) s=argv[i]+2; else i++; if (sscanf (s,"%d,%d",&i1,&i2) == 2) alleged_next_session=i2; continue; } else if (argv[i][1] == '#' || !strcmp(opt,"-dry-run")) { dry_run = 1; continue; } else if (argv[i][1] == '?' || !strcmp(opt,"-help")) { PRINT_VERSION (argv[0]); printf ("- usage: %s [-dvd-compat] [-overburn] [-speed=1] \\\n" " -[ZM] /dev/dvd \n",argv[0]); printf (" for see 'mkisofs %s'\n",opt); exit (FATAL_START(EINVAL)); } else if (strstr (opt,"-version")) { PRINT_VERSION (argv[0]); printf (" front-ending to %s: ",mkisofs_argv[0]); fflush (stdout); #if defined(__unix) || defined(__unix__) setuid(getuid()); { char *argv[3]; argv[0] = mkisofs_argv[0]; argv[1] = "-version"; argv[2] = NULL; execvp (mkisofs_argv[0],argv); } #elif defined(_WIN32) if (_spawnl (_P_WAIT,mkisofs_argv[0], "mkisofs.exe","-version",NULL) != -1) exit(0); #endif fprintf (stderr,"\n- unable to execute %s: ", mkisofs_argv[0]), perror (NULL), exit (FATAL_START(errno)); } } if (dev_found && in_device) { if (*in_device == '=') in_device++; if (1 || dev_found == 'Z') { if ((in_image = strchr(in_device,'='))) { #if defined(__unix) || defined(__unix__) uid_t euid=geteuid(); seteuid (getuid()); /* get real for parsing -[ZM] a=b */ #endif while (in_image) { *in_image='\0'; set_errno(0); #ifdef _WIN32 /* have to treat d:=image.iso separately:-( */ if (in_image[-1] == ':') break; #endif if (access (in_device,F_OK)==0 || errno!=ENOENT) break; *in_image='=', in_image=strchr(in_image+1,'='); } if (errno) fprintf (stderr,":-( \"%s\": unexpected errno:", in_device), perror (NULL), exit (FATAL_START(errno)); if (in_image) { in_image++; if (sscanf(in_image,"/dev/fd/%u",&imgfd) == 1) imgfd = dup (imgfd); /* validate descriptor */ #ifdef OPEN_DIRECTIO else if ((imgfd = OPEN_DIRECTIO(in_image,O_RDONLY))<0) #else else #endif imgfd = open64(in_image,O_RDONLY); if (imgfd < 0) fprintf (stderr,":-( unable to open64(\"%s\"," "O_RDONLY): ",in_image), perror (NULL), exit(FATAL_START(errno)); if (!strcmp(in_image,"/dev/zero")) zero_image=1; } #if defined(__unix) || defined(__unix__) seteuid (euid); /* revert to saved [set-]uid */ #endif } } /* * Sets up in_fd, out_fd, ioctl_handle and poor_man variable. * This procedure is platform-specific. If the program * has to be installed set-root-uid, then this procedure * is the one to drop privileges [if appropriate]. */ out_device=setup_fds (in_device); *(long *)saved_descriptors[0].type = 0; /* redundant:-) */ if (mmc_profile&0x10000) /* blank media */ n=0, set_errno(EIO); else { n=pread64 (in_fd,saved_descriptors,2048,VOLDESC_OFF*CD_BLOCK); if (n==0) set_errno(EIO); /* end-of-file reached? */ } if (n!=2048 && dev_found=='M') perror (":-( unable to pread64(2) primary volume descriptor"), fprintf (stderr," you most likely want to use -Z option.\n"), exit (FATAL_START(errno)); if (dev_found=='M' || ((dev_found=='F' && !(mmc_profile&0x10000)) && memcmp(saved_descriptors[0].type,"\0\0\0\0\0\0",6))) { if (memcmp (saved_descriptors[0].type,"\1CD001",6)) fprintf (stderr,":-( %s doesn't look like isofs...\n", in_device), exit(FATAL_START(EMEDIUMTYPE)); next_session=setup_C_parm(C_parm,saved_descriptors); if (imgfd>=0) { if (zero_image) { off64_t off=(atoi(C_parm)-16)*CD_BLOCK; dup2(in_fd,imgfd); /* kludge! */ if (lseek64 (imgfd,off,SEEK_SET) == (off64_t)-1) fprintf (stderr,":-( %s: unable to lseek(%"LLD"): ", in_device,off), perror (NULL), exit(FATAL_START(errno)); } else if (alleged_next_session!=next_session) fprintf (stderr,"%s: -C argument is %s.\n", argv[0],alleged_next_session>=0? "insane":"undefined"), exit(FATAL_START(EINVAL)); } else if (next_session > (0x200000-0x5000)) /* 4GB/2K-40MB/2K */ if (/*(mmc_profile&0xFFFF)<0x20 ||*/ ((mmc_profile&0xFFFF)<0x40 && !no_4gb_check)) fprintf (stderr,":-( next session would cross 4GB " "boundary, aborting...\n"), exit (FATAL_START(ENOSPC)); mkisofs_argv[mkisofs_argc++] = "-C"; mkisofs_argv[mkisofs_argc++] = C_parm; #ifdef CANNOT_PASS_DEV_FD_N_TO_MKISOFS # ifdef PASS_STDIN_TO_MKISOFS M_parm = "-"; # else M_parm = get_M_parm (in_fd,in_device); # endif #else # ifdef PASS_STDIN_TO_MKISOFS M_parm = "/dev/fd/0"; # else sprintf (M_parm,"/dev/fd/%d",in_fd); # endif #endif mkisofs_argv[mkisofs_argc++] = "-M"; mkisofs_argv[mkisofs_argc++] = M_parm; len = 3 + strlen(C_parm) + 3 + strlen(M_parm); growisofs_argc += 4; } else { if (!memcmp (saved_descriptors[0].type,"\1CD001",6)) warn_for_isofs = 1; if (next_session<0) next_session = 0; continue; } *(long *)saved_descriptors[0].type = 0; } else { mkisofs_argv[mkisofs_argc++] = argv[i]; } } if (in_device == NULL) fprintf (stderr,"%s: previous \"session\" device is not specified, " "do use -M or -Z option\n",argv[0]), exit (FATAL_START(EINVAL)); if (imgfd<0) { if (mkisofs_argc==1 && dev_found!='F') fprintf (stderr,"%s: no mkisofs options specified, " "aborting...\n",argv[0]), exit (FATAL_START(EINVAL)); } else if ((mkisofs_argc-growisofs_argc)>1) fprintf (stderr,"%s: no mkisofs options are permitted with =, " "aborting...\n",argv[0]), exit (FATAL_START(EINVAL)); mkisofs_argv[mkisofs_argc] = NULL; assert (next_session!=-1); assert (in_fd!=-1); assert (out_fd!=-1); /* * ensure highest_ecc_block is properly aligned * to ensure access atomicity */ assert ((((size_t)&highest_ecc_block)&(sizeof(highest_ecc_block)-1))==0); /* * Ensure the_buffer_size is degree of 2 */ if (!dry_run) { unsigned int shift=0,sz=the_buffer_size; while (sz>>=1) shift++; if (shift<20) shift=20; /* 1MB is minumum */ sz = 1<1024*1024) { size_t phys_mem = (size_t)sysconf(_SC_PHYS_PAGES) * (size_t)sysconf(_SC_PAGESIZE); if (phys_mem) { phys_mem /= 2; /* normally PHYS is a bit smaller than * sheer 2**n amount of RAM cells, so * we commonly land on 1/4 RAM */ while (the_buffer_size > phys_mem) the_buffer_size /= 2; } } #endif /* mmap buffer so that we can use it with /dev/raw/rawN */ # if defined(MAP_ANONYMOUS) && !(defined(__sun) || defined(sun)) the_buffer = mmap (NULL,the_buffer_size,PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS,-1,(off_t)0); # else { int fd; if ((fd=open ("/dev/zero",O_RDWR)) < 0) perror (":-( unable to open \"/dev/zero\"?"), exit(FATAL_START(errno)); the_buffer = mmap (NULL,the_buffer_size,PROT_READ|PROT_WRITE, MAP_SHARED,fd,0); close (fd); } # endif if (the_buffer == MAP_FAILED) fprintf (stderr,":-( unable to anonymously mmap %d: ",the_buffer_size), perror (NULL), exit (FATAL_START(errno)); #elif defined(_WIN32) if (the_buffer_size>1024*1024) { MEMORYSTATUSEX mem; mem.dwLength = sizeof(mem); if (GlobalMemoryStatusEx (&mem)) { mem.ullAvailPhys /= 2; while (the_buffer_size > mem.ullAvailPhys) the_buffer_size /= 2; } } the_buffer = VirtualAlloc (NULL,the_buffer_size,MEM_COMMIT,PAGE_READWRITE); if (the_buffer == NULL) fprintf (stderr,":-( unable to VirtualAlloc %d: ",the_buffer_size), perror (NULL), exit (FATAL_START(errno)); #endif /* never finalize disc at multi-sessioning DVD±R recordings... */ { int profile = mmc_profile&0xFFFF; if (next_session>0 && (profile==0x2B || profile==0x1B || profile==0x11 || profile==0x15 || profile==0x16)) dvd_compat=0; /* ... except when filling the media up:-) */ if (next_session>0 && zero_image) dvd_compat=1; } if (warn_for_isofs) { int fd=-1; #if defined(__unix) || defined(__unix__) fd=open("/dev/tty",O_RDONLY); #elif defined(_WIN32) fd=_open("CONIN$",O_RDONLY); #endif if (fd>=0) { if (isatty (fd)) warn_for_isofs |= 2; close (fd); } else if (isatty (0)) warn_for_isofs |= 2; if (no_tty_check || (warn_for_isofs&2)) fprintf (stderr,"WARNING: %s already carries isofs!\n",in_device), printf ("About to execute '"); else fprintf (stderr,"FATAL: %s already carries isofs!\n",in_device), exit(FATAL_START(EBUSY)); } else printf ("Executing '"); if (imgfd>=0) printf ("builtin_dd if=%s of=%s obs=32k seek=%u", in_image,out_device,next_session/16); else { for (i=0;i=0) do { int f; \ if ((f=fcntl ((fd),F_GETFD)) < 0) f=0; \ fcntl ((fd),F_SETFD,f|FD_CLOEXEC); } while (0) CLOSEONEXEC(in_fd); CLOSEONEXEC(out_fd); #if !(defined(__APPLE__) && defined(__MACH__)) CLOSEONEXEC(ioctl_fd); #endif #undef CLOSEONEXEC #endif if (poor_man) { out_device=in_device; if (!ioctl_device) ioctl_device=out_device; switch (mmc_profile&0xFFFF) { case 0x11: /* DVD-R Sequential */ case 0x12: /* DVD-RAM */ case 0x13: /* DVD-RW Restricted Overwrite */ case 0x14: /* DVD-RW Sequential */ case 0x15: /* DVD-R Dual Layer Sequential */ #if 0 /* reserved for now... */ case 0x16: /* DVD-R Dual Layer Jump */ #endif case 0x1A: /* DVD+RW */ case 0x1B: /* DVD+R */ case 0x2A: /* DVD+RW Double Layer */ case 0x2B: /* DVD+R Double Layer */ case 0x41: /* BD-R SRM */ #if 0 /* I haven't seen RRM-capable unit... */ case 0x42: /* BD-R RRM */ #endif case 0x43: /* BD-RE */ break; default: fprintf (stderr,":-( %s: mounted media doesn't appear to be " "[supported] DVD%sRW, DVD%sR or Blu-ray Disc\n", out_device,plusminus_locale(),plusminus_locale()), exit(FATAL_START(EMEDIUMTYPE)); break; } } if (dev_found == 'F') /* is it optimal spot? */ { off64_t capacity = 0; printf("next_session=%"LLD"\n", next_session*CD_BLOCK); if (ioctl_handle!=INVALID_HANDLE) capacity = get_capacity (ioctl_handle); printf("capacity=%"LLD"\n", capacity); exit(0); } if (imgfd>=0) { quiet--; if (builtin_dd (imgfd,out_fd,next_session*CD_BLOCK) < 0) { int err = errno; set_errno(err&FATAL_MASK); /* they might be passing FATAL_START */ perror (":-( write failed"), exit (err); } if (alleged_next_session <= 0) set_errno(0), exit (0); } else pipe_mkisofs_up (mkisofs_argv,in_fd,out_fd,next_session*CD_BLOCK); /* * Recall that second 32KB written in this session can be found * in saved_descriptors[]! And now note that poor_man_rewritable() * fills the first 32KB of the_buffer with volume descriptor set * from the beginning of the volume, if appropriate that is! The * latter is a workaround hook for DVD-RW Restricted Overwrite * interfering with DVD+RW kernel patch... Is this mode ugly or * is it ugly? G-r-r-r... */ if (next_session!=0 && (!poor_man || poor_man_rewritable (ioctl_handle,the_buffer))) { struct iso_primary_descriptor *descr = (struct iso_primary_descriptor *)the_buffer; if (memcmp (saved_descriptors[0].type,"\1CD001",6)) fprintf (stderr,":-( %s:%d doesn't look like isofs!\n", out_device,next_session), exit (set_errno(EMEDIUMTYPE)); fprintf (stderr,"%s: copying volume descriptor(s)\n", poor_man?ioctl_device:out_device); new_size = from_733(saved_descriptors[0].volume_space_size) + next_session; if (!poor_man && (set_errno(0), pread64 (out_fd,descr,DVD_BLOCK, VOLDESC_OFF*CD_BLOCK)) != DVD_BLOCK) set_errno(errno?errno:EIO), perror (":-( unable to pread64(2) volume descriptors set"), exit (errno); memcpy (descr+0,&saved_descriptors[0],sizeof(*descr)); to_733(descr[0].volume_space_size,new_size); /* * copy secondary volume descriptor(s) which are expected to * appear in the very same order. */ for (i=1;i #include #define SIGS_BLOCK 1 #define SIGS_UNBLOCK 0 extern "C" void sigs_mask (int); extern "C" void atsignals (void (*)(void)); #define DVD_1X 1352 // 1385 * 1000 / 1024 #define BD_1X 4390 // 4495.5 * 1000 / 1024 static int media_written=0,next_track=1, is_dao=0,quickgrown=0,do_reload=1, _1x=DVD_1X,_64k=0,bdr_plus_pow=0; static unsigned int stdnap=(256*1000)/DVD_1X,// measured in milliseconds buf_size=512; // measured in KBs static class __velocity__ { private: int value; public: int operator=(int v) { if (v>=_1x) stdnap=(buf_size*500)/v; else stdnap=(buf_size*500)/_1x; return value = v; } operator int() { return value; } } velocity; static void *ioctl_handle=(void *)-1; #ifndef ioctl_fd #define ioctl_fd ((long)ioctl_handle) #endif static unsigned int next_wr_addr=1; // it starts as boolean static unsigned int dao_blocks=0; static unsigned int cap2kstart=0; static unsigned char formats[260],disc_info[32]; extern "C" { extern int dvd_compat,test_write,no_reload,mmc_profile,_argc, wrvfy,no_opc,spare; extern double speed_factor; extern char *ioctl_device,**_argv; } extern "C" int __1x (void) { return _1x; } extern "C" int fumount (int fd) { Scsi_Command cmd; return cmd.umount(fd); } extern "C" int media_reload (char *name=NULL,struct stat *sb=NULL,unsigned int cap2k=0) { if (name==NULL) { Scsi_Command cmd(ioctl_handle); pioneer_stop (cmd); #if defined(RELOAD_NEVER_NEEDED) #undef RELOAD_NEVER_NEEDED #define RELOAD_NEVER_NEEDED 1 #else #define RELOAD_NEVER_NEEDED 0 #endif if (RELOAD_NEVER_NEEDED || no_reload>0) { cmd[0] = 0x1E; // ALLOW MEDIA REMOVAL cmd[5] = 0; cmd.transport (); return (errno=0); } #if !RELOAD_NEVER_NEEDED char fdstr[12],cap2kstr[12]; int n; if ((n=fcntl (ioctl_fd,F_GETFD))<0) n=0; fcntl (ioctl_fd,F_SETFD,n&~FD_CLOEXEC); sprintf (fdstr,"%ld",ioctl_fd); sprintf (cap2kstr,"%u",cap2kstart); execlp(_argv[0],no_reload<0?"-eject":"-reload", fdstr,ioctl_device,cap2kstr,(void *)NULL); } else { { Scsi_Command cmd; unsigned char c[8]; unsigned int cap2kend; if (!cmd.associate (name,sb)) return 1; cmd[0] = 0x25; // READ CAPACITY cmd[9] = 0; if (!cmd.transport (READ,c,sizeof(c))) cap2kend = c[0]<<24|c[1]<<16|c[2]<<8|c[3]; else cap2kend = (unsigned int)-1; if (cmd.is_reload_needed(cap2k==cap2kend)) { fprintf (stderr,"%s: reloading tray\n",name); cmd[0] = 0x1E; // ALLOW MEDIA REMOVAL cmd[5] = 0; if (cmd.transport ()) return 1; while (1) // Pioneer DVR-x05 needs this... { cmd[0] = 0x1B; // START/STOP UNIT cmd[1] = 0x1; // "IMMED" cmd[4] = 0x2; // "Eject" cmd[5] = 0; if (cmd.transport() == 0x20407) // "OP IN PROGRESS" { poll (NULL,0,333); continue; } break; } // yes, once again, non-"IMMED"... cmd[0] = 0x1B; // START/STOP UNIT cmd[4] = 0x2; // "Eject" cmd[5] = 0; if (cmd.transport()) return 1; } #if defined(__sun) || defined(sun) else if (volmgt_running()) { setuid(getuid()); execl("/usr/bin/volrmmount","volrmmount","-i",name,(void*)NULL); return 0; // not normally reached } else if (1) { char srlink[256],*dsk; static char dev[]="/dev/dsk/"; int len; /* this basically handles /dev/rsrN link */ if ((len=readlink(name,srlink,sizeof(srlink)-1))>0 && (srlink[len]='\0',(dsk=strstr(srlink,"dsk/"))!=NULL)) { name=(char *)alloca(sizeof(dev)+strlen(dsk+4)+1); strcpy(name,dev), strcat(name,dsk+4); } setuid(getuid()); execl("/usr/bin/rmmount","rmmount",name,(void*)NULL); return 0; // not reached on SunOS 5.11 } #endif else return 0; // m-m-m-m! patched kernel:-) } if (no_reload>=0) { Scsi_Command cmd; if (cmd.associate (name,sb)) { cmd[0] = 0x1B; // START/STOP UNIT cmd[1] = 0x1; // "IMMED" cmd[4] = 0x3; // "Load" cmd[5] = 0; cmd.transport (); } errno=0; // ignore all errors on load } return 0; #endif } return 1; } extern "C" int get_mmc_profile (void *fd) { Scsi_Command cmd(fd); unsigned char buf[8],inq[128]; int profile=0,once=1,blank=0,err; unsigned int len; #if defined(__APPLE__) && defined(__MACH__) // The reason for Mac OS X specific MMCIO appearing here and there // is that raw SCSI requires exclusive access, while I'm not ready // to give it up yet... // I don't call INQUIRY, because kernel ensures that // MMCDeviceInterface is attached to an MMC device... if ((err=MMCIO(fd,GetConfiguration,0,0,buf,sizeof(buf)))) sperror ("GET CONFIGURATION",err), exit (FATAL_START(errno)); profile = buf[6]<<8|buf[7]; #else // INQUIRY is considered to be "non-intrusive" in a sense that // it won't interfere with any other operation nor clear sense // data, which might be important to retain for security reasons. cmd[0] = 0x12; // INQUIRY cmd[4] = 36; cmd[5] = 0; if ((err=cmd.transport(READ,inq,36))) sperror ("INQUIRY",err), exit (FATAL_START(errno)); // make sure we're talking to MMC unit, for security reasons... if ((inq[0]&0x1F) != 5) fprintf (stderr,":-( not an MMC unit!\n"), exit (FATAL_START(EINVAL)); do { cmd[0] = 0x46; cmd[8] = sizeof(buf); cmd[9] = 0; if ((err=cmd.transport(READ,buf,sizeof(buf)))) sperror ("GET CONFIGURATION",err), fprintf (stderr,":-( non-MMC unit?\n"), exit (FATAL_START(errno)); if ((profile = buf[6]<<8|buf[7]) || !once) break; // no media? cmd[0] = 0; // TEST UNIT READY cmd[5] = 0; if ((cmd.transport()&0xFFF00) != 0x23A00) break; // try to load tray... cmd[0] = 0x1B; // START/STOP UNIT cmd[4] = 0x3; // "Load" cmd[5] = 0; if ((err=cmd.transport ())) sperror ("LOAD TRAY",err), exit (FATAL_START(errno)); #if 1 wait_for_unit (cmd); #else // consume sense data, most likely "MEDIA MAY HAVE CHANGED" cmd[0] = 0; // TEST UNIT READY cmd[5] = 0; if ((err=cmd.transport ()) == -1) sperror ("TEST UNIT READY",err), exit (FATAL_START(errno)); #endif } while (once--); #endif // !Mac OS X if (profile==0 || (profile&0x70)==0) // no or non-DVD/BD media... return profile; if ((profile&0xF0) == 0x40) { unsigned char header[16]; _1x = BD_1X; _64k = 1; if (profile==0x41) // BD-R SRM { // Check for POW feature... #if defined(__APPLE__) && defined(__MACH__) err = MMCIO(fd,GetConfiguration,2,0x38,header,16); #else cmd[0] = 0x46; // GET CONFIGURATION cmd[1] = 2; // ask for the only feature... cmd[3] = 0x38; // the "BD-R Pseudo-Overwrite" one cmd[8] = 16; // The feature should be there, right? cmd[9] = 0; err = cmd.transport (READ,header,16); #endif if (err==0 && (header[0]<<24|header[1]<<16|header[2]<<8|header[3])>=12 && (header[8+2]&1)==1) bdr_plus_pow=1; } } #if defined(__APPLE__) && defined(__MACH__) err = MMCIO(fd,ReadDiscInformation,disc_info,sizeof(disc_info)); #else cmd[0] = 0x51; // READ DISC INFORMATION cmd[8] = sizeof(disc_info); cmd[9] = 0; err = cmd.transport (READ,disc_info,sizeof(disc_info)); #endif if (err) sperror ("READ DISC INFORMATION",err), exit (FATAL_START(errno)); // see if it's blank media if ((disc_info[2]&3) == 0) blank=0x10000; if (profile != 0x1A && profile != 0x2A && // DVD+RW profile != 0x13 && profile != 0x12 && // DVD-R[AM] profile != 0x43 && !(profile == 0x41 && blank)) // BD-R[E] return blank|profile; #if defined(__APPLE__) && defined(__MACH__) err = MMCIO(fd,ReadFormatCapacities,formats,12); #else cmd[0] = 0x23; // READ FORMAT CAPACITIES cmd[8] = 12; cmd[9] = 0; err = cmd.transport (READ,formats,12); #endif if (err) sperror ("READ FORMAT CAPACITIES",err), exit (FATAL_START(errno)); len = formats[3]; if (len&7 || len<16) fprintf (stderr,":-( FORMAT allocaion length isn't sane"), exit (FATAL_START(EINVAL)); #if defined(__APPLE__) && defined(__MACH__) err = MMCIO(fd,ReadFormatCapacities,formats,4+len); #else cmd[0] = 0x23; // READ FORMAT CAPACITIES cmd[7] = (4+len)>>8; cmd[8] = (4+len)&0xFF; cmd[9] = 0; err = cmd.transport (READ,formats,4+len); #endif if (err) sperror ("READ FORMAT CAPACITIES",err), exit (FATAL_START(errno)); if (len != formats[3]) fprintf (stderr,":-( parameter length inconsistency\n"), exit(FATAL_START(EINVAL)); // see if it's not formatted if ((formats[8]&3) != 2) blank = 0x10000; return blank|profile; } static unsigned int get_2k_capacity (Scsi_Command &cmd,void *fd=NULL) { unsigned char buf[32]; unsigned int ret=0; unsigned int nwa,free_blocks; int i,obligatory,len,err; obligatory=0x00; switch (mmc_profile&0xFFFF) { case 0x1A: // DVD+RW obligatory=0x26; case 0x13: // DVD-RW Restricted Overwrite for (i=8,len=formats[3];i>2) == obligatory) break; if (i==len) { fprintf (stderr,":-( can't locate obligatory format descriptor\n"); return 0; } ret = formats[4+i+0]<<24; ret |= formats[4+i+1]<<16; ret |= formats[4+i+2]<<8; ret |= formats[4+i+3]; nwa = formats[4+5]<<16|formats[4+6]<<8|formats[4+7]; if (nwa>2048) ret *= nwa/2048; else if (nwa<2048) ret /= 2048/nwa; break; case 0x12: // DVD-RAM // As for the moment of this writing I don't format DVD-RAM. // Therefore I just pull formatted capacity for now... ret = formats[4+0]<<24; ret |= formats[4+1]<<16; ret |= formats[4+2]<<8; ret |= formats[4+3]; nwa = formats[4+5]<<16|formats[4+6]<<8|formats[4+7]; if (nwa>2048) ret *= nwa/2048; else if (nwa<2048) ret /= 2048/nwa; break; case 0x11: // DVD-R case 0x14: // DVD-RW Sequential case 0x15: // DVD-R Dual Layer Sequential case 0x16: // DVD-R Dual Layer Jump case 0x1B: // DVD+R case 0x2B: // DVD+R Double Layer case 0x41: // BD-R SRM #if defined(__APPLE__) && defined(__MACH__) if (fd) err = MMCIO(fd,ReadTrackInformation,1,next_track,buf,sizeof(buf)); else #endif { cmd[0] = 0x52; // READ TRACK INFORMATION cmd[1] = 1; cmd[4] = next_track>>8; cmd[5] = next_track&0xFF; // last track, set up earlier cmd[8] = sizeof(buf); cmd[9] = 0; err = cmd.transport (READ,buf,sizeof(buf)); } if (err) { sperror ("READ TRACK INFORMATION",err); return 0; } nwa = 0; if (buf[7]&1 && !bdr_plus_pow) // NWA_V { nwa = buf[12]<<24; nwa |= buf[13]<<16; nwa |= buf[14]<<8; nwa |= buf[15]; } free_blocks = buf[16]<<24; free_blocks |= buf[17]<<16; free_blocks |= buf[18]<<8; free_blocks |= buf[19]; ret = nwa + free_blocks; break; case 0x43: // BD-RE // just pull formatted capacity for now... ret = formats[4+0]<<24; ret |= formats[4+1]<<16; ret |= formats[4+2]<<8; ret |= formats[4+3]; break; default: break; } return ret; } extern "C" off64_t get_capacity (void *fd) { Scsi_Command cmd(fd); return (off64_t)get_2k_capacity(cmd,fd)*2048; } extern "C" float get_buffer_stats (void *fd) { Scsi_Command cmd(fd); unsigned char bcap[12]; unsigned int bsize,bfree; int err; cmd[0] = 0x5C; // READ BUFFER CAPACITY cmd[8] = sizeof(bcap); cmd[9] = 0; if ((err=cmd.transport (READ,bcap,sizeof(bcap)))) return -1.0f; bsize = bcap[4]<<24|bcap[5]<<16|bcap[6]<<8|bcap[7]; bfree = bcap[8]<<24|bcap[9]<<16|bcap[10]<<8|bcap[11]; return bsize ? (1.0f - (float)bfree/(float)bsize) : 0.0f; } ssize_t poor_mans_pwrite64 (int fd,const void *_buff,size_t size,off64_t foff) { Scsi_Command cmd(ioctl_handle); /* screw first argument */ unsigned char bcap[12]; const unsigned char *buff=(const unsigned char *)_buff; unsigned int lba,nbl,bsize,bfree; int retries=0,errcode; static int dao_toggle=-1; if (foff&0x7FFF || size&0x7FFF) // 32K block size return (errno=EINVAL,-1); lba = (unsigned int)(foff/2048); nbl = (unsigned int)(size/2048); if (!media_written && next_wr_addr) { if ((lba+nbl) <= next_wr_addr) return size; else if (next_wr_addr > lba) nbl -= (next_wr_addr-lba), size -= (next_wr_addr-lba)<<11, buff += (next_wr_addr-lba)<<11, lba = next_wr_addr; } #if defined(__sun) || defined(sun) else next_wr_addr = lba; #endif if (dao_toggle<0) dao_toggle=is_dao; { static unsigned int first_wr_addr=0; if (!media_written) { sigs_mask (SIGS_BLOCK); first_wr_addr = lba; } else if (lba >= (first_wr_addr+buf_size/2)) // measured in 2KBs! { sigs_mask (SIGS_UNBLOCK); } } if (dao_blocks!=0 && (lba+nbl)>dao_blocks) nbl = dao_blocks-lba; while (1) { cmd[0] = wrvfy?0x2E:0x2A; // WRITE [AND VERIFY] (10) cmd[2] = (lba>>24)&0xff; // Logical Block Addrss cmd[3] = (lba>>16)&0xff; cmd[4] = (lba>>8)&0xff; cmd[5] = lba&0xff; cmd[7] = (nbl>>8)&0xff; cmd[8] = nbl&0xff; cmd[9] = 0; #if 0 cmd[0] = 0xAA; // WRITE(12) cmd[2] = (lba>>24)&0xff; // Logical Block Addrss cmd[3] = (lba>>16)&0xff; cmd[4] = (lba>>8)&0xff; cmd[5] = lba&0xff; cmd[8] = (nbl>>8)&0xff; cmd[9] = nbl&0xff; cmd[10] = 0x80; // "Streaming" cmd[11] = 0; #endif // // First writes can be long, especially in DAO mode... // I wish I could complement this with "if (lba==0)," // but some units might choose to fill the buffer before // they take the first nap... // cmd.timeout(dao_toggle?180:60); // // It should also be noted that under Linux these values // (if actually respected by kernel!) can turn out bogus. // The problem is that I scale them to milliseconds as // documentation requires/implies, while kernel treats // them as "jiffies." I could/should have used HZ macro // (or sysconf(_SC_CLK_TCK)), but recent kernels maintain // own higher HZ value and disrespects the user-land one. // Sending them down as milliseconds is just safer... // if (!(errcode=cmd.transport (WRITE,(void *)buff,size))) break; //--- WRITE failed ---// #if defined(__sun) || defined(sun) // // Solaris can slice USB WRITEs to multiple ones. Here I try // to find out which slice has failed. I expect we get here // only when we re-enter the loop... // if (lba==next_wr_addr && errcode==0x52102) // "INVALID ADDRESS FOR WRITE" { unsigned char track[32]; cmd[0] = 0x52; // READ TRACK INFORMATION cmd[1] = 1; cmd[4] = next_track>>8; cmd[5] = next_track&0xFF; cmd[8] = sizeof(track); cmd[9] = 0; if (!cmd.transport (READ,track,sizeof(track))) { if (track[7]&1) // NWA_V { next_wr_addr = track[12]<<24; next_wr_addr |= track[13]<<16; next_wr_addr |= track[14]<<8; next_wr_addr |= track[15]; if (lbanext_wr_addr) { nbl -= next_wr_addr-lba, size -= (next_wr_addr-lba)<<11, buff += (next_wr_addr-lba)<<11, lba = next_wr_addr; continue; } } } } #endif if (errcode==0x20408) // "LONG WRITE IN PROGRESS" { // Apparently only Pioneer units do this... if (velocity == 0) { if (handle_events(cmd) & (1<<6)) continue; goto sync_cache; } cmd[0] = 0x5C; // READ BUFFER CAPACITY cmd[8] = sizeof(bcap); cmd[9] = 0; if (cmd.transport (READ,bcap,sizeof(bcap))) bfree=0, bsize=buf_size; else { bsize = bcap[4]<<24|bcap[5]<<16|bcap[6]<<8|bcap[7]; bfree = bcap[8]<<24|bcap[9]<<16|bcap[10]<<8|bcap[11]; bsize /= 1024, bfree /= 1024; // xlate to KB } // check for sanity and drain 1/2 of the buffer if (bsize < buf_size/2) bsize = buf_size/2; else bsize /= 2; if (bfree > bsize) bfree = bsize; int msecs=0; if ((msecs=(bsize-bfree)*1000) > velocity) { msecs /= velocity; retries++; if (dao_toggle) dao_toggle=-1; } else // lots of free buffer reported? { if (dao_toggle) { dao_toggle=-1; if ((handle_events(cmd) & (1<<6))) continue; msecs = bsize*1000; msecs /= velocity; } else if (!retries++) continue; // Pioneer units seem to return bogus values at high // velocities now and then... I reserve for 4 ECC // blocks, which is ~6 ms at 16x. But note that some // OSes, Linux 2.4 among then, will nap for 10 or 20. msecs = (4*32*1000)/velocity; } // Retry values were increased because high-speed units // start recordings at only portion of velocity and it // takes more retries to write lead-in... Another common // reason for stucks are recalibrations at zone edges... if (retries > (dao_toggle?768:192)) fprintf (stderr,":-? the LUN appears to be stuck " "writing LBA=%xh, keep retrying in %dms\n", lba,msecs), retries=1; if (msecs>=0) { poll (NULL,0,msecs); continue; } lba |= 0x80000000; // signal insane bfree... } else if (lba==0 && errcode==0x20401) // "IN PROCESS OF BECOMING READY" { if ((handle_events(cmd) & (1<<6))) continue; if (++retries > 3) { fprintf (stderr,":-! the LUN is still in process of becoming ready, " "retrying in 5 secs...\n"), retries=0; sigs_mask (SIGS_UNBLOCK); poll (NULL,0,5000); } else sigs_mask (SIGS_UNBLOCK), poll (NULL,0,stdnap); continue; } else if (errcode==0x20404 || // "FORMAT IN PROGRESS" errcode==0x20407 || // "OPERATION IN PROGRESS" errcode==0x52C00) // "COMMAND SEQUENCE ERROR" { // Happens under automounter control? In general I recommend // to disable it! This code is a tribute to users who disregard // the instructions... sync_cache: // should I check if lba+nbl is multiple of 64K in BD case? cmd[0] = 0x35; // SYNC CACHE cmd[9] = 0; if (!cmd.transport()) { if (++retries > 1) { fprintf (stderr,":-! the LUN appears to be stuck at %xh, " "retrying in 5 secs...\n",lba), retries=0; sigs_mask (SIGS_UNBLOCK); poll (NULL,0,5000); } else if (errcode==0x52C00) fprintf (stderr,":-! \"COMMAND SEQUENCE ERROR\"@LBA=%xh. " "Is media being read?\n",lba); continue; } lba |= 0x40000000; // signal "can't SYNC CACHE" } { char cmdname[64]; sprintf (cmdname,"WRITE@LBA=%xh",lba), sperror (cmdname,errcode); } if (lba==0) { if (ASC(errcode)==0x30) fprintf (stderr,":-( media is not formatted or unsupported.\n"); else if ((mmc_profile&0xFFFF)==0x14 /*&& ASC(errcode)==0x64*/) fprintf (stderr,":-( attempt to re-run with " "-dvd-compat -dvd-compat to engage DAO or " "apply full blanking procedure\n"); } #if 1 // safety net: leave DL sessions open and resumable... if ((mmc_profile&0xFFFF)==0x2B && errcode!=0x52100) media_written=0; #endif return -1; } next_wr_addr = lba+nbl; media_written = 1; if (dao_toggle<0) dao_toggle=0; return size; } // spare == -1 "format without spare" // spare == 0 "format with minimal spare" // spare > 0 "format with default descriptor" static void bd_r_format (Scsi_Command &cmd) { int err,i,len; unsigned char descr[12],*f; if (spare < 0) return; // no format -> no spare area:-) len = formats[3]; if (spare == 0) // locate descriptor with maximum capacity { unsigned int max,cap; int j; i = 0; for (max=0,j=8;j>2) == 0x32) { cap = f[0]<<24|f[1]<<16|f[2]<<8|f[3]; if (max < cap) max=cap,i=j; } } if (i==0) fprintf (stderr,":-( can't locate format 0x32 descriptor\n"), exit (FATAL_START(errno=EMEDIUMTYPE)); } else i = 8; // grab default descriptor memset (descr,0,sizeof(descr)); descr[1]=0x02; // "IMMED" flag descr[3]=0x08; // "Descriptor Lenght" (LSB) memcpy (descr+4,formats+4+i,5); descr[4+4]&=~3; // ensure "Format Subtype" is set to SRM+POW f = descr+4; fprintf (stderr,"%s: pre-formatting blank BD-R for %.1fGB...\n", ioctl_device,(f[0]<<24|f[1]<<16|f[2]<<8|f[3])*2048.0/1e9); cmd[0] = 0x04; // FORMAT UNIT cmd[1] = 0x11; // "FmtData" and "Format Code" cmd[5] = 0; if ((err=cmd.transport(WRITE,descr,sizeof(descr)))) sperror ("FORMAT UNIT",err), exit (FATAL_START(errno)); wait_for_unit (cmd); cmd[0] = 0x35; // FLUSH CACHE cmd[9] = 0; cmd.transport(); } static void bd_re_format (Scsi_Command &cmd) { int err,i,len; unsigned char descr[12],*f; len = formats[3]; if (spare == 0) // locate descriptor with maximum capacity { unsigned int max,cap; int j; i = 0; for (max=0,j=8;j>2) == 0x30) { cap = f[0]<<24|f[1]<<16|f[2]<<8|f[3]; if (max < cap) max=cap,i=j; } } if (i==0) fprintf (stderr,":-( can't locate format 0x30 descriptor\n"), exit (FATAL_START(errno=EMEDIUMTYPE)); } else if (spare < 0) // locate descriptor 0x31 { for (i=8;i>2) == 0x31) break; if (i==len) fprintf (stderr,":-( can't locate format 0x31 descriptor\n"), exit (FATAL_START(errno=EMEDIUMTYPE)); } else i = 8; // grab default descriptor memset (descr,0,sizeof(descr)); descr[1]=0x02; // "IMMED" flag descr[3]=0x08; // "Descriptor Lenght" (LSB) memcpy (descr+4,formats+4+i,5); f = descr+4; fprintf (stderr,"%s: pre-formatting blank BD-RE for %.1fGB...\n", ioctl_device,(f[0]<<24|f[1]<<16|f[2]<<8|f[3])*2048.0/1e9); cmd[0] = 0x04; // FORMAT UNIT cmd[1] = 0x11; // "FmtData" and "Format Code" cmd[5] = 0; if ((err=cmd.transport(WRITE,descr,sizeof(descr)))) sperror ("FORMAT UNIT",err), exit(FATAL_START(errno)); wait_for_unit (cmd); cmd[0] = 0x35; // FLUSH CACHE cmd[9] = 0; cmd.transport(); } static void plus_rw_format (Scsi_Command &cmd) { int err,i,len; unsigned char descr[12]; if ((formats[4+4]&3) == 1) // Unformatted media { fprintf (stderr,"%s: pre-formatting blank DVD+RW...\n",ioctl_device); for (i=8,len=formats[3];i>2) == 0x26) break; if (i==len) fprintf (stderr,":-( can't locate DVD+RW format descriptor\n"), exit(FATAL_START(EMEDIUMTYPE)); memset (descr,0,sizeof(descr)); descr[1]=0x02; // "IMMED" flag descr[3]=0x08; // "Descriptor Length" (LSB) memcpy (descr+4,formats+4+i,4); descr[8]=0x26<<2; // "Format type" 0x26 cmd[0] = 0x04; // FORMAT UNIT cmd[1] = 0x11; // "FmtData" and "Format Code" cmd[5] = 0; if ((err=cmd.transport(WRITE,descr,sizeof(descr)))) sperror ("FORMAT UNIT",err), exit(FATAL_START(errno)); wait_for_unit (cmd); cmd[0] = 0x35; // FLUSH CACHE cmd[9] = 0; if ((err=cmd.transport())) sperror ("FLUSH CACHE",err), exit(FATAL_START(errno)); } } static int plus_rw_restart_format (Scsi_Command &cmd, off64_t size) { unsigned char descr[12]; unsigned int lead_out,blocks; int err,i,len; if (!dvd_compat && size!=0) { blocks = (unsigned int)(size/2048); blocks += 15, blocks &= ~15; lead_out = 0; lead_out |= formats[4+0], lead_out <<= 8; lead_out |= formats[4+1], lead_out <<= 8; lead_out |= formats[4+2], lead_out <<= 8; lead_out |= formats[4+3]; if (blocks<=lead_out) // no need to resume format... return 0; } fprintf (stderr,"%s: restarting DVD+RW format...\n",ioctl_device); for (i=8,len=formats[3];i>2) == 0x26) break; if (i==len) fprintf (stderr,":-( can't locate DVD+RW format descriptor\n"), exit(FATAL_START(EMEDIUMTYPE)); memset (descr,0,sizeof(descr)); descr[1]=0x02; // "IMMED" flag descr[3]=0x08; // "Descriptor Length" (LSB) #if 1 memcpy (descr+4,formats+4+i,4); #else memset (descr+4,-1,4); #endif descr[8]=0x26<<2; // "Format type" 0x26 descr[11]=1; // "Restart Format" cmd[0] = 0x04; // FORMAT UNIT cmd[1] = 0x11; // "FmtData" and "Format Code" cmd[5] = 0; if ((err=cmd.transport(WRITE,descr,sizeof(descr)))) { sperror ("RESTART FORMAT",err); return 1; } wait_for_unit(cmd); return 0; } extern "C" int plusminus_r_C_parm (void *fd,char *C_parm) { unsigned int next_session, prev_session,err; Scsi_Command cmd(fd); unsigned char buf[36]; if ((disc_info[2]&3) != 1) fprintf (stderr,":-( media is not appendable\n"), exit(FATAL_START(EMEDIUMTYPE)); // allow to resume even --v-- incomplete sessions // if (((disc_info[2]>>2)&3) > 1) fprintf (stderr,":-( last session is not empty\n"), exit(FATAL_START(EMEDIUMTYPE)); if (mmc_profile==0x41 && bdr_plus_pow) next_track = disc_info[6]|disc_info[11]<<8; else next_track = disc_info[5]|disc_info[10]<<8; #if defined(__APPLE__) && defined(__MACH__) err = MMCIO(fd,ReadTrackInformation,1,next_track,buf,sizeof(buf)); #else cmd[0] = 0x52; // READ TRACK INFORMATION cmd[1] = 1; cmd[4] = next_track>>8; cmd[5] = next_track; // ask for last track cmd[8] = sizeof(buf); cmd[9] = 0; err = cmd.transport (READ,buf,sizeof(buf)); #endif if (err) { sperror ("READ TRACK INFORMATION",err); if (ASC(err)==0x24) // hp dvd200i returns 24 if media is full fprintf (stderr,":-( media must be full already\n"); exit (FATAL_START(errno)); } if (mmc_profile==0x41 && bdr_plus_pow && buf[7]&1) // NWA_V { next_session = buf[12]<<24; next_session |= buf[13]<<16; next_session |= buf[14]<<8; next_session |= buf[15]; } else { next_session = buf[8]<<24; // Track Start Address next_session |= buf[9]<<16; next_session |= buf[10]<<8; next_session |= buf[11]; } if (mmc_profile==0x41 && bdr_plus_pow) prev_session=0; else { // // All manuals say the data is fabricated, presumably implying // that one should use another command. But we stick to this one // because kernel uses this very command to mount multi-session // discs. // #if defined(__APPLE__) && defined(__MACH__) err = MMCIO(fd,ReadTableOfContents,0,1,0,buf,12); #else cmd[0] = 0x43; // READ TOC cmd[2] = 1; // "Session info" cmd[8] = 12; cmd[9] = 0; err = cmd.transport (READ,buf,12); #endif if (err) sperror ("READ SESSION INFO",err), exit (FATAL_START(errno)); prev_session = buf[8]<<24; prev_session |= buf[9]<<16; prev_session |= buf[10]<<8; prev_session |= buf[11]; } sprintf (C_parm,"%d,%d",prev_session+16,next_session); return next_session; } static unsigned char *pull_page2A (Scsi_Command &cmd) { unsigned char *page2A,header[12]; unsigned int len,bdlen; int err; cmd[0] = 0x5A; // MODE SENSE cmd[1] = 0x08; // "Disable Block Descriptors" cmd[2] = 0x2A; // "Capabilities and Mechanical Status" cmd[8] = sizeof(header); // header only to start with cmd[9] = 0; if ((err=cmd.transport(READ,header,sizeof(header)))) sperror ("MODE SENSE#2A",err), exit(FATAL_START(errno)); len = (header[0]<<8|header[1])+2; bdlen = header[6]<<8|header[7]; if (bdlen) // should never happen as we set "DBD" above { if (len < (8+bdlen+30)) fprintf (stderr,":-( LUN is impossible to bear with...\n"), exit(FATAL_START(EINVAL)); } else if (len < (8+2+(unsigned int)header[9]))// SANYO does this. len = 8+2+header[9]; page2A = (unsigned char *)malloc(len); if (page2A == NULL) fprintf (stderr,":-( memory exhausted\n"), exit(FATAL_START(ENOMEM)); cmd[0] = 0x5A; // MODE SENSE cmd[1] = 0x08; // "Disable Block Descriptors" cmd[2] = 0x2A; // "Capabilities and Mechanical Status" cmd[7] = len>>8; cmd[8] = len; // real length this time cmd[9] = 0; if ((err=cmd.transport(READ,page2A,len))) sperror ("MODE SENSE#2A",err), exit(FATAL_START(errno)); len -= 2; if (len < ((unsigned int)page2A[0]<<8|page2A[1])) // paranoia:-) page2A[0] = len>>8, page2A[1] = len; return page2A; } static int pull_velocity (Scsi_Command &cmd,unsigned char *d) { unsigned int len; int v,err; class autofree perf; #if 0 // 8x AccessTek derivatives, such as OptoRite DD0405 if ((d[4]&2) == 0) return -1; #endif len = (d[0]<<24|d[1]<<16|d[2]<<8|d[3])-4; if (len%16) return -1; if (len==0) return -1; // LG GCA-4040N:-( len += 8; if (len == (8+16)) { velocity = d[8+ 4]<<24|d[8+ 5]<<16|d[8+ 6]<<8|d[8+ 7]; v = d[8+12]<<24|d[8+13]<<16|d[8+14]<<8|d[8+15]; if (v>velocity) velocity=v; // CAV? } else // ZCLV { unsigned int n = (len-8)/16; unsigned char *p; perf = (unsigned char *)malloc (len); if (perf == NULL) fprintf (stderr,":-( memory exhausted\n"), exit(FATAL_START(ENOMEM)); cmd[0]=0xAC; // GET PERFORMANCE cmd[1]=4; // ask for "Overall Write Performance" cmd[8]=n>>8; cmd[9]=n; // real number of descriptors cmd[10]=0; // ask for descriptor in effect cmd[11]=0; if ((err=cmd.transport(READ,perf,len))) sperror ("GET CURRENT PERFORMANCE",err), exit (FATAL_START(errno)); // Pick the highest speed... for (p=perf+8,len-=8;len;p+=16,len-=16) { v=p[ 4]<<24|p[ 5]<<16|p[ 6]<<8|p[ 7]; if (v > velocity) velocity = v; v=p[12]<<24|p[13]<<16|p[14]<<8|p[15]; if (v > velocity) velocity = v; // ZCAV? } } return 0; } static int set_speed_B6h (Scsi_Command &cmd,unsigned int dvdplus, unsigned char *page2A) { unsigned int len; int err; unsigned char d[8+16]; class autofree perf; cmd[0]=0xAC; // GET PERFORMACE cmd[9]=1; // start with one descriptor cmd[10]=0x3; // ask for "Write Speed Descriptor" cmd[11]=0; if ((err=cmd.transport(READ,d,sizeof(d)))) { sperror ("GET PERFORMANCE",err); fprintf (stderr,":-( falling down to SET CD SPEED\n"); return -1; } len = (d[0]<<24|d[1]<<16|d[2]<<8|d[3])-4; if (len%16) // insane length { fprintf (stderr,":-( GET PERFORMANCE: insane Performance Data Length\n"); return -1; } perf = (unsigned char *)malloc(len+=8); if (perf == NULL) fprintf (stderr,":-( memory exhausted\n"), exit(FATAL_START(ENOMEM)); if (len == sizeof(d)) memcpy (perf,d,sizeof(d)); else { unsigned int n=(len-8)/16; cmd[0]=0xAC; // GET PERFORMANCE cmd[8]=n>>8; cmd[9]=n; // real number of descriptors cmd[10]=0x3; // ask for "Write Speed Descriptor" cmd[11]=0; if ((err=cmd.transport(READ,perf,len))) sperror ("GET PERFORMANCE",err), exit (FATAL_START(errno)); } int targetv=0,errp=0; do { memset (d,0,sizeof(d)); cmd[0]=0xAC; // GET PERFORMANCE cmd[1]=4; // ask for "Overall Write performance" cmd[9]=1; cmd[10]=0; // ask for descriptor in effect cmd[11]=0; if (errp || (errp=cmd.transport(READ,d,sizeof(d)))) #if 0 sperror ("GET CURRENT PERFORMANCE",errp), exit (FATAL_START(errno)); // well, if it passed above, we // expect it to pass this too... #else // Pioneer doesn't report current speed through GET PERFORMANCE:-( { emulated_err: if (page2A == NULL) return -1; unsigned int plen,hlen; plen = (page2A[0]<<8|page2A[1]) + 2; hlen = 8 + (page2A[6]<<8|page2A[7]); unsigned char * const p = page2A + hlen; if (plen<(hlen+32) || p[1]<(32-2)) return -1; // well, SET CD SPEED wouldn't work... velocity = p[28]<<8|p[29]; } #endif else if ((errp = pull_velocity (cmd,d)) < 0) goto emulated_err; if (speed_factor != 0.0) { int i,j=len-8,v,v0,v1,minv,closesti,closestv=0; unsigned char *wsdp=perf+8; for (minv=0x7fffffff,i=0;i(15*_1x)) speed_factor /= 16.0; else if (minv>(11*_1x)) speed_factor /= 12.0; else if (minv>(7*_1x)) speed_factor /= 8.0; else if (minv>(5*_1x)) speed_factor /= 6.0; else if (minv>(3*_1x)) speed_factor /= 4.0; else if (dvdplus && minv>(2*DVD_1X)) // 2.4x is like 1x for DVD+, but it turned out that there're // units, most notably "DVDRW IDE1004," burning DVD+ at // lower speed, so I have to watch out for it... speed_factor /= 2.4; else if (minv>=(2*_1x)) speed_factor /= 2.0; v0=(int)(speed_factor*minv + 0.5); for (closesti=0,minv=0x7fffffff,i=0;i>24; pd[9]=cap>>16; pd[10]=cap>>8; pd[11]=cap; #endif memcpy(pd+12,wsdp+closesti+8,4); // copy "Read Speed" memcpy(pd+20,wsdp+closesti+12,4); // copy "Write Speed" pd[18]=pd[26]=1000>>8; // set both "Read Time" and pd[19]=pd[27]=1000&0xFF; // "Write Time" to 1000ms cmd[0]=0xB6; // SET STREAMING cmd[10]=sizeof(pd); cmd[11]=0; if ((err=cmd.transport (WRITE,pd,sizeof(pd)))) sperror ("SET STREAMING",err), exit (FATAL_START(errno)); // I pull the Page 2A in either case, because unit might // have provided irrelevant information (Plextor). Not to // mention cases when it simply failed to reply to GET // CURRENT PERFORMANCE (Pioneer, LG). unsigned int plen = (page2A[0]<<8|page2A[1]) + 2; cmd[0] = 0x5A; // MODE SENSE cmd[1] = 0x08; // "Disable Block Descriptors" cmd[2] = 0x2A; // "Capabilities and Mechanical Status" cmd[7] = plen>>8; cmd[8] = plen; // real length this time cmd[9] = 0; if ((err=cmd.transport(READ,page2A,plen))) sperror ("MODE SENSE#2A",err), exit(FATAL_START(errno)); } else if (targetv) { if (targetv!=velocity) { if (errp==0) // check Page 2A for speed then... { errp=-1; continue; } if (velocity==0 || velocity>=_1x) { fprintf(stderr,":-? Failed to change write speed: %d->%d\n", (int)velocity,targetv); if (velocity%d\n", (int)velocity,targetv); exit (FATAL_START(EINVAL)); } } break; } } while (targetv); return targetv; } static int set_speed_BBh (Scsi_Command &cmd,unsigned char *page2A) { unsigned int plen,hlen; int err; plen = (page2A[0]<<8|page2A[1]) + 2; hlen = 8 + (page2A[6]<<8|page2A[7]); unsigned char * const p = page2A + hlen; if (plen<(hlen+32) || p[1]<(32-2)) return -1; int targetv=0; do { velocity = p[28]<<8|p[29]; if (speed_factor != 0.0) { int i,j,v,v0,v1,minv,closesti,closestv=0; unsigned char *wsdp=p+32; j=(p[30]<<8|p[31])*4; for (minv=0x7fffffff,i=0;i>8; cmd[8] = plen; // real length this time cmd[9] = 0; if ((err=cmd.transport(READ,page2A,plen))) sperror ("MODE SENSE#2A",err), exit(FATAL_START(errno)); } else if (targetv) { if (targetv!=velocity) fprintf (stderr,":-( Failed to change write speed: %d->%d\n", (int)velocity,targetv), exit (FATAL_START(EINVAL)); break; } } while (targetv); return targetv; } static int plusminus_pages_setup (Scsi_Command &cmd,int profile) { unsigned int plen,hlen,dvddash=0,dvdplus=0; unsigned char header[16],p32; int err; class autofree page2A; switch (profile) { case 0x1A: // DVD+RW p32 = 0; dvdplus=1; break; case 0x1B: // DVD+R // // Even though MMC-4 draft explicitely exempts DVD+RW/+R media // from those being affected by Page 05h settings, some // firmwares apparently pay attention to Multi-session flag // when finalizing DVD+R media. Well, we probably can't blame // vendors as specification is still a work in progress, not to // mention that it was published after DVD+R was introduced to // the market. // p32 = dvd_compat?0x02:0xC0; dvdplus=1; break; case 0x13: // DVD-RW Restricted Overwrite p32 = 0xC0; // NB! Test Write in not option here dvddash = 1; break; case 0x11: // DVD-R Sequential case 0x14: // DVD-RW Sequential case 0x15: // DVD-R Dual Layer Sequential p32 = 0xC0; if (next_track==1) do { if (dvd_compat >= (profile==0x14?2:256)) { is_dao = 1, fprintf (stderr,"%s: engaging DVD-%s DAO upon user request...\n", ioctl_device,profile==0x14?"RW":"R"); break; } // Try to figure out if we have to go for DAO... cmd[0] = 0x46; // GET CONFIGURATION cmd[1] = 2; // ask for the only feature... cmd[3] = 0x21; // the "Incremental Streaming Writable" one cmd[8] = 16; // The feature should be there, right? cmd[9] = 0; if ((err=cmd.transport (READ,header,16))) sperror ("GET FEATURE 21h",err), exit(FATAL_START(errno)); hlen = header[0]<<24|header[1]<<16|header[2]<<8|header[3]; // See if Feature 21h is "current," if not, engage DAO... if (hlen>=12 && (header[8+2]&1)==0) { is_dao = dvd_compat = 1; fprintf (stderr,"%s: FEATURE 21h is not on, engaging DAO...\n", ioctl_device); break; } } while (0); if (is_dao) p32 |=2; // DAO if (profile==0x15 || // DVD-R DL Seq has no notion of multi-session dvd_compat) p32 &= 0x3F; // Single-session if (test_write) p32 |= 0x10; // Test Write for debugging purposes dvddash=1; break; case 0x16: // DVD-R Dual Layer Jump p32 = 0xC0; p32 |= 4; // Jump if (test_write) p32 |= 0x10; // Test Write for debugging purposes dvddash=1; break; default: p32 = 0; break; } if (dvddash || p32) page05_setup (cmd,profile,p32); page2A = pull_page2A (cmd); plen = (page2A[0]<<8|page2A[1]) + 2; hlen = 8 + (page2A[6]<<8|page2A[7]); unsigned char * const p = page2A + hlen; if (plen<(hlen+14) || p[1]<(14-2)) // no "Buffer Size Supported" buf_size = 512; // bogus value else buf_size = (p[12]<<8|p[13]); if (buf_size<512) // bogus value buf_size = 512; // GET PERFORMANCE/SET STREAMING are listed as mandatory, so that // this is actually the most likely path... if (set_speed_B6h (cmd,dvdplus,page2A) >= 0) goto opc; if (plen<(hlen+30) || p[1]<(30-2)) // no "Current Write Speed" present { if (dvddash) { fprintf (stderr,":-( \"Current Write Speed\" descriptor " "is not present, faking 1x...\n"), velocity = _1x; speed_factor = 0.0; } else velocity = 0; } else do { velocity = p[28]<<8|p[29]; // Some units, most notably NEC and derivatives, were observed // to report CD-R descriptors... if (velocity < DVD_1X) // must be bogus { velocity=0; break; } if (plen<(hlen+32) || p[1]<(32-2)) // no write descriptors break; int n = p[30]<<8|p[31],minv=1<<16; unsigned char *p_= p+32; for (;n--;p_+=4) { int v=p_[2]<<8|p_[3]; if (v==0) continue; // Lite-On returns zeros here... if (v<_1x) // must be bogus velocity=0; if (v < minv) minv = v; } if (dvdplus && minv>2*_1x) // See corresponding comment in set_speed_B6h() speed_factor /= 2.4; } while (0); if (speed_factor != 0.0 && (velocity==0 || set_speed_BBh (cmd,page2A)<0) ) fprintf (stderr,":-( can't control writing velocity, " "-speed option is ignored\n"); opc: if (!dvddash || test_write || no_opc) return 0; // See if OPC is required... cmd[0] = 0x51; // READ DISC INFORMATION cmd[8] = 8; cmd[9] = 0; if ((err=cmd.transport (READ,header,8))) sperror ("READ DISC INFORMATION",err), exit(FATAL_START(errno)); if ((header[0]<<8|header[1]) <= 0x20) { cmd[0] = 0x54; // SEND OPC INFORMATION cmd[1] = 1; // "Perform OPC" cmd[9] = 0; cmd.timeout(120); // NEC units can be slooo...w if ((err=cmd.transport())) do { if (err==0x17301) // "POWER CALIBRATION AREA ALMOST FULL" { fprintf (stderr,":-! WARNING: Power Calibration Area " "is almost full\n"); break; } sperror ("PERFORM OPC",err); if ((err&0x0FF00)==0x07300 && (err&0xF0000)!=0x10000) exit(FATAL_START(errno)); /* * The rest of errors are ignored, most notably observed: * 0x52000, "INVALID COMMAND" Panasonic LD-F321 * 0x52700, "WRITE PROTECTED" Plextor PX-712A * 0x52C00, "COMMAND SEQUENCE ERROR" Plextor PX-716UF * 0x53002, "MEDIA NOT SUPPORTED" Toshiba TS-H542A */ } while (0); } handle_events(cmd); return 0; } static int minus_rw_quickgrow (Scsi_Command &cmd,off64_t size) { unsigned char format[12]; unsigned int lead_out,blocks,type; int i,len,err; type = formats[4+4]&3; if (type==2 && size!=0) { blocks = (unsigned int)(size/2048); blocks += 15, blocks &= ~15; lead_out = 0; lead_out |= formats[4+0], lead_out <<= 8; lead_out |= formats[4+1], lead_out <<= 8; lead_out |= formats[4+2], lead_out <<= 8; lead_out |= formats[4+3]; if (blocks<=lead_out) // no need to grow the session... return 0; } // look for Quick Grow descriptor... for (i=8,len=formats[3];i>2) == 0x13) break; if (i==len) // no Quick Grow descriptor { if (type != 2) quickgrown=1; // in reality quick formatted... return 0; } else { blocks = 0; blocks |= formats[i+0], blocks <<= 8; blocks |= formats[i+1], blocks <<= 8; blocks |= formats[i+2], blocks <<= 8; blocks |= formats[i+3]; if (type==2 && blocks==0) // nowhere no grow... return 0; } quickgrown=1; fprintf (stderr,"%s: \"Quick Grow\" session...\n",ioctl_device); memset (format,0,sizeof(format)); format [1] = 2; // "IMMED" format [3] = 8; // "Length" format [8] = 0x13<<2; // "Quick Grow" format [11] = 16; cmd[0] = 0x4; // FORMAT UNIT cmd[1] = 0x11; cmd[5] = 0; if ((err=cmd.transport (WRITE,format,sizeof(format)))) { sperror ("QUICK GROW",err); return 1; } return wait_for_unit (cmd); } static int minus_r_reserve_track (Scsi_Command &cmd,off64_t size) { int err; unsigned int blocks; blocks = (unsigned int)(size/2048); if (is_dao) dao_blocks = blocks; else blocks += 15, blocks &= ~15; fprintf (stderr,"%s: reserving %u blocks",ioctl_device,blocks); if (is_dao && blocks<380000) fprintf (stderr,"\b, warning for short DAO recording"), poll (NULL,0,5000); fprintf (stderr,"\n"); cmd[0] = 0x53; // RESERVE TRACK cmd[5] = blocks>>24; cmd[6] = blocks>>16; cmd[7] = blocks>>8; cmd[8] = blocks; cmd[9] = 0; if ((err=cmd.transport ())) { sperror ("RESERVE TRACK",err); return 1; } return 0; } static void plus_r_dl_split (Scsi_Command &cmd,off64_t size) { int err; unsigned int blocks,split; unsigned char dvd_20[4+8]; cmd[0] = 0xAD; // READ DVD STRUCTURE cmd[7] = 0x20; // "DVD+R Double Layer Boundary Information" cmd[9] = sizeof(dvd_20); cmd[11] = 0; if ((err=cmd.transport(READ,dvd_20,sizeof(dvd_20)))) sperror ("READ DVD+R DL BOUNDARY INFORMATION",err), exit (FATAL_START(errno)); if ((dvd_20[0]<<8|dvd_20[1]) < 10) fprintf (stderr,":-( insane DVD+R DL BI structure length\n"), exit (FATAL_START(EINVAL)); if (dvd_20[4]&0x80) { fprintf (stderr,":-? L0 Data Zone Capacity is set already\n"); return; } split = dvd_20[8]<<24|dvd_20[9]<<16|dvd_20[10]<<8|dvd_20[11]; blocks = (unsigned int)(size/2048); blocks += 15, blocks &= ~15; if (blocks <= split) fprintf (stderr,":-( more than 50%% of space will be *wasted*!\n" " use single layer media for this recording\n"), exit (FATAL_START(EMEDIUMTYPE)); blocks /= 16; blocks += 1; blocks /= 2; blocks *= 16; fprintf (stderr,"%s: splitting layers at %u blocks\n", ioctl_device,blocks); memset (dvd_20,0,sizeof(dvd_20)); dvd_20[1] = sizeof(dvd_20)-2; dvd_20[8] = blocks>>24; dvd_20[9] = blocks>>16; dvd_20[10] = blocks>>8; dvd_20[11] = blocks; cmd[0] = 0xBF; // SEND DVD STRUCTURE cmd[7] = 0x20; // "DVD+R Double Layer Recording Information" cmd[9] = sizeof(dvd_20); cmd[11] = 0; cmd.timeout(180); // Lite-on seems to fire off calibration here if ((err=cmd.transport(WRITE,dvd_20,sizeof(dvd_20)))) sperror ("SEND DVD+R DOUBLE LAYER RECORDING INFORMATION",err), exit (FATAL_START(errno)); } static int flush_cache (Scsi_Command &cmd, int even_sync=1) { int err; cmd[0] = 0x35; // FLUSH CACHE cmd[1] = 0x02; // "IMMED" cmd[9] = 0; if (!(err=cmd.transport())) wait_for_unit (cmd); else sperror ("FLUSH CACHE",err); if (even_sync) // Pioneer apparently needs this, { // non-IMMED FLUSH that is... cmd[0] = 0x35; // FLUSH CACHE cmd[9] = 0; if (is_dao) cmd.timeout (15*60); if ((err=cmd.transport())) { sperror ("SYNCHRONOUS FLUSH CACHE",err); return err; } } return 0; } // // atexit/signal handlers // extern "C" void no_r_finalize () { while (media_written) { Scsi_Command cmd(ioctl_handle); fprintf (stderr,"%s: flushing cache\n",ioctl_device); if (flush_cache (cmd)) break; pioneer_stop (cmd); media_written = 0; errno = EINTR; } _exit (errno); } extern "C" void plus_rw_finalize () { int saved_errno = errno; sigs_mask(SIGS_BLOCK); while (media_written) { Scsi_Command cmd(ioctl_handle); int err; fprintf (stderr,"%s: flushing cache\n",ioctl_device); if (flush_cache (cmd)) break; if (!dvd_compat) { fprintf (stderr,"%s: stopping de-icing\n",ioctl_device); cmd[0] = 0x5B; // CLOSE TRACK/SESSION cmd[1] = 0x01; // "IMMED" cmd[2] = 0; // "Stop De-Icing" cmd[9] = 0; if ((err=cmd.transport())) sperror ("STOP DE-ICING",err); if (wait_for_unit (cmd)) break; } fprintf (stderr,"%s: writing lead-out\n",ioctl_device); cmd[0] = 0x5B; // CLOSE TRACK/SESSION cmd[1] = 0x01; // "IMMED" cmd[2] = 0x02; // "Close session" cmd[9] = 0; if ((err=cmd.transport())) sperror ("CLOSE SESSION",err); if (wait_for_unit (cmd)) break; media_written = 0; next_wr_addr = 0; if (do_reload && media_reload()) perror (":-( unable to reload tray");// not actually reached errno = saved_errno; } _exit (errno); } extern "C" void bd_re_finalize () { int saved_errno = errno; sigs_mask(SIGS_BLOCK); while (media_written) { Scsi_Command cmd(ioctl_handle); fprintf (stderr,"%s: flushing cache\n",ioctl_device); if (flush_cache (cmd)) break; media_written = 0; next_wr_addr = 0; if (do_reload && media_reload()) perror (":-( unable to reload tray");// not actually reached errno = saved_errno; } _exit (errno); } extern "C" void plus_r_finalize () { int saved_errno = errno; sigs_mask(SIGS_BLOCK); while (media_written) { Scsi_Command cmd(ioctl_handle); int mode,err; fprintf (stderr,"%s: flushing cache\n",ioctl_device); if (flush_cache(cmd)) break; fprintf (stderr,"%s: closing track\n",ioctl_device); cmd[0] = 0x5B; // CLOSE TRACK/SESSION cmd[1] = 0x01; // "IMMED" cmd[2] = 0x01; // "Close Track" cmd[4] = next_track>>8; cmd[5] = next_track; cmd[9] = 0; if ((err=cmd.transport())) sperror ("CLOSE TRACK",err), saved_errno = errno; if (wait_for_unit (cmd)) break; if (dvd_compat) // HP dvd520n insists on "finalize with minimum radius" // with DVD+R DL ---------------vv mode = ((mmc_profile&0xFFFF)==0x2B)?0x05:0x06, fprintf (stderr,"%s: closing disc\n",ioctl_device); else mode = 0x02, fprintf (stderr,"%s: closing session\n",ioctl_device); while (1) // Pioneer DVR-K04RA { cmd[0] = 0x5B; // CLOSE TRACK/SESSION cmd[1] = 0x01; // "IMMED" cmd[2] = mode; // "Close session" cmd[9] = 0; err = cmd.transport(); if (err == 0x20407) // "OP IN PROGRESS" { poll (NULL,0,stdnap); continue; } else if (err) sperror (dvd_compat?"CLOSE DISC":"CLOSE SESSION",err), saved_errno = errno; break; } if (wait_for_unit (cmd)) break; media_written = 0; if (do_reload && media_reload()) perror (":-( unable to reload tray");// not actually reached errno = saved_errno; } _exit (errno); } extern "C" void minus_rw_finalize () { int saved_errno = errno; sigs_mask(SIGS_BLOCK); while (media_written) { Scsi_Command cmd(ioctl_handle); int err; fprintf (stderr,"%s: flushing cache\n",ioctl_device); if (flush_cache(cmd)) break; if (quickgrown) { fprintf (stderr,"%s: writing lead-out\n",ioctl_device); cmd[0] = 0x5B; // CLOSE TRACK/SESSION cmd[1] = 0x01; // "IMMED" cmd[2] = 0x02; // "Close Session" cmd[9] = 0; if ((err=cmd.transport())) sperror ("CLOSE SESSION",err), saved_errno = errno; if (wait_for_unit (cmd)) break; quickgrown=0; } media_written = 0; next_wr_addr = 0; if (do_reload) { if (media_reload()) perror (":-( unable to reload tray");// not actually reached } else return; // Restricted Overwrite is just ugly! errno = saved_errno; } _exit (errno); } extern "C" void minus_r_finalize () { int saved_errno = errno; sigs_mask(SIGS_BLOCK); while (media_written) { Scsi_Command cmd(ioctl_handle); int err; fprintf (stderr,"%s: flushing cache\n",ioctl_device); if (flush_cache(cmd,!is_dao)) break; if (!is_dao) { fprintf (stderr,"%s: updating RMA\n",ioctl_device); cmd[0] = 0x5B; // CLOSE TRACK/SESSION cmd[1] = 0x01; // "IMMED" cmd[2] = 0x01; // "Close Track" cmd[4] = next_track>>8; cmd[5] = next_track; cmd[9] = 0; if ((err=cmd.transport())) sperror ("CLOSE TRACK",err), saved_errno = errno; if (wait_for_unit (cmd)) break; fprintf (stderr,"%s: closing %s\n",ioctl_device, dvd_compat?"disc":"session"); while (1) // Pioneer DVR-K04RA { cmd[0] = 0x5B; // CLOSE TRACK/SESSION cmd[1] = 0x01; // "IMMED" cmd[2] = 0x02; // "Close Session" cmd[9] = 0; err = cmd.transport(); if (err == 0x20407) // "OP IN PROGRESS" { poll (NULL,0,stdnap); continue; } else if (err) sperror (dvd_compat?"CLOSE DISC":"CLOSE SESSION",err), saved_errno = errno; break; } if (wait_for_unit (cmd)) break; } media_written = 0; if (do_reload && media_reload()) perror (":-( unable to reload tray");// not actually reached errno = saved_errno; } _exit (errno); } extern "C" void bd_r_finalize () { int saved_errno = errno; sigs_mask(SIGS_BLOCK); while (media_written) { Scsi_Command cmd(ioctl_handle); int err; fprintf (stderr,"%s: flushing cache\n",ioctl_device); if (flush_cache(cmd)) break; fprintf (stderr,"%s: closing track\n",ioctl_device); cmd[0] = 0x5B; // CLOSE TRACK/SESSION cmd[1] = 0x01; // "IMMED" cmd[2] = 0x01; // "Close Track" cmd[4] = next_track>>8; cmd[5] = next_track; cmd[9] = 0; if ((err=cmd.transport())) sperror ("CLOSE TRACK",err), saved_errno = errno; if (wait_for_unit (cmd)) break; if (!bdr_plus_pow) { fprintf (stderr,"%s: closing session\n",ioctl_device); while (1) // Pioneer DVR-K04RA { cmd[0] = 0x5B; // CLOSE TRACK/SESSION cmd[1] = 0x01; // "IMMED" cmd[2] = 0x02; // "Close session" cmd[9] = 0; err = cmd.transport(); if (err == 0x20407) // "OP IN PROGRESS" { poll (NULL,0,stdnap); continue; } else if (err) sperror ("CLOSE SESSION",err), saved_errno = errno; break; } if (wait_for_unit (cmd)) break; } media_written = 0; if (do_reload && media_reload()) perror (":-( unable to reload tray");// not actually reached errno = saved_errno; } _exit (errno); } extern "C" void ram_reload () { int saved_errno = errno; sigs_mask(SIGS_BLOCK); if (media_written && do_reload) media_reload(); _exit (saved_errno); } // // poor_mans_setup takes care of a lot of things. // It's invoked right before first write and if necessary/applicable // prepares Page 05, reserves track, sets up atexit and signal handlers. // typedef ssize_t (*pwrite64_t)(int,const void *,size_t,off64_t); extern "C" pwrite64_t poor_mans_setup (void *fd,off64_t leadout) { Scsi_Command cmd(ioctl_handle=fd); int err,profile=mmc_profile&0xFFFF; // We might have loaded media ourselves, in which case we // should lock the door... cmd[0] = 0x1E; // PREVENT/ALLOW MEDIA REMOVAL cmd[4] = 1; // "Prevent" cmd[5] = 0; if ((err=cmd.transport ())) sperror ("PREVENT MEDIA REMOVAL",err), exit (FATAL_START(errno)); handle_events(cmd); // This actually belongs in get_mmc_profile, but the trouble // is that Mac OS X does not implement ReadCapacity method and // therefore we have to do it here, after we went exclusive... if (!(mmc_profile&0x10000)) // non-blank media { unsigned char c[8]; cmd[0] = 0x25; // READ CAPACITY cmd[9] = 0; if (!cmd.transport (READ,c,sizeof(c))) cap2kstart = c[0]<<24|c[1]<<16|c[2]<<8|c[3]; } switch (profile) { case 0x1A: // DVD+RW switch (disc_info[7]&3) // Background formatting { case 0: // blank plus_rw_format (cmd); break; case 1: // suspended plus_rw_restart_format (cmd,leadout); break; case 2: // in progress case 3: // complete break; } plusminus_pages_setup (cmd,profile); atexit (plus_rw_finalize); atsignals (plus_rw_finalize); break; case 0x1B: // DVD+R case 0x2B: // DVD+R Double Layer plusminus_pages_setup(cmd,profile); if (profile==0x2B && next_track==1 && dvd_compat && leadout) plus_r_dl_split (cmd,leadout); atexit (plus_r_finalize); if (next_wr_addr) { atsignals (no_r_finalize); next_wr_addr=(unsigned int)-1; } else atsignals (plus_r_finalize); break; case 0x13: // DVD-RW Restricted Overwrite plusminus_pages_setup (cmd,profile); // // A side note. Quick Grow can't be performed earlier, // as then reading is not possible. // minus_rw_quickgrow (cmd,leadout); atexit (minus_rw_finalize); atsignals (minus_rw_finalize); break; case 0x11: // DVD-R Sequential case 0x14: // DVD-RW Sequential case 0x15: // DVD-R Dual Layer Sequential plusminus_pages_setup (cmd,profile); if ((profile==0x15) && leadout) { unsigned char dvd_10[4+16]; unsigned int layer_size,data_size=(unsigned int)(leadout/2048); cmd[0] = 0xAD; // READ DVD STRUCTURE cmd[7] = 0x10; cmd[9] = sizeof(dvd_10); cmd[11] = 0; if ((err=cmd.transport(READ,dvd_10,sizeof(dvd_10)))) sperror("READ DVD STRUCTURE #10h",err), exit(FATAL_START(errno)); layer_size = dvd_10[4+13]<<16, layer_size |= dvd_10[4+14]<<8, layer_size |= dvd_10[4+15]; if (data_size <= layer_size) fprintf (stderr,":-( more than 50%% of space will be *wasted*!\n" " use single layer media for this recording\n"), exit(FATAL_START(EMEDIUMTYPE)); } if (is_dao && leadout) minus_r_reserve_track(cmd,leadout); atexit (minus_r_finalize); if (next_wr_addr) { atsignals (no_r_finalize); next_wr_addr=(unsigned int)-1; } else atsignals (minus_r_finalize); break; case 0x12: // DVD-RAM // exclusively for speed settings... plusminus_pages_setup (cmd,profile); atexit (ram_reload); atsignals (ram_reload); break; case 0x41: // BD-R SRM if ((disc_info[2]&3) == 0) // blank bd_r_format (cmd); // exclusively for speed settings... plusminus_pages_setup (cmd,profile); atexit (bd_r_finalize); if (next_wr_addr) { atsignals (no_r_finalize); next_wr_addr=(unsigned int)-1; } else atsignals (bd_r_finalize); break; case 0x43: // BR-RE if ((disc_info[2]&3) == 0) // blank bd_re_format (cmd); // exclusively for speed settings... plusminus_pages_setup (cmd,profile); atexit(bd_re_finalize); atsignals(bd_re_finalize); break; case 0x42: // BD-R RRM is not supported yet... case 0x16: // DVD-R Dual Layer Jump not supported for reasons // discussed at web-page... default: fprintf (stderr,":-( mounted media[%X] is not supported\n",profile), exit(FATAL_START(EMEDIUMTYPE)); break; } if (velocity) fprintf (stderr,"%s: \"Current Write Speed\" is %.1fx%dKBps.\n", ioctl_device,velocity/(double)_1x,_1x); if (next_wr_addr==(unsigned int)-1) do { unsigned char track[32]; next_wr_addr = 0; cmd[0] = 0x52; // READ TRACK INFORMATION cmd[1] = 1; cmd[4] = next_track>>8; cmd[5] = next_track&0xFF; // last track, set up earlier cmd[8] = sizeof(track); cmd[9] = 0; if (cmd.transport (READ,track,sizeof(track))) break; if (track[7]&1) // NWA_V { next_wr_addr = track[12]<<24; next_wr_addr |= track[13]<<16; next_wr_addr |= track[14]<<8; next_wr_addr |= track[15]; } if (next_wr_addr != // track start (unsigned int)(track[8]<<24|track[9]<<16|track[10]<<8|track[11])) fprintf (stderr,":-? resuming track#%d from LBA#%d\n", next_track,next_wr_addr); } while (0); else next_wr_addr = 0; return poor_mans_pwrite64; } extern "C" int poor_man_rewritable (void *fd, void *buf) { Scsi_Command cmd(fd); int err,profile=mmc_profile&0xFFFF; if (profile!=0x13 && // not DVD-RW Restricted Overwrite profile!=0x12 && // nor DVD-RAM profile!=0x1A && // nor DVD+RW profile!=0x2A && // nor DVD+RW Double Layer !(profile==0x41 && bdr_plus_pow) && // nor BD-R SRM+POW profile!=0x42 && // nor BD-R RRM profile!=0x43) // nor BD-RE return 0; if (profile==0x13) // DVD-RW Restricted Overwrite { // Yet another restriction of Restricted Overwrite mode? // Pioneer DVR-x05 can't read a bit otherwise... do_reload=0; minus_rw_finalize (); // with do_reload==0! do_reload=1; } else if (profile==0x1A || // DVD+RW profile==0x2A || // DVD+RW Double Layer (profile&=0xF0)==0x40) // BD-R[E] { fprintf (stderr,"%s: flushing cache\n",ioctl_device); if (flush_cache(cmd)) exit (errno); } cmd[0] = 0x28; // READ(10) cmd[5] = 16; // LBA#16, volume descriptor set cmd[8] = 16; // 32KB cmd[9] = 0; if ((err=cmd.transport (READ,buf,16*2048))) sperror ("READ@LBA=10h",err), exit(errno); return 1; } dvd+rw-tools-7.1/dvd+rw-format.cpp0000644000000100000010000005714110762241234015176 0ustar binbin/* * BD/DVD±RW/-RAM format 7.1 by Andy Polyakov . * * Use-it-on-your-own-risk, GPL bless... * * For further details see http://fy.chalmers.se/~appro/linux/DVD+RW/. * * Revision history: * * 2.0: * - deploy "IMMED" bit in "FORMAT UNIT"; * 2.1: * - LP64 fix; * - USB workaround; * 3.0: * - C++-fication for better portability; * - SYSV signals for better portability; * - -lead-out option for improved DVD+RW compatibility; * - tested with SONY DRU-500A; * 4.0: * - support for DVD-RW formatting and blanking, tool name becomes * overloaded... * 4.1: * - re-make it work under Linux 2.2 kernel; * 4.2: * - attempt to make DVD-RW Quick Format truly quick, upon release * is verified to work with Pioneer DVR-x05; * - media reload is moved to growisofs where is actually belongs; * 4.3: * - -blank to imply -force; * - reject -blank in DVD+RW context and -lead-out in DVD-RW; * 4.4: * - support for -force=full in DVD-RW context; * - ask unit to perform OPC if READ DISC INFORMATION doesn't return * any OPC descriptors; * 4.5: * - increase timeout for OPC, NEC multi-format derivatives might * require more time to fulfill the OPC procedure; * 4.6: * - -force to ignore error from READ DISC INFORMATION; * - -force was failing under FreeBSD with 'unable to unmount'; * - undocumented -gui flag to ease progress indicator parsing for * GUI front-ends; * 4.7: * - when formatting DVD+RW, Pioneer DVR-x06 remained unaccessible for * over 2 minutes after dvd+rw-format exited and user was frustrated * to poll the unit himself, now dvd+rw-format does it for user; * 4.8: * - DVD-RW format fails if preceeded by dummy recording; * - make sure we talk to MMC unit, be less intrusive; * - unify error reporting; * - permit for -lead-out even for blank DVD+RW, needed(?) for SANYO * derivatives; * 4.9: * - permit for DVD-RW blank even if format descriptors are not present; * 4.10: * - add support for DVD-RAM; * 6.0: * - versioning harmonization; * - WIN32 port; * - Initial DVD+RW Double Layer support; * - Ignore "WRITE PROTECTED" error in OPC; * 6.1: * - ± localization; * - Treat only x73xx OPC errors as fatal; * 7.0: * - Blu-ray Disc support; * - Mac OS X 10>=2 support; * 7.1: * - refine x73xx error treatment; */ #include #include #include #if defined(__unix) || defined(__unix__) #include #include #include #include #include #include #include #include #endif #include "transport.hxx" static void usage (char *prog) { fprintf (stderr,"- usage: %s [-force[=full]] [-lead-out|-blank[=full]]\n" " [-ssa[=none|default|max|XXXm]] /dev/dvd\n",prog), exit(1); } #ifdef _WIN32 #include # if defined(__GNUC__) # define __shared__ __attribute__((section(".shared"),shared)) # elif defined(_MSC_VER) # define __shared__ # pragma data_seg(".shared") # pragma comment(linker,"/section:.shared,rws") # endif static volatile int shared_progress_indicator __shared__ = 0; # if defined(_MSC_VER) # pragma data_seg() # endif static volatile int *progress = &shared_progress_indicator; static HANDLE Process = NULL; BOOL WINAPI GoBackground (DWORD code) { if (*progress) { FreeConsole(); /* detach from console upon ctrl-c or window close */ return TRUE; } return FALSE; } void KillForeground (void) { fprintf(stderr,"\n"); TerminateProcess(Process,0); } #else static volatile int *progress; #endif static const char *gui_action=NULL; static const char *str = "|/-\\",*backspaces="\b\b\b\b\b\b\b\b\b\b"; #if defined(__unix) || defined(__unix__) extern "C" void alarm_handler (int no) { static int i=0,len=1,old_progress=0; int new_progress = *progress; if (gui_action) { fprintf (stderr,"* %s %.1f%%\n",gui_action, 100.0*new_progress/65536.0); alarm(3); return; } if (new_progress != old_progress) len = fprintf (stderr,"%.*s%.1f%%",len,backspaces, 100.0*new_progress/65536.0) - len, old_progress = new_progress; else fprintf (stderr,"\b%c",str[i]), i++, i&=3; alarm(1); } #endif int main (int argc, char *argv[]) { unsigned char formats[260],dinfo[32],inq[128]; char *dev=NULL,*p; unsigned int capacity,lead_out,mmc_profile,err; int len,i; int force=0,full=0,compat=0,blank=0,ssa=0,do_opc=0,gui=0, blu_ray=0,not_pow=0; #ifdef _WIN32 DWORD ppid; char filename[MAX_PATH],*progname; /* * Foreground process spawns itself and simply waits for shared * progress indicator to increment... */ if (sscanf(argv[0],":%u:",&ppid) != 1) { int i=0,len=1,old_progress=0,new_progress; sprintf (filename,":%u:",GetCurrentProcessId()); progname = argv[0]; argv[0] = filename; Process = (HANDLE)_spawnv (_P_NOWAIT,progname,argv); if (Process == (HANDLE)-1) perror("_spawnv"), ExitProcess(errno); while(1) { if (WaitForSingleObject(Process,1000) == WAIT_OBJECT_0) { ppid = 0; /* borrow DWORD variable */ GetExitCodeProcess(Process,&ppid); ExitProcess(ppid); } new_progress = *progress; if (new_progress != old_progress) len = fprintf (stderr,"%.*s%.1f%%",len,backspaces, 100.0*new_progress/65536.0) - len, old_progress = new_progress; else if (new_progress) fprintf (stderr,"\b%c",str[i]), i++, i&=3; } } /* * ... while background process does *all* the job... */ Process = OpenProcess (PROCESS_TERMINATE,FALSE,ppid); if (Process == NULL) perror("OpenProcess"), ExitProcess(errno); atexit (KillForeground); SetConsoleCtrlHandler (GoBackground,TRUE); GetModuleFileName (NULL,filename,sizeof(filename)); progname = strrchr(filename,'\\'); if (progname) argv[0] = progname+1; else argv[0] = filename; #elif defined(__unix) || defined(__unix__) pid_t pid; { int fd; char *s; if ((fd=mkstemp (s=strdup("/tmp/dvd+rw-format.XXXXXX"))) < 0) fprintf (stderr,":-( unable to mkstemp(\"%s\")",s), exit(1); ftruncate(fd,sizeof(*progress)); unlink(s); progress = (int *)mmap(NULL,sizeof(*progress),PROT_READ|PROT_WRITE, MAP_SHARED,fd,0); close (fd); if (progress == MAP_FAILED) perror (":-( unable to mmap anonymously"), exit(1); } *progress = 0; if ((pid=fork()) == (pid_t)-1) perror (":-( unable to fork()"), exit(1); if (pid) { struct sigaction sa; sigaction (SIGALRM,NULL,&sa); sa.sa_flags &= ~SA_RESETHAND; sa.sa_flags |= SA_RESTART; sa.sa_handler = alarm_handler; sigaction (SIGALRM,&sa,NULL); alarm(1); while ((waitpid(pid,&i,0) != pid) && !WIFEXITED(i)) ; if (WEXITSTATUS(i) == 0) fprintf (stderr,"\n"); exit (0); } #endif fprintf (stderr,"* BD/DVD%sRW/-RAM format utility by , " "version 7.1.\n",plusminus_locale()); for (i=1;i='0' && p[1]<='9')) { char *s=NULL; double a=strtod(p+1,&s); if (s) { if (*s=='g' || *s=='G') a *= 1e9; else if (*s=='m' || *s=='M') a *= 1e6; else if (*s=='k' || *s=='K') a *= 1e3; } ssa = (int)(a/2e3); // amount of 2K } else ssa=0; // invalid? } } else if (argv[i][1] == 'g') gui=1; else usage(argv[0]); #ifdef _WIN32 else if (argv[i][1] == ':') #else else if (*argv[i] == '/') #endif dev = argv[i]; else usage (argv[0]); } if (dev==NULL) usage (argv[0]); Scsi_Command cmd; if (!cmd.associate(dev)) fprintf (stderr,":-( unable to open(\"%s\"): ",dev), perror (NULL), exit(1); cmd[0] = 0x12; // INQUIRY cmd[4] = 36; cmd[5] = 0; if ((err=cmd.transport(READ,inq,36))) sperror ("INQUIRY",err), exit (1); if ((inq[0]&0x1F) != 5) fprintf (stderr,":-( not an MMC unit!\n"), exit (1); cmd[0] = 0x46; // GET CONFIGURATION cmd[8] = 8; cmd[9] = 0; if ((err=cmd.transport(READ,formats,8))) sperror ("GET CONFIGURATION",err), exit (1); mmc_profile = formats[6]<<8|formats[7]; blu_ray = ((mmc_profile&0xF0)==0x40); if (mmc_profile!=0x1A && mmc_profile!=0x2A && mmc_profile!=0x14 && mmc_profile!=0x13 && mmc_profile!=0x12 && !blu_ray) fprintf (stderr,":-( mounted media doesn't appear to be " "DVD%sRW, DVD-RAM or Blu-ray\n",plusminus_locale()), exit (1); /* * First figure out how long the actual list is. Problem here is * that (at least Linux) USB units absolutely insist on accurate * cgc.buflen and you can't just set buflen to arbitrary value * larger than actual transfer length. */ int once=1; do { cmd[0] = 0x23; // READ FORMAT CAPACITIES cmd[8] = 4; cmd[9] = 0; if ((err=cmd.transport(READ,formats,4))) { if (err==0x62800 && once) // "MEDIUM MAY HAVE CHANGED" { cmd[0] = 0; // TEST UNIT READY cmd[5] = 0; cmd.transport(); // just swallow it... continue; } sperror ("READ FORMAT CAPACITIES",err), exit (1); } } while (once--); len = formats[3]; if (len&7 || len<8) fprintf (stderr,":-( allocation length isn't sane\n"), exit(1); cmd[0] = 0x23; // READ FORMAT CAPACITIES cmd[7] = (4+len)>>8; // now with real length... cmd[8] = (4+len)&0xFF; cmd[9] = 0; if ((err=cmd.transport(READ,formats,4+len))) sperror ("READ FORMAT CAPACITIES",err), exit (1); if (len != formats[3]) fprintf (stderr,":-( parameter length inconsistency\n"), exit(1); if (mmc_profile==0x1A || mmc_profile==0x2A) // DVD+RW { for (i=8;i>2) == 0x26) break; } else if (mmc_profile==0x12) // DVD-RAM { unsigned int v,ref; unsigned char *f,descr=0x01; int j; switch (ssa) { case -1: // no ssa for (ref=0,i=len,j=8;j>2) == 0x00) { v=f[0]<<24|f[1]<<16|f[2]<<8|f[3]; if (v>ref) ref=v,i=j; } } break; case 1: // first ssa for (i=8;i>2) == 0x01) break; break; case 2: // default ssa descr=0x00; case 3: // max ssa for (ref=0xFFFFFFFF,i=len,j=8;j>2) == descr) { v=f[0]<<24|f[1]<<16|f[2]<<8|f[3]; if (v nothing to do case 0: exit (0); break; case 1: // min spare <- max capacity i=len; for (max=0,j=8;j>2) == 0x32) { cap = f[0]<<24|f[1]<<16|f[2]<<8|f[3]; if (max < cap) max=cap,i=j; } } break; case 2: // default ssa i=8; // just grab first descriptor break; case 3: // max, ~10GB, is too big to trust user fprintf (stderr,"- -ssa=max is not supported for BD-R, " "specify explicit size with -ssa=XXXG\n"); exit (1); default: i=len; for (max=0,min=0xffffffff,j=8;j>2) == 0x32) { cap = f[0]<<24|f[1]<<16|f[2]<<8|f[3]; if (max < cap) max=cap; if (min > cap) min=cap; } } if (max==0) break; // for simplicity adjust spare size according to DL(!) rules ssa += 8192, ssa &= ~16383; // i.e. in 32MB increments f = formats+4; capacity = f[0]<<24|f[1]<<16|f[2]<<8|f[3]; cap = capacity - ssa; // place it within given boundaries if (cap < min) cap = min; else if (cap > max) cap = max; i = 8; f = formats+4+i; f[0] = cap>>24; f[1] = cap>>16; f[2] = cap>>8; f[3] = cap; f[4] = 0x32<<2 | not_pow; f[5] = 0; f[6] = 0; f[7] = 0; break; } if (i>2) == 0x31) break; break; case 0: i = 8; if ((formats[4+4]&3)==2)// same capacity for already formatted { f = formats+4+i; memcpy (f,formats+4,4); f[4] = 0x30<<2; f[5] = 0; f[6] = 0; f[7] = 0; } break; case 1: // min spare <- max capacity i=len; for (max=0,j=8;j>2) == 0x30) { cap = f[0]<<24|f[1]<<16|f[2]<<8|f[3]; if (max < cap) max=cap,i=j; } } break; case 2: // default ssa i=8; // just grab first descriptor break; case 3: // max spare <- min capacity i=len; for (min=0xffffffff,j=8;j>2) == 0x30) { cap=f[0]<<24|f[1]<<16|f[2]<<8|f[3]; if (min > cap) min=cap,i=j; } } break; default: i=len; capacity=0; for (max=0,min=0xffffffff,j=8;j>2) == 0x30) { cap = f[0]<<24|f[1]<<16|f[2]<<8|f[3]; if (max < cap) max=cap; if (min > cap) min=cap; } else if ((f[4]>>2) == 0x31) capacity = f[0]<<24|f[1]<<16|f[2]<<8|f[3]; } if (max==0 || capacity==0) break; // for simplicity adjust spare size according to DL(!) rules ssa += 8192, ssa &= ~16383; // i.e. in 32MB increments cap = capacity - ssa; // place it within given boundaries if (cap < min) cap = min; else if (cap > max) cap = max; i = 8; f = formats+4+i; f[0] = cap>>24; f[1] = cap>>16; f[2] = cap>>8; f[3] = cap; f[4] = 0x30<<2; f[5] = 0; f[6] = 0; f[7] = 0; break; } if (i>2)==0x30) f[4] |= full?2:3; // "Full" or "Quick Certification" } } else // DVD-RW { int descr=full?0x10:0x15; for (i=8;i>2) == descr) break; if (descr==0x15 && i==len) { fprintf (stderr,":-( failed to locate \"Quick Format\" descriptor.\n"); for (i=8;i>2) == 0x10) break; } } if (i==len) { fprintf (stderr,":-( can't locate appropriate format descriptor\n"); if (blank) i=0; else exit(1); } capacity = 0; if (blu_ray) { capacity |= formats[4+0], capacity <<= 8; capacity |= formats[4+1], capacity <<= 8; capacity |= formats[4+2], capacity <<= 8; capacity |= formats[4+3]; } else { capacity |= formats[4+i+0], capacity <<= 8; capacity |= formats[4+i+1], capacity <<= 8; capacity |= formats[4+i+2], capacity <<= 8; capacity |= formats[4+i+3]; } if (mmc_profile==0x1A || mmc_profile==0x2A) // DVD+RW fprintf (stderr,"* %.1fGB DVD+RW media detected.\n", 2048.0*capacity/1e9); else if (mmc_profile==0x12) // DVD-RAM fprintf (stderr,"* %.1fGB DVD-RAM media detected.\n", 2048.0*capacity/1e9); else if (blu_ray) // BD fprintf (stderr,"* %.1fGB BD media detected.\n", 2048.0*capacity/1e9); else // DVD-RW fprintf (stderr,"* %.1fGB DVD-RW media in %s mode detected.\n", 2048.0*capacity/1e9, mmc_profile==0x13?"Restricted Overwrite":"Sequential"); lead_out = 0; lead_out |= formats[4+0], lead_out <<= 8; lead_out |= formats[4+1], lead_out <<= 8; lead_out |= formats[4+2], lead_out <<= 8; lead_out |= formats[4+3]; cmd[0] = 0x51; // READ DISC INFORMATION cmd[8] = sizeof(dinfo); cmd[9] = 0; if ((err=cmd.transport(READ,dinfo,sizeof(dinfo)))) { sperror ("READ DISC INFORMATION",err); if (!force) exit(1); memset (dinfo,0xff,sizeof(dinfo)); cmd[0] = 0x35; cmd[9] = 0; cmd.transport(); } do_opc = ((dinfo[0]<<8|dinfo[1])<=0x20); if (dinfo[2]&3) // non-blank media { if (!force) { if (mmc_profile==0x1A || mmc_profile==0x2A || mmc_profile==0x13 || mmc_profile==0x12) fprintf (stderr,"- media is already formatted, lead-out is currently at\n" " %d KiB which is %.1f%% of total capacity.\n", lead_out*2,(100.0*lead_out)/capacity); else fprintf (stderr,"- media is not blank\n"); offer_options: if (mmc_profile == 0x1A || mmc_profile == 0x2A) fprintf (stderr,"- you have the option to re-run %s with:\n" " -lead-out to elicit lead-out relocation for better\n" " DVD-ROM compatibility, data is not affected;\n" " -force to enforce new format (not recommended)\n" " and wipe the data.\n", argv[0]); else if (mmc_profile == 0x12) fprintf (stderr,"- you have the option to re-run %s with:\n" " -format=full to perform full (lengthy) reformat;\n" " -ssa[=none|default|max]\n" " to grow, eliminate, reset to default or\n" " maximize Supplementary Spare Area.\n", argv[0]); else if (mmc_profile == 0x43) fprintf (stderr,"- you have the option to re-run %s with:\n" " -format=full to perform (lengthy) Full Certification;\n" " -ssa[=none|default|max|XXX[GM]]\n" " to eliminate or adjust Spare Area.\n", argv[0]); else if (blu_ray) // BD-R fprintf (stderr,"- BD-R can be pre-formatted only once\n"); else { fprintf (stderr,"- you have the option to re-run %s with:\n", argv[0]); if (i) fprintf (stderr, " -force[=full] to enforce new format or mode transition\n" " and wipe the data;\n"); fprintf (stderr," -blank[=full] to change to Sequential mode.\n"); } exit (0); } else if (cmd.umount()) perror (errno==EBUSY ? ":-( unable to proceed with format" : ":-( unable to umount"), exit (1); } else { if (mmc_profile==0x14 && blank==0 && !force) { fprintf (stderr,"- media is blank\n"); fprintf (stderr,"- given the time to apply full blanking procedure I've chosen\n" " to insist on -force option upon mode transition.\n"); exit (0); } else if (mmc_profile==041 && !force) { fprintf (stderr,"- media is blank\n"); fprintf (stderr,"- as BD-R can be pre-formance only once, I've chosen\n" " to insist on -force option.\n"); } force = 0; } if (((mmc_profile == 0x1A || mmc_profile == 0x2A) && blank) || (mmc_profile != 0x1A && compat) || (mmc_profile != 0x12 && mmc_profile != 0x43 && ssa) ) { fprintf (stderr,"- illegal command-line option for this media.\n"); goto offer_options; } else if ((mmc_profile == 0x1A || mmc_profile == 0x2A) && full) { fprintf (stderr,"- unimplemented command-line option for this media.\n"); goto offer_options; } #if defined(__unix) || defined(__unix__) /* * You can suspend, terminate, etc. the parent. We will keep on * working in background... */ setsid(); signal(SIGHUP,SIG_IGN); signal(SIGINT,SIG_IGN); signal(SIGTERM,SIG_IGN); #endif if (compat && force) str="relocating lead-out"; else if (blank) str="blanking"; else str="formatting"; if (gui) gui_action=str; else fprintf (stderr,"* %s .",str); *progress = 1; // formats[i] becomes "Format Unit Parameter List" formats[i+0] = 0; // "Reserved" formats[i+1] = 2; // "IMMED" flag formats[i+2] = 0; // "Descriptor Length" (MSB) formats[i+3] = 8; // "Descriptor Length" (LSB) handle_events(cmd); if (mmc_profile==0x1A || mmc_profile==0x2A) // DVD+RW { if (compat && force && (dinfo[2]&3)) formats[i+4+0]=formats[i+4+1]=formats[i+4+2]=formats[i+4+3]=0, formats[i+4+7] = 1; // "Restart format" cmd[0] = 0x04; // FORMAT UNIT cmd[1] = 0x11; // "FmtData" and "Format Code" cmd[5] = 0; if ((err=cmd.transport(WRITE,formats+i,12))) sperror ("FORMAT UNIT",err), exit(1); if (wait_for_unit (cmd,progress)) exit (1); if (!compat) { cmd[0] = 0x5B; // CLOSE TRACK/SESSION cmd[1] = 1; // "IMMED" flag on cmd[2] = 0; // "Stop De-Icing" cmd[9] = 0; if ((err=cmd.transport())) sperror ("STOP DE-ICING",err), exit(1); if (wait_for_unit (cmd,progress)) exit (1); } cmd[0] = 0x5B; // CLOSE TRACK/SESSION cmd[1] = 1; // "IMMED" flag on cmd[2] = 2; // "Close Session" cmd[9] = 0; if ((err=cmd.transport())) sperror ("CLOSE SESSION",err), exit(1); if (wait_for_unit (cmd,progress)) exit (1); } else if (mmc_profile==0x12) // DVD-RAM { cmd[0] = 0x04; // FORMAT UNIT cmd[1] = 0x11; // "FmtData"|"Format Code" cmd[5] = 0; formats[i+1] = 0x82; // "FOV"|"IMMED" if ((formats[i+4+4]>>2) != 0x01 && !full) formats[i+1] |= 0x20,// |"DCRT" cmd[1] |= 0x08; // |"CmpLst" if ((err=cmd.transport(WRITE,formats+i,12))) sperror ("FORMAT UNIT",err), exit(1); if (wait_for_unit (cmd,progress)) exit(1); } else if (mmc_profile==0x43) // BD-RE { cmd[0] = 0x04; // FORMAT UNIT cmd[1] = 0x11; // "FmtData"|"Format Code" cmd[5] = 0; formats[i+1] = 0x82; // "FOV"|"IMMED" if (full && (formats[i+4+4]>>2)!=0x31) formats[i+4+4] |= 2;// "Full Certificaton" else if ((formats[i+4+4]>>2)==0x30) formats[i+4+4] |= 3;// "Quick Certification" if ((err=cmd.transport(WRITE,formats+i,12))) sperror ("FORMAT UNIT",err), exit(1); if (wait_for_unit (cmd,progress)) exit(1); } else if (mmc_profile==0x41) // BD-R { cmd[0] = 0x04; // FORMAT UNIT cmd[1] = 0x11; // "FmtData"|"Format Code" cmd[5] = 0; formats[i+1] = 0x2; // IMMED" if ((err=cmd.transport(WRITE,formats+i,12))) sperror ("FORMAT UNIT",err), exit(1); if (wait_for_unit (cmd,progress)) exit(1); } else // DVD-RW { page05_setup (cmd,mmc_profile); if (do_opc) { cmd[0] = 0x54; // SEND OPC INFORMATION cmd[1] = 1; // "Perform OPC" cmd[9] = 0; cmd.timeout(120); // NEC units can be slooo...w if ((err=cmd.transport())) { if (err==0x17301) // "POWER CALIBRATION AREA ALMOST FULL" fprintf (stderr,":-! WARNING: Power Calibration Area " "is almost full\n"); else { sperror ("PERFORM OPC",err); if ((err&0x0FF00)==0x07300 && (err&0xF0000)!=0x10000) exit (1); /* The rest of errors are ignored, see groisofs_mmc.cpp * for further details... */ } } } if (blank) // DVD-RW blanking procedure { cmd[0] = 0xA1; // BLANK cmd[1] = blank; cmd[11] = 0; if ((err=cmd.transport())) sperror ("BLANK",err), exit(1); if (wait_for_unit (cmd,progress)) exit (1); } else // DVD-RW format { if ((formats[i+4+4]>>2)==0x15) // make it really quick formats[i+4+0]=formats[i+4+1]=formats[i+4+2]=formats[i+4+3]=0; cmd[0] = 0x04; // FORMAT UNIT cmd[1] = 0x11; // "FmtData" and "Format Code" cmd[5] = 0; if ((err=cmd.transport(WRITE,formats+i,12))) sperror ("FORMAT UNIT",err), exit(1); if (wait_for_unit (cmd,progress)) exit (1); cmd[0] = 0x35; // FLUSH CACHE cmd[9] = 0; cmd.transport (); } } pioneer_stop (cmd,progress); #if 0 cmd[0] = 0x1E; // ALLOW MEDIA REMOVAL cmd[5] = 0; if (cmd.transport ()) return 1; cmd[0] = 0x1B; // START/STOP UNIT cmd[4] = 0x2; // "Eject" cmd[5] = 0; if (cmd.transport()) return 1; cmd[0] = 0x1B; // START/STOP UNIT cmd[1] = 0x1; // "IMMED" cmd[4] = 0x3; // "Load" cmd[5] = 0; cmd.transport (); #endif return 0; } dvd+rw-tools-7.1/dvd+rw-mediainfo.cpp0000644000000100000010000007307310505271421015637 0ustar binbin/* * DVD Media Info utility by Andy Polyakov . * * This code is in public domain. */ #include #include #include #include #include "transport.hxx" #ifdef _WIN32 #define LLU "I64u" #else #define LLU "llu" #endif int main(int argc,char *argv[]) { Scsi_Command cmd; unsigned char header[48],inq[128],*page2A=NULL; char cmdname[64]; int i,j,ntracks,err,dvd_dash=0,dvd_plus=0, plus_mediaid_printed=0,page2A_printed=0,dvd_ram_spare=0; unsigned short profile, dvd_0E=0,dvd_10=0,dvd_11=0,dvd_0A=0,dvd_C0=0, prf_23=0,prf_24=0,prf_38=0; int _1x=1385,blu_ray=0; if (argc<2) fprintf (stderr,"usage: %s /dev/dvd\n",argv[0]), exit (FATAL_START(EINVAL)); if (!cmd.associate(argv[1])) fprintf (stderr,"%s: unable to open: ",argv[1]), perror (NULL), exit (FATAL_START(errno)); cmd[0] = 0x12; // INQUIRY cmd[4] = 36; cmd[5] = 0; if ((err=cmd.transport(READ,inq,36))) sperror ("INQUIRY",err), exit (FATAL_START(errno)); if ((inq[0]&0x1F) != 5) fprintf (stderr,":-( not an MMC unit!\n"), exit (FATAL_START(EINVAL)); if (argc>1) printf ("INQUIRY: [%.8s][%.16s][%.4s]\n", inq+8,inq+16,inq+32); #if 0 wait_for_unit(cmd); #else // consume sense data, most commonly "MEDIUM MAY HAVE CHANGED"... cmd[0] = 0; // TEST UNIT READY cmd[5] = 0; if ((err=cmd.transport()) == -1) sperror ("TEST UNIT READY",err), exit (FATAL_START(errno)); #endif if (argc>2) do { unsigned char *pages; int len,n; static int pagecode=0x3F; printf ("MODE SENSE[#%02Xh]:\n",pagecode); cmd[0] = 0x5A; // MODE SENSE cmd[1] = 0x08; // "Disable Block Descriptors" cmd[2] = pagecode; // initially "All Pages" cmd[8] = 12; cmd[9] = 0; if ((err=cmd.transport(READ,header,12))) sprintf (cmdname,"MODE SENSE#%02X",pagecode), sperror (cmdname,err), exit(errno); if (header[6]<<8|header[7]) fprintf (stderr,":-( \"DBD\" is not respected\n"), exit(errno); len=(header[0]<<8|header[1])+2; if (len < (8+2+header[9])) { if (pagecode == 0x3F) { pagecode = 0x5; continue; } len = 8+2+header[9]; } if (!(pages=(unsigned char *)malloc(len))) exit(1); cmd[0] = 0x5A; cmd[1] = 0x08; cmd[2] = pagecode; cmd[7] = len>>8; cmd[8] = len; cmd[9] = 0; cmd.transport(READ,pages,len); for(i=8+(pages[6]<<8|pages[7]);i>8; cmd[8] = len; cmd[9] = 0; if ((err=cmd.transport(READ,page2A,len))) sperror ("MODE SENSE#2A",err), exit (errno); if (page2A[6]<<8|page2A[7]) fprintf (stderr,":-( \"DBD\" is not respected\n"); else if (len<((page2A[0]<<8|page2A[1])+2) || len<(8+2+page2A[9])) { len=(page2A[0]<<8|page2A[1])+2; if (len < (8+2+page2A[9])) len = 8+2+page2A[9]; page2A=(unsigned char *)malloc(len); continue; } if (!(page2A[8+(page2A[6]<<8|page2A[7])+2]&8)) fprintf (stderr,":-( not a DVD unit\n"), exit (FATAL_START(EINVAL)); if (page2A==header) page2A=NULL; else if (argc>2 && !page2A_printed) { printf("MODE SENSE[#2A]:\n"); for(i=8+(page2A[6]<<8|page2A[7]);i>16; cmd[7] = len>>8; cmd[8] = len; cmd[9] = 0; cmd.transport(READ,profiles,len); printf ("GET [CURRENT] CONFIGURATION:\n"); for(i=8;i2) sperror (blu_ray?"READ BD STRUCTURE#FF":"READ DVD STRUCTURE#FF",err); break; } len = (header[0]<<8|header[1]) + 2; if (!(p = (unsigned char *)malloc(len))) break; cmd[0] = 0xAD; cmd[1] = blu_ray; // Media Type cmd[7] = 0xFF; cmd[8] = len>>8; cmd[9] = len; cmd[11] = 0; if ((err=cmd.transport(READ,p,len))) break; for (j=4;j2) sprintf (cmdname,"READ BD STRUCTURE#%02x",format), sperror (cmdname,cmd.sense()); break; } len = (header[0]<<8|header[1]) + 2; if (len>sizeof(di)) len=sizeof(di); cmd[0] = 0xAD; cmd[1] = blu_ray; // Media Type cmd[7] = format; cmd[8] = len>>8; cmd[9] = len; cmd[11] = 0; if ((err=cmd.transport(READ,&di,len))) break; if (argc>2) { printf ("READ BD STRUCTURE[#%02xh]:",format); for (j=0;j<(int)len;j++) printf("%c%02x",j?' ':'\t',di[j]); printf("\n"); } if (di[4+0]=='D' && di[4+1]=='I') printf (" Media ID: %6.6s/%-3.3s\n",di+4+100,di+4+106); } while (0); else if (dvd_0E || dvd_11) do { union { unsigned char _e[4+40],_11[4+256]; } dvd; unsigned int len; unsigned char format=dvd_plus?0x11:0x0E; cmd[0] = 0xAD; cmd[7] = format; cmd[9] = 4; cmd[11] = 0; if ((err=cmd.transport(READ,header,4))) { if (argc>2) sprintf (cmdname,"READ DVD STRUCTURE#%02x",format), sperror (cmdname,cmd.sense()); break; } len = (header[0]<<8|header[1]) + 2; if (len>sizeof(dvd)) len=sizeof(dvd); cmd[0] = 0xAD; cmd[7] = format; cmd[8] = len>>8; cmd[9] = len; cmd[11] = 0; if ((err=cmd.transport(READ,&dvd,len))) break; if (argc>2) { printf ("READ DVD STRUCTURE[#%02xh]:",format); for (j=0,i=dvd_dash?sizeof(dvd._e):sizeof(dvd._11);j=(hlen+32)) { n = (p[30]<<8|p[31])*4; for (i=0;i2) sperror ("GET CURRENT PERFORMACE",err); break; } len = (d[0]<<24|d[1]<<16|d[2]<<8|d[3])-4; if (len%16) // insane length { if (argc>2) fprintf (stderr,":-( insane GET PERFORMANCE length %u\n",len); break; } len+=8; if (len < sizeof(d)) { if (argc>2) fprintf (stderr,":-( empty GET CURRENT PERFORMACE descriptor\n"); break; } if (len == sizeof(d)) p = d; else // ever happens? { unsigned int n=(len-8)/16; p = (unsigned char *)malloc(len); // will leak... cmd[0]=0xAC; // GET PERFORMANCE cmd[1]=4; // ask for "Overall Write Performance" cmd[8]=n>>8; cmd[9]=n; // real number of descriptors cmd[10]=0; // ask for descriptor in effect cmd[11]=0; if ((err=cmd.transport(READ,p,len))) { sperror ("GET CURRENT PERFORMANCE",err); break; } } if (argc>2) { printf ("GET CURRENT PERFORMANCE:\t"); for (i=4;i %u]\n", i==0?"Write Performance:":"", (double)rv/_1x,_1x,rv, p[0]<<24|p[1]<<16|p[2]<<8 |p[3], p[8]<<24|p[9]<<16|p[10]<<8|p[11]); else printf (" %-23s%.1fx%d=%uKB/s@%u -> %.1fx%d=%uKB/s@%u\n", i==0?"Write Performance:":"", (double)rv/_1x,_1x,rv,p[0]<<24|p[1]<<16|p[2]<<8 |p[3], (double)wv/_1x,_1x,wv,p[8]<<24|p[9]<<16|p[10]<<8|p[11]); } } while (0); do { unsigned char d[8+16],*p; unsigned int len,rv,wv,i; cmd[0]=0xAC; // GET PERFORMANCE cmd[9]=1; // start with one descriptor cmd[10]=0x3; // ask for "Write Speed Descriptor" cmd[11]=0; if ((err=cmd.transport(READ,d,sizeof(d)))) { if (argc>2) sperror ("GET PERFORMACE",err); break; } len = (d[0]<<24|d[1]<<16|d[2]<<8|d[3])-4; if (len%16) // insage length { if (argc>2) fprintf (stderr,":-( insane GET PERFORMANCE length %u\n",len); break; } len+=8; if (len < sizeof(d)) { fprintf (stderr,":-( empty GET PERFORMACE descriptor\n"); break; } if (len == sizeof(d)) p = d; else { unsigned int n=(len-8)/16; p = (unsigned char *)malloc(len); // will leak... cmd[0]=0xAC; // GET PERFORMANCE cmd[8]=n>>8; cmd[9]=n; // real number of descriptors cmd[10]=0x3; // ask for "Write Speed Descriptor" cmd[11]=0; if ((err=cmd.transport(READ,p,len))) { sperror ("GET PERFORMANCE",err); break; } } if (argc>2) { printf ("GET PERFORMANCE:\t"); for (i=8;i2) sprintf (cmdname,"READ DVD STRUCTURE#%X",dvd_dash), sperror (cmdname,cmd.sense()); break; } printf("READ DVD STRUCTURE[#%Xh]:",dvd_dash<0?0:dvd_dash); if (argc>2) for (j=0;j<20-4;j++) printf("%c%02x",j?' ':'\t',header[4+j]); printf("\n"); switch(book&0xF0) { case 0x00: brand="-ROM"; break; case 0x10: brand="-RAM"; break; case 0x20: brand="-R"; break; case 0x30: brand="-RW"; break; case 0x90: brand="+RW"; break; case 0xA0: brand="+R"; break; case 0xE0: brand="+R DL"; break; default: brand=NULL; break; } printf (" Media Book Type: %02Xh, ",book); if (brand) printf ("DVD%s book [revision %d]\n", brand,book&0xF); else printf ("unrecognized value\n"); if (header[4+1]&0xF0) dvd_ram_spare=5120; else dvd_ram_spare=12800; if (dvd_plus && !plus_mediaid_printed) printf (" Media ID: %.8s/%.3s\n",header+23,header+31); phys_start = header[4+5]<<16, phys_start |= header[4+6]<<8, phys_start |= header[4+7]; if ((header[4+2]&0x60)==0) // single-layer phys_end = header[4+9]<<16, phys_end |= header[4+10]<<8, phys_end |= header[4+11]; else phys_end = header[4+13]<<16, phys_end |= header[4+14]<<8, phys_end |= header[4+15]; if (phys_end>0) phys_end -= phys_start; if (phys_end>0) phys_end += 1; printf (" %s %u*2KB=%"LLU"\n", dvd_dash>=0?"Legacy lead-out at:":"Last border-out at:", phys_end,phys_end*2048LL); if (dvd_dash<=0) break; cmd[0] = 0xAD; cmd[7] = 0; dvd_dash=-1; cmd[9] = 20; cmd[11] = 0; if ((err=cmd.transport(READ,header,20))) { sperror ("READ DVD-R STRUCTURE",err); break; } } while (1); if (profile==0x10 && (header[4]&0xF0)==0) printf ("DVD-ROM media detected, exiting...\n"), exit(0); if (profile==0x2B) do { unsigned char s[4+8]; cmd[0] = 0xAD; // READ DVD STRUCTURE cmd[7] = 0x20; // "DVD+R Double Layer Boundary Information" cmd[9] = sizeof(s); cmd[11] = 0; if ((err=cmd.transport(READ,s,sizeof(s)))) { sperror ("READ LAYER BOUNDARY INFORMATION",err); break; } if ((s[0]<<8|s[1]) < 10) { fprintf (stderr,":-( insane LBI structure length\n"); break; } printf ("DVD+R DOUBLE LAYER BOUNDARY INFORMATION:\n"); printf (" L0 Data Zone Capacity: %u*2KB, can %s be set\n", s[8]<<24|s[9]<<16|s[10]<<8|s[11], s[4]&0x80?"no longer":"still"); } while (0); if (profile==0x16) do { unsigned char s[4+8]; // Layer Jump specific structures. Note that growisofs doesn't // perform Layer Jump recordings, see web-page for details... cmd[0] = 0xAD; // READ DVD STRUCTURE cmd[7] = 0x21; // "DVD-R DL Shifted Middle Area Start Address" cmd[9] = sizeof(s); cmd[11] = 0; if ((err=cmd.transport(READ,s,sizeof(s)))) argc>2 ? sperror ("READ SHIFTED MIDDLE AREA START ADDRESS",err) : (void)0; else if ((s[0]<<8|s[1]) < 10) fprintf (stderr,":-( insane SMASA structure length\n"); else { printf ("DVD-R DL SHIFTED MIDDLE AREA START ADDRESS:\n"); printf (" Shifted Middle Area: %u*2KB, can %s be set\n", s[8]<<24|s[9]<<16|s[10]<<8|s[11], s[4]&0x80?"no longer":"still"); } cmd[0] = 0xAD; // READ DVD STRUCTURE cmd[7] = 0x22; // "DVD-R DL Jump Interval Size" cmd[9] = sizeof(s); cmd[11] = 0; if ((err=cmd.transport(READ,s,sizeof(s)))) argc>2 ? sperror ("READ JUMP INTERVAL SIZE",err) : (void)0; else if ((s[0]<<8|s[1]) < 10) fprintf (stderr,":-( insane JIS structure length\n"); else { printf ("DVD-R DL JUMP INTERVAL SIZE:\n"); printf (" Jump Interval Size: %u*2KB\n", s[8]<<24|s[9]<<16|s[10]<<8|s[11]); } cmd[0] = 0xAD; // READ DVD STRUCTURE cmd[7] = 0x23; // "DVD-R DL Manual Layer Jump Address" cmd[9] = sizeof(s); cmd[11] = 0; if ((err=cmd.transport(READ,s,sizeof(s)))) argc>2 ? sperror ("READ MANUAL LAYER JUMP ADDRESS",err) : (void)0; else if ((s[0]<<8|s[1]) < 10) fprintf (stderr,":-( insane MLJA structure length\n"); else { printf ("DVD-R DL MANUAL LAYER JUMP ADDRESS:\n"); printf (" Manual Layer Jump: %u*2KB\n", s[8]<<24|s[9]<<16|s[10]<<8|s[11]); } cmd[0] = 0xAD; // READ DVD STRUCTURE cmd[7] = 0x24; // "DVD-R DL Remapping Address" cmd[9] = sizeof(s); cmd[11] = 0; if ((err=cmd.transport(READ,s,sizeof(s)))) argc>2 ? sperror ("READ REMAPPING ADDRESS",err) : (void)0; else if ((s[0]<<8|s[1]) < 10) fprintf (stderr,":-( insane RA structure length\n"); else { printf ("DVD-R DL REMAPPING ADDRESS:\n"); printf (" Remapping Jump: %u*2KB\n", s[8]<<24|s[9]<<16|s[10]<<8|s[11]); } } while (0); if (profile==0x12 && dvd_0A) do { unsigned char s[16]; unsigned int a,b; if (dvd_0A>sizeof(s)) dvd_0A=sizeof(s); cmd[0] = 0xAD; // READ DVD STRUCTURE cmd[7] = 0x0A; // "DVD-RAM Spare Area Information" cmd[8] = dvd_0A>>8; cmd[9] = dvd_0A; cmd[11] = 0; if ((err=cmd.transport(READ,s,dvd_0A))) { sperror ("READ DVD-RAM SPARE INFORMATION",err); break; } if (dvd_0A<8) break; printf ("DVD-RAM SPARE AREA INFORMATION:\n"); a = s[ 4]<<24|s[ 5]<<16|s[ 6]<<8|s[ 7]; b = dvd_ram_spare; printf (" Primary SA: %u/%u=%.1f%% free\n",a,b,(a*100.0)/b); if (dvd_0A<16) break; a = s[ 8]<<24|s[ 9]<<16|s[10]<<8|s[11]; b = s[12]<<24|s[13]<<16|s[14]<<8|s[15]; printf (" Supplementary SA: %u/%u=%.1f%% free\n",a,b,(a*100.0)/b); } while (0); if (profile==0x12 && dvd_C0) do { unsigned char s[8]; if (dvd_C0>sizeof(s)) dvd_C0=sizeof(s); cmd[0] = 0xAD; // READ DVD STRUCTURE cmd[7] = 0xC0; // "Write Protection Status" cmd[8] = dvd_C0>>8; cmd[9] = dvd_C0; cmd[11] = 0; if ((err=cmd.transport(READ,s,dvd_C0))) { sperror ("READ WRITE PROTECTION STATUS",err); break; } if (dvd_C0<8) break; printf ("DVD-RAM WRITE PROTECTION STATUS:\n"); if (s[4]&0x04) printf (" Write protect tab on cartridge is set to protected\n"); if (s[4]&0x01) printf (" Software Write Protection until Power down is on\n"); printf (" Persistent Write Protection is %s\n",s[4]&0x02?"on":"off"); } while (0); if (blu_ray && dvd_0A) do { unsigned char s[16]; unsigned int a,b; if (dvd_0A>sizeof(s)) dvd_0A=sizeof(s); cmd[0] = 0xAD; // READ DVD STRUCTURE cmd[1] = blu_ray; cmd[7] = 0x0A; // "BD Spare Area Information" cmd[8] = dvd_0A>>8; cmd[9] = dvd_0A; cmd[11] = 0; if ((err=cmd.transport(READ,s,dvd_0A))) { if (err!=0x33100 || argc>2) sperror ("READ BD SPARE INFORMATION",err); break; } a = s[ 8]<<24|s[ 9]<<16|s[10]<<8|s[11]; b = s[12]<<24|s[13]<<16|s[14]<<8|s[15]; if (dvd_0A<16 || b==0) break; printf ("BD SPARE AREA INFORMATION:\n"); printf (" Spare Area: %u/%u=%.1f%% free\n",a,b,(a*100.0)/b); } while (0); if (blu_ray && prf_38) do { unsigned char pow[16]; cmd[0] = 0x51; // READ DISC INFORMATION cmd[1] = 0x10; // "POW Resources Information" cmd[8] = sizeof(pow); cmd[9] = 0; if ((err=cmd.transport(READ,pow,sizeof(pow)))) { sperror ("READ ROW RESOURCES INFORMATION",err); break; } printf ("POW RESOURCES INFORMATION:\n"); printf (" Remaining Replacements:%u\n",pow[ 4]<<24|pow[ 5]<<16|pow[ 6]<<8|pow[ 7]); printf (" Remaining Map Entries: %u\n",pow[ 8]<<24|pow[ 9]<<16|pow[10]<<8|pow[11]); printf (" Remaining Updates: %u\n",pow[12]<<24|pow[13]<<16|pow[14]<<8|pow[15]); } while(0); if (blu_ray && argc>2) do { unsigned char *p,*pac,*s; unsigned int len; cmd[0] = 0xAD; // READ DVD STRUCTURE cmd[1] = blu_ray; cmd[7] = 0x30; // "Physical Access Control (PAC)" cmd[9] = 4; cmd[11] = 0; if ((err=cmd.transport(READ,header,4))) { if (err!=0x33100 || argc>2) sperror ("READ BD PAC LIST",err); break; } len = (header[0]<<8|header[1])+2; if (!(p = (unsigned char *)malloc(len))) break; cmd[0] = 0xAD; // READ DVD STRUCTURE cmd[1] = blu_ray; cmd[7] = 0x30; // "Physical Access Control (PAC)" cmd[8] = len>>8; cmd[9] = len; cmd[11] = 0; if ((err=cmd.transport(READ,p,len))) { sperror ("READ BD PAC LIST",err); break; } printf ("BD WRITTEN PAC HEADERS:\n"); for (pac=p+4;pac2) for (j=0;j<16;j++) printf("%c%02x",j?' ':'\t',header[j]); printf ("\n"); printf (" Disc status: %s\n",disc_status[header[2]&3]); printf (" Number of Sessions: %d\n",header[9]<<8|header[4]); printf (" State of Last Session: %s\n",session_state[(header[2]>>2)&3]); if ((header[2]&3)!=2) printf (" \"Next\" Track: %d\n",header[10]<<8|header[5]); printf (" Number of Tracks: %d",ntracks=header[11]<<8|header[6]); if (header[7]&3) printf ("\n BG Format Status: %s",bg_format[header[7]&3]); if ((header[7]&3) == 2) { unsigned char sense[18]; cmd[0] = 0x03; // REQUEST SENSE cmd[4] = sizeof(sense); cmd[5] = 0; if (!cmd.transport (READ,sense,sizeof(sense)) && sense[15]&0x80) printf (", %.1f%% complete",(sense[16]<<8|sense[17])/655.36); } printf ("\n"); while (prf_23 || profile==0x12 || (header[2]&0x10 && argc>2)) { unsigned char formats[260]; int len; unsigned int capacity,blocksize; static const char *type[] = { "reserved", "unformatted", "formatted", "no media" }; printf ("READ FORMAT CAPACITIES:\n"); cmd[0] = 0x23; // READ FORMAT CAPACITIES cmd[8] = 12; cmd[9] = 0; if ((err=cmd.transport(READ,formats,12))) { sperror ("READ FORMAT CAPACITIES",err); break; } len = formats[3]; if (len&7 || len<16) { fprintf (stderr,":-( allocation length isn't sane %d\n",len); break; } cmd[0] = 0x23; // READ FORMAT CAPACITIES cmd[7] = (4+len)>>8; // now with real length... cmd[8] = (4+len)&0xFF; cmd[9] = 0; if ((err=cmd.transport(READ,formats,4+len))) { sperror ("READ FORMAT CAPACITIES",err); break; } if (len != formats[3]) { fprintf (stderr,":-( parameter length inconsistency\n"); break; } if (blu_ray) blocksize=2048; else blocksize=formats[9]<<16|formats[10]<<8|formats[11]; printf(" %s:\t\t%u*%u=",type[formats[8]&3], capacity=formats[4]<<24|formats[5]<<16|formats[6]<<8|formats[7], blocksize); printf("%"LLU"\n",(unsigned long long)capacity*blocksize); for(i=12;i>24; cmd[3] = i>>16; cmd[4] = i>>8; cmd[5] = i; cmd[8] = len; cmd[9] = 0; if ((err=cmd.transport(READ,header,len))) { if (i=38) { if (len==32) { len=40; continue; } else { break; } } else if ((profile==0x15 || profile==0x16) && (header[5]&0xC0) && (header[0]<<8|header[1])>=46) { if (len==32) { len=48; continue; } else { break; } } len = 0; } printf ("READ TRACK INFORMATION[#%d]:",i); if (argc>2) for (j=0;j<16;j++) printf("%c%02x",j?' ':'\t',header[j]); printf ("\n"); if ((profile&0x0F)==0x0B) // DVD+R { printf (" Track State: %s\n", plus_track_state[header[6]>>6]); } else { printf (" Track State: %s%s", ((header[6]>>5)==1 && (header[7]&1))?"in":"", dash_track_state[header[6]>>5]); if (header[5]&0x20) printf(",damaged"); printf ("\n"); } printf (" Track Start Address: %u*2KB\n", header[8]<<24|header[9]<<16|header[10]<<8|header[11]); if (header[7]&1) printf (" Next Writable Address: %u*2KB\n", header[12]<<24|header[13]<<16|header[14]<<8|header[15]); printf (" Free Blocks: %u*2KB\n", header[16]<<24|header[17]<<16|header[18]<<8|header[19]); if (header[6]&0x10) printf (" Fixed Packet Size: %u*2KB\n", header[20]<<24|header[21]<<16|header[22]<<8|header[23]); printf (header[5]&0x40? // check upon LJ bit " Zone End Address: %u*2KB\n": " Track Size: %u*2KB\n", header[24]<<24|header[25]<<16|header[26]<<8|header[27]); if (header[7]&2) printf (" Last Recorded Address: %u*2KB\n", header[28]<<24|header[29]<<16|header[30]<<8|header[31]); if (len>=40) printf (" ROM Compatibility LBA: %u\n", header[36]<<24|header[37]<<16|header[38]<<8|header[39]); if (len>=48 && header[5]&0xC0) // check upon LJRS bits printf (" LJRS field: %u\n",header[5]>>6), printf (" Next Layer Jump: %u\n", header[40]<<24|header[41]<<16|header[42]<<8|header[43]), printf (" Last Layer Jump: %u\n", header[44]<<24|header[45]<<16|header[46]<<8|header[47]); } do { unsigned char *toc,*p; int len,sony; cmd[0] = 0x43; // READ TOC cmd[6] = 1; cmd[8] = 12; cmd[9] = 0; if ((err=cmd.transport (READ,header,12))) { if (argc>2) sperror ("READ TOC",err); break; } len = (header[0]<<8|header[1])+2; toc = (unsigned char *)malloc (len); printf ("FABRICATED TOC:"); if (argc>2) printf ("\t\t%u %x %x",header[0]<<8|header[1],header[2],header[3]); printf ("\n"); cmd[0] = 0x43; // READ TOC cmd[6] = header[2]; // "First Track Number" cmd[7] = len>>8; cmd[8] = len; cmd[9] = 0; if ((err=cmd.transport (READ,toc,len))) { sperror ("READ TOC",err); break; } for (p=toc+4,i=4;i2) sperror ("GET SESSION INFO",err); break; } len = header[4+4]<<24|header[4+5]<<16|header[4+6]<<8|header[4+7]; printf (" Multi-session Info: #%u@%u\n",header[4+2],len); cmd[0] = 0x43; // READ TOC cmd[8] = 12; cmd[9] = 0x40; // "SONY Vendor bit" if ((err=cmd.transport (READ,header,12))) { if (argc>2) sperror ("GET SONY SESSION INFO",err); break; } sony = header[4+4]<<24|header[4+5]<<16|header[4+6]<<8|header[4+7]; if (len != sony) printf (" SONY Session Info: #%u@%u\n",header[4+2],sony); } while (0); do { unsigned int ccity,bsize; cmd[0] = 0x25; // READ CAPACITY cmd[9] = 0; if ((err=cmd.transport (READ,header,8))) { if (argc>2) sperror ("READ CAPACITY",err); break; } ccity = header[0]<<24|header[1]<<16|header[2]<<8|header[3]; if (ccity) ccity++; bsize = header[4]<<24|header[5]<<16|header[6]<<8|header[7]; printf ("READ CAPACITY: %u*%u=%"LLU"\n", ccity,bsize, (unsigned long long)ccity*bsize); } while (0); return 0; } dvd+rw-tools-7.1/dvd+rw-booktype.cpp0000644000000100000010000004770310447202054015542 0ustar binbin#if 0 # # Utility for manipulating Book Type Field of Physical Format Descriptor # located in lead-in of DVD+RW media. This is 9th version. 2nd version # added initial support for 2nd generation DVD+RW drives. 3rd version # adds support for DVD+R unit settings. 4th version checks if the unit # is of RICOH design and reliably recognizes drive generation. 5th # version adds support for Benq derivatives. 6th version fixes problem # with USB connected units? 7th version adds support for BTC units. # 8th version fixes typos in BTC support, adds support for fraudulent # NEC firmwares and Lite-On based units. 9th version adds support for # LG, Plextor and dual-format and double-layer Benq units. Keep in mind # that booktyping support might appear in certain firmware revisions, # not necessarily all. # # The code is in public domain. # # See http://www.dvdplusrw.org/resources/bitsettings.html # for further details. # /bin/sh << EOS MODNAME=\`expr "/$0" : '\(.*[^/]\)/*$' : '.*/\(..*\)' : '\(.*\)\..*$'\` case "`uname -s`" in SunOS) (set -x; g++ -fno-exceptions -O -o \$MODNAME "$0" -lvolmgt) ;; *) (set -x; g++ -fno-exceptions -O -o \$MODNAME "$0") ;; esac EOS exit #endif #include #include #include #include #include "transport.hxx" #define OPT_MEDIA 0x01 #define OPT_UNIT 0x02 #define OPT_UNITRW 0x03 #define OPT_UNITR 0x04 #define OPT_MASK 0x0F #define OPT_INQ 0x10 #define INTERNAL_RELOAD 0x20 unsigned short profile; const char *dev; const char *bookname (unsigned char book,char *unknown=NULL) { const char *ret; switch(book&0xF0) { case 0x00: ret="-ROM"; break; case 0x10: ret="-RAM"; break; case 0x20: ret="-R"; break; case 0x30: ret="-RW"; break; case 0x90: ret="+RW"; break; case 0xA0: ret="+R"; break; case 0xE0: ret="+R DL"; break; default: ret=NULL; if (unknown) sprintf (unknown,"?%02X",book&0xFF); break; } return ret; } int mediainfo (Scsi_Command &cmd) { unsigned char buf[8],book; const char *brand; int err; cmd[0]=0xAD; // READ DVD STRUCTURE cmd[9]=sizeof(buf); cmd[11]=0; if ((err=cmd.transport(READ,buf,sizeof(buf)))) { sperror ("READ DVD STRUCTURE#0",err); return 1; } book=buf[4]; brand=bookname (book); printf ("Current media Book Type Field is %02xh: ",book); if (brand) printf ("DVD%s specification [revision %d]\n", brand,book&0xF); else printf ("unrecognized value\n"); return 0; } int ricoh (Scsi_Command &cmd,int action,int book,int gen=2) { int err,cnt=0; unsigned char buf[6]; const char *brand; char unknown [16]="?unknown"; switch (action) { case OPT_INQ|OPT_MEDIA: break; case OPT_INQ: case OPT_INQ|OPT_UNIT: if (gen==1) goto inq_unitrw; cmd[0]=0xFA; cmd[1]=0x10; cmd[8]=sizeof(buf); cmd[11]=0; if (!cmd.transport(READ,buf,sizeof(buf))) { brand = bookname (buf[4],unknown), printf ("The unit will brand DVD+R media as DVD%s\n", brand?brand:unknown), cnt++; } case OPT_INQ|OPT_UNITRW: inq_unitrw: cmd[0]=0xFA; cmd[1]=0x00; cmd[8]=sizeof(buf); cmd[11]=0; if (!cmd.transport(READ,buf,sizeof(buf))) { brand = bookname (buf[4],unknown), printf ("The unit will format DVD+RW media as DVD%s\n", brand?brand:unknown), cnt++; } if (cnt==0) printf ("Unable to determine unit settings. " "Default settings vary from\n" "firmware to firmware. Set " "explicitly to be certain.\n"); break; case OPT_INQ|OPT_UNITR: if (gen==1) { fprintf (stderr,":-( not applicable to 1st gen unit\n"); return 1; } cmd[0]=0xFA; cmd[1]=0x10; cmd[8]=sizeof(buf); cmd[11]=0; if (!cmd.transport(READ,buf,sizeof(buf))) { brand = bookname (buf[4],unknown), printf ("The unit will brand DVD+R media as DVD%s\n", brand?brand:unknown); } break; case OPT_MEDIA: if (profile!=0x1A) { fprintf (stderr,":-( action is applicable to DVD+RW only\n"); break; } if (book != 0x92 && book != 0x01) { fprintf (stderr,":-( BookType#%02x is not applicable\n",book); break; } cmd[0]=0xF9; cmd[1]=0x02; cmd[2]=book; cmd[3]=(gen==1?0:0xFF); cmd[11]=0; if ((err=cmd.transport())) return sperror("RICOH_F9h(2)",err),1; else return mediainfo(cmd); break; case OPT_UNIT: if (profile==0x1B || profile==0x2B) goto set_unitr; case OPT_UNITRW: if (book != 0x92 && book != 0x01) { fprintf (stderr,":-( BookType#%02x is not applicable\n",book); break; } cmd[0]=0xF9; cmd[1]=(gen==1?0x01:0x0C); cmd[2]=book; cmd[3]=(gen==1?0:0xFF); cmd[11]=0; if ((err=cmd.transport())) sperror (gen==1?"RICOH(1)":"RICOH(0Ch)",err); else printf ("Unit was instructed to format DVD+RW as DVD%s\n", book==0x01?"-ROM":"+RW"); break; case OPT_UNITR: set_unitr: if (gen==1) { fprintf (stderr,":-( not applicable to 1st gen unit\n"); return 1; } if (book != 0xA1 && book != 0x01) { fprintf (stderr,":-( BookType#%02x is not applicable\n",book); break; } cmd[0]=0xF9; cmd[1]=0x14; cmd[2]=book; cmd[3]=0xFF; cmd[11]=0; if ((err=cmd.transport())) sperror ("RICOH(14h)",err); else printf ("Unit was instructed to brand DVD+R as DVD%s\n", book==0x01?"-ROM":"+R"); break; default: break; } return 0; } int benq (Scsi_Command &cmd,int action,int book,int gen=2) { int err; unsigned char word[2]; const char *brand; char unknown [16]="?unknown"; switch (action) { case OPT_INQ|OPT_MEDIA: break; case OPT_INQ: case OPT_INQ|OPT_UNIT: cmd[0]=0xFF; cmd[1]=0x10; cmd[11]=0; if ((err=cmd.transport(READ,word,sizeof(word)))) sperror ("BENQ_FFh(10h)",err); else { brand = bookname (word[0],unknown), printf ("The unit will brand DVD+R media as DVD%s\n", brand?brand:unknown); } case OPT_INQ|OPT_UNITRW: cmd[0]=0xFF; cmd[1]=0x00; cmd[11]=0; if ((err=cmd.transport(READ,word,sizeof(word)))) sperror ("BENQ_FFh(00h)",err); else { brand = bookname (word[0],unknown), printf ("The unit will format DVD+RW media as DVD%s\n", brand?brand:unknown); } break; case OPT_INQ|OPT_UNITR: cmd[0]=0xFF; cmd[1]=0x10; cmd[11]=0; if ((err=cmd.transport(READ,word,sizeof(word)))) sperror ("BENQ_FFh(10h)",err); else { brand = bookname (word[0],unknown), printf ("The unit will brand DVD+R media as DVD%s\n", brand?brand:unknown); } if (gen<3) break; cmd[0]=0xFF; cmd[1]=0x10; cmd[2]=0x01; cmd[11]=0; if ((err=cmd.transport(READ,word,sizeof(word)))) sperror ("BENQ_FFh(10h,1)",err); else { brand = bookname (word[0],unknown), printf ("The unit will brand DVD+R DL meda as DVD%s\n", brand?brand:unknown); } break; case OPT_MEDIA: if (profile!=0x1A) { fprintf (stderr,":-( action is applicable to DVD+RW only\n"); break; } if (book != 0x92 && book != 0x01) { fprintf (stderr,":-( BookType#%02x is not applicable\n",book); break; } #if 0 cmd[0]=0x1E; // PREVENT/ALLOW MEDIA REMOVAL cmd[4]=1; // "Prevent" cmd[5]=0; if ((err=cmd.transport())) { sperror ("PREVENT MEDIA REMOVAL",err); break; } #endif cmd[0]=0xFE; cmd[1]=2; cmd[2]=book; cmd[11]=0; if ((err=cmd.transport())) sperror ("BENQ_FEh(2)",err); else action = INTERNAL_RELOAD; break; case OPT_UNIT: if (profile==0x1B || profile==0x2B) goto set_unitr; case OPT_UNITRW: if (book != 0x92 && book != 0x01) { fprintf (stderr,":-( BookType#%02x is not applicable\n",book); break; } cmd[0]=0xFE; cmd[1]=4; cmd[2]=book; cmd[11]=0; if ((err=cmd.transport())) sperror ("BENQ_FEh(4)",err); else printf ("Unit was instructed to format DVD+RW as DVD%s\n", book==0x01?"-ROM":"+RW"); break; case OPT_UNITR: set_unitr: if (book != 0xA1 && book != 0x01) { fprintf (stderr,":-( BookType#%02x is not applicable\n",book); break; } cmd[0]=0xFE; cmd[1]=5; cmd[2]=book; cmd[11]=0; if ((err=cmd.transport())) sperror ("BENQ_FEh(5)",err); else printf ("Unit was instructed to brand DVD+R as DVD%s\n", book==0x01?"-ROM":"+R"); if (gen<3) break; cmd[0]=0xFE; cmd[0]=5; cmd[2]=book==0x01?0x01:0xE1; cmd[3]=1; cmd[11]=0; if ((err=cmd.transport())) sperror ("BENQ_FEh(5,1)",err); else printf ("Unit was instructed to brand DVD+R DL as DVD%s\n", book==0x01?"-ROM":"+R DL"); break; default: break; } cmd[0]=0xFD; cmd[1]=0xF2; cmd[2]='B'; cmd[3]='E'; cmd[4]='N'; cmd[5]='Q'; cmd[11]=0; if ((err=cmd.transport())) sperror ("BENQ_FDh(F2h)",err); if (action!=INTERNAL_RELOAD) return 0; cmd[0]=0x1E; // PREVENT/ALLOW MEDIA REMOVAL cmd[5]=0; if ((err=cmd.transport())) return sperror ("ALLOW MEDIA REMOVAL",err),1; cmd[0]=0x1B; // START/STOP UNIT cmd[4]=0x2; // "Eject" cmd[5]=0; if ((err=cmd.transport())) return sperror ("EJECT",err),1; cmd[0]=0x1B; // START/STOP UNIT cmd[4]=0x3; // "Load" cmd[5]=0; if ((err=cmd.transport())) return sperror ("LOAD TRAY",err),1; if (wait_for_unit (cmd)) return 1; return mediainfo (cmd); } int btc (Scsi_Command &cmd,int action,int book,int gen=0) { int err,obligatory=0; const char *brand=""; switch (action) { case OPT_INQ|OPT_MEDIA: break; case OPT_INQ: case OPT_INQ|OPT_UNIT: case OPT_INQ|OPT_UNITRW: case OPT_INQ|OPT_UNITR: fprintf (stderr,":-( Can't inquiry unit settings, " "you have to set booktype prior every " "recording to be sure.\n"); return 1; break; case OPT_MEDIA: if (profile!=0x1A && profile!=0x14 && profile!=13) { fprintf (stderr,":-( action is applicable to DVD±RW only\n"); break; } obligatory = (profile==0x1A)?0x92:0x32; if (book!=obligatory && book!=0x01) { fprintf (stderr,":-( BookType#%02x is not applicable\n",book); break; } cmd[0]=0x1E; // PREVENT/ALLOW MEDIA REMOVAL cmd[4]=1; // "Prevent" cmd[5]=0; if ((err=cmd.transport())) { sperror ("PREVENT MEDIA REMOVAL",err); break; } cmd[0]=0xFA; cmd[2]=book; cmd[10]=0xAA; cmd[11]=0xFF; if ((err=cmd.transport())) sperror ("BTC_FAh[2]",err); else wait_for_unit (cmd); cmd[0]=0x1E; // PREVENT/ALLOW MEDIA REMOVAL cmd[5]=0; if ((err=cmd.transport())) return sperror ("ALLOW MEDIA REMOVAL",err),1; break; case OPT_UNIT: if (profile==0x1B || profile==0x2B || profile==0x11) goto set_unitr; case OPT_UNITRW: fprintf (stderr,":-( Can't set RW unit settings, " "manipulate media.\n"); return 1; break; case OPT_UNITR: set_unitr: // unit requires media in, so we can rely on profile... if (profile==0x1B) obligatory=0xA1, brand="+R"; else if (profile==0x2B) obligatory=0xE1, brand="+R DL"; else obligatory=0x20, brand="-R"; if (book!=obligatory && book!=0x01) { fprintf (stderr,":-( BookType#%02x is not applicable\n",book); return 1; } cmd[0]=0xFA; cmd[3]=book; cmd[4]=book==0x01?0:1; cmd[10]=0xAA; cmd[11]=0xFF; if ((err=cmd.transport())) return sperror ("BTC_FAh[3]",err),1; else printf ("Unit was instructed to brand DVD%s as DVD%s\n", brand,book==0x01?"-ROM":brand); return 0; break; default: break; } return mediainfo (cmd); } int liteon (Scsi_Command &cmd,int action,int book,unsigned char *buf=NULL) { int err; switch (action) { case OPT_INQ|OPT_MEDIA: break; case OPT_INQ: case OPT_INQ|OPT_UNIT: case OPT_INQ|OPT_UNITRW: case OPT_INQ|OPT_UNITR: if (buf==NULL || buf[0]==0) { printf ("Unit will brand DVD+plus media with " "corresponding booktype, e.g. DVD+R as DVD+R...\n"); return buf==NULL?1:0; } if (buf[0]==1) printf ("Unit will brand DVD+plus media as DVD-ROM\n"); else if (buf[0]==2) printf ("Unit will format DVD+RW media as DVD+RW\n"); else fprintf (stderr,":-? Insane unit setting %02x\n",buf[0]); return buf[0]==1?0:1; break; case OPT_MEDIA: fprintf (stderr,":-( Direct DVD+RW media manipulations " "are not supported.\n" " Use -unit flag instead and apply " "-dvd-compat recordinging procedure.\n"); return 1; break; case OPT_UNIT: case OPT_UNITRW: case OPT_UNITR: cmd[0]=0xDF; cmd[2]=0x0F; cmd[3]=1; cmd[4]=book==0x01?1:0; cmd[11]=0; if ((err=cmd.transport())) return sperror ("LITEON_DFh[1]",err),1; if (book==0x01) printf ("Unit was instructed to brand DVD+plus media " "as DVD-ROM\n"); else printf ("Unit was instructed to brand DVD+plus media with " "corresponding booktype, e.g. DVD+R as DVD+R...\n"); return 0; break; default: break; } return mediainfo (cmd); } // As per http://www-user.tu-chemnitz.de/~noe/Bitsetting/ int plextor (Scsi_Command &cmd,int action,int book,int gen=2) { int err; unsigned char vector[8]; switch (action) { case OPT_INQ|OPT_MEDIA: break; case OPT_INQ|OPT_UNITRW: case OPT_UNITRW: case OPT_MEDIA: fprintf (stderr,":-( DVD+RW booktyping is not supported\n"); return 1; break; case OPT_INQ: case OPT_INQ|OPT_UNIT: case OPT_INQ|OPT_UNITR: cmd[0] = 0xE9; cmd[2] = 0x22; cmd[3] = 0x0A; cmd[9] = sizeof(vector); cmd[11] = 0; if ((err=cmd.transport(READ,vector,sizeof(vector)))) return sperror ("PLEXTOR_E9h(A)",err),1; printf ("Unit will brand DVD+R media as DVD%s\n", vector[2]?"-ROM":"+R"); if (gen<3) return 0; cmd[0] = 0xE9; cmd[2] = 0x22; cmd[3] = 0x0E; cmd[9] = sizeof(vector); cmd[11] = 0; if ((err=cmd.transport(READ,vector,sizeof(vector)))) return sperror ("PLEXTOR_E9h(E)",err),1; printf ("Unit will brand DVD+R DL media as DVD%s\n", vector[2]?"-ROM":"+R DL"); return 0; break; case OPT_UNIT: case OPT_UNITR: cmd[0] = 0xE9; cmd[1] = 0x10; cmd[2] = 0x22; cmd[3] = 0x0A; cmd[5] = book&0xF0?0:1; cmd[9] = sizeof(vector); cmd[11] = 0; if ((err=cmd.transport(READ,vector,sizeof(vector)))) return sperror ("PLEXTOR_E9h(A)",err),1; printf ("Unit was instructed to brand DVD+R media as DVD%s\n", book&0xF0?"+R":"-ROM"); if (gen<3) return 0; cmd[0] = 0xE9; cmd[1] = 0x10; cmd[2] = 0x22; cmd[3] = 0x0E; cmd[5] = book&0xF0?0:1; cmd[9] = sizeof(vector); cmd[11] = 0; if ((err=cmd.transport(READ,vector,sizeof(vector)))) return sperror ("PLEXTOR_E9h(E)",err),1; printf ("Unit was instructed to brand DVD+R DL media as DVD%s\n", book&0xF0?"+R DL":"-ROM"); return 0; break; default: break; } return mediainfo (cmd); } int lg (Scsi_Command &cmd,int action,int book) { int err; unsigned char vector[4]; switch (action) { case OPT_INQ|OPT_MEDIA: break; case OPT_INQ|OPT_UNITRW: case OPT_UNITRW: case OPT_MEDIA: fprintf (stderr,":-( DVD+RW booktyping is not supported\n"); return 1; break; case OPT_INQ: case OPT_INQ|OPT_UNIT: case OPT_INQ|OPT_UNITR: cmd[0] = 0xFA; cmd[8] = sizeof(vector); cmd[9] = 0; if ((err=cmd.transport(READ,vector,sizeof(vector)))) return sperror ("LG_FAh",err),1; printf ("Unit will brand DVD+R media as DVD%s\n", vector[0]&0xF0?"+R":"-ROM"); printf ("Unit will brand DVD+R DL media as DVD%s\n", vector[1]&0xF0?"+R DL":"-ROM"); return 0; break; case OPT_UNIT: case OPT_UNITR: memset (vector,0,sizeof(vector)); if (book&0xF0) vector[0] = 0xA0, // DVD+R vector[1] = 0xE0; // DVD+R DL cmd[0] = 0xFC; cmd[2] = '+'; cmd[3] = 'R'; cmd[4] = 'T'; cmd[5] = 'B'; cmd[8] = sizeof(vector); cmd[9] = 0; if ((err=cmd.transport(WRITE,vector,sizeof(vector)))) return sperror ("LG_FCh",err),1; printf ("Unit was instructed to brand DVD+R media as DVD%s\n", book&0xF0?"+R":"-ROM"); return 0; break; default: break; } return mediainfo (cmd); } int main(int argc,char *argv[]) { Scsi_Command cmd; unsigned char buf[128]; int book=0,action=0; int plusgeneration=0,dashcapable=0,ramcapable=0; int i,err,hp=0,plx=0; for(dev=NULL,i=1;i264) { fprintf (stderr,":-( insane profile list length [%d]\n",len); return 1; } unsigned char *list=new unsigned char[len]; cmd[0]=0x46; // GET CONFIGURATION cmd[1]=2; cmd[7]=len>>8; cmd[8]=len; cmd[9]=0; if ((err=cmd.transport(READ,list,len))) return sperror("GET CONFIGURATION",err),1; for (plusgeneration=1,i=12;i0 && (dvd_C0[4]&0x02)) { printf ("The disc is write protected already.\n"); break; } else if (rdonly<0 && !(dvd_C0[4]&0x02)) { printf ("The disc is unprotected already.\n"); break; } memset (dvd_C0,0,sizeof(dvd_C0)); dvd_C0[1]=6; if (rdonly>0) dvd_C0[4]|=2; // "PWP" on else dvd_C0[4]&=~2; // "PWP" off cmd[0]=0xBF; // SEND DVD STRUCTURE cmd[7]=0xC0; cmd[9]=sizeof(dvd_C0); cmd[11]=0; if ((err=cmd.transport(WRITE,dvd_C0,sizeof(dvd_C0)))) { sperror("SEND DVD STRUCTURE#C0",err); return 1; } // Verify... cmd[0]=0xAD; // READ DVD STRUCTURE cmd[7]=0xC0; cmd[9]=sizeof(dvd_C0); cmd[11]=0; if ((err=cmd.transport(READ,dvd_C0,sizeof(dvd_C0)))) { sperror("READ DVD STRUCTURE#C0",err); return 1; } printf ("Persistent Write Protection is %s\n", dvd_C0[4]&0x02 ? "on" : "off"); } while (0); return 0; } dvd+rw-tools-7.1/rpl8.cpp0000644000000100000010000002210507544636012013372 0ustar binbin#if 0 # # The code is in public domain, May-September 2002. # # RICOH Program Loader 8 (primarily DVD+RW units) for Linux 2.4 and # upwards. So far has been tested with: # - HP dvd100i # - Ricoh MP5120A # - Philips DVDRW208 # - HP dvd200i # # This is second version adding support for Program Loader 9 and # firmware consistency check. # # The program never updates Program Loader code so that you should # always be able to downgrade your firmware in case something goes # wrong. # # - make sure you have no media in drive, optionally go to single user # mode; # - run 'rpl8 /dev/cdrom firmware.image.bin'; # - observe the program backing up your current firmware and uploading # new one; # - take system down and cycle the power; # # There is -dump-only option which only backs up firmware. Note that # you might have to cycle the power even after -dump-only. # # "ide-scsi: Strange, packet command initiated yet DRQ isn't asserted" # surrounded by other nasty messages logged on console is "normal." # /bin/sh << EOS MODNAME=\`expr "/$0" : '\(.*[^/]\)/*$' : '.*/\(..*\)' : '\(.*\)\..*$'\` case "`uname -s`" in SunOS) (set -x; g++ -fno-exceptions -O -o \$MODNAME "$0" -lvolmgt) ;; *) (set -x; g++ -fno-exceptions -O -o \$MODNAME "$0") ;; esac EOS exit #endif #if defined(__linux) #define _XOPEN_SOURCE 500 #endif #include #include #include #include #include #include #include #include #include "transport.hxx" #define BUFFER_BLOCK (16*1024) #define FIRMWARE_SIZE (1024*1024) #define MAGIC_OFFSET (32*1024) #if FIRMWARE_SIZE%BUFFER_BLOCK!=0 #error "Invalid BUFFER_BLOCK" #endif #if MAGIC_OFFSET%BUFFER_BLOCK!=0 #error "Invalid BUFFER_BLOCK" #endif int main (int argc,char *argv[]) { int fd,in,out,off,j; struct stat sb; char dump[64],*meaning; unsigned char *buffer,*firmware,sigfile[6],sigunit[6],ascq='\xFF'; struct { unsigned char type,skip[7]; char vendor[8],ident[16],revision[4]; } inq; Scsi_Command cmd; unsigned char *sense=cmd.sense(); if (argc<3) { fprintf (stderr,"usage: %s /dev/cdrom " "[-[dump-only]|firmware.bin]\n",argv[0]); return 1; } if ((fd=open ("/dev/zero",O_RDWR)) < 0) { fprintf (stderr,"unable to open(\"/dev/zero\"): "), perror(NULL); return 1; } buffer = (unsigned char *)mmap(NULL,BUFFER_BLOCK,PROT_READ|PROT_WRITE, MAP_PRIVATE,fd,0); if (buffer==MAP_FAILED) { fprintf (stderr,"unable to anonymously mmap %d bytes: ",BUFFER_BLOCK), perror (NULL); return 1; } close (fd); if (!cmd.associate(argv[1])) { fprintf (stderr,"%s: unable to open: ",argv[1]), perror (NULL); return 1; } cmd[0]=0x12; // INQUIRY cmd[4]=sizeof(inq); cmd[5]=0; if (cmd.transport(READ,(unsigned char *)&inq,sizeof(inq))) { perror("unable to INQUIRY, examine /var/log/message"); return 1; } if ((inq.type&0x1F) != 5) { fprintf (stderr,"\"%s\" is not a CD-ROM device\n",argv[1]); return 1; } cmd[0]=0x00; // TEST UNIT READY cmd[5]=0x80; if (!cmd.transport()) { fprintf (stderr,"\"%s\" is ready, is media present?\n",argv[1]); return 1; } if (sense[12] != 0x3A) // should reply with "NO MEDIA" { fprintf (stderr,"\"%s\": unexpected ASC=%02xh/ASCQ=%02xh\n", argv[1],sense[12],sense[13]); return 1; } sprintf (dump,"%.8s%.16s%.4s.bin",inq.vendor,inq.ident,inq.revision); for(j=0;dump[j];j++) if (dump[j]==' ') dump[j]='_'; in=-1, firmware=NULL; if (argv[2][0] != '-') { unsigned short cksum; int i; if ((in=open(argv[2],O_RDONLY)) < 0) { fprintf (stderr,"unable to open(\"%s\"): ",argv[2]), perror (NULL); return 1; } if (fstat(in,&sb) < 0) { fprintf (stderr,"unable to stat(\"%s\"): ",argv[2]), perror (NULL); return 1; } if (sb.st_size != FIRMWARE_SIZE) { fprintf (stderr,"\"%s\": size is insane\n",argv[2]); return 1; } firmware=(unsigned char *)mmap(NULL,FIRMWARE_SIZE,PROT_READ, MAP_PRIVATE,in,0); if (firmware==MAP_FAILED) { fprintf (stderr,"unable to mmap(\"%s\"): ",argv[2]), perror (NULL); return 1; } close (in); memcpy(sigfile,firmware+MAGIC_OFFSET+4,6); if (memcmp(sigfile,"MMP5",4)) { fprintf (stderr,"\"%s\": signature is insane\n",argv[2]); return 1; } for (cksum=0,i=MAGIC_OFFSET;i>8) != firmware[FIRMWARE_SIZE-2] ) { fprintf (stderr,"\"%s\" is corrupted or encrypted\n",argv[2]); return 1; } } if ((out=open(dump,O_WRONLY|O_CREAT|O_EXCL,0666)) < 0) { fprintf (stderr,"unable to creat(\"%s\"): ",dump), perror (NULL); return 1; } cmd[0]=0xE4; // INVOKE PROGRAM LOADER cmd[9]=(1<<6)&0x40; if (cmd.transport()) { fprintf (stderr,"\"%s\": unable to invoke RICOH Program Loader " "[ASC=%02xh/ASCQ=%02xh]\n", argv[1],sense[12],sense[13]); return 1; } do { // not really a loop, just a way to get rid of goto's... poll(NULL,0,250); cmd[0]=0xE4; // INVOKE PROGRAM LOADER cmd[9]=(1<<6)&0x40; if (cmd.transport()) { fprintf (stderr,"\"%s\"[E4]: unexpected ASC=%02xh/ASCQ=%02xh\n", argv[1],sense[12],sense[13]); break; } poll(NULL,0,250); cmd[0]=0x12; // INQUIRY cmd[4]=sizeof(inq); cmd[5]=0; if (cmd.transport(READ,(unsigned char *)&inq,sizeof(inq))) { perror("unable to INQUIRY, examine /var/log/message"); break; } if (memcmp(&inq.vendor,"RICOH Program Loader 8",24) && memcmp(&inq.vendor,"RICOH Program Loader 9",24) ) { fprintf (stderr,"\"%s\": unknown Program Loader " "%.8s|%.16s|%.4s\n", argv[1],inq.vendor,inq.ident,inq.revision); break; } fprintf (stderr,"\"%s\": dumping firmware image to \"%s\" ", argv[1],dump); for (off=0;off>24)&0xFF; cmd[3]=(off>>16)&0xFF; cmd[4]=(off>>8)&0xFF; cmd[5]=(off)&0xFF; cmd[7]=(BUFFER_BLOCK>>8)&0xFF; cmd[8]=(BUFFER_BLOCK)&0xFF; cmd[9]=0; if (cmd.transport(READ,buffer,BUFFER_BLOCK)) { fprintf (stderr,"\"%s\"[E3]: unexpected " "ASC=%02xh/ASCQ=%02xh\n", argv[1],sense[12],sense[13]); in=-1; break; } if (write(out,buffer,BUFFER_BLOCK) != BUFFER_BLOCK) { fprintf (stderr,"unable to write(\"%s\"): ",dump), perror(NULL); in=-1; break; } fprintf (stderr,"."); if (off==MAGIC_OFFSET) { memcpy(sigunit,buffer+4,6); } } fprintf (stderr,"\n"); if (in==-1) break; /* dump only or we failed to backup */ #if 1 if (memcmp(sigunit,sigfile,6)) { fprintf (stderr,"\"%s\": firmware signature mismatch " "[%.6s|%.6s]\n", argv[1],sigunit,sigfile); break; } #endif fprintf (stderr,"\"%s\": uploading firmware image",argv[1]); for (off=0;off>16)&0xFF; cmd[6]=(BUFFER_BLOCK>>8)&0xFF; cmd[7]=(BUFFER_BLOCK)&0xFF; cmd[11]=0; if (cmd.transport(WRITE,firmware+off,BUFFER_BLOCK)) { fprintf (stderr,"\"%s\"[E2]: unexpected " "ASC=%02xh/ASCQ=%02xh\n", argv[1],sense[12],sense[13]); in=-1; break; } fprintf (stderr,"."); } fprintf (stderr,"\n"); if (in==-1) break; /* failed to upload */ fprintf (stderr,"\"%s\": unlocking firmware\n",argv[1]); cmd[0]=0xE0; // UNLOCK FIRMWARE cmd[3]=0; cmd[9]=0; if (cmd.transport()) { fprintf (stderr,"\"%s\"[E0]: unexpected " "ASC=%02xh/ASCQ=%02xh\n", argv[1],sense[12],sense[13]); break; } fprintf (stderr,"\"%s\": ¡POINT OF NO RETURN!\n",argv[1]); poll(NULL,0,3000); signal (SIGINT,SIG_IGN); signal (SIGTERM,SIG_IGN); cmd[0]=0xE1; // FIRE IT UP // -----v----- don't update the Program Loader code cmd[9]=(0<<6)&0x40; if (cmd.transport()) { fprintf (stderr,"\"%s\"[E1]: unexpected " "ASC=%02xh/ASCQ=%02xh\n", argv[1],sense[12],sense[13]); break; } while(1) { poll(NULL,0,250); cmd[0]=0x00; // TEST UNIT READY cmd[5]=0x80; if (!cmd.transport()) continue; // never happens? if (sense[12] == 0xF7) { if (ascq != sense[13]) { switch (ascq=sense[13]) { case 0: meaning="preparing"; break; case 1: meaning="erasing contents"; break; case 2: meaning="writing contents"; break; case 3: meaning="verifying contents"; break; default:meaning="unknown meaning"; break; } fprintf (stderr,"\"%s\": %s\n",argv[1],meaning); } } else if (sense[12] == 0x3A) /* no media */ { fprintf (stderr,"\"%s\": firmware upgrade succeeded:-)\n", argv[1]), fprintf (stderr,"\"%s\": " "¡CYCLE THE POWER BEFORE PROCEEDING!\n", argv[1]); break; } else { fprintf (stderr,"\"%s\"[0]: unexpected " "ASC=%02xh/ASCQ=%02xh\n", argv[1],sense[12],sense[13]); in=-1; break; } } } while (0); cmd[0]=0xE4; // TERMINATE PROGRAM LOADER cmd[9]=(0<<6)&0x40; if (cmd.transport()) { fprintf (stderr,"\"%s\": unexpected ASC=%02xh/ASCQ=%02xh\n", argv[1],sense[12],sense[13]); return 1; } return in<0; } dvd+rw-tools-7.1/btcflash.cpp0000644000000100000010000001642610044303426014271 0ustar binbin/* * Firmware flash utility for BTC DRW1008 DVD+/-RW recorder * Version 2004/04/29 * By David Huang * This work is dedicated to the public domain * * This utility may also work with other BTC DVD recorders, such as * the DRW1004 and DRW1108, but they have not been tested. * * USE AT YOUR OWN RISK! * btcflash is provided AS IS, with NO WARRANTY, either expressed or implied. * * Requires "transport.hxx" from Andy Polyakov's DVD+RW tools: * http://fy.chalmers.se/~appro/linux/DVD+RW/tools/ * If obtained as part of dvd+rw-tools it can be built with * 'make +btcflash'. * * Firmware files may be obtained by running BTC's Windows flash * utility, then searching in the WINDOWS\TEMP or WINNT\TEMP directory * for a *.HEX file. It will probably be in a subdirectory named * PAC*.tmp.DIR, and the HEX file will be named Vnnnn.HEX, where nnnn * is the firmware version number. You'll also find IDEFLASH.EXE or * BTCFLASH.EXE in the same directory. * * This utility will also accept firmware files in ".BIN" format. */ #include #include #include #include #include "transport.hxx" const unsigned int FLASHSIZE = 0x100000; /* BTC flash is 1MB */ unsigned char *loadfirmware(const char *); int getbyte(char *&); unsigned short calcsum(unsigned char *); unsigned char * loadfirmware(const char *firmware) { FILE *f; char line[80], *p; unsigned char *fwbuf; int bank, length, offset, type, hexsum; int i, b; fwbuf = new unsigned char[FLASHSIZE]; if (!fwbuf) { fprintf(stderr, "Could not allocate memory for firmware\n"); return NULL; } f = fopen(firmware, "r"); if (!f) { fprintf(stderr, "%s: Unable to open: ", firmware); perror(NULL); return NULL; } // Get length of file. If it's exactly FLASHSIZE, assume it's a // .bin file. Otherwise, try to read it as a .hex file. fseek(f, 0, SEEK_END); if (ftell(f) == FLASHSIZE) { rewind(f); if (fread(fwbuf, 1, FLASHSIZE, f) != FLASHSIZE) { fprintf(stderr, "%s: Short read\n", firmware); return NULL; } fclose(f); return fwbuf; } rewind(f); memset(fwbuf, 0xff, FLASHSIZE); bank = 0; while (fgets(line, sizeof(line), f)) { if (line[0] != ':') continue; p = line + 1; length = getbyte(p); offset = getbyte(p) << 8 | getbyte(p); type = getbyte(p); if (length < 0 || offset < 0 || type < 0 || (type != 0 && length != 0)) { fprintf(stderr, "Malformed line: %s", line); return NULL; } else if (length == 0) { if (strncmp(line, ":00000155AA", 11) == 0) { if (++bank >= 16) { fprintf(stderr, "Firmware file larger than 1MB\n"); return NULL; } continue; } else if (strncmp(line, ":00000001FF", 11) == 0) break; else { fprintf(stderr, "Malformed line: %s", line); return NULL; } } hexsum = (length + (offset >> 8) + (offset & 0xff)) & 0xff; for (i = 0; i < length; i++, offset++) { b = getbyte(p); hexsum = (hexsum + b) & 0xff; if (b < 0) { fprintf(stderr, "Short line: %s", line); return NULL; } fwbuf[(bank << 16) | offset] = (char)b; } hexsum = (0x100 - hexsum) & 0xff; if (hexsum != getbyte(p)) { fprintf(stderr, "Checksum mismatch: %s", line); return NULL; } } fclose(f); if (bank != 15) { fprintf(stderr, "Firmware file too small\n"); return NULL; } return fwbuf; } int getbyte(char *&p) { int h, l; h = *p; if (h >= '0' && h <= '9') h -= '0'; else if (h >= 'A' && h <= 'F') h -= 'A' - 10; else if (h >= 'a' && h <= 'f') h -= 'a' - 10; else return -1; l = *(p+1); if (l >= '0' && l <= '9') l -= '0'; else if (l >= 'A' && l <= 'F') l -= 'A' - 10; else if (l >= 'a' && l <= 'f') l -= 'a' - 10; else return -1; p += 2; return (h << 4) | l; } unsigned short calcsum(unsigned char *fwbuf) { unsigned int flashsum, i; for(flashsum = 0, i = 0; i < FLASHSIZE; i++) flashsum += fwbuf[i]; return (flashsum & 0xffff); } int main(int argc, char *argv[]) { const char *fwfile; char confirm[5]; unsigned char *fwbuf, inq[128], csbuf[32]; unsigned short checksum; Scsi_Command cmd; int err; unsigned int offset; if (argc < 3) { fprintf(stderr, "Usage: %s /dev/cdrom firmware\n", argv[0]); return 1; } printf("BTC DVD+/-RW firmware flash utility 2004/04/29\n"); printf("USE AT YOUR OWN RISK!\n\n"); if (!cmd.associate(argv[1])) { fprintf(stderr, "%s: unable to open: ", argv[1]); perror (NULL); return 1; } fwfile = argv[2]; if (!(fwbuf = loadfirmware(fwfile))) return 1; checksum = calcsum(fwbuf); printf("Loaded firmware from %s\nFirmware checksum is %04X\n", fwfile, checksum); cmd[0] = 0x12; // INQUIRY cmd[4] = 36; cmd[5] = 0; if (err = cmd.transport(READ, inq, 36)) { sperror("INQUIRY", err); return 1; } printf("Drive is currently: [%.8s][%.16s][%.4s]\n", inq+8, inq+16, inq+32); printf("Firmware appears to be: [%.8s][%.16s][%.4s]\n\n", fwbuf+0x40bc, fwbuf+0x40c4, fwbuf+0x40d4); if (strncmp((char*)inq + 8, (char*)fwbuf + 0x40bc, 24) != 0) printf( "***********************************************" "***********\n" "WARNING! THIS FIRMWARE DOES NOT SEEM TO BE FOR " "THIS DRIVE!\n" "***********************************************" "***********\n"); printf("Type \"YES\" to proceed with flash: "); fflush(stdout); fgets(confirm, sizeof(confirm), stdin); if (strcmp(confirm, "YES\n") != 0) { printf("\nFlash canceled.\n"); return 0; } printf("\nUploading firmware...\n"); // Upload firmware for (offset = 0; offset < FLASHSIZE; offset += 0x1000) { cmd[0] = 0x3B; // WRITE BUFFER cmd[1] = 6; // Download Microcode with Offsets cmd[2] = 0; // Buffer ID 0 cmd[3] = (offset >> 16) & 0xff; cmd[4] = (offset >> 8) & 0xff; cmd[5] = 0x20; cmd[6] = 0; // Length 0x1000 cmd[7] = 0x10; cmd[8] = 0; cmd[9] = 0; if (err = cmd.transport(WRITE, fwbuf + offset, 0x1000)) { sperror("WRITE BUFFER[1]", err); return 1; } } // Upload checksum memset(csbuf, 0, 32); csbuf[30] = (checksum >> 8); csbuf[31] = (checksum & 0xff); cmd[0] = 0x3B; // WRITE BUFFER cmd[1] = 6; // Download Microcode with Offsets cmd[2] = 0; // Buffer ID 0 cmd[3] = 0; // Offset 0 cmd[4] = 0; cmd[5] = 0; cmd[6] = 0; // Length 0x20 cmd[7] = 0; cmd[8] = 0x20; cmd[9] = 0; if (err = cmd.transport(WRITE, csbuf, 0x20)) { sperror("WRITE BUFFER[2]", err); return 1; } printf("Flashing drive...\n"); // Firmware uploaded; now flash it! cmd[0] = 0x3B; // WRITE BUFFER cmd[1] = 7; // Download Microcode with Offsets and Save cmd[2] = 0; // Buffer ID 0 cmd[3] = 0; // Offset 0 cmd[4] = 0; cmd[5] = 0; cmd[6] = 0; // Length 0 cmd[7] = 0; cmd[8] = 0; cmd[9] = 0; if (err = cmd.transport()) { sperror("WRITE BUFFER[3]", err); return 1; } sleep(50); // Let drive sit for a while before bothering it while (1) { sleep(1); cmd[0] = 0; // TEST UNIT READY cmd[5] = 0; err = cmd.transport(); // Wait until it returns either ready or // not ready/medium not present if ((err == 0) || (SK(err) == 2 && ASC(err) == 0x3A)) break; } cmd[0] = 0x12; // INQUIRY cmd[4] = 36; cmd[5] = 0; if (err = cmd.transport(READ, inq, 36)) { sperror("INQUIRY[2]", err); return 1; } printf("Drive is now: [%.8s][%.16s][%.4s]\n\n", inq+8, inq+16, inq+32); printf("Please reboot before using the drive.\n"); return 0; } dvd+rw-tools-7.1/LICENSE0000444000000100000010000004307605022066500013002 0ustar binbin GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave, Cambridge, MA 02139, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. dvd+rw-tools-7.1/index.html0000644000000100000010000017534610763210131014001 0ustar binbin Blu-ray Disc/DVD+RW/+R/-R[W] for Linux


Blu-ray Disc/ DVD+RW/+R/ -R[W] for Linux

by <appro@fy.chalmers.se>, March 2008
Japanese


Q. What is this page (not) about?
A.  Maybe to your disappointment it is not about video(*). The scope of this page is primarily computer storage applications of Blu-ray Disc and DVD±RW/±R, things like backup, archiving, data exchange... The downloadable files are an optional [and essentially historical] Linux 2.4 kernel DVD+RW patch and a couple of user-land utilities dubbed as dvd+rw-tools.

(*) Though it doesn't mean that you can't burn DVD-Video discs with dvd+rw-tools. [Unlike Video-CD] DVD-Video is "molded" in an ordinary data file system and therefore no explicit support by the burning program is actually required. In other words it is the DVD-Video content preparation which is beyond the scope of this page.

Q. Kernel patch? What would it be good for?
A. It should be explicitly noted that the user-land utilities, dvd+rw-tools, do suffice for BD/DVD recording without explicit kernel support. So if they fulfill your requirements, then patching the kernel is by all means optional. Kernel support would make it possible to house a read-write file system, e.g. udf, vfat, ext2, etc. and accomodate "live" data on suitable optical media. However, I have to recommend to deploy it with caution, see tutorial for further details. Above mentioned 2.4 patch is provided mostly for historical reasons. Linux kernel version 2.6>=10 is equipped with packet writing driver which supports even DVD rewritable media, but I haven't tested it myself, so don't ask:-)

Q. What are the dvd+rw-tools for?
A. As implied/already mentioned - to master the Blu-ray Disc and DVD media, both +RW/+R and -R[W]. I could simply refer to the tutorial below, but figured that couple of words about the [original] design ideas behind growisofs, the principal burning utility, wouldn't harm. Even though a modified kernel can let you put for example an ext2 file system on DVD+RW, it's probably not very practical, because you most likely want to access the data on an arbitrary computer. Or in other words you most likely want ISO9660. The trouble is that you might as well want to add data now and then. And what options do you have in the lack of multiple sessions (no, DVD+RW has no notion of multiple sessions)? Complete re-mastering which takes more and more time as data set grows? Well, yes, unless you employ growisofs! Growisofs provides the way to both lay down and grow an ISO9660 file system on (as well as to burn an arbitrary pre-mastered image to) all supported optical media.

Q. But if they support both + and - recording strategies, why are they called dvd+rw-tools?
A. For historical/nostalgical reasons, as originally they did support exclusively DVD+plus. On the other hand now, when the vast majority of DVD burners that are being introduced to the market today are DVD+capable, the name most likely refers to your unit in either case. And you can always consider the plus in the name as notion of a unique quality, such as "seamless" multisessioning, not as reference to some particular format:-)

Q. Do I still need cdrtools?
A. Yes. It should be explicitly noted that growisofs is a front-end to mkisofs, i.e. invokes mkisofs to perform the actual ISO9660 file system layout. Secondly, the DVD burners available on the market can burn even CD-R[W] media and cdrecord is the tool for this job.

Q. Does it work with my recorder unit?
A. If your unit is MMC compliant, then the answer is "most likely it just does." Well, as the probability of your unit being non-MMC compliant is virtually zero, the answer in practice is unconditionally "most likely." The [core] tools were reported to work with a wide range of drives, including [but not limited to] HP dvd[12345]x0i, Ricoh MP512x, Philips DVDRW[248]xx, SONY DRU-[157]x0, NEC ND-[1234]xx0, TDK indiDVD 4x0N, Plextor PX-[57]xx, Benq DW[48]00A, OptoRite DD0[24]0x, Lite-On LDW-[4816]xxS, as well as nonplus units such as Pioneer DVR-x0[45679], LG GxA-40[248]x, Toshiba SD-R[56]112, Panasonic UJ-811, LF-D[35]1x, and not the least all-mighty SW-5582...

Q. Is there a GUI front-end available for dvd+rw-tools?
A. K3b, version 0.10 and later, and nautilus-cd-burner, version 0.5.1 and later, are both hiding growisofs behind their pretty buttons and menus:-) Keep in mind that those are not directly related to dvd+rw-tools development effort and GUI users should turn elsewhere for end-user support. Oh! dvd+rw-tools 5.10.x is a minimum requirement for GUI frontends...

Q. I don't run Linux. What are my options?
A. Version 5.4 adds support for OpenBSD/NetBSD. Version 5.6 adds support for Solaris 2.x [commercial licensing terms for distribution on Solaris are to be settled with Inserve Technology]. Version 5.8 features FreeBSD port contributed by Matthew Dillon, FreeBSD Development Team alumnus. Hewlett-Packard Company has donated HP-UX 11 support for 5.14(*). IRIX 6.x support appears in 5.19, Win32 one - in 6.0, while Mac OS X - in 7.0...

¡ Common usage tip! Whenever separately available [and unless stated otherwise], do use character-type device entry with dvd+rw-tools, e.g. OpenBSD/NetBSD users should stick to /dev/rcdXc.
(*) As of 5.14 HP-UX support was classified as "initial." Version 5.18 in turn is the one which has undergone HP quality assurance testing and is delivered on HP software depot.


Foreword

As of May 2003 I've decided to advise users to turn to <cdwrite@other.debian.org> on support matters. It's an open list, meaning that you don't have to be subscribed to post a problem report. List archives can be found at both subscribe page and mail-archive.com. When submitting report, provide versioning information, exact command line, exact output generated by the program and complement it with dvd+rw-mediainfo output for resulting recording. Do check couple of last archived months, as the issue might have been discussed recently. If you've chosen to contact me personally and haven't heard back within a week or so, then you most likely overlooked something on this page. Please read it more attentively...

Special thanks for hardware donations [in chronological order]:
Inserve Technology  HP  LinuxFund  comm*tech 


Tutorial


  • If your burner unit is managed by some Linux(*) removable media automounting/autoplaying facility, such as autofs, supermount, subfs/submount, magicdev, autorun or similar, take it out of its control! I can't help you with the latter, check your system documentation (such as google perhaps:-) for specific instructions. Failure to take your unit out of Linux(*) automounting/autoplaying facility control can result in busted recording, a coaster! At the very least you have to make sure your unit is not automounted during recordings.
    (*) dvd+rw-tools support Solaris volume manager and IRIX mediad in more gracious manner and it's safe to leave recorder under their control.

  • Remember to consult Hardware Compatibility Notes for possible caveats or vendor-specific instructions for your unit. Well, such reminder belongs at the end of tutorial, but I consider it important enough to bring it up already here:-)

  • If you have an IDE unit and run 2.4.x kernel, you most likely want to "route" it through ide-scsi emulation layer by either:

    • passing "hdX=ide-scsi" argument to kernel;
    • appending following lines to your /etc/modules.conf:
      options ide-cd ignore=hdX
      pre-install sg modprobe ide-scsi
      pre-install sr_mod modprobe ide-scsi
      pre-install ide-scsi modprobe ide-cd
      

    Keep in mind that once hdX is routed through ide-scsi, you can no longer refer to /dev/hdX(*), but to corresponding /dev/scdN only.

    (*) well, except as in hdparm -d [0|1] /dev/hdX. As for DMA settings. Several users of NEC[-based] units have reported that their systems crash during DVD recording. The problem appears to be related to DMA settings, at least switching it off reportedly helps. The problem appears to be specific to some IDE chipsets...

  • If you have an external unit, just get it working as CD-ROM first. I myself have limited experience with USB or IEEE1394/Firewire optical storage devices and have to direct you elsewhere for specific instructions. I however am confident that if you manage to get your drive working reliably as CD-ROM and CD-R[W] burner, then you won't have any troubles with dvd+rw-tools either. USB connected drives were reported to be working fine since eternity. Firewire connected drives in turn were reported to fail miserably under 2.4.18. The failure didn't seem to be DVD recording related as it reportedly failed burning even CD-R media. Firewire support was substantially revamped in 2.4.19, and dvd+rw-tools were reported to work with this and later kernels.

  • If you're running 2.4.19 or .20, consider applying this drivers/scsi/sg.c patch. The bug is fixed in .21. I write "consider" and not "do" for the following reasons:

    • dvd+rw-tools are not affected by this bug (as they don't use SG_IO interface), cdrecord [potentially] is;
    • I however haven't actually experienced the problem with cdrecord (maybe yet, kernel could have managed to keep buffers neatly aligned while talking to cdrecord those times I tried), it was VMware that has failed miserably on me;

    As of version 5.6 dvd+rw-tools add support for SG_IO pass-through or in other words support for Linux 2>=5[.43], where "generic" SCSI interface can be bypassed by issuing SG_IO ioctl directly against block device, such as /dev/hdX. I wish it worked without need for interim patches #1 and #2, (the latter is relative to 2.5.69-75, the 1st problem is addressed in .71, 2nd one - .75-bk3 in "last minute" prior first 2.6 cut. As for 2.6 in more general sense. As you can imagine this new interface renders ide-scsi layer superfluous and "the[ir] official plan™" is to scrap it. I'm not really fond of the idea, but not for /dev/sg* account. I mean I [personally] would prefer to keep ide-scsi and use SG_IO pass-through with /dev/scdN, rather than with /dev/hdX:-)

    If you have to make dvd+rw-tools work under Linux kernel 2.6.8, then upgrade the tool-chain to 5.21.x or later and manually reward the installed binaries with set-root-uid flag. But the "supported" recommendation is to just stay away from this particular kernel version. As for 2.6>8, dvd+rw-tools 5.21.x is requirement. Oh! dvd+rw-booktype utility would require set-root-uid privilege then. Given its semi-official status and the fact that this utility works only with limited number of units, installation procedure abstains from installing dvd+rw-booktype set-root-uid, leaving this security sensitive choice to the end-user.

  • Download, unpack and compile the the tool-chain. To build the thing do pick the .tar.gz archive, which contains Makefile as well as .spec file. You will need both C and C++ compilers installed. Separate source code files found in the download catalog are provided mainly for on-line reference purposes (such as revision history:-).

    If your Linux kernel supports multiple ABIs (e.g. Linux-sparc64 can run even 32-bit Linux-sparc applications, as well as Linux-x86_64 can execute legacy 32-bit i386 binaries), make sure you compile for native 64-bit ABI (which can normally be done with 'make TARGET_ARCH=-m64'). The problem here is that 64-bit kernel has to explicitly convert ioctl structures passed by 32-bit applications and apparently it does really lousy job when it comes to CDROM_SEND_PACKET ioctl deployed by dvd+rw-tools.

  • As new media products and brands are being introduced to the market all the time, it apparently pays off to periodically check for firmware updates. For elder units firmware update might even be an absolute requirement for using new media. Special note for HP users. HP no longer posts firmware updates on a web-page. Instead they let some Windows auto-update gizmo to pick firmware updates among dvd[1-6]00*.exe files in their FTP directory, so that readers of this page tend to miss them...

  • Formatting the BD and DVD+RW media. Virgin BD and DVD+RW media needs to be initally formatted prior usage. Once again, only virgin BD and DVD+RW media needs to be formatted. As of version 5.10 growisofs detects blanks and applies initial formatting procedure automatically. Otherwise same effect can be achieved by passing the device name, e.g. /dev/scd0, as an argument to dvd+rw-format. Though in BD case it does offer more flexibility than growisofs. To make formatting process reasonably fast, less than 1 minute, the media gets formatted only partially, as you can notice by observing progress indicator displayed by dvd+rw-format. The final indicator value varies from firmware to firmware, values as low as 1.6% were observed. But it does not mean that you can only write that little. The unit keeps formatting transparently, as you add more data. Oh! Do keep in mind that DVD capacity of 4.7GB is expressed in salesman's GB, i.e. 10003 and not 10243. And so is one of BD.

    It was observed that excessive reformats can render DVD+RW media unusable already after 10-20 reformats. It appears to be a firmware deficiency, not some common media defect [at least it was perfectly possible to salvage the media in a unit of different brand], but I don't recommend [enforced] reformat in either case.

    Note that re-formatting procedure does not substitute for blanking. If you want to nullify the media, e.g. for privacy reasons, do it explicitly with 'growisofs -Z /dev/scdN=/dev/zero'. Otherwise just write over previous recording as it simply wasn't there, no re-formatting is required.

  • Burning with growisofs. There is hardly a need for manual for growisofs. In a nutshell growisofs just passes all command line arguments to mkisofs and dumps its output directly onto the media. The first part means that you basically can [well, should] consult mkisofs manual page and accompanying reference documentation (including multisession related section[s]) and the second part means that you shouldn't expect an ISO-image on the standard output (nor make sure you have enough free temporary storage:-). Differences from mkisofs command line are:

    • you may not use -o option;
    • you don't have to specify -C option, growisofs will construct one for you;
    • there is internal -Z option for initial session recording, this substitutes for originally suggested 'mkisofs | dd of=/dev/scd0';

    Otherwise everything that applies to [multisession] mastering with mkisofs applies to growisofs as well. For example just like with mkisofs you should make a note on which options you used to master the initial "session" with and stick to them, e.g.:

    growisofs -Z /dev/scd0 -R -J /some/files
    growisofs -M /dev/scd0 -R -J /more/files
    

    Oh! Do make sure you have at least mkisofs 1.14 on your $PATH (mkisofs 1.14 is part of cdrtools 1.10). If you consider passing /same/files as argument, or in other words consider deploying growisofs for incremental multisession backups, then you shall find this '-old-root' extension to mkisofs 2.0-2.01 simply indispensable. The idea and implementation by Patrick Ohly is to "graft" recording sessions as separate directories. Each backup increment/directory is ment to contain both updated files and references to previously backed up ones, which facilitates comparison between increments as well as fine-graded restore.

    Number of users asked about opposite to multisessioning: multivolume support. Being essentially a recording program growisofs does not support multiple volumes by itself. There're couple of front-ends I can recommend that arrange for this: scdbackup and shunt. But back to growisofs...

    In addition to intuitive -Z interpretation, growisofs [version 3.3 and later] recognizes special form of -Z command line option which permits burning of arbitrary pre-mastered images. The "magic" command is:

    growisofs -Z /dev/scd0=image.iso
    

    where image.iso represents an arbitrary object in the file system, such as file, named pipe or device entry. No, nothing is "growing" here and command name is counter-intuitive in this particular context. And here is even less intuitive:-) If you wish to burn down output generated by an arbitrary program, you can use:

    dumpsomething | growisofs -Z /dev/scd0=/dev/fd/0
    

    Burning BD-R/DVD±R implies extra limitations:

    • Needless to say that you have only one shot with -Z option:-)
    • Unlike DVD+RW, DVD±R media does have notion of multiple sessions. However! Not all legacy units can "see" beyond the first one. Few DVD-ROM units are capable of DVD-R multiborder playback, even fewer support DVD+R multisessioning. In other words your DVD burner might be the only unit in your vicinity capable to access data added at different occasions.
    • Even if your DVD unit does "sense" multiple sessions, Linux kernel [2.4] sometimes fails to pull that information from the drive:-( Till the problem is looked into and resolved you can work it around by reloading corresponding driver, most likely 'rmmod sr_mod'.
    • Linux kernel 2.6<10 users might experience problems mounting multisession media with last session starting beyond 2.2GB boundary. As fast-acting remedy I can suggest to route your unit through ide-scsi, the way it was under 2.4. Even though it's declared unsupported it actually still works in 2.6 (I for one still use it).
    • If you go for BD-R/DVD±R multisessioning, you have to use mkisofs from cdrtools-2.0 or later or apply this patch.
    • And when it comes to DVD+R Double Layer and DVD-R Dual Layer recordings, growisofs applies yet another limitation, purely artificial. Taking into consideration Double Layer media prices growisofs is programmed to refuse to perform unappendable recordings which are less than 1/2 of blank capacity and to advice to use single layer media instead.
    • DVD-R Dual Layer multisessioning is not supported for a reason discussed on the -RW companion page. Once again, as of the moment of this writing DVD-R Dual Layer recordings come out unappendable and can not be grown.

    And once again, do keep in mind that 4.7GB are salesman's GB, i.e. 10003 and not 10243. If translated to "real" GB, single layer DVD±R[W] capacity is not larger than 4.4GiB, and BD - not larger than 23.3GiB! It should also be noted that earlier growisofs versions did not check if there is enough space on media to accommodate the data set to be burned, meaning that it was your sole responsibility to make sure "overburn" condition is not raised. As of version 5.2 growisofs performs the necessary checks for you and refuses to start recording if "overburn" condition appears to be unavoidable. This behaviour can be overridden with -overburn command-line option.

  • If you're satisfied with growisofs, then you should just proceed to the next chapter and abstain from applying the optional 2.4.x kernel patch. If you haven't stopped reading beyond this line, download the patch, apply it, rebuild the kernel or modules and re-install (kernel or cdrom.o and sr_mod.o modules, whichever appropriate), but don't ask me how. As you could have noticed, patch targets SCSI CD-ROM module. This means that you have to "route" your IDE unit through ide-scsi to get this one working. To see it in action, insert formatted DVD+RW media and try to access it, 'dd if=/dev/scdN count=0' would do. Then verify that kernel logs "srN: mmc-3 profile: 1Ah". You should now be able to 'mkisofs -pad . | dd of=/dev/scdN obs=32k' or even 'mke2fs -b 2048 /dev/scdN' and observe kernel logging "srN: dirty DVD+RW media."

    Linux 2.6 DVD+RW kernel support is planned in line with DVD+MRW kernel support. This [unfortunately] means that industry has to deliver a DVD+MRW capable unit first. Yes, the last sentence means that despite all the promises, there are no such units available on the market yet. As of the 1st of August 2003, Ricoh MP5240A, Philips DVDRW416K or BenQ DW400A do not actually implement Mt.Rainier/EasyWrite support. It remains to be seen if they will offer it in form of firmware upgrade. In either case, the [original] project goal is not only read-write support for DVD+[M]RW capable units themselves, but even playback of DVD+MRW formatted media in legacy DVD-ROM units (when defect list will be read and interpreted by OS software in opposite to Mt.Rainier firmware).

  • Even though kernel now permits to build and mount arbitrary file system, there is one thing you must keep in mind before you just proceed, no matter how tempting it might appear.

    As you might know DVD+RW media can sustain only around 1000 overwrites. The thing about fully fledged file systems is that every read [or tight bunch of 'em] is accompanied by corresponding i-node update or in other words a write! Now, let's say you lookup the mount point (e.g. ls /mnt/dvd) ten times a day. This gives you a 100 days lifetime on your mountpoint and therefore media. Not really much, huh? So do use noatime mount option with DVD+RW media or have it mounted read-only most of the time. However! Every read-write mount "costs" a super-block update. So that if you remount the media say 3 times a day, it would last for about a year [supermount would exhaust the "budget" way sooner]... Defect management [in firmware, a.k.a. Mt.Rainier, or at file system level] would improve the situation, but ideally file system driver should definitely refrain from modifying the super-block [marking it dirty] if nothing was actually written since last mount. Given the development status of Linux UDF the chances for seeing the latter implemented [for UDF] are more than just conceivable. The request is already filed and even possible solution is being discussed. But why not give UDF a shot already then? By default UDF write support is unfortunately disabled and you might have to reconfigure the kernel and rebuild modules. Alternatively [my preferred option actually] fetch the code at SourceForge and build the module separately. Of course you will have to fetch and build udftools as well. But once it's done just type:

    mkudffs --spartable=2 --media-type=cdrw /dev/scdN
    mount -o rw,noatime /dev/scdN /mnt/cdrom
    

    mkudffs command line options were suggested by UDF maintainer, Ben Fennema.

  • Performance optimization. This paragraph applies only if you've patched the kernel. As some of you might remember the original recommendation was "do use obs=32k for optimal performance." Well, it was rather naive of me to say so, as common block device layer completely reorganizes the stream so that '>/dev/scd0' is as good as '|dd of=/dev/scd0 obs=32k'. It should also be noted that dumping to /dev/scd0 puts quite a pressure on VM subsystem, as the data passes through block buffer cache. To minimize the pressure and improve overall system performance bind the cdrom device to a raw device, e.g. 'raw /dev/raw/raw1 /dev/scd0', growisofs will locate and use it automatically. obs=32k makes perfect sense with /dev/raw devices, but dd (as well as most other programs, e.g. tar) won't work as /dev/raw expects specifically aligned buffer... As temporary workaround, just to get you going so that you can start figuring things out, consider this "hacklet"...


Compatibility: caveat lector


This paragraph discusses "DVD-ROM compatibility," or playability of already recorded media in legacy units. Blank media compatibility issues, or cases such as failure to start or fulfill recording because of poor media support by burner firmware, are beyond the current scope. Turn to your vendor for list of supported media and/or to the public to share your experience.

In order to optimize seek times DVD[-ROM] players calibrate their mechanics every time the media is loaded by sliding the optical head some place, picking up the signal and noting the physical block address underneath the lens. In order for this procedure to work with re-writable/recordable media, that particular spot has to be written to [or de-iced in DVD+RW terms]. Some units slide the head to 30mm [radial] to calibrate, some to 35mm. In order to keep such players "happy," make sure that at least 1GB is written [before you attempt to mount it in DVD-ROM unit].

Other units attempt to seek to lead-out [or vicinity of it] for calibration purposes. Now the catch is that it's perfectly possible to produce a DVD+RW disc without lead-out. Most notably media initially formatted with dvd+rw-format [apparently] doesn't have any lead-out, not to mention that practically whole surface remains virgin. If you fail to mount/play DVD+RW media, attempt to

dvd+rw-format -lead-out /dev/scdN

which relocates the lead-out next to outermost written sector as well as makes sure there is no virgin surface before it. Previously written data is not affected by this operation.

Then non-finalized DVD+R and Sequential DVD-R[W] discs don't have lead-out either(*). If you fail to mount/play DVD+R media and wish to sacrifice the remaining space for immediate compatibility, just fill the media up(**). Alternatively if you master volume in a single take and don't plan to use it for multisessioning(***), you have the option to invoke growisofs with -dvd-compat option and cut the real lead-out directly after the first session.

(*) Well, there are lead-outs at the session edges, but the problem is that "End Physical Sector Number of Data Area" field in "Control Data Zone" of the lead-in contains address of the largest media sector, which makes affected DVD[-ROM] players calibrate at the outermost edge instead of the first session. Actually I fail to understand why don't they burn the address of last sector of the first session in the lead-in even on multisession discs...
(**) But beware the 4GB limit! If 4GB is already an issue, or if you don't feel like throwing unrelated data on the media in question, then invoke 'growisofs -M /dev/scd0=/dev/zero' (supported by 5.6 and later). Alternative is to re-master the whole volume, naturally with -dvd-compat option.
(***) E.g. when mastering DVD-Video disc:-) Note that -dvd-video option [passed to mkisofs] engages -dvd-compat automatically.


Then we have logical format compatibility issue(s). Probably the very ground for all the controversy around DVD+RW, rather around DVD+RW media not being playable in a whole range of players. DVD+RW Alliance was keen to blame on DVD-ROM vendors, even claiming that they deliberately block playback. But the fact is that format specifications don't explicitly say that unrecognized format [designated by "Book Type" field in "Control Data Zone" of the lead-in] should be treated as DVD-ROM and [in my opinion] it was rather naive of them to claim and expect that the media will be playable in "virtually all players." This deficiency was recognized by practically all DVD+RW vendors [well, apparently by "traditional" DVD+RW vendors and not "latest generation" vendors such as Sony, NEC, TDK...] and a secret vendor-specific command manipulating this "Book Type" field was implemented. So if you fail to mount/play DVD+RW media, you might have an option to

dvd+rw-booktype -dvd-rom -media /dev/scdN

Once again. Not all vendors support this and you can't expect this utility to work with all recorders.

It's naturally not possible to manipulate the "Book Type" field on DVD+R media, that is not after the lead-in is written [which takes place at the moment the first session gets closed]. But it might be possible to control how it [lead-in] is going to be branded by programming the drive in advance:

dvd+rw-booktype -dvd-rom -unit+r /dev/scdN

Meaning that if you fail to play DVD+R media, you can attempt to burn another disc with more appropriate unit settings. For more background information about dvd+rw-booktype, see "Compatibility Bitsettings" article at dvdplusrw.org.

There [potentially] are other logical DVD+RW(*) format incompatibilities, but the "Book Type" issue discussed above is the only one "officially" recognized. Well, it's actually understandable as it's the only one that can be recognized and addressed by a DVD+RW vendor alone. Recognition of other incompatibilities would require cooperation from DVD[-ROM] player vendors and that's something they apparently are not willing to show referring to the fact that DVD+RW format is not approved [and apparently never will be] by DVD Forum(**).

(*) Finalized DVD+R media branded with DVD-ROM "Book Type" is virtually identical to DVD-ROM.
(**) To which I say "so what?" DVD Forum is an alliance of manufacturers just like DVD+RW Alliance is. It [or any other party for that matter] has no authority to deny a technology development initiative.


Finally there is a physical incompatibility issue. They claim that there are optical pick-ups out there not being capable to decode the track because of low reflectivity of DVD+RW media surface. I write "they claim," because in the lack of cooperation from DVD[-ROM] vendors it's not possible to distinguish physical from logical format incompatibility, which I find important to tell apart in order to make sure at least logical format incompatibility issues don't persist over time. It might be as trivial as following. As you surely know [already], DVD+RW has same reflectivity as dual-layer DVD-ROM. Now the catch is that the linear pit density in turn is same as of single-layer one. Meaning that if player makes assumptions about linear pit density based on reflectivity, then it won't be able to trace the track... But either way, there is very little you can do about this one, but to try another player...


Technical Ramblings


As for multisession ISO9660 [DVD] recordings! Unfortunately, Linux ISOFS implementation had certain deficiency which limits interoperability of such recordings. In order to understand it, have a look at sample ISO9660 layout to the right... Now, the problem is that isofs i-nodes(*) are 32 bits wide (on a 32-bit Linux) and represent offsets of corresponding directory entries (light-greens), byte offsets from the beginning of media. This means that no directory (green areas) may cross 4GB boundary without being effectively corrupted:-( It should be noted that in reality it's a bit better than it looks on the picture, as mkisofs collects all the directories in the beginning of any particular session (there normally are no blues between greens). The first session is therefore never subject to i-node wrap-around, but not the subsequent ones! Once again, files themselves may reside beyond the 4GB boundary, but not the directories, in particular not in further sessions. Having noted that directory entries are actually specified to start at even offsets, I figured that it's perfectly possible to "stretch" the limit to 8GB. But in order to assure maximum interoperability, you should not let any session start past 4GB minus space required for directory structures, e.g. if the last session is to fill the media up, it has to be >400MB. As of version 5.3 growisofs refuses to append a new session beyond 4GB-40MB limit(**), where 40MB is pretty much arbitrary chosen large value, large for directory catalogs that is. Yet it doesn't actually guarantee that you can't suffer from i-node wrap-around. Interim fs/isofs 2.4 kernel patch was addressed to those who have actually ran into the problem and have to salvage the data. Even though permanent solution for this problem appears in Linux kernel 2.6.8 (thanks to Paul Serice effort), growisofs keeps checking for this 4GB limit in order to ensure broader compatibility of final DVD recordings. This check is not performed for Blu-ray Disc recordings, as probability that a member of such user community would run something elder than 2.6.9 is considered diminishingly low.
(*) i-node is a number uniquely identifying a single file in a file system
(**) well, as DVD+R Double Layer support was introduced in 5.20, -use-the-force-luke=4gms option was added to override this behaviour (naturally recommended for Linux kernel 2.6>=8 users and kernel developers only;-)


Why media reload is performed after every recording with growisofs? Well, it's performed only if you didn't patch the kernel:-) But no, I do not insist on patching the kernel! All I'm saying is that in the lack of kernel support, media reload is performed for the following reasons. In order to optimize file access kernel maintains so called block cache, so that repetitive requests for same data are met directly from memory and don't result in multiple physical I/O. Now the catch is that block cache layer remains totally unaware of growisofs activities, growisofs bypasses the block cache. This means that block cache inevitably becomes out of sync, which in turn might appear to you as corrupted data. Media reload is performed when flushing the block cache is not an option, e.g. only privileged user is allowed to perform it. Second reason is to force kernel to readjust last addressable block in case it was changed as result of recording. This is done to preclude spurious "attempts to access beyond end of device."


What does [kernel] "DVD+RW support" really mean? Even though DVD+RW has no notion of [multiple] sessions, to ensure compatibility with DVD-ROM it's essential to issue "CLOSE TRACK/SESSION (5Bh)" MMC command to terminate/suspend background formatting (if any in progress) whenever you intend to eject the media or simply stop writing and want better read performance (e.g. remount file system read-only). This is what the patch is basically about: noting when/if media was written to and "finalizing" at unlock door.

Secondly, whenever you employ fully fledged file system, I/O requests get inevitably fragmented. "Fragmented" means following. Even though you can address the data with 2KB granularity, it [data] is physically laid out in 32KB chunks. This in turn means that for example writing of 2KB block involves reading of 32KB chunk, replacing corresponding 2KB and writing down of modified 32KB chunk. "Fragmented requests" are those that are smaller than 32KB or/and cross the modulus 32KB boundaries. In order to optimize the process certain caching algorithm is implemented in unit's firmware. Obviously it can't adequately meet all possible situations. And so in such unfortunate situations the drive apparently stops processing I/O requests returning "COMMAND SEQUENCE ERROR (2Ch)" ASC. This is the second essential of "DVD+RW support," namely injecting of "SYNCHRONIZE CACHE (35h)" MMC command in reply to the error condition in question. The command flushes the cached buffers which makes it possible to resume the data flow.

Unfortunately the above paragraph doesn't seem to apply to the 1st generation drives, Ricoh MP5120A and derivatives:-( "SYNCHRONIZE CACHE (35h)" doesn't seem to be sufficient and the unit keeps replying with "COMMAND SEQUENCE ERROR (2Ch)" going into end-less loop. This makes it impossible to deploy arbitrary file system. I'm open for suggestions... Meanwhile the I've chosen to simply suspend I/O till the media is unmounted. Even 2nd gen unit were reported to exhibit similar [but not the same] behaviour under apparently extremely rare circumstances. At least I failed to reproduce the problem... The problem reportedly disappears with firmware upgrade...

Then some [most?] of post-2nd gen units, from most vendors, seem to not bother about complying with DVD+RW specification, "true random write with 2KB granularity" part in particular. Instead they apparently expect host to apply procedure pretty much equivalent to DVD-RW Restricted Overwrite. To be more specific host seems to be expected to coalesce 2KB requests and perform aligned writes at native DVD ECC blocksize, which is 32KB. Formally this should not be required, but it's the reality of marketplace:-(

This one really beats me. Sometimes the unit simply stops writing signaling a vendor specific positioning error, 03h/15h/82h to be specific. Especially if the media is newly formatted. Couple of work theories. One is that block buffer cache reorders requests so that they are not sequential anymore, "FLUSH CACHE" might do the trick. Another one is that under "underrun" condition background formatting kicks off and has to be explicitly stopped. "Underrun" is in quotes because the unit is supposed to handle temporary data stream outages gracefully. If you run into this (you most likely will), try to complement growisofs command line with [undocumented] -poor-man option (which has to be first in the command line). This option eliminates request reorders and minimizes possibility for "underrun" condition (by releasing the pressure off VM subsystem).


The original idea was to implement DVD+RW support in drivers/cdrom/cdrom.c. Unfortunately SCSI layer maintains private "writeable" flag controlling the ability to issue WRITE commands. The flag is impossible to reach for from the Unified CD-ROM driver. But why am I talking about SCSI when there are only IDE units out there (at least for the time being)? Well, as you most likely want to occasionally burn even CD-R[W] with cdrecord you want it to go through ide-scsi emulation layer anyway. So I figured that SCSI CD-ROM driver is the one to aim for even for DVD+RW.

Unfortunately it was not possible to implement it completely in sr_mod.o:-( Minor drivers/cdrom/cdrom.c modification was required to sense the media before decision about whether or not to permit write open. That's because DVD+RW drives are morphing their behaviour after currently mounted media and it's essential to identify newly inserted media.

Special comment about "what a dirty hack!!!" To my great surprise it turned out that time-out value you pass in cdrom_generic_command is simply ignored and time-out is set to pre-compiled value of 30 seconds. Unfortunately it's way too low for formatting purposes and I had to do something about it. Alternative to "the dirty hack" was to add another argument to sr_do_ioctl and modify all the calls to it... I've chosen to take over those 31 unused bits from the "quiet" argument instead of modifying all the calls (too boring).

But even if time-out value passed down to kernel (with either CDROM_SEND_PACKET or SG_IO ioctl) is taken into consideration, it's apparently not interpreted as user-land code expects it to. As I figured... There is no documentation on CDROM_SEND_PACKET, but following the common sense most programmers (including myself:-) expect it to be interpreted in at least platform-independent manner, such as milliseconds maybe? SG_IO timeout in turn is documented to be measured in milliseconds... Neither of this holds true! Kernel treats these values as "jiffies," which is a platform-dependent value representing time elapsed between timer interrupts. But if we attempt to send down "jiffies," it might turn out wrong too [at least for the moment of this writing]. The catch is that [IA-32] kernel developers figured it's cool to shorten "jiffy," but didn't care to provide user-land with actual value (well, not of actual interest, too much legacy code to deal with) nor scale timeouts accordingly in respect to the legacy value of 10ms.

There is another kernel "deficiency" I ran into while working on the (original version of) dvd+rw-format utility. The drive provides background formatting progress status, but unfortunately it's impossible to access it. That's because progress counter is returned [in reply to "TEST UNIT READY"] as "NO SENSE/LOGICAL UNIT NOT READY/FORMAT IN PROGRESS" sense bytes but with "GOOD" status. Apparently any sense data with "GOOD" status is discarded by the common SCSI layer.

It was pointed out to me that DVD+RW units work with Acard SCSI to IDE bridges.


What does plus in DVD+RW/+R stand for? Originally this paragraph started as following:

The key feature of DVD+RW/+R media is high [spatial] frequency wobbled [pre-]groove with addressing information modulated into it. This makes it possible to resume interrupted [or deliberately suspended] burning process with accuracy high enough for DVD[-ROM] player not to "notice" anything at playback time. Recovery from buffer underrun condition in DVD-RW/-R case in turn is way less accurate procedure, and the problem is that the provided accuracy is very much what average player can tolerate. Now given that both provided and tolerated inaccuracies are proportional to respectively writing and reading velocities there basically no guarantee that DVD-RW/-R recording that suffered from buffer underrun will be universally playable.

Well, it turned out that I was wrong about one thing. I failed to recognize that DVD-R[W] groove also provides for adequately accurate recovery from buffer underrun condition/lossless linking. Not as accurate as DVD+RW, but accurate enough for splices to be playable in virtually any DVD-ROM/-Video unit. Yet! When it comes to DVD-R[W] recording specificaton apparently insists that you choose between

  • buffer underrun protection and
  • full DVD-ROM/-Video compatibility.

The specification asserts that the latter is achieved only in Disc-at-once recording mode and only if data-stream was maintained uninterrupted throughout whole recording. Once again. Even though most vendors implement lossless linking in DAO mode(*), full DVD-ROM/-Video compatibility is guaranteed only if recording didn't suffer from buffer underruns. The problem is that "offended" sectors are denoted with certain linking chunk appearing as degraded user data, few bytes, which are supposed to be "corrected away" by ECC procedure(**). DVD+ splices are in turn only few bits large and are "accounted" to sync patterns, not to user data area. So that even if suffered from buffer underrun, DVD+ sector is logically indistiguishable from DVD-ROM. Which is why it's commonly referred to that DVD+RW/+R combine DVD-ROM/-Video compatibility with [unconditional] buffer underrun protection.

As already mentioned, DVD+ groove has "addressing information modulated into it," ADIP (ADress In Pre-groove). This gives you an advantage of writing DVD+RW in truly arbitrary order, even to virgin surface and practically instantly (after ~40 seconds long initial format procedure). In addition, DVD+RW can be conveniently written to with 2KB granularity(***). DVD-RW in turn can only be overwritten in arbitrary order. Meaning that it either has to be completely formatted first (it takes an hour to format 1x media), or initially written to in a sequential manner. And it should also be noted that block overwrite is never an option if DVD-RW media was recorded in [compatible] Disc-at-once or even Incremental mode, only whole disc blanking is.

Unlike DVD-R[W], DVD+R[W] recordings can be suspended at any time without any side effects. Consider following scenario. You have a lot of data coming in [at lower rate], which is to be recorded into one file. Meanwhile it turns out that you have to retrieve previously recorded data. This would naturally require suspention of recording. Most notably in DVD-R [and naturally DVD-RW Sequential] case it would result in a hole in the file being recorded. So called linking area, most commonly 32KB gap, has to be introduced. So that you either have to wait till the file is complete or figure out how to deal with holey files. Thanks to ADIP, DVD+R recording is resumed from the very point it was suspended at. In DVD-RW Restricted Overwrite case no gaps are introduced, but if the media was formatted only minimally, suspension/resuming procedure has to be applied and it takes ~40 seconds to perform one. In DVD+RW case, suspension/resuming is instant regardless media state.

What does all of the above mean in practice? Well, I was actually hoping that readers would [be able to] figure it out by themselves. Apparently a couple of "guiding" words are needed... It means that it's trivial to employ DVD+RW for housing of live and arbitrary file system, no special modifications to target file system driver are required... Real-time VBR (Variable Bit Rate) Video recordings are children's game...

Sometimes DVD+RW/+R recording strategy is referred to as packet writing. I myself am reluctant to call it so (or TAO/SAO/DAO) for the following reason. Despite the fact that DVD-R[W] provides for lossless linking (within a packet/extent only), packets/extents are still denoted with certain linking information which distinguishes it (recording mode in question) from e.g. Disc-at-once. Now the point is that written DVD+RW/+R media, rather its Data Zone, does not contain any linking information and is logically indistinguishable from one written in DVD-R[W] Disc-at-once mode (or DVD-ROM for that matter).

It's maintained that signal from DVD+ groove (the one essential for recording, not reading) is much stronger, which makes it quite resistant to dust, scratches, etc.

Now we can also discuss differences between Double/Dual Layer implementations. DVD+R Double Layer permits for arbitrary layer break positioning yet maintaining contiguous logical block addressing. In other words address of the block following the break is always address of the block preceding one plus 1, even for arbitrarily positioned break. DVD-R Dual Layer on the other hand implies unconditionally disjoint logical block addressing [for arbitrarily positioned layer break that is]. This is because block addresses as recorded by unit are pre-defined by DVD-dash groove structure. In practice it means that file system layout has to effectively have a hole, which "covers" twice the space between chosen layer break position and outermost edge of the recordable area. And in even more practical terms this means that mastering programs have to be explicitly adapted for DVD-R layer break positioning. Unlike DVD+plus that is.

(*) According to Mt. Fuji draft buffer underrun protection is not even an option in DVD-R DAO: "If a buffer under-run occurs, the logical unit shall stop writing immediately and the logical unit shall start writing of Lead-out." Protection is defined in Incremental Sequential mode and DVD-RW context. By the way, note that earlier versions of this draft also discuss DVD+RW. You should be aware that they refer to abandoned version which has very little to do with DVD+RW/+R implementation being discussed here.
(**) ECC redundancy does permit for more degradation, more that this linking chunk that is, so that it hadly affects the playability.
(***) DVD "native" block size is 32KB, and 2KB granularity is nothing but a trick, but you're excused from playing it, i.e. reading 32KB, replacing corresponding 2KB and writing 32KB back.