/*
 * growisofs 5.19 by Andy Polyakov <appro@fy.chalmers.se>.
 *
 * 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;
 */
#define PRINT_VERSION(cmd)	do {			\
    char *s=strrchr((cmd),'/');				\
    s ? s++ : (s=(cmd));				\
    printf ("* %.*sgrowisofs by <appro@fy.chalmers.se>,"\
	    " version 5.19-1,\n",(int)(s-(cmd)),(cmd));	\
} while (0)

#define _LARGEFILE_SOURCE 
#define _LARGEFILE64_SOURCE
#define _FILE_OFFSET_BITS 64
#if defined(__linux)
/* ... and "engage" glibc large file support */
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__)
#define off64_t  off_t
#define stat64   stat
#define fstat64  fstat
#define open64   open
#define pread64  pread
#define pwrite64 pwrite
#define lseek64  lseek
#endif

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <signal.h>
#include <poll.h>
#include <assert.h>
#include <sys/time.h>

#define	FATAL_START(err)	(0x80|(err))
#ifndef EMEDIUMTYPE
#define EMEDIUMTYPE     EINVAL
#endif
#ifndef ENOMEDIUM
#define ENOMEDIUM       ENODEV
#endif

typedef ssize_t (*pwrite64_t)(int,const void *,size_t,off64_t);
/*
 * Symbols from growisofs_mmc.cpp
 */
/*
 * These might 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);
/*
 * These never terminate the program.
 * Pay attention to the return value.
 */
int        media_reload		(char *file,struct stat *ref);
int        fumount		(int fd);
off64_t    get_capacity		(void *fd);
int        poor_man_rewritable	(void *fd,void *buf);

/* 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)

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 pwrite64_t pwrite64_method = pwrite64;
/*
 * Page-aligned 2*DVD_BLOCK=64KB large buffer, second 32KB contain
 * "next" session volume descriptors that were written in the beginning
 * of current recording...
 */
static char *the_buffer;
/*
 * id_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;

int    dvd_compat=0, test_write=0, no_reload=0, mmc_profile=0;
double speed_factor=0.0;
char  *ioctl_device;
int    _argc;
char **_argv;

#if defined(__linux)

#include <linux/cdrom.h>
#include <sys/ioctl.h>
#include <linux/raw.h>

char *find_raw_device(struct stat64 *sb)
{ int   i,rawctl,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;
}

char *setup_fds (char *device)
{ char *odevice;
  uid_t uid=getuid();
  struct stat64 sb,sc;

    /*
     * 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;
    }

    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 0x1B:	/* DVD+R			*/
	open_poor_man:
	    poor_man = 1;
	    out_fd = dup(in_fd);
	    setuid (uid);	/* drop all privileges	*/
	    return ioctl_device=device;
	case 0x1A:	/* DVD+RW			*/
	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));
    }

    /*
     * 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 0x1B:	/* DVD+R			*/
	case 0x1A:	/* DVD+RW			*/
	open_poor_man:
	    poor_man = 1;
	    out_fd = dup(ioctl_fd);
	    setuid (uid);	/* drop all privileges	*/
	    return ioctl_device=device;
	case 0x12:	/* DVD-RAM			*/
	    if (poor_man>0) goto open_poor_man;
	    out_fd = dup(ioctl_fd);
	    goto opened_rw;
	default:
	    fprintf (stderr,":-( %s: media is not recognized as "
			    "recordable DVD: %X\n",device,mmc_profile);
	    exit (FATAL_START(EMEDIUMTYPE));
    }
    /* not actually reached */
    setuid(uid);
    goto open_rw;
}

#elif defined(__FreeBSD__)

#include <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 0x1B:	/* DVD+R			*/
	case 0x1A:	/* DVD+RW			*/
	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>

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!
     */
    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);
	}
	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.
     */
    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 0x1B:	/* DVD+R			*/
	case 0x1A:	/* DVD+RW			*/
	    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));
    }
    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

#include <sys/mknod.h>
#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%xt%x%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%xt%x%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 0x1B:	/* DVD+R			*/
	case 0x1A:	/* DVD+RW			*/
	    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)
    {	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 0x1B:	/* DVD+R			*/
	case 0x1A:	/* DVD+RW			*/
	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;
}

#else
#error "Unsupported OS"
#endif

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==0x13 || profile==0x12)
    {	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==0x1B || profile==0x11 || profile==0x14)
	next_session=plusminus_r_C_parm (ioctl_handle,C_parm);

  return next_session;
}

int builtin_dd (int infd,int outfd,off64_t outoff)
{ char		*s;
  int   	 n,off,fd;
  struct stat64  sb;
  off64_t	 capacity=0,tracksize=0,startoff=outoff;
  pid_t		 pid=(pid_t)-1,ppid=getpid();
  volatile struct { time_t zero; off64_t current,final; } *progress;

    if ((fd=mkstemp (s=strdup("/tmp/dvd+rw.XXXXXX"))) < 0)
	fprintf (stderr,":-( unable to mkstemp(\"%s\")",s),
	exit(FATAL_START(errno));

    ftruncate(fd,sizeof(*progress));
    unlink(s), free(s);

    progress = (void *)mmap (NULL,sizeof(*progress),PROT_READ|PROT_WRITE,
			MAP_SHARED,fd,(off_t)0);
    close (fd);
    if (progress == MAP_FAILED)
	fprintf (stderr,":-( unable to anonymously mmap %lu?\n",
			sizeof(*progress)),
	perror (NULL), exit(FATAL_START(errno));

    if (fstat64 (infd,&sb))
	perror (":-( unable to fstat64"), exit(FATAL_START(errno));

    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;

    if (!dry_run && quiet<=0 && (pid=fork()) == 0)
    { double		ratio,velocity,slept;
      int		delta,nfirst=0;
      off64_t		lastcurrent=outoff,current;
      struct timeval	tv;

	close (infd);
	close (outfd);
	while (kill (ppid,0)==0)
	{   gettimeofday (&tv,NULL); slept =  tv.tv_usec/1e6+tv.tv_sec;
	    poll (NULL,0,3333);
	    gettimeofday (&tv,NULL); slept -= tv.tv_usec/1e6+tv.tv_sec;
	    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*1385);
		fprintf (stdout,"%10lld/%lld (%4.1f%%) @%.1fx, "
				"remaining %d:%02d\n",
				current,progress->final,100.0/ratio,
				velocity,delta/60,delta%60);
		lastcurrent=current;
	    }
	    else
		fprintf (stdout,"%10lld/%lld (%4.1f%%) @0x, remaining ??:??\n",
				current,progress->final,0.0);
	    fflush (stdout);
	}
	exit(0); /* not [normally] reached */
    }

    /* 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;

		descr=(struct iso_primary_descriptor *)(the_buffer+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 if (dry_run)
				close(infd), close(outfd),
				exit(FATAL_START(ENOSPC));
			    else
			    {	n = -1; errno = FATAL_START(ENOSPC);
				goto out;
			    }
			}
		    }
		    /* else already checked for overburn condition */
		}
		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; 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; errno = FATAL_START(ENOTDIR);	/* ... or whatever */
		goto out;
	    }

	    if (dry_run) close(infd), close(outfd), exit(0);

	    if (poor_man)
		/*
		 * 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);

	    /* Write two 32KB chunks, not one 64KB... */
	    if ((n=(*pwrite64_method) (outfd,the_buffer,DVD_BLOCK,outoff))
			!= DVD_BLOCK)
	    {	if (n>0)	errno = FATAL_START(EIO);
		else if (n==0)	errno = FATAL_START(ENOSPC);
		n = -1;
		goto out;
	    }
	    outoff += DVD_BLOCK;
	    progress->current=outoff;

	    if ((n=(*pwrite64_method) (outfd,the_buffer+DVD_BLOCK,DVD_BLOCK,outoff))
			!= DVD_BLOCK)
	    {	if (n>0)	errno = EIO;
		else if (n==0)	errno = ENOSPC;
		n = -1;
		goto out;
	    }
	    outoff += DVD_BLOCK;
	    progress->current=outoff;

	    break;
	}
    }
    if (n<=0) goto out;

    /* yeah, really kludgy, shuffling file descriptor like that... */
    if (zero_image)
	close(infd), infd=open64 ("/dev/zero",O_RDONLY);

    off = 0;
    /*
     * From now on only the first 32K of the_buffer are used!
     */
    while ((n=read (infd,the_buffer+off,DVD_BLOCK-off)) > 0)
    {	off += n;
	if (off == DVD_BLOCK)
	{   off = 0;
	    if ((n=(*pwrite64_method) (outfd,the_buffer,DVD_BLOCK,outoff))
			!= DVD_BLOCK)
	    {	if (n>0)	errno = EIO;
		else if (n==0)	errno = ENOSPC;
		n = -1;
		break;
	    }
	    outoff += DVD_BLOCK;
	    progress->current=outoff;
	}
    }
    if (off)	/* pad to the closest 32K is needed */
    {	memset (the_buffer+off,0,DVD_BLOCK-off);
	if ((n=(*pwrite64_method) (outfd,the_buffer,DVD_BLOCK,outoff))
			!= DVD_BLOCK)
	{    if (n>0)		errno = EIO;
	     else if (n==0)	errno = ENOSPC;
	     n = -1;
	}
	outoff += DVD_BLOCK;
    }

    printf ("builtin_dd: %lld*2KB out @ average %.1fx1385KBps\n",
	    (outoff-startoff)/2048,
	    ((outoff-startoff)/(1024.0*1385.0))/(time(NULL)-progress->zero));
out:
    { int saved_errno=errno;

	if (pid!=(pid_t)-1) kill (pid,SIGTERM), waitpid (pid,NULL,0);

	errno = outoff ? saved_errno : (n--?saved_errno:FATAL_START(EIO));
    }

  return n;
}

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 mkisofs"), exit(FATAL_START(errno));
    else if (mkisofs_pid == 0)
    {	dup2  (fildes[1],1);
	close (fildes[0]);
	close (fildes[1]);
	close (outfd);	/* redundant:-) */

#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);
	/*
	 * 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,":-( mkisofs has failed: %d\n",WEXITSTATUS(ret)),
	    exit (1);
    }
    else if (n<0)
    { int err = errno;
	errno = err&0x7F;	/* they might be passing FATAL_START */
	perror (":-( write failed"), exit (err);
    }
}

/*
 * This may not be larger than 32KB!
 */
#define MAX_IVDs	16
#define IVDs_SIZE	(sizeof(struct iso_primary_descriptor)*MAX_IVDs)

int main (int argc, char *argv[])
{ int  imgfd=-1;
  char *in_device=NULL,*out_device=NULL,*in_image=NULL,*env;
  char dev_found;
  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  next_session=-1,alleged_next_session=-1,new_size;
  struct iso_primary_descriptor *descr;

    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));
    }
    /*
     * 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;

	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"))
	{   if (fstat (fd,&fdst) < 0)
		perror (":-( unable to fstat"),	exit (1);

	    close (fd);

	    if (media_reload (argv[2],&fdst))
		perror (":-( unable to reload tray"), exit (1);
	    exit (0);
	}
	exit(1);
    }

    /* mmap buffer so that we can use it with /dev/raw/rawN */
#if defined(MAP_ANONYMOUS) && !(defined(__sun) || defined(sun))
    the_buffer = mmap (NULL,2*DVD_BLOCK,PROT_READ|PROT_WRITE,
			MAP_PRIVATE|MAP_ANONYMOUS,-1,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,2*DVD_BLOCK,PROT_READ|PROT_WRITE,
			MAP_PRIVATE,fd,0);
	close (fd);
    }
#endif
    if (the_buffer == MAP_FAILED)
	fprintf (stderr,":-( unable to anonymously mmap %d: ",2*DVD_BLOCK),
	perror (NULL), exit (FATAL_START(errno));

    mkisofs_argv = malloc ((argc+3)*sizeof(char *));
    if (mkisofs_argv == NULL)
        fprintf (stderr,":-( unable to allocate %lu bytes: ",
			(argc+3)*sizeof(char *)),
	perror (NULL), exit (FATAL_START(errno));

    env = getenv ("MKISOFS");
    mkisofs_argv[0] = (env?env:"mkisofs"), 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 (!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[output] 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,"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%16)
				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%16)
				fprintf (stderr,":-( insane tracksize%c%d option\n",
						o[9],dao_size),
				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%16)
				fprintf (stderr,":-( insane seek%c%d option\n",
						o[4],next_session),
				exit(FATAL_START(EINVAL));
			}
		    }
		}
		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/cdrom <mkisofs options>\n",argv[0]);
		printf ("  for <mkisofs options> see 'mkisofs %s'\n",opt);
		exit (FATAL_START(EINVAL));
	    }
	    else if (strstr (opt,"-version"))
	    {	PRINT_VERSION (argv[0]);
		printf ("  front-ending to %s: ",mkisofs_argv[0]);
		fflush (stdout);
		setuid(getuid());
		execlp (mkisofs_argv[0],mkisofs_argv[0],"-version",NULL);
		fprintf (stderr,"\n- %s: unable to execute %s: ",
				argv[0],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,'=')))
		{ uid_t euid=geteuid();

		    seteuid (getuid());	/* get real for parsing -[ZM] a=b */

		    while (in_image)
		    {	*in_image='\0';
			errno=0;
			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 */
			else
			    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;
		    }

		    seteuid (euid);	/* revert to saved [set-]uid */
		}
	    }

	    /*
	     * 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 *)the_buffer=0;	/* redundant:-)	*/
	    if (mmc_profile&0x10000)	/* blank media	*/
		n=0, errno=EIO;
	    else
	    {	n=pread64 (in_fd,the_buffer,2048,VOLDESC_OFF*CD_BLOCK);
		if (n==0) 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')
	    {	if (memcmp (the_buffer,"\1CD001",6))
	            fprintf (stderr,":-( %s doesn't look like isofs...\n",
		    		in_device), exit(FATAL_START(EMEDIUMTYPE));

		next_session=setup_C_parm(C_parm,
				(struct iso_primary_descriptor *)the_buffer);

		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 */
		    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 (the_buffer,"\1CD001",6))
		    warn_for_isofs = 1;
		if (next_session<0) next_session = 0;
		continue;
	    }
	}
	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)
	    fprintf (stderr,"%s: no mkisofs options specified, "
			    "aborting...\n",argv[0]),
	    exit (FATAL_START(EINVAL));
    }
    else if ((mkisofs_argc-growisofs_argc)>1)
	fprintf (stderr,"%s: no mkisofs options are permitted with =, "
			"aborting...\n",argv[0]),
	exit (FATAL_START(EINVAL));

    mkisofs_argv[mkisofs_argc] = NULL;

    assert (next_session!=-1);
    assert (in_fd!=-1);
    assert (out_fd!=-1);

    /* never finalize disc at multi-sessioning DVD±R recordings...	*/
    { int profile = mmc_profile&0xFFFF;
	if (next_session>0 &&
	    (profile==0x1B || profile==0x11))	dvd_compat=0;
	/* ... except when filling the media up:-)			*/
	if (next_session>0 && zero_image)	dvd_compat=1;
    }

    if (warn_for_isofs)
    { int fd=open("/dev/tty",O_RDONLY);

	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");
    }

#define CLOSEONEXEC(fd)	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);
    CLOSEONEXEC(ioctl_fd);
#undef CLOSEONEXEC

    if (!dry_run && (poor_man || next_session==0))	/* unmount media */
    {	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 (argv[0],"-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;
	}
	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));
	}
    }

    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 0x1A:	/* DVD+RW			*/
	    case 0x1B:	/* DVD+R			*/
		break;
	    default:
		fprintf (stderr,":-( %s: mounted media doesn't appear to be "
	    			"DVD±RW or DVD±R\n",out_device),
		exit(FATAL_START(EMEDIUMTYPE));
		break;
	}
    }

    if (imgfd>=0)
    {	quiet--;
	if (builtin_dd (imgfd,out_fd,next_session*CD_BLOCK) < 0)
	{ int err = errno;
	    errno = err&0x7F;     /* they might be passing FATAL_START */
	    perror (":-( write failed"), exit (err);
	}
	errno = 0;
	exit (0);
	/* NOT REACHED */
    }

    pipe_mkisofs_up (mkisofs_argv,in_fd,out_fd,next_session*CD_BLOCK);

    /*
     * Recall that second 32KB written in this session are still
     * in the upper half of the_buffer! And now note that
     * poor_man_rewritable() fills the first 32KB with volume
     * descriptor set from 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)))
    {
	descr = (struct iso_primary_descriptor *)the_buffer;

	if (memcmp (descr+16,"\1CD001",6))
	    fprintf (stderr,":-( %s:%d doesn't look like isofs!\n",
				out_device,next_session),
	    exit ((errno=EMEDIUMTYPE));

	fprintf (stderr,"%s: copying volume descriptor(s)\n",
			poor_man?ioctl_device:out_device);

	new_size = from_733(descr[16].volume_space_size) + next_session;

	if (!poor_man && (errno=0, pread64 (out_fd,descr,DVD_BLOCK,
				  	    VOLDESC_OFF*CD_BLOCK)) != DVD_BLOCK)
	    errno=errno?errno:EIO,
	    perror (":-( unable to pread64(2) volume descriptors set"),
	    exit (errno);

	memcpy (descr+0,descr+16,sizeof(struct iso_primary_descriptor));
	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 (descr[16+i].type[0] == (unsigned char)255)	break;
	    if (memcmp (descr[16+i].id,"CD001",5))		break;

	    if (descr[16+i].type[0] != descr[i].type[0])
	    {	fprintf (stderr,":-? volume descriptor mismatch, did you "
				"use same mkisofs options?\n");
		break;
	    }
	    memcpy (descr+i,descr+16+i,sizeof(struct iso_primary_descriptor));
	    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);
    }

    errno = 0;
    exit (0);
}
