/* * 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 #include "genisoimage.h" #include #include #ifdef SORTING #include "match.h" #endif /* SORTING */ #include #include #ifdef DVD_VIDEO #include "dvd_reader.h" #include "dvd_file.h" #include "ifo_read.h" #endif #ifdef APPLE_HYB #include #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"};