dvd.rw-tools/growisofs.c

3595 lines
113 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* growisofs 7.1 by Andy Polyakov <appro@fy.chalmers.se>.
*
* 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;
* - <20> 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 <linux/raw.h> 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 <appro@fy.chalmers.se>,"\
" version 7.1,\n",(int)(s-(cmd)),(cmd)); \
} while (0)
#define _LARGEFILE_SOURCE
#define _LARGEFILE64_SOURCE
#define _FILE_OFFSET_BITS 64
#if defined(__linux) || defined(__GLIBC__)
/* ... 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 <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <assert.h>
#include "mp.h"
#if defined(__unix) || defined(__unix__)
# include <unistd.h>
# include <sys/time.h>
# include <sys/wait.h>
# include <sys/mman.h>
# include <signal.h>
# include <poll.h>
# include <sys/resource.h>
# include <sys/utsname.h>
# include <errno.h>
# define set_errno(e) (errno=(e))
# define FATAL_START(e) (0x80|(e))
# define FATAL_MASK 0x7F
#if defined (__FreeBSD__) || defined (__FreeBSD_kernel__)
# include <sys/syscall.h>
# 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 <io.h>
# include <process.h>
# 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 <linux/types.h>
#include <linux/cdrom.h>
#include <scsi/scsi.h>
#include <sys/ioctl.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#ifndef _LINUX_WAIT_H
#define _LINUX_WAIT_H /* linux headers are impaired */
#endif
#include <linux/capability.h>
#if 0
#include <linux/raw.h>
#else
/* As they threat to remove <linux/raw.h>, 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<<CAP_SYS_RAWIO;
d.inheritable = 0;
if (syscall(SYS_capset,&h,&d) < 0) break;
/* drop all privileges & effective capabilities */
/* setuid(uid) seems to preserve saved uid
after dropping CAP_SETUID above... */
setresuid (uid,uid,uid);
d.effective = 1<<CAP_SYS_RAWIO;
if (syscall(SYS_capset,&h,&d) < 0) break;
#endif
} while (0);
#endif
setuid (uid); /* drop all privileges */
return ioctl_device=device;
case 0x12: /* DVD-RAM */
case 0x1A: /* DVD+RW */
case 0x2A: /* DVD+RW Double Layer */
case 0x43: /* BD-RE */
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));
}
/*
* 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__) || defined (__FreeBSD_kernel__)
#include <sys/cdio.h>
#include <camlib.h>
#include <cam/scsi/scsi_pass.h>
#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 <volmgt.h>
#include <sys/cdio.h>
#include <dlfcn.h>
#include <pwd.h>
#include <stdarg.h>
#include <alloca.h>
#if defined(__SunOS) && __SunOS >= 510
#include <priv.h>
/* 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 <sys/mknod.h>
#endif
#include <sys/diskio.h>
#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 <sys/attributes.h>
#include <sys/param.h>
#include <mediad.h>
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 <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/scsi/SCSITaskLib.h>
#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 (u<butlz) butlz=u;
}
off++;
outoff += DVD_BLOCK;
progress.current=outoff;
}
n = (reader_exit==(unsigned int)-1)?0:(set_errno(reader_exit),-1);
printf ("builtin_dd: %"LLD"*2KB out @ average %.1fx%dKBps\n",
(outoff-startoff)/2048,
((outoff-startoff)/(1024.0*__1x()))/(time(NULL)-progress.zero),
__1x());
out:
if (quiet<=0) progress.zero=0; /* shut the progress_print thread up */
{ int saved_errno=errno;
if (dry_run) close(infd), close(outfd), exit(errno);
set_errno(outoff ? saved_errno : (n--?saved_errno:FATAL_START(EIO)));
}
return n;
}
/*
* Setup pipe to mkisofs and start it
*/
#if defined(__unix) || defined(__unix__)
void pipe_mkisofs_up (char *mkisofs_argv[],int infd,int outfd,off64_t outoff)
{ pid_t mkisofs_pid;
int fildes[2],ret,n;
if (pipe (fildes) < 0)
perror (":-( unable to create pipe"), exit(FATAL_START(errno));
if ((mkisofs_pid=fork ()) == (pid_t)-1)
perror (":-( unable to fork genisoimage"), exit(FATAL_START(errno));
else if (mkisofs_pid == 0)
{ dup2 (fildes[1],1);
close (fildes[0]);
close (fildes[1]);
close (outfd); /* redundant:-) */
if (next_session)
{
#ifdef PASS_STDIN_TO_MKISOFS
dup2(infd,0);
close(infd);
infd=0;
#endif
if ((n=fcntl (infd,F_GETFD))<0) n=0;
fcntl (infd,F_SETFD,n&~FD_CLOEXEC);
}
else
close (infd);
/*
* If platform-specific setup_fds did not drop privileges,
* do it now. I ignore return value because if it fails,
* then privileges were dropped already.
*/
setuid(getuid());
execvp (mkisofs_argv[0],mkisofs_argv);
fprintf (stderr,":-( unable to execute %s: ",mkisofs_argv[0]),
perror (NULL), exit (FATAL_START(errno));
}
close (fildes[1]);
n=builtin_dd(fildes[0],outfd,outoff);
if (n==0) /* mkisofs must have finished, consume the exit code */
{ if ((waitpid (mkisofs_pid,&ret,0)) == -1)
perror (":-( waitpid failed"), exit (errno);
if (!WIFEXITED(ret) || WEXITSTATUS(ret)!=0)
fprintf (stderr,":-( %s has failed: %d\n",mkisofs_argv[0],WEXITSTATUS(ret)),
exit (errno=WIFEXITED(ret)?WEXITSTATUS(ret):255);
}
else if (n<0)
{ int err = errno;
errno = err&FATAL_MASK; /* they might be passing FATAL_START */
perror (":-( write failed"), exit (err);
}
}
#elif defined(_WIN32)
void pipe_mkisofs_up (char *mkisofs_argv[],int infd,int outfd,off64_t outoff)
{ STARTUPINFO si;
PROCESS_INFORMATION pi;
HANDLE hRead;
int n;
char *cmd = (char *)saved_descriptors; /* borrow it */
memset (&si,0,sizeof(si));
si.cb = sizeof(si);
si.dwFlags = STARTF_USESTDHANDLES;
if (!CreatePipe (&hRead,&si.hStdOutput,NULL,0))
perror (":-( unable to create pipe"), exit (FATAL_START(errno));
SetHandleInformation (si.hStdOutput,HANDLE_FLAG_INHERIT,HANDLE_FLAG_INHERIT);
si.hStdInput = (HANDLE)_get_osfhandle (infd);
SetHandleInformation (si.hStdInput,HANDLE_FLAG_INHERIT,HANDLE_FLAG_INHERIT);
si.hStdError = GetStdHandle (STD_ERROR_HANDLE);
SetHandleInformation (si.hStdError,HANDLE_FLAG_INHERIT,HANDLE_FLAG_INHERIT);
{ char **argv,*arg;
size_t len0;
size_t cmdsz = sizeof(saved_descriptors);
if (cmdsz>32*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,":-( genisoimage 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
# if defined (__FreeBSD__) || defined (__FreeBSD_kernel__)
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 ("GENISOIMAGE");
mkisofs_argv[0] = (env?env:"genisoimage");
#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<argc;i++)
{ int len=strlen(argv[i]);
char *opt;
dev_found = '\0';
if (argv[i][0] == '-')
{ opt = argv[i] + ((argv[i][1]=='-')?(len--,1):0);
if (argv[i][1] == 'M')
{ if (len > 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 <genisoimage options>\n",argv[0]);
printf (" for <genisoimage options> see 'genisoimage %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 genisoimage options specified, "
"aborting...\n",argv[0]),
exit (FATAL_START(EINVAL));
}
else if ((mkisofs_argc-growisofs_argc)>1)
fprintf (stderr,"%s: no genisoimage 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<<shift;
if (sz < the_buffer_size) sz<<=1;
the_buffer_size = sz;
}
else
the_buffer_size = 2*DVD_BLOCK;
#if defined(__unix) || defined(__unix__)
#if defined(_SC_PAGESIZE) && defined(_SC_PHYS_PAGES)
if (the_buffer_size>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<56>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<mkisofs_argc;i++) printf ("%s ",mkisofs_argv[i]);
printf ("| builtin_dd of=%s obs=32k seek=%u",
out_device,next_session/16);
}
printf ("'\n");
fflush (stdout);
/* if ((warn_for_isofs&2) && !dry_run && !no_tty_check)
{ fprintf (stderr,"Sleeping for 5 sec...\a"), poll (NULL,0,1000);
fprintf (stderr,"\b\b\b\b\b\b\b\b4 sec...\a"), poll (NULL,0,1000);
fprintf (stderr,"\b\b\b\b\b\b\b\b3 sec...\a"), poll (NULL,0,1000);
fprintf (stderr,"\b\b\b\b\b\b\b\b2 sec...\a"), poll (NULL,0,1000);
fprintf (stderr,"\b\b\b\b\b\b\b\b1 sec...\a"), poll (NULL,0,1000);
fprintf (stderr,"\b\b\b\b\b\b\b\b0 sec...\r");
} */
#if defined(__unix) || defined(__unix__)
#define CLOSEONEXEC(fd) if (fd>=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<MAX_IVDs;i++)
{ if (saved_descriptors[i].type[0] == (unsigned char)255) break;
if (memcmp (saved_descriptors[i].id,"CD001",5)) break;
if (saved_descriptors[i].type[0] != descr[i].type[0])
{ fprintf (stderr,":-? volume descriptor mismatch, did you "
"use same genisoimage options?\n");
break;
}
memcpy (descr+i,&saved_descriptors[i],sizeof(*descr));
to_733(descr[i].volume_space_size,new_size);
}
if ((*pwrite64_method) (out_fd,descr,DVD_BLOCK,
VOLDESC_OFF*CD_BLOCK) != DVD_BLOCK)
perror (":-( unable to pwrite64(2) volume descriptors set"),
exit (errno);
}
set_errno(0);
exit (0);
}