Redistributed with explicit permission from Patrick Ohly. See the originating URL, http://home.pages.de/~ohly/#mkisofs-root, for further details and eventual updates. 8<---8<---8<---8<---8<---8<---8<---8<---8<---8<---8<---8<---8<--- This patch adds two new command line options: -root dir Moves all files and directories into dir in the image. This is essentially the same as using -graft-points and adding dir in front of every pathspec, but is easier to use. dir may actually be several levels deep. It is cre­ ated with the same permissions as other graft points. -old-root dir This option is necessary when writing a multises­ sion image and the previous (or even older) session was written with -root dir. Using a directory name not found in the previous session causes mkisofs to abort with an error. Without this option, mkisofs would not be able to find unmodified files and would be forced to write their data into the image once more. -root and -old-root are meant to be used together to do incremental backups. The initial session would e.g. use: mkisofs -root monday . The next incremental backup with mkisofs -root tuesday -old-root monday would take another snapshot of these directories. The first snapshot would be be found in 'monday', the second one in 'tuesday', but only modified or new files need to be written into the second session. It is applied by unpacking cdrtools-2.0.tar.gz, entering the cdrtools-2.0/mkisofs directory and typing patch -p1 . + The next incremental backup with mkisofs \-root backup_2 + \-old-root backup_1 + would take another snapshot of these directories. The first + snapshot would be found in backup_1, the second one in + backup_2, but only modified or new files need to be written + into the second session. + + Without these options, new files would be added and old ones would be + preserved. But old ones would be overwritten if the file was + modified. Recovering the files by copying the whole directory back + from CD would also restore files that were deleted + intentionally. Accessing several older versions of a file requires + support by the operating system to choose which sessions are to be + mounted. + .TP .B \-split-output Split the output image into several files of approximately 1 GB. This helps to create DVD sized iso9660 images on operating systems without diff -r -c mkisofs/mkisofs.c mkisofs2/mkisofs.c *** mkisofs/mkisofs.c Sat Dec 7 20:59:41 2002 --- mkisofs2/mkisofs.c Sun Mar 23 17:56:04 2003 *************** *** 401,406 **** --- 401,409 ---- #define OPTION_HFS_BLESS 2040 #define OPTION_HFS_PARMS 2041 + #define OPTION_RELOC_ROOT 2042 + #define OPTION_RELOC_OLD_ROOT 2043 + #endif /* APPLE_HYB */ static int save_pname = 0; *************** *** 510,515 **** --- 513,522 ---- 0, NULL, "Do not pad output to a multiple of 32k", ONE_DASH}, {{"prev-session", required_argument, NULL, 'M'}, 'M', "FILE", "Set path to previous session to merge", ONE_DASH}, + {{"root", required_argument, NULL, OPTION_RELOC_ROOT}, + '\0', "DIR", "Set root directory for all new files and directories", ONE_DASH}, + {{"old-root", required_argument, NULL, OPTION_RELOC_OLD_ROOT}, + '\0', "DIR", "Set root directory in previous session that is searched for files", ONE_DASH}, {{"omit-version-number", no_argument, NULL, 'N'}, 'N', NULL, "Omit version number from ISO9660 filename (violates ISO9660)", ONE_DASH}, {{"new-dir-mode", required_argument, NULL, OPTION_NEW_DIR_MODE}, *************** *** 1126,1131 **** --- 1133,1140 ---- #endif struct stat statbuf; char *merge_image = NULL; + char *reloc_root = NULL; + char *reloc_old_root = NULL; struct iso_directory_record *mrootp = NULL; struct output_fragment *opnt; int longind; *************** *** 1469,1474 **** --- 1478,1489 ---- case 'M': merge_image = optarg; break; + case OPTION_RELOC_ROOT: + reloc_root = optarg; + break; + case OPTION_RELOC_OLD_ROOT: + reloc_old_root = optarg; + break; case 'N': omit_version_number++; warn_violate++; *************** *** 2543,2548 **** --- 2558,2569 ---- memset(&de, 0, sizeof(de)); + /* + * PO: + * Isn't root NULL at this time anyway? + * I think it is created by the first call to + * find_or_create_directory() below. + */ de.filedir = root; /* We need this to bootstrap */ if (cdrecord_data != NULL && merge_image == NULL) { *************** *** 2652,2664 **** short_name = NULL; ! if (node != NULL) { char *pnt; char *xpnt; ! *node = '\0'; ! escstrcpy(graft_point, arg); ! *node = '='; /* * Remove unwanted "./" & "/" sequences from start... --- 2673,2700 ---- short_name = NULL; ! if (node != NULL || reloc_root) { char *pnt; char *xpnt; + size_t len; + + /* insert -root prefix */ + if (reloc_root != NULL) { + strcpy(graft_point, reloc_root); + len = strlen(graft_point); + if (graft_point[len] != '/' ) { + graft_point[len] = '/'; + len++; + } + } else { + len = 0; + } ! if (node) { ! *node = '\0'; ! escstrcpy(graft_point + len, arg); ! *node = '='; ! } /* * Remove unwanted "./" & "/" sequences from start... *************** *** 2673,2679 **** strcpy(graft_point, xpnt); } while (xpnt > graft_point); ! node = escstrcpy(nodename, ++node); graft_dir = root; xpnt = graft_point; --- 2709,2719 ---- strcpy(graft_point, xpnt); } while (xpnt > graft_point); ! if (node) { ! node = escstrcpy(nodename, ++node); ! } else { ! node = arg; ! } graft_dir = root; xpnt = graft_point; *************** *** 2834,2840 **** * side, since we may need to create some additional directories. */ if (merge_image != NULL) { ! if (merge_previous_session(root, mrootp) < 0) { #ifdef USE_LIBSCHILY comerrno(EX_BAD, "Cannot merge previous session.\n"); #else --- 2874,2881 ---- * side, since we may need to create some additional directories. */ if (merge_image != NULL) { ! if (merge_previous_session(root, mrootp, ! reloc_root, reloc_old_root) < 0) { #ifdef USE_LIBSCHILY comerrno(EX_BAD, "Cannot merge previous session.\n"); #else diff -r -c mkisofs/mkisofs.h mkisofs2/mkisofs.h *** mkisofs/mkisofs.h Sat Dec 7 20:59:42 2002 --- mkisofs2/mkisofs.h Sun Mar 23 17:57:05 2003 *************** *** 494,500 **** extern void merge_remaining_entries __PR((struct directory *, struct directory_entry **, int)); extern int merge_previous_session __PR((struct directory *, ! struct iso_directory_record *)); extern int get_session_start __PR((int *)); /* joliet.c */ --- 494,501 ---- extern void merge_remaining_entries __PR((struct directory *, struct directory_entry **, int)); extern int merge_previous_session __PR((struct directory *, ! struct iso_directory_record *, ! char *, char *)); extern int get_session_start __PR((int *)); /* joliet.c */ diff -r -c mkisofs/multi.c mkisofs2/multi.c *** mkisofs/multi.c Wed Dec 25 15:15:24 2002 --- mkisofs2/multi.c Sat Apr 5 17:33:11 2003 *************** *** 1512,1520 **** * directory entries, so that we can determine how large each directory is. */ int ! merge_previous_session(this_dir, mrootp) struct directory *this_dir; struct iso_directory_record *mrootp; { struct directory_entry **orig_contents = NULL; struct directory_entry *odpnt = NULL; --- 1512,1522 ---- * directory entries, so that we can determine how large each directory is. */ int ! merge_previous_session(this_dir, mrootp, reloc_root, reloc_old_root) struct directory *this_dir; struct iso_directory_record *mrootp; + char *reloc_root; + char *reloc_old_root; { struct directory_entry **orig_contents = NULL; struct directory_entry *odpnt = NULL; *************** *** 1526,1540 **** --- 1528,1659 ---- lstatbuf; int retcode; + /* skip leading slash */ + while (reloc_old_root && reloc_old_root[0] == PATH_SEPARATOR) { + reloc_old_root++; + } + while (reloc_root && reloc_root[0] == PATH_SEPARATOR) { + reloc_root++; + } + /* * Parse the same directory in the image that we are merging for * multisession stuff. */ orig_contents = read_merging_directory(mrootp, &n_orig); if (orig_contents == NULL) { + if (reloc_old_root) { + #ifdef USE_LIBSCHILY + comerrno(EX_BAD, "Reading old session failed, cannot execute -old-root.\n"); + #else + fprintf(stderr, "Reading old session failed, cannot execute -old-root.\n"); + #endif + return (-1); + } return (0); } + if (reloc_old_root && reloc_old_root[0]) { + struct directory_entry **new_orig_contents = orig_contents; + int new_n_orig = n_orig; + + /* decend until we reach the original root */ + while (reloc_old_root[0]) { + int i; + char *next; + int last; + + for (next = reloc_old_root; *next && *next != PATH_SEPARATOR; next++); + if (*next) { + last = 0; + *next = 0; + next++; + } else { + last = 1; + } + while (*next == PATH_SEPARATOR) { + next++; + } + + for (i = 0; i < new_n_orig; i++) { + struct iso_directory_record subroot; + + if (new_orig_contents[i]->name != NULL + && strcmp(new_orig_contents[i]->name, reloc_old_root) != 0) { + /* Not the same name continue */ + continue; + } + /* + * enter directory, free old one only if not the top level, + * which is still needed + */ + subroot = new_orig_contents[i]->isorec; + if (new_orig_contents != orig_contents) { + free_mdinfo(new_orig_contents, new_n_orig); + } + new_orig_contents = read_merging_directory(&subroot, &new_n_orig); + + if (!new_orig_contents) { + #ifdef USE_LIBSCHILY + comerrno(EX_BAD, "Reading directory %s in old session failed, cannot execute -old-root.\n", reloc_old_root ); + #else + fprintf(stderr, "Reading directory %s in old session failed, cannot execute -old-root.\n", reloc_old_root ); + #endif + return (-1); + } + + i = -1; + break; + } + + if (i == new_n_orig) { + #ifdef USE_LIBSCHILY + comerrno(EX_BAD, "-old-root (sub)directory %s not found in old session.\n", reloc_old_root ); + #else + fprintf(stderr, "-old-root (sub)directory %s not found in old session.\n", reloc_old_root ); + #endif + return (-1); + } + + /* restore string, proceed to next sub directory */ + if (!last) { + reloc_old_root[strlen(reloc_old_root)] = PATH_SEPARATOR; + } + reloc_old_root = next; + } + + /* + * preserve the old session, skipping those dirs/files that are found again + * in the new root + */ + for (s_entry = this_dir->contents; s_entry; s_entry = s_entry->next) { + status = stat_filter(s_entry->whole_name, &statbuf); + lstatus = lstat_filter(s_entry->whole_name, &lstatbuf); + + /* check_prev_session() will search for s_entry and remove it from + orig_contents if found */ + retcode = check_prev_session(orig_contents, n_orig, s_entry, + &statbuf, &lstatbuf, NULL); + if (retcode == -1) + return (-1); + } + merge_remaining_entries(this_dir, orig_contents, n_orig); + + /* use new directory */ + free_mdinfo(orig_contents, n_orig); + orig_contents = new_orig_contents; + n_orig = new_n_orig; + + if (reloc_root && reloc_root[0]) { + /* also decend into new root before searching for files */ + this_dir = find_or_create_directory(this_dir, reloc_root, NULL, TRUE); + if (!this_dir) { + return (-1); + } + } + } + + /* * Now we scan the directory itself, and look at what is inside of it. */ *************** *** 1595,1601 **** s_entry->whole_name, s_entry, 1); dflag = merge_previous_session(child, ! &odpnt->isorec); if (dflag == -1) { return (-1); } --- 1728,1735 ---- s_entry->whole_name, s_entry, 1); dflag = merge_previous_session(child, ! &odpnt->isorec, ! reloc_old_root, NULL ); if (dflag == -1) { return (-1); } *************** *** 1605,1615 **** } } ! /* ! * Whatever is left over, are things which are no longer in the tree on ! * disk. We need to also merge these into the tree. ! */ ! merge_remaining_entries(this_dir, orig_contents, n_orig); free_mdinfo(orig_contents, n_orig); return (1); } --- 1739,1751 ---- } } ! if (!reloc_old_root) { ! /* ! * Whatever is left over, are things which are no longer in the tree on ! * disk. We need to also merge these into the tree. ! */ ! merge_remaining_entries(this_dir, orig_contents, n_orig); ! } free_mdinfo(orig_contents, n_orig); return (1); }