summaryrefslogtreecommitdiff
path: root/genisoimage/write.c
diff options
context:
space:
mode:
Diffstat (limited to 'genisoimage/write.c')
-rw-r--r--genisoimage/write.c2840
1 files changed, 2840 insertions, 0 deletions
diff --git a/genisoimage/write.c b/genisoimage/write.c
new file mode 100644
index 0000000..a423ab1
--- /dev/null
+++ b/genisoimage/write.c
@@ -0,0 +1,2840 @@
+/*
+ * This file has been modified for the cdrkit suite.
+ *
+ * The behaviour and appearence of the program code below can differ to a major
+ * extent from the version distributed by the original author(s).
+ *
+ * For details, see Changelog file distributed with the cdrkit package. If you
+ * received this file from another source then ask the distributing person for
+ * a log of modifications.
+ *
+ */
+
+/* @(#)write.c 1.88 06/02/01 joerg */
+/* Parts from @(#)write.c 1.106 07/02/17 joerg */
+/*
+ * Program write.c - dump memory structures to file for iso9660 filesystem.
+ *
+ * Written by Eric Youngdale (1993).
+ *
+ * Copyright 1993 Yggdrasil Computing, Incorporated
+ * Copyright (c) 1999-2003 J. Schilling
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* APPLE_HYB James Pearson j.pearson@ge.ucl.ac.uk 23/2/2000 */
+
+#include <mconfig.h>
+#include "genisoimage.h"
+#include <timedefs.h>
+#include <fctldefs.h>
+#ifdef SORTING
+#include "match.h"
+#endif /* SORTING */
+#include <errno.h>
+#include <schily.h>
+#ifdef DVD_VIDEO
+#include "dvd_reader.h"
+#include "dvd_file.h"
+#include "ifo_read.h"
+#endif
+#ifdef APPLE_HYB
+#include <ctype.h>
+#endif
+
+#ifdef VMS
+#include "vms.h"
+#endif
+
+/* Max number of sectors we will write at one time */
+#define NSECT 16
+
+/* Counters for statistics */
+
+static int table_size = 0;
+static int total_dir_size = 0;
+static int rockridge_size = 0;
+static struct directory **pathlist;
+static int next_path_index = 1;
+static int sort_goof;
+
+static int is_rr_dir = 0;
+
+struct output_fragment *out_tail;
+struct output_fragment *out_list;
+
+struct iso_primary_descriptor vol_desc;
+
+void set_721(char *pnt, unsigned int i);
+void set_722(char *pnt, unsigned int i);
+void set_723(char *pnt, unsigned int i);
+void set_731(char *pnt, unsigned int i);
+void set_732(char *pnt, unsigned int i);
+void set_733(char *pnt, unsigned int i);
+int get_731(char *p);
+int get_732(char *p);
+int get_733(char *p);
+static int xawrite(void *buffer, int size, int count, FILE *file,
+ int submode, BOOL islast);
+void xfwrite(void *buffer, int size, int count, FILE *file, int submode,
+ BOOL islast);
+static int assign_directory_addresses(struct directory *node);
+#ifdef APPLE_HYB
+static void write_one_file(char *filename, off_t size, FILE *outfile,
+ off_t off);
+#else
+static void write_one_file(char *filename, off_t size, FILE *outfile);
+#endif
+static void write_files(FILE *outfile);
+#if 0
+static void dump_filelist __PR((void));
+#endif
+static int compare_dirs(const void *rr, const void *ll);
+int sort_directory(struct directory_entry **sort_dir, int rr);
+static int root_gen(void);
+static BOOL assign_file_addresses(struct directory *dpnt, BOOL isnest);
+static void free_one_directory(struct directory *dpnt);
+static void free_directories(struct directory *dpnt);
+void generate_one_directory(struct directory *dpnt, FILE *outfile);
+static void build_pathlist(struct directory *node);
+static int compare_paths(void const *r, void const *l);
+static int generate_path_tables(void);
+void memcpy_max(char *to, char *from, int max);
+void outputlist_insert(struct output_fragment *frag);
+static int file_write(FILE *outfile);
+static int pvd_write(FILE *outfile);
+static int xpvd_write(FILE *outfile);
+static int evd_write(FILE *outfile);
+static int vers_write(FILE *outfile);
+static int graftcp(char *to, char *from, char *ep);
+static int pathcp(char *to, char *from, char *ep);
+static int pathtab_write(FILE *outfile);
+static int exten_write(FILE *outfile);
+int oneblock_size(int starting_extent);
+static int pathtab_size(int starting_extent);
+static int startpad_size(int starting_extent);
+static int interpad_size(int starting_extent);
+static int endpad_size(int starting_extent);
+static int file_gen(void);
+static int dirtree_dump(void);
+static int dirtree_fixup(int starting_extent);
+static int dirtree_size(int starting_extent);
+static int ext_size(int starting_extent);
+static int dirtree_write(FILE *outfile);
+static int dirtree_cleanup(FILE *outfile);
+static int startpad_write(FILE *outfile);
+static int interpad_write(FILE *outfile);
+static int endpad_write(FILE *outfile);
+#ifdef APPLE_HYB
+static int hfs_pad;
+static int hfs_get_parms(char *key);
+static void hfs_file_gen(int start_extent);
+static void gen_prepboot(void);
+Ulong get_adj_size(int Csize);
+int adj_size(int Csize, int start_extent, int extra);
+void adj_size_other(struct directory *dpnt);
+static int hfs_hce_write(FILE * outfile);
+int insert_padding_file(int size);
+#endif /* APPLE_HYB */
+
+#ifdef SORTING
+static int compare_sort(const void * rr, const void * ll);
+static void reassign_link_addresses(struct directory * dpnt);
+static int sort_file_addresses(void);
+#endif /* SORTING */
+
+/*
+ * Routines to actually write the disc. We write sequentially so that
+ * we could write a tape, or write the disc directly
+ */
+#define FILL_SPACE(X) memset(vol_desc.X, ' ', sizeof (vol_desc.X))
+
+void
+set_721(char *pnt, unsigned int i)
+{
+ pnt[0] = i & 0xff;
+ pnt[1] = (i >> 8) & 0xff;
+}
+
+void
+set_722(char *pnt, unsigned int i)
+{
+ pnt[0] = (i >> 8) & 0xff;
+ pnt[1] = i & 0xff;
+}
+
+void
+set_723(char *pnt, unsigned int i)
+{
+ pnt[3] = pnt[0] = i & 0xff;
+ pnt[2] = pnt[1] = (i >> 8) & 0xff;
+}
+
+void
+set_731(char *pnt, unsigned int i)
+{
+ pnt[0] = i & 0xff;
+ pnt[1] = (i >> 8) & 0xff;
+ pnt[2] = (i >> 16) & 0xff;
+ pnt[3] = (i >> 24) & 0xff;
+}
+
+void
+set_732(char *pnt, unsigned int i)
+{
+ pnt[3] = i & 0xff;
+ pnt[2] = (i >> 8) & 0xff;
+ pnt[1] = (i >> 16) & 0xff;
+ pnt[0] = (i >> 24) & 0xff;
+}
+
+void
+set_733(char *pnt, unsigned int i)
+{
+ pnt[7] = pnt[0] = i & 0xff;
+ pnt[6] = pnt[1] = (i >> 8) & 0xff;
+ pnt[5] = pnt[2] = (i >> 16) & 0xff;
+ pnt[4] = pnt[3] = (i >> 24) & 0xff;
+}
+
+int
+get_731(char *p)
+{
+ return ((p[0] & 0xff)
+ | ((p[1] & 0xff) << 8)
+ | ((p[2] & 0xff) << 16)
+ | ((p[3] & 0xff) << 24));
+}
+
+int
+get_732(char *p)
+{
+ return ((p[3] & 0xff)
+ | ((p[2] & 0xff) << 8)
+ | ((p[1] & 0xff) << 16)
+ | ((p[0] & 0xff) << 24));
+}
+
+int
+get_733(char *p)
+{
+ return ((p[0] & 0xff)
+ | ((p[1] & 0xff) << 8)
+ | ((p[2] & 0xff) << 16)
+ | ((p[3] & 0xff) << 24));
+}
+
+void
+xfwrite(void *buffer, int size, int count, FILE *file, int submode, BOOL islast)
+{
+ /*
+ * This is a hack that could be made better.
+ * XXXIs this the only place?
+ * It is definitely needed on Operating Systems that do not allow to
+ * write files that are > 2GB. If the system is fast enough to be able
+ * to feed 1400 KB/s writing speed of a DVD-R drive, use stdout.
+ * If the system cannot do this reliable, you need to use this hacky
+ * option.
+ */
+ static int idx = 0;
+
+#ifdef XFWRITE_DEBUG
+ if (count != 1 || (size % 2048) != 0)
+ fprintf(stderr, "Count: %d, size: %d\n", count, size);
+#endif
+
+ if (split_output != 0 &&
+ (idx == 0 || ftell(file) >= ((off_t)1024 * 1024 * 1024))) {
+ char nbuf[512];
+ extern char *outfile;
+
+ if (idx == 0)
+ unlink(outfile);
+ sprintf(nbuf, "%s_%02d", outfile, idx++);
+ file = freopen(nbuf, "wb", file);
+ if (file == NULL) {
+#ifdef USE_LIBSCHILY
+ comerr("Cannot open '%s'.\n", nbuf);
+#else
+ fprintf(stderr, "Cannot open '%s'.\n", nbuf);
+ exit(1);
+#endif
+ }
+ }
+ while (count) {
+ int got;
+
+ seterrno(0);
+ if (osecsize != 0)
+ got = xawrite(buffer, size, count, file, submode, islast);
+ else
+ got = fwrite(buffer, size, count, file);
+
+ if (got <= 0) {
+#ifdef USE_LIBSCHILY
+ comerr("cannot fwrite %d*%d\n", size, count);
+#else
+ fprintf(stderr, "cannot fwrite %d*%d\n", size, count);
+ exit(1);
+#endif
+ }
+ /*
+ * This comment is in hope to prevent silly people from
+ * e.g. SuSE (who did not yet learn C but believe that
+ * they need to patch other peoples code) from changing the
+ * next cast into an illegal lhs cast expression.
+ * The cast below is the correct way to handle the problem.
+ * The (void *) cast is to avoid a GCC warning like:
+ * "warning: dereferencing type-punned pointer will break \
+ * strict-aliasing rules"
+ * which is wrong this code. (void *) introduces a compatible
+ * intermediate type in the cast list.
+ */
+ count -= got;
+ buffer = (void *)(((char *)buffer) + size * got);
+ }
+}
+
+static int
+xawrite(void *buffer, int size, int count, FILE *file, int submode, BOOL islast)
+{
+ register char *p = buffer;
+ register int amt = size * count;
+ register int n;
+ struct xa_subhdr subhdr[2];
+
+ if (osecsize == 2048)
+ return (fwrite(buffer, size, count, file));
+
+ if (amt % 2048)
+ comerrno(EX_BAD,
+ "Trying to write %d bytes (not a multiple of 2048).\n",
+ amt);
+
+ subhdr[0].file_number = subhdr[1].file_number = 0;
+ subhdr[0].channel_number = subhdr[1].channel_number = 0;
+ subhdr[0].coding = subhdr[1].coding = 0;
+
+ while (amt > 0) {
+#ifdef LATER
+ if (submode < 0)
+ subhdr[0].sub_mode = subhdr[1].sub_mode = XA_SUBH_DATA;
+ else
+ subhdr[0].sub_mode = subhdr[1].sub_mode = submode;
+#else
+ subhdr[0].sub_mode = subhdr[1].sub_mode = XA_SUBH_DATA;
+#endif
+
+ if ((amt <= 2048) && islast) {
+ subhdr[0].sub_mode = subhdr[1].sub_mode
+ |= (XA_SUBH_EOR|XA_SUBH_EOF);
+ }
+ n = fwrite(&subhdr, sizeof (subhdr), 1, file);
+ if (n <= 0)
+ return (n);
+
+ n = fwrite(p, 2048, 1, file);
+ if (n <= 0)
+ return (n);
+
+ p += 2048;
+ amt -= 2048;
+ }
+ return (1);
+}
+
+#ifdef APPLE_HYB
+/*
+ * use the deferred_write struct to store info about the hfs_boot_file
+ */
+static struct deferred_write mac_boot;
+
+#endif /* APPLE_HYB */
+static struct deferred_write *dw_head = NULL,
+ *dw_tail = NULL;
+
+unsigned int last_extent_written = 0;
+static Uint path_table_index;
+time_t begun;
+
+/*
+ * We recursively walk through all of the directories and assign extent
+ * numbers to them. We have already assigned extent numbers to everything that
+ * goes in front of them
+ */
+static int
+assign_directory_addresses(struct directory *node)
+{
+ int dir_size;
+ struct directory *dpnt;
+
+ dpnt = node;
+
+ while (dpnt) {
+ /* skip if it's hidden */
+ if (dpnt->dir_flags & INHIBIT_ISO9660_ENTRY) {
+ dpnt = dpnt->next;
+ continue;
+ }
+ /*
+ * If we already have an extent for this (i.e. it came from a
+ * multisession disc), then don't reassign a new extent.
+ */
+ dpnt->path_index = next_path_index++;
+ if (dpnt->extent == 0) {
+ dpnt->extent = last_extent;
+ dir_size = ISO_BLOCKS(dpnt->size);
+
+ last_extent += dir_size;
+
+ /*
+ * Leave room for the CE entries for this directory.
+ * Keep them close to the reference directory so that
+ * access will be quick.
+ */
+ if (dpnt->ce_bytes) {
+ last_extent += ISO_BLOCKS(dpnt->ce_bytes);
+ }
+ }
+ if (dpnt->subdir) {
+ assign_directory_addresses(dpnt->subdir);
+ }
+ dpnt = dpnt->next;
+ }
+ return (0);
+}
+
+#ifdef APPLE_HYB
+static void
+write_one_file(char *filename, off_t size, FILE *outfile, off_t off)
+#else
+static void
+write_one_file(char *filename, off_t size, FILE *outfile)
+#endif /* APPLE_HYB */
+{
+ /*
+ * It seems that there are still stone age C-compilers
+ * around.
+ * The Metrowerks C found on BeOS/PPC does not allow
+ * more than 32kB of local vars.
+ * As we do not need to call write_one_file() recursively
+ * we make buffer static.
+ */
+static char buffer[SECTOR_SIZE * NSECT];
+ FILE *infile;
+ off_t remain;
+ int use;
+
+ char *mirror_name;
+ unsigned char md5[16];
+ int include_in_jigdo = list_file_in_jigdo(filename, size, &mirror_name, md5);
+
+ if ((infile = fopen(filename, "rb")) == NULL) {
+#ifdef USE_LIBSCHILY
+ comerr("cannot open '%s'\n", filename);
+#else
+#ifndef HAVE_STRERROR
+ fprintf(stderr, "cannot open '%s': (%d)\n",
+ filename, errno);
+#else
+ fprintf(stderr, "cannot open '%s': %s\n",
+ filename, strerror(errno));
+#endif
+ exit(1);
+#endif
+ }
+#ifdef APPLE_HYB
+ fseek(infile, off, SEEK_SET);
+#endif /* APPLE_HYB */
+ remain = size;
+
+ if (include_in_jigdo)
+ write_jt_match_record(filename, mirror_name, SECTOR_SIZE, size, md5);
+
+ while (remain > 0) {
+ int amt;
+
+ use = (remain > SECTOR_SIZE * NSECT - 1 ?
+ NSECT * SECTOR_SIZE : remain);
+ use = ISO_ROUND_UP(use); /* Round up to nearest sector */
+ /* boundary */
+ memset(buffer, 0, use);
+ seterrno(0);
+ amt = fread(buffer, 1, use, infile);
+ if (amt < use && amt < remain) {
+ /*
+ * Note that genisoimage is not star and no 100% archiver.
+ * We only detect file growth if the new size does not
+ * match 'use' at the last read.
+ */
+ if (geterrno() == 0) {
+#ifdef USE_LIBSCHILY
+ comerrno(EX_BAD,
+ "File '%s' did shrink.\n"
+ "Files must not be changed while genisoimage runs!\n",
+ filename);
+#else
+ fprintf(stderr,
+ "File '%s' did shrink.\n"
+ "Files must not be changed while genisoimage runs!\n",
+ filename);
+ exit(EX_BAD);
+#endif
+ }
+#ifdef USE_LIBSCHILY
+ comerr("Cannot read from '%s'\n", filename);
+#else
+ fprintf(stderr, "Cannot read from '%s'\n", filename);
+ exit(1);
+#endif
+ }
+ if (!include_in_jigdo)
+ jtwrite(buffer, use, 1,
+ XA_SUBH_DATA, remain <= (SECTOR_SIZE * NSECT));
+ xfwrite(buffer, use, 1, outfile,
+ XA_SUBH_DATA, remain <= (SECTOR_SIZE * NSECT));
+ last_extent_written += use / SECTOR_SIZE;
+#if 0
+ if ((last_extent_written % 1000) < use / SECTOR_SIZE) {
+ fprintf(stderr, "%d..", last_extent_written);
+ }
+#else
+ if (verbose > 0 &&
+ (int)(last_extent_written % (gui ? 500 : 5000)) <
+ use / SECTOR_SIZE) {
+ time_t now;
+ time_t the_end;
+ double frac;
+
+ time(&now);
+ frac = last_extent_written / (1.0 * last_extent);
+ the_end = begun + (now - begun) / frac;
+#ifndef NO_FLOATINGPOINT
+ fprintf(stderr, "%6.2f%% done, estimate finish %s",
+ frac * 100., ctime(&the_end));
+#else
+ fprintf(stderr, "%3d.%-02d%% done, estimate finish %s",
+ (int)(frac * 100.),
+ (int)((frac+.00005) * 10000.)%100,
+ ctime(&the_end));
+#endif
+ fflush(stderr);
+ }
+#endif
+ remain -= use;
+ }
+ fclose(infile);
+}/* write_one_file(... */
+
+static void
+write_files(FILE *outfile)
+{
+ struct deferred_write *dwpnt,
+ *dwnext;
+
+ dwpnt = dw_head;
+ while (dwpnt) {
+/*#define DEBUG*/
+#ifdef DEBUG
+ fprintf(stderr,
+ "The file name is %s and pad is %d, size is %lld and extent is %d\n",
+ dwpnt->name, dwpnt->pad,
+ (Llong)dwpnt->size, dwpnt->extent);
+#endif
+ if (dwpnt->table) {
+ jtwrite(dwpnt->table, ISO_ROUND_UP(dwpnt->size), 1, XA_SUBH_DATA, TRUE);
+ xfwrite(dwpnt->table, ISO_ROUND_UP(dwpnt->size), 1,
+ outfile,
+ XA_SUBH_DATA, TRUE);
+ last_extent_written += ISO_BLOCKS(dwpnt->size);
+ table_size += dwpnt->size;
+/* fprintf(stderr, "Size %lld ", (Llong)dwpnt->size); */
+ free(dwpnt->table);
+ dwpnt->table = NULL;
+ } else {
+
+#ifdef VMS
+ vms_write_one_file(dwpnt->name, dwpnt->size, outfile);
+#else
+#ifdef APPLE_HYB
+ write_one_file(dwpnt->name, dwpnt->size, outfile,
+ dwpnt->off);
+#else
+ write_one_file(dwpnt->name, dwpnt->size, outfile);
+#endif /* APPLE_HYB */
+#endif
+ free(dwpnt->name);
+ dwpnt->name = NULL;
+ }
+
+
+#ifndef DVD_VIDEO
+#define dvd_video 0
+#endif
+
+#ifndef APPLE_HYB
+#define apple_hyb 0
+#endif
+
+#if defined(APPLE_HYB) || defined(DVD_VIDEO)
+
+ if (apple_hyb || dvd_video) {
+ /*
+ * we may have to pad out ISO files to work with HFS
+ * clump sizes
+ */
+ char blk[SECTOR_SIZE];
+ Uint i;
+
+ for (i = 0; i < dwpnt->pad; i++) {
+ jtwrite(blk, SECTOR_SIZE, 1, 0, FALSE);
+ xfwrite(blk, SECTOR_SIZE, 1, outfile, 0, FALSE);
+ last_extent_written++;
+ }
+ }
+#endif /* APPLE_HYB || DVD_VIDEO */
+
+
+ dwnext = dwpnt;
+ dwpnt = dwpnt->next;
+ free(dwnext);
+ dwnext = NULL;
+ }
+}/* write_files(... */
+
+#if 0
+static void
+dump_filelist()
+{
+ struct deferred_write *dwpnt;
+
+ dwpnt = dw_head;
+ while (dwpnt) {
+ fprintf(stderr, "File %s\n", dwpnt->name);
+ dwpnt = dwpnt->next;
+ }
+ fprintf(stderr, "\n");
+}
+
+#endif
+
+static int
+compare_dirs(const void *rr, const void *ll)
+{
+ char *rpnt,
+ *lpnt;
+ struct directory_entry **r,
+ **l;
+
+ r = (struct directory_entry **) rr;
+ l = (struct directory_entry **) ll;
+ rpnt = (*r)->isorec.name;
+ lpnt = (*l)->isorec.name;
+
+#ifdef APPLE_HYB
+ /*
+ * resource fork MUST (not sure if this is true for HFS volumes) be
+ * before the data fork - so force it here
+ */
+ if ((*r)->assoc && (*r)->assoc == (*l))
+ return (1);
+ if ((*l)->assoc && (*l)->assoc == (*r))
+ return (-1);
+#endif /* APPLE_HYB */
+
+ /* If the entries are the same, this is an error. */
+ if (strcmp(rpnt, lpnt) == 0) {
+#ifdef USE_LIBSCHILY
+ errmsgno(EX_BAD,
+ "Error: '%s' and '%s' have the same ISO9660 name '%s'.\n",
+ (*r)->whole_name, (*l)->whole_name,
+ rpnt);
+#else
+ fprintf(stderr,
+ "Error: '%s' and '%s' have the same ISO9660 name '%s'.\n",
+ (*r)->whole_name, (*l)->whole_name,
+ rpnt);
+#endif
+ sort_goof++;
+ }
+ /* Check we don't have the same RR name */
+ if (use_RockRidge && !is_rr_dir) {
+ /*
+ * entries *can* have the same RR name in the "rr_moved"
+ * directory so skip checks if we're in reloc_dir
+ */
+ if (!(strcmp((*r)->name, (*l)->name))) {
+#ifdef USE_LIBSCHILY
+ errmsgno(EX_BAD,
+ "Error: '%s' and '%s' have the same Rock Ridge name '%s'.\n",
+ (*r)->whole_name, (*l)->whole_name,
+ (*r)->name);
+#else
+ fprintf(stderr,
+ "Error: '%s' and '%s' have the same Rock Ridge name '%s'.\n",
+ (*r)->whole_name, (*l)->whole_name,
+ (*r)->name);
+#endif
+ sort_goof++;
+ }
+ }
+ /*
+ * Put the '.' and '..' entries on the head of the sorted list. For
+ * normal ASCII, this always happens to be the case, but out of band
+ * characters cause this not to be the case sometimes.
+ * FIXME(eric) - these tests seem redundant, in that the name is never
+ * assigned these values. It will instead be \000 or \001, and thus
+ * should always be sorted correctly. I need to figure out why I
+ * thought I needed this in the first place.
+ */
+#if 0
+ if (strcmp(rpnt, ".") == 0)
+ return (-1);
+ if (strcmp(lpnt, ".") == 0)
+ return (1);
+
+ if (strcmp(rpnt, "..") == 0)
+ return (-1);
+ if (strcmp(lpnt, "..") == 0)
+ return (1);
+#else
+ /*
+ * The code above is wrong (as explained in Eric's comment), leading to
+ * incorrect sort order iff the -L option ("allow leading dots") is in
+ * effect and a directory contains entries that start with a dot.
+ * (TF, Tue Dec 29 13:49:24 CET 1998)
+ */
+ if ((*r)->isorec.name_len[0] == 1 && *rpnt == 0)
+ return (-1); /* '.' */
+ if ((*l)->isorec.name_len[0] == 1 && *lpnt == 0)
+ return (1);
+
+ if ((*r)->isorec.name_len[0] == 1 && *rpnt == 1)
+ return (-1); /* '..' */
+ if ((*l)->isorec.name_len[0] == 1 && *lpnt == 1)
+ return (1);
+#endif
+
+ while (*rpnt && *lpnt) {
+ if (*rpnt == ';' && *lpnt != ';')
+ return (-1);
+ if (*rpnt != ';' && *lpnt == ';')
+ return (1);
+
+ if (*rpnt == ';' && *lpnt == ';')
+ return (0);
+
+ if (*rpnt == '.' && *lpnt != '.')
+ return (-1);
+ if (*rpnt != '.' && *lpnt == '.')
+ return (1);
+
+ if ((unsigned char) *rpnt < (unsigned char) *lpnt)
+ return (-1);
+ if ((unsigned char) *rpnt > (unsigned char) *lpnt)
+ return (1);
+ rpnt++;
+ lpnt++;
+ }
+ if (*rpnt)
+ return (1);
+ if (*lpnt)
+ return (-1);
+ return (0);
+}
+
+/*
+ * Function: sort_directory
+ *
+ * Purpose: Sort the directory in the appropriate ISO9660
+ * order.
+ *
+ * Notes: Returns 0 if OK, returns > 0 if an error occurred.
+ */
+int
+sort_directory(struct directory_entry **sort_dir, int rr)
+{
+ int dcount = 0;
+ int xcount = 0;
+ int j;
+ int i,
+ len;
+ struct directory_entry *s_entry;
+ struct directory_entry **sortlist;
+
+ /* need to keep a count of how many entries are hidden */
+ s_entry = *sort_dir;
+ while (s_entry) {
+ if (s_entry->de_flags & INHIBIT_ISO9660_ENTRY)
+ xcount++;
+ dcount++;
+ s_entry = s_entry->next;
+ }
+
+ if (dcount == 0) {
+ return (0);
+ }
+ /* OK, now we know how many there are. Build a vector for sorting. */
+ sortlist = (struct directory_entry **)
+ e_malloc(sizeof (struct directory_entry *) * dcount);
+
+ j = dcount - 1;
+ dcount = 0;
+ s_entry = *sort_dir;
+ while (s_entry) {
+ if (s_entry->de_flags & INHIBIT_ISO9660_ENTRY) {
+ /* put any hidden entries at the end of the vector */
+ sortlist[j--] = s_entry;
+ } else {
+ sortlist[dcount] = s_entry;
+ dcount++;
+ }
+ len = s_entry->isorec.name_len[0];
+ s_entry->isorec.name[len] = 0;
+ s_entry = s_entry->next;
+ }
+
+ /* Each directory is required to contain at least . and .. */
+ if (dcount < 2) {
+#ifdef USE_LIBSCHILY
+ errmsgno(EX_BAD,
+ "Directory size too small (. or .. missing ??%s)\n",
+ "?"); /* Try to avoid a GCC trigraph warning */
+#else
+ fprintf(stderr,
+ "Directory size too small (. or .. missing ??%s)\n",
+ "?"); /* Try to avoid a GCC trigraph warning */
+#endif
+ sort_goof = 1;
+
+ } else {
+ /* only sort the non-hidden entries */
+ sort_goof = 0;
+ is_rr_dir = rr;
+#ifdef PROTOTYPES
+ qsort(sortlist, dcount, sizeof (struct directory_entry *),
+ (int (*) (const void *, const void *)) compare_dirs);
+#else
+ qsort(sortlist, dcount, sizeof (struct directory_entry *),
+ compare_dirs);
+#endif
+
+ /*
+ * Now reassemble the linked list in the proper sorted order
+ * We still need the hidden entries, as they may be used in
+ * the Joliet tree.
+ */
+ for (i = 0; i < dcount + xcount - 1; i++) {
+ sortlist[i]->next = sortlist[i + 1];
+ }
+
+ sortlist[dcount + xcount - 1]->next = NULL;
+ *sort_dir = sortlist[0];
+ }
+
+ free(sortlist);
+ sortlist = NULL;
+ return (sort_goof);
+}
+
+static int
+root_gen()
+{
+ init_fstatbuf();
+
+ root_record.length[0] = 1 +
+ offsetof(struct iso_directory_record, name[0]);
+ root_record.ext_attr_length[0] = 0;
+ set_733((char *) root_record.extent, root->extent);
+ set_733((char *) root_record.size, ISO_ROUND_UP(root->size));
+ iso9660_date(root_record.date, root_statbuf.st_mtime);
+ root_record.flags[0] = ISO_DIRECTORY;
+ root_record.file_unit_size[0] = 0;
+ root_record.interleave[0] = 0;
+ set_723(root_record.volume_sequence_number, volume_sequence_number);
+ root_record.name_len[0] = 1;
+ return (0);
+}
+
+#ifdef SORTING
+/*
+ * sorts deferred_write entries based on the sort weight
+ */
+static int
+compare_sort(const void *rr, const void *ll)
+{
+ struct deferred_write **r;
+ struct deferred_write **l;
+ int r_sort;
+ int l_sort;
+
+ r = (struct deferred_write **) rr;
+ l = (struct deferred_write **) ll;
+ r_sort = (*r)->s_entry->sort;
+ l_sort = (*l)->s_entry->sort;
+
+ if (r_sort != l_sort)
+ return (r_sort < l_sort ? 1 : -1);
+ else
+ return ((*r)->extent - (*l)->extent);
+}
+
+/*
+ * reassign start extents to files that are "hard links" to
+ * files that may have been sorted
+ */
+static void
+reassign_link_addresses(struct directory *dpnt)
+{
+ struct directory_entry *s_entry;
+ struct file_hash *s_hash;
+
+ while (dpnt) {
+ s_entry = dpnt->contents;
+ for (s_entry = dpnt->contents; s_entry; s_entry = s_entry->next) {
+ /* link files have already been given the weight NOT_SORTED */
+ if (s_entry->sort == NOT_SORTED)
+ {
+ /* update the start extent */
+ s_hash = find_hash(s_entry->dev, s_entry->inode);
+ if (s_hash) {
+ set_733((char *) s_entry->isorec.extent,
+ s_hash->starting_block);
+ s_entry->starting_block = s_hash->starting_block;
+ }
+ }
+
+ if (verbose > 2 && s_entry->size != 0) {
+ fprintf(stderr, "%8u %8u ",
+ s_entry->starting_block,
+ (unsigned int)(s_entry->starting_block + ISO_BLOCKS(s_entry->size) - 1));
+
+ if (s_entry->inode != TABLE_INODE) {
+ fprintf(stderr, "%s\n", s_entry->whole_name);
+ } else {
+ fprintf(stderr, "%s%s%s\n",
+ s_entry->filedir->whole_name,
+ SPATH_SEPARATOR, trans_tbl);
+ }
+ }
+ }
+ if (dpnt->subdir) {
+ reassign_link_addresses(dpnt->subdir);
+ }
+
+ dpnt = dpnt->next;
+ }
+}
+
+/*
+ * sort files in order of the given sort weight
+ */
+static int
+sort_file_addresses()
+{
+ struct deferred_write *dwpnt;
+ struct deferred_write **sortlist;
+ struct directory_entry *s_entry;
+ int start_extent;
+ int num = 0;
+ int i;
+
+ /* need to store start extents for linked files */
+ flush_hash();
+
+ /* find out how many files we have */
+ dwpnt = dw_head;
+ while (dwpnt) {
+ num++;
+ dwpnt = dwpnt->next;
+ }
+
+ /* return if we have none */
+ if (num == 0) {
+ return (1);
+ }
+
+ /* save the start extent of the first file */
+ start_extent = dw_head->extent;
+
+ /* set up vector to store entries */
+ sortlist = (struct deferred_write **)
+ e_malloc(sizeof (struct deferred_write *) * num);
+
+ for (i = 0, dwpnt = dw_head; i < num; i++, dwpnt = dwpnt->next)
+ sortlist[i] = dwpnt;
+
+ /* sort the list */
+#ifdef PROTOTYPES
+ qsort(sortlist, num, sizeof (struct deferred_write *),
+ (int (*)(const void *, const void *))compare_sort);
+#else
+ qsort(sortlist, num, sizeof (struct deferred_write *), compare_sort);
+#endif
+
+ /* reconstruct the linked list */
+ for (i = 0; i < num-1; i++) {
+ sortlist[i]->next = sortlist[i+1];
+ }
+
+ sortlist[num-1]->next = NULL;
+ dw_head = sortlist[0];
+
+ free(sortlist);
+
+ /* set the new start extents for the sorted list */
+ for (i = 0, dwpnt = dw_head; i < num; i++, dwpnt = dwpnt->next) {
+ s_entry = dwpnt->s_entry;
+ dwpnt->extent = s_entry->starting_block = start_extent;
+ set_733((char *) s_entry->isorec.extent, start_extent);
+
+ start_extent += ISO_BLOCKS(s_entry->size);
+#ifdef DVD_VIDEO
+ /*
+ * Shouldn't this be done for every type of sort? Otherwise
+ * we will loose every pad info we add if we sort the files
+ */
+ if (dvd_video) {
+ start_extent += dwpnt->pad;
+ }
+#endif /* DVD_VIDEO */
+
+ /* cache start extents for any linked files */
+ add_hash(s_entry);
+ }
+
+ return (0);
+}
+#endif /* SORTING */
+
+
+
+static BOOL
+assign_file_addresses(struct directory *dpnt, BOOL isnest)
+{
+ struct directory *finddir;
+ struct directory_entry *s_entry;
+ struct file_hash *s_hash;
+ struct deferred_write *dwpnt;
+ char whole_path[PATH_MAX];
+#ifdef DVD_VIDEO
+ char dvd_path[PATH_MAX];
+ title_set_info_t * title_set_info = NULL;
+ char *p;
+#endif
+ BOOL ret = FALSE;
+
+ while (dpnt) {
+#ifdef DVD_VIDEO
+ if (dvd_video && root == dpnt->parent &&
+ ((p = strstr(dpnt->whole_name, "VIDEO_TS")) != 0)&&
+ strcmp(p, "VIDEO_TS") == 0) {
+
+ int maxlen = strlen(dpnt->whole_name)-8;
+ if (maxlen > (sizeof (dvd_path)-1))
+ maxlen = sizeof (dvd_path)-1;
+ strncpy(dvd_path, dpnt->whole_name, maxlen);
+ dvd_path[maxlen] = '\0';
+
+#ifdef DEBUG
+ fprintf(stderr, "Found 'VIDEO_TS', the path is %s \n", dvd_path);
+#endif
+ title_set_info = DVDGetFileSet(dvd_path);
+ if (title_set_info == 0) {
+ /*
+ * Do not switch off -dvd-video but let is fail later.
+ */
+/* dvd_video = 0;*/
+ errmsgno(EX_BAD, "Unable to parse DVD-Video structures.\n");
+ } else {
+ ret = TRUE;
+ }
+ }
+#endif /* DVD_VIDEO */
+
+ s_entry = dpnt->contents;
+ for (s_entry = dpnt->contents; s_entry;
+ s_entry = s_entry->next) {
+ /*
+ * If we already have an extent for this entry, then
+ * don't assign a new one. It must have come from a
+ * previous session on the disc. Note that we don't
+ * end up scheduling the thing for writing either.
+ */
+ if (get_733(s_entry->isorec.extent) != 0) {
+ continue;
+ }
+ /*
+ * This saves some space if there are symlinks present
+ */
+ s_hash = find_hash(s_entry->dev, s_entry->inode);
+ if (s_hash) {
+ if (verbose > 2) {
+ fprintf(stderr, "Cache hit for '%s%s%s'\n", s_entry->filedir->de_name,
+ SPATH_SEPARATOR,
+ s_entry->name);
+ }
+ set_733((char *) s_entry->isorec.extent,
+ s_hash->starting_block);
+ set_733((char *) s_entry->isorec.size,
+ s_hash->size);
+#ifdef SORTING
+ /* check for non-directory files */
+ if (do_sort && ((s_entry->isorec.flags[0] & ISO_DIRECTORY) == 0)) {
+ /* make sure the real file has the highest weighting */
+ s_hash->de->sort = MAX(s_entry->sort, s_hash->de->sort);
+ /* flag this as a potential non-sorted file */
+ s_entry->sort = NOT_SORTED;
+ }
+#endif /* SORTING */
+ continue;
+ }
+ /*
+ * If this is for a directory that is not a . or
+ * a .. entry, then look up the information for the
+ * entry. We have already assigned extents for
+ * directories, so we just need to fill in the blanks
+ * here.
+ */
+ if (strcmp(s_entry->name, ".") != 0 &&
+ strcmp(s_entry->name, "..") != 0 &&
+ s_entry->isorec.flags[0] & ISO_DIRECTORY) {
+ finddir = dpnt->subdir;
+ while (1 == 1) {
+ if (finddir->self == s_entry)
+ break;
+ finddir = finddir->next;
+ if (!finddir) {
+#ifdef DVD_VIDEO
+ if (title_set_info != 0) {
+ DVDFreeFileSet(title_set_info);
+ }
+#endif
+ comerrno(EX_BAD,
+ "Fatal goof - could not find dir entry for '%s'\n",
+ s_entry->name);
+ }
+ }
+ set_733((char *) s_entry->isorec.extent,
+ finddir->extent);
+ s_entry->starting_block = finddir->extent;
+ s_entry->size = ISO_ROUND_UP(finddir->size);
+ total_dir_size += s_entry->size;
+ add_hash(s_entry);
+ set_733((char *) s_entry->isorec.size,
+ ISO_ROUND_UP(finddir->size));
+ continue;
+ }
+ /*
+ * If this is . or .., then look up the relevant info
+ * from the tables.
+ */
+ if (strcmp(s_entry->name, ".") == 0) {
+ set_733((char *) s_entry->isorec.extent,
+ dpnt->extent);
+
+ /*
+ * Set these so that the hash table has the
+ * correct information
+ */
+ s_entry->starting_block = dpnt->extent;
+ s_entry->size = ISO_ROUND_UP(dpnt->size);
+
+ add_hash(s_entry);
+ s_entry->starting_block = dpnt->extent;
+ set_733((char *) s_entry->isorec.size,
+ ISO_ROUND_UP(dpnt->size));
+ continue;
+ }
+ if (strcmp(s_entry->name, "..") == 0) {
+ if (dpnt == root) {
+ total_dir_size += root->size;
+ }
+ set_733((char *) s_entry->isorec.extent,
+ dpnt->parent->extent);
+
+ /*
+ * Set these so that the hash table has the
+ * correct information
+ */
+ s_entry->starting_block = dpnt->parent->extent;
+ s_entry->size =
+ ISO_ROUND_UP(dpnt->parent->size);
+
+ add_hash(s_entry);
+ s_entry->starting_block = dpnt->parent->extent;
+ set_733((char *) s_entry->isorec.size,
+ ISO_ROUND_UP(dpnt->parent->size));
+ continue;
+ }
+ /*
+ * Some ordinary non-directory file. Just schedule
+ * the file to be written. This is all quite
+ * straightforward, just make a list and assign
+ * extents as we go. Once we get through writing all
+ * of the directories, we should be ready write out
+ * these files
+ */
+ if (s_entry->size) {
+ dwpnt = (struct deferred_write *)
+ e_malloc(sizeof (struct deferred_write));
+ /* save this directory entry for later use */
+ dwpnt->s_entry = s_entry;
+ /* set the initial padding to zero */
+ dwpnt->pad = 0;
+#ifdef DVD_VIDEO
+ if (dvd_video && (title_set_info != 0)) {
+ int pad;
+
+ pad = DVDGetFilePad(title_set_info, s_entry->name);
+ if (pad < 0) {
+ errmsgno(EX_BAD,
+ "Implementation botch. Video pad for file %s is %d\n",
+ s_entry->name, pad),
+ comerrno(EX_BAD,
+ "Either the *.IFO file is bad or you found a genisoimage bug.\n");
+ }
+ dwpnt->pad = pad;
+ if (verbose > 0 && pad != 0) {
+ fprintf(stderr,
+ "The pad was %d for file %s\n", dwpnt->pad, s_entry->name);
+ }
+ }
+#endif /* DVD_VIDEO */
+#ifdef APPLE_HYB
+ /*
+ * maybe an offset to start of the real
+ * file/fork
+ */
+ dwpnt->off = s_entry->hfs_off;
+#else
+ dwpnt->off = (off_t)0;
+#endif /* APPLE_HYB */
+ if (dw_tail) {
+ dw_tail->next = dwpnt;
+ dw_tail = dwpnt;
+ } else {
+ dw_head = dwpnt;
+ dw_tail = dwpnt;
+ }
+ if (s_entry->inode == TABLE_INODE) {
+ dwpnt->table = s_entry->table;
+ dwpnt->name = NULL;
+ sprintf(whole_path, "%s%s%s",
+ s_entry->filedir->whole_name,
+ SPATH_SEPARATOR, trans_tbl);
+ } else {
+ dwpnt->table = NULL;
+ strcpy(whole_path, s_entry->whole_name);
+ dwpnt->name = strdup(whole_path);
+ }
+ dwpnt->next = NULL;
+ dwpnt->size = s_entry->size;
+ dwpnt->extent = last_extent;
+ set_733((char *) s_entry->isorec.extent,
+ last_extent);
+ s_entry->starting_block = last_extent;
+ add_hash(s_entry);
+ last_extent += ISO_BLOCKS(s_entry->size);
+#ifdef DVD_VIDEO
+ /* Shouldn't we always add the pad info? */
+ if (dvd_video) {
+ last_extent += dwpnt->pad;
+ }
+#endif /* DVD_VIDEO */
+ if (verbose > 2 && !do_sort) {
+ fprintf(stderr, "%8d %8u %s\n",
+ s_entry->starting_block,
+ last_extent - 1, whole_path);
+ }
+#ifdef DBG_ISO
+ if (ISO_BLOCKS(s_entry->size) > 500) {
+ fprintf(stderr,
+ "Warning: large file '%s'\n",
+ whole_path);
+ fprintf(stderr,
+ "Starting block is %d\n",
+ s_entry->starting_block);
+ fprintf(stderr,
+ "Reported file size is %lld\n",
+ (Llong)s_entry->size);
+
+ }
+#endif
+#ifdef NOT_NEEDED /* Never use this code if you like to create a DVD */
+
+ if (last_extent > (800000000 >> 11)) {
+ /* More than 800Mb? Punt */
+ fprintf(stderr,
+ "Extent overflow processing file '%s'\n",
+ whole_path);
+ fprintf(stderr,
+ "Starting block is %d\n",
+ s_entry->starting_block);
+ fprintf(stderr,
+ "Reported file size is %lld\n",
+ (Llong)s_entry->size);
+ exit(1);
+ }
+#endif
+ continue;
+ }
+ /*
+ * This is for zero-length files. If we leave the
+ * extent 0, then we get screwed, because many readers
+ * simply drop files that have an extent of zero.
+ * Thus we leave the size 0, and just assign the
+ * extent number.
+ */
+ set_733((char *) s_entry->isorec.extent, last_extent);
+ }
+ if (dpnt->subdir) {
+ if (assign_file_addresses(dpnt->subdir, TRUE))
+ ret = TRUE;
+ }
+ dpnt = dpnt->next;
+ }
+#ifdef DVD_VIDEO
+ if (title_set_info != NULL) {
+ DVDFreeFileSet(title_set_info);
+ }
+ if (dvd_video && !ret && !isnest) {
+ errmsgno(EX_BAD,
+ "Could not find correct 'VIDEO_TS' directory.\n");
+ }
+#endif /* DVD_VIDEO */
+ return (ret);
+} /* assign_file_addresses(... */
+
+static void
+free_one_directory(struct directory *dpnt)
+{
+ struct directory_entry *s_entry;
+ struct directory_entry *s_entry_d;
+
+ s_entry = dpnt->contents;
+ while (s_entry) {
+ s_entry_d = s_entry;
+ s_entry = s_entry->next;
+
+ if (s_entry_d->rr_attributes) {
+ free(s_entry_d->rr_attributes);
+ s_entry_d->rr_attributes = NULL;
+ }
+ if (s_entry_d->name != NULL) {
+ free(s_entry_d->name);
+ s_entry_d->name = NULL;
+ }
+ if (s_entry_d->whole_name != NULL) {
+ free(s_entry_d->whole_name);
+ s_entry_d->whole_name = NULL;
+ }
+#ifdef APPLE_HYB
+ if (apple_both && s_entry_d->hfs_ent && !s_entry_d->assoc)
+ free(s_entry_d->hfs_ent);
+#endif /* APPLE_HYB */
+
+ free(s_entry_d);
+ s_entry_d = NULL;
+ }
+ dpnt->contents = NULL;
+}/* free_one_directory(... */
+
+static void
+free_directories(struct directory *dpnt)
+{
+ while (dpnt) {
+ free_one_directory(dpnt);
+ if (dpnt->subdir)
+ free_directories(dpnt->subdir);
+ dpnt = dpnt->next;
+ }
+}
+
+void
+generate_one_directory(struct directory *dpnt, FILE *outfile)
+{
+ unsigned int ce_address = 0;
+ char *ce_buffer;
+ unsigned int ce_index = 0;
+ unsigned int ce_size;
+ unsigned int dir_index;
+ char *directory_buffer;
+ int new_reclen;
+ struct directory_entry *s_entry;
+ struct directory_entry *s_entry_d;
+ unsigned int total_size;
+
+ total_size = ISO_ROUND_UP(dpnt->size);
+ directory_buffer = (char *) e_malloc(total_size);
+ memset(directory_buffer, 0, total_size);
+ dir_index = 0;
+
+ ce_size = ISO_ROUND_UP(dpnt->ce_bytes);
+ ce_buffer = NULL;
+
+ if (ce_size > 0) {
+ ce_buffer = (char *) e_malloc(ce_size);
+ memset(ce_buffer, 0, ce_size);
+
+ ce_index = 0;
+
+ /* Absolute byte address of CE entries for this directory */
+ ce_address = last_extent_written + (total_size >> 11);
+ ce_address = ce_address << 11;
+ }
+ s_entry = dpnt->contents;
+ while (s_entry) {
+ /* skip if it's hidden */
+ if (s_entry->de_flags & INHIBIT_ISO9660_ENTRY) {
+ s_entry = s_entry->next;
+ continue;
+ }
+ /*
+ * We do not allow directory entries to cross sector
+ * boundaries. Simply pad, and then start the next entry at
+ * the next sector
+ */
+ new_reclen = s_entry->isorec.length[0];
+ if ((dir_index & (SECTOR_SIZE - 1)) + new_reclen >=
+ SECTOR_SIZE) {
+ dir_index = ISO_ROUND_UP(dir_index);
+ }
+ memcpy(directory_buffer + dir_index, &s_entry->isorec,
+ offsetof(struct iso_directory_record, name[0]) +
+ s_entry->isorec.name_len[0]);
+ dir_index += offsetof(struct iso_directory_record, name[0]) +
+ s_entry->isorec.name_len[0];
+
+ /* Add the Rock Ridge attributes, if present */
+ if (s_entry->rr_attr_size) {
+ if (dir_index & 1) {
+ directory_buffer[dir_index++] = 0;
+ }
+ /*
+ * If the RR attributes were too long, then write the
+ * CE records, as required.
+ */
+ if (s_entry->rr_attr_size != s_entry->total_rr_attr_size) {
+ struct iso_xa_dir_record *xadp;
+ unsigned char *pnt;
+ int len,
+ nbytes;
+
+ /*
+ * Go through the entire record, first skip
+ * the XA record and then fix up the
+ * CE entries so that the extent and offset
+ * are correct
+ */
+ pnt = s_entry->rr_attributes;
+ len = s_entry->total_rr_attr_size;
+
+ if (len >= 14) {
+ xadp = (struct iso_xa_dir_record *)pnt;
+
+ if (xadp->signature[0] == 'X' && xadp->signature[1] == 'A' &&
+ xadp->reserved[0] == '\0') {
+ len -= 14;
+ pnt += 14;
+ }
+ }
+
+ while (len > 3) {
+#ifdef DEBUG
+ if (ce_size <= 0) {
+ fprintf(stderr,
+ "Warning: ce_index(%d) && ce_address(%d) not initialized\n",
+ ce_index, ce_address);
+ }
+#endif
+
+ if (pnt[0] == 'C' && pnt[1] == 'E') {
+ nbytes = get_733((char *) pnt + 20);
+
+ if ((ce_index & (SECTOR_SIZE - 1)) + nbytes >=
+ SECTOR_SIZE) {
+ ce_index = ISO_ROUND_UP(ce_index);
+ }
+ set_733((char *) pnt + 4,
+ (ce_address + ce_index) >> 11);
+ set_733((char *) pnt + 12,
+ (ce_address + ce_index) & (SECTOR_SIZE - 1));
+
+
+ /*
+ * Now store the block in the
+ * ce buffer
+ */
+ memcpy(ce_buffer + ce_index,
+ pnt + pnt[2], nbytes);
+ ce_index += nbytes;
+ if (ce_index & 1) {
+ ce_index++;
+ }
+ }
+ len -= pnt[2];
+ pnt += pnt[2];
+ }
+
+ }
+ rockridge_size += s_entry->total_rr_attr_size;
+ memcpy(directory_buffer + dir_index,
+ s_entry->rr_attributes,
+ s_entry->rr_attr_size);
+ dir_index += s_entry->rr_attr_size;
+ }
+ if (dir_index & 1) {
+ directory_buffer[dir_index++] = 0;
+ }
+ s_entry_d = s_entry;
+ s_entry = s_entry->next;
+
+ /*
+ * Joliet doesn't use the Rock Ridge attributes, so we free
+ * it here.
+ */
+ if (s_entry_d->rr_attributes) {
+ free(s_entry_d->rr_attributes);
+ s_entry_d->rr_attributes = NULL;
+ }
+ }
+
+ if (dpnt->size != dir_index) {
+#ifdef USE_LIBSCHILY
+ errmsgno(EX_BAD,
+ "Unexpected directory length %lld expected: %d '%s'\n",
+ (Llong)dpnt->size,
+ dir_index, dpnt->de_name);
+#else
+ fprintf(stderr,
+ "Unexpected directory length %lld expected: %d '%s'\n",
+ (Llong)dpnt->size,
+ dir_index, dpnt->de_name);
+#endif
+ }
+ jtwrite(directory_buffer, total_size, 1, 0, FALSE);
+ xfwrite(directory_buffer, total_size, 1, outfile, 0, FALSE);
+ last_extent_written += total_size >> 11;
+ free(directory_buffer);
+ directory_buffer = NULL;
+
+ if (ce_size > 0) {
+ if (ce_index != dpnt->ce_bytes) {
+#ifdef USE_LIBSCHILY
+ errmsgno(EX_BAD,
+ "Continuation entry record length mismatch %d expected: %d.\n",
+ ce_index, dpnt->ce_bytes);
+#else
+ fprintf(stderr,
+ "Continuation entry record length mismatch %d expected: %d.\n",
+ ce_index, dpnt->ce_bytes);
+#endif
+ }
+ jtwrite(ce_buffer, ce_size, 1, 0, FALSE);
+ xfwrite(ce_buffer, ce_size, 1, outfile, 0, FALSE);
+ last_extent_written += ce_size >> 11;
+ free(ce_buffer);
+ ce_buffer = NULL;
+ }
+}/* generate_one_directory(... */
+
+static void
+build_pathlist(struct directory *node)
+{
+ struct directory *dpnt;
+
+ dpnt = node;
+
+ while (dpnt) {
+ /* skip if it's hidden */
+ if ((dpnt->dir_flags & INHIBIT_ISO9660_ENTRY) == 0)
+ pathlist[dpnt->path_index] = dpnt;
+
+ if (dpnt->subdir)
+ build_pathlist(dpnt->subdir);
+ dpnt = dpnt->next;
+ }
+}/* build_pathlist(... */
+
+static int
+compare_paths(void const *r, void const *l)
+{
+ struct directory const *ll = *(struct directory * const *) l;
+ struct directory const *rr = *(struct directory * const *) r;
+
+ if (rr->parent->path_index < ll->parent->path_index) {
+ return (-1);
+ }
+ if (rr->parent->path_index > ll->parent->path_index) {
+ return (1);
+ }
+ return (strcmp(rr->self->isorec.name, ll->self->isorec.name));
+
+}/* compare_paths(... */
+
+static int
+generate_path_tables()
+{
+ struct directory_entry *de = NULL;
+ struct directory *dpnt;
+ int fix;
+ int i;
+ int j;
+ int namelen;
+ char *npnt;
+ char *npnt1;
+ int tablesize;
+
+ /* First allocate memory for the tables and initialize the memory */
+ tablesize = path_blocks << 11;
+ path_table_m = (char *) e_malloc(tablesize);
+ path_table_l = (char *) e_malloc(tablesize);
+ memset(path_table_l, 0, tablesize);
+ memset(path_table_m, 0, tablesize);
+
+ /*
+ * Now start filling in the path tables. Start with root directory
+ */
+
+ path_table_index = 0;
+ pathlist = (struct directory **) e_malloc(sizeof (struct directory *)
+ * next_path_index);
+ memset(pathlist, 0, sizeof (struct directory *) * next_path_index);
+ build_pathlist(root);
+
+ do {
+ fix = 0;
+#ifdef PROTOTYPES
+ qsort(&pathlist[1], next_path_index - 1,
+ sizeof (struct directory *),
+ (int (*) (const void *, const void *)) compare_paths);
+#else
+ qsort(&pathlist[1], next_path_index - 1,
+ sizeof (struct directory *),
+ compare_paths);
+#endif
+
+ for (j = 1; j < next_path_index; j++) {
+ if (pathlist[j]->path_index != j) {
+ pathlist[j]->path_index = j;
+ fix++;
+ }
+ }
+ } while (fix);
+
+ for (j = 1; j < next_path_index; j++) {
+ dpnt = pathlist[j];
+ if (!dpnt) {
+#ifdef USE_LIBSCHILY
+ comerrno(EX_BAD, "Entry %d not in path tables\n", j);
+#else
+ fprintf(stderr, "Entry %d not in path tables\n", j);
+ exit(1);
+#endif
+ }
+ npnt = dpnt->de_name;
+
+ /* So the root comes out OK */
+ if ((*npnt == 0) || (dpnt == root)) {
+ npnt = ".";
+ }
+ npnt1 = strrchr(npnt, PATH_SEPARATOR);
+ if (npnt1) {
+ npnt = npnt1 + 1;
+ }
+ de = dpnt->self;
+ if (!de) {
+#ifdef USE_LIBSCHILY
+ comerrno(EX_BAD,
+ "Fatal ISO9660 goof - directory has amnesia\n");
+#else
+ fprintf(stderr,
+ "Fatal ISO9660 goof - directory has amnesia\n");
+ exit(1);
+#endif
+ }
+ namelen = de->isorec.name_len[0];
+
+ path_table_l[path_table_index] = namelen;
+ path_table_m[path_table_index] = namelen;
+ path_table_index += 2;
+
+ set_731(path_table_l + path_table_index, dpnt->extent);
+ set_732(path_table_m + path_table_index, dpnt->extent);
+ path_table_index += 4;
+
+ if (dpnt->parent->path_index > 0xffff) {
+#ifdef USE_LIBSCHILY
+ comerrno(EX_BAD,
+ "Unable to generate sane path tables - too many directories (%d)\n",
+ dpnt->parent->path_index);
+#else
+ fprintf(stderr,
+ "Unable to generate sane path tables - too many directories (%d)\n",
+ dpnt->parent->path_index);
+ exit(1);
+#endif
+ }
+
+ set_721(path_table_l + path_table_index,
+ dpnt->parent->path_index);
+ set_722(path_table_m + path_table_index,
+ dpnt->parent->path_index);
+ path_table_index += 2;
+
+ for (i = 0; i < namelen; i++) {
+ path_table_l[path_table_index] = de->isorec.name[i];
+ path_table_m[path_table_index] = de->isorec.name[i];
+ path_table_index++;
+ }
+ if (path_table_index & 1) {
+ path_table_index++; /* For odd lengths we pad */
+ }
+ }
+
+ free(pathlist);
+ pathlist = NULL;
+ if (path_table_index != path_table_size) {
+#ifdef USE_LIBSCHILY
+ errmsgno(EX_BAD,
+ "Path table lengths do not match %d expected: %d\n",
+ path_table_index,
+ path_table_size);
+#else
+ fprintf(stderr,
+ "Path table lengths do not match %d expected: %d\n",
+ path_table_index,
+ path_table_size);
+#endif
+ }
+ return (0);
+}/* generate_path_tables(... */
+
+void
+memcpy_max(char *to, char *from, int max)
+{
+ int n = strlen(from);
+
+ if (n > max) {
+ n = max;
+ }
+ memcpy(to, from, n);
+
+}/* memcpy_max(... */
+
+void
+outputlist_insert(struct output_fragment *frag)
+{
+ struct output_fragment *nfrag;
+
+ nfrag = e_malloc(sizeof (*frag));
+ movebytes(frag, nfrag, sizeof (*frag));
+ nfrag->of_start_extent = 0;
+
+ if (out_tail == NULL) {
+ out_list = out_tail = nfrag;
+ } else {
+ out_tail->of_next = nfrag;
+ out_tail = nfrag;
+ }
+}
+
+static int
+file_write(FILE *outfile)
+{
+ Uint should_write;
+
+#ifdef APPLE_HYB
+ char buffer[SECTOR_SIZE];
+
+ memset(buffer, 0, sizeof (buffer));
+
+ if (apple_hyb) {
+
+ int i;
+
+ /*
+ * write out padding to round up to HFS allocation block
+ */
+ for (i = 0; i < hfs_pad; i++) {
+ jtwrite(buffer, sizeof (buffer), 1, 0, FALSE);
+ xfwrite(buffer, sizeof (buffer), 1, outfile, 0, FALSE);
+ last_extent_written++;
+ }
+ }
+#endif /* APPLE_HYB */
+
+ /*
+ * OK, all done with that crap. Now write out the directories. This is
+ * where the fur starts to fly, because we need to keep track of each
+ * file as we find it and keep track of where we put it.
+ */
+ should_write = last_extent - session_start;
+
+ if (verbose > 2) {
+#ifdef DBG_ISO
+ fprintf(stderr,
+ "Total directory extents being written = %d\n",
+ last_extent);
+#endif
+
+#ifdef APPLE_HYB
+ if (apple_hyb)
+ fprintf(stderr,
+ "Total extents scheduled to be written (inc HFS) = %d\n",
+ last_extent - session_start);
+ else
+#endif /* APPLE_HYB */
+
+ fprintf(stderr,
+ "Total extents scheduled to be written = %u\n",
+ last_extent - session_start);
+ }
+ /* Now write all of the files that we need. */
+ write_files(outfile);
+
+#ifdef APPLE_HYB
+ /* write out extents/catalog/dt file */
+ if (apple_hyb) {
+
+ jtwrite(hce->hfs_ce, HFS_BLOCKSZ, hce->hfs_tot_size, 0, FALSE);
+ xfwrite(hce->hfs_ce, HFS_BLOCKSZ, hce->hfs_tot_size, outfile, 0, FALSE);
+
+ /* round up to a whole CD block */
+ if (HFS_ROUND_UP(hce->hfs_tot_size) -
+ hce->hfs_tot_size * HFS_BLOCKSZ) {
+ jtwrite(buffer,
+ HFS_ROUND_UP(hce->hfs_tot_size) -
+ hce->hfs_tot_size * HFS_BLOCKSZ, 1, 0, FALSE);
+ xfwrite(buffer,
+ HFS_ROUND_UP(hce->hfs_tot_size) -
+ hce->hfs_tot_size * HFS_BLOCKSZ, 1, outfile, 0, FALSE);
+ }
+ last_extent_written += ISO_ROUND_UP(hce->hfs_tot_size *
+ HFS_BLOCKSZ) / SECTOR_SIZE;
+
+ /* write out HFS boot block */
+ if (mac_boot.name)
+ write_one_file(mac_boot.name, mac_boot.size, outfile,
+ mac_boot.off);
+ }
+#endif /* APPLE_HYB */
+
+ /* The rest is just fluff. */
+ if (verbose == 0) {
+ return (0);
+ }
+#ifdef APPLE_HYB
+ if (apple_hyb) {
+ fprintf(stderr,
+ "Total extents actually written (inc HFS) = %d\n",
+ last_extent_written - session_start);
+ fprintf(stderr, "(Size of ISO volume = %d, HFS extra = %d)\n",
+ last_extent_written - session_start - hfs_extra,
+ hfs_extra);
+ } else
+#else
+ fprintf(stderr, "Total extents actually written = %d\n",
+ last_extent_written - session_start);
+#endif /* APPLE_HYB */
+
+ /* Hard links throw us off here */
+ if (should_write != (last_extent - session_start)) {
+ fprintf(stderr,
+ "Number of extents written not what was predicted. Please fix.\n");
+ fprintf(stderr, "Predicted = %d, written = %d\n",
+ should_write, last_extent);
+ }
+ fprintf(stderr, "Total translation table size: %d\n", table_size);
+ fprintf(stderr, "Total rockridge attributes bytes: %d\n",
+ rockridge_size);
+ fprintf(stderr, "Total directory bytes: %d\n", total_dir_size);
+ fprintf(stderr, "Path table size(bytes): %d\n", path_table_size);
+
+#ifdef DEBUG
+ fprintf(stderr,
+ "next extent, last_extent, last_extent_written %d %d %d\n",
+ next_extent, last_extent, last_extent_written);
+#endif
+
+ return (0);
+
+}/* iso_write(... */
+
+/*
+ * Function to write the PVD for the disc.
+ */
+static int
+pvd_write(FILE *outfile)
+{
+ char iso_time[17];
+ int should_write;
+ struct tm local;
+ struct tm gmt;
+
+
+ time(&begun);
+
+ local = *localtime(&begun);
+ gmt = *gmtime(&begun);
+
+ /*
+ * There was a comment here about breaking in the year 2000.
+ * That's not true, in 2000 tm_year == 100, so 1900+tm_year == 2000.
+ */
+ sprintf(iso_time, "%4.4d%2.2d%2.2d%2.2d%2.2d%2.2d00",
+ 1900 + local.tm_year,
+ local.tm_mon + 1, local.tm_mday,
+ local.tm_hour, local.tm_min, local.tm_sec);
+
+ local.tm_min -= gmt.tm_min;
+ local.tm_hour -= gmt.tm_hour;
+ local.tm_yday -= gmt.tm_yday;
+ if (local.tm_yday < -2) /* Hit new-year limit */
+ local.tm_yday = 1; /* Local is GMT + 1 day */
+ iso_time[16] = (local.tm_min + 60 *
+ (local.tm_hour + 24 * local.tm_yday)) / 15;
+
+ /* Next we write out the primary descriptor for the disc */
+ memset(&vol_desc, 0, sizeof (vol_desc));
+ vol_desc.type[0] = ISO_VD_PRIMARY;
+ memcpy(vol_desc.id, ISO_STANDARD_ID, sizeof (ISO_STANDARD_ID));
+ vol_desc.version[0] = 1;
+
+ memset(vol_desc.system_id, ' ', sizeof (vol_desc.system_id));
+ memcpy_max(vol_desc.system_id, system_id, strlen(system_id));
+
+ memset(vol_desc.volume_id, ' ', sizeof (vol_desc.volume_id));
+ memcpy_max(vol_desc.volume_id, volume_id, strlen(volume_id));
+
+ should_write = last_extent - session_start;
+ set_733((char *) vol_desc.volume_space_size, should_write);
+ set_723(vol_desc.volume_set_size, volume_set_size);
+ set_723(vol_desc.volume_sequence_number, volume_sequence_number);
+ set_723(vol_desc.logical_block_size, SECTOR_SIZE);
+
+ /*
+ * The path tables are used by DOS based machines to cache directory
+ * locations
+ */
+ set_733((char *) vol_desc.path_table_size, path_table_size);
+ set_731(vol_desc.type_l_path_table, path_table[0]);
+ set_731(vol_desc.opt_type_l_path_table, path_table[1]);
+ set_732(vol_desc.type_m_path_table, path_table[2]);
+ set_732(vol_desc.opt_type_m_path_table, path_table[3]);
+
+ /* Now we copy the actual root directory record */
+ memcpy(vol_desc.root_directory_record, &root_record,
+ offsetof(struct iso_directory_record, name[0]) + 1);
+
+ /*
+ * The rest is just fluff. It looks nice to fill in many of these
+ * fields, though.
+ */
+ FILL_SPACE(volume_set_id);
+ if (volset_id)
+ memcpy_max(vol_desc.volume_set_id, volset_id, strlen(volset_id));
+
+ FILL_SPACE(publisher_id);
+ if (publisher)
+ memcpy_max(vol_desc.publisher_id, publisher, strlen(publisher));
+
+ FILL_SPACE(preparer_id);
+ if (preparer)
+ memcpy_max(vol_desc.preparer_id, preparer, strlen(preparer));
+
+ FILL_SPACE(application_id);
+ if (appid)
+ memcpy_max(vol_desc.application_id, appid, strlen(appid));
+
+ FILL_SPACE(copyright_file_id);
+ if (copyright)
+ memcpy_max(vol_desc.copyright_file_id, copyright,
+ strlen(copyright));
+
+ FILL_SPACE(abstract_file_id);
+ if (abstract)
+ memcpy_max(vol_desc.abstract_file_id, abstract,
+ strlen(abstract));
+
+ FILL_SPACE(bibliographic_file_id);
+ if (biblio)
+ memcpy_max(vol_desc.bibliographic_file_id, biblio,
+ strlen(biblio));
+
+ FILL_SPACE(creation_date);
+ FILL_SPACE(modification_date);
+ FILL_SPACE(expiration_date);
+ FILL_SPACE(effective_date);
+ vol_desc.file_structure_version[0] = 1;
+ FILL_SPACE(application_data);
+
+ memcpy(vol_desc.creation_date, iso_time, 17);
+ memcpy(vol_desc.modification_date, iso_time, 17);
+ memcpy(vol_desc.expiration_date, "0000000000000000", 17);
+ memcpy(vol_desc.effective_date, iso_time, 17);
+
+ if (use_XA) {
+ char *xap = &((char *)&vol_desc)[1024];
+
+ memcpy(&xap[0], "CD-XA001", 8); /* XA Sign. */
+ memcpy(&xap[8], "\0\0", 2); /* XA flags */
+ memcpy(&xap[10], "\0\0\0\0\0\0\0\0", 8); /* Start dir */
+ memcpy(&xap[18], "\0\0\0\0\0\0\0\0", 8); /* Reserved */
+ }
+
+ /* if not a bootable cd do it the old way */
+ jtwrite(&vol_desc, SECTOR_SIZE, 1, 0, FALSE);
+ xfwrite(&vol_desc, SECTOR_SIZE, 1, outfile, 0, FALSE);
+ last_extent_written++;
+ return (0);
+}
+
+/*
+ * Function to write the Extended PVD for the disc.
+ */
+static int
+xpvd_write(FILE *outfile)
+{
+ vol_desc.type[0] = ISO_VD_SUPPLEMENTARY;
+ vol_desc.version[0] = 2;
+ vol_desc.file_structure_version[0] = 2;
+
+ /* if not a bootable cd do it the old way */
+ jtwrite(&vol_desc, SECTOR_SIZE, 1, 0, FALSE);
+ xfwrite(&vol_desc, SECTOR_SIZE, 1, outfile, 0, FALSE);
+ last_extent_written++;
+ return (0);
+}
+
+/*
+ * Function to write the EVD for the disc.
+ */
+static int
+evd_write(FILE *outfile)
+{
+ struct iso_primary_descriptor evol_desc;
+
+ /*
+ * Now write the end volume descriptor. Much simpler than the other
+ * one
+ */
+ memset(&evol_desc, 0, sizeof (evol_desc));
+ evol_desc.type[0] = (unsigned char) ISO_VD_END;
+ memcpy(evol_desc.id, ISO_STANDARD_ID, sizeof (ISO_STANDARD_ID));
+ evol_desc.version[0] = 1;
+ jtwrite(&evol_desc, SECTOR_SIZE, 1, 0, TRUE);
+ xfwrite(&evol_desc, SECTOR_SIZE, 1, outfile, 0, TRUE);
+ last_extent_written += 1;
+ return (0);
+}
+
+/*
+ * Function to write the version information for the disc.
+ */
+static int
+vers_write(FILE *outfile)
+{
+ char vers[SECTOR_SIZE+1];
+ int X_ac;
+ char **X_av;
+ char *cp;
+ int i;
+ int idx = 4;
+ int len;
+ extern char version_string[];
+ extern int path_ind;
+
+ /* Now write the version descriptor. */
+ memset(vers, 0, sizeof (vers));
+ strcpy(vers, "MKI ");
+
+ cp = vers;
+ X_ac = saved_ac();
+ X_av = saved_av();
+ strcpy(&cp[idx], ctime(&begun));
+ idx += 25;
+ strcpy(&cp[idx], version_string);
+ idx += strlen(version_string);
+ for (i = 1; i < X_ac; i++) {
+ len = strlen(X_av[i]);
+ if ((idx + len + 2) >= SECTOR_SIZE)
+ break;
+ cp[idx++] = ' ';
+ /*
+ * Do not give away secret information when not in debug mode.
+ */
+ if (debug)
+ strcpy(&cp[idx], X_av[i]);
+ else if (i >= path_ind)
+ len = graftcp(&cp[idx], X_av[i], &vers[SECTOR_SIZE-1]);
+ else if (X_av[i][0] == '/')
+ len = pathcp(&cp[idx], X_av[i], &vers[SECTOR_SIZE-1]);
+ else
+ strcpy(&cp[idx], X_av[i]);
+ idx += len;
+ }
+
+ cp[SECTOR_SIZE - 1] = '\0';
+ /* Per default: keep privacy. Blackout the version and arguments. */
+ if(getenv("ISODEBUG")) {
+ jtwrite(vers, SECTOR_SIZE, 1, 0, TRUE);
+ xfwrite(vers, SECTOR_SIZE, 1, outfile, 0, TRUE);
+ } else {
+ jtwrite(calloc(SECTOR_SIZE, 1), SECTOR_SIZE, 1, 0, TRUE);
+ xfwrite(calloc(SECTOR_SIZE, 1), SECTOR_SIZE, 1, outfile, 0, TRUE);
+ }
+ last_extent_written += 1;
+ return (0);
+}
+
+/*
+ * Avoid to write unwanted information into the version info string.
+ */
+static int
+graftcp(char *to, char *from, char *ep)
+{
+ int len = strlen(from);
+ char *node = NULL;
+
+ if (use_graft_ptrs)
+ node = findgequal(from);
+
+ if (node == NULL) {
+ len = 0;
+ node = from;
+ } else {
+ len = node - from;
+ *node = '\0';
+ strncpy(to, from, ep - to);
+ *node++ = '=';
+ to += len++;
+ *to++ = '=';
+ }
+ return (len + pathcp(to, node, ep));
+}
+
+static int
+pathcp(char *to, char *from, char *ep)
+{
+ int len = strlen(from);
+ char *p;
+
+ p = strrchr(from, '/');
+ if (p == NULL) {
+ strncpy(to, from, ep - to);
+ } else {
+ if (p[1] == '\0') {
+ --p;
+ while (p > from && *p != '/')
+ --p;
+ }
+ len = 0;
+ if (*p == '/') {
+ strncpy(to, "...", ep - to);
+ to += 3;
+ len = 3;
+ }
+ if (to < ep) {
+ strncpy(to, p, ep - to);
+ len += strlen(to);
+ }
+ }
+ return (len);
+}
+
+
+/*
+ * Function to write the path table for the disc.
+ */
+static int
+pathtab_write(FILE *outfile)
+{
+ /* Next we write the path tables */
+ jtwrite(path_table_l, path_blocks << 11, 1, 0, FALSE);
+ xfwrite(path_table_l, path_blocks << 11, 1, outfile, 0, FALSE);
+ last_extent_written += path_blocks;
+ jtwrite(path_table_m, path_blocks << 11, 1, 0, FALSE);
+ xfwrite(path_table_m, path_blocks << 11, 1, outfile, 0, FALSE);
+ last_extent_written += path_blocks;
+ free(path_table_l);
+ free(path_table_m);
+ path_table_l = NULL;
+ path_table_m = NULL;
+ return (0);
+}
+
+static int
+exten_write(FILE *outfile)
+{
+ jtwrite(extension_record, SECTOR_SIZE, 1, 0, FALSE);
+ xfwrite(extension_record, SECTOR_SIZE, 1, outfile, 0, FALSE);
+ last_extent_written++;
+ return (0);
+}
+
+/*
+ * Functions to describe padding block at the start of the disc.
+ */
+int
+oneblock_size(int starting_extent)
+{
+ last_extent++;
+ return (0);
+}
+
+/*
+ * Functions to describe path table size.
+ */
+static int
+pathtab_size(int starting_extent)
+{
+ path_table[0] = starting_extent;
+
+ path_table[1] = 0;
+ path_table[2] = path_table[0] + path_blocks;
+ path_table[3] = 0;
+ last_extent += 2 * path_blocks;
+ return (0);
+}
+
+/*
+ * Functions to describe padding blocks before PVD.
+ */
+static int
+startpad_size(int starting_extent)
+{
+ last_extent = session_start + 16;
+ return (0);
+}
+
+/*
+ * Functions to describe padding blocks between sections.
+ */
+static int
+interpad_size(int starting_extent)
+{
+ int emod = 0;
+
+#ifdef needed
+ starting_extent += 16; /* First add 16 pad blocks */
+#endif
+ if ((emod = starting_extent % 16) != 0) {
+ starting_extent += 16 - emod; /* Now pad to mod 16 # */
+ }
+ last_extent = starting_extent;
+ return (0);
+}
+
+/*
+ * Functions to describe padding blocks at end of disk.
+ */
+static int
+endpad_size(int starting_extent)
+{
+ starting_extent += 150; /* 150 pad blocks (post gap) */
+ last_extent = starting_extent;
+ return (0);
+}
+
+static int
+file_gen()
+{
+#ifdef APPLE_HYB
+ int start_extent = last_extent; /* orig ISO files start */
+
+#endif /* APPLE_HYB */
+
+ if (!assign_file_addresses(root, FALSE)) {
+#ifdef DVD_VIDEO
+ if (dvd_video) {
+ comerrno(EX_BAD, "Unable to make a DVD-Video image.\n"
+ "Possible reasons:\n"
+ " - VIDEO_TS subdirectory was not found on specified location\n"
+ " - VIDEO_TS has invalid contents\n"
+ );
+ }
+#else
+ ; /* EMPTY */
+#endif
+ }
+
+
+#ifdef SORTING
+ if (do_sort) {
+ if (sort_file_addresses() == 0)
+ reassign_link_addresses(root);
+ }
+#endif /* SORTING */
+
+#ifdef APPLE_HYB
+ /*
+ * put this here for the time being - may when I've worked out how to
+ * use Eric's new system for creating/writing parts of the image it
+ * may move to it's own routine
+ */
+ if (apple_hyb)
+ hfs_file_gen(start_extent);
+#ifdef PREP_BOOT
+ else if (use_prep_boot || use_chrp_boot)
+ gen_prepboot();
+#endif /* PREP_BOOT */
+#endif /* APPLE_HYB */
+
+ return (0);
+}
+
+static int
+dirtree_dump()
+{
+ if (verbose > 2) {
+ dump_tree(root);
+ }
+ return (0);
+}
+
+static int
+dirtree_fixup(int starting_extent)
+{
+ if (use_RockRidge && reloc_dir)
+ finish_cl_pl_entries();
+
+ if (use_RockRidge)
+ update_nlink_field(root);
+ return (0);
+}
+
+static int
+dirtree_size(int starting_extent)
+{
+ assign_directory_addresses(root);
+ return (0);
+}
+
+static int
+ext_size(int starting_extent)
+{
+ extern int extension_record_size;
+ struct directory_entry *s_entry;
+
+ extension_record_extent = starting_extent;
+ s_entry = root->contents;
+ set_733((char *) s_entry->rr_attributes + s_entry->rr_attr_size - 24,
+ extension_record_extent);
+ set_733((char *) s_entry->rr_attributes + s_entry->rr_attr_size - 8,
+ extension_record_size);
+ last_extent++;
+ return (0);
+}
+
+static int
+dirtree_write(FILE *outfile)
+{
+ generate_iso9660_directories(root, outfile);
+ return (0);
+}
+
+static int
+dirtree_cleanup(FILE *outfile)
+{
+ free_directories(root);
+ return (0);
+}
+
+static int
+startpad_write(FILE *outfile)
+{
+ char buffer[SECTOR_SIZE];
+ int i;
+ int npad;
+
+ memset(buffer, 0, sizeof (buffer));
+
+ npad = session_start + 16 - last_extent_written;
+
+ for (i = 0; i < npad; i++) {
+ jtwrite(buffer, sizeof (buffer), 1, 0, FALSE);
+ xfwrite(buffer, sizeof (buffer), 1, outfile, 0, FALSE);
+ last_extent_written++;
+ }
+
+ return (0);
+}
+
+static int
+interpad_write(FILE *outfile)
+{
+ char buffer[SECTOR_SIZE];
+ int i;
+ int npad = 0;
+
+ memset(buffer, 0, sizeof (buffer));
+
+#ifdef needed
+ npad = 16;
+#endif
+ if ((i = last_extent_written % 16) != 0)
+ npad += 16 - i;
+
+ for (i = 0; i < npad; i++) {
+ jtwrite(buffer, sizeof (buffer), 1, 0, FALSE);
+ xfwrite(buffer, sizeof (buffer), 1, outfile, 0, FALSE);
+ last_extent_written++;
+ }
+
+ return (0);
+}
+
+static int
+endpad_write(FILE *outfile)
+{
+ char buffer[SECTOR_SIZE];
+ int i;
+
+ memset(buffer, 0, sizeof (buffer));
+
+ for (i = 0; i < 150; i++) {
+ jtwrite(buffer, sizeof (buffer), 1, 0, FALSE);
+ xfwrite(buffer, sizeof (buffer), 1, outfile, 0, FALSE);
+ last_extent_written++;
+ }
+
+ return (0);
+}
+
+#ifdef APPLE_HYB
+
+/*
+ * hfs_get_parms: get HFS parameters from the command line
+ */
+
+static int
+hfs_get_parms(char *key)
+{
+ int ret = 0;
+ char *p;
+
+ if (hfs_parms == NULL)
+ return (ret);
+
+ if ((p = strstr(hfs_parms, key)) != NULL) {
+ p += strlen(key) + 1;
+ sscanf(p, "%d", &ret);
+ }
+
+ return (ret);
+}
+
+/*
+ * hfs_file_gen: set up "fake" HFS volume using the ISO9660 tree
+ */
+static void
+hfs_file_gen(int start_extent)
+{
+ int Csize; /* clump size for HFS vol */
+ int loop;
+ int last_extent_save = last_extent;
+ char *p;
+
+ /* allocate memory for the libhfs/genisoimage extra info */
+ hce = (hce_mem *) e_malloc(sizeof (hce_mem));
+
+ hce->error = (char *) e_malloc(1024);
+
+ /* mark as unallocated for use later */
+ hce->hfs_ce = hce->hfs_hdr = hce->hfs_map = 0;
+
+ /* reserve space for the label partition - if it is needed */
+#ifdef PREP_BOOT
+ /* a PReP bootable partition needs the map.. */
+ if (gen_pt || use_prep_boot || use_chrp_boot)
+#else
+ if (gen_pt)
+#endif /* PREP_BOOT */
+ hce->hfs_map_size = HFS_MAP_SIZE;
+ else
+ hce->hfs_map_size = 0;
+
+ /* set the HFS parameter string to upper case */
+ if (hfs_parms) {
+ for (p = hfs_parms; *p; p++)
+ *p = toupper(*p);
+ }
+
+ /* set the initial factor to increase Catalog file size */
+ if ((hce->ctc_size = hfs_get_parms("CTC")) == 0)
+ hce->ctc_size = CTC;
+
+ /* set the max size of the Catalog file */
+ if ((hce->max_XTCsize = hfs_get_parms("MAX_XTCSIZE")) == 0)
+ hce->max_XTCsize = MAX_XTCSIZE;
+
+ /* set the number of time to try to make an HFS volume */
+ if ((loop = hfs_get_parms("CTC_LOOP")) == 0)
+ loop = CTC_LOOP;
+
+ /*
+ * "create" the HFS volume (just the header, catalog/extents files) if
+ * there's a problem with the Catalog file being too small, we keep on
+ * increasing the size (up to CTC_LOOP) times and try again.
+ * Unfortunately I don't know enough about the inner workings of HFS,
+ * so I can't workout the size of the Catalog file in advance (and I
+ * don't want to "grow" as is is normally allowed to), therefore, this
+ * approach is a bit over the top as it involves throwing away the
+ * "volume" we have created and trying again ...
+ */
+ do {
+ hce->error[0] = '\0';
+
+ /* attempt to create the Mac volume */
+ Csize = make_mac_volume(root, start_extent);
+
+ /* if we have a problem ... */
+ if (Csize < 0) {
+ /*
+ * we've made too many attempts, or got some other
+ * error
+ */
+ if (loop == 0 || errno != HCE_ERROR) {
+ /* HCE_ERROR is not a valid errno value */
+ if (errno == HCE_ERROR)
+ errno = 0;
+
+ /* exit with the error */
+ if (*hce->error)
+ fprintf(stderr, "%s\n", hce->error);
+ perr(hfs_error);
+ } else {
+ /* increase Catalog file size factor */
+ hce->ctc_size *= CTC;
+
+ /*
+ * reset the initial "last_extent" and try
+ * again
+ */
+ last_extent = last_extent_save;
+ }
+ } else {
+ /* everything OK - just carry on ... */
+ loop = 0;
+ }
+ }
+ while (loop--);
+
+ hfs_extra = HFS_ROUND_UP(hce->hfs_tot_size) / SECTOR_SIZE;
+
+ last_extent += hfs_extra;
+
+ /* generate the Mac label and HFS partition maps */
+ mac_boot.name = hfs_boot_file;
+
+ /*
+ * only generate the partition tables etc. if we are making a bootable
+ * CD - or if the -part option is given
+ */
+ if (gen_pt) {
+ if (gen_mac_label(&mac_boot)) {
+ if (*hce->error)
+ fprintf(stderr, "%s\n", hce->error);
+ perr(hfs_error);
+ }
+ }
+ /* set Autostart filename if required */
+ if (autoname) {
+ if (autostart())
+ perr("Autostart filename must less than 12 characters");
+ }
+ /* finished with any HFS type errors */
+ free(hce->error);
+ hce->error = 0;
+
+ /*
+ * the ISO files need to start on a multiple of the HFS allocation
+ * blocks, so find out how much padding we need
+ */
+
+ /*
+ * take in accout alignment of files wrt HFS volume start - remove any
+ * previous session as well
+ */
+ start_extent -= session_start;
+ hfs_pad = ROUND_UP(start_extent*SECTOR_SIZE +
+ (hce->hfs_hdr_size + hce->hfs_map_size) * HFS_BLOCKSZ,
+ Csize) / SECTOR_SIZE;
+
+ hfs_pad -= (start_extent + (hce->hfs_hdr_size + hce->hfs_map_size) /
+ HFS_BLK_CONV);
+
+#ifdef PREP_BOOT
+ gen_prepboot_label(hce->hfs_map);
+#endif /* PREP_BOOT */
+
+}
+
+#ifdef PREP_BOOT
+static void
+gen_prepboot()
+{
+ /*
+ * we need to allocate the hce struct since hce->hfs_map is used to
+ * generate the fdisk partition map required for PReP booting
+ */
+ hce = (hce_mem *) e_malloc(sizeof (hce_mem));
+
+ /* mark as unallocated for use later */
+ hce->hfs_ce = hce->hfs_hdr = hce->hfs_map = 0;
+
+ /* reserve space for the label partition - if it is needed */
+ hce->hfs_map_size = HFS_MAP_SIZE;
+
+ hce->hfs_map = (unsigned char *) e_malloc(hce->hfs_map_size * HFS_BLOCKSZ);
+ gen_prepboot_label(hce->hfs_map);
+}
+
+#endif /* PREP_BOOT */
+
+/*
+ * get_adj_size: get the ajusted size of the volume with the HFS
+ * allocation block size for each file
+ */
+Ulong
+get_adj_size(int Csize)
+{
+ struct deferred_write *dw;
+ Ulong size = 0;
+ int count = 0;
+
+ /* loop through all the files finding the new total size */
+ for (dw = dw_head; dw; dw = dw->next) {
+ size += (ROUND_UP(dw->size, Csize)/HFS_BLOCKSZ);
+ count++;
+ }
+
+ /*
+ * crude attempt to prevent overflows - HFS can only cope with a
+ * maximum of about 65536 forks (actually less) - this will trap cases
+ * when we have far too many files
+ */
+
+ if (count >= 65536)
+ return (-1);
+ else
+ return (size);
+}
+
+/*
+ * adj_size: adjust the ISO record entries for all files
+ * based on the HFS allocation block size
+ */
+int
+adj_size(int Csize, int start_extent, int extra)
+{
+ struct deferred_write *dw;
+ struct directory_entry *s_entry;
+ int size;
+
+ /* get the adjusted start_extent (with padding) */
+ /* take in accout alignment of files wrt HFS volume start */
+
+ start_extent -= session_start;
+
+ start_extent = ROUND_UP(start_extent*SECTOR_SIZE + extra*HFS_BLOCKSZ,
+ Csize) / SECTOR_SIZE;
+
+ start_extent -= (extra / HFS_BLK_CONV);
+
+ start_extent += session_start;
+
+ /* initialise file hash */
+ flush_hash();
+
+ /*
+ * loop through all files changing their starting blocks and finding
+ * any padding needed to written out latter
+ */
+ for (dw = dw_head; dw; dw = dw->next) {
+ s_entry = dw->s_entry;
+ s_entry->starting_block = dw->extent = start_extent;
+ set_733((char *) s_entry->isorec.extent, start_extent);
+ size = ROUND_UP(dw->size, Csize) / SECTOR_SIZE;
+ dw->pad = size - ISO_ROUND_UP(dw->size) / SECTOR_SIZE;
+
+ /*
+ * cache non-HFS files - as there may be multiple links to
+ * these files (HFS files can't have multiple links). We will
+ * need to change the starting extent of the other links later
+ */
+ if (!s_entry->hfs_ent)
+ add_hash(s_entry);
+
+ start_extent += size;
+ }
+
+ return (start_extent);
+}
+
+/*
+ * adj_size_other: adjust any non-HFS files that may be linked
+ * to an existing file (i.e. not have a deferred_write
+ * entry of it's own
+ */
+void
+adj_size_other(struct directory *dpnt)
+{
+ struct directory_entry *s_entry;
+ struct file_hash *s_hash;
+
+ while (dpnt) {
+ s_entry = dpnt->contents;
+ for (s_entry = dpnt->contents; s_entry;
+ s_entry = s_entry->next) {
+ /*
+ * if it's an HFS file or a directory - then ignore
+ * (we're after non-HFS files)
+ */
+ if (s_entry->hfs_ent ||
+ (s_entry->isorec.flags[0] & ISO_DIRECTORY))
+ continue;
+
+ /*
+ * find any cached entry and assign new starting
+ * extent
+ */
+ s_hash = find_hash(s_entry->dev, s_entry->inode);
+ if (s_hash) {
+ set_733((char *) s_entry->isorec.extent,
+ s_hash->starting_block);
+ /* not vital - but tidy */
+ s_entry->starting_block =
+ s_hash->starting_block;
+ }
+ }
+ if (dpnt->subdir) {
+ adj_size_other(dpnt->subdir);
+ }
+ dpnt = dpnt->next;
+ }
+
+ /* clear file hash */
+ flush_hash();
+}
+
+/*
+ * hfs_hce_write: write out the HFS header stuff
+ */
+static int
+hfs_hce_write(FILE *outfile)
+{
+ char buffer[SECTOR_SIZE];
+ int n = 0;
+ int r; /* HFS hdr output */
+ int tot_size = hce->hfs_map_size + hce->hfs_hdr_size;
+
+ memset(buffer, 0, sizeof (buffer));
+
+ /*
+ * hack time ... if the tot_size is greater than 32Kb then
+ * it won't fit in the first 16 blank SECTORS (64 512 byte
+ * blocks, as most of this is padding, we just truncate this
+ * data to 64x4xHFS_BLOCKSZ ... hope this is OK ...
+ */
+
+ if (tot_size > 64) tot_size = 64;
+
+ /* get size in CD blocks == 4xHFS_BLOCKSZ == 2048 */
+ n = tot_size / HFS_BLK_CONV;
+ r = tot_size % HFS_BLK_CONV;
+
+ /* write out HFS volume header info */
+ jtwrite(hce->hfs_map, HFS_BLOCKSZ, tot_size, 0, FALSE);
+ xfwrite(hce->hfs_map, HFS_BLOCKSZ, tot_size, outfile, 0, FALSE);
+
+ /* fill up to a complete CD block */
+ if (r) {
+ jtwrite(buffer, HFS_BLOCKSZ, HFS_BLK_CONV - r, 0, FALSE);
+ xfwrite(buffer, HFS_BLOCKSZ, HFS_BLK_CONV - r, outfile, 0, FALSE);
+ n++;
+ }
+ last_extent_written += n;
+ return (0);
+}
+
+/*
+ * insert_padding_file : insert a dumy file to make volume at least
+ * 800k
+ *
+ * XXX If we ever need to write more then 2 GB, make size off_t
+ */
+int
+insert_padding_file(int size)
+{
+ struct deferred_write *dwpnt;
+
+ /* get the size in bytes */
+ size *= HFS_BLOCKSZ;
+
+ dwpnt = (struct deferred_write *)
+ e_malloc(sizeof (struct deferred_write));
+ dwpnt->s_entry = 0;
+ /* set the padding to zero */
+ dwpnt->pad = 0;
+ /* set offset to zero */
+ dwpnt->off = (off_t)0;
+
+ /*
+ * don't need to wory about the s_entry stuff as it won't be touched#
+ * at this point onwards
+ */
+
+ /* insert the entry in the list */
+ if (dw_tail) {
+ dw_tail->next = dwpnt;
+ dw_tail = dwpnt;
+ } else {
+ dw_head = dwpnt;
+ dw_tail = dwpnt;
+ }
+
+ /* aloocate memory as a "Table" file */
+ dwpnt->table = e_malloc(size);
+ dwpnt->name = NULL;
+
+ dwpnt->next = NULL;
+ dwpnt->size = size;
+ dwpnt->extent = last_extent;
+ last_extent += ISO_BLOCKS(size);
+
+ /* retune the size in HFS blocks */
+ return (ISO_ROUND_UP(size) / HFS_BLOCKSZ);
+}
+
+struct output_fragment hfs_desc = {NULL, NULL, NULL, hfs_hce_write, "HFS volume header"};
+
+#endif /* APPLE_HYB */
+
+struct output_fragment startpad_desc = {NULL, startpad_size, NULL, startpad_write, "Initial Padblock"};
+struct output_fragment voldesc_desc = {NULL, oneblock_size, root_gen, pvd_write, "Primary Volume Descriptor"};
+struct output_fragment xvoldesc_desc = {NULL, oneblock_size, NULL, xpvd_write, "Enhanced Volume Descriptor"};
+struct output_fragment end_vol = {NULL, oneblock_size, NULL, evd_write, "End Volume Descriptor" };
+struct output_fragment version_desc = {NULL, oneblock_size, NULL, vers_write, "Version block" };
+struct output_fragment pathtable_desc = {NULL, pathtab_size, generate_path_tables, pathtab_write, "Path table"};
+struct output_fragment dirtree_desc = {NULL, dirtree_size, NULL, dirtree_write, "Directory tree" };
+struct output_fragment dirtree_clean = {NULL, dirtree_fixup, dirtree_dump, dirtree_cleanup, "Directory tree cleanup" };
+struct output_fragment extension_desc = {NULL, ext_size, NULL, exten_write, "Extension record" };
+struct output_fragment files_desc = {NULL, NULL, file_gen, file_write, "The File(s)"};
+struct output_fragment interpad_desc = {NULL, interpad_size, NULL, interpad_write, "Intermediate Padblock"};
+struct output_fragment endpad_desc = {NULL, endpad_size, NULL, endpad_write, "Ending Padblock"};