diff options
Diffstat (limited to 'genisoimage/write.c')
-rw-r--r-- | genisoimage/write.c | 2840 |
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"}; |