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