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