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