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