I'm attaching a patch to add a new parameter -keep-top-dirs to genisoimage.
Motivation: I have a large archive folder with many top level directories, each named like yyyy-mm-dd. I want to archive it on discs, but it doesn't fit on a single disc, so I have to specify a subset of directories as input to genisoimage. I want to keep the directory structure, the disc shall have the yyyy-mm-dd directories as entries of the root directory. For example, I want to burn 50 folders like 2015-* to disc 1, and 60 folders named 2016-* to disc b. IIUC there is no existing way to keep top level directories with a simple parameter list like: genisoimage -o output.iso 2015-* I understand that currently there are the following options: - copy/move all desired directories to a temporary directory and use genisoimage -o output.iso /path/to/temporary/directory - instead of coping/moving, add symbolic links to a temporary directory for each desired directory. - use the -graft-points option and use a pathspec list like dir1=dir1 dir2=dir2 dir3=dir3 I consider all of these option inconvenient, especially if the list of directories isn't small. With the attached patch, it is possible to achieve my requirement with this simple command: genisoimage -o output.iso -keep-top-dirs 2015-* The patch is on top of version cdrkit_1.1.11-3.1ubuntu1.dsc Feedback welcome. Please kindly let me know if I should rather provide a patch on top of a different version. Is there interest to merge it? Regards, Kai
# User Kai Engert <k...@kuix.de> Add genisoimage parameter -keep-top-dirs diff -r 643d90fc2314 genisoimage/genisoimage.1 --- a/genisoimage/genisoimage.1 Wed Dec 29 14:34:35 2021 +0100 +++ b/genisoimage/genisoimage.1 Thu Dec 30 00:08:05 2021 +0100 @@ -559,16 +559,26 @@ .B \-graft\-points Allow use of graft points for filenames. If this option is used, all filenames are checked for graft points. The filename is divided at the first unescaped equal sign. All occurrences of `\(rs' and `=' characters must be escaped with `\(rs' if .B \-graft\-points has been specified. .TP +.B \-keep\-top\-dirs +If a pathspec parameter refers to a directory, add it as a +subdirectory of the root directory (do not merge the directory contents +to the root directory). Option -keep-top-dirs cannot be combined with +option -graft-points, it is a shortcut for a parameter list like +-graft-points dir1=dir1 dir2=dir2 dir3=dir3 ... +.IP +Because '.' has a special meaning, the option is ignored when using '.' +as a pathspec parameter. +.TP .BI \-hide " glob" Hide any files matching .IR glob , a shell wildcard pattern, from being seen in the ISO9660 or Rock Ridge directory. .I glob may match any part of the filename or path. If .I glob diff -r 643d90fc2314 genisoimage/genisoimage.c --- a/genisoimage/genisoimage.c Wed Dec 29 14:34:35 2021 +0100 +++ b/genisoimage/genisoimage.c Thu Dec 30 00:08:05 2021 +0100 @@ -177,16 +177,17 @@ int volume_set_size = 1; int volume_sequence_number = 1; struct eltorito_boot_entry_info *first_boot_entry = NULL; struct eltorito_boot_entry_info *last_boot_entry = NULL; struct eltorito_boot_entry_info *current_boot_entry = NULL; int use_graft_ptrs; /* Use graft points */ +int keep_top_dirs; /* Use graft points */ int jhide_trans_tbl; /* Hide TRANS.TBL from Joliet tree */ int hide_rr_moved; /* Name RR_MOVED .rr_moved in Rock Ridge tree */ int omit_period = 0; /* Violates iso9660, but these are a pain */ int transparent_compression = 0; /* So far only works with linux */ int omit_version_number = 0; /* May violate iso9660, but noone uses vers */ int no_rr = 0; /* Do not use RR attributes from old session */ int force_rr = 0; /* Force to use RR attributes from old session */ Uint RR_relocation_depth = 6; /* Violates iso9660, but most systems work */ @@ -401,16 +402,18 @@ #define OPTION_XA_RATIONALIZED 1067 #define OPTION_SUNX86BOOT 1068 #define OPTION_SUNX86LABEL 1069 #define OPTION_ALLOW_LEADING_DOTS 1070 #define OPTION_PUBLISHER 1071 +#define OPTION_KEEP_TOP_DIRS 1072 + #ifdef JIGDO_TEMPLATE #define OPTION_JTT_OUTPUT 1101 #define OPTION_JTJ_OUTPUT 1102 #define OPTION_JT_MIN_SIZE 1103 #define OPTION_JT_PATH_MAP 1104 #define OPTION_JT_MD5_LIST 1105 #define OPTION_JT_INCLUDE 1106 #define OPTION_JT_EXCLUDE 1107 @@ -644,16 +647,18 @@ {{"path-list", required_argument, NULL, OPTION_P_LIST}, '\0', "FILE", "File with list of pathnames to process", ONE_DASH}, {{"preparer", required_argument, NULL, 'p'}, 'p', "PREP", "Set Volume preparer", ONE_DASH}, {{"print-size", no_argument, NULL, OPTION_PRINT_SIZE}, '\0', NULL, "Print estimated filesystem size and exit", ONE_DASH}, {{"publisher", required_argument, NULL, OPTION_PUBLISHER}, '\0', "PUB", "Set Volume publisher", ONE_DASH}, + {{"keep-top-dirs", no_argument, NULL, OPTION_KEEP_TOP_DIRS}, + '\0', NULL, "If pathspec is a directory then keep it as a top level entry", ONE_DASH}, {{"publisher", required_argument, NULL, 'P'}, 'P', "PUB", "Set Volume publisher", ONE_DASH}, {{"quiet", no_argument, NULL, OPTION_QUIET}, '\0', NULL, "Run quietly", ONE_DASH}, {{"rational-rock", no_argument, NULL, 'r'}, 'r', NULL, "Generate rationalized Rock Ridge directory information", ONE_DASH}, {{"rock", no_argument, NULL, 'R'}, 'R', NULL, "Generate Rock Ridge directory information", ONE_DASH}, @@ -877,16 +882,17 @@ static void susage(int excode); static void usage(int excode); int iso9660_date(char *result, time_t crtime); static void hide_reloc_dir(void); static char *get_pnames(int argc, char **argv, int opt, char *pname, int pnsize, FILE *fp); char *findgequal(char *s); static char *escstrcpy(char *to, char *from); +static int all_chars_are_dot_or_pathsep(const char *str); void *e_malloc(size_t size); static int read_one_rcfile(char *filename) { int linum = 0; char linebuffer[256]; FILE *fp; @@ -1354,16 +1360,19 @@ /* A filename that we take as input. */ optind--; have_cmd_line_pathspec = 1; goto parse_input_files; case OPTION_USE_GRAFT: use_graft_ptrs = 1; break; + case OPTION_KEEP_TOP_DIRS: + keep_top_dirs = 1; + break; case 'C': /* * This is a temporary hack until cdrecord gets the * proper hooks in it. */ cdrecord_data = optarg; break; case OPTION_GUI: @@ -2773,16 +2782,25 @@ comerrno(EX_BAD, "Can't have -prep-boot with -apple\n"); #else fprintf(stderr, "Can't have -prep-boot with -apple\n"); exit(1); #endif } #endif /* PREP_BOOT */ + if (use_graft_ptrs && keep_top_dirs) { +#ifdef USE_LIBSCHILY + comerrno(EX_BAD, "Can't have -keep-top-dirs with -graft-points\n"); +#else + fprintf(stderr, "Can't have -keep-top-dirs with -graft-points\n"); + exit(1); +#endif + } + if (apple_hyb || apple_ext) apple_both = 1; if (probe) /* we need to search for all types of Apple/Unix files */ hfs_select = ~0; if (apple_both && verbose && !(hfs_select || *afpfile || magic_filename)) { @@ -3287,40 +3305,55 @@ fprintf(stderr, "Invalid node - '%s'.\n", node); exit(1); #endif } else { /* * Now see whether the user wants to add a regular * file or a directory at this point. */ - if (S_ISDIR(st.st_mode)) { + int add_directory_contents = 0; + + if (!keep_top_dirs && S_ISDIR(st.st_mode)) { + add_directory_contents = 1; + } else { + if (short_name == NULL) { + short_name = strrchr(node, + PATH_SEPARATOR); + if (short_name == NULL || + short_name < node) { + short_name = node; + } else { + short_name++; + } + } + if (short_name != NULL && all_chars_are_dot_or_pathsep(short_name)) { + /* + * This happens if -keep-top-dirs is used + * and . is used as a pathspec parameter. + * In this case we don't have a printable short name, + * so let's ignore -keep-top-dirs for the . parameter. + */ + add_directory_contents = 1; + } + } + if (add_directory_contents) { if (debug) { fprintf(stderr, "graft_dir: '%s : %s', node: '%s', (scan)\n", graft_dir->whole_name, graft_dir->de_name, node); } if (!scan_directory_tree(graft_dir, node, &de)) { exit(1); } if (debug) { fprintf(stderr, "scan done\n"); } } else { - if (short_name == NULL) { - short_name = strrchr(node, - PATH_SEPARATOR); - if (short_name == NULL || - short_name < node) { - short_name = node; - } else { - short_name++; - } - } if (debug) { fprintf(stderr, "graft_dir: '%s : %s', node: '%s', short_name: '%s'\n", graft_dir->whole_name, graft_dir->de_name, node, short_name); } #ifdef APPLE_HYB if (!insert_file_entry(graft_dir, node, @@ -3810,16 +3843,28 @@ } p++; } if (debug) fprintf(stderr, "ESC: '%s'\n", to); return (to); } +static int +all_chars_are_dot_or_pathsep(const char *str) +{ + while (*str) { + if (*str != PATH_SEPARATOR && *str != '.') { + return 0; + } + ++str; + } + return 1; +} + void * e_malloc(size_t size) { void *pt = 0; if ((size > 0) && ((pt = malloc(size)) == NULL)) { #ifdef USE_LIBSCHILY comerr("Not enough memory\n");