plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

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 }