/* * 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. * */ /* @(#)tree.c 1.82 04/06/12 joerg */ /* * File tree.c - scan directory tree and build memory structures for iso9660 * filesystem * * Written by Eric Youngdale (1993). * * Copyright 1993 Yggdrasil Computing, Incorporated * Copyright (c) 1999,2000-2004 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. */ /* ADD_FILES changes made by Ross Biro biro@yggdrasil.com 2/23/95 */ /* APPLE_HYB James Pearson j.pearson@ge.ucl.ac.uk 23/2/2000 */ #include #include "genisoimage.h" #include "match.h" #include "udf.h" #include "exclude.h" #include #include #include #include #include extern int allow_limited_size; #ifdef VMS #include #include #include "vms.h" #endif /* * Autoconf should be able to figure this one out for us and let us know * whether the system has memmove or not. */ #ifndef HAVE_MEMMOVE #define memmove(d, s, n) bcopy((s), (d), (n)) #endif static Uchar symlink_buff[PATH_MAX+1]; static char *filetype(int t); static char *rstr(char *s1, char *s2); static void stat_fix(struct stat * st); int stat_filter(char *path, struct stat *st); int lstat_filter(char *path, struct stat *st); static int sort_n_finish(struct directory *this_dir); static void generate_reloc_directory(void); static void attach_dot_entries(struct directory *dirnode, struct stat *dir_stat, struct stat *parent_stat); static void update_nlink(struct directory_entry *s_entry, int value); static void increment_nlink(struct directory_entry *s_entry); char *find_rr_attribute(unsigned char *pnt, int len, char *attr_type); void finish_cl_pl_entries(void); int scan_directory_tree(struct directory *this_dir, char *path, struct directory_entry *de); #ifdef APPLE_HYB int insert_file_entry(struct directory *this_dir, char *whole_path, char *short_name, int have_rsrc); #else int insert_file_entry(struct directory *this_dir, char *whole_path, char *short_name); #endif void generate_iso9660_directories(struct directory *node, FILE *outfile); struct directory *find_or_create_directory(struct directory *parent, const char *path, struct directory_entry *de, int flag, struct stat* stat_template); static void delete_directory(struct directory *parent, struct directory *child); int sort_tree(struct directory *node); void dump_tree(struct directory *node); void update_nlink_field(struct directory *node); struct directory_entry *search_tree_file(struct directory *node, char *filename); void init_fstatbuf(void); extern int verbose; struct stat fstatbuf; /* We use this for the artificial */ /* entries we create */ struct stat root_statbuf; /* Stat buffer for root directory */ struct directory *reloc_dir; static char * filetype(int t) { static char unkn[32]; if (S_ISFIFO(t)) /* 1 */ return ("fifo"); if (S_ISCHR(t)) /* 2 */ return ("chr"); if (S_ISMPC(t)) /* 3 */ return ("multiplexed chr"); if (S_ISDIR(t)) /* 4 */ return ("dir"); if (S_ISNAM(t)) /* 5 */ return ("named file"); if (S_ISBLK(t)) /* 6 */ return ("blk"); if (S_ISMPB(t)) /* 7 */ return ("multiplexed blk"); if (S_ISREG(t)) /* 8 */ return ("regular file"); if (S_ISCNT(t)) /* 9 */ return ("contiguous file"); if (S_ISLNK(t)) /* 10 */ return ("symlink"); if (S_ISSHAD(t)) /* 11 */ return ("Solaris shadow inode"); if (S_ISSOCK(t)) /* 12 */ return ("socket"); if (S_ISDOOR(t)) /* 13 */ return ("door"); if (S_ISWHT(t)) /* 14 */ return ("whiteout"); if (S_ISEVC(t)) /* 15 */ return ("event count"); /* * Needs to be last in case somebody makes this * a supported file type. */ if ((t & S_IFMT) == 0) /* 0 (unallocated) */ return ("unallocated"); sprintf(unkn, "octal '%o'", t & S_IFMT); return (unkn); } /* * Check if s1 ends in strings s2 */ static char * rstr(char *s1, char *s2) { int l1; int l2; l1 = strlen(s1); l2 = strlen(s2); if (l2 > l1) return ((char *) NULL); if (strcmp(&s1[l1 - l2], s2) == 0) return (&s1[l1 - l2]); return ((char *) NULL); } static void stat_fix(struct stat *st) { int adjust_modes = 0; if (S_ISREG(st->st_mode)) adjust_modes = rationalize_filemode; else if (S_ISDIR(st->st_mode)) adjust_modes = rationalize_dirmode; else adjust_modes = (rationalize_filemode || rationalize_dirmode); /* * If rationalizing, override the uid and gid, since the * originals will only be useful on the author's system. */ if (rationalize_uid) st->st_uid = uid_to_use; if (rationalize_gid) st->st_gid = gid_to_use; if (adjust_modes) { if (S_ISREG(st->st_mode) && (filemode_to_use != 0)) { st->st_mode = filemode_to_use | S_IFREG; } else if (S_ISDIR(st->st_mode) && (dirmode_to_use != 0)) { st->st_mode = dirmode_to_use | S_IFDIR; } else { /* * Make sure the file modes make sense. Turn * on all read bits. Turn on all exec/search * bits if any exec/search bit is set. Turn * off all write bits, and all special mode * bits (on a r/o fs lock bits are useless, * and with uid+gid 0 don't want set-id bits, * either). */ st->st_mode |= 0444; #if !defined(_WIN32) && !defined(__DJGPP__) /* make all file "executable" */ if (st->st_mode & 0111) #endif st->st_mode |= 0111; st->st_mode &= ~07222; } } } int stat_filter(char *path, struct stat *st) { int result = stat(path, st); if (result >= 0 && rationalize) stat_fix(st); return (result); } int lstat_filter(char *path, struct stat *st) { int result = lstat(path, st); if (result >= 0 && rationalize) stat_fix(st); return (result); } static int sort_n_finish(struct directory *this_dir) { struct directory_entry *s_entry; struct directory_entry *s_entry1; struct directory_entry *table; int count; int d1; int d2; int d3; register int new_reclen; char *c; int status = 0; int tablesize = 0; char newname[MAX_ISONAME+1]; char rootname[MAX_ISONAME+1]; char extname[MAX_ISONAME+1]; /* * Here we can take the opportunity to toss duplicate entries from the * directory. */ /* ignore if it's hidden */ if (this_dir->dir_flags & INHIBIT_ISO9660_ENTRY) { return (0); } table = NULL; init_fstatbuf(); /* * If we had artificially created this directory, then we might be * missing the required '.' entries. Create these now if we need * them. */ if ((this_dir->dir_flags & (DIR_HAS_DOT | DIR_HAS_DOTDOT)) != (DIR_HAS_DOT | DIR_HAS_DOTDOT)) { fstatbuf.st_mode = new_dir_mode | S_IFDIR; fstatbuf.st_nlink = 2; attach_dot_entries(this_dir, &fstatbuf, &fstatbuf); } flush_file_hash(); s_entry = this_dir->contents; while (s_entry) { /* ignore if it's hidden */ if (s_entry->de_flags & INHIBIT_ISO9660_ENTRY) { s_entry = s_entry->next; continue; } /* First assume no conflict, and handle this case */ if (!(s_entry1 = find_file_hash(s_entry->isorec.name))) { add_file_hash(s_entry); s_entry = s_entry->next; continue; } #ifdef APPLE_HYB /* * if the pair are associated, then skip (as they have the * same name!) */ if (apple_both && s_entry1->assoc && s_entry1->assoc == s_entry) { s_entry = s_entry->next; continue; } #endif /* APPLE_HYB */ if (s_entry1 == s_entry) { #ifdef USE_LIBSCHILY comerrno(EX_BAD, "Fatal goof, file '%s' already in hash table.\n", s_entry->isorec.name); #else fprintf(stderr, "Fatal goof, file '%s' already in hash table.\n", s_entry->isorec.name); exit(1); #endif } /* * OK, handle the conflicts. Try substitute names until we * come up with a winner */ strcpy(rootname, s_entry->isorec.name); /* * Strip off the non-significant part of the name so that we * are left with a sensible root filename. If we don't find * a '.', then try a ';'. */ c = strchr(rootname, '.'); /* * In case we ever allow more than on dot, only modify the * section past the last dot if the file name starts with a * dot. */ if (c != NULL && c == rootname && c != strrchr(rootname, '.')) { c = strrchr(rootname, '.'); } extname[0] = '\0'; /* In case we have no ext. */ if (c) { strcpy(extname, c); *c = 0; /* Cut off complete ext. */ } else { /* * Could not find any '.'. */ c = strchr(rootname, ';'); if (c) { *c = 0; /* Cut off version number */ } } c = strchr(extname, ';'); if (c) { *c = 0; /* Cut off version number */ } d1 = strlen(rootname); if (full_iso9660_filenames || iso9660_level > 1) { d2 = strlen(extname); /* * 31/37 chars minus the 3 characters we are * appending below to create unique filenames. */ if ((d1 + d2) > (iso9660_namelen - 3)) rootname[iso9660_namelen - 3 - d2] = 0; } else { if (d1 > 5) rootname[5] = 0; } new_reclen = strlen(rootname); sprintf(newname, "%s000%s%s", rootname, extname, ((s_entry->isorec.flags[0] & ISO_DIRECTORY) || omit_version_number ? "" : ";1")); for (d1 = 0; d1 < 36; d1++) { for (d2 = 0; d2 < 36; d2++) { for (d3 = 0; d3 < 36; d3++) { newname[new_reclen + 0] = (d1 <= 9 ? '0' + d1 : 'A' + d1 - 10); newname[new_reclen + 1] = (d2 <= 9 ? '0' + d2 : 'A' + d2 - 10); newname[new_reclen + 2] = (d3 <= 9 ? '0' + d3 : 'A' + d3 - 10); if (debug) fprintf(stderr, "NEW name '%s'\n", newname); #ifdef VMS /* Sigh. VAXCRTL seems to be broken here */ { int ijk = 0; while (newname[ijk]) { if (newname[ijk] == ' ') newname[ijk] = '0'; ijk++; } } #endif if (!find_file_hash(newname)) goto got_valid_name; } } } /* If we fell off the bottom here, we were in real trouble. */ #ifdef USE_LIBSCHILY comerrno(EX_BAD, "Unable to generate unique name for file %s\n", s_entry->name); #else fprintf(stderr, "Unable to generate unique name for file %s\n", s_entry->name); exit(1); #endif got_valid_name: /* * OK, now we have a good replacement name. Now decide which * one of these two beasts should get the name changed */ if (s_entry->priority < s_entry1->priority) { if (verbose > 0) { fprintf(stderr, "Using %s for %s%s%s (%s)\n", newname, this_dir->whole_name, SPATH_SEPARATOR, s_entry->name, s_entry1->name); } s_entry->isorec.name_len[0] = strlen(newname); new_reclen = offsetof(struct iso_directory_record, name[0]) + strlen(newname); if (use_XA || use_RockRidge) { if (new_reclen & 1) new_reclen++; /* Pad to an even byte */ new_reclen += s_entry->rr_attr_size; } if (new_reclen & 1) new_reclen++; /* Pad to an even byte */ s_entry->isorec.length[0] = new_reclen; strcpy(s_entry->isorec.name, newname); #ifdef APPLE_HYB /* has resource fork - needs new name */ if (apple_both && s_entry->assoc) { struct directory_entry *s_entry2 = s_entry->assoc; /* * resource fork name *should* be the same as * the data fork */ s_entry2->isorec.name_len[0] = s_entry->isorec.name_len[0]; strcpy(s_entry2->isorec.name, s_entry->isorec.name); s_entry2->isorec.length[0] = new_reclen; } #endif /* APPLE_HYB */ } else { delete_file_hash(s_entry1); if (verbose > 0) { fprintf(stderr, "Using %s for %s%s%s (%s)\n", newname, this_dir->whole_name, SPATH_SEPARATOR, s_entry1->name, s_entry->name); } s_entry1->isorec.name_len[0] = strlen(newname); new_reclen = offsetof(struct iso_directory_record, name[0]) + strlen(newname); if (use_XA || use_RockRidge) { if (new_reclen & 1) new_reclen++; /* Pad to an even byte */ new_reclen += s_entry1->rr_attr_size; } if (new_reclen & 1) new_reclen++; /* Pad to an even byte */ s_entry1->isorec.length[0] = new_reclen; strcpy(s_entry1->isorec.name, newname); add_file_hash(s_entry1); #ifdef APPLE_HYB /* has resource fork - needs new name */ if (apple_both && s_entry1->assoc) { struct directory_entry *s_entry2 = s_entry1->assoc; /* * resource fork name *should* be the same as * the data fork */ s_entry2->isorec.name_len[0] = s_entry1->isorec.name_len[0]; strcpy(s_entry2->isorec.name, s_entry1->isorec.name); s_entry2->isorec.length[0] = new_reclen; } #endif /* APPLE_HYB */ } add_file_hash(s_entry); s_entry = s_entry->next; } if (generate_tables && !find_file_hash(trans_tbl) && (reloc_dir != this_dir) && (this_dir->extent == 0)) { /* First we need to figure out how big this table is */ for (s_entry = this_dir->contents; s_entry; s_entry = s_entry->next) { if (strcmp(s_entry->name, ".") == 0 || strcmp(s_entry->name, "..") == 0) continue; #ifdef APPLE_HYB /* skip table entry for the resource fork */ if (apple_both && (s_entry->isorec.flags[0] & ISO_ASSOCIATED)) continue; #endif /* APPLE_HYB */ if (s_entry->de_flags & INHIBIT_ISO9660_ENTRY) continue; if (s_entry->table) { /* * Max namelen, a space before and a space * after the iso filename. */ tablesize += MAX_ISONAME + 2 + strlen(s_entry->table); } } } if (tablesize > 0) { table = (struct directory_entry *) e_malloc(sizeof (struct directory_entry)); memset(table, 0, sizeof (struct directory_entry)); table->table = NULL; table->next = this_dir->contents; this_dir->contents = table; table->filedir = root; table->isorec.flags[0] = ISO_FILE; table->priority = 32768; iso9660_date(table->isorec.date, fstatbuf.st_mtime); table->inode = TABLE_INODE; table->dev = (dev_t) UNCACHED_DEVICE; set_723(table->isorec.volume_sequence_number, volume_sequence_number); set_733((char *) table->isorec.size, tablesize); table->realsize = tablesize; table->size = tablesize; table->filedir = this_dir; if (jhide_trans_tbl) table->de_flags |= INHIBIT_JOLIET_ENTRY; /* table->name = strdup("");*/ table->name = strdup(trans_tbl); /* * We use sprintf() to create the strings, for this reason * we need to add one byte for the null character at the * end of the string even though we don't use it. */ table->table = (char *) e_malloc(ISO_ROUND_UP(tablesize)+1); memset(table->table, 0, ISO_ROUND_UP(tablesize)+1); iso9660_file_length(trans_tbl, table, 0); if (use_XA || use_RockRidge) { fstatbuf.st_mode = 0444 | S_IFREG; fstatbuf.st_nlink = 1; generate_xa_rr_attributes("", trans_tbl, table, &fstatbuf, &fstatbuf, 0); } } /* * We have now chosen the 8.3 names and we should now know the length * of every entry in the directory. */ for (s_entry = this_dir->contents; s_entry; s_entry = s_entry->next) { /* skip if it's hidden */ if (s_entry->de_flags & INHIBIT_ISO9660_ENTRY) { continue; } new_reclen = strlen(s_entry->isorec.name); /* First update the path table sizes for directories. */ if (s_entry->isorec.flags[0] & ISO_DIRECTORY) { if (strcmp(s_entry->name, ".") != 0 && strcmp(s_entry->name, "..") != 0) { path_table_size += new_reclen + offsetof(struct iso_path_table, name[0]); if (new_reclen & 1) path_table_size++; } else { new_reclen = 1; if (this_dir == root && strlen(s_entry->name) == 1) { path_table_size += new_reclen + offsetof(struct iso_path_table, name[0]); } } } if (path_table_size & 1) path_table_size++; /* For odd lengths we pad */ s_entry->isorec.name_len[0] = new_reclen; new_reclen += offsetof(struct iso_directory_record, name[0]); if (new_reclen & 1) new_reclen++; new_reclen += s_entry->rr_attr_size; if (new_reclen & 1) new_reclen++; if (new_reclen > 0xff) { #ifdef USE_LIBSCHILY comerrno(EX_BAD, "Fatal error - RR overflow (reclen %d) for file %s\n", new_reclen, s_entry->name); #else fprintf(stderr, "Fatal error - RR overflow (reclen %d) for file %s\n", new_reclen, s_entry->name); exit(1); #endif } s_entry->isorec.length[0] = new_reclen; } status = sort_directory(&this_dir->contents, (reloc_dir == this_dir)); if (status > 0) { fprintf(stderr, "Unable to sort directory %s\n", this_dir->whole_name); if(merge_warn_msg) fprintf(stderr, merge_warn_msg); exit(1); } /* * If we are filling out a TRANS.TBL, generate the entries that will * go in the thing. */ if (table) { count = 0; for (s_entry = this_dir->contents; s_entry; s_entry = s_entry->next) { if (s_entry == table) continue; if (!s_entry->table) continue; if (strcmp(s_entry->name, ".") == 0 || strcmp(s_entry->name, "..") == 0) continue; #ifdef APPLE_HYB /* skip table entry for the resource fork */ if (apple_both && (s_entry->isorec.flags[0] & ISO_ASSOCIATED)) continue; #endif /* APPLE_HYB */ if (s_entry->de_flags & INHIBIT_ISO9660_ENTRY) continue; /* * Warning: we cannot use the return value of sprintf * because old BSD based sprintf() implementations * will return a pointer to the result instead of a * count. * Old mkiofs introduced a space after the iso * filename to make parsing TRANS.TBL easier. */ sprintf(table->table + count, "%c %-*s%s", s_entry->table[0], MAX_ISONAME + 1, s_entry->isorec.name, s_entry->table + 1); count += strlen(table->table + count); free(s_entry->table); /* * for a memory file, set s_entry->table to the * correct data - which is stored in * s_entry->whole_name */ if (s_entry->de_flags & MEMORY_FILE) { s_entry->table = s_entry->whole_name; s_entry->whole_name = NULL; } else { s_entry->table = NULL; } } if (count != tablesize) { #ifdef USE_LIBSCHILY comerrno(EX_BAD, "Translation table size mismatch %d %d\n", count, tablesize); #else fprintf(stderr, "Translation table size mismatch %d %d\n", count, tablesize); exit(1); #endif } } /* * Now go through the directory and figure out how large this one will * be. Do not split a directory entry across a sector boundary */ s_entry = this_dir->contents; this_dir->ce_bytes = 0; while (s_entry) { /* skip if it's hidden */ if (s_entry->de_flags & INHIBIT_ISO9660_ENTRY) { s_entry = s_entry->next; continue; } new_reclen = s_entry->isorec.length[0]; if ((this_dir->size & (SECTOR_SIZE - 1)) + new_reclen >= SECTOR_SIZE) this_dir->size = (this_dir->size + (SECTOR_SIZE - 1)) & ~(SECTOR_SIZE - 1); this_dir->size += new_reclen; /* See if continuation entries were used on disc */ if (use_RockRidge && s_entry->rr_attr_size != s_entry->total_rr_attr_size) { unsigned char *pnt; int len; int nbytes; pnt = s_entry->rr_attributes; len = s_entry->total_rr_attr_size; pnt = parse_xa(pnt, &len, 0); /* pnt = parse_xa(pnt, &len, s_entry);*/ /* * We make sure that each continuation entry record is * not split across sectors, but each file could in * theory have more than one CE, so we scan through * and figure out what we need. */ while (len > 3) { if (pnt[0] == 'C' && pnt[1] == 'E') { nbytes = get_733((char *) pnt + 20); if ((this_dir->ce_bytes & (SECTOR_SIZE - 1)) + nbytes >= SECTOR_SIZE) this_dir->ce_bytes = ISO_ROUND_UP(this_dir->ce_bytes); /* * Now store the block in the * ce buffer */ this_dir->ce_bytes += nbytes; if (this_dir->ce_bytes & 1) this_dir->ce_bytes++; } len -= pnt[2]; pnt += pnt[2]; } } s_entry = s_entry->next; } return (status); } static void generate_reloc_directory() { time_t current_time; struct directory_entry *s_entry; /* Create an entry for our internal tree */ time(¤t_time); reloc_dir = (struct directory *) e_malloc(sizeof (struct directory)); memset(reloc_dir, 0, sizeof (struct directory)); reloc_dir->parent = root; reloc_dir->next = root->subdir; root->subdir = reloc_dir; reloc_dir->depth = 1; if (hide_rr_moved) { reloc_dir->whole_name = strdup("./.rr_moved"); reloc_dir->de_name = strdup(".rr_moved"); } else { reloc_dir->whole_name = strdup("./rr_moved"); reloc_dir->de_name = strdup("rr_moved"); } reloc_dir->extent = 0; /* Now create an actual directory entry */ s_entry = (struct directory_entry *) e_malloc(sizeof (struct directory_entry)); memset(s_entry, 0, sizeof (struct directory_entry)); s_entry->next = root->contents; reloc_dir->self = s_entry; /* The rr_moved entry will not appear in the Joliet tree. */ reloc_dir->dir_flags |= INHIBIT_JOLIET_ENTRY; s_entry->de_flags |= INHIBIT_JOLIET_ENTRY; /* Hiding RR_MOVED seems not to be possible..... */ #ifdef HIDE_RR reloc_dir->dir_flags |= INHIBIT_ISO9660_ENTRY; s_entry->de_flags |= INHIBIT_ISO9660_ENTRY; #endif root->contents = s_entry; root->contents->name = strdup(reloc_dir->de_name); root->contents->filedir = root; root->contents->isorec.flags[0] = ISO_DIRECTORY; root->contents->priority = 32768; iso9660_date(root->contents->isorec.date, current_time); root->contents->inode = UNCACHED_INODE; root->contents->dev = (dev_t) UNCACHED_DEVICE; set_723(root->contents->isorec.volume_sequence_number, volume_sequence_number); iso9660_file_length(reloc_dir->de_name, root->contents, 1); init_fstatbuf(); if (use_XA || use_RockRidge) { fstatbuf.st_mode = 0555 | S_IFDIR; fstatbuf.st_nlink = 2; generate_xa_rr_attributes("", hide_rr_moved ? ".rr_moved" : "rr_moved", s_entry, &fstatbuf, &fstatbuf, 0); }; /* Now create the . and .. entries in rr_moved */ /* Now create an actual directory entry */ memset(&root_statbuf, 0x0, sizeof(struct stat)); /* be sure */ attach_dot_entries(reloc_dir, &fstatbuf, &root_statbuf); } /* * Function: attach_dot_entries * * Purpose: Create . and .. entries for a new directory. * * Arguments: dir_stat contains the ownership/permission information * for dirnode, and parent_stat contains * ownership/permission information for its parent * * * Notes: Only used for artificial directories that * we are creating. */ static void attach_dot_entries(struct directory *dirnode, struct stat *dir_stat, struct stat *parent_stat) { struct directory_entry *s_entry; struct directory_entry *orig_contents; int deep_flag = 0; init_fstatbuf(); orig_contents = dirnode->contents; if ((dirnode->dir_flags & DIR_HAS_DOTDOT) == 0) { s_entry = (struct directory_entry *) e_malloc(sizeof (struct directory_entry)); memcpy(s_entry, dirnode->self, sizeof (struct directory_entry)); #ifdef APPLE_HYB if (dirnode->self->hfs_ent) { s_entry->hfs_ent = (hfsdirent *) e_malloc(sizeof (hfsdirent)); memcpy(s_entry->hfs_ent, dirnode->self->hfs_ent, sizeof (hfsdirent)); } #endif s_entry->name = strdup(".."); s_entry->whole_name = NULL; s_entry->isorec.name_len[0] = 1; s_entry->isorec.flags[0] = ISO_DIRECTORY; iso9660_file_length("..", s_entry, 1); iso9660_date(s_entry->isorec.date, fstatbuf.st_mtime); set_723(s_entry->isorec.volume_sequence_number, volume_sequence_number); set_733(s_entry->isorec.size, SECTOR_SIZE); s_entry->realsize = SECTOR_SIZE; memset(s_entry->isorec.extent, 0, 8); s_entry->filedir = dirnode->parent; dirnode->contents = s_entry; dirnode->contents->next = orig_contents; orig_contents = s_entry; if (use_XA || use_RockRidge) { generate_xa_rr_attributes("", "..", s_entry, parent_stat, parent_stat, 0); } dirnode->dir_flags |= DIR_HAS_DOTDOT; } if ((dirnode->dir_flags & DIR_HAS_DOT) == 0) { s_entry = (struct directory_entry *) e_malloc(sizeof (struct directory_entry)); memcpy(s_entry, dirnode->self, sizeof (struct directory_entry)); #ifdef APPLE_HYB if (dirnode->self->hfs_ent) { s_entry->hfs_ent = (hfsdirent *) e_malloc(sizeof (hfsdirent)); memcpy(s_entry->hfs_ent, dirnode->self->hfs_ent, sizeof (hfsdirent)); } #endif s_entry->name = strdup("."); s_entry->whole_name = NULL; s_entry->isorec.name_len[0] = 1; s_entry->isorec.flags[0] = ISO_DIRECTORY; iso9660_file_length(".", s_entry, 1); iso9660_date(s_entry->isorec.date, fstatbuf.st_mtime); set_723(s_entry->isorec.volume_sequence_number, volume_sequence_number); set_733(s_entry->isorec.size, SECTOR_SIZE); s_entry->realsize=SECTOR_SIZE; memset(s_entry->isorec.extent, 0, 8); s_entry->filedir = dirnode; dirnode->contents = s_entry; dirnode->contents->next = orig_contents; if (use_XA || use_RockRidge) { if (dirnode == root) { deep_flag |= NEED_CE | NEED_SP; /* For extension record */ } generate_xa_rr_attributes("", ".", s_entry, dir_stat, dir_stat, deep_flag); } dirnode->dir_flags |= DIR_HAS_DOT; } } static void update_nlink(struct directory_entry *s_entry, int value) { unsigned char *pnt; int len; pnt = s_entry->rr_attributes; len = s_entry->total_rr_attr_size; pnt = parse_xa(pnt, &len, 0); while (len >= 4) { if (pnt[3] != 1 && pnt[3] != 2) { #ifdef USE_LIBSCHILY errmsgno(EX_BAD, "**BAD RRVERSION (%d) for %c%c\n", pnt[3], pnt[0], pnt[1]); #else fprintf(stderr, "**BAD RRVERSION (%d) for %c%c\n", pnt[3], pnt[0], pnt[1]); #endif } if (pnt[0] == 'P' && pnt[1] == 'X') { set_733((char *) pnt + 12, value); break; } len -= pnt[2]; pnt += pnt[2]; } } static void increment_nlink(struct directory_entry *s_entry) { unsigned char *pnt; int len, nlink; pnt = s_entry->rr_attributes; len = s_entry->total_rr_attr_size; pnt = parse_xa(pnt, &len, 0); while (len >= 4) { if (pnt[3] != 1 && pnt[3] != 2) { #ifdef USE_LIBSCHILY errmsgno(EX_BAD, "**BAD RRVERSION (%d) for %c%c\n", pnt[3], pnt[0], pnt[1]); #else fprintf(stderr, "**BAD RRVERSION (%d) for %c%c\n", pnt[3], pnt[0], pnt[1]); #endif } if (pnt[0] == 'P' && pnt[1] == 'X') { nlink = get_733((char *) pnt + 12); set_733((char *) pnt + 12, nlink + 1); break; } len -= pnt[2]; pnt += pnt[2]; } } char * find_rr_attribute(unsigned char *pnt, int len, char *attr_type) { pnt = parse_xa(pnt, &len, 0); while (len >= 4) { if (pnt[3] != 1 && pnt[3] != 2) { #ifdef USE_LIBSCHILY errmsgno(EX_BAD, "**BAD RRVERSION (%d) for %c%c\n", pnt[3], pnt[0], pnt[1]); #else fprintf(stderr, "**BAD RRVERSION (%d) for %c%c\n", pnt[3], pnt[0], pnt[1]); #endif } if (strncmp((char *) pnt, attr_type, 2) == 0) return ((char *) pnt); else if (strncmp((char *) pnt, "ST", 2) == 0) return (NULL); len -= pnt[2]; pnt += pnt[2]; } return (NULL); } void finish_cl_pl_entries() { struct directory_entry *s_entry; struct directory_entry *s_entry1; struct directory *d_entry; /* if the reloc_dir is hidden (empty), then return */ if (reloc_dir->dir_flags & INHIBIT_ISO9660_ENTRY) return; s_entry = reloc_dir->contents; s_entry = s_entry->next->next; /* Skip past . and .. */ for (; s_entry; s_entry = s_entry->next) { /* skip if it's hidden */ if (s_entry->de_flags & INHIBIT_ISO9660_ENTRY) { continue; } d_entry = reloc_dir->subdir; while (d_entry) { if (d_entry->self == s_entry) break; d_entry = d_entry->next; }; if (!d_entry) { #ifdef USE_LIBSCHILY comerrno(EX_BAD, "Unable to locate directory parent\n"); #else fprintf(stderr, "Unable to locate directory parent\n"); exit(1); #endif }; if (s_entry->filedir != NULL && s_entry->parent_rec != NULL) { char *rr_attr; /* * First fix the PL pointer in the directory in the * rr_reloc dir */ s_entry1 = d_entry->contents->next; /* set_733((char *) s_entry1->rr_attributes +*/ /* s_entry1->total_rr_attr_size - 8,*/ /* s_entry->filedir->extent); */ /* * The line above won't work when entry was read from * the previous session, because if total_rr_attr_size * was odd when recording previous session, now we have * total_rr_attr_size off by 1 due to padding. * * So, just search for the attributes by name */ rr_attr = find_rr_attribute(s_entry1->rr_attributes, s_entry1->total_rr_attr_size, "PL"); if (rr_attr != NULL) set_733(rr_attr + 4, s_entry->filedir->extent); /* Now fix the CL pointer */ s_entry1 = s_entry->parent_rec; /* set_733((char *) s_entry1->rr_attributes +*/ /* s_entry1->total_rr_attr_size - 8, d_entry->extent); */ rr_attr = find_rr_attribute(s_entry1->rr_attributes, s_entry1->total_rr_attr_size, "CL"); if (rr_attr != NULL) set_733(rr_attr + 4, d_entry->extent); } s_entry->filedir = reloc_dir; /* Now we can fix this */ } /* * Next we need to modify the NLINK terms in the assorted root * directory records to account for the presence of the RR_MOVED * directory */ increment_nlink(root->self); increment_nlink(root->self->next); d_entry = root->subdir; while (d_entry) { increment_nlink(d_entry->contents->next); d_entry = d_entry->next; }; finish_cl_pl_for_prev_session(); } /* * Function: scan_directory_tree * * Purpose: Walk through a directory on the local machine * filter those things we don't want to include * and build our representation of a dir. * * Notes: */ int scan_directory_tree(struct directory *this_dir, char *path, struct directory_entry *de) { DIR *current_dir; char whole_path[PATH_MAX]; struct dirent *d_entry; struct directory *parent; int dflag; char *old_path; if (verbose > 1) { fprintf(stderr, "Scanning %s\n", path); } /*#define check_needed*/ #ifdef check_needed /* * Trying to use this to avoid directory loops from hard links * or followed symlinks does not work. It would prevent us from * implementing merge directories. */ if (this_dir->dir_flags & DIR_WAS_SCANNED) { fprintf(stderr, "Already scanned directory %s\n", path); return (1); /* It's a directory */ } #endif this_dir->dir_flags |= DIR_WAS_SCANNED; errno = 0; /* Paranoia */ current_dir = opendir(path); d_entry = NULL; /* * Apparently NFS sometimes allows you to open the directory, but then * refuses to allow you to read the contents. Allow for this */ old_path = path; if (current_dir) { errno = 0; d_entry = readdir(current_dir); } if (!current_dir || !d_entry) { int ret = 1; #ifdef USE_LIBSCHILY errmsg("Unable to open directory %s\n", path); #else fprintf(stderr, "Unable to open directory %s\n", path); #endif if (errno == ENOTDIR) { /* Mark as not a directory */ de->isorec.flags[0] &= ~ISO_DIRECTORY; ret = 0; } if (current_dir) closedir(current_dir); return (ret); } #ifdef ABORT_DEEP_ISO_ONLY if ((this_dir->depth > RR_relocation_depth) && !use_RockRidge) { static BOOL did_hint = FALSE; errmsgno(EX_BAD, "Directories too deep for '%s' (%d) max is %d; ignored - continuing.\n", path, this_dir->depth, RR_relocation_depth); if (!did_hint) { did_hint = TRUE; errmsgno(EX_BAD, "To include the complete directory tree,\n"); errmsgno(EX_BAD, "use Rock Ridge extensions via -R or -r,\n"); errmsgno(EX_BAD, "or allow deep ISO9660 directory nesting via -D.\n"); } closedir(current_dir); return (1); } #endif parent = de->filedir; /* * Set up the struct for the current directory, and insert it into * the tree */ #ifdef VMS vms_path_fixup(path); #endif /* * if entry for this sub-directory is hidden, then hide this directory */ if (de->de_flags & INHIBIT_ISO9660_ENTRY) this_dir->dir_flags |= INHIBIT_ISO9660_ENTRY; if (de->de_flags & INHIBIT_JOLIET_ENTRY) this_dir->dir_flags |= INHIBIT_JOLIET_ENTRY; #ifdef SORTING /* * set any sort weighting from it's own directory entry - if a * directory is given a weighting, then all the contents will use * this as the default weighting */ this_dir->sort = de->sort; #endif /* SORTING */ /* * Now we scan the directory itself, and look at what is inside of it. */ dflag = 0; while (1 == 1) { /* * The first time through, skip this, since we already asked * for the first entry when we opened the directory. */ if (dflag) d_entry = readdir(current_dir); dflag++; if (!d_entry) break; /* OK, got a valid entry */ /* If we do not want all files, then pitch the backups. */ if (!all_files) { if (strchr(d_entry->d_name, '~') || strchr(d_entry->d_name, '#') || rstr(d_entry->d_name, ".bak")) { if (verbose > 0) { fprintf(stderr, "Ignoring file %s\n", d_entry->d_name); } continue; } } #ifdef APPLE_HYB if (apple_both) { /* * exclude certain HFS type files/directories for the * time being */ if (hfs_exclude(d_entry->d_name)) continue; } #endif /* APPLE_HYB */ if (strlen(path) + strlen(d_entry->d_name) + 2 > sizeof (whole_path)) { #ifdef USE_LIBSCHILY errmsgno(EX_BAD, "Path name %s/%s too long.\n", path, d_entry->d_name); comerrno(EX_BAD, "Overflow of stat buffer\n"); #else fprintf(stderr, "Path name %s/%s too long.\n", path, d_entry->d_name); fprintf(stderr, "Overflow of stat buffer\n"); exit(1); #endif }; /* Generate the complete ASCII path for this file */ strcpy(whole_path, path); #ifndef VMS if (whole_path[strlen(whole_path) - 1] != '/') strcat(whole_path, "/"); #endif strcat(whole_path, d_entry->d_name); /** Should we exclude this file ? */ if (matches(d_entry->d_name) || matches(whole_path)) { if (verbose > 1) { fprintf(stderr, "Excluded by match: %s\n", whole_path); } continue; } if (generate_tables && strcmp(d_entry->d_name, trans_tbl) == 0) { /* * Ignore this entry. We are going to be generating * new versions of these files, and we need to ignore * any originals that we might have found. */ if (verbose > 1) { fprintf(stderr, "Excluded: %s\n", whole_path); } continue; } /* * If we already have a '.' or a '..' entry, then don't insert * new ones. */ if (strcmp(d_entry->d_name, ".") == 0 && this_dir->dir_flags & DIR_HAS_DOT) { continue; } if (strcmp(d_entry->d_name, "..") == 0 && this_dir->dir_flags & DIR_HAS_DOTDOT) { continue; } #if 0 if (verbose > 1) fprintf(stderr, "%s\n", whole_path); #endif /* This actually adds the entry to the directory in question.*/ #ifdef APPLE_HYB insert_file_entry(this_dir, whole_path, d_entry->d_name, 0); #else insert_file_entry(this_dir, whole_path, d_entry->d_name); #endif /* APPLE_HYB */ } closedir(current_dir); #ifdef APPLE_HYB /* * if we cached the HFS info stuff for this directory, then delete it */ if (this_dir->hfs_info) { del_hfs_info(this_dir->hfs_info); this_dir->hfs_info = 0; } #endif /* APPLE_HYB */ return (1); } /* * Function: insert_file_entry * * Purpose: Insert one entry into our directory node. * * Note: * This function inserts a single entry into the directory. It * is assumed that all filtering and decision making regarding what * we want to include has already been made, so the purpose of this * is to insert one entry (file, link, dir, etc), into this directory. * Note that if the entry is a dir (or if we are following links, * and the thing it points to is a dir), then we will scan those * trees before we return. */ #ifdef APPLE_HYB int insert_file_entry(struct directory *this_dir, char *whole_path, char *short_name, int have_rsrc) #else int insert_file_entry(struct directory *this_dir, char *whole_path, char *short_name) #endif /* APPLE_HYB */ { struct stat statbuf, lstatbuf; struct directory_entry *s_entry, *s_entry1; int lstatus; int status; int deep_flag; int no_scandir = 0; #ifdef APPLE_HYB int x_hfs = 0; int htype = TYPE_NONE; #endif /* APPLE_HYB */ status = stat_filter(whole_path, &statbuf); lstatus = lstat_filter(whole_path, &lstatbuf); if ((status == -1) && (lstatus == -1)) { /* * This means that the file doesn't exist, or isn't accessible. * Sometimes this is because of NFS permissions problems. */ #ifdef USE_LIBSCHILY errmsg("Non-existent or inaccessible: %s\n", whole_path); #else fprintf(stderr, "Non-existent or inaccessible: %s\n", whole_path); #endif return (0); } if (this_dir == root && strcmp(short_name, ".") == 0) root_statbuf = statbuf; /* Save this for later on */ /* We do this to make sure that the root entries are consistent */ if (this_dir == root && strcmp(short_name, "..") == 0) { statbuf = root_statbuf; lstatbuf = root_statbuf; } if (S_ISLNK(lstatbuf.st_mode)) { /* * Here we decide how to handle the symbolic links. Here we * handle the general case - if we are not following links or * there is an error, then we must change something. If RR * is in use, it is easy, we let RR describe the file. If * not, then we punt the file. */ if ((status || !follow_links)) { if (use_RockRidge) { status = 0; statbuf.st_size = (off_t)0; STAT_INODE(statbuf) = UNCACHED_INODE; statbuf.st_dev = (dev_t) UNCACHED_DEVICE; statbuf.st_mode = (statbuf.st_mode & ~S_IFMT) | S_IFREG; } else { if (follow_links) { #ifdef USE_LIBSCHILY /* XXX errno may be wrong! */ errmsg("Unable to stat file %s - ignoring and continuing.\n", whole_path); #else fprintf(stderr, "Unable to stat file %s - ignoring and continuing.\n", whole_path); #endif } else { #ifdef USE_LIBSCHILY errmsgno(EX_BAD, "Symlink %s ignored - continuing.\n", whole_path); #else fprintf(stderr, "Symlink %s ignored - continuing.\n", whole_path); #endif return (0); /* Non Rock Ridge discs */ /* - ignore all symlinks */ } } } /* * Here we handle a different kind of case. Here we have a * symlink, but we want to follow symlinks. If we run across * a directory loop, then we need to pretend that we are not * following symlinks for this file. If this is the first * time we have seen this, then make this seem as if there was * no symlink there in the first place */ if (follow_links && S_ISDIR(statbuf.st_mode)) { if (strcmp(short_name, ".") && strcmp(short_name, "..")) { if (find_directory_hash(statbuf.st_dev, STAT_INODE(statbuf))) { if (!use_RockRidge) { fprintf(stderr, "Already cached directory seen (%s)\n", whole_path); return (0); } lstatbuf = statbuf; /* * XXX when this line was active, * XXX genisoimage did not include all * XXX files if it was called with '-f' * XXX (follow symlinks). * XXX Now scan_directory_tree() * XXX checks if the directory has * XXX already been scanned via the * XXX DIR_WAS_SCANNED flag. */ /* no_scandir = 1;*/ } else { lstatbuf = statbuf; add_directory_hash(statbuf.st_dev, STAT_INODE(statbuf)); } } } /* * For non-directories, we just copy the stat information over * so we correctly include this file. */ if (follow_links && !S_ISDIR(statbuf.st_mode)) { lstatbuf = statbuf; } } /* * Add directories to the cache so that we don't waste space even if * we are supposed to be following symlinks. */ if (follow_links && strcmp(short_name, ".") && strcmp(short_name, "..") && S_ISDIR(statbuf.st_mode)) { add_directory_hash(statbuf.st_dev, STAT_INODE(statbuf)); } #ifdef VMS if (!S_ISDIR(lstatbuf.st_mode) && (statbuf.st_fab_rfm != FAB$C_FIX && statbuf.st_fab_rfm != FAB$C_STMLF)) { fprintf(stderr, "Warning - file %s has an unsupported VMS record" " format (%d)\n", whole_path, statbuf.st_fab_rfm); } #endif if (S_ISREG(lstatbuf.st_mode) && (status = access(whole_path, R_OK))) { #ifdef USE_LIBSCHILY errmsg("File %s is not readable - ignoring\n", whole_path); #else fprintf(stderr, "File %s is not readable (errno = %d) - ignoring\n", whole_path, errno); #endif return (0); } /* print a warning but don't spam too much */ if (S_ISREG(lstatbuf.st_mode) && (lstatbuf.st_size >= (off_t)0xFFFFFFFF)) { static int udf_warned; if( !allow_limited_size || verbose>1) fprintf(stderr, "File %s is larger than 4GiB-1.\n", whole_path); if( !allow_limited_size) { fprintf(stderr, "-allow-limited-size was not specified. There is no way do represent this file size. Aborting.\n"); exit(1); } if(verbose>=1 && ! udf_warned ) { udf_warned++; fprintf(stderr, "This size can only be represented in the UDF filesystem.\n" "Make sure that your clients support and use it.\n" "ISO9660, Joliet, RockRidge, HFS will display incorrect size.\n"); } } /* * Add this so that we can detect directory loops with hard links. * If we are set up to follow symlinks, then we skip this checking. */ if (!follow_links && S_ISDIR(lstatbuf.st_mode) && strcmp(short_name, ".") && strcmp(short_name, "..")) { if (find_directory_hash(statbuf.st_dev, STAT_INODE(statbuf))) { #ifdef USE_LIBSCHILY /* comerrno(EX_BAD,*/ /* "Directory loop - fatal goof (%s %lx %lu).\n",*/ errmsgno(EX_BAD, "Warning: Directory loop (%s dev: %lx ino: %lu).\n", whole_path, (unsigned long) statbuf.st_dev, (unsigned long) STAT_INODE(statbuf)); #else /* fprintf(stderr,*/ /* "Directory loop - fatal goof (%s %lx %lu).\n",*/ fprintf(stderr, "Warning: Directory loop (%s dev: %lx ino: %lu).\n", whole_path, (unsigned long) statbuf.st_dev, (unsigned long) STAT_INODE(statbuf)); #endif } add_directory_hash(statbuf.st_dev, STAT_INODE(statbuf)); } if (!S_ISCHR(lstatbuf.st_mode) && !S_ISBLK(lstatbuf.st_mode) && !S_ISFIFO(lstatbuf.st_mode) && !S_ISSOCK(lstatbuf.st_mode) && !S_ISLNK(lstatbuf.st_mode) && !S_ISREG(lstatbuf.st_mode) && !S_ISDIR(lstatbuf.st_mode)) { if ( ! (this_dir == root && strcmp(short_name, "..") == 0)) { fprintf(stderr, "Unknown file type (%s) %s - ignoring and continuing.\n", filetype((int) lstatbuf.st_mode), whole_path); } return (0); } /* Who knows what trash this is - ignore and continue */ if (status) { #ifdef USE_LIBSCHILY errmsg("Unable to stat file %s - ignoring and continuing.\n", whole_path); #else fprintf(stderr, "Unable to stat file %s - ignoring and continuing.\n", whole_path); #endif return (0); } /* * Check to see if we have already seen this directory node. If so, * then we don't create a new entry for it, but we do want to recurse * beneath it and add any new files we do find. */ if (S_ISDIR(statbuf.st_mode)) { int dflag; for (s_entry = this_dir->contents; s_entry; s_entry = s_entry->next) { if (strcmp(s_entry->name, short_name) == 0) { break; } } if (s_entry != NULL && strcmp(short_name, ".") && strcmp(short_name, "..")) { struct directory *child; if ((s_entry->de_flags & RELOCATED_DIRECTORY) != 0) { for (s_entry = reloc_dir->contents; s_entry; s_entry = s_entry->next) { if (strcmp(s_entry->name, short_name) == 0) { break; } } child = find_or_create_directory(reloc_dir, whole_path, s_entry, 1, NULL); } else { child = find_or_create_directory(this_dir, whole_path, s_entry, 1, NULL); /* * If unable to scan directory, mark this as a * non-directory */ } /* if (no_scandir)*/ if (0) dflag = 1; else dflag = scan_directory_tree(child, whole_path, s_entry); if (!dflag) { lstatbuf.st_mode = (lstatbuf.st_mode & ~S_IFMT) | S_IFREG; } return (0); } } #ifdef APPLE_HYB /* Should we exclude this HFS file ? - only works with -hfs */ if (!have_rsrc && apple_hyb && strcmp(short_name, ".") && strcmp(short_name, "..")) { if ((x_hfs = (hfs_matches(short_name) || hfs_matches(whole_path))) == 1) { if (verbose > 1) { fprintf(stderr, "Hidden from HFS tree: %s\n", whole_path); } } } /* * check we are a file, using Apple extensions and have a .resource * part and not excluded */ if (S_ISREG(lstatbuf.st_mode) && !have_rsrc && apple_both && !x_hfs) { char rsrc_path[PATH_MAX]; /* rsrc fork filename */ /* construct the resource full path */ htype = get_hfs_rname(whole_path, short_name, rsrc_path); /* check we can read the resouce fork */ if (htype) { struct stat rstatbuf, rlstatbuf; /* some further checks on the file */ status = stat_filter(rsrc_path, &rstatbuf); lstatus = lstat_filter(rsrc_path, &rlstatbuf); /* if (!status && !lstatus && S_ISREG(rlstatbuf.st_mode)*/ /* && rlstatbuf.st_size > (off_t)0) { */ if (!status && !lstatus && S_ISREG(rstatbuf.st_mode) && rstatbuf.st_size > (off_t)0) { /* * have a resource file - insert it into the * current directory but flag that we have a * resource fork */ insert_file_entry(this_dir, rsrc_path, short_name, htype); } } } #endif /* APPLE_HYB */ s_entry = (struct directory_entry *) e_malloc(sizeof (struct directory_entry)); /* memset the whole struct, not just the isorec.extent part JCP */ memset(s_entry, 0, sizeof (struct directory_entry)); s_entry->next = this_dir->contents; /* memset(s_entry->isorec.extent, 0, 8); */ this_dir->contents = s_entry; deep_flag = 0; s_entry->table = NULL; s_entry->name = strdup(short_name); s_entry->whole_name = strdup(whole_path); s_entry->de_flags = 0; /* * If the current directory is hidden, then hide all it's members * otherwise check if this entry needs to be hidden as well */ if (this_dir->dir_flags & INHIBIT_ISO9660_ENTRY) { s_entry->de_flags |= INHIBIT_ISO9660_ENTRY; } else if (strcmp(short_name, ".") != 0 && strcmp(short_name, "..") != 0) { if (i_matches(short_name) || i_matches(whole_path)) { if (verbose > 1) { fprintf(stderr, "Hidden from ISO9660 tree: %s\n", whole_path); } s_entry->de_flags |= INHIBIT_ISO9660_ENTRY; } if (h_matches(short_name) || h_matches(whole_path)) { if (verbose > 1) { fprintf(stderr, "Hidden ISO9660 attribute: %s\n", whole_path); } s_entry->de_flags |= HIDDEN_FILE; } } if (this_dir != reloc_dir && this_dir->dir_flags & INHIBIT_JOLIET_ENTRY) { s_entry->de_flags |= INHIBIT_JOLIET_ENTRY; } else if (strcmp(short_name, ".") != 0 && strcmp(short_name, "..") != 0) { if (j_matches(short_name) || j_matches(whole_path)) { if (verbose > 1) { fprintf(stderr, "Hidden from Joliet tree: %s\n", whole_path); } s_entry->de_flags |= INHIBIT_JOLIET_ENTRY; } } #ifdef SORTING /* inherit any sort weight from parent directory */ s_entry->sort = this_dir->sort; #ifdef DVD_VIDEO /* * No use at all to do a sort if we don't make a dvd video/audio */ /* * Assign special weights to VIDEO_TS and AUDIO_TS files. * This can't be done with sort_matches for two reasons: * first, we need to match against the destination (DVD) * path rather than the source path, and second, there are * about 2400 different file names to check, each needing * a different priority, and adding that many patterns to * sort_matches would slow things to a crawl. */ if (dvd_video) { s_entry->sort = assign_dvd_weights(s_entry->name, this_dir, s_entry->sort); /* turn on sorting if necessary, regardless of cmd-line options */ if ((s_entry->sort != this_dir->sort) && do_sort == 0) do_sort++; } #endif /* see if this entry should have a new weighting */ if (do_sort && strcmp(short_name, ".") != 0 && strcmp(short_name, "..") != 0) { s_entry->sort = sort_matches(whole_path, s_entry->sort); } #endif /* SORTING */ s_entry->filedir = this_dir; s_entry->isorec.flags[0] = ISO_FILE; if (s_entry->de_flags & HIDDEN_FILE) s_entry->isorec.flags[0] |= ISO_EXISTENCE; s_entry->isorec.ext_attr_length[0] = 0; iso9660_date(s_entry->isorec.date, statbuf.st_mtime); s_entry->isorec.file_unit_size[0] = 0; s_entry->isorec.interleave[0] = 0; #ifdef APPLE_HYB if (apple_both && !x_hfs) { s_entry->hfs_ent = NULL; s_entry->assoc = NULL; s_entry->hfs_off = (off_t)0; s_entry->hfs_type = htype; if (have_rsrc) { /* associated (rsrc) file */ s_entry->isorec.flags[0] |= ISO_ASSOCIATED; /* set the type of HFS file */ s_entry->hfs_type = have_rsrc; /* * don't want the rsrc file to be included in any * Joliet tree */ s_entry->de_flags |= INHIBIT_JOLIET_ENTRY; } else if (s_entry->next) { /* * if previous entry is an associated file, * then "link" it to this file i.e. we have a * data/resource pair */ if (s_entry->next->isorec.flags[0] & ISO_ASSOCIATED) { s_entry->assoc = s_entry->next; /* share the same HFS parameters */ s_entry->hfs_ent = s_entry->next->hfs_ent; s_entry->hfs_type = s_entry->next->hfs_type; } } /* allocate HFS entry if required */ if (apple_both && strcmp(short_name, ".") && strcmp(short_name, "..")) { if (!s_entry->hfs_ent) { hfsdirent *hfs_ent; hfs_ent = (hfsdirent *) e_malloc(sizeof (hfsdirent)); /* fill in the defaults */ memset(hfs_ent, 0, sizeof (hfsdirent)); s_entry->hfs_ent = hfs_ent; } /* * the resource fork is processed first, but the * data fork's time info is used in preference * i.e. time info is set from the resource fork * initially, then it is set from the data fork */ if (have_rsrc) { /* set rsrc size */ s_entry->hfs_ent->u.file.rsize = lstatbuf.st_size; /* * this will be overwritten - but might as * well set it here ... */ s_entry->hfs_ent->crdate = lstatbuf.st_ctime; s_entry->hfs_ent->mddate = lstatbuf.st_mtime; } else { /* set data size */ s_entry->hfs_ent->u.file.dsize = lstatbuf.st_size; s_entry->hfs_ent->crdate = lstatbuf.st_ctime; s_entry->hfs_ent->mddate = lstatbuf.st_mtime; } } } #endif /* APPLE_HYB */ if (strcmp(short_name, ".") == 0) { this_dir->dir_flags |= DIR_HAS_DOT; } if (strcmp(short_name, "..") == 0) { this_dir->dir_flags |= DIR_HAS_DOTDOT; } if (this_dir->parent && this_dir->parent == reloc_dir && strcmp(short_name, "..") == 0) { s_entry->inode = UNCACHED_INODE; s_entry->dev = (dev_t) UNCACHED_DEVICE; deep_flag = NEED_PL; } else #ifdef APPLE_HYB if (have_rsrc) { /* don't want rsrc files to be cached */ s_entry->inode = UNCACHED_INODE; s_entry->dev = (dev_t) UNCACHED_DEVICE; } else #endif /* APPLE_HYB */ { s_entry->inode = STAT_INODE(statbuf); s_entry->dev = statbuf.st_dev; } set_723(s_entry->isorec.volume_sequence_number, volume_sequence_number); iso9660_file_length(short_name, s_entry, S_ISDIR(statbuf.st_mode)); s_entry->rr_attr_size = 0; s_entry->total_rr_attr_size = 0; s_entry->rr_attributes = NULL; /* Directories are assigned sizes later on */ if (!S_ISDIR(statbuf.st_mode)) { if (S_ISCHR(lstatbuf.st_mode) || S_ISBLK(lstatbuf.st_mode) || S_ISFIFO(lstatbuf.st_mode) || S_ISSOCK(lstatbuf.st_mode) || S_ISLNK(lstatbuf.st_mode)) { s_entry->size = (off_t)0; statbuf.st_size = (off_t)0; } else { s_entry->size = statbuf.st_size; } set_733((char *) s_entry->isorec.size, statbuf.st_size); s_entry->realsize = statbuf.st_size; } else { s_entry->isorec.flags[0] |= ISO_DIRECTORY; } #ifdef APPLE_HYB /* if the directory is HFS excluded, then we don't have an hfs_ent */ if (apple_both && s_entry->hfs_ent && (s_entry->isorec.flags[0] & ISO_DIRECTORY)) { /* get the Mac directory name */ get_hfs_dir(whole_path, short_name, s_entry); /* if required, set ISO directory name from HFS name */ if (use_mac_name) iso9660_file_length(s_entry->hfs_ent->name, s_entry, 1); } #endif /* APPLE_HYB */ if (strcmp(short_name, ".") != 0 && strcmp(short_name, "..") != 0 && S_ISDIR(statbuf.st_mode) && this_dir->depth > RR_relocation_depth) { struct directory *child; if (!reloc_dir) generate_reloc_directory(); /* * Replicate the entry for this directory. The old one will * stay where it is, and it will be neutered so that it no * longer looks like a directory. The new one will look like * a directory, and it will be put in the reloc_dir. */ s_entry1 = (struct directory_entry *) e_malloc(sizeof (struct directory_entry)); memcpy(s_entry1, s_entry, sizeof (struct directory_entry)); s_entry1->table = NULL; s_entry1->name = strdup(this_dir->contents->name); s_entry1->whole_name = strdup(this_dir->contents->whole_name); s_entry1->next = reloc_dir->contents; reloc_dir->contents = s_entry1; s_entry1->priority = 32768; s_entry1->parent_rec = this_dir->contents; set_723(s_entry1->isorec.volume_sequence_number, volume_sequence_number); deep_flag = NEED_RE; if (use_XA || use_RockRidge) { generate_xa_rr_attributes(whole_path, short_name, s_entry1, &statbuf, &lstatbuf, deep_flag); } deep_flag = 0; /* * We need to set this temporarily so that the parent to this * is correctly determined. */ s_entry1->filedir = reloc_dir; child = find_or_create_directory(reloc_dir, whole_path, s_entry1, 0, NULL); /* if (!no_scandir)*/ if (!0) scan_directory_tree(child, whole_path, s_entry1); s_entry1->filedir = this_dir; statbuf.st_size = (off_t)0; statbuf.st_mode &= 0777; set_733((char *) s_entry->isorec.size, 0); s_entry->realsize=0; s_entry->size = 0; s_entry->isorec.flags[0] = ISO_FILE; s_entry->inode = UNCACHED_INODE; s_entry->de_flags |= RELOCATED_DIRECTORY; deep_flag = NEED_CL; } if (generate_tables && strcmp(s_entry->name, ".") != 0 && strcmp(s_entry->name, "..") != 0) { char buffer[SECTOR_SIZE]; int nchar; switch (lstatbuf.st_mode & S_IFMT) { case S_IFDIR: sprintf(buffer, "D\t%s\n", s_entry->name); break; /* * extra for WIN32 - if it doesn't have the major/minor defined, then * S_IFBLK and S_IFCHR type files are unlikely to exist anyway ... * code similar to that in rock.c */ #if 0 /* * Use the device handling code from */ #ifndef major #define major(dev) (sizeof (dev_t) <= 2 ? ((dev) >> 8) : \ (sizeof (dev_t) <= 4 ? (((dev) >> 8) >> 8) : \ (((dev) >> 16) >> 16))) #define minor(dev) (sizeof (dev_t) <= 2 ? (dev) & 0xff : \ (sizeof (dev_t) <= 4 ? (dev) & 0xffff : \ (dev) & 0xffffffff)) #endif #endif #ifdef S_IFBLK case S_IFBLK: sprintf(buffer, "B\t%s\t%lu %lu\n", s_entry->name, (unsigned long) major(statbuf.st_rdev), (unsigned long) minor(statbuf.st_rdev)); break; #endif #ifdef S_IFIFO case S_IFIFO: sprintf(buffer, "P\t%s\n", s_entry->name); break; #endif #ifdef S_IFCHR case S_IFCHR: sprintf(buffer, "C\t%s\t%lu %lu\n", s_entry->name, (unsigned long) major(statbuf.st_rdev), (unsigned long) minor(statbuf.st_rdev)); break; #endif #ifdef S_IFLNK case S_IFLNK: #ifdef HAVE_READLINK nchar = readlink(whole_path, (char *) symlink_buff, sizeof (symlink_buff)-1); #else nchar = -1; #endif symlink_buff[nchar < 0 ? 0 : nchar] = 0; sprintf(buffer, "L\t%s\t%s\n", s_entry->name, symlink_buff); break; #endif #ifdef S_IFSOCK case S_IFSOCK: sprintf(buffer, "S\t%s\n", s_entry->name); break; #endif case S_IFREG: default: sprintf(buffer, "F\t%s\n", s_entry->name); break; }; s_entry->table = strdup(buffer); } if (S_ISDIR(statbuf.st_mode)) { int dflag; if (strcmp(short_name, ".") != 0 && strcmp(short_name, "..") != 0) { struct directory *child; child = find_or_create_directory(this_dir, whole_path, s_entry, 1, NULL); if (no_scandir) dflag = 1; else dflag = scan_directory_tree(child, whole_path, s_entry); if (!dflag) { lstatbuf.st_mode = (lstatbuf.st_mode & ~S_IFMT) | S_IFREG; if (child->contents == NULL) { delete_directory(this_dir, child); } } } /* If unable to scan directory, mark this as a non-directory */ } if (use_RockRidge && this_dir == root && strcmp(s_entry->name, ".") == 0) { deep_flag |= NEED_CE | NEED_SP; /* For extension record */ } /* Now figure out how much room this file will take in the directory */ #ifdef APPLE_HYB /* if the file is HFS excluded, then we don't have an hfs_ent */ if (apple_both && !have_rsrc && s_entry->hfs_ent) { if (S_ISREG(lstatbuf.st_mode)) { /* it's a regular file */ /* fill in the rest of the HFS entry */ get_hfs_info(whole_path, short_name, s_entry); /* if required, set ISO directory name from HFS name */ if (use_mac_name) iso9660_file_length(s_entry->hfs_ent->name, s_entry, 0); /* print details about the HFS file */ if (verbose > 2) print_hfs_info(s_entry); /* * copy the new ISO9660 name to the rsrc fork * - if it exists */ if (s_entry->assoc) strcpy(s_entry->assoc->isorec.name, s_entry->isorec.name); /* * we can't handle hard links in the hybrid case, so we * "uncache" the file. The downside to this is that * hard linked files are added to the output image * more than once (we've already done this for rsrc * files) */ if (apple_hyb) { s_entry->inode = UNCACHED_INODE; s_entry->dev = (dev_t) UNCACHED_DEVICE; } } else if (!(s_entry->isorec.flags[0] & ISO_DIRECTORY)) { /* not a directory .. */ /* * no mac equivalent, so ignore - have to be careful * here, the hfs_ent may be also be for a relocated * directory */ if (s_entry->hfs_ent && !(s_entry->de_flags & RELOCATED_DIRECTORY)) free(s_entry->hfs_ent); s_entry->hfs_ent = NULL; } /* * if the rsrc size is zero, then we don't need the entry, so * we might as well delete it - this will only happen if we * didn't know the rsrc size from the rsrc file size */ if (s_entry->assoc && s_entry->assoc->size == 0) delete_rsrc_ent(s_entry); } if (apple_ext && s_entry->assoc) { /* need Apple extensions for the resource fork as well */ generate_xa_rr_attributes(whole_path, short_name, s_entry->assoc, &statbuf, &lstatbuf, deep_flag); } /* leave out resource fork for the time being */ /* * XXX This is most likely wrong and should just be: * XXX if (use_XA || use_RockRidge) { */ /* if ((use_XA || use_RockRidge) && !have_rsrc) {*/ if (use_XA || use_RockRidge) { #else if (use_XA || use_RockRidge) { #endif /* APPLE_HYB */ generate_xa_rr_attributes(whole_path, short_name, s_entry, &statbuf, &lstatbuf, deep_flag); } return (1); } void generate_iso9660_directories(struct directory *node, FILE *outfile) { struct directory *dpnt; dpnt = node; while (dpnt) { if (dpnt->extent > session_start) { generate_one_directory(dpnt, outfile); } if (dpnt->subdir) generate_iso9660_directories(dpnt->subdir, outfile); dpnt = dpnt->next; } } /* * Function: find_or_create_directory * * Purpose: Locate a directory entry in the tree, create if needed. * If a directory is created and stat_template is non-null, * create the directory with ownership, permissions, etc., * from stat_template, otherwise use fallback defaults. * * Arguments: parent & de are never NULL at the same time. */ struct directory * find_or_create_directory(struct directory *parent, const char *path, struct directory_entry *de, int flag, struct stat *stat_template) { struct directory *dpnt; struct directory_entry *orig_de; struct directory *next_brother; const char *cpnt; const char *pnt; struct stat my_statbuf; orig_de = de; /* * XXX It seems that the tree that has been read from the * XXX previous session does not carry whole_name entries. * XXX We provide a hack in multi.c:find_or_create_directory() * XXX that should be removed when a reasonable method could * XXX be found. */ if (path == NULL) { fprintf(stderr, "Warning: missing whole name for: '%s'\n", de->name); path = de->name; } pnt = strrchr(path, PATH_SEPARATOR); if (pnt == NULL) { pnt = path; } else { pnt++; } if (parent != NULL) { dpnt = parent->subdir; while (dpnt) { /* * Weird hack time - if there are two directories by * the same name in the reloc_dir, they are not * treated as the same thing unless the entire path * matches completely. */ if (flag && strcmp(dpnt->de_name, pnt) == 0) { return (dpnt); } dpnt = dpnt->next; } } /* * We don't know if we have a valid directory entry for this one yet. * If not, we need to create one. */ if (de == NULL) { de = (struct directory_entry *) e_malloc(sizeof (struct directory_entry)); memset(de, 0, sizeof (struct directory_entry)); de->next = parent->contents; parent->contents = de; de->name = strdup(pnt); de->whole_name = strdup(path); de->filedir = parent; de->isorec.flags[0] = ISO_DIRECTORY; de->priority = 32768; de->inode = UNCACHED_INODE; de->dev = (dev_t) UNCACHED_DEVICE; set_723(de->isorec.volume_sequence_number, volume_sequence_number); iso9660_file_length(pnt, de, 1); /* * If we were given a stat template, use it for * ownership/permissions, otherwise use fallback defaults. */ init_fstatbuf(); if (stat_template) { my_statbuf = *stat_template; } else { my_statbuf = fstatbuf; /* defaults */ my_statbuf.st_mode = new_dir_mode; } my_statbuf.st_mode &= ~S_IFMT; /* zero out file type */ my_statbuf.st_mode |= S_IFDIR; /* force to be a directory */ my_statbuf.st_nlink = 2; /* * Apply attributes from my_statbuf to the new directory. */ if (use_XA || use_RockRidge) { generate_xa_rr_attributes("", (char *) pnt, de, &my_statbuf, &my_statbuf, 0); } iso9660_date(de->isorec.date, fstatbuf.st_mtime); #ifdef APPLE_HYB if (apple_both) { /* give the directory an HFS entry */ hfsdirent *hfs_ent; hfs_ent = (hfsdirent *) e_malloc(sizeof (hfsdirent)); /* fill in the defaults */ memset(hfs_ent, 0, sizeof (hfsdirent)); hfs_ent->crdate = my_statbuf.st_ctime; hfs_ent->mddate = my_statbuf.st_mtime; de->hfs_ent = hfs_ent; /* get the Mac directory name */ get_hfs_dir((char *) path, (char *) pnt, de); } #endif /* APPLE_HYB */ } /* * If we don't have a directory for this one yet, then allocate it now, * and patch it into the tree in the appropriate place. */ dpnt = (struct directory *) e_malloc(sizeof (struct directory)); memset(dpnt, 0, sizeof (struct directory)); dpnt->next = NULL; dpnt->subdir = NULL; dpnt->self = de; dpnt->contents = NULL; dpnt->whole_name = strdup(path); cpnt = strrchr(path, PATH_SEPARATOR); if (cpnt) cpnt++; else cpnt = path; dpnt->de_name = strdup(cpnt); dpnt->size = 0; dpnt->extent = 0; dpnt->jextent = 0; dpnt->jsize = 0; #ifdef APPLE_HYB dpnt->hfs_ent = de->hfs_ent; #endif /* APPLE_HYB */ if (orig_de == NULL) { struct stat xstatbuf; struct stat parent_statbuf; int sts; /* * Now add a . and .. entry in the directory itself. This is a * little tricky - if the real directory exists, we need to * stat it first. Otherwise, we use the fictitious fstatbuf * which points to the time at which genisoimage was started. */ if (parent == NULL || parent->whole_name[0] == '\0') sts = -1; else sts = stat_filter(parent->whole_name, &parent_statbuf); if (sts != 0) { parent_statbuf = fstatbuf; parent_statbuf.st_mode = new_dir_mode | S_IFDIR; parent_statbuf.st_nlink = 2; } if (debug && parent) { fprintf(stderr, "stat parent->whole_name: '%s' -> %d.\n", parent->whole_name, sts); } attach_dot_entries(dpnt, &my_statbuf, &parent_statbuf); } if (!parent || parent == root) { if (!root) { root = dpnt; /* First time through for root */ /* directory only */ root->depth = 0; root->parent = root; } else { dpnt->depth = 1; if (!root->subdir) { root->subdir = dpnt; } else { next_brother = root->subdir; while (next_brother->next) next_brother = next_brother->next; next_brother->next = dpnt; } dpnt->parent = parent; } } else { /* Come through here for normal traversal of tree */ #ifdef DEBUG fprintf(stderr, "%s(%d) ", path, dpnt->depth); #endif if (parent->depth > RR_relocation_depth) { /* * XXX to prevent this, we would need to add * XXX support for RR directory relocation * XXX to find_or_create_directory() */ #ifdef USE_LIBSCHILY comerrno(EX_BAD, "Directories too deep for '%s' (%d) max is %d.\n", path, parent->depth, RR_relocation_depth); #else fprintf(stderr, "Directories too deep for '%s' (%d) max is %d.\n", path, parent->depth, RR_relocation_depth); exit(1); #endif } dpnt->parent = parent; dpnt->depth = parent->depth + 1; if (!parent->subdir) { parent->subdir = dpnt; } else { next_brother = parent->subdir; while (next_brother->next) next_brother = next_brother->next; next_brother->next = dpnt; } } return (dpnt); } /* * Function: delete_directory * * Purpose: Locate a directory entry in the tree, create if needed. * * Arguments: */ static void delete_directory(parent, child) struct directory *parent; struct directory *child; { struct directory *tdir; if (child->contents != NULL) { #ifdef USE_LIBSCHILY comerrno(EX_BAD, "Unable to delete non-empty directory\n"); #else fprintf(stderr, "Unable to delete non-empty directory\n"); exit(1); #endif } free(child->whole_name); child->whole_name = NULL; free(child->de_name); child->de_name = NULL; #ifdef APPLE_HYB if (apple_both && child->hfs_ent) free(child->hfs_ent); #endif /* APPLE_HYB */ if (parent->subdir == child) { parent->subdir = child->next; } else { for (tdir = parent->subdir; tdir->next != NULL; tdir = tdir->next) { if (tdir->next == child) { tdir->next = child->next; break; } } if (tdir == NULL) { #ifdef USE_LIBSCHILY comerrno(EX_BAD, "Unable to locate child directory in parent list\n"); #else fprintf(stderr, "Unable to locate child directory in parent list\n"); exit(1); #endif } } free(child); } int sort_tree(struct directory *node) { struct directory *dpnt; int ret = 0; dpnt = node; while (dpnt) { ret = sort_n_finish(dpnt); if (ret) { break; } if (dpnt->subdir) sort_tree(dpnt->subdir); dpnt = dpnt->next; } return (ret); } void dump_tree(struct directory *node) { struct directory *dpnt; dpnt = node; while (dpnt) { fprintf(stderr, "%4d %5d %s\n", dpnt->extent, dpnt->size, dpnt->de_name); if (dpnt->subdir) dump_tree(dpnt->subdir); dpnt = dpnt->next; } } void update_nlink_field(struct directory *node) { struct directory *dpnt; struct directory *xpnt; struct directory_entry *s_entry; int i; dpnt = node; while (dpnt) { if (dpnt->dir_flags & INHIBIT_ISO9660_ENTRY) { dpnt = dpnt->next; continue; } /* * First, count up the number of subdirectories this guy has. */ for (i = 0, xpnt = dpnt->subdir; xpnt; xpnt = xpnt->next) if ((xpnt->dir_flags & INHIBIT_ISO9660_ENTRY) == 0) i++; /* * Next check to see if we have any relocated directories in * this directory. The nlink field will include these as * real directories when they are properly relocated. * In the non-rockridge disk, the relocated entries appear as * zero length files. */ for (s_entry = dpnt->contents; s_entry; s_entry = s_entry->next) { if ((s_entry->de_flags & RELOCATED_DIRECTORY) != 0 && (s_entry->de_flags & INHIBIT_ISO9660_ENTRY) == 0) { i++; } } /* Now update the field in the Rock Ridge entry. */ update_nlink(dpnt->self, i + 2); /* Update the '.' entry for this directory. */ update_nlink(dpnt->contents, i + 2); /* Update all of the '..' entries that point to this guy. */ for (xpnt = dpnt->subdir; xpnt; xpnt = xpnt->next) update_nlink(xpnt->contents->next, i + 2); if (dpnt->subdir) update_nlink_field(dpnt->subdir); dpnt = dpnt->next; } } /* * something quick and dirty to locate a file given a path * recursively walks down path in filename until it finds the * directory entry for the desired file */ struct directory_entry * search_tree_file(struct directory *node, char *filename) { struct directory_entry *depnt; struct directory *dpnt; char *p1; char *rest; char *subdir; /* strip off next directory name from filename */ subdir = strdup(filename); if ((p1 = strchr(subdir, '/')) == subdir) { fprintf(stderr, "call to search_tree_file with an absolute path, stripping\n"); fprintf(stderr, "initial path separator. Hope this was intended...\n"); memmove(subdir, subdir + 1, strlen(subdir) - 1); p1 = strchr(subdir, '/'); } /* do we need to find a subdirectory */ if (p1) { *p1 = '\0'; #ifdef DEBUG_TORITO fprintf(stderr, "Looking for subdir called %s\n", p1); #endif rest = p1 + 1; #ifdef DEBUG_TORITO fprintf(stderr, "Remainder of path name is now %s\n", rest); #endif dpnt = node->subdir; while (dpnt) { #ifdef DEBUG_TORITO fprintf(stderr, "%4d %5d %s\n", dpnt->extent, dpnt->size, dpnt->de_name); #endif if (strcmp(subdir, dpnt->de_name) == 0) { #ifdef DEBUG_TORITO fprintf(stderr, "Calling next level with filename = %s", rest); #endif return (search_tree_file(dpnt, rest)); } dpnt = dpnt->next; } /* if we got here means we couldnt find the subdir */ return (NULL); } else { /* look for a normal file now */ depnt = node->contents; while (depnt) { #ifdef DEBUG_TORITO fprintf(stderr, "%4d %5d %s\n", depnt->isorec.extent, depnt->size, depnt->name); #endif if (strcmp(filename, depnt->name) == 0) { #ifdef DEBUG_TORITO fprintf(stderr, "Found our file %s", filename); #endif return (depnt); } depnt = depnt->next; } /* if we got here means we couldnt find the subdir */ return (NULL); } #ifdef ERIC_FUN fprintf(stderr, "We cant get here in search_tree_file :-/ \n"); #endif } void init_fstatbuf() { time_t current_time; if (fstatbuf.st_ctime == 0) { time(¤t_time); if (rationalize_uid) fstatbuf.st_uid = uid_to_use; else fstatbuf.st_uid = getuid(); if (rationalize_gid) fstatbuf.st_gid = gid_to_use; else fstatbuf.st_gid = getgid(); fstatbuf.st_ctime = current_time; fstatbuf.st_mtime = current_time; fstatbuf.st_atime = current_time; } }