1 /* 2 * Initialize a fossil file system from an ISO9660 image already in the 3 * file system. This is a fairly bizarre thing to do, but it lets us generate 4 * installation CDs that double as valid Plan 9 disk partitions. 5 * People having trouble booting the CD can just copy it into a disk 6 * partition and you've got a working Plan 9 system. 7 * 8 * I've tried hard to keep all the associated cruft in this file. 9 * If you deleted this file and cut out the three calls into it from flfmt.c, 10 * no traces would remain. 11 */ 12 13 #include "stdinc.h" 14 #include "dat.h" 15 #include "fns.h" 16 #include "flfmt9660.h" 17 #include <bio.h> 18 #include <ctype.h> 19 20 static Biobuf *b; 21 22 enum{ 23 Tag = 0x96609660, 24 Blocksize = 2048, 25 }; 26 27 #pragma varargck type "s" uchar* 28 #pragma varargck type "L" uchar* 29 #pragma varargck type "B" uchar* 30 #pragma varargck type "N" uchar* 31 #pragma varargck type "T" uchar* 32 #pragma varargck type "D" uchar* 33 34 typedef struct Voldesc Voldesc; 35 struct Voldesc { 36 uchar magic[8]; /* 0x01, "CD001", 0x01, 0x00 */ 37 uchar systemid[32]; /* system identifier */ 38 uchar volumeid[32]; /* volume identifier */ 39 uchar unused[8]; /* character set in secondary desc */ 40 uchar volsize[8]; /* volume size */ 41 uchar charset[32]; 42 uchar volsetsize[4]; /* volume set size = 1 */ 43 uchar volseqnum[4]; /* volume sequence number = 1 */ 44 uchar blocksize[4]; /* logical block size */ 45 uchar pathsize[8]; /* path table size */ 46 uchar lpathloc[4]; /* Lpath */ 47 uchar olpathloc[4]; /* optional Lpath */ 48 uchar mpathloc[4]; /* Mpath */ 49 uchar ompathloc[4]; /* optional Mpath */ 50 uchar rootdir[34]; /* root directory */ 51 uchar volsetid[128]; /* volume set identifier */ 52 uchar publisher[128]; 53 uchar prepid[128]; /* data preparer identifier */ 54 uchar applid[128]; /* application identifier */ 55 uchar notice[37]; /* copyright notice file */ 56 uchar abstract[37]; /* abstract file */ 57 uchar biblio[37]; /* bibliographic file */ 58 uchar cdate[17]; /* creation date */ 59 uchar mdate[17]; /* modification date */ 60 uchar xdate[17]; /* expiration date */ 61 uchar edate[17]; /* effective date */ 62 uchar fsvers; /* file system version = 1 */ 63 }; 64 65 static void 66 dumpbootvol(void *a) 67 { 68 Voldesc *v; 69 70 v = a; 71 print("magic %.2ux %.5s %.2ux %2ux\n", 72 v->magic[0], v->magic+1, v->magic[6], v->magic[7]); 73 if(v->magic[0] == 0xFF) 74 return; 75 76 print("system %.32T\n", v->systemid); 77 print("volume %.32T\n", v->volumeid); 78 print("volume size %.4N\n", v->volsize); 79 print("charset %.2ux %.2ux %.2ux %.2ux %.2ux %.2ux %.2ux %.2ux\n", 80 v->charset[0], v->charset[1], v->charset[2], v->charset[3], 81 v->charset[4], v->charset[5], v->charset[6], v->charset[7]); 82 print("volume set size %.2N\n", v->volsetsize); 83 print("volume sequence number %.2N\n", v->volseqnum); 84 print("logical block size %.2N\n", v->blocksize); 85 print("path size %.4L\n", v->pathsize); 86 print("lpath loc %.4L\n", v->lpathloc); 87 print("opt lpath loc %.4L\n", v->olpathloc); 88 print("mpath loc %.4B\n", v->mpathloc); 89 print("opt mpath loc %.4B\n", v->ompathloc); 90 print("rootdir %D\n", v->rootdir); 91 print("volume set identifier %.128T\n", v->volsetid); 92 print("publisher %.128T\n", v->publisher); 93 print("preparer %.128T\n", v->prepid); 94 print("application %.128T\n", v->applid); 95 print("notice %.37T\n", v->notice); 96 print("abstract %.37T\n", v->abstract); 97 print("biblio %.37T\n", v->biblio); 98 print("creation date %.17s\n", v->cdate); 99 print("modification date %.17s\n", v->mdate); 100 print("expiration date %.17s\n", v->xdate); 101 print("effective date %.17s\n", v->edate); 102 print("fs version %d\n", v->fsvers); 103 } 104 105 typedef struct Cdir Cdir; 106 struct Cdir { 107 uchar len; 108 uchar xlen; 109 uchar dloc[8]; 110 uchar dlen[8]; 111 uchar date[7]; 112 uchar flags; 113 uchar unitsize; 114 uchar gapsize; 115 uchar volseqnum[4]; 116 uchar namelen; 117 uchar name[1]; /* chumminess */ 118 }; 119 #pragma varargck type "D" Cdir* 120 121 static int 122 Dfmt(Fmt *fmt) 123 { 124 char buf[128]; 125 Cdir *c; 126 127 c = va_arg(fmt->args, Cdir*); 128 if(c->namelen == 1 && c->name[0] == '\0' || c->name[0] == '\001') { 129 snprint(buf, sizeof buf, ".%s dloc %.4N dlen %.4N", 130 c->name[0] ? "." : "", c->dloc, c->dlen); 131 } else { 132 snprint(buf, sizeof buf, "%.*T dloc %.4N dlen %.4N", c->namelen, c->name, 133 c->dloc, c->dlen); 134 } 135 fmtstrcpy(fmt, buf); 136 return 0; 137 } 138 139 char longc, shortc; 140 static void 141 bigend(void) 142 { 143 longc = 'B'; 144 } 145 146 static void 147 littleend(void) 148 { 149 longc = 'L'; 150 } 151 152 static ulong 153 big(void *a, int n) 154 { 155 uchar *p; 156 ulong v; 157 int i; 158 159 p = a; 160 v = 0; 161 for(i=0; i<n; i++) 162 v = (v<<8) | *p++; 163 return v; 164 } 165 166 static ulong 167 little(void *a, int n) 168 { 169 uchar *p; 170 ulong v; 171 int i; 172 173 p = a; 174 v = 0; 175 for(i=0; i<n; i++) 176 v |= (*p++<<(i*8)); 177 return v; 178 } 179 180 /* numbers in big or little endian. */ 181 static int 182 BLfmt(Fmt *fmt) 183 { 184 ulong v; 185 uchar *p; 186 char buf[20]; 187 188 p = va_arg(fmt->args, uchar*); 189 190 if(!(fmt->flags&FmtPrec)) { 191 fmtstrcpy(fmt, "*BL*"); 192 return 0; 193 } 194 195 if(fmt->r == 'B') 196 v = big(p, fmt->prec); 197 else 198 v = little(p, fmt->prec); 199 200 sprint(buf, "0x%.*lux", fmt->prec*2, v); 201 fmt->flags &= ~FmtPrec; 202 fmtstrcpy(fmt, buf); 203 return 0; 204 } 205 206 /* numbers in both little and big endian */ 207 static int 208 Nfmt(Fmt *fmt) 209 { 210 char buf[100]; 211 uchar *p; 212 213 p = va_arg(fmt->args, uchar*); 214 215 sprint(buf, "%.*L %.*B", fmt->prec, p, fmt->prec, p+fmt->prec); 216 fmt->flags &= ~FmtPrec; 217 fmtstrcpy(fmt, buf); 218 return 0; 219 } 220 221 static int 222 asciiTfmt(Fmt *fmt) 223 { 224 char *p, buf[256]; 225 int i; 226 227 p = va_arg(fmt->args, char*); 228 for(i=0; i<fmt->prec; i++) 229 buf[i] = *p++; 230 buf[i] = '\0'; 231 for(p=buf+strlen(buf); p>buf && p[-1]==' '; p--) 232 ; 233 p[0] = '\0'; 234 fmt->flags &= ~FmtPrec; 235 fmtstrcpy(fmt, buf); 236 return 0; 237 } 238 239 static void 240 ascii(void) 241 { 242 fmtinstall('T', asciiTfmt); 243 } 244 245 static int 246 runeTfmt(Fmt *fmt) 247 { 248 Rune buf[256], *r; 249 int i; 250 uchar *p; 251 252 p = va_arg(fmt->args, uchar*); 253 for(i=0; i*2+2<=fmt->prec; i++, p+=2) 254 buf[i] = (p[0]<<8)|p[1]; 255 buf[i] = L'\0'; 256 for(r=buf+i; r>buf && r[-1]==L' '; r--) 257 ; 258 r[0] = L'\0'; 259 fmt->flags &= ~FmtPrec; 260 return fmtprint(fmt, "%S", buf); 261 } 262 263 static void 264 getsect(uchar *buf, int n) 265 { 266 if(Bseek(b, n*2048, 0) != n*2048 || Bread(b, buf, 2048) != 2048) 267 { 268 abort(); 269 sysfatal("reading block at %,d: %r\n", n*2048); 270 } 271 } 272 273 static Header *h; 274 static int fd; 275 static char *file9660; 276 static int off9660; 277 static ulong startoff; 278 static ulong endoff; 279 static ulong fsoff; 280 static uchar root[2048]; 281 static Voldesc *v; 282 static ulong iso9660start(Cdir*); 283 static void iso9660copydir(Fs*, File*, Cdir*); 284 static void iso9660copyfile(Fs*, File*, Cdir*); 285 286 void 287 iso9660init(int xfd, Header *xh, char *xfile9660, int xoff9660) 288 { 289 uchar sect[2048], sect2[2048]; 290 291 fmtinstall('L', BLfmt); 292 fmtinstall('B', BLfmt); 293 fmtinstall('N', Nfmt); 294 fmtinstall('D', Dfmt); 295 296 fd = xfd; 297 h = xh; 298 file9660 = xfile9660; 299 off9660 = xoff9660; 300 301 if((b = Bopen(file9660, OREAD)) == nil) 302 vtFatal("Bopen %s: %r", file9660); 303 304 getsect(root, 16); 305 ascii(); 306 307 v = (Voldesc*)root; 308 if(memcmp(v->magic, "\x01CD001\x01\x00", 8) != 0) 309 vtFatal("%s not a cd image", file9660); 310 311 startoff = iso9660start((Cdir*)v->rootdir)*Blocksize; 312 endoff = little(v->volsize, 4); /* already in bytes */ 313 314 fsoff = off9660 + h->data*h->blockSize; 315 if(fsoff > startoff) 316 vtFatal("fossil data starts after cd data"); 317 if(off9660 + (vlong)h->end*h->blockSize < endoff) 318 vtFatal("fossil data ends before cd data"); 319 if(fsoff%h->blockSize) 320 vtFatal("cd offset not a multiple of fossil block size"); 321 322 /* Read "same" block via CD image and via Fossil image */ 323 getsect(sect, startoff/Blocksize); 324 if(seek(fd, startoff-off9660, 0) < 0) 325 vtFatal("cannot seek to first data sector on cd via fossil"); 326 fprint(2, "look for %lud at %lud\n", startoff, startoff-off9660); 327 if(readn(fd, sect2, Blocksize) != Blocksize) 328 vtFatal("cannot read first data sector on cd via fossil"); 329 if(memcmp(sect, sect2, Blocksize) != 0) 330 vtFatal("iso9660 offset is a lie %08ux %08ux", *(long*)sect, *(long*)sect2); 331 } 332 333 void 334 iso9660labels(Disk *disk, uchar *buf, void (*write)(int, u32int)) 335 { 336 ulong sb, eb, bn, lb, llb; 337 Label l; 338 int lpb; 339 uchar sect[Blocksize]; 340 341 if(!diskReadRaw(disk, PartData, (startoff-fsoff)/h->blockSize, buf)) 342 vtFatal("disk read failed: %r"); 343 getsect(sect, startoff/Blocksize); 344 if(memcmp(buf, sect, Blocksize) != 0) 345 vtFatal("fsoff is wrong"); 346 347 sb = (startoff-fsoff)/h->blockSize; 348 eb = (endoff-fsoff+h->blockSize-1)/h->blockSize; 349 350 lpb = h->blockSize/LabelSize; 351 352 /* for each reserved block, mark label */ 353 llb = ~0; 354 l.type = BtData; 355 l.state = BsAlloc; 356 l.tag = Tag; 357 l.epoch = 1; 358 l.epochClose = ~(u32int)0; 359 for(bn=sb; bn<eb; bn++){ 360 lb = bn/lpb; 361 if(lb != llb){ 362 if(llb != ~0) 363 (*write)(PartLabel, llb); 364 memset(buf, 0, h->blockSize); 365 } 366 llb = lb; 367 labelPack(&l, buf, bn%lpb); 368 } 369 if(llb != ~0) 370 (*write)(PartLabel, llb); 371 } 372 373 void 374 iso9660copy(Fs *fs) 375 { 376 File *root; 377 378 root = fileOpen(fs, "/active"); 379 iso9660copydir(fs, root, (Cdir*)v->rootdir); 380 fileDecRef(root); 381 vtRUnlock(fs->elk); 382 if(!fsSnapshot(fs, nil, nil, 0)) 383 vtFatal("snapshot failed: %R"); 384 vtRLock(fs->elk); 385 } 386 387 /* 388 * The first block used is the first data block of the leftmost file in the tree. 389 * (Just an artifact of how mk9660 works.) 390 */ 391 static ulong 392 iso9660start(Cdir *c) 393 { 394 uchar sect[Blocksize]; 395 396 while(c->flags&2){ 397 getsect(sect, little(c->dloc, 4)); 398 c = (Cdir*)sect; 399 c = (Cdir*)((uchar*)c+c->len); /* skip dot */ 400 c = (Cdir*)((uchar*)c+c->len); /* skip dotdot */ 401 /* oops: might happen if leftmost directory is empty or leftmost file is zero length! */ 402 if(little(c->dloc, 4) == 0) 403 vtFatal("error parsing cd image or unfortunate cd image"); 404 } 405 return little(c->dloc, 4); 406 } 407 408 static void 409 iso9660copydir(Fs *fs, File *dir, Cdir *cd) 410 { 411 ulong off, end, len; 412 uchar sect[Blocksize], *esect, *p; 413 Cdir *c; 414 415 len = little(cd->dlen, 4); 416 off = little(cd->dloc, 4)*Blocksize; 417 end = off+len; 418 esect = sect+Blocksize; 419 420 for(; off<end; off+=Blocksize){ 421 getsect(sect, off/Blocksize); 422 p = sect; 423 while(p < esect){ 424 c = (Cdir*)p; 425 if(c->len <= 0) 426 break; 427 if(c->namelen!=1 || c->name[0]>1) 428 iso9660copyfile(fs, dir, c); 429 p += c->len; 430 } 431 } 432 } 433 434 static char* 435 getname(uchar **pp) 436 { 437 uchar *p; 438 int l; 439 440 p = *pp; 441 l = *p; 442 *pp = p+1+l; 443 if(l == 0) 444 return ""; 445 memmove(p, p+1, l); 446 p[l] = 0; 447 return (char*)p; 448 } 449 450 static char* 451 getcname(Cdir *c) 452 { 453 uchar *up; 454 char *p, *q; 455 456 up = &c->namelen; 457 p = getname(&up); 458 for(q=p; *q; q++) 459 *q = tolower(*q); 460 return p; 461 } 462 463 static char 464 dmsize[12] = 465 { 466 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 467 }; 468 469 static ulong 470 getcdate(uchar *p) /* yMdhmsz */ 471 { 472 Tm tm; 473 int y, M, d, h, m, s, tz; 474 475 y=p[0]; M=p[1]; d=p[2]; 476 h=p[3]; m=p[4]; s=p[5]; tz=p[6]; 477 USED(tz); 478 if (y < 70) 479 return 0; 480 if (M < 1 || M > 12) 481 return 0; 482 if (d < 1 || d > dmsize[M-1]) 483 return 0; 484 if (h > 23) 485 return 0; 486 if (m > 59) 487 return 0; 488 if (s > 59) 489 return 0; 490 491 memset(&tm, 0, sizeof tm); 492 tm.sec = s; 493 tm.min = m; 494 tm.hour = h; 495 tm.mday = d; 496 tm.mon = M-1; 497 tm.year = 1900+y; 498 tm.zone[0] = 0; 499 return tm2sec(&tm); 500 } 501 502 static int ind; 503 504 static void 505 iso9660copyfile(Fs *fs, File *dir, Cdir *c) 506 { 507 Dir d; 508 DirEntry de; 509 int sysl; 510 uchar score[VtScoreSize]; 511 ulong off, foff, len, mode; 512 uchar *p; 513 File *f; 514 515 ind++; 516 memset(&d, 0, sizeof d); 517 p = c->name + c->namelen; 518 if(((uintptr)p) & 1) 519 p++; 520 sysl = (uchar*)c + c->len - p; 521 if(sysl <= 0) 522 vtFatal("missing plan9 directory entry on %d/%d/%.*s", c->namelen, c->name[0], c->namelen, c->name); 523 d.name = getname(&p); 524 d.uid = getname(&p); 525 d.gid = getname(&p); 526 if((uintptr)p & 1) 527 p++; 528 d.mode = little(p, 4); 529 if(d.name[0] == 0) 530 d.name = getcname(c); 531 d.mtime = getcdate(c->date); 532 d.atime = d.mtime; 533 534 if(d.mode&DMDIR) print("%*scopy %s %s %s %luo\n", ind*2, "", d.name, d.uid, d.gid, d.mode); 535 536 mode = d.mode&0777; 537 if(d.mode&DMDIR) 538 mode |= ModeDir; 539 if((f = fileCreate(dir, d.name, mode, d.uid)) == nil) 540 vtFatal("could not create file '%s': %r", d.name); 541 if(d.mode&DMDIR) 542 iso9660copydir(fs, f, c); 543 else{ 544 len = little(c->dlen, 4); 545 off = little(c->dloc, 4)*Blocksize; 546 for(foff=0; foff<len; foff+=h->blockSize){ 547 localToGlobal((off+foff-fsoff)/h->blockSize, score); 548 if(!fileMapBlock(f, foff/h->blockSize, score, Tag)) 549 vtFatal("fileMapBlock: %R"); 550 } 551 if(!fileSetSize(f, len)) 552 vtFatal("fileSetSize: %R"); 553 } 554 if(!fileGetDir(f, &de)) 555 vtFatal("fileGetDir: %R"); 556 de.uid = d.uid; 557 de.gid = d.gid; 558 de.mtime = d.mtime; 559 de.atime = d.atime; 560 de.mode = d.mode&0777; 561 if(!fileSetDir(f, &de, "sys")) 562 vtFatal("fileSetDir: %R"); 563 fileDecRef(f); 564 ind--; 565 } 566