1 #include <u.h> 2 #include <libc.h> 3 #include <bio.h> 4 #include <libsec.h> 5 6 #include "iso9660.h" 7 8 static int readisodesc(Cdimg*, Voldesc*); 9 static int readjolietdesc(Cdimg*, Voldesc*); 10 11 /* 12 * It's not strictly conforming; instead it's enough to 13 * get us up and running; presumably the real CD writing 14 * will take care of being conforming. 15 * 16 * Things not conforming include: 17 * - no path table 18 * - root directories are of length zero 19 */ 20 Cdimg* 21 createcd(char *file, Cdinfo info) 22 { 23 int fd, xfd; 24 Cdimg *cd; 25 26 if(access(file, AEXIST) == 0){ 27 werrstr("file already exists"); 28 return nil; 29 } 30 31 if((fd = create(file, ORDWR, 0666)) < 0) 32 return nil; 33 34 cd = emalloc(sizeof *cd); 35 cd->file = atom(file); 36 37 Binit(&cd->brd, fd, OREAD); 38 39 if((xfd = open(file, ORDWR)) < 0) 40 sysfatal("can't open file again: %r"); 41 Binit(&cd->bwr, xfd, OWRITE); 42 43 Crepeat(cd, 0, 16*Blocksize); 44 Cputisopvd(cd, info); 45 if(info.flags & CDbootable){ 46 cd->bootimage = info.bootimage; 47 cd->loader = info.loader; 48 cd->flags |= info.flags & (CDbootable|CDbootnoemu); 49 Cputbootvol(cd); 50 } 51 52 if(readisodesc(cd, &cd->iso) < 0) 53 assert(0); 54 if(info.flags & CDplan9) 55 cd->flags |= CDplan9; 56 else if(info.flags & CDrockridge) 57 cd->flags |= CDrockridge; 58 if(info.flags & CDjoliet) { 59 Cputjolietsvd(cd, info); 60 if(readjolietdesc(cd, &cd->joliet) < 0) 61 assert(0); 62 cd->flags |= CDjoliet; 63 } 64 Cputendvd(cd); 65 66 if(info.flags & CDdump){ 67 cd->nulldump = Cputdumpblock(cd); 68 cd->flags |= CDdump; 69 } 70 if(cd->flags & CDbootable){ 71 Cputbootcat(cd); 72 Cupdatebootvol(cd); 73 } 74 75 if(info.flags & CDconform) 76 cd->flags |= CDconform; 77 78 cd->flags |= CDnew; 79 cd->nextblock = Cwoffset(cd) / Blocksize; 80 assert(cd->nextblock != 0); 81 82 return cd; 83 } 84 85 Cdimg* 86 opencd(char *file, Cdinfo info) 87 { 88 int fd, xfd; 89 Cdimg *cd; 90 Dir *d; 91 92 if((fd = open(file, ORDWR)) < 0) { 93 if(access(file, AEXIST) == 0) 94 return nil; 95 return createcd(file, info); 96 } 97 98 if((d = dirfstat(fd)) == nil) { 99 close(fd); 100 return nil; 101 } 102 if(d->length == 0 || d->length % Blocksize) { 103 werrstr("bad length %lld", d->length); 104 close(fd); 105 free(d); 106 return nil; 107 } 108 109 cd = emalloc(sizeof *cd); 110 cd->file = atom(file); 111 cd->nextblock = d->length / Blocksize; 112 assert(cd->nextblock != 0); 113 free(d); 114 115 Binit(&cd->brd, fd, OREAD); 116 117 if((xfd = open(file, ORDWR)) < 0) 118 sysfatal("can't open file again: %r"); 119 Binit(&cd->bwr, xfd, OWRITE); 120 121 if(readisodesc(cd, &cd->iso) < 0) { 122 free(cd); 123 close(fd); 124 close(xfd); 125 return nil; 126 } 127 128 /* lowercase because of isostring */ 129 if(strstr(cd->iso.systemid, "iso9660") == nil 130 && strstr(cd->iso.systemid, "utf8") == nil) { 131 werrstr("unknown systemid %s", cd->iso.systemid); 132 free(cd); 133 close(fd); 134 close(xfd); 135 return nil; 136 } 137 138 if(strstr(cd->iso.systemid, "plan 9")) 139 cd->flags |= CDplan9; 140 if(strstr(cd->iso.systemid, "iso9660")) 141 cd->flags |= CDconform; 142 if(strstr(cd->iso.systemid, "rrip")) 143 cd->flags |= CDrockridge; 144 if(strstr(cd->iso.systemid, "boot")) 145 cd->flags |= CDbootable; 146 if(readjolietdesc(cd, &cd->joliet) == 0) 147 cd->flags |= CDjoliet; 148 if(hasdump(cd)) 149 cd->flags |= CDdump; 150 151 return cd; 152 } 153 154 ulong 155 big(void *a, int n) 156 { 157 uchar *p; 158 ulong v; 159 int i; 160 161 p = a; 162 v = 0; 163 for(i=0; i<n; i++) 164 v = (v<<8) | *p++; 165 return v; 166 } 167 168 ulong 169 little(void *a, int n) 170 { 171 uchar *p; 172 ulong v; 173 int i; 174 175 p = a; 176 v = 0; 177 for(i=0; i<n; i++) 178 v |= (*p++<<(i*8)); 179 return v; 180 } 181 182 void 183 Creadblock(Cdimg *cd, void *buf, ulong block, ulong len) 184 { 185 assert(block != 0); /* nothing useful there */ 186 187 Bflush(&cd->bwr); 188 if(Bseek(&cd->brd, (vlong)block * Blocksize, 0) != 189 (vlong)block * Blocksize) 190 sysfatal("error seeking to block %lud", block); 191 if(Bread(&cd->brd, buf, len) != len) 192 sysfatal("error reading %lud bytes at block %lud: %r %lld", 193 len, block, Bseek(&cd->brd, 0, 2)); 194 } 195 196 int 197 parsedir(Cdimg *cd, Direc *d, uchar *buf, int len, char *(*cvtname)(uchar*, int)) 198 { 199 enum { NAMELEN = 28 }; 200 char name[NAMELEN]; 201 uchar *p; 202 Cdir *c; 203 204 memset(d, 0, sizeof *d); 205 206 c = (Cdir*)buf; 207 208 if(c->len > len) { 209 werrstr("buffer too small"); 210 return -1; 211 } 212 213 if(c->namelen == 1 && c->name[0] == '\0') 214 d->name = atom("."); 215 else if(c->namelen == 1 && c->name[0] == '\001') 216 d->name = atom(".."); 217 else if(cvtname) 218 d->name = cvtname(c->name, c->namelen); 219 220 d->block = little(c->dloc, 4); 221 d->length = little(c->dlen, 4); 222 223 if(c->flags & 2) 224 d->mode |= DMDIR; 225 226 /*BUG: do we really need to parse the plan 9 fields? */ 227 /* plan 9 use fields */ 228 if((cd->flags & CDplan9) && cvtname == isostring 229 && (c->namelen != 1 || c->name[0] > 1)) { 230 p = buf+33+c->namelen; 231 if((p-buf)&1) 232 p++; 233 assert(p < buf+c->len); 234 assert(*p < NAMELEN); 235 if(*p != 0) { 236 memmove(name, p+1, *p); 237 name[*p] = '\0'; 238 d->confname = d->name; 239 d->name = atom(name); 240 } 241 p += *p+1; 242 assert(*p < NAMELEN); 243 memmove(name, p+1, *p); 244 name[*p] = '\0'; 245 d->uid = atom(name); 246 p += *p+1; 247 assert(*p < NAMELEN); 248 memmove(name, p+1, *p); 249 name[*p] = '\0'; 250 d->gid = atom(name); 251 p += *p+1; 252 if((p-buf)&1) 253 p++; 254 d->mode = little(p, 4); 255 } 256 257 // BUG: rock ridge extensions 258 return 0; 259 } 260 261 void 262 setroot(Cdimg *cd, ulong block, ulong dloc, ulong dlen) 263 { 264 assert(block != 0); 265 266 Cwseek(cd, (vlong)block * Blocksize + offsetof(Cvoldesc, rootdir[0]) + 267 offsetof(Cdir, dloc[0])); 268 Cputn(cd, dloc, 4); 269 Cputn(cd, dlen, 4); 270 } 271 272 void 273 setvolsize(Cdimg *cd, uvlong block, ulong size) 274 { 275 assert(block != 0); 276 277 Cwseek(cd, block * Blocksize + offsetof(Cvoldesc, volsize[0])); 278 Cputn(cd, size, 4); 279 } 280 281 void 282 setpathtable(Cdimg *cd, ulong block, ulong sz, ulong lloc, ulong bloc) 283 { 284 assert(block != 0); 285 286 Cwseek(cd, (vlong)block * Blocksize + offsetof(Cvoldesc, pathsize[0])); 287 Cputn(cd, sz, 4); 288 Cputnl(cd, lloc, 4); 289 Cputnl(cd, 0, 4); 290 Cputnm(cd, bloc, 4); 291 Cputnm(cd, 0, 4); 292 assert(Cwoffset(cd) == (vlong)block * Blocksize + 293 offsetof(Cvoldesc, rootdir[0])); 294 } 295 296 297 static void 298 parsedesc(Voldesc *v, Cvoldesc *cv, char *(*string)(uchar*, int)) 299 { 300 v->systemid = string(cv->systemid, sizeof cv->systemid); 301 302 v->pathsize = little(cv->pathsize, 4); 303 v->lpathloc = little(cv->lpathloc, 4); 304 v->mpathloc = little(cv->mpathloc, 4); 305 306 v->volumeset = string(cv->volumeset, sizeof cv->volumeset); 307 v->publisher = string(cv->publisher, sizeof cv->publisher); 308 v->preparer = string(cv->preparer, sizeof cv->preparer); 309 v->application = string(cv->application, sizeof cv->application); 310 311 v->abstract = string(cv->abstract, sizeof cv->abstract); 312 v->biblio = string(cv->biblio, sizeof cv->biblio); 313 v->notice = string(cv->notice, sizeof cv->notice); 314 } 315 316 static int 317 readisodesc(Cdimg *cd, Voldesc *v) 318 { 319 static uchar magic[] = { 0x01, 'C', 'D', '0', '0', '1', 0x01, 0x00 }; 320 Cvoldesc cv; 321 322 memset(v, 0, sizeof *v); 323 324 Creadblock(cd, &cv, 16, sizeof cv); 325 if(memcmp(cv.magic, magic, sizeof magic) != 0) { 326 werrstr("bad pvd magic"); 327 return -1; 328 } 329 330 if(little(cv.blocksize, 2) != Blocksize) { 331 werrstr("block size not %d", Blocksize); 332 return -1; 333 } 334 335 cd->iso9660pvd = 16; 336 parsedesc(v, &cv, isostring); 337 338 return parsedir(cd, &v->root, cv.rootdir, sizeof cv.rootdir, isostring); 339 } 340 341 static int 342 readjolietdesc(Cdimg *cd, Voldesc *v) 343 { 344 int i; 345 static uchar magic[] = { 0x02, 'C', 'D', '0', '0', '1', 0x01, 0x00 }; 346 Cvoldesc cv; 347 348 memset(v, 0, sizeof *v); 349 350 for(i=16; i<24; i++) { 351 Creadblock(cd, &cv, i, sizeof cv); 352 if(memcmp(cv.magic, magic, sizeof magic) != 0) 353 continue; 354 if(cv.charset[0] != 0x25 || cv.charset[1] != 0x2F 355 || (cv.charset[2] != 0x40 && cv.charset[2] != 0x43 && cv.charset[2] != 0x45)) 356 continue; 357 break; 358 } 359 360 if(i==24) { 361 werrstr("could not find Joliet SVD"); 362 return -1; 363 } 364 365 if(little(cv.blocksize, 2) != Blocksize) { 366 werrstr("block size not %d", Blocksize); 367 return -1; 368 } 369 370 cd->jolietsvd = i; 371 parsedesc(v, &cv, jolietstring); 372 373 return parsedir(cd, &v->root, cv.rootdir, sizeof cv.rootdir, jolietstring); 374 } 375 376 /* 377 * CD image buffering routines. 378 */ 379 void 380 Cputc(Cdimg *cd, int c) 381 { 382 assert(Boffset(&cd->bwr) >= 16*Blocksize || c == 0); 383 384 if(Boffset(&cd->bwr) == 0x9962) 385 if(c >= 256) abort(); 386 if(Bputc(&cd->bwr, c) < 0) 387 sysfatal("Bputc: %r"); 388 Bflush(&cd->brd); 389 } 390 391 void 392 Cputnl(Cdimg *cd, uvlong val, int size) 393 { 394 switch(size) { 395 default: 396 sysfatal("bad size %d in Cputnl", size); 397 case 2: 398 if(val >= (1<<16)) 399 sysfatal("value %llud too big for size %d in Cputnl", 400 val, size); 401 Cputc(cd, val); 402 Cputc(cd, val>>8); 403 break; 404 case 4: 405 if(val >= (1ULL<<32)) 406 sysfatal("value %llud too big for size %d in Cputnl", 407 val, size); 408 Cputc(cd, val); 409 Cputc(cd, val>>8); 410 Cputc(cd, val>>16); 411 Cputc(cd, val>>24); 412 break; 413 case 8: 414 Cputc(cd, val); 415 Cputc(cd, val>>8); 416 Cputc(cd, val>>16); 417 Cputc(cd, val>>24); 418 Cputc(cd, val>>32); 419 Cputc(cd, val>>40); 420 Cputc(cd, val>>48); 421 Cputc(cd, val>>56); 422 break; 423 } 424 } 425 426 void 427 Cputnm(Cdimg *cd, uvlong val, int size) 428 { 429 switch(size) { 430 default: 431 sysfatal("bad size %d in Cputnm", size); 432 case 2: 433 if(val >= (1<<16)) 434 sysfatal("value %llud too big for size %d in Cputnl", 435 val, size); 436 Cputc(cd, val>>8); 437 Cputc(cd, val); 438 break; 439 case 4: 440 if(val >= (1ULL<<32)) 441 sysfatal("value %llud too big for size %d in Cputnl", 442 val, size); 443 Cputc(cd, val>>24); 444 Cputc(cd, val>>16); 445 Cputc(cd, val>>8); 446 Cputc(cd, val); 447 break; 448 case 8: 449 Cputc(cd, val>>56); 450 Cputc(cd, val>>48); 451 Cputc(cd, val>>40); 452 Cputc(cd, val>>32); 453 Cputc(cd, val>>24); 454 Cputc(cd, val>>16); 455 Cputc(cd, val>>8); 456 Cputc(cd, val); 457 break; 458 } 459 } 460 461 void 462 Cputn(Cdimg *cd, uvlong val, int size) 463 { 464 Cputnl(cd, val, size); 465 Cputnm(cd, val, size); 466 } 467 468 /* 469 * ASCII/UTF string writing 470 */ 471 void 472 Crepeat(Cdimg *cd, int c, int n) 473 { 474 while(n-- > 0) 475 Cputc(cd, c); 476 } 477 478 void 479 Cputs(Cdimg *cd, char *s, int size) 480 { 481 int n; 482 483 if(s == nil) { 484 Crepeat(cd, ' ', size); 485 return; 486 } 487 488 for(n=0; n<size && *s; n++) 489 Cputc(cd, *s++); 490 if(n<size) 491 Crepeat(cd, ' ', size-n); 492 } 493 494 void 495 Cwrite(Cdimg *cd, void *buf, int n) 496 { 497 assert(Boffset(&cd->bwr) >= 16*Blocksize); 498 499 if(Bwrite(&cd->bwr, buf, n) != n) 500 sysfatal("Bwrite: %r"); 501 Bflush(&cd->brd); 502 } 503 504 void 505 Cputr(Cdimg *cd, Rune r) 506 { 507 Cputc(cd, r>>8); 508 Cputc(cd, r); 509 } 510 511 void 512 Crepeatr(Cdimg *cd, Rune r, int n) 513 { 514 int i; 515 516 for(i=0; i<n; i++) 517 Cputr(cd, r); 518 } 519 520 void 521 Cputrs(Cdimg *cd, Rune *s, int osize) 522 { 523 int n, size; 524 525 size = osize/2; 526 if(s == nil) 527 Crepeatr(cd, (Rune)' ', size); 528 else { 529 for(n=0; *s && n<size; n++) 530 Cputr(cd, *s++); 531 if(n<size) 532 Crepeatr(cd, ' ', size-n); 533 } 534 if(osize&1) 535 Cputc(cd, 0); /* what else can we do? */ 536 } 537 538 void 539 Cputrscvt(Cdimg *cd, char *s, int size) 540 { 541 Rune r[256]; 542 543 strtorune(r, s); 544 Cputrs(cd, strtorune(r, s), size); 545 } 546 547 void 548 Cpadblock(Cdimg *cd) 549 { 550 int n; 551 ulong nb; 552 553 n = Blocksize - (Boffset(&cd->bwr) % Blocksize); 554 if(n != Blocksize) 555 Crepeat(cd, 0, n); 556 557 nb = Boffset(&cd->bwr)/Blocksize; 558 assert(nb != 0); 559 if(nb > cd->nextblock) 560 cd->nextblock = nb; 561 } 562 563 void 564 Cputdate(Cdimg *cd, ulong ust) 565 { 566 Tm *tm; 567 568 if(ust == 0) { 569 Crepeat(cd, 0, 7); 570 return; 571 } 572 tm = gmtime(ust); 573 Cputc(cd, tm->year); 574 Cputc(cd, tm->mon+1); 575 Cputc(cd, tm->mday); 576 Cputc(cd, tm->hour); 577 Cputc(cd, tm->min); 578 Cputc(cd, tm->sec); 579 Cputc(cd, 0); 580 } 581 582 void 583 Cputdate1(Cdimg *cd, ulong ust) 584 { 585 Tm *tm; 586 char str[20]; 587 588 if(ust == 0) { 589 Crepeat(cd, '0', 16); 590 Cputc(cd, 0); 591 return; 592 } 593 tm = gmtime(ust); 594 sprint(str, "%.4d%.2d%.2d%.2d%.2d%.4d", 595 tm->year+1900, 596 tm->mon+1, 597 tm->mday, 598 tm->hour, 599 tm->min, 600 tm->sec*100); 601 Cputs(cd, str, 16); 602 Cputc(cd, 0); 603 } 604 605 void 606 Cwseek(Cdimg *cd, vlong offset) 607 { 608 Bseek(&cd->bwr, offset, 0); 609 } 610 611 uvlong 612 Cwoffset(Cdimg *cd) 613 { 614 return Boffset(&cd->bwr); 615 } 616 617 void 618 Cwflush(Cdimg *cd) 619 { 620 Bflush(&cd->bwr); 621 } 622 623 uvlong 624 Croffset(Cdimg *cd) 625 { 626 return Boffset(&cd->brd); 627 } 628 629 void 630 Crseek(Cdimg *cd, vlong offset) 631 { 632 Bseek(&cd->brd, offset, 0); 633 } 634 635 int 636 Cgetc(Cdimg *cd) 637 { 638 int c; 639 640 Cwflush(cd); 641 if((c = Bgetc(&cd->brd)) == Beof) { 642 fprint(2, "getc at %llud\n", Croffset(cd)); 643 assert(0); 644 //sysfatal("Bgetc: %r"); 645 } 646 return c; 647 } 648 649 void 650 Cread(Cdimg *cd, void *buf, int n) 651 { 652 Cwflush(cd); 653 if(Bread(&cd->brd, buf, n) != n) 654 sysfatal("Bread: %r"); 655 } 656 657 char* 658 Crdline(Cdimg *cd, int c) 659 { 660 Cwflush(cd); 661 return Brdline(&cd->brd, c); 662 } 663 664 int 665 Clinelen(Cdimg *cd) 666 { 667 return Blinelen(&cd->brd); 668 } 669 670