flfmt9660.c (10445B)
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 "C" 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 typedef struct Cdir Cdir; 66 struct Cdir { 67 uchar len; 68 uchar xlen; 69 uchar dloc[8]; 70 uchar dlen[8]; 71 uchar date[7]; 72 uchar flags; 73 uchar unitsize; 74 uchar gapsize; 75 uchar volseqnum[4]; 76 uchar namelen; 77 uchar name[1]; /* chumminess */ 78 }; 79 #pragma varargck type "D" Cdir* 80 81 static int 82 Dfmt(Fmt *fmt) 83 { 84 char buf[128]; 85 Cdir *c; 86 87 c = va_arg(fmt->args, Cdir*); 88 if(c->namelen == 1 && c->name[0] == '\0' || c->name[0] == '\001') { 89 snprint(buf, sizeof buf, ".%s dloc %.4N dlen %.4N", 90 c->name[0] ? "." : "", c->dloc, c->dlen); 91 } else { 92 snprint(buf, sizeof buf, "%.*C dloc %.4N dlen %.4N", c->namelen, c->name, 93 c->dloc, c->dlen); 94 } 95 fmtstrcpy(fmt, buf); 96 return 0; 97 } 98 99 static ulong 100 big(void *a, int n) 101 { 102 uchar *p; 103 ulong v; 104 int i; 105 106 p = a; 107 v = 0; 108 for(i=0; i<n; i++) 109 v = (v<<8) | *p++; 110 return v; 111 } 112 113 static ulong 114 little(void *a, int n) 115 { 116 uchar *p; 117 ulong v; 118 int i; 119 120 p = a; 121 v = 0; 122 for(i=0; i<n; i++) 123 v |= (*p++<<(i*8)); 124 return v; 125 } 126 127 /* numbers in big or little endian. */ 128 static int 129 BLfmt(Fmt *fmt) 130 { 131 ulong v; 132 uchar *p; 133 char buf[20]; 134 135 p = va_arg(fmt->args, uchar*); 136 137 if(!(fmt->flags&FmtPrec)) { 138 fmtstrcpy(fmt, "*BL*"); 139 return 0; 140 } 141 142 if(fmt->r == 'B') 143 v = big(p, fmt->prec); 144 else 145 v = little(p, fmt->prec); 146 147 sprint(buf, "0x%.*lux", fmt->prec*2, v); 148 fmt->flags &= ~FmtPrec; 149 fmtstrcpy(fmt, buf); 150 return 0; 151 } 152 153 /* numbers in both little and big endian */ 154 static int 155 Nfmt(Fmt *fmt) 156 { 157 char buf[100]; 158 uchar *p; 159 160 p = va_arg(fmt->args, uchar*); 161 162 sprint(buf, "%.*L %.*B", fmt->prec, p, fmt->prec, p+fmt->prec); 163 fmt->flags &= ~FmtPrec; 164 fmtstrcpy(fmt, buf); 165 return 0; 166 } 167 168 static int 169 asciiTfmt(Fmt *fmt) 170 { 171 char *p, buf[256]; 172 int i; 173 174 p = va_arg(fmt->args, char*); 175 for(i=0; i<fmt->prec; i++) 176 buf[i] = *p++; 177 buf[i] = '\0'; 178 for(p=buf+strlen(buf); p>buf && p[-1]==' '; p--) 179 ; 180 p[0] = '\0'; 181 fmt->flags &= ~FmtPrec; 182 fmtstrcpy(fmt, buf); 183 return 0; 184 } 185 186 static void 187 ascii(void) 188 { 189 fmtinstall('C', asciiTfmt); 190 } 191 192 static void 193 getsect(uchar *buf, int n) 194 { 195 if(Bseek(b, n*2048, 0) != n*2048 || Bread(b, buf, 2048) != 2048) 196 { 197 abort(); 198 sysfatal("reading block at %,d: %r", n*2048); 199 } 200 } 201 202 static Header *h; 203 static int fd; 204 static char *file9660; 205 static int off9660; 206 static ulong startoff; 207 static ulong endoff; 208 static ulong fsoff; 209 static uchar root[2048]; 210 static Voldesc *v; 211 static ulong iso9660start(Cdir*); 212 static void iso9660copydir(Fs*, File*, Cdir*); 213 static void iso9660copyfile(Fs*, File*, Cdir*); 214 215 void 216 iso9660init(int xfd, Header *xh, char *xfile9660, int xoff9660) 217 { 218 uchar sect[2048], sect2[2048]; 219 220 fmtinstall('L', BLfmt); 221 fmtinstall('B', BLfmt); 222 fmtinstall('N', Nfmt); 223 fmtinstall('D', Dfmt); 224 225 fd = xfd; 226 h = xh; 227 file9660 = xfile9660; 228 off9660 = xoff9660; 229 230 if((b = Bopen(file9660, OREAD)) == nil) 231 sysfatal("Bopen %s: %r", file9660); 232 233 getsect(root, 16); 234 ascii(); 235 236 v = (Voldesc*)root; 237 if(memcmp(v->magic, "\001CD001\001\000", 8) != 0) 238 sysfatal("%s not a cd image", file9660); 239 240 startoff = iso9660start((Cdir*)v->rootdir)*Blocksize; 241 endoff = little(v->volsize, 4); /* already in bytes */ 242 243 fsoff = off9660 + h->data*h->blockSize; 244 if(fsoff > startoff) 245 sysfatal("fossil data starts after cd data"); 246 if(off9660 + (vlong)h->end*h->blockSize < endoff) 247 sysfatal("fossil data ends before cd data"); 248 if(fsoff%h->blockSize) 249 sysfatal("cd offset not a multiple of fossil block size"); 250 251 /* Read "same" block via CD image and via Fossil image */ 252 getsect(sect, startoff/Blocksize); 253 if(seek(fd, startoff-off9660, 0) < 0) 254 sysfatal("cannot seek to first data sector on cd via fossil"); 255 fprint(2, "look for %lud at %lud\n", startoff, startoff-off9660); 256 if(readn(fd, sect2, Blocksize) != Blocksize) 257 sysfatal("cannot read first data sector on cd via fossil"); 258 if(memcmp(sect, sect2, Blocksize) != 0) 259 sysfatal("iso9660 offset is a lie"); 260 } 261 262 void 263 iso9660labels(Disk *disk, uchar *buf, void (*write)(int, u32int)) 264 { 265 ulong sb, eb, bn, lb, llb; 266 Label l; 267 int lpb; 268 uchar sect[Blocksize]; 269 270 if(!diskReadRaw(disk, PartData, (startoff-fsoff)/h->blockSize, buf)) 271 sysfatal("disk read failed: %r"); 272 getsect(sect, startoff/Blocksize); 273 if(memcmp(buf, sect, Blocksize) != 0) 274 sysfatal("fsoff is wrong"); 275 276 sb = (startoff-fsoff)/h->blockSize; 277 eb = (endoff-fsoff+h->blockSize-1)/h->blockSize; 278 279 lpb = h->blockSize/LabelSize; 280 281 /* for each reserved block, mark label */ 282 llb = ~0; 283 l.type = BtData; 284 l.state = BsAlloc; 285 l.tag = Tag; 286 l.epoch = 1; 287 l.epochClose = ~(u32int)0; 288 for(bn=sb; bn<eb; bn++){ 289 lb = bn/lpb; 290 if(lb != llb){ 291 if(llb != ~0) 292 (*write)(PartLabel, llb); 293 memset(buf, 0, h->blockSize); 294 } 295 llb = lb; 296 labelPack(&l, buf, bn%lpb); 297 } 298 if(llb != ~0) 299 (*write)(PartLabel, llb); 300 } 301 302 void 303 iso9660copy(Fs *fs) 304 { 305 File *root; 306 307 root = fileOpen(fs, "/active"); 308 iso9660copydir(fs, root, (Cdir*)v->rootdir); 309 fileDecRef(root); 310 runlock(&fs->elk); 311 if(!fsSnapshot(fs, nil, nil, 0)) 312 sysfatal("snapshot failed: %r"); 313 rlock(&fs->elk); 314 } 315 316 /* 317 * The first block used is the first data block of the leftmost file in the tree. 318 * (Just an artifact of how mk9660 works.) 319 */ 320 static ulong 321 iso9660start(Cdir *c) 322 { 323 uchar sect[Blocksize]; 324 325 while(c->flags&2){ 326 getsect(sect, little(c->dloc, 4)); 327 c = (Cdir*)sect; 328 c = (Cdir*)((uchar*)c+c->len); /* skip dot */ 329 c = (Cdir*)((uchar*)c+c->len); /* skip dotdot */ 330 /* oops: might happen if leftmost directory is empty or leftmost file is zero length! */ 331 if(little(c->dloc, 4) == 0) 332 sysfatal("error parsing cd image or unfortunate cd image"); 333 } 334 return little(c->dloc, 4); 335 } 336 337 static void 338 iso9660copydir(Fs *fs, File *dir, Cdir *cd) 339 { 340 ulong off, end, len; 341 uchar sect[Blocksize], *esect, *p; 342 Cdir *c; 343 344 len = little(cd->dlen, 4); 345 off = little(cd->dloc, 4)*Blocksize; 346 end = off+len; 347 esect = sect+Blocksize; 348 349 for(; off<end; off+=Blocksize){ 350 getsect(sect, off/Blocksize); 351 p = sect; 352 while(p < esect){ 353 c = (Cdir*)p; 354 if(c->len <= 0) 355 break; 356 if(c->namelen!=1 || c->name[0]>1) 357 iso9660copyfile(fs, dir, c); 358 p += c->len; 359 } 360 } 361 } 362 363 static char* 364 getname(uchar **pp) 365 { 366 uchar *p; 367 int l; 368 369 p = *pp; 370 l = *p; 371 *pp = p+1+l; 372 if(l == 0) 373 return ""; 374 memmove(p, p+1, l); 375 p[l] = 0; 376 return (char*)p; 377 } 378 379 static char* 380 getcname(Cdir *c) 381 { 382 uchar *up; 383 char *p, *q; 384 385 up = &c->namelen; 386 p = getname(&up); 387 for(q=p; *q; q++) 388 *q = tolower(*q); 389 return p; 390 } 391 392 static char 393 dmsize[12] = 394 { 395 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 396 }; 397 398 static ulong 399 getcdate(uchar *p) /* yMdhmsz */ 400 { 401 Tm tm; 402 int y, M, d, h, m, s, tz; 403 404 y=p[0]; M=p[1]; d=p[2]; 405 h=p[3]; m=p[4]; s=p[5]; tz=p[6]; 406 USED(tz); 407 if (y < 70) 408 return 0; 409 if (M < 1 || M > 12) 410 return 0; 411 if (d < 1 || d > dmsize[M-1]) 412 return 0; 413 if (h > 23) 414 return 0; 415 if (m > 59) 416 return 0; 417 if (s > 59) 418 return 0; 419 420 memset(&tm, 0, sizeof tm); 421 tm.sec = s; 422 tm.min = m; 423 tm.hour = h; 424 tm.mday = d; 425 tm.mon = M-1; 426 tm.year = 1900+y; 427 tm.zone[0] = 0; 428 return tm2sec(&tm); 429 } 430 431 static int ind; 432 433 static void 434 iso9660copyfile(Fs *fs, File *dir, Cdir *c) 435 { 436 Dir d; 437 DirEntry de; 438 int sysl; 439 uchar score[VtScoreSize]; 440 ulong off, foff, len, mode; 441 uchar *p; 442 File *f; 443 444 ind++; 445 memset(&d, 0, sizeof d); 446 p = c->name + c->namelen; 447 if(((uintptr)p) & 1) 448 p++; 449 sysl = (uchar*)c + c->len - p; 450 if(sysl <= 0) 451 sysfatal("missing plan9 directory entry on %d/%d/%.*s", c->namelen, c->name[0], c->namelen, c->name); 452 d.name = getname(&p); 453 d.uid = getname(&p); 454 d.gid = getname(&p); 455 if((uintptr)p & 1) 456 p++; 457 d.mode = little(p, 4); 458 if(d.name[0] == 0) 459 d.name = getcname(c); 460 d.mtime = getcdate(c->date); 461 d.atime = d.mtime; 462 463 if(d.mode&DMDIR) print("%*scopy %s %s %s %luo\n", ind*2, "", d.name, d.uid, d.gid, d.mode); 464 465 mode = d.mode&0777; 466 if(d.mode&DMDIR) 467 mode |= ModeDir; 468 if((f = fileCreate(dir, d.name, mode, d.uid)) == nil) 469 sysfatal("could not create file '%s': %r", d.name); 470 if(d.mode&DMDIR) 471 iso9660copydir(fs, f, c); 472 else{ 473 len = little(c->dlen, 4); 474 off = little(c->dloc, 4)*Blocksize; 475 for(foff=0; foff<len; foff+=h->blockSize){ 476 localToGlobal((off+foff-fsoff)/h->blockSize, score); 477 if(!fileMapBlock(f, foff/h->blockSize, score, Tag)) 478 sysfatal("fileMapBlock: %r"); 479 } 480 if(!fileSetSize(f, len)) 481 sysfatal("fileSetSize: %r"); 482 } 483 if(!fileGetDir(f, &de)) 484 sysfatal("fileGetDir: %r"); 485 de.uid = d.uid; 486 de.gid = d.gid; 487 de.mtime = d.mtime; 488 de.atime = d.atime; 489 de.mode = d.mode&0777; 490 if(!fileSetDir(f, &de, "sys")) 491 sysfatal("fileSetDir: %r"); 492 fileDecRef(f); 493 ind--; 494 }