Blob


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 */
13 #include "stdinc.h"
14 #include "dat.h"
15 #include "fns.h"
16 #include "flfmt9660.h"
17 #include <bio.h>
18 #include <ctype.h>
20 static Biobuf *b;
22 enum{
23 Tag = 0x96609660,
24 Blocksize = 2048,
25 };
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*
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 };
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*
81 static int
82 Dfmt(Fmt *fmt)
83 {
84 char buf[128];
85 Cdir *c;
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 }
99 static ulong
100 big(void *a, int n)
102 uchar *p;
103 ulong v;
104 int i;
106 p = a;
107 v = 0;
108 for(i=0; i<n; i++)
109 v = (v<<8) | *p++;
110 return v;
113 static ulong
114 little(void *a, int n)
116 uchar *p;
117 ulong v;
118 int i;
120 p = a;
121 v = 0;
122 for(i=0; i<n; i++)
123 v |= (*p++<<(i*8));
124 return v;
127 /* numbers in big or little endian. */
128 static int
129 BLfmt(Fmt *fmt)
131 ulong v;
132 uchar *p;
133 char buf[20];
135 p = va_arg(fmt->args, uchar*);
137 if(!(fmt->flags&FmtPrec)) {
138 fmtstrcpy(fmt, "*BL*");
139 return 0;
142 if(fmt->r == 'B')
143 v = big(p, fmt->prec);
144 else
145 v = little(p, fmt->prec);
147 sprint(buf, "0x%.*lux", fmt->prec*2, v);
148 fmt->flags &= ~FmtPrec;
149 fmtstrcpy(fmt, buf);
150 return 0;
153 /* numbers in both little and big endian */
154 static int
155 Nfmt(Fmt *fmt)
157 char buf[100];
158 uchar *p;
160 p = va_arg(fmt->args, uchar*);
162 sprint(buf, "%.*L %.*B", fmt->prec, p, fmt->prec, p+fmt->prec);
163 fmt->flags &= ~FmtPrec;
164 fmtstrcpy(fmt, buf);
165 return 0;
168 static int
169 asciiTfmt(Fmt *fmt)
171 char *p, buf[256];
172 int i;
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--)
180 p[0] = '\0';
181 fmt->flags &= ~FmtPrec;
182 fmtstrcpy(fmt, buf);
183 return 0;
186 static void
187 ascii(void)
189 fmtinstall('C', asciiTfmt);
192 static void
193 getsect(uchar *buf, int n)
195 if(Bseek(b, n*2048, 0) != n*2048 || Bread(b, buf, 2048) != 2048)
197 abort();
198 sysfatal("reading block at %,d: %r", n*2048);
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*);
215 void
216 iso9660init(int xfd, Header *xh, char *xfile9660, int xoff9660)
218 uchar sect[2048], sect2[2048];
220 fmtinstall('L', BLfmt);
221 fmtinstall('B', BLfmt);
222 fmtinstall('N', Nfmt);
223 fmtinstall('D', Dfmt);
225 fd = xfd;
226 h = xh;
227 file9660 = xfile9660;
228 off9660 = xoff9660;
230 if((b = Bopen(file9660, OREAD)) == nil)
231 sysfatal("Bopen %s: %r", file9660);
233 getsect(root, 16);
234 ascii();
236 v = (Voldesc*)root;
237 if(memcmp(v->magic, "\001CD001\001\000", 8) != 0)
238 sysfatal("%s not a cd image", file9660);
240 startoff = iso9660start((Cdir*)v->rootdir)*Blocksize;
241 endoff = little(v->volsize, 4); /* already in bytes */
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");
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");
262 void
263 iso9660labels(Disk *disk, uchar *buf, void (*write)(int, u32int))
265 ulong sb, eb, bn, lb, llb;
266 Label l;
267 int lpb;
268 uchar sect[Blocksize];
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");
276 sb = (startoff-fsoff)/h->blockSize;
277 eb = (endoff-fsoff+h->blockSize-1)/h->blockSize;
279 lpb = h->blockSize/LabelSize;
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);
295 llb = lb;
296 labelPack(&l, buf, bn%lpb);
298 if(llb != ~0)
299 (*write)(PartLabel, llb);
302 void
303 iso9660copy(Fs *fs)
305 File *root;
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);
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)
323 uchar sect[Blocksize];
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");
334 return little(c->dloc, 4);
337 static void
338 iso9660copydir(Fs *fs, File *dir, Cdir *cd)
340 ulong off, end, len;
341 uchar sect[Blocksize], *esect, *p;
342 Cdir *c;
344 len = little(cd->dlen, 4);
345 off = little(cd->dloc, 4)*Blocksize;
346 end = off+len;
347 esect = sect+Blocksize;
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;
363 static char*
364 getname(uchar **pp)
366 uchar *p;
367 int l;
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;
379 static char*
380 getcname(Cdir *c)
382 uchar *up;
383 char *p, *q;
385 up = &c->namelen;
386 p = getname(&up);
387 for(q=p; *q; q++)
388 *q = tolower(*q);
389 return p;
392 static char
393 dmsize[12] =
395 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
396 };
398 static ulong
399 getcdate(uchar *p) /* yMdhmsz */
401 Tm tm;
402 int y, M, d, h, m, s, tz;
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;
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);
431 static int ind;
433 static void
434 iso9660copyfile(Fs *fs, File *dir, Cdir *c)
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;
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;
463 if(d.mode&DMDIR) print("%*scopy %s %s %s %luo\n", ind*2, "", d.name, d.uid, d.gid, d.mode);
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");
480 if(!fileSetSize(f, len))
481 sysfatal("fileSetSize: %r");
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--;