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