Robert Millan 2009-11-08 22:51:41 +00:00
parent 4b4c4f6400
commit 63eb2d63b1
14 changed files with 4270 additions and 812 deletions

View file

@ -20,16 +20,16 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
static char rcsid[] ="$Id: mkisofs.c,v 1.10.1.3 1998/06/02 03:36:16 eric Exp $";
/* ADD_FILES changes made by Ross Biro biro@yggdrasil.com 2/23/95 */
static char rcsid[] ="$Id: mkisofs.c,v 1.29 1998/06/02 03:43:45 eric Exp $";
#include <errno.h>
#include "mkisofs.h"
#include "config.h"
#include "mkisofs.h"
#ifdef linux
#include <getopt.h>
#else
#include "getopt.h"
#endif
#include "iso9660.h"
@ -60,7 +60,7 @@ static char rcsid[] ="$Id: mkisofs.c,v 1.10.1.3 1998/06/02 03:36:16 eric Exp $";
struct directory * root = NULL;
static char version_string[] = "mkisofs v1.11.3";
static char version_string[] = "mkisofs 1.12b4";
FILE * discimage;
unsigned int next_extent = 0;
@ -69,19 +69,30 @@ unsigned int session_start = 0;
unsigned int path_table_size = 0;
unsigned int path_table[4] = {0,};
unsigned int path_blocks = 0;
unsigned int jpath_table_size = 0;
unsigned int jpath_table[4] = {0,};
unsigned int jpath_blocks = 0;
struct iso_directory_record root_record;
struct iso_directory_record jroot_record;
char * extension_record = NULL;
int extension_record_extent = 0;
static int extension_record_size = 0;
int extension_record_size = 0;
/* These variables are associated with command line options */
int use_eltorito = 0;
int use_RockRidge = 0;
int verbose = 0;
int use_Joliet = 0;
int verbose = 1;
int all_files = 0;
int follow_links = 0;
int rationalize = 0;
int generate_tables = 0;
int print_size = 0;
int split_output = 0;
char * preparer = PREPARER_DEFAULT;
char * publisher = PUBLISHER_DEFAULT;
char * appid = APPID_DEFAULT;
@ -101,6 +112,8 @@ int RR_relocation_depth = 6; /* Violates iso9660, but most systems work */
int full_iso9660_filenames = 0; /* Used with Amiga. Disc will not work with
DOS */
int allow_leading_dots = 0; /* DOS cannot read names with leading dots */
int split_SL_component = 1; /* circumvent a bug in the SunOS driver */
int split_SL_field = 1; /* circumvent a bug in the SunOS */
struct rcopts{
char * tag;
@ -120,6 +133,110 @@ struct rcopts rcopt[] = {
{NULL, NULL}
};
/*
* In case it isn't obvious, the option handling code was ripped off from GNU-ld.
*/
struct ld_option
{
/* The long option information. */
struct option opt;
/* The short option with the same meaning ('\0' if none). */
char shortopt;
/* The name of the argument (NULL if none). */
const char *arg;
/* The documentation string. If this is NULL, this is a synonym for
the previous option. */
const char *doc;
enum
{
/* Use one dash before long option name. */
ONE_DASH,
/* Use two dashes before long option name. */
TWO_DASHES,
/* Don't mention this option in --help output. */
NO_HELP
} control;
};
/* Codes used for the long options with no short synonyms. 150 isn't
special; it's just an arbitrary non-ASCII char value. */
#define OPTION_HELP 150
#define OPTION_QUIET 151
#define OPTION_NOSPLIT_SL_COMPONENT 152
#define OPTION_NOSPLIT_SL_FIELD 153
#define OPTION_PRINT_SIZE 154
#define OPTION_SPLIT_OUTPUT 155
static const struct ld_option ld_options[] =
{
{ {"all-files", no_argument, NULL, 'a'},
'a', NULL, "Process all files (don't skip backup files)", ONE_DASH },
{ {"appid", required_argument, NULL, 'A'},
'A', "ID", "Set Application ID" , ONE_DASH },
{ {"eltorito-boot", required_argument, NULL, 'b'},
'b', "FILE", "Set El Torito boot image name" , ONE_DASH },
{ {"eltorito-catalog", required_argument, NULL, 'c'},
'c', "FILE", "Set El Torito boot catalog name" , ONE_DASH },
{ {"cdwrite-params", required_argument, NULL, 'C'},
'C', "PARAMS", "Magic paramters from cdwrite" , ONE_DASH },
{ {"omit-period", no_argument, NULL, 'd'},
'd', NULL, "Omit trailing periods from filenames", ONE_DASH },
{ {"disable-deep-relocation", no_argument, NULL, 'D'},
'D', NULL, "Disable deep directory relocation", ONE_DASH },
{ {"follow-links", no_argument, NULL, 'f'},
'f', NULL, "Follow symbolic links", ONE_DASH },
{ {"help", no_argument, NULL, OPTION_HELP},
'\0', NULL, "Print option help", ONE_DASH },
{ {NULL, required_argument, NULL, 'i'},
'i', "ADD_FILES", "No longer supported" , TWO_DASHES },
{ {"joliet", no_argument, NULL, 'J'},
'J', NULL, "Generate Joliet directory information", ONE_DASH },
{ {"full-iso9660-filenames", no_argument, NULL, 'l'},
'l', NULL, "Allow full 32 character filenames for iso9660 names", ONE_DASH },
{ {"allow-leading-dots", no_argument, NULL, 'L'},
'L', NULL, "Allow iso9660 filenames to start with '.'", ONE_DASH },
{ {"exclude", required_argument, NULL, 'm'},
'm', "GLOBFILE", "Exclude file name" , ONE_DASH },
{ {"prev-session", required_argument, NULL, 'M'},
'M', "FILE", "Set path to previous session to merge" , ONE_DASH },
{ {"omit-version-number", no_argument, NULL, 'N'},
'N', NULL, "Omit version number from iso9660 filename", ONE_DASH },
{ {"no-split-symlink-components", no_argument, NULL, 0},
0, NULL, "Inhibit splitting symlink components" , ONE_DASH },
{ {"no-split-symlink-fields", no_argument, NULL, 0},
0, NULL, "Inhibit splitting symlink fields" , ONE_DASH },
{ {"output", required_argument, NULL, 'o'},
'o', "FILE", "Set output file name" , 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, '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 },
{ {"split-output", no_argument, NULL, OPTION_SPLIT_OUTPUT},
'\0', NULL, "Split output into files of approx. 1GB size", ONE_DASH },
{ {"translation-table", no_argument, NULL, 'T'},
'T', NULL, "Generate translation tables for systems that don't understand long filenames", ONE_DASH },
{ {"verbose", no_argument, NULL, 'v'},
'v', NULL, "Verbose", ONE_DASH },
{ {"volid", required_argument, NULL, 'V'},
'V', "ID", "Set Volume ID" , ONE_DASH },
{ {"old-exclude", required_argument, NULL, 'x'},
'x', "FILE", "Exclude file name(depreciated)" , ONE_DASH }
#ifdef ERIC_neverdef
{ {"transparent-compression", no_argument, NULL, 'z'},
'z', NULL, "Enable transparent compression of files", ONE_DASH },
#endif
};
#define OPTION_COUNT (sizeof ld_options / sizeof ld_options[0])
#if defined(ultrix) || defined(_AUX_SOURCE)
char *strdup(s)
char *s;{char *c;if(c=(char *)malloc(strlen(s)+1))strcpy(c,s);return c;}
@ -179,7 +296,11 @@ void FDECL1(read_rcfile, char *, appname)
}
if (!rcfile)
return;
fprintf(stderr, "Using \"%s\"\n", filename);
if ( verbose > 0 )
{
fprintf(stderr, "Using \"%s\"\n", filename);
}
/* OK, we got it. Now read in the lines and parse them */
linum = 0;
while (fgets(linebuffer, sizeof(linebuffer), rcfile))
@ -260,20 +381,113 @@ void FDECL1(read_rcfile, char *, appname)
char * path_table_l = NULL;
char * path_table_m = NULL;
char * jpath_table_l = NULL;
char * jpath_table_m = NULL;
int goof = 0;
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
void usage(){
const char * program_name = "mkisofs";
#if 0
fprintf(stderr,"Usage:\n");
fprintf(stderr,
"mkisofs [-o outfile] [-R] [-V volid] [-v] [-a] \
[-T]\n [-l] [-d] [-V] [-D] [-L] [-p preparer]"
#ifdef ADD_FILES
"[-i file] \n"
#endif
"[-P publisher] [ -A app_id ] [-z] \n \
[-b boot_image_name] [-c boot_catalog-name] \
[-x path -x path ...] path\n");
exit(1);
#endif
int i;
const char **targets, **pp;
fprintf (stderr, "Usage: %s [options] file...\n", program_name);
fprintf (stderr, "Options:\n");
for (i = 0; i < OPTION_COUNT; i++)
{
if (ld_options[i].doc != NULL)
{
int comma;
int len;
int j;
fprintf (stderr, " ");
comma = FALSE;
len = 2;
j = i;
do
{
if (ld_options[j].shortopt != '\0'
&& ld_options[j].control != NO_HELP)
{
fprintf (stderr, "%s-%c", comma ? ", " : "", ld_options[j].shortopt);
len += (comma ? 2 : 0) + 2;
if (ld_options[j].arg != NULL)
{
if (ld_options[j].opt.has_arg != optional_argument)
{
fprintf (stderr, " ");
++len;
}
fprintf (stderr, "%s", ld_options[j].arg);
len += strlen (ld_options[j].arg);
}
comma = TRUE;
}
++j;
}
while (j < OPTION_COUNT && ld_options[j].doc == NULL);
j = i;
do
{
if (ld_options[j].opt.name != NULL
&& ld_options[j].control != NO_HELP)
{
fprintf (stderr, "%s-%s%s",
comma ? ", " : "",
ld_options[j].control == TWO_DASHES ? "-" : "",
ld_options[j].opt.name);
len += ((comma ? 2 : 0)
+ 1
+ (ld_options[j].control == TWO_DASHES ? 1 : 0)
+ strlen (ld_options[j].opt.name));
if (ld_options[j].arg != NULL)
{
fprintf (stderr, " %s", ld_options[j].arg);
len += 1 + strlen (ld_options[j].arg);
}
comma = TRUE;
}
++j;
}
while (j < OPTION_COUNT && ld_options[j].doc == NULL);
if (len >= 30)
{
fprintf (stderr, "\n");
len = 0;
}
for (; len < 30; len++)
fputc (' ', stderr);
fprintf (stderr, "%s\n", ld_options[i].doc);
}
}
exit(1);
}
@ -326,15 +540,18 @@ extern char * cdwrite_data;
int FDECL2(main, int, argc, char **, argv){
char * outfile;
struct directory_entry de;
#ifdef HAVE_SBRK
unsigned long mem_start;
#endif
struct stat statbuf;
char * scan_tree;
char * merge_image = NULL;
struct iso_directory_record * mrootp = NULL;
struct output_fragment * opnt;
int longind;
char shortopts[OPTION_COUNT * 3 + 2];
struct option longopts[OPTION_COUNT + 1];
int c;
#ifdef ADD_FILES
char *add_file_file = NULL;
#endif
if (argc < 2)
usage();
@ -343,9 +560,56 @@ int FDECL2(main, int, argc, char **, argv){
read_rcfile(argv[0]);
outfile = NULL;
while ((c = getopt(argc, argv, "i:o:V:RrfvaTp:P:b:c:x:dDlLNzA:M:m:C:")) != EOF)
/*
* Copy long option initialization from GNU-ld.
*/
/* Starting the short option string with '-' is for programs that
expect options and other ARGV-elements in any order and that care about
the ordering of the two. We describe each non-option ARGV-element
as if it were the argument of an option with character code 1. */
{
int i, is, il;
shortopts[0] = '-';
is = 1;
il = 0;
for (i = 0; i < OPTION_COUNT; i++)
{
if (ld_options[i].shortopt != '\0')
{
shortopts[is] = ld_options[i].shortopt;
++is;
if (ld_options[i].opt.has_arg == required_argument
|| ld_options[i].opt.has_arg == optional_argument)
{
shortopts[is] = ':';
++is;
if (ld_options[i].opt.has_arg == optional_argument)
{
shortopts[is] = ':';
++is;
}
}
}
if (ld_options[i].opt.name != NULL)
{
longopts[il] = ld_options[i].opt;
++il;
}
}
shortopts[is] = '\0';
longopts[il].name = NULL;
}
while ((c = getopt_long_only (argc, argv, shortopts, longopts, &longind)) != EOF)
switch (c)
{
case 1:
/*
* A filename that we take as input.
*/
optind--;
goto parse_input_files;
case 'C':
/*
* This is a temporary hack until cdwrite gets the proper hooks in
@ -353,6 +617,13 @@ int FDECL2(main, int, argc, char **, argv){
*/
cdwrite_data = optarg;
break;
case 'i':
fprintf(stderr, "-i option no longer supported.\n");
exit(1);
break;
case 'J':
use_Joliet++;
break;
case 'a':
all_files++;
break;
@ -388,14 +659,6 @@ int FDECL2(main, int, argc, char **, argv){
case 'f':
follow_links++;
break;
case 'i':
#ifdef ADD_FILES
add_file_file = optarg;
break;
#else
usage();
exit(1);
#endif
case 'l':
full_iso9660_filenames++;
break;
@ -418,6 +681,9 @@ int FDECL2(main, int, argc, char **, argv){
exit(1);
};
break;
case OPTION_PRINT_SIZE:
print_size++;
break;
case 'P':
publisher = optarg;
if(strlen(publisher) > 128) {
@ -425,6 +691,9 @@ int FDECL2(main, int, argc, char **, argv){
exit(1);
};
break;
case OPTION_QUIET:
verbose = 0;
break;
case 'R':
use_RockRidge++;
break;
@ -432,6 +701,9 @@ int FDECL2(main, int, argc, char **, argv){
rationalize++;
use_RockRidge++;
break;
case OPTION_SPLIT_OUTPUT:
split_output++;
break;
case 'T':
generate_tables++;
break;
@ -449,16 +721,32 @@ int FDECL2(main, int, argc, char **, argv){
transparent_compression++;
#endif
break;
case 'x':
case 'm':
/*
* Somehow two options to do basically the same thing got added somewhere along
* the way. The 'match' code supports limited globbing, so this is the one
* that got selected. Unfortunately the 'x' switch is probably more intuitive.
*/
add_match(optarg);
break;
case 'x':
exclude(optarg);
case OPTION_HELP:
usage ();
exit (0);
break;
case OPTION_NOSPLIT_SL_COMPONENT:
split_SL_component = 0;
break;
case OPTION_NOSPLIT_SL_FIELD:
split_SL_field = 0;
break;
default:
usage();
exit(1);
}
parse_input_files:
#ifdef __NetBSD__
{
int resource;
@ -476,7 +764,7 @@ int FDECL2(main, int, argc, char **, argv){
mem_start = (unsigned long) sbrk(0);
#endif
if(verbose) fprintf(stderr,"%s\n", version_string);
if(verbose > 1) fprintf(stderr,"%s\n", version_string);
if( (cdwrite_data != NULL && merge_image == NULL)
|| (cdwrite_data == NULL && merge_image != NULL) )
@ -489,12 +777,6 @@ int FDECL2(main, int, argc, char **, argv){
scan_tree = argv[optind];
#ifdef ADD_FILES
if (add_file_file) {
add_file(add_file_file);
}
add_file_list (argc, argv, optind+1);
#endif
if(!scan_tree){
usage();
@ -557,28 +839,186 @@ int FDECL2(main, int, argc, char **, argv){
}
/*
* Scan the actual directory (and any we find below it)
* for files to write out to the output image.
* Create an empty root directory. If we ever scan it for real, we will fill in the
* contents.
*/
if (!scan_directory_tree(argv[optind], &de, mrootp))
find_or_create_directory(NULL, "", &de, TRUE);
/*
* Scan the actual directory (and any we find below it)
* for files to write out to the output image. Note - we
* take multiple source directories and keep merging them
* onto the image.
*/
while(optind < argc)
{
exit(1);
char * node;
struct directory * graft_dir;
struct stat st;
char * short_name;
int status;
char graft_point[1024];
/*
* We would like a syntax like:
*
* /tmp=/usr/tmp/xxx
*
* where the user can specify a place to graft each
* component of the tree. To do this, we may have to create
* directories along the way, of course.
* Secondly, I would like to allow the user to do something
* like:
*
* /home/baz/RMAIL=/u3/users/baz/RMAIL
*
* so that normal files could also be injected into the tree
* at an arbitrary point.
*
* The idea is that the last component of whatever is being
* entered would take the name from the last component of
* whatever the user specifies.
*
* The default will be that the file is injected at the
* root of the image tree.
*/
node = strchr(argv[optind], '=');
short_name = NULL;
if( node != NULL )
{
char * pnt;
char * xpnt;
*node = '\0';
strcpy(graft_point, argv[optind]);
*node = '=';
node++;
graft_dir = root;
xpnt = graft_point;
if( *xpnt == PATH_SEPARATOR )
{
xpnt++;
}
/*
* Loop down deeper and deeper until we
* find the correct insertion spot.
*/
while(1==1)
{
pnt = strchr(xpnt, PATH_SEPARATOR);
if( pnt == NULL )
{
if( *xpnt != '\0' )
{
short_name = xpnt;
}
break;
}
*pnt = '\0';
graft_dir = find_or_create_directory(graft_dir,
graft_point,
NULL, TRUE);
*pnt = PATH_SEPARATOR;
xpnt = pnt + 1;
}
}
else
{
graft_dir = root;
node = argv[optind];
}
/*
* Now see whether the user wants to add a regular file,
* or a directory at this point.
*/
status = stat_filter(node, &st);
if( status != 0 )
{
/*
* This is a fatal error - the user won't be getting what
* they want if we were to proceed.
*/
fprintf(stderr, "Invalid node - %s\n", node);
exit(1);
}
else
{
if( S_ISDIR(st.st_mode) )
{
if (!scan_directory_tree(graft_dir, node, &de))
{
exit(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( !insert_file_entry(graft_dir, node, short_name) )
{
exit(1);
}
}
}
optind++;
}
/*
* Now merge in any previous sessions. This is driven on the source
* side, since we may need to create some additional directories.
*/
if( merge_image != NULL )
{
merge_previous_session(root, mrootp);
}
/*
* Fix a couple of things in the root directory so that everything
* is self consistent.
* Sort the directories in the required order (by ISO9660). Also,
* choose the names for the 8.3 filesystem if required, and do
* any other post-scan work.
*/
root->self = root->contents; /* Fix this up so that the path tables get done right */
goof += sort_tree(root);
if(reloc_dir) sort_n_finish(reloc_dir);
if( use_Joliet )
{
goof += joliet_sort_tree(root);
}
if (goof) exit(1);
/*
* Fix a couple of things in the root directory so that everything
* is self consistent.
*/
root->self = root->contents; /* Fix this up so that the path
tables get done right */
/*
* OK, ready to write the file. Open it up, and generate the thing.
*/
if (outfile){
if (print_size){
discimage = fopen("/dev/null", "w");
if (!discimage){
fprintf(stderr,"Unable to open /dev/null\n");
exit(1);
}
} else if (outfile){
discimage = fopen(outfile, "w");
if (!discimage){
fprintf(stderr,"Unable to open disc image file\n");
@ -593,54 +1033,120 @@ int FDECL2(main, int, argc, char **, argv){
path_blocks = (path_table_size + (SECTOR_SIZE - 1)) >> 11;
if (path_blocks & 1) path_blocks++;
path_table[0] = session_start + 0x10 + 2 + (use_eltorito ? 1 : 0);
path_table[1] = 0;
path_table[2] = path_table[0] + path_blocks;
path_table[3] = 0;
jpath_blocks = (jpath_table_size + (SECTOR_SIZE - 1)) >> 11;
if (jpath_blocks & 1) jpath_blocks++;
last_extent += path_table[2] - session_start + path_blocks;
/* The next free block */
/*
* Start to set up the linked list that we use to track the
* contents of the disc.
*/
outputlist_insert(&padblock_desc);
/* The next step is to go through the directory tree and assign extent
numbers for all of the directories */
/*
* PVD for disc.
*/
outputlist_insert(&voldesc_desc);
assign_directory_addresses(root);
/*
* SVD for El Torito. MUST be immediately after the PVD!
*/
if( use_eltorito)
{
outputlist_insert(&torito_desc);
}
if(extension_record) {
struct directory_entry * s_entry;
extension_record_extent = last_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);
};
/*
* SVD for Joliet.
*/
if( use_Joliet)
{
outputlist_insert(&joliet_desc);
}
if (use_RockRidge && reloc_dir)
finish_cl_pl_entries();
/*
* Finally the last volume desctiptor.
*/
outputlist_insert(&end_vol);
/* Now we generate the path tables that are used by DOS to improve directory
access times. */
generate_path_tables();
/* Generate root record for volume descriptor. */
generate_root_record();
outputlist_insert(&pathtable_desc);
if( use_Joliet)
{
outputlist_insert(&jpathtable_desc);
}
if (verbose)
dump_tree(root);
outputlist_insert(&dirtree_desc);
if( use_Joliet)
{
outputlist_insert(&jdirtree_desc);
}
outputlist_insert(&dirtree_clean);
if(extension_record)
{
outputlist_insert(&extension_desc);
}
outputlist_insert(&files_desc);
/*
* Allow room for the various headers we will be writing. There
* will always be a primary and an end volume descriptor.
*/
last_extent = session_start;
/*
* Calculate the size of all of the components of the disc, and assign
* extent numbers.
*/
for(opnt = out_list; opnt; opnt = opnt->of_next )
{
if( opnt->of_size != NULL )
{
(*opnt->of_size)(last_extent);
}
}
/*
* Generate the contents of any of the sections that we want to generate.
* Not all of the fragments will do anything here - most will generate the
* data on the fly when we get to the write pass.
*/
for(opnt = out_list; opnt; opnt = opnt->of_next )
{
if( opnt->of_generate != NULL )
{
(*opnt->of_generate)();
}
}
if( in_image != NULL )
{
fclose(in_image);
}
iso_write(discimage);
/*
* Now go through the list of fragments and write the data that corresponds to
* each one.
*/
for(opnt = out_list; opnt; opnt = opnt->of_next )
{
if( opnt->of_write != NULL )
{
(*opnt->of_write)(discimage);
}
}
if( verbose > 0 )
{
#ifdef HAVE_SBRK
fprintf(stderr,"Max brk space used %x\n",
(unsigned int)(((unsigned long)sbrk(0)) - mem_start));
fprintf(stderr,"Max brk space used %x\n",
(unsigned int)(((unsigned long)sbrk(0)) - mem_start));
#endif
fprintf(stderr,"%d extents written (%d Mb)\n", last_extent, last_extent >> 9);
fprintf(stderr,"%d extents written (%d Mb)\n", last_extent, last_extent >> 9);
}
#ifdef VMS
return 1;
#else