writeisofs.c

Go to the documentation of this file.
00001 
00002 /* writeisofs - simple ISO9660-format-image writing utility */
00003 
00004 #include <errno.h>
00005 #include <stdio.h>
00006 #include <time.h>
00007 #include <stdlib.h>
00008 #include <fcntl.h>
00009 #include <string.h>
00010 #include <unistd.h>
00011 #include <dirent.h>
00012 #include <ctype.h>
00013 #include <ibm/partition.h>
00014 
00015 #include <sys/stat.h>
00016 
00017 #define Writefield(fd, f) Write(fd, &(f), sizeof(f))
00018 
00019 extern char *optarg;
00020 extern int optind;
00021 
00022 typedef unsigned char u_int8_t;
00023 typedef unsigned short int u_int16_t;
00024 typedef unsigned long int u_int32_t;
00025 
00026 #ifndef min
00027 #define min(a,b) ((a) < (b) ? (a) : (b))
00028 #endif
00029 
00030 #define FLAG_DIR        2
00031 
00032 #include <sys/types.h>
00033 #include <sys/stat.h>
00034 
00035 #define NAMELEN         (DIRSIZ+5)
00036 #define ISONAMELEN      12
00037 #define PLATFORM_80X86  0
00038 
00039 #define ISO_SECTOR 2048
00040 #define VIRTUAL_SECTOR  512
00041 
00042 #define CURRENTDIR      "."
00043 #define PARENTDIR       ".."
00044 
00045 /*  *** CD (disk) data structures ********************* */
00046 
00047 /* primary volume descriptor */
00048 
00049 struct pvd {
00050         u_int8_t one;
00051         char set[6];
00052         u_int8_t zero;
00053         char system[32];
00054         char volume[32];
00055         u_int8_t zeroes1[8];
00056         u_int32_t sectors[2];
00057         u_int8_t zeroes2[32];
00058         u_int16_t setsize[2];
00059         u_int16_t seq[2];
00060         u_int16_t sectorsize[2];
00061         u_int32_t pathtable[2];
00062         u_int32_t first_little_pathtable_start;
00063         u_int32_t second_little_pathtable_start;
00064         u_int32_t first_big_pathtable_start;
00065         u_int32_t second_big_pathtable_start;
00066         u_int8_t rootrecord[34];
00067         u_int8_t volumeset[128];
00068         u_int8_t publisher[128];
00069         u_int8_t preparer[128];
00070         u_int8_t application[128];
00071         u_int8_t copyrightfile[37];
00072         u_int8_t abstractfile[37];
00073         u_int8_t bibliofile[37];
00074         u_int8_t create[17];
00075         u_int8_t modified[17];
00076         char expiry[17];
00077         u_int8_t effective[17];
00078         u_int8_t one2;
00079         u_int8_t zero2;
00080         u_int8_t zeroes3[512];
00081         u_int8_t zeroes4[653];
00082 };
00083 
00084 /* boot record volume descriptor */
00085 
00086 struct bootrecord {
00087         u_int8_t        indicator;      /* 0 */
00088         char            set[5];         /* "CD001" */
00089         u_int8_t        version;        /* 1 */
00090         char            ident[32];      /* "EL TORITO SPECIFICATION" */
00091         u_int8_t        zero[32];       /* unused, must be 0 */
00092         u_int32_t       bootcatalog;    /* starting sector of boot catalog */
00093         u_int8_t        zero2[1973];    /* unused, must be 0 */
00094 };
00095 
00096 /* boot catalog validation entry */
00097 
00098 struct bc_validation {
00099         u_int8_t        headerid;       /* 1 */
00100         u_int8_t        platform;       /* 0: 80x86; 1: powerpc; 2: mac */
00101         u_int8_t        zero[2];        /* unused, must be 0 */
00102         char            idstring[24];   /* id string */
00103         u_int16_t       checksum;
00104         u_int8_t        keys[2];        /* 0x55AA */
00105 };
00106 
00107 /* boot catalog initial/default entry */
00108 
00109 #define INDICATE_BOOTABLE       0x88
00110 
00111 #define BOOTMEDIA_NONE          0
00112 #define BOOTMEDIA_120M          1
00113 #define BOOTMEDIA_144M          2
00114 #define BOOTMEDIA_288M          3
00115 #define BOOTMEDIA_HARDDISK      4
00116 
00117 struct bc_initial {
00118         u_int8_t        indicator;      /* INDICATE_BOOTABLE */
00119         u_int8_t        media;          /* BOOTMEDIA_* */
00120         u_int16_t       seg;            /* load segment or 0 for default */
00121         u_int8_t        type;           /* system type (from part. table) */
00122         u_int8_t        zero;
00123         u_int16_t       sectors;
00124         u_int32_t       startsector;
00125         u_int8_t        zero2[20];
00126 };
00127 
00128 /* directory entry */
00129 
00130 struct dir {
00131         u_int8_t        recordsize;
00132         u_int8_t        extended;
00133         u_int32_t       datasector[2];
00134         u_int32_t       filesize[2];
00135         u_int8_t        year;
00136         u_int8_t        month;
00137         u_int8_t        day;
00138         u_int8_t        hour;
00139         u_int8_t        minute;
00140         u_int8_t        second;
00141         u_int8_t        offset;
00142         u_int8_t        flags;
00143         u_int8_t        interleaved;
00144         u_int8_t        interleavegap;
00145         u_int16_t       sequence[2];
00146         u_int8_t        namelen;
00147         char            name[NAMELEN];
00148 };
00149 
00150 /*  *** program (memory) data structures ********************* */
00151 
00152 struct node {
00153         char name[NAMELEN];
00154         int isdir;
00155         int pathtablerecord;
00156         struct node *firstchild, *nextchild;
00157 
00158         /* filled out at i/o time */
00159         u_int32_t startsector, bytesize;
00160 };
00161 
00162 int n_reserved_pathtableentries = 0, n_used_pathtableentries = 0;
00163 int harddisk_emulation = 0;
00164 int system_type = 0;
00165 
00166 int get_system_type(int fd);
00167 
00168 ssize_t
00169 Write(int fd, void *buf, ssize_t len)
00170 {
00171         ssize_t r;
00172         if((r=write(fd, buf, len)) != len) {
00173                 if(r < 0) { perror("write"); }
00174                 fprintf(stderr, "failed or short write - aborting.\n");
00175                 exit(1);
00176         }
00177         return len;
00178 }
00179 
00180 off_t
00181 Lseek(int fd, off_t pos, int rel)
00182 {
00183         off_t r;
00184 
00185         if((r=lseek(fd, pos, rel)) < 0) {
00186                 perror("lseek");
00187                 fprintf(stderr, "lseek failed - aborting.\n");
00188                 exit(1);
00189         }
00190 
00191         return r;
00192 }
00193 
00194 void
00195 writesector(int fd, char *block, int *currentsector)
00196 {
00197         Write(fd, block, ISO_SECTOR);
00198         (*currentsector)++;
00199         return;
00200 }
00201 
00202 void
00203 seeksector(int fd, int sector, int *currentsector)
00204 {
00205         Lseek(fd, sector*ISO_SECTOR, SEEK_SET);
00206         *currentsector = sector;
00207 }
00208 
00209 void
00210 seekwritesector(int fd, int sector, char *block, int *currentsector)
00211 {
00212         seeksector(fd, sector, currentsector);
00213         writesector(fd, block, currentsector);
00214 }
00215 
00216 ssize_t
00217 Read(int fd, void *buf, ssize_t len)
00218 {
00219         ssize_t r;
00220         if((r=read(fd, buf, len)) != len) {
00221                 if(r < 0) { perror("read"); }
00222                 fprintf(stderr, "failed or short read.\n");
00223                 exit(1);
00224         }
00225 
00226         return len;
00227 }
00228 
00229 void both16(unsigned char *both, unsigned short i16)
00230 {
00231         unsigned char *little, *big;
00232 
00233         little = both;
00234         big = both + 2;
00235 
00236         little[0] = big[1] = i16        & 0xFF;
00237         little[1] = big[0] = (i16 >> 8) & 0xFF;
00238 }
00239 
00240 void both32(unsigned char *both, unsigned long i32)
00241 {
00242         unsigned char *little, *big;
00243 
00244         little = both;
00245         big = both + 4;
00246 
00247         little[0] = big[3] = i32         & 0xFF;
00248         little[1] = big[2] = (i32 >>  8) & 0xFF;
00249         little[2] = big[1] = (i32 >> 16) & 0xFF;
00250         little[3] = big[0] = (i32 >> 24) & 0xFF;
00251 }
00252 
00253 #define MINDIRLEN        1
00254 #define MAXDIRLEN       31
00255 
00256 #define MAXLEVEL 8
00257 
00258 static int cmpf(const void *v1, const void *v2)
00259 {
00260         struct node *n1, *n2;
00261         int i;
00262         char f1[NAMELEN], f2[NAMELEN];
00263 
00264         n1 = (struct node *) v1;
00265         n2 = (struct node *) v2;
00266         strcpy(f1, n1->name);
00267         strcpy(f2, n2->name);
00268         for(i = 0; i < strlen(f1); i++) f1[i] = toupper(f1[i]);
00269         for(i = 0; i < strlen(f2); i++) f2[i] = toupper(f2[i]);
00270 
00271 
00272         return -strcmp(f1, f2);
00273 }
00274 
00275 void
00276 maketree(struct node *thisdir, char *name, int level)
00277 {
00278         DIR *dir;
00279         struct dirent *e;
00280         struct node *dirnodes = NULL;
00281         int reserved_dirnodes = 0, used_dirnodes = 0;
00282         struct node *child;
00283 
00284         thisdir->firstchild = NULL;
00285         thisdir->isdir = 1;
00286         thisdir->startsector = 0xdeadbeef;
00287 
00288         if(level >= MAXLEVEL) {
00289                 fprintf(stderr, "ignoring entries in %s (too deep for iso9660)\n",
00290                         name);
00291                 return;
00292         }
00293 
00294         if(!(dir = opendir(CURRENTDIR))) {
00295                 perror("opendir");
00296                 return;
00297         }
00298 
00299         /* how many entries do we need to allocate? */
00300         while(readdir(dir)) reserved_dirnodes++;
00301         if(!reserved_dirnodes) {
00302                 closedir(dir);
00303                 return;
00304         }
00305 
00306         if(!(dirnodes = malloc(sizeof(*dirnodes)*reserved_dirnodes))) {
00307                 fprintf(stderr, "couldn't allocate dirnodes (%d bytes)\n",
00308                         sizeof(*dirnodes)*reserved_dirnodes);
00309                 exit(1);
00310         }
00311 
00312 
00313         /* remember all entries in this dir */
00314         rewinddir(dir);
00315 
00316         child = dirnodes;
00317         while((e=readdir(dir))) {
00318                 struct stat st;
00319                 mode_t type;
00320                 if(!strcmp(e->d_name, CURRENTDIR) || !strcmp(e->d_name, PARENTDIR))
00321                         continue;
00322                 if(stat(e->d_name, &st) < 0) {
00323                         perror(e->d_name);
00324                         fprintf(stderr, "failed to stat file/dir\n");
00325                         exit(1);
00326                 }
00327 
00328                 type = st.st_mode & S_IFMT;
00329 
00330 /*
00331                 printf("%s type: %x dir: %x file: %x\n",
00332                         e->d_name, type, S_IFDIR, S_IFREG);
00333                         */
00334                 if(type != S_IFDIR && type != S_IFREG)
00335                         continue;
00336 
00337                 used_dirnodes++;
00338                 if(used_dirnodes > reserved_dirnodes) {
00339                         fprintf(stderr, "huh, directory entries appeared "
00340         "(not enough pre-allocated nodes; this can't happen) ?\n");
00341                         exit(1);
00342                 }
00343 
00344                 if(type == S_IFDIR) {
00345                         child->isdir = 1;
00346                 } else {
00347                         child->isdir = 0;
00348                         child->firstchild = NULL;
00349                 }
00350                 strncpy(child->name, e->d_name, sizeof(child->name));
00351 
00352                 child++;
00353         }
00354 
00355         closedir(dir);
00356 
00357         if(!used_dirnodes)
00358                 return;
00359 
00360         if(!(dirnodes=realloc(dirnodes, used_dirnodes*sizeof(*dirnodes)))) {
00361                 fprintf(stderr, "realloc() of dirnodes failed - aborting\n");
00362                 exit(1);
00363         }
00364 
00365         qsort(dirnodes, used_dirnodes, sizeof(*dirnodes), cmpf);
00366 
00367         child = dirnodes;
00368 
00369         while(used_dirnodes--) {
00370                 child->nextchild = thisdir->firstchild;
00371                 thisdir->firstchild = child;
00372                 if(child->isdir) {
00373                         if(chdir(child->name) < 0) {
00374                                 perror(child->name);
00375                         } else {
00376                                 maketree(child, child->name, level+1);
00377                                 if(chdir(PARENTDIR) < 0) {
00378                                         perror("chdir() failed");
00379                                         fprintf(stderr, "couldn't chdir() to parent, aborting\n");
00380                                         exit(1);
00381                                 }
00382                         }
00383                 }
00384 
00385                 child++;
00386         }
00387 
00388 }
00389 
00390 void
00391 little32(unsigned char *dest, u_int32_t src)
00392 {
00393         dest[0] = ((src >>  0) & 0xFF);
00394         dest[1] = ((src >>  8) & 0xFF);
00395         dest[2] = ((src >> 16) & 0xFF);
00396         dest[3] = ((src >> 24) & 0xFF);
00397 
00398         return;
00399 }
00400 
00401 void
00402 little16(unsigned char *dest, u_int16_t src)
00403 {
00404         dest[0] = ((src >>  0) & 0xFF);
00405         dest[1] = ((src >>  8) & 0xFF);
00406 
00407         return;
00408 }
00409 
00410 void
00411 big32(unsigned char *dest, u_int32_t src)
00412 {
00413         dest[3] = ((src >>  0) & 0xFF);
00414         dest[2] = ((src >>  8) & 0xFF);
00415         dest[1] = ((src >> 16) & 0xFF);
00416         dest[0] = ((src >> 24) & 0xFF);
00417         return;
00418 }
00419 
00420 void
00421 big16(unsigned char *dest, u_int16_t src)
00422 {
00423         dest[1] = ((src >>  0) & 0xFF);
00424         dest[0] = ((src >>  8) & 0xFF);
00425         return;
00426 }
00427 
00428 
00429 void
00430 traversetree(struct node *root, int level, int littleendian,
00431         int maxlevel, int *bytes, int fd, int parentrecord, int *recordno)
00432 {
00433         struct node *child;
00434         struct pte {
00435                 u_int8_t len;
00436                 u_int8_t zero;
00437                 u_int32_t startsector;
00438                 u_int16_t parent;
00439         } pte;
00440 
00441         if(level == maxlevel) {
00442                 int i;
00443                 char newname[NAMELEN];
00444                 if(!root->isdir)
00445                         return;
00446                 pte.zero = 0;
00447                 if(level == 1) {
00448                         /* root */
00449                         pte.len = 1;
00450                         pte.parent = 1;
00451                         root->name[0] = root->name[1] = '\0';
00452                 } else {
00453                         pte.len = strlen(root->name);
00454                         pte.parent = parentrecord;
00455                 }
00456                 pte.startsector = root->startsector;
00457                 root->pathtablerecord = (*recordno)++;
00458 
00459                 if(littleendian) {
00460                         little32((unsigned char *) &pte.startsector, pte.startsector);
00461                         little16((unsigned char *) &pte.parent, pte.parent);
00462                 } else {
00463                         big32((unsigned char *) &pte.startsector, pte.startsector);
00464                         big16((unsigned char *) &pte.parent, pte.parent);
00465                 }
00466 
00467                 *bytes += Write(fd, &pte.len, sizeof(pte.len));
00468                 *bytes += Write(fd, &pte.zero, sizeof(pte.zero));
00469                 *bytes += Write(fd, &pte.startsector, sizeof(pte.startsector));
00470                 *bytes += Write(fd, &pte.parent, sizeof(pte.parent));
00471                 if(!(pte.len%2))
00472                         root->name[pte.len++] = '\0';
00473                 for(i = 0; i < pte.len; i++)
00474                         newname[i] = toupper(root->name[i]);
00475                 *bytes += Write(fd, newname, pte.len);
00476                 return;
00477         }
00478 
00479         for(child = root->firstchild; child; child = child->nextchild)
00480                 if(child->isdir)
00481                         traversetree(child, level+1, littleendian,
00482                                 maxlevel, bytes, fd, root->pathtablerecord,
00483                                         recordno);
00484 
00485         return;
00486 }
00487 
00488 int
00489 makepathtables(struct node *root, int littleendian, int *bytes, int fd)
00490 {
00491         int level;
00492         static char block[ISO_SECTOR];
00493         int recordno;
00494 
00495         recordno = 1;
00496 
00497         *bytes = 0;
00498 
00499         for(level = 1; level <= MAXLEVEL; level++)
00500                 traversetree(root, 1, littleendian, level, bytes, fd, 1, &recordno);
00501 
00502         if(*bytes % ISO_SECTOR) {
00503                 ssize_t x;
00504                 x = ISO_SECTOR-(*bytes % ISO_SECTOR);
00505                 write(fd, block, x);
00506                 *bytes += x;
00507         }
00508 
00509         return *bytes/ISO_SECTOR;
00510 }
00511 
00512 ssize_t
00513 write_direntry(char *origname, u_int32_t sector, u_int32_t size, int isdir,
00514         int fd)
00515 {
00516         int namelen, total = 0;
00517         struct dir entry;
00518         char copyname[NAMELEN];
00519 
00520         memset(&entry, 0, sizeof(entry));
00521 
00522         if(!strcmp(origname, CURRENTDIR)) {
00523                 namelen = 1;
00524         } else if(!strcmp(origname, PARENTDIR)) {
00525                 entry.name[0] = '\001';
00526                 namelen = 1;
00527         } else {
00528                 int i;
00529                 strcpy(copyname, origname);
00530                 namelen = strlen(copyname);
00531 
00532                 if(namelen > ISONAMELEN) {
00533                         fprintf(stderr, "%s: truncated, too long for iso9660\n", copyname);
00534                         namelen = ISONAMELEN;
00535                         copyname[namelen] = '\0';
00536                 }
00537 
00538                 strcpy(entry.name, copyname);
00539                 for(i = 0; i < namelen; i++)
00540                         entry.name[i] = toupper(entry.name[i]);
00541 
00542                 /* padding byte + system field */
00543                 entry.name[namelen]   = '\0';
00544                 entry.name[namelen+1] = '\0';
00545                 entry.name[namelen+2] = '\0';
00546         }
00547         entry.namelen = namelen;        /* original length */
00548         if(!(namelen%2)) namelen++;     /* length with padding byte */
00549 
00550 
00551         /* XXX 2 extra bytes for 'system use'.. */
00552         entry.recordsize = 33 + namelen;
00553         both32((unsigned char *) entry.datasector, sector);
00554         both32((unsigned char *) entry.filesize, size);
00555 
00556         if(isdir) entry.flags = FLAG_DIR;
00557 
00558         /* XXX node date */
00559 
00560         both16((unsigned char *) entry.sequence, 1);
00561         
00562          total = Write(fd, &entry.recordsize, sizeof(entry.recordsize));
00563          total += Write(fd, &entry.extended, sizeof(entry.extended));
00564          total += Write(fd, entry.datasector, sizeof(entry.datasector));
00565          total += Write(fd, entry.filesize, sizeof(entry.filesize));
00566          total += Write(fd, &entry.year, sizeof(entry.year));
00567          total += Write(fd, &entry.month, sizeof(entry.month));
00568          total += Write(fd, &entry.day, sizeof(entry.day));
00569          total += Write(fd, &entry.hour, sizeof(entry.hour));
00570          total += Write(fd, &entry.minute, sizeof(entry.minute));
00571          total += Write(fd, &entry.second, sizeof(entry.second));
00572          total += Write(fd, &entry.offset, sizeof(entry.offset));
00573          total += Write(fd, &entry.flags, sizeof(entry.flags));
00574          total += Write(fd, &entry.interleaved, sizeof(entry.interleaved));
00575          total += Write(fd, &entry.interleavegap, sizeof(entry.interleavegap));
00576          total += Write(fd, entry.sequence, sizeof(entry.sequence));
00577          total += Write(fd, &entry.namelen, sizeof(entry.namelen));
00578          total += Write(fd, entry.name, namelen);
00579 
00580         if(total != entry.recordsize || (total % 2) != 0) {
00581                 printf("%2d, %2d!  ", total, entry.recordsize);
00582                 printf("%3d = %3d - %2d + %2d\n",
00583                 entry.recordsize, sizeof(entry), sizeof(entry.name), namelen);
00584         }
00585 
00586         return entry.recordsize;
00587 }
00588 
00589 void
00590 writedata(struct node *parent, struct node *root,
00591         int fd, int *currentsector, int dirs, struct dir *rootentry,
00592         int rootsize, int remove_after)
00593 {
00594         static char buf[1024*1024];
00595         struct node *c;
00596         ssize_t written = 0, rest;
00597 
00598         for(c = root->firstchild; c; c = c->nextchild) {
00599                 if(c->isdir && chdir(c->name) < 0) {
00600                         perror(c->name);
00601                         fprintf(stderr, "couldn't chdir to %s - aborting\n",
00602                                 c->name);
00603                         exit(1);
00604                 }
00605                 writedata(root, c, fd, currentsector, dirs, rootentry, rootsize, remove_after);
00606                 if(c->isdir && chdir(PARENTDIR) < 0) {
00607                         perror("chdir to ..");
00608                         fprintf(stderr, "couldn't chdir to parent - "
00609                                 "aborting\n");
00610                         exit(1);
00611                 }
00612         }
00613 
00614         /* write nodes depth-first, down-top */
00615 
00616         if(root->isdir && dirs) {
00617                 /* dir */
00618                 written = 0;
00619                 root->startsector = *currentsector;
00620                 written += write_direntry(CURRENTDIR, root->startsector,
00621                         root->bytesize, root->isdir, fd);
00622                 if(parent) {
00623                         written += write_direntry(PARENTDIR, parent->startsector,
00624                                 root->bytesize, root->isdir, fd);
00625                 } else {
00626                         written += write_direntry(PARENTDIR, root->startsector,
00627                                 root->bytesize, root->isdir, fd);
00628                 }
00629                 for(c = root->firstchild; c; c = c->nextchild) {
00630                         off_t cur1, cur2;
00631                         ssize_t written_before;
00632                         cur1 = Lseek(fd, 0, SEEK_CUR);
00633                         written_before = written;
00634                         written += write_direntry(c->name,
00635                                 c->startsector, c->bytesize, c->isdir, fd);
00636                         cur2 = Lseek(fd, 0, SEEK_CUR);
00637                         if(cur1/ISO_SECTOR != (cur2-1)/ISO_SECTOR) {
00638                                 /* passed a sector boundary, argh! */
00639                                 Lseek(fd, cur1, SEEK_SET);
00640                                 written = written_before;
00641                                 rest=(ISO_SECTOR-(written % ISO_SECTOR));
00642                                 memset(buf, 0, rest);
00643                                 Write(fd, buf, rest);
00644                                 written += rest;
00645                                 written += write_direntry(c->name,
00646                                   c->startsector, c->bytesize, c->isdir, fd);
00647                         }
00648                 }
00649                 root->bytesize = written;
00650         } else if(!root->isdir && !dirs) {
00651                 /* file */
00652                 struct stat st;
00653                 ssize_t rem;
00654                 int filefd;
00655                 
00656                 if(stat(root->name, &st) < 0) {
00657                         perror(root->name);
00658                         fprintf(stderr, "couldn't stat %s - aborting\n", root->name);
00659                         exit(1);
00660                 }
00661 
00662                 if((filefd = open(root->name, O_RDONLY)) < 0) {
00663                         perror(root->name);
00664                         fprintf(stderr, "couldn't open %s - aborting\n", root->name);
00665                         exit(1);
00666                 }
00667 
00668                 rem = st.st_size;
00669 
00670                 root->startsector = *currentsector;
00671 
00672                 while(rem > 0) {
00673                         ssize_t chunk;
00674                         chunk = min(sizeof(buf), rem);
00675                         Read(filefd, buf, chunk);
00676                         Write(fd, buf, chunk);
00677                         rem -= chunk;
00678                 }
00679 
00680                 close(filefd);
00681 
00682                 root->bytesize = written = st.st_size;
00683                 if(remove_after && unlink(root->name) < 0) {
00684                         perror("unlink");
00685                         fprintf(stderr, "couldn't remove %s\n", root->name);
00686                 }
00687         } else { 
00688                 /* nothing to be done */
00689                 return;
00690         }
00691 
00692         /* fill out sector with zero bytes */
00693 
00694         if((rest=(ISO_SECTOR-(written % ISO_SECTOR)))) {
00695                 memset(buf, 0, rest);
00696                 Write(fd, buf, rest);
00697                 written += rest;
00698         }
00699 
00700         /* update dir size with padded size */
00701 
00702         if(root->isdir) { root->bytesize = written; }
00703 
00704         *currentsector += written/ISO_SECTOR;
00705 }
00706 
00707 void
00708 writebootcatalog(int fd, int  *currentsector, int imagesector, int imagesectors)
00709 {
00710         static char buf[ISO_SECTOR];
00711         struct bc_validation validate;
00712         struct bc_initial initial;
00713 
00714         ssize_t written, rest;
00715         u_int16_t *v, sum = 0;
00716         int i;
00717 
00718         /* write validation entry */
00719         
00720         memset(&validate, 0, sizeof(validate));
00721         validate.headerid = 1;
00722         validate.platform = PLATFORM_80X86;
00723         strcpy(validate.idstring, "");
00724         validate.keys[0] = 0x55;
00725         validate.keys[1] = 0xaa;
00726 
00727         v = (u_int16_t *) &validate; 
00728         for(i = 0; i < sizeof(validate)/2; i++)
00729                 sum += v[i];
00730         validate.checksum = 65535 - sum + 1; /* sum must be 0 */
00731 
00732         written = Write(fd, &validate, sizeof(validate));
00733 
00734         /* write initial/default entry */
00735 
00736         memset(&initial, 0, sizeof(initial));
00737 
00738         initial.indicator = INDICATE_BOOTABLE;
00739         if (harddisk_emulation)
00740         {
00741                 initial.media = BOOTMEDIA_HARDDISK;
00742                 initial.type = system_type;
00743         }
00744         else
00745                 initial.media = BOOTMEDIA_144M;
00746         /* initial.sectors = imagesectors; */
00747         initial.sectors = 1;
00748         initial.startsector = imagesector;
00749 
00750         written += Write(fd, &initial, sizeof(initial));
00751 
00752         /* fill out the rest of the sector with 0's */
00753 
00754         if((rest = ISO_SECTOR - (written % 2048))) {
00755                 memset(buf, 0, sizeof(buf));
00756                 written += Write(fd, buf, rest);
00757         }
00758 
00759         (*currentsector) += written / ISO_SECTOR;
00760 
00761         return;
00762 }
00763 
00764 int
00765 writebootimage(char *bootimage, int bootfd, int fd, int *currentsector)
00766 {
00767         static char buf[1024*64];
00768         ssize_t chunk, written = 0, rest;
00769         int virtuals;
00770 
00771         while((chunk=read(bootfd, buf, sizeof(buf))) > 0)
00772                 written += Write(fd, buf, chunk);
00773 
00774         if(chunk < 0) {
00775                 perror("read boot image");
00776                 exit(1);
00777         }
00778 
00779         virtuals = written / VIRTUAL_SECTOR;
00780 
00781         if((rest = ISO_SECTOR - (written % 2048))) {
00782                 memset(buf, 0, sizeof(buf));
00783                 written += Write(fd, buf, rest);
00784         }
00785 
00786         (*currentsector) += written/ISO_SECTOR;
00787 
00788         return virtuals;
00789 }
00790 
00791 void
00792 writebootrecord(int fd, int *currentsector, int bootcatalogsector)
00793 {
00794         int i;
00795         static struct bootrecord bootrecord;
00796         ssize_t w = 0;
00797         /* boot record volume descriptor */
00798 
00799         memset(&bootrecord, 0, sizeof(bootrecord));
00800         bootrecord.set[0] = 'C';
00801         bootrecord.set[1] = 'D';
00802         bootrecord.set[2] = '0';
00803         bootrecord.set[3] = '0';
00804         bootrecord.set[4] = '1';
00805         bootrecord.version = 1;
00806         bootrecord.bootcatalog = bootcatalogsector;
00807         strcpy(bootrecord.ident, "EL TORITO SPECIFICATION");
00808         for(i = strlen(bootrecord.ident);
00809                 i < sizeof(bootrecord.ident); i++)
00810                 bootrecord.ident[i] = '\0';
00811 
00812         w  = Writefield(fd, bootrecord.indicator);
00813         w += Writefield(fd, bootrecord.set);
00814         w += Writefield(fd, bootrecord.version);
00815         w += Writefield(fd, bootrecord.ident);
00816         w += Writefield(fd, bootrecord.zero);
00817         w += Writefield(fd, bootrecord.bootcatalog);
00818         w += Writefield(fd, bootrecord.zero2);
00819 
00820         if(w != ISO_SECTOR) {
00821                 fprintf(stderr, "WARNING: something went wrong - boot record (%d) isn't a sector size (%d)\n",
00822                         w, ISO_SECTOR);
00823         }
00824 
00825         (*currentsector)++;
00826 }
00827 
00828 int
00829 main(int argc, char *argv[])
00830 {
00831         int currentsector = 0;
00832         int imagesector, imagesectors;
00833         int bootfd, fd, i, ch, nsectors;
00834         int remove_after = 0;
00835         static char block[ISO_SECTOR];
00836         static struct pvd pvd;
00837         char *label = "ISO9660";
00838         struct tm *now;
00839         time_t nowtime;
00840         char timestr[20], *prog;
00841         char *bootimage = NULL;
00842         struct node root;
00843         int pvdsector;
00844         int bigpath, littlepath, pathbytes = 0, dirsector, filesector, enddir;
00845         int bootvolumesector, bootcatalogsector;
00846 
00847         prog = argv[0];
00848 
00849         /* This check is to prevent compiler padding screwing up
00850          * our format.
00851          */
00852 
00853         if(sizeof(struct pvd) != ISO_SECTOR) {
00854                 fprintf(stderr, "Something confusing happened at\n"
00855                         "compile-time; pvd should be a sector size. %d != %d\n",
00856                         sizeof(struct pvd), ISO_SECTOR);
00857                 return 1;
00858         }
00859 
00860         while ((ch = getopt(argc, argv, "Rb:hl:")) != -1) {
00861                 switch(ch) {
00862                         case 'h':
00863                                 harddisk_emulation= 1;
00864                                 break;
00865                         case 'l':
00866                                 label = optarg;
00867                                 break;
00868                         case 'r':
00869                                 remove_after = 1;
00870                                 break;
00871                         case 'b':
00872                                 bootimage = optarg;
00873                                 if((bootfd = open(bootimage, O_RDONLY)) < 0) {
00874                                         perror(bootimage);
00875                                         return 1;
00876                                 }
00877                                 break;
00878                 }
00879         }
00880 
00881         argc -= optind;
00882         argv += optind;
00883 
00884         if(argc != 2) {
00885                 fprintf(stderr, "usage: %s [-l <label>] [-b <bootfloppyimage>] <dir> <isofile>\n",
00886                         prog);
00887                 return 1;
00888         }
00889 
00890         /* create .iso file */
00891 
00892         if((fd=open(argv[1], O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0) {
00893                 perror(argv[1]);
00894                 return 1;
00895         }
00896 
00897         /* go to where the iso has to be made from */
00898 
00899         if(chdir(argv[0]) < 0) {
00900                 perror(argv[0]);
00901                 return 1;
00902         }
00903 
00904         /* collect dirs and files */
00905 
00906         fprintf(stderr, " * traversing input tree\n");
00907 
00908         maketree(&root, "", 1);
00909 
00910         fprintf(stderr, " * writing initial zeroes and pvd\n");
00911 
00912         /* first sixteen sectors are zero */
00913 
00914         memset(block, 0, sizeof(block));
00915 
00916         for(i = 0; i < 16; i++)
00917                 writesector(fd, block, &currentsector);
00918 
00919         /* Primary Volume Descriptor */
00920         memset(&pvd, 0, sizeof(pvd));
00921         pvd.one = 1;
00922         pvd.set[0] = 67;
00923         pvd.set[1] = 68;
00924         pvd.set[2] = 48;
00925         pvd.set[3] = 48;
00926         pvd.set[4] = 49;
00927         pvd.set[5] =  1;
00928         pvd.set[5] =  1;
00929 
00930         strncpy(pvd.volume, label, sizeof(pvd.volume)-1);
00931         for(i = strlen(pvd.volume); i < sizeof(pvd.volume); i++)
00932                 pvd.volume[i] = ' ';
00933         for(i = 0; i < sizeof(pvd.system); i++)
00934                 pvd.system[i] = ' ';
00935 
00936         both16((unsigned char *) pvd.setsize, 1);
00937         both16((unsigned char *) pvd.seq, 1);
00938         both16((unsigned char *) pvd.sectorsize, ISO_SECTOR);
00939 
00940         /* fill time fields */
00941         time(&nowtime);
00942         now = gmtime(&nowtime);
00943         strftime(timestr, sizeof(timestr), "%Y%m%d%H%M%S000", now);
00944         memcpy(pvd.create, timestr, strlen(timestr));
00945         memcpy(pvd.modified, timestr, strlen(timestr));
00946         memcpy(pvd.effective, timestr, strlen(timestr));
00947         strcpy(pvd.expiry, "0000000000000000"); /* not specified */
00948         pvdsector = currentsector;
00949 
00950         writesector(fd, (char *) &pvd, &currentsector);
00951 
00952         if(bootimage) {
00953                 fprintf(stderr, " * writing boot record volume descriptor\n");
00954                 bootvolumesector = currentsector;
00955                 writebootrecord(fd, &currentsector, 0);
00956         }
00957 
00958         /* volume descriptor set terminator */
00959         memset(block, 0, sizeof(block));
00960         block[0] = 255;
00961         block[1] =  67;
00962         block[2] =  68;
00963         block[3] =  48;
00964         block[4] =  48;
00965         block[5] =  49;
00966         block[6] =   1;
00967 
00968         writesector(fd, block, &currentsector);
00969 
00970         if(bootimage) {
00971                 /* write the boot catalog */
00972                 fprintf(stderr, " * writing the boot catalog\n");
00973                 bootcatalogsector = currentsector;
00974                 if (harddisk_emulation)
00975                         system_type = get_system_type(bootfd);
00976                 writebootcatalog(fd, &currentsector, imagesector, imagesectors);
00977 
00978                 /* write boot image */
00979                 fprintf(stderr, " * writing the boot image\n");
00980                 imagesector = currentsector;
00981                 imagesectors = writebootimage(bootimage, bootfd,
00982                         fd, &currentsector);
00983                 fprintf(stderr, " * image: %d virtual sectors @ sector 0x%x\n",
00984                         imagesectors, imagesector);
00985 
00986                 close(bootfd);
00987         }
00988 
00989         /* write out all the file data */
00990 
00991         filesector = currentsector;
00992         fprintf(stderr, " * writing file data\n");
00993         writedata(NULL, &root, fd, &currentsector, 0,
00994                 (struct dir *) &pvd.rootrecord, sizeof(pvd.rootrecord),
00995                 remove_after);
00996 
00997         /* write out all the dir data */
00998 
00999         dirsector = currentsector;
01000         fprintf(stderr, " * writing dir data\n");
01001         writedata(NULL, &root, fd, &currentsector, 1,
01002                 (struct dir *) &pvd.rootrecord, sizeof(pvd.rootrecord),
01003                         remove_after);
01004         enddir = currentsector;
01005         seeksector(fd, dirsector, &currentsector);
01006         fprintf(stderr, " * rewriting dir data\n");
01007         fflush(NULL);
01008         writedata(NULL, &root, fd, &currentsector, 1,
01009                 (struct dir *) &pvd.rootrecord, sizeof(pvd.rootrecord),
01010                         remove_after);
01011         if(currentsector != enddir) {
01012                 fprintf(stderr, "warning: inconsistent directories - "
01013                         "I have a bug! iso may be broken.\n");
01014         }
01015 
01016         /* now write the path table in both formats */
01017 
01018         fprintf(stderr, " * writing big-endian path table\n");
01019         bigpath = currentsector;
01020         currentsector += makepathtables(&root, 0, &pathbytes, fd);
01021 
01022         fprintf(stderr, " * writing little-endian path table\n");
01023         littlepath = currentsector;
01024         currentsector += makepathtables(&root, 1, &pathbytes, fd);
01025 
01026         /* this is the size of the iso filesystem for use in the pvd later */
01027 
01028         nsectors = currentsector;
01029         both32((unsigned char *) pvd.sectors, nsectors);
01030 
01031         /* *********** Filesystem writing done ************************* */
01032 
01033         /* finish and rewrite the pvd. */
01034         fprintf(stderr, " * rewriting pvd\n");
01035         seekwritesector(fd, pvdsector, (char *) &pvd, &currentsector);
01036 
01037         both32((unsigned char *) pvd.pathtable, pathbytes);
01038         little32((unsigned char *) &pvd.first_little_pathtable_start, littlepath);
01039         little32((unsigned char *) &pvd.first_big_pathtable_start, bigpath);
01040 
01041         /* write root dir entry in pvd */
01042         seeksector(fd, pvdsector, &currentsector);
01043         Lseek(fd, (int)((char *) &pvd.rootrecord - (char *) &pvd), SEEK_CUR);
01044         if(write_direntry(CURRENTDIR, root.startsector, root.bytesize,
01045                 root.isdir, fd) > sizeof(pvd.rootrecord)) {
01046                 fprintf(stderr, "warning: unexpectedly large root record\n");
01047         }
01048 
01049         if(bootimage) {
01050                 fprintf(stderr, " * rewriting boot catalog\n");
01051                 seeksector(fd, bootcatalogsector, &currentsector);
01052                 writebootcatalog(fd, &currentsector, imagesector, imagesectors);
01053 
01054                 /* finish and rewrite the boot record volume descriptor */
01055                 fprintf(stderr, " * rewriting the boot rvd\n");
01056                 seeksector(fd, bootvolumesector, &currentsector);
01057                 writebootrecord(fd, &currentsector, bootcatalogsector);
01058         }
01059 
01060         fprintf(stderr, " * all ok\n");
01061 
01062         return 0;
01063 }
01064 
01065 int get_system_type(int fd)
01066 {
01067         off_t old_pos;
01068         size_t size;
01069         ssize_t r;
01070         int type;
01071         struct part_entry *partp;
01072         unsigned char bootsector[512];
01073 
01074         errno= 0;
01075         old_pos= lseek(fd, SEEK_SET, 0);
01076         if (old_pos == -1 && errno != 0)
01077         {
01078                 fprintf(stderr, "bootimage file is not seekable: %s\n",
01079                         strerror(errno));
01080                 exit(1);
01081         }
01082         size= sizeof(bootsector);
01083         r= read(fd, bootsector, size);
01084         if (r != size)
01085         {
01086                 fprintf(stderr, "error reading bootimage file: %s\n",
01087                         r < 0 ? strerror(errno) : "unexpected EOF");
01088                 exit(1);
01089         }
01090         if (bootsector[size-2] != 0x55 && bootsector[size-1] != 0xAA)
01091         {
01092                 fprintf(stderr, "bad magic in bootimage file\n");
01093                 exit(1);
01094         }
01095 
01096         partp= (struct part_entry *)&bootsector[PART_TABLE_OFF];
01097         type= partp->sysind;
01098         if (type == NO_PART)
01099         {
01100                 fprintf(stderr, "first partition table entry is unused\n");
01101                 exit(1);
01102         }
01103         if (!(partp->bootind & ACTIVE_FLAG))
01104         {
01105                 fprintf(stderr, "first partition table entry is not active\n");
01106                 exit(1);
01107         }
01108 
01109         lseek(fd, SEEK_SET, old_pos);
01110         return type;
01111 }

Generated on Fri Apr 14 22:57:13 2006 for minix by  doxygen 1.4.6