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
dumpbootvol(void * a)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
Dfmt(Fmt * fmt)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
bigend(void)141 bigend(void)
142 {
143 longc = 'B';
144 }
145
146 static void
littleend(void)147 littleend(void)
148 {
149 longc = 'L';
150 }
151
152 static ulong
big(void * a,int n)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
little(void * a,int n)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
BLfmt(Fmt * fmt)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
Nfmt(Fmt * fmt)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
asciiTfmt(Fmt * fmt)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
ascii(void)240 ascii(void)
241 {
242 fmtinstall('T', asciiTfmt);
243 }
244
245 static int
runeTfmt(Fmt * fmt)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
getsect(uchar * buf,int n)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*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
iso9660init(int xfd,Header * xh,char * xfile9660,int xoff9660)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
iso9660labels(Disk * disk,uchar * buf,void (* write)(int,u32int))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
iso9660copy(Fs * fs)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
iso9660start(Cdir * c)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
iso9660copydir(Fs * fs,File * dir,Cdir * cd)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*
getname(uchar ** pp)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*
getcname(Cdir * c)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
getcdate(uchar * p)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
iso9660copyfile(Fs * fs,File * dir,Cdir * c)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