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*
createcd(char * file,Cdinfo info)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*
opencd(char * file,Cdinfo info)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
big(void * a,int n)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
little(void * a,int n)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
Creadblock(Cdimg * cd,void * buf,ulong block,ulong len)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
parsedir(Cdimg * cd,Direc * d,uchar * buf,int len,char * (* cvtname)(uchar *,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
setroot(Cdimg * cd,ulong block,ulong dloc,ulong dlen)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
setvolsize(Cdimg * cd,ulong block,ulong size)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
setpathtable(Cdimg * cd,ulong block,ulong sz,ulong lloc,ulong bloc)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
parsedesc(Voldesc * v,Cvoldesc * cv,char * (* string)(uchar *,int))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
readisodesc(Cdimg * cd,Voldesc * v)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
readjolietdesc(Cdimg * cd,Voldesc * v)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
Cputc(Cdimg * cd,int c)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
Cputnl(Cdimg * cd,ulong val,int size)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
Cputnm(Cdimg * cd,ulong val,int size)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
Cputn(Cdimg * cd,long val,int size)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
Crepeat(Cdimg * cd,int c,int n)435 Crepeat(Cdimg *cd, int c, int n)
436 {
437 	while(n-- > 0)
438 		Cputc(cd, c);
439 }
440 
441 void
Cputs(Cdimg * cd,char * s,int size)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
Cwrite(Cdimg * cd,void * buf,int n)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
Cputr(Cdimg * cd,Rune r)468 Cputr(Cdimg *cd, Rune r)
469 {
470 	Cputc(cd, r>>8);
471 	Cputc(cd, r);
472 }
473 
474 void
Crepeatr(Cdimg * cd,Rune r,int n)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
Cputrs(Cdimg * cd,Rune * s,int osize)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
Cputrscvt(Cdimg * cd,char * s,int size)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
Cpadblock(Cdimg * cd)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
Cputdate(Cdimg * cd,ulong ust)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
Cputdate1(Cdimg * cd,ulong ust)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
Cwseek(Cdimg * cd,ulong offset)569 Cwseek(Cdimg *cd, ulong offset)
570 {
571 	Bseek(&cd->bwr, offset, 0);
572 }
573 
574 ulong
Cwoffset(Cdimg * cd)575 Cwoffset(Cdimg *cd)
576 {
577 	return Boffset(&cd->bwr);
578 }
579 
580 void
Cwflush(Cdimg * cd)581 Cwflush(Cdimg *cd)
582 {
583 	Bflush(&cd->bwr);
584 }
585 
586 ulong
Croffset(Cdimg * cd)587 Croffset(Cdimg *cd)
588 {
589 	return Boffset(&cd->brd);
590 }
591 
592 void
Crseek(Cdimg * cd,ulong offset)593 Crseek(Cdimg *cd, ulong offset)
594 {
595 	Bseek(&cd->brd, offset, 0);
596 }
597 
598 int
Cgetc(Cdimg * cd)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
Cread(Cdimg * cd,void * buf,int n)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*
Crdline(Cdimg * cd,int c)621 Crdline(Cdimg *cd, int c)
622 {
623 	Cwflush(cd);
624 	return Brdline(&cd->brd, c);
625 }
626 
627 int
Clinelen(Cdimg * cd)628 Clinelen(Cdimg *cd)
629 {
630 	return Blinelen(&cd->brd);
631 }
632