plan9port

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

cdrdwr.c (11160B)


      1 #include <u.h>
      2 #include <libc.h>
      3 #include <bio.h>
      4 #include <libsec.h>
      5 
      6 #include "iso9660.h"
      7 
      8 static int readisodesc(Cdimg*, Voldesc*);
      9 static int readjolietdesc(Cdimg*, Voldesc*);
     10 
     11 /*
     12  * It's not strictly conforming; instead it's enough to
     13  * get us up and running; presumably the real CD writing
     14  * will take care of being conforming.
     15  *
     16  * Things not conforming include:
     17  *	- no path table
     18  *	- root directories are of length zero
     19  */
     20 Cdimg*
     21 createcd(char *file, Cdinfo info)
     22 {
     23 	int fd, xfd;
     24 	Cdimg *cd;
     25 
     26 	if(access(file, AEXIST) == 0){
     27 		werrstr("file already exists");
     28 		return nil;
     29 	}
     30 
     31 	if((fd = create(file, ORDWR, 0666)) < 0)
     32 		return nil;
     33 
     34 	cd = emalloc(sizeof *cd);
     35 	cd->file = atom(file);
     36 
     37 	Binit(&cd->brd, fd, OREAD);
     38 
     39 	if((xfd = open(file, ORDWR)) < 0)
     40 		sysfatal("can't open file again: %r");
     41 	Binit(&cd->bwr, xfd, OWRITE);
     42 
     43 	Crepeat(cd, 0, 16*Blocksize);
     44 	Cputisopvd(cd, info);
     45 	if(info.flags & CDbootable){
     46 		cd->bootimage = info.bootimage;
     47 		cd->flags |= CDbootable;
     48 		Cputbootvol(cd);
     49 	}
     50 
     51 	if(readisodesc(cd, &cd->iso) < 0)
     52 		assert(0);
     53 	if(info.flags & CDplan9)
     54 		cd->flags |= CDplan9;
     55 	else if(info.flags & CDrockridge)
     56 		cd->flags |= CDrockridge;
     57 	if(info.flags & CDjoliet) {
     58 		Cputjolietsvd(cd, info);
     59 		if(readjolietdesc(cd, &cd->joliet) < 0)
     60 			assert(0);
     61 		cd->flags |= CDjoliet;
     62 	}
     63 	Cputendvd(cd);
     64 
     65 	if(info.flags & CDdump){
     66 		cd->nulldump = Cputdumpblock(cd);
     67 		cd->flags |= CDdump;
     68 	}
     69 	if(cd->flags & CDbootable){
     70 		Cputbootcat(cd);
     71 		Cupdatebootvol(cd);
     72 	}
     73 
     74 	if(info.flags & CDconform)
     75 		cd->flags |= CDconform;
     76 
     77 	cd->flags |= CDnew;
     78 	cd->nextblock = Cwoffset(cd) / Blocksize;
     79 	assert(cd->nextblock != 0);
     80 
     81 	return cd;
     82 }
     83 
     84 Cdimg*
     85 opencd(char *file, Cdinfo info)
     86 {
     87 	int fd, xfd;
     88 	Cdimg *cd;
     89 	Dir *d;
     90 
     91 	if((fd = open(file, ORDWR)) < 0) {
     92 		if(access(file, AEXIST) == 0)
     93 			return nil;
     94 		return createcd(file, info);
     95 	}
     96 
     97 	if((d = dirfstat(fd)) == nil) {
     98 		close(fd);
     99 		return nil;
    100 	}
    101 	if(d->length == 0 || d->length % Blocksize) {
    102 		werrstr("bad length %lld", d->length);
    103 		close(fd);
    104 		free(d);
    105 		return nil;
    106 	}
    107 
    108 	cd = emalloc(sizeof *cd);
    109 	cd->file = atom(file);
    110 	cd->nextblock = d->length / Blocksize;
    111 	assert(cd->nextblock != 0);
    112 	free(d);
    113 
    114 	Binit(&cd->brd, fd, OREAD);
    115 
    116 	if((xfd = open(file, ORDWR)) < 0)
    117 		sysfatal("can't open file again: %r");
    118 	Binit(&cd->bwr, xfd, OWRITE);
    119 
    120 	if(readisodesc(cd, &cd->iso) < 0) {
    121 		free(cd);
    122 		close(fd);
    123 		close(xfd);
    124 		return nil;
    125 	}
    126 
    127 	/* lowercase because of isostring */
    128 	if(strstr(cd->iso.systemid, "iso9660") == nil
    129 	&& strstr(cd->iso.systemid, "utf8") == nil) {
    130 		werrstr("unknown systemid %s", cd->iso.systemid);
    131 		free(cd);
    132 		close(fd);
    133 		close(xfd);
    134 		return nil;
    135 	}
    136 
    137 	if(strstr(cd->iso.systemid, "plan 9"))
    138 		cd->flags |= CDplan9;
    139 	if(strstr(cd->iso.systemid, "iso9660"))
    140 		cd->flags |= CDconform;
    141 	if(strstr(cd->iso.systemid, "rrip"))
    142 		cd->flags |= CDrockridge;
    143 	if(strstr(cd->iso.systemid, "boot"))
    144 		cd->flags |= CDbootable;
    145 	if(readjolietdesc(cd, &cd->joliet) == 0)
    146 		cd->flags |= CDjoliet;
    147 	if(hasdump(cd))
    148 		cd->flags |= CDdump;
    149 
    150 	return cd;
    151 }
    152 
    153 ulong
    154 big(void *a, int n)
    155 {
    156 	uchar *p;
    157 	ulong v;
    158 	int i;
    159 
    160 	p = a;
    161 	v = 0;
    162 	for(i=0; i<n; i++)
    163 		v = (v<<8) | *p++;
    164 	return v;
    165 }
    166 
    167 ulong
    168 little(void *a, int n)
    169 {
    170 	uchar *p;
    171 	ulong v;
    172 	int i;
    173 
    174 	p = a;
    175 	v = 0;
    176 	for(i=0; i<n; i++)
    177 		v |= (*p++<<(i*8));
    178 	return v;
    179 }
    180 
    181 void
    182 Creadblock(Cdimg *cd, void *buf, ulong block, ulong len)
    183 {
    184 	assert(block != 0);	/* nothing useful there */
    185 
    186 	Bflush(&cd->bwr);
    187 	if(Bseek(&cd->brd, block*Blocksize, 0) != block*Blocksize)
    188 		sysfatal("error seeking to block %lud", block);
    189 	if(Bread(&cd->brd, buf, len) != len)
    190 		sysfatal("error reading %lud bytes at block %lud: %r %lld", len, block, Bseek(&cd->brd, 0, 2));
    191 }
    192 
    193 int
    194 parsedir(Cdimg *cd, Direc *d, uchar *buf, int len, char *(*cvtname)(uchar*, int))
    195 {
    196 	enum { NAMELEN = 28 };
    197 	char name[NAMELEN];
    198 	uchar *p;
    199 	Cdir *c;
    200 
    201 	memset(d, 0, sizeof *d);
    202 
    203 	c = (Cdir*)buf;
    204 
    205 	if(c->len > len) {
    206 		werrstr("buffer too small");
    207 		return -1;
    208 	}
    209 
    210 	if(c->namelen == 1 && c->name[0] == '\0')
    211 		d->name = atom(".");
    212 	else if(c->namelen == 1 && c->name[0] == '\001')
    213 		d->name = atom("..");
    214 	else if(cvtname)
    215 		d->name = cvtname(c->name, c->namelen);
    216 
    217 	d->block = little(c->dloc, 4);
    218 	d->length = little(c->dlen, 4);
    219 
    220 	if(c->flags & 2)
    221 		d->mode |= DMDIR;
    222 
    223 /*BUG: do we really need to parse the plan 9 fields? */
    224 	/* plan 9 use fields */
    225 	if((cd->flags & CDplan9) && cvtname == isostring
    226 	&& (c->namelen != 1 || c->name[0] > 1)) {
    227 		p = buf+33+c->namelen;
    228 		if((p-buf)&1)
    229 			p++;
    230 		assert(p < buf+c->len);
    231 		assert(*p < NAMELEN);
    232 		if(*p != 0) {
    233 			memmove(name, p+1, *p);
    234 			name[*p] = '\0';
    235 			d->confname = d->name;
    236 			d->name = atom(name);
    237 		}
    238 		p += *p+1;
    239 		assert(*p < NAMELEN);
    240 		memmove(name, p+1, *p);
    241 		name[*p] = '\0';
    242 		d->uid = atom(name);
    243 		p += *p+1;
    244 		assert(*p < NAMELEN);
    245 		memmove(name, p+1, *p);
    246 		name[*p] = '\0';
    247 		d->gid = atom(name);
    248 		p += *p+1;
    249 		if((p-buf)&1)
    250 			p++;
    251 		d->mode = little(p, 4);
    252 	}
    253 
    254 	/* BUG: rock ridge extensions */
    255 	return 0;
    256 }
    257 
    258 void
    259 setroot(Cdimg *cd, ulong block, ulong dloc, ulong dlen)
    260 {
    261 	assert(block != 0);
    262 
    263 	Cwseek(cd, block*Blocksize+offsetof(Cvoldesc, rootdir[0])+offsetof(Cdir, dloc[0]));
    264 	Cputn(cd, dloc, 4);
    265 	Cputn(cd, dlen, 4);
    266 }
    267 
    268 void
    269 setvolsize(Cdimg *cd, ulong block, ulong size)
    270 {
    271 	assert(block != 0);
    272 
    273 	Cwseek(cd, block*Blocksize+offsetof(Cvoldesc, volsize[0]));
    274 	Cputn(cd, size, 4);
    275 }
    276 
    277 void
    278 setpathtable(Cdimg *cd, ulong block, ulong sz, ulong lloc, ulong bloc)
    279 {
    280 	assert(block != 0);
    281 
    282 	Cwseek(cd, block*Blocksize+offsetof(Cvoldesc, pathsize[0]));
    283 	Cputn(cd, sz, 4);
    284 	Cputnl(cd, lloc, 4);
    285 	Cputnl(cd, 0, 4);
    286 	Cputnm(cd, bloc, 4);
    287 	Cputnm(cd, 0, 4);
    288 	assert(Cwoffset(cd) == block*Blocksize+offsetof(Cvoldesc, rootdir[0]));
    289 }
    290 
    291 
    292 static void
    293 parsedesc(Voldesc *v, Cvoldesc *cv, char *(*string)(uchar*, int))
    294 {
    295 	v->systemid = string(cv->systemid, sizeof cv->systemid);
    296 
    297 	v->pathsize = little(cv->pathsize, 4);
    298 	v->lpathloc = little(cv->lpathloc, 4);
    299 	v->mpathloc = little(cv->mpathloc, 4);
    300 
    301 	v->volumeset = string(cv->volumeset, sizeof cv->volumeset);
    302 	v->publisher = string(cv->publisher, sizeof cv->publisher);
    303 	v->preparer = string(cv->preparer, sizeof cv->preparer);
    304 	v->application = string(cv->application, sizeof cv->application);
    305 
    306 	v->abstract = string(cv->abstract, sizeof cv->abstract);
    307 	v->biblio = string(cv->biblio, sizeof cv->biblio);
    308 	v->notice = string(cv->notice, sizeof cv->notice);
    309 }
    310 
    311 static int
    312 readisodesc(Cdimg *cd, Voldesc *v)
    313 {
    314 	static uchar magic[] = { 0x01, 'C', 'D', '0', '0', '1', 0x01, 0x00 };
    315 	Cvoldesc cv;
    316 
    317 	memset(v, 0, sizeof *v);
    318 
    319 	Creadblock(cd, &cv, 16, sizeof cv);
    320 	if(memcmp(cv.magic, magic, sizeof magic) != 0) {
    321 		werrstr("bad pvd magic");
    322 		return -1;
    323 	}
    324 
    325 	if(little(cv.blocksize, 2) != Blocksize) {
    326 		werrstr("block size not %d", Blocksize);
    327 		return -1;
    328 	}
    329 
    330 	cd->iso9660pvd = 16;
    331 	parsedesc(v, &cv, isostring);
    332 
    333 	return parsedir(cd, &v->root, cv.rootdir, sizeof cv.rootdir, isostring);
    334 }
    335 
    336 static int
    337 readjolietdesc(Cdimg *cd, Voldesc *v)
    338 {
    339 	int i;
    340 	static uchar magic[] = { 0x02, 'C', 'D', '0', '0', '1', 0x01, 0x00 };
    341 	Cvoldesc cv;
    342 
    343 	memset(v, 0, sizeof *v);
    344 
    345 	for(i=16; i<24; i++) {
    346 		Creadblock(cd, &cv, i, sizeof cv);
    347 		if(memcmp(cv.magic, magic, sizeof magic) != 0)
    348 			continue;
    349 		if(cv.charset[0] != 0x25 || cv.charset[1] != 0x2F
    350 		|| (cv.charset[2] != 0x40 && cv.charset[2] != 0x43 && cv.charset[2] != 0x45))
    351 			continue;
    352 		break;
    353 	}
    354 
    355 	if(i==24) {
    356 		werrstr("could not find Joliet SVD");
    357 		return -1;
    358 	}
    359 
    360 	if(little(cv.blocksize, 2) != Blocksize) {
    361 		werrstr("block size not %d", Blocksize);
    362 		return -1;
    363 	}
    364 
    365 	cd->jolietsvd = i;
    366 	parsedesc(v, &cv, jolietstring);
    367 
    368 	return parsedir(cd, &v->root, cv.rootdir, sizeof cv.rootdir, jolietstring);
    369 }
    370 
    371 /*
    372  * CD image buffering routines.
    373  */
    374 void
    375 Cputc(Cdimg *cd, int c)
    376 {
    377 	assert(Boffset(&cd->bwr) >= 16*Blocksize || c == 0);
    378 
    379 if(Boffset(&cd->bwr) == 0x9962)
    380 if(c >= 256) abort();
    381 	if(Bputc(&cd->bwr, c) < 0)
    382 		sysfatal("Bputc: %r");
    383 	Bflush(&cd->brd);
    384 }
    385 
    386 void
    387 Cputnl(Cdimg *cd, ulong val, int size)
    388 {
    389 	switch(size) {
    390 	default:
    391 		sysfatal("bad size %d in bputnl", size);
    392 	case 2:
    393 		Cputc(cd, val);
    394 		Cputc(cd, val>>8);
    395 		break;
    396 	case 4:
    397 		Cputc(cd, val);
    398 		Cputc(cd, val>>8);
    399 		Cputc(cd, val>>16);
    400 		Cputc(cd, val>>24);
    401 		break;
    402 	}
    403 }
    404 
    405 void
    406 Cputnm(Cdimg *cd, ulong val, int size)
    407 {
    408 	switch(size) {
    409 	default:
    410 		sysfatal("bad size %d in bputnl", size);
    411 	case 2:
    412 		Cputc(cd, val>>8);
    413 		Cputc(cd, val);
    414 		break;
    415 	case 4:
    416 		Cputc(cd, val>>24);
    417 		Cputc(cd, val>>16);
    418 		Cputc(cd, val>>8);
    419 		Cputc(cd, val);
    420 		break;
    421 	}
    422 }
    423 
    424 void
    425 Cputn(Cdimg *cd, long val, int size)
    426 {
    427 	Cputnl(cd, val, size);
    428 	Cputnm(cd, val, size);
    429 }
    430 
    431 /*
    432  * ASCII/UTF string writing
    433  */
    434 void
    435 Crepeat(Cdimg *cd, int c, int n)
    436 {
    437 	while(n-- > 0)
    438 		Cputc(cd, c);
    439 }
    440 
    441 void
    442 Cputs(Cdimg *cd, char *s, int size)
    443 {
    444 	int n;
    445 
    446 	if(s == nil) {
    447 		Crepeat(cd, ' ', size);
    448 		return;
    449 	}
    450 
    451 	for(n=0; n<size && *s; n++)
    452 		Cputc(cd, *s++);
    453 	if(n<size)
    454 		Crepeat(cd, ' ', size-n);
    455 }
    456 
    457 void
    458 Cwrite(Cdimg *cd, void *buf, int n)
    459 {
    460 	assert(Boffset(&cd->bwr) >= 16*Blocksize);
    461 
    462 	if(Bwrite(&cd->bwr, buf, n) != n)
    463 		sysfatal("Bwrite: %r");
    464 	Bflush(&cd->brd);
    465 }
    466 
    467 void
    468 Cputr(Cdimg *cd, Rune r)
    469 {
    470 	Cputc(cd, r>>8);
    471 	Cputc(cd, r);
    472 }
    473 
    474 void
    475 Crepeatr(Cdimg *cd, Rune r, int n)
    476 {
    477 	int i;
    478 
    479 	for(i=0; i<n; i++)
    480 		Cputr(cd, r);
    481 }
    482 
    483 void
    484 Cputrs(Cdimg *cd, Rune *s, int osize)
    485 {
    486 	int n, size;
    487 
    488 	size = osize/2;
    489 	if(s == nil)
    490 		Crepeatr(cd, (Rune)' ', size);
    491 	else {
    492 		for(n=0; *s && n<size; n++)
    493 			Cputr(cd, *s++);
    494 		if(n<size)
    495 			Crepeatr(cd, ' ', size-n);
    496 	}
    497 	if(osize&1)
    498 		Cputc(cd, 0);	/* what else can we do? */
    499 }
    500 
    501 void
    502 Cputrscvt(Cdimg *cd, char *s, int size)
    503 {
    504 	Rune r[256];
    505 
    506 	strtorune(r, s);
    507 	Cputrs(cd, strtorune(r, s), size);
    508 }
    509 
    510 void
    511 Cpadblock(Cdimg *cd)
    512 {
    513 	int n;
    514 	ulong nb;
    515 
    516 	n = Blocksize - (Boffset(&cd->bwr) % Blocksize);
    517 	if(n != Blocksize)
    518 		Crepeat(cd, 0, n);
    519 
    520 	nb = Boffset(&cd->bwr)/Blocksize;
    521 	assert(nb != 0);
    522 	if(nb > cd->nextblock)
    523 		cd->nextblock = nb;
    524 }
    525 
    526 void
    527 Cputdate(Cdimg *cd, ulong ust)
    528 {
    529 	Tm *tm;
    530 
    531 	if(ust == 0) {
    532 		Crepeat(cd, 0, 7);
    533 		return;
    534 	}
    535 	tm = gmtime(ust);
    536 	Cputc(cd, tm->year);
    537 	Cputc(cd, tm->mon+1);
    538 	Cputc(cd, tm->mday);
    539 	Cputc(cd, tm->hour);
    540 	Cputc(cd, tm->min);
    541 	Cputc(cd, tm->sec);
    542 	Cputc(cd, 0);
    543 }
    544 
    545 void
    546 Cputdate1(Cdimg *cd, ulong ust)
    547 {
    548 	Tm *tm;
    549 	char str[20];
    550 
    551 	if(ust == 0) {
    552 		Crepeat(cd, '0', 16);
    553 		Cputc(cd, 0);
    554 		return;
    555 	}
    556 	tm = gmtime(ust);
    557 	sprint(str, "%.4d%.2d%.2d%.2d%.2d%.4d",
    558 		tm->year+1900,
    559 		tm->mon+1,
    560 		tm->mday,
    561 		tm->hour,
    562 		tm->min,
    563 		tm->sec*100);
    564 	Cputs(cd, str, 16);
    565 	Cputc(cd, 0);
    566 }
    567 
    568 void
    569 Cwseek(Cdimg *cd, ulong offset)
    570 {
    571 	Bseek(&cd->bwr, offset, 0);
    572 }
    573 
    574 ulong
    575 Cwoffset(Cdimg *cd)
    576 {
    577 	return Boffset(&cd->bwr);
    578 }
    579 
    580 void
    581 Cwflush(Cdimg *cd)
    582 {
    583 	Bflush(&cd->bwr);
    584 }
    585 
    586 ulong
    587 Croffset(Cdimg *cd)
    588 {
    589 	return Boffset(&cd->brd);
    590 }
    591 
    592 void
    593 Crseek(Cdimg *cd, ulong offset)
    594 {
    595 	Bseek(&cd->brd, offset, 0);
    596 }
    597 
    598 int
    599 Cgetc(Cdimg *cd)
    600 {
    601 	int c;
    602 
    603 	Cwflush(cd);
    604 	if((c = Bgetc(&cd->brd)) == Beof) {
    605 		fprint(2, "getc at %lud\n", Croffset(cd));
    606 		assert(0);
    607 		/*sysfatal("Bgetc: %r"); */
    608 	}
    609 	return c;
    610 }
    611 
    612 void
    613 Cread(Cdimg *cd, void *buf, int n)
    614 {
    615 	Cwflush(cd);
    616 	if(Bread(&cd->brd, buf, n) != n)
    617 		sysfatal("Bread: %r");
    618 }
    619 
    620 char*
    621 Crdline(Cdimg *cd, int c)
    622 {
    623 	Cwflush(cd);
    624 	return Brdline(&cd->brd, c);
    625 }
    626 
    627 int
    628 Clinelen(Cdimg *cd)
    629 {
    630 	return Blinelen(&cd->brd);
    631 }