/* * Program mkisofs.c - generate iso9660 filesystem based upon directory * tree on hard disk. Written by Eric Youngdale (1993). Copyright 1993 Yggdrasil Computing, Incorporated 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. */ #include "mkisofs.h" #include #ifdef linux #include #endif #include "iso9660.h" #include #ifndef VMS #include #else #include #include "vms.h" #endif #include #include #ifndef VMS #include #endif #include "exclude.h" #ifdef __NetBSD__ #include #include #endif struct directory * root = NULL; static char version_string[] = "mkisofs v1.03"; FILE * discimage; unsigned int next_extent = 0; unsigned int last_extent = 0; unsigned int path_table_size = 0; unsigned int path_table[4] = {0,}; unsigned int path_blocks = 0; struct iso_directory_record root_record; static int timezone_offset; char * extension_record = NULL; int extension_record_extent = 0; static int extension_record_size = 0; /* These variables are associated with command line options */ int use_RockRidge = 0; int verbose = 0; int all_files = 0; int follow_links = 0; int generate_tables = 0; char * preparer = PREPARER_DEFAULT; char * publisher = PUBLISHER_DEFAULT; char * appid = APPID_DEFAULT; char * copyright = COPYRIGHT_DEFAULT; char * biblio = BIBLIO_DEFAULT; char * abstract = ABSTRACT_DEFAULT; char * volset_id = VOLSET_ID_DEFAULT; char * volume_id = VOLUME_ID_DEFAULT; char * system_id = SYSTEM_ID_DEFAULT; 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 RR_relocation_depth = 6; /* Violates iso9660, but most systems work */ int full_iso9660_filenames = 0; /* Used with Amiga. Disc will not work with DOS */ struct rcopts{ char * tag; char ** variable; }; struct rcopts rcopt[] = { {"PREP", &preparer}, {"PUBL", &publisher}, {"APPI", &appid}, {"COPY", ©right}, {"BIBL", &biblio}, {"ABST", &abstract}, {"VOLS", &volset_id}, {"VOLI", &volume_id}, {"SYSI", &system_id}, {NULL, NULL} }; #ifdef ultrix char *strdup(s) char *s;{char *c;if(c=(char *)malloc(strlen(s)+1))strcpy(c,s);return c;} #endif void FDECL1(read_rcfile, char *, appname) { FILE * rcfile; struct rcopts * rco; char * pnt, *pnt1; char linebuffer[256]; rcfile = fopen(".mkisofsrc","r"); if(!rcfile) { if(strlen(appname)+sizeof(".mkisofsrc") > sizeof(linebuffer)) return; strcpy(linebuffer, appname); pnt = strrchr(linebuffer,'/'); if(!pnt) return; pnt++; strcpy(pnt, ".mkisofsrc"); rcfile = fopen(linebuffer,"r"); fprintf(stderr, "Using %s.\n", linebuffer); } else { fprintf(stderr, "Using ./.mkisofsrc.\n"); } if(!rcfile) return; /* OK, we got it. Now read in the lines and parse them */ while(!feof(rcfile)) { fgets(linebuffer, sizeof(linebuffer), rcfile); pnt = linebuffer; while(1==1) { if(*pnt == ' ' || *pnt == '\t' || *pnt == '\n' || *pnt == 0) break; if(islower(*pnt)) *pnt = toupper(*pnt); pnt++; } /* OK, now find the '=' sign */ while(*pnt && *pnt != '=' && *pnt != '#') pnt++; if(*pnt == '#') continue; /* SKip comment */ if(*pnt != '=') continue; /* Skip to next line */ pnt++; /* Skip past '=' sign */ while(*pnt == ' ' || *pnt == '\t') pnt++; /* And skip past whitespace */ /* Now get rid of trailing newline */ pnt1 = pnt; while(*pnt1) { if(*pnt1 == '\n') *pnt1 = 0; else pnt1++; }; pnt1 = linebuffer; while(*pnt1 == ' ' || *pnt1 == '\t') pnt1++; /* OK, now figure out which option we have */ for(rco = rcopt; rco->tag; rco++) { if(strncmp(rco->tag, pnt1, 4) == 0) { *rco->variable = strdup(pnt); break; }; } } fclose(rcfile); } char * path_table_l = NULL; char * path_table_m = NULL; int goof = 0; void usage(){ fprintf(stderr,"Usage:\n"); fprintf(stderr, "mkisofs [-o outfile] [-R] [-V volid] [-v] [-a] \ [-T]\n [-l] [-d] [-V] [-D] [-p preparer] \ [-P publisher] [ -A app_id ] [-z] \ [-x path -x path ...] path\n"); exit(1); } int get_iso9660_timezone_offset(){ struct tm gm; struct tm * pt; time_t ctime; int local_min, gmt_min; time(&ctime); pt = gmtime(&ctime); gm = *pt; pt = localtime(&ctime); if(gm.tm_year < pt->tm_year) gm.tm_yday = -1; if(gm.tm_year > pt->tm_year) pt->tm_yday = -1; gmt_min = gm.tm_min + 60*(gm.tm_hour + 24*gm.tm_yday); local_min = pt->tm_min + 60*(pt->tm_hour + 24*pt->tm_yday); return (gmt_min - local_min)/15; } /* Fill in date in the iso9660 format */ int FDECL2(iso9660_date,char *, result, time_t, ctime){ struct tm *local; local = localtime(&ctime); result[0] = local->tm_year; result[1] = local->tm_mon + 1; result[2] = local->tm_mday; result[3] = local->tm_hour; result[4] = local->tm_min; result[5] = local->tm_sec; result[6] = timezone_offset; return 0; } int FDECL3(iso9660_file_length,const char*, name, struct directory_entry *, sresult, int, dirflag){ int seen_dot = 0; int seen_semic = 0; char * result; int priority = 32767; int tildes = 0; int ignore = 0; int extra = 0; int current_length = 0; int chars_after_dot = 0; int chars_before_dot = 0; const char * pnt; result = sresult->isorec.name; if(strcmp(name,".") == 0){ if(result) *result = 0; return 1; }; if(strcmp(name,"..") == 0){ if(result) { *result++ = 1; *result++ = 0; } return 1; }; pnt = name; while(*pnt){ #ifdef VMS if(strcmp(pnt,".DIR;1") == 0) break; #endif if(*pnt == '#') {priority = 1; pnt++; continue; }; if(*pnt == '~') {priority = 1; tildes++; pnt++; continue;}; if(*pnt == ';') {seen_semic = 1; *result++ = *pnt++; continue; }; if(ignore) {pnt++; continue;}; if(seen_semic){ if(*pnt >= '0' && *pnt <= '9') *result++ = *pnt; extra++; pnt++; continue; }; if(full_iso9660_filenames) { /* Here we allow a more relaxed syntax. */ if(*pnt == '.') { if (seen_dot) {ignore++; continue;} seen_dot++; } if(current_length < 30) *result++ = (islower(*pnt) ? toupper(*pnt) : *pnt); } else { /* Dos style filenames */ if(*pnt == '.') { if (seen_dot) {ignore++; continue;} if(result) *result++ = '.'; seen_dot++; } else if (seen_dot) { if(chars_after_dot < 3) { chars_after_dot++; if(result) *result++ = (islower(*pnt) ? toupper(*pnt) : *pnt); } } else { if(chars_before_dot < 8) { chars_before_dot++; if(result) *result++ = (islower(*pnt) ? toupper(*pnt) : *pnt); }; }; }; current_length++; pnt++; }; if(tildes == 2){ int prio1 = 0; pnt = name; while (*pnt && *pnt != '~') pnt++; if (*pnt) pnt++; while(*pnt && *pnt != '~'){ prio1 = 10*prio1 + *pnt - '0'; pnt++; }; priority = prio1; }; if (!dirflag){ if (!seen_dot && !omit_period) { if (result) *result++ = '.'; extra++; }; if(!omit_version_number && !seen_semic) { if(result){ *result++ = ';'; *result++ = '1'; }; extra += 2; } }; if(result) *result++ = 0; #if 1 /* WALNUT CREEK HACKS -- rab 950126 */ { int i, c, len; char *r; assert(result); assert(omit_version_number); assert(omit_period); assert(extra == 0); r = sresult->isorec.name; len = strlen(r); if (r[len - 1] == '.') { assert(seen_dot && chars_after_dot == 0); r[--len] = '\0'; seen_dot = 0; } for (i = 0; i < len; ++i) { c = r[i]; if (c == '.') { if (dirflag) { fprintf(stderr, "changing DIR %s to ", r); r[i] = '\0'; fprintf(stderr, "%s\n", r); chars_after_dot = 0; seen_dot = 0; extra = 0; break; } } else if (!isalnum(c) && c != '_') { fprintf(stderr, "changing %s to ", r); r[i] = '_'; fprintf(stderr, "%s\n", r); } } } #endif sresult->priority = priority; return chars_before_dot + chars_after_dot + seen_dot + extra; } int FDECL2(main, int, argc, char **, argv){ char * outfile; struct directory_entry de; unsigned int mem_start; struct stat statbuf; char * scan_tree; int c; if (argc < 2) usage(); /* Get the defaults from the .mkisofsrc file */ read_rcfile(argv[0]); outfile = NULL; while ((c = getopt(argc, argv, "o:V:RfvaTp:P:x:dDlNzA:")) != EOF) switch (c) { case 'p': preparer = optarg; if(strlen(preparer) > 128) { fprintf(stderr,"Preparer string too long\n"); exit(1); }; break; case 'P': publisher = optarg; if(strlen(publisher) > 128) { fprintf(stderr,"Publisher string too long\n"); exit(1); }; break; case 'A': appid = optarg; if(strlen(appid) > 128) { fprintf(stderr,"Application-id string too long\n"); exit(1); }; break; case 'd': omit_period++; break; case 'D': RR_relocation_depth = 32767; break; case 'l': full_iso9660_filenames++; break; case 'N': omit_version_number++; break; case 'o': outfile = optarg; break; case 'f': follow_links++; break; case 'R': use_RockRidge++; break; case 'V': volume_id = optarg; break; case 'v': verbose++; break; case 'a': all_files++; break; case 'T': generate_tables++; break; case 'z': #ifdef VMS fprintf(stderr,"Transparent compression not supported with VMS\n"); exit(1); #else transparent_compression++; #endif break; case 'x': exclude(optarg); break; default: usage(); exit(1); } #ifdef __NetBSD__ { int resource; struct rlimit rlp; if (getrlimit(RLIMIT_DATA,&rlp) == -1) perror("Warning: getrlimit"); else { rlp.rlim_cur=33554432; if (setrlimit(RLIMIT_DATA,&rlp) == -1) perror("Warning: setrlimit"); } } #endif mem_start = (unsigned int) sbrk(0); if(verbose) fprintf(stderr,"%s\n", version_string); /* Now find the timezone offset */ timezone_offset = get_iso9660_timezone_offset(); /* The first step is to scan the directory tree, and take some notes */ scan_tree = argv[optind]; if(!scan_tree){ usage(); exit(1); }; #ifndef VMS if(scan_tree[strlen(scan_tree)-1] != '/') { scan_tree = (char *) e_malloc(strlen(argv[optind])+2); strcpy(scan_tree, argv[optind]); strcat(scan_tree, "/"); }; #endif if(use_RockRidge){ #if 1 extension_record = generate_rr_extension_record("RRIP_1991A", "THE ROCK RIDGE INTERCHANGE PROTOCOL PROVIDES SUPPORT FOR POSIX FILE SYSTEM SEMANTICS", "PLEASE CONTACT DISC PUBLISHER FOR SPECIFICATION SOURCE. SEE PUBLISHER IDENTIFIER IN PRIMARY VOLUME DESCRIPTOR FOR CONTACT INFORMATION.", &extension_record_size); #else extension_record = generate_rr_extension_record("IEEE_P1282", "THE IEEE P1282 PROTOCOL PROVIDES SUPPORT FOR POSIX FILE SYSTEM SEMANTICS", "PLEASE CONTACT THE IEEE STANDARDS DEPARTMENT,PISCATAWAY, NJ, USA FOR THE P1282 SPECIFICATION.", &extension_record_size); #endif }; stat(argv[optind], &statbuf); add_directory_hash(statbuf.st_dev, STAT_INODE(statbuf)); de.filedir = root; /* We need this to bootstrap */ scan_directory_tree(argv[optind], &de); root->self = root->contents; /* Fix this up so that the path tables get done right */ if(reloc_dir) sort_n_finish(reloc_dir); if (goof) exit(1); if (outfile){ discimage = fopen(outfile, "w"); if (!discimage){ fprintf(stderr,"Unable to open disc image file\n"); exit(1); }; } else discimage = stdout; /* Now assign addresses on the disc for the path table. */ path_blocks = (path_table_size + (SECTOR_SIZE - 1)) >> 11; if (path_blocks & 1) path_blocks++; path_table[0] = 0x14; path_table[1] = path_table[0] + path_blocks; path_table[2] = path_table[1] + path_blocks; path_table[3] = path_table[2] + path_blocks; last_extent = path_table[3] + path_blocks; /* The next free block */ /* The next step is to go through the directory tree and assign extent numbers for all of the directories */ assign_directory_addresses(root); if(extension_record) { struct directory_entry * s_entry; extension_record_extent = last_extent++; s_entry = root->contents; set_733(s_entry->rr_attributes + s_entry->rr_attr_size - 24, extension_record_extent); set_733(s_entry->rr_attributes + s_entry->rr_attr_size - 8, extension_record_size); }; if (use_RockRidge && reloc_dir) finish_cl_pl_entries(); /* 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(); dump_tree(root); iso_write(discimage); fprintf(stderr,"Max brk space used %x\n", ((unsigned int)sbrk(0)) - mem_start); fprintf(stderr,"%d extents written (%d Mb)\n", last_extent, last_extent >> 9); #ifdef VMS return 1; #else return 0; #endif } void *e_malloc(size_t size) { void* pt; if((pt=malloc(size))==NULL) { printf("Not enougth memory\n"); exit (1); } return pt; }