1 2 /* writeisofs - simple ISO9660-format-image writing utility */ 3 4 #if HAVE_NBTOOL_CONFIG_H 5 #include "nbtool_config.h" 6 #endif 7 8 #include <errno.h> 9 #include <stdio.h> 10 #include <time.h> 11 #include <stdlib.h> 12 #include <fcntl.h> 13 #include <string.h> 14 #include <unistd.h> 15 #include <dirent.h> 16 #include <assert.h> 17 #include <ctype.h> 18 #include <partition.h> 19 20 #include <sys/stat.h> 21 22 #define Writefield(fd, f) Write(fd, &(f), sizeof(f)) 23 24 extern char *optarg; 25 extern int optind; 26 27 #ifndef min 28 #define min(a,b) ((a) < (b) ? (a) : (b)) 29 #endif 30 31 #define FLAG_DIR 2 32 33 #include <sys/types.h> 34 #include <sys/stat.h> 35 36 #define NAMELEN 500 37 #define ISONAMELEN 12 /* XXX could easily be 31 */ 38 #define PLATFORM_80X86 0 39 40 #define ISO_SECTOR 2048 41 #define VIRTUAL_SECTOR 512 42 #define ROUNDUP(v, n) (((v)+(n)-1)/(n)) 43 44 #define CURRENTDIR "." 45 #define PARENTDIR ".." 46 47 /* *** CD (disk) data structures ********************* */ 48 49 /* primary volume descriptor */ 50 51 struct pvd { 52 u_int8_t one; 53 char set[6]; 54 u_int8_t zero; 55 char system[32]; 56 char volume[32]; 57 u_int8_t zeroes1[8]; 58 u_int32_t sectors[2]; 59 u_int8_t zeroes2[32]; 60 u_int16_t setsize[2]; 61 u_int16_t seq[2]; 62 u_int16_t sectorsize[2]; 63 u_int32_t pathtablesize[2]; 64 u_int32_t first_little_pathtable_start; 65 u_int32_t second_little_pathtable_start; 66 u_int32_t first_big_pathtable_start; 67 u_int32_t second_big_pathtable_start; 68 u_int8_t rootrecord[34]; 69 u_int8_t volumeset[128]; 70 u_int8_t publisher[128]; 71 u_int8_t preparer[128]; 72 u_int8_t application[128]; 73 u_int8_t copyrightfile[37]; 74 u_int8_t abstractfile[37]; 75 u_int8_t bibliofile[37]; 76 u_int8_t create[17]; 77 u_int8_t modified[17]; 78 char expiry[17]; 79 u_int8_t effective[17]; 80 u_int8_t one2; 81 u_int8_t zero2; 82 u_int8_t zeroes3[512]; 83 u_int8_t zeroes4[653]; 84 }; 85 86 /* boot record volume descriptor */ 87 88 struct bootrecord { 89 u_int8_t indicator; /* 0 */ 90 char set[5]; /* "CD001" */ 91 u_int8_t version; /* 1 */ 92 char ident[32]; /* "EL TORITO SPECIFICATION" */ 93 u_int8_t zero[32]; /* unused, must be 0 */ 94 u_int32_t bootcatalog; /* starting sector of boot catalog */ 95 u_int8_t zero2[1973]; /* unused, must be 0 */ 96 }; 97 98 /* boot catalog validation entry */ 99 100 struct bc_validation { 101 u_int8_t headerid; /* 1 */ 102 u_int8_t platform; /* 0: 80x86; 1: powerpc; 2: mac */ 103 u_int8_t zero[2]; /* unused, must be 0 */ 104 char idstring[24]; /* id string */ 105 u_int16_t checksum; 106 u_int8_t keys[2]; /* 0x55AA */ 107 }; 108 109 /* boot catalog initial/default entry */ 110 111 #define INDICATE_BOOTABLE 0x88 112 113 #define BOOTMEDIA_UNSPECIFIED -1 114 #define BOOTMEDIA_NONE 0 115 #define BOOTMEDIA_1200K 1 116 #define BOOTMEDIA_1440K 2 117 #define BOOTMEDIA_2880K 3 118 #define BOOTMEDIA_HARDDISK 4 119 120 struct bc_initial { 121 u_int8_t indicator; /* INDICATE_BOOTABLE */ 122 u_int8_t media; /* BOOTMEDIA_* */ 123 u_int16_t seg; /* load segment or 0 for default */ 124 u_int8_t type; /* system type (from part. table) */ 125 u_int8_t zero; 126 u_int16_t sectors; 127 u_int32_t startsector; 128 u_int8_t zero2[20]; 129 }; 130 131 /* directory entry */ 132 133 struct dir { 134 u_int8_t recordsize; 135 u_int8_t extended; 136 u_int32_t datasector[2]; 137 u_int32_t filesize[2]; 138 u_int8_t year; 139 u_int8_t month; 140 u_int8_t day; 141 u_int8_t hour; 142 u_int8_t minute; 143 u_int8_t second; 144 u_int8_t offset; 145 u_int8_t flags; 146 u_int8_t interleaved; 147 u_int8_t interleavegap; 148 u_int16_t sequence[2]; 149 u_int8_t namelen; 150 char name[NAMELEN]; 151 }; 152 153 /* *** program (memory) data structures ********************* */ 154 155 struct node { 156 char name[NAMELEN]; 157 time_t timestamp; 158 int isdir; 159 int pathtablerecord; 160 struct node *firstchild, *nextchild; 161 162 /* filled out at i/o time */ 163 u_int32_t startsector, bytesize; 164 }; 165 166 int n_reserved_pathtableentries = 0, n_used_pathtableentries = 0; 167 int bootmedia = BOOTMEDIA_UNSPECIFIED; 168 unsigned long bootseg = 0; 169 int system_type = 0; 170 171 int get_system_type(int fd); 172 173 ssize_t 174 Write(int fd, void *buf, ssize_t len) 175 { 176 ssize_t r; 177 if((r=write(fd, buf, len)) != len) { 178 if(r < 0) { perror("write"); } 179 fprintf(stderr, "failed or short write - aborting.\n"); 180 exit(1); 181 } 182 return len; 183 } 184 185 off_t 186 Lseek(int fd, off_t pos, int rel) 187 { 188 off_t r; 189 190 if((r=lseek(fd, pos, rel)) < 0) { 191 perror("lseek"); 192 fprintf(stderr, "lseek failed - aborting.\n"); 193 exit(1); 194 } 195 196 return r; 197 } 198 199 void 200 writesector(int fd, char *block, int *currentsector) 201 { 202 Write(fd, block, ISO_SECTOR); 203 (*currentsector)++; 204 return; 205 } 206 207 void 208 seeksector(int fd, int sector, int *currentsector) 209 { 210 Lseek(fd, sector*ISO_SECTOR, SEEK_SET); 211 *currentsector = sector; 212 } 213 214 void 215 seekwritesector(int fd, int sector, char *block, int *currentsector) 216 { 217 seeksector(fd, sector, currentsector); 218 writesector(fd, block, currentsector); 219 } 220 221 ssize_t 222 Read(int fd, void *buf, ssize_t len) 223 { 224 ssize_t r; 225 if((r=read(fd, buf, len)) != len) { 226 if(r < 0) { perror("read"); } 227 fprintf(stderr, "failed or short read.\n"); 228 exit(1); 229 } 230 231 return len; 232 } 233 234 void both16(unsigned char *both, unsigned short i16) 235 { 236 unsigned char *little, *big; 237 238 little = both; 239 big = both + 2; 240 241 little[0] = big[1] = i16 & 0xFF; 242 little[1] = big[0] = (i16 >> 8) & 0xFF; 243 } 244 245 void both32(unsigned char *both, unsigned long i32) 246 { 247 unsigned char *little, *big; 248 249 little = both; 250 big = both + 4; 251 252 little[0] = big[3] = i32 & 0xFF; 253 little[1] = big[2] = (i32 >> 8) & 0xFF; 254 little[2] = big[1] = (i32 >> 16) & 0xFF; 255 little[3] = big[0] = (i32 >> 24) & 0xFF; 256 } 257 258 #define MINDIRLEN 1 259 #define MAXDIRLEN 31 260 261 #define MAXLEVEL 8 262 263 static int cmpf(const void *v1, const void *v2) 264 { 265 struct node *n1, *n2; 266 int i; 267 char f1[NAMELEN], f2[NAMELEN]; 268 269 n1 = (struct node *) v1; 270 n2 = (struct node *) v2; 271 strcpy(f1, n1->name); 272 strcpy(f2, n2->name); 273 for(i = 0; i < strlen(f1); i++) f1[i] = toupper(f1[i]); 274 for(i = 0; i < strlen(f2); i++) f2[i] = toupper(f2[i]); 275 276 277 return -strcmp(f1, f2); 278 } 279 280 void 281 maketree(struct node *thisdir, char *name, int level) 282 { 283 DIR *dir; 284 struct dirent *e; 285 struct node *dirnodes = NULL; 286 int reserved_dirnodes = 0, used_dirnodes = 0; 287 struct node *child; 288 289 thisdir->firstchild = NULL; 290 thisdir->isdir = 1; 291 thisdir->startsector = 0xdeadbeef; 292 293 if(level >= MAXLEVEL) { 294 fprintf(stderr, "ignoring entries in %s (too deep for iso9660)\n", 295 name); 296 return; 297 } 298 299 if(!(dir = opendir(CURRENTDIR))) { 300 perror("opendir"); 301 return; 302 } 303 304 /* how many entries do we need to allocate? */ 305 while(readdir(dir)) reserved_dirnodes++; 306 if(!reserved_dirnodes) { 307 closedir(dir); 308 return; 309 } 310 311 if(!(dirnodes = malloc(sizeof(*dirnodes)*reserved_dirnodes))) { 312 fprintf(stderr, "couldn't allocate dirnodes (%d bytes)\n", 313 sizeof(*dirnodes)*reserved_dirnodes); 314 exit(1); 315 } 316 317 318 /* remember all entries in this dir */ 319 rewinddir(dir); 320 321 child = dirnodes; 322 while((e=readdir(dir))) { 323 struct stat st; 324 mode_t type; 325 if(!strcmp(e->d_name, CURRENTDIR) || !strcmp(e->d_name, PARENTDIR)) 326 continue; 327 if(stat(e->d_name, &st) < 0) { 328 perror(e->d_name); 329 fprintf(stderr, "failed to stat file/dir\n"); 330 exit(1); 331 } 332 333 type = st.st_mode & S_IFMT; 334 335 /* 336 printf("%s type: %x dir: %x file: %x\n", 337 e->d_name, type, S_IFDIR, S_IFREG); 338 */ 339 if(type != S_IFDIR && type != S_IFREG) 340 continue; 341 342 used_dirnodes++; 343 if(used_dirnodes > reserved_dirnodes) { 344 fprintf(stderr, "huh, directory entries appeared " 345 "(not enough pre-allocated nodes; this can't happen) ?\n"); 346 exit(1); 347 } 348 349 if(type == S_IFDIR) { 350 child->isdir = 1; 351 } else { 352 child->isdir = 0; 353 child->firstchild = NULL; 354 } 355 strlcpy(child->name, e->d_name, sizeof(child->name)); 356 child->timestamp = st.st_mtime; 357 358 child++; 359 } 360 361 closedir(dir); 362 363 if(!used_dirnodes) 364 return; 365 366 if(!(dirnodes=realloc(dirnodes, used_dirnodes*sizeof(*dirnodes)))) { 367 fprintf(stderr, "realloc() of dirnodes failed - aborting\n"); 368 exit(1); 369 } 370 371 qsort(dirnodes, used_dirnodes, sizeof(*dirnodes), cmpf); 372 373 child = dirnodes; 374 375 while(used_dirnodes--) { 376 child->nextchild = thisdir->firstchild; 377 thisdir->firstchild = child; 378 if(child->isdir) { 379 if(chdir(child->name) < 0) { 380 perror(child->name); 381 } else { 382 maketree(child, child->name, level+1); 383 if(chdir(PARENTDIR) < 0) { 384 perror("chdir() failed"); 385 fprintf(stderr, "couldn't chdir() to parent, aborting\n"); 386 exit(1); 387 } 388 } 389 } 390 391 child++; 392 } 393 394 } 395 396 void 397 little32(unsigned char *dest, u_int32_t src) 398 { 399 dest[0] = ((src >> 0) & 0xFF); 400 dest[1] = ((src >> 8) & 0xFF); 401 dest[2] = ((src >> 16) & 0xFF); 402 dest[3] = ((src >> 24) & 0xFF); 403 404 return; 405 } 406 407 void 408 little16(unsigned char *dest, u_int16_t src) 409 { 410 dest[0] = ((src >> 0) & 0xFF); 411 dest[1] = ((src >> 8) & 0xFF); 412 413 return; 414 } 415 416 void 417 big32(unsigned char *dest, u_int32_t src) 418 { 419 dest[3] = ((src >> 0) & 0xFF); 420 dest[2] = ((src >> 8) & 0xFF); 421 dest[1] = ((src >> 16) & 0xFF); 422 dest[0] = ((src >> 24) & 0xFF); 423 return; 424 } 425 426 void 427 big16(unsigned char *dest, u_int16_t src) 428 { 429 dest[1] = ((src >> 0) & 0xFF); 430 dest[0] = ((src >> 8) & 0xFF); 431 return; 432 } 433 434 435 void 436 traversetree(struct node *root, int level, int littleendian, 437 int maxlevel, int *bytes, int fd, int parentrecord, int *recordno) 438 { 439 struct node *child; 440 struct pte { 441 u_int8_t len; 442 u_int8_t zero; 443 u_int32_t startsector; 444 u_int16_t parent; 445 } pte; 446 447 if(level == maxlevel) { 448 int i; 449 char newname[NAMELEN]; 450 if(!root->isdir) 451 return; 452 pte.zero = 0; 453 if(level == 1) { 454 /* root */ 455 pte.len = 1; 456 pte.parent = 1; 457 root->name[0] = root->name[1] = '\0'; 458 } else { 459 pte.len = strlen(root->name); 460 pte.parent = parentrecord; 461 } 462 pte.startsector = root->startsector; 463 root->pathtablerecord = (*recordno)++; 464 465 if(littleendian) { 466 little32((unsigned char *) &pte.startsector, pte.startsector); 467 little16((unsigned char *) &pte.parent, pte.parent); 468 } else { 469 big32((unsigned char *) &pte.startsector, pte.startsector); 470 big16((unsigned char *) &pte.parent, pte.parent); 471 } 472 473 *bytes += Write(fd, &pte.len, sizeof(pte.len)); 474 *bytes += Write(fd, &pte.zero, sizeof(pte.zero)); 475 *bytes += Write(fd, &pte.startsector, sizeof(pte.startsector)); 476 *bytes += Write(fd, &pte.parent, sizeof(pte.parent)); 477 if(!(pte.len%2)) 478 root->name[pte.len++] = '\0'; 479 for(i = 0; i < pte.len; i++) 480 newname[i] = toupper(root->name[i]); 481 *bytes += Write(fd, newname, pte.len); 482 return; 483 } 484 485 for(child = root->firstchild; child; child = child->nextchild) 486 if(child->isdir) 487 traversetree(child, level+1, littleendian, 488 maxlevel, bytes, fd, root->pathtablerecord, 489 recordno); 490 491 return; 492 } 493 494 int 495 makepathtables(struct node *root, int littleendian, int *bytes, int fd) 496 { 497 int level; 498 static char block[ISO_SECTOR]; 499 int recordno; 500 501 recordno = 1; 502 503 *bytes = 0; 504 505 for(level = 1; level <= MAXLEVEL; level++) 506 traversetree(root, 1, littleendian, level, bytes, fd, 1, &recordno); 507 508 if(*bytes % ISO_SECTOR) { 509 ssize_t x; 510 x = ISO_SECTOR-(*bytes % ISO_SECTOR); 511 write(fd, block, x); 512 *bytes += x; 513 } 514 515 return *bytes/ISO_SECTOR; 516 } 517 518 ssize_t 519 write_direntry(struct node * n, char *origname, int fd) 520 { 521 int namelen, total = 0; 522 struct dir entry; 523 char copyname[NAMELEN]; 524 struct tm tm; 525 526 memset(&entry, 0, sizeof(entry)); 527 528 if(!strcmp(origname, CURRENTDIR)) { 529 entry.name[0] = '\000'; 530 namelen = 1; 531 } else if(!strcmp(origname, PARENTDIR)) { 532 entry.name[0] = '\001'; 533 namelen = 1; 534 } else { 535 int i; 536 strlcpy(copyname, origname, sizeof(copyname)); 537 namelen = strlen(copyname); 538 539 /* XXX No check for 8+3 ? (DOS compatibility) */ 540 assert(ISONAMELEN <= NAMELEN-2); 541 if(namelen > ISONAMELEN) { 542 fprintf(stderr, "%s: truncated, too long for iso9660\n", copyname); 543 namelen = ISONAMELEN; 544 copyname[namelen] = '\0'; 545 } 546 547 strlcpy(entry.name, copyname, namelen+1); 548 for(i = 0; i < namelen; i++) 549 entry.name[i] = toupper(entry.name[i]); 550 551 /* padding byte + system field */ 552 entry.name[namelen] = '\0'; 553 entry.name[namelen+1] = '\0'; 554 entry.name[namelen+2] = '\0'; 555 } 556 entry.namelen = namelen; /* original length */ 557 if(!(namelen%2)) namelen++; /* length with padding byte */ 558 559 560 /* XXX 2 extra bytes for 'system use'.. */ 561 entry.recordsize = 33 + namelen; 562 both32((unsigned char *) entry.datasector, n->startsector); 563 both32((unsigned char *) entry.filesize, n->bytesize); 564 565 if(n->isdir) entry.flags = FLAG_DIR; 566 567 memcpy(&tm, gmtime(&n->timestamp), sizeof(tm)); 568 entry.year = (unsigned)tm.tm_year > 255 ? 255 : tm.tm_year; 569 entry.month = tm.tm_mon + 1; 570 entry.day = tm.tm_mday; 571 entry.hour = tm.tm_hour; 572 entry.minute = tm.tm_min; 573 entry.second = tm.tm_sec; 574 entry.offset = 0; /* Posix uses UTC timestamps! */ 575 576 both16((unsigned char *) entry.sequence, 1); 577 578 total = Write(fd, &entry.recordsize, sizeof(entry.recordsize)); 579 total += Write(fd, &entry.extended, sizeof(entry.extended)); 580 total += Write(fd, entry.datasector, sizeof(entry.datasector)); 581 total += Write(fd, entry.filesize, sizeof(entry.filesize)); 582 total += Write(fd, &entry.year, sizeof(entry.year)); 583 total += Write(fd, &entry.month, sizeof(entry.month)); 584 total += Write(fd, &entry.day, sizeof(entry.day)); 585 total += Write(fd, &entry.hour, sizeof(entry.hour)); 586 total += Write(fd, &entry.minute, sizeof(entry.minute)); 587 total += Write(fd, &entry.second, sizeof(entry.second)); 588 total += Write(fd, &entry.offset, sizeof(entry.offset)); 589 total += Write(fd, &entry.flags, sizeof(entry.flags)); 590 total += Write(fd, &entry.interleaved, sizeof(entry.interleaved)); 591 total += Write(fd, &entry.interleavegap, sizeof(entry.interleavegap)); 592 total += Write(fd, entry.sequence, sizeof(entry.sequence)); 593 total += Write(fd, &entry.namelen, sizeof(entry.namelen)); 594 total += Write(fd, entry.name, namelen); 595 596 if(total != entry.recordsize || (total % 2) != 0) { 597 printf("%2d, %2d! ", total, entry.recordsize); 598 printf("%3d = %3d - %2d + %2d\n", 599 entry.recordsize, sizeof(entry), sizeof(entry.name), namelen); 600 } 601 602 return entry.recordsize; 603 } 604 605 void 606 writedata(struct node *parent, struct node *root, 607 int fd, int *currentsector, int dirs, struct dir *rootentry, 608 int rootsize, int remove_after) 609 { 610 static char buf[1024*1024]; 611 struct node *c; 612 ssize_t written = 0, rest; 613 614 for(c = root->firstchild; c; c = c->nextchild) { 615 if(c->isdir && chdir(c->name) < 0) { 616 perror(c->name); 617 fprintf(stderr, "couldn't chdir to %s - aborting\n", 618 c->name); 619 exit(1); 620 } 621 writedata(root, c, fd, currentsector, dirs, rootentry, rootsize, remove_after); 622 if(c->isdir && chdir(PARENTDIR) < 0) { 623 perror("chdir to .."); 624 fprintf(stderr, "couldn't chdir to parent - " 625 "aborting\n"); 626 exit(1); 627 } 628 } 629 630 /* write nodes depth-first, down-top */ 631 632 if(root->isdir && dirs) { 633 /* dir */ 634 written = 0; 635 root->startsector = *currentsector; 636 written += write_direntry(root, CURRENTDIR, fd); 637 if(parent) { 638 written += write_direntry(parent, PARENTDIR, fd); 639 } else { 640 written += write_direntry(root, PARENTDIR, fd); 641 } 642 for(c = root->firstchild; c; c = c->nextchild) { 643 off_t cur1, cur2; 644 ssize_t written_before; 645 cur1 = Lseek(fd, 0, SEEK_CUR); 646 written_before = written; 647 written += write_direntry(c, c->name, fd); 648 cur2 = Lseek(fd, 0, SEEK_CUR); 649 if(cur1/ISO_SECTOR != (cur2-1)/ISO_SECTOR) { 650 /* passed a sector boundary, argh! */ 651 Lseek(fd, cur1, SEEK_SET); 652 written = written_before; 653 rest=(ISO_SECTOR-(written % ISO_SECTOR)); 654 memset(buf, 0, rest); 655 Write(fd, buf, rest); 656 written += rest; 657 written += write_direntry(c, c->name, fd); 658 } 659 } 660 root->bytesize = written; 661 } else if(!root->isdir && !dirs) { 662 /* file */ 663 struct stat st; 664 ssize_t rem; 665 int filefd; 666 667 if(stat(root->name, &st) < 0) { 668 perror(root->name); 669 fprintf(stderr, "couldn't stat %s - aborting\n", root->name); 670 exit(1); 671 } 672 673 if((filefd = open(root->name, O_RDONLY)) < 0) { 674 perror(root->name); 675 fprintf(stderr, "couldn't open %s - aborting\n", root->name); 676 exit(1); 677 } 678 679 rem = st.st_size; 680 681 root->startsector = *currentsector; 682 683 while(rem > 0) { 684 ssize_t chunk; 685 chunk = min(sizeof(buf), rem); 686 Read(filefd, buf, chunk); 687 Write(fd, buf, chunk); 688 rem -= chunk; 689 } 690 691 close(filefd); 692 693 root->bytesize = written = st.st_size; 694 if(remove_after && unlink(root->name) < 0) { 695 perror("unlink"); 696 fprintf(stderr, "couldn't remove %s\n", root->name); 697 } 698 } else { 699 /* nothing to be done */ 700 return; 701 } 702 703 /* fill out sector with zero bytes */ 704 705 if((rest=(ISO_SECTOR-(written % ISO_SECTOR)))) { 706 memset(buf, 0, rest); 707 Write(fd, buf, rest); 708 written += rest; 709 } 710 711 /* update dir size with padded size */ 712 713 if(root->isdir) { root->bytesize = written; } 714 715 *currentsector += written/ISO_SECTOR; 716 } 717 718 void 719 writebootcatalog(int fd, int *currentsector, int imagesector, int imagesectors) 720 { 721 static char buf[ISO_SECTOR]; 722 struct bc_validation validate; 723 struct bc_initial initial; 724 725 ssize_t written, rest; 726 u_int16_t *v, sum = 0; 727 int i; 728 729 /* write validation entry */ 730 731 memset(&validate, 0, sizeof(validate)); 732 validate.headerid = 1; 733 validate.platform = PLATFORM_80X86; 734 strcpy(validate.idstring, ""); 735 validate.keys[0] = 0x55; 736 validate.keys[1] = 0xaa; 737 738 v = (u_int16_t *) &validate; 739 for(i = 0; i < sizeof(validate)/2; i++) 740 sum += v[i]; 741 validate.checksum = 65535 - sum + 1; /* sum must be 0 */ 742 743 written = Write(fd, &validate, sizeof(validate)); 744 745 /* write initial/default entry */ 746 747 memset(&initial, 0, sizeof(initial)); 748 749 initial.indicator = INDICATE_BOOTABLE; 750 initial.media = bootmedia; 751 initial.seg = (u_int16_t) (bootseg & 0xFFFF); 752 initial.sectors = 1; 753 if (bootmedia == BOOTMEDIA_HARDDISK) 754 { 755 initial.type = system_type; 756 } 757 if (bootmedia == BOOTMEDIA_NONE) 758 { 759 initial.sectors = imagesectors; 760 } 761 initial.startsector = imagesector; 762 763 written += Write(fd, &initial, sizeof(initial)); 764 765 /* fill out the rest of the sector with 0's */ 766 767 if((rest = ISO_SECTOR - (written % ISO_SECTOR))) { 768 memset(buf, 0, sizeof(buf)); 769 written += Write(fd, buf, rest); 770 } 771 772 (*currentsector) += written / ISO_SECTOR; 773 774 return; 775 } 776 777 int 778 writebootimage(char *bootimage, int bootfd, int fd, int *currentsector, 779 char *appendsectorinfo, struct node *root) 780 { 781 static unsigned char buf[1024*64], *addr; 782 ssize_t written = 0, rest; 783 int virtuals, rem; 784 struct stat sb; 785 struct bap { 786 off_t sector; 787 int length; 788 } bap[2]; 789 790 bap[0].length = bap[0].sector = 0; 791 bap[1].length = bap[1].sector = 0; 792 793 if (fstat(bootfd, &sb) < 0) { 794 perror("stat boot image"); 795 exit(1); 796 } 797 798 rem = sb.st_size; 799 800 while(rem > 0) { 801 int want; 802 want = rem < sizeof(buf) ? rem : sizeof(buf); 803 Read(bootfd, buf, want); 804 if (written == 0) { 805 /* check some properties at beginning. */ 806 if (buf[0] == 1 && buf[1] == 3) { 807 fprintf(stderr, "boot image %s is an a.out executable\n", 808 bootimage); 809 exit(1); 810 } 811 if (rem >= VIRTUAL_SECTOR 812 && (buf[510] != 0x55 || buf[511] != 0xaa) ) { 813 fprintf(stderr, "invalid boot sector (bad magic.)\n"); 814 exit(1); 815 } 816 } 817 written += Write(fd, buf, want); 818 rem -= want; 819 } 820 821 if(appendsectorinfo) { 822 struct node *n; 823 for(n = root->firstchild; n; n = n ->nextchild) { 824 if(!strcasecmp(appendsectorinfo, n->name)) { 825 bap[0].sector = n->startsector; 826 bap[0].length = ROUNDUP(n->bytesize, ISO_SECTOR); 827 break; 828 } 829 } 830 if(!n) { 831 fprintf(stderr, "%s not found in root.\n", 832 appendsectorinfo); 833 exit(1); 834 } 835 836 fprintf(stderr, " * appended sector info: 0x%x len 0x%x\n", 837 bap[0].sector, bap[0].length); 838 839 addr = buf; 840 addr[0] = bap[0].length; 841 assert(addr[0] > 0); 842 addr[1] = (bap[0].sector >> 0) & 0xFF; 843 addr[2] = (bap[0].sector >> 8) & 0xFF; 844 addr[3] = (bap[0].sector >> 16) & 0xFF; 845 addr[4] = 0; 846 addr[5] = 0; 847 848 written += Write(fd, addr, 6); 849 } 850 851 virtuals = ROUNDUP(written, VIRTUAL_SECTOR); 852 assert(virtuals * VIRTUAL_SECTOR >= written); 853 854 if((rest = ISO_SECTOR - (written % ISO_SECTOR))) { 855 memset(buf, 0, sizeof(buf)); 856 written += Write(fd, buf, rest); 857 } 858 859 (*currentsector) += written/ISO_SECTOR; 860 861 return virtuals; 862 } 863 864 void 865 writebootrecord(int fd, int *currentsector, int bootcatalogsector) 866 { 867 int i; 868 static struct bootrecord bootrecord; 869 ssize_t w = 0; 870 /* boot record volume descriptor */ 871 872 memset(&bootrecord, 0, sizeof(bootrecord)); 873 bootrecord.set[0] = 'C'; 874 bootrecord.set[1] = 'D'; 875 bootrecord.set[2] = '0'; 876 bootrecord.set[3] = '0'; 877 bootrecord.set[4] = '1'; 878 bootrecord.version = 1; 879 bootrecord.bootcatalog = bootcatalogsector; 880 strcpy(bootrecord.ident, "EL TORITO SPECIFICATION"); 881 for(i = strlen(bootrecord.ident); 882 i < sizeof(bootrecord.ident); i++) 883 bootrecord.ident[i] = '\0'; 884 885 w = Writefield(fd, bootrecord.indicator); 886 w += Writefield(fd, bootrecord.set); 887 w += Writefield(fd, bootrecord.version); 888 w += Writefield(fd, bootrecord.ident); 889 w += Writefield(fd, bootrecord.zero); 890 w += Writefield(fd, bootrecord.bootcatalog); 891 w += Writefield(fd, bootrecord.zero2); 892 893 if(w != ISO_SECTOR) { 894 fprintf(stderr, "WARNING: something went wrong - boot record (%d) isn't a sector size (%d)\n", 895 w, ISO_SECTOR); 896 } 897 898 (*currentsector)++; 899 } 900 901 int 902 main(int argc, char *argv[]) 903 { 904 int currentsector = 0; 905 int imagesector, imagesectors; 906 int bootfd, fd, i, ch, nsectors; 907 int remove_after = 0; 908 static char block[ISO_SECTOR]; 909 static struct pvd pvd; 910 char *label = "ISO9660"; 911 struct tm *now; 912 time_t nowtime; 913 char timestr[20], *prog; 914 char *bootimage = NULL; 915 struct node root; 916 int pvdsector; 917 int bigpath, littlepath, pathbytes = 0, dirsector, filesector, enddir; 918 int bootvolumesector, bootcatalogsector; 919 char *appendsectorinfo = NULL; 920 921 prog = argv[0]; 922 923 /* This check is to prevent compiler padding screwing up 924 * our format. 925 */ 926 927 if(sizeof(struct pvd) != ISO_SECTOR) { 928 fprintf(stderr, "Something confusing happened at\n" 929 "compile-time; pvd should be a sector size. %d != %d\n", 930 sizeof(struct pvd), ISO_SECTOR); 931 return 1; 932 } 933 934 while ((ch = getopt(argc, argv, "a:b:B:s:Rb:hl:nfF")) != -1) { 935 switch(ch) { 936 case 's': 937 if(optarg[0] != '0' || optarg[1] != 'x') { 938 fprintf(stderr, "%s: -s<hex>\n", 939 argv[0]); 940 return 1; 941 } 942 bootseg = strtoul(optarg+2, NULL, 16); 943 break; 944 case 'h': 945 bootmedia= BOOTMEDIA_HARDDISK; 946 break; 947 case 'n': 948 bootmedia= BOOTMEDIA_NONE; 949 break; 950 case 'f': 951 bootmedia= BOOTMEDIA_1440K; 952 break; 953 case 'F': 954 bootmedia= BOOTMEDIA_2880K; 955 break; 956 case 'a': 957 if(!(appendsectorinfo = strdup(optarg))) 958 exit(1); 959 break; 960 case 'l': 961 label = optarg; 962 break; 963 case 'R': 964 remove_after = 1; 965 break; 966 case 'B': 967 bootimage = optarg; 968 if((bootfd = open(bootimage, O_RDONLY)) < 0) { 969 perror(bootimage); 970 return 1; 971 } 972 break; 973 } 974 } 975 976 argc -= optind; 977 argv += optind; 978 979 /* Args check */ 980 981 if(argc != 2) { 982 fprintf(stderr, "usage: %s [-l <label>] [-(b|B) <bootimage> [-n|-f|-F|-h] [-s <bootsegment>] [ -a <appendfile> ] <dir> <isofile>\n", 983 prog); 984 return 1; 985 } 986 987 if((bootimage && bootmedia == BOOTMEDIA_UNSPECIFIED) || 988 (!bootimage && bootmedia != BOOTMEDIA_UNSPECIFIED)) { 989 fprintf(stderr, "%s: provide both boot image and boot type or neither.\n", 990 prog); 991 return 1; 992 } 993 994 if(!bootimage && bootseg) { 995 fprintf(stderr, "%s: boot seg provided but no boot image\n", 996 prog); 997 return 1; 998 } 999 1000 if(appendsectorinfo) { 1001 if(!bootimage || bootmedia != BOOTMEDIA_NONE) { 1002 fprintf(stderr, "%s: append sector info where?\n", 1003 prog); 1004 return 1; 1005 } 1006 } 1007 1008 /* create .iso file */ 1009 1010 if((fd=open(argv[1], O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0) { 1011 perror(argv[1]); 1012 return 1; 1013 } 1014 1015 /* go to where the iso has to be made from */ 1016 1017 if(chdir(argv[0]) < 0) { 1018 perror(argv[0]); 1019 return 1; 1020 } 1021 1022 /* collect dirs and files */ 1023 1024 fprintf(stderr, " * traversing input tree\n"); 1025 1026 maketree(&root, "", 1); 1027 1028 fprintf(stderr, " * writing initial zeroes and pvd\n"); 1029 1030 /* first sixteen sectors are zero */ 1031 1032 memset(block, 0, sizeof(block)); 1033 1034 for(i = 0; i < 16; i++) 1035 writesector(fd, block, ¤tsector); 1036 1037 /* Primary Volume Descriptor */ 1038 memset(&pvd, 0, sizeof(pvd)); 1039 pvd.one = 1; 1040 pvd.set[0] = 67; 1041 pvd.set[1] = 68; 1042 pvd.set[2] = 48; 1043 pvd.set[3] = 48; 1044 pvd.set[4] = 49; 1045 pvd.set[5] = 1; 1046 pvd.set[5] = 1; 1047 1048 strncpy(pvd.volume, label, sizeof(pvd.volume)-1); 1049 for(i = strlen(pvd.volume); i < sizeof(pvd.volume); i++) 1050 pvd.volume[i] = ' '; 1051 for(i = 0; i < sizeof(pvd.system); i++) 1052 pvd.system[i] = ' '; 1053 1054 both16((unsigned char *) pvd.setsize, 1); 1055 both16((unsigned char *) pvd.seq, 1); 1056 both16((unsigned char *) pvd.sectorsize, ISO_SECTOR); 1057 1058 /* fill time fields */ 1059 time(&nowtime); 1060 now = gmtime(&nowtime); 1061 strftime(timestr, sizeof(timestr), "%Y%m%d%H%M%S000", now); 1062 memcpy(pvd.create, timestr, strlen(timestr)); 1063 memcpy(pvd.modified, timestr, strlen(timestr)); 1064 memcpy(pvd.effective, timestr, strlen(timestr)); 1065 strcpy(pvd.expiry, "0000000000000000"); /* not specified */ 1066 pvdsector = currentsector; 1067 1068 writesector(fd, (char *) &pvd, ¤tsector); 1069 1070 if(bootimage) { 1071 fprintf(stderr, " * writing boot record volume descriptor\n"); 1072 bootvolumesector = currentsector; 1073 writebootrecord(fd, ¤tsector, 0); 1074 } 1075 1076 /* volume descriptor set terminator */ 1077 memset(block, 0, sizeof(block)); 1078 block[0] = 255; 1079 block[1] = 67; 1080 block[2] = 68; 1081 block[3] = 48; 1082 block[4] = 48; 1083 block[5] = 49; 1084 block[6] = 1; 1085 1086 writesector(fd, block, ¤tsector); 1087 1088 if(bootimage) { 1089 /* write the boot catalog */ 1090 fprintf(stderr, " * writing the boot catalog\n"); 1091 bootcatalogsector = currentsector; 1092 if (bootmedia == BOOTMEDIA_HARDDISK) 1093 system_type = get_system_type(bootfd); 1094 writebootcatalog(fd, ¤tsector, 0, 0); 1095 1096 /* write boot image */ 1097 fprintf(stderr, " * writing the boot image\n"); 1098 imagesector = currentsector; 1099 imagesectors = writebootimage(bootimage, bootfd, 1100 fd, ¤tsector, NULL, &root); 1101 fprintf(stderr, " * image: %d %d-byte sectors @ cd sector 0x%x\n", 1102 imagesectors, VIRTUAL_SECTOR, imagesector); 1103 } 1104 1105 /* write out all the file data */ 1106 1107 filesector = currentsector; 1108 fprintf(stderr, " * writing file data\n"); 1109 writedata(NULL, &root, fd, ¤tsector, 0, 1110 (struct dir *) &pvd.rootrecord, sizeof(pvd.rootrecord), 1111 remove_after); 1112 1113 /* write out all the dir data */ 1114 1115 dirsector = currentsector; 1116 fprintf(stderr, " * writing dir data\n"); 1117 writedata(NULL, &root, fd, ¤tsector, 1, 1118 (struct dir *) &pvd.rootrecord, sizeof(pvd.rootrecord), 1119 remove_after); 1120 enddir = currentsector; 1121 seeksector(fd, dirsector, ¤tsector); 1122 fprintf(stderr, " * rewriting dir data\n"); 1123 fflush(NULL); 1124 writedata(NULL, &root, fd, ¤tsector, 1, 1125 (struct dir *) &pvd.rootrecord, sizeof(pvd.rootrecord), 1126 remove_after); 1127 if(currentsector != enddir) { 1128 fprintf(stderr, "warning: inconsistent directories - " 1129 "I have a bug! iso may be broken.\n"); 1130 } 1131 1132 /* now write the path table in both formats */ 1133 1134 fprintf(stderr, " * writing big-endian path table\n"); 1135 bigpath = currentsector; 1136 currentsector += makepathtables(&root, 0, &pathbytes, fd); 1137 1138 fprintf(stderr, " * writing little-endian path table\n"); 1139 littlepath = currentsector; 1140 currentsector += makepathtables(&root, 1, &pathbytes, fd); 1141 1142 both32((unsigned char *) pvd.pathtablesize, pathbytes); 1143 little32((unsigned char *) &pvd.first_little_pathtable_start, littlepath); 1144 big32((unsigned char *) &pvd.first_big_pathtable_start, bigpath); 1145 1146 /* this is the size of the iso filesystem for use in the pvd later */ 1147 1148 nsectors = currentsector; 1149 both32((unsigned char *) pvd.sectors, nsectors); 1150 1151 /* *********** Filesystem writing done ************************* */ 1152 1153 /* finish and rewrite the pvd. */ 1154 fprintf(stderr, " * rewriting pvd\n"); 1155 seekwritesector(fd, pvdsector, (char *) &pvd, ¤tsector); 1156 1157 /* write root dir entry in pvd */ 1158 seeksector(fd, pvdsector, ¤tsector); 1159 Lseek(fd, (int)((char *) &pvd.rootrecord - (char *) &pvd), SEEK_CUR); 1160 if(write_direntry(&root, CURRENTDIR, fd) > sizeof(pvd.rootrecord)) { 1161 fprintf(stderr, "warning: unexpectedly large root record\n"); 1162 } 1163 1164 if(bootimage) { 1165 fprintf(stderr, " * rewriting boot catalog\n"); 1166 seeksector(fd, bootcatalogsector, ¤tsector); 1167 writebootcatalog(fd, ¤tsector, imagesector, imagesectors); 1168 1169 /* finish and rewrite the boot record volume descriptor */ 1170 fprintf(stderr, " * rewriting the boot rvd\n"); 1171 seeksector(fd, bootvolumesector, ¤tsector); 1172 writebootrecord(fd, ¤tsector, bootcatalogsector); 1173 1174 if(appendsectorinfo) { 1175 Lseek(bootfd, 0, SEEK_SET); 1176 fprintf(stderr, " * rewriting boot image\n"); 1177 seeksector(fd, imagesector, ¤tsector); 1178 writebootimage(bootimage, bootfd, 1179 fd, ¤tsector, appendsectorinfo, &root); 1180 1181 } 1182 close(bootfd); 1183 } 1184 1185 fprintf(stderr, " * all ok\n"); 1186 1187 return 0; 1188 } 1189 1190 int get_system_type(int fd) 1191 { 1192 off_t old_pos; 1193 size_t size; 1194 ssize_t r; 1195 int type; 1196 struct part_entry *partp; 1197 unsigned char bootsector[512]; 1198 1199 errno= 0; 1200 old_pos= lseek(fd, 0, SEEK_SET); 1201 if (old_pos == -1 && errno != 0) 1202 { 1203 fprintf(stderr, "bootimage file is not seekable: %s\n", 1204 strerror(errno)); 1205 exit(1); 1206 } 1207 size= sizeof(bootsector); 1208 r= read(fd, bootsector, size); 1209 if (r != size) 1210 { 1211 fprintf(stderr, "error reading bootimage file: %s\n", 1212 r < 0 ? strerror(errno) : "unexpected EOF"); 1213 exit(1); 1214 } 1215 if (bootsector[size-2] != 0x55 && bootsector[size-1] != 0xAA) 1216 { 1217 fprintf(stderr, "bad magic in bootimage file\n"); 1218 exit(1); 1219 } 1220 1221 partp= (struct part_entry *)&bootsector[PART_TABLE_OFF]; 1222 type= partp->sysind; 1223 if (type == NO_PART) 1224 { 1225 fprintf(stderr, "first partition table entry is unused\n"); 1226 exit(1); 1227 } 1228 if (!(partp->bootind & ACTIVE_FLAG)) 1229 { 1230 fprintf(stderr, "first partition table entry is not active\n"); 1231 exit(1); 1232 } 1233 1234 lseek(fd, old_pos, SEEK_SET); 1235 return type; 1236 } 1237