1 /*-
2 * Copyright (c) 1993, 1994
3 * The Regents of the University of California. All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley
6 * by Pace Willisson (pace@blitz.com). The Rock Ridge Extension
7 * Support code is derived from software contributed to Berkeley
8 * by Atsushi Murai (amurai@spec.co.jp).
9 *
10 * %sccs.include.redist.c%
11 *
12 * @(#)cd9660_rrip.c 8.6 (Berkeley) 12/05/94
13 */
14
15 #include <sys/param.h>
16 #include <sys/systm.h>
17 #include <sys/namei.h>
18 #include <sys/buf.h>
19 #include <sys/file.h>
20 #include <sys/vnode.h>
21 #include <sys/mount.h>
22 #include <sys/kernel.h>
23 #include <sys/stat.h>
24 #include <sys/types.h>
25
26 #include <sys/time.h>
27
28 #include <isofs/cd9660/iso.h>
29 #include <isofs/cd9660/cd9660_node.h>
30 #include <isofs/cd9660/cd9660_rrip.h>
31 #include <isofs/cd9660/iso_rrip.h>
32
33 /*
34 * POSIX file attribute
35 */
36 static int
cd9660_rrip_attr(p,ana)37 cd9660_rrip_attr(p,ana)
38 ISO_RRIP_ATTR *p;
39 ISO_RRIP_ANALYZE *ana;
40 {
41 ana->inop->inode.iso_mode = isonum_733(p->mode);
42 ana->inop->inode.iso_uid = isonum_733(p->uid);
43 ana->inop->inode.iso_gid = isonum_733(p->gid);
44 ana->inop->inode.iso_links = isonum_733(p->links);
45 ana->fields &= ~ISO_SUSP_ATTR;
46 return ISO_SUSP_ATTR;
47 }
48
49 static void
cd9660_rrip_defattr(isodir,ana)50 cd9660_rrip_defattr(isodir,ana)
51 struct iso_directory_record *isodir;
52 ISO_RRIP_ANALYZE *ana;
53 {
54 /* But this is a required field! */
55 printf("RRIP without PX field?\n");
56 cd9660_defattr(isodir,ana->inop,NULL);
57 }
58
59 /*
60 * Symbolic Links
61 */
62 static int
cd9660_rrip_slink(p,ana)63 cd9660_rrip_slink(p,ana)
64 ISO_RRIP_SLINK *p;
65 ISO_RRIP_ANALYZE *ana;
66 {
67 register ISO_RRIP_SLINK_COMPONENT *pcomp;
68 register ISO_RRIP_SLINK_COMPONENT *pcompe;
69 int len, wlen, cont;
70 char *outbuf, *inbuf;
71
72 pcomp = (ISO_RRIP_SLINK_COMPONENT *)p->component;
73 pcompe = (ISO_RRIP_SLINK_COMPONENT *)((char *)p + isonum_711(p->h.length));
74 len = *ana->outlen;
75 outbuf = ana->outbuf;
76 cont = ana->cont;
77
78 /*
79 * Gathering a Symbolic name from each component with path
80 */
81 for (;
82 pcomp < pcompe;
83 pcomp = (ISO_RRIP_SLINK_COMPONENT *)((char *)pcomp + ISO_RRIP_SLSIZ
84 + isonum_711(pcomp->clen))) {
85
86 if (!cont) {
87 if (len < ana->maxlen) {
88 len++;
89 *outbuf++ = '/';
90 }
91 }
92 cont = 0;
93
94 inbuf = "..";
95 wlen = 0;
96
97 switch (*pcomp->cflag) {
98
99 case ISO_SUSP_CFLAG_CURRENT:
100 /* Inserting Current */
101 wlen = 1;
102 break;
103
104 case ISO_SUSP_CFLAG_PARENT:
105 /* Inserting Parent */
106 wlen = 2;
107 break;
108
109 case ISO_SUSP_CFLAG_ROOT:
110 /* Inserting slash for ROOT */
111 /* start over from beginning(?) */
112 outbuf -= len;
113 len = 0;
114 break;
115
116 case ISO_SUSP_CFLAG_VOLROOT:
117 /* Inserting a mount point i.e. "/cdrom" */
118 /* same as above */
119 outbuf -= len;
120 len = 0;
121 inbuf = ana->imp->im_mountp->mnt_stat.f_mntonname;
122 wlen = strlen(inbuf);
123 break;
124
125 case ISO_SUSP_CFLAG_HOST:
126 /* Inserting hostname i.e. "kurt.tools.de" */
127 inbuf = hostname;
128 wlen = hostnamelen;
129 break;
130
131 case ISO_SUSP_CFLAG_CONTINUE:
132 cont = 1;
133 /* fall thru */
134 case 0:
135 /* Inserting component */
136 wlen = isonum_711(pcomp->clen);
137 inbuf = pcomp->name;
138 break;
139 default:
140 printf("RRIP with incorrect flags?");
141 wlen = ana->maxlen + 1;
142 break;
143 }
144
145 if (len + wlen > ana->maxlen) {
146 /* indicate error to caller */
147 ana->cont = 1;
148 ana->fields = 0;
149 ana->outbuf -= *ana->outlen;
150 *ana->outlen = 0;
151 return 0;
152 }
153
154 bcopy(inbuf,outbuf,wlen);
155 outbuf += wlen;
156 len += wlen;
157
158 }
159 ana->outbuf = outbuf;
160 *ana->outlen = len;
161 ana->cont = cont;
162
163 if (!isonum_711(p->flags)) {
164 ana->fields &= ~ISO_SUSP_SLINK;
165 return ISO_SUSP_SLINK;
166 }
167 return 0;
168 }
169
170 /*
171 * Alternate name
172 */
173 static int
cd9660_rrip_altname(p,ana)174 cd9660_rrip_altname(p,ana)
175 ISO_RRIP_ALTNAME *p;
176 ISO_RRIP_ANALYZE *ana;
177 {
178 char *inbuf;
179 int wlen;
180 int cont;
181
182 inbuf = "..";
183 wlen = 0;
184 cont = 0;
185
186 switch (*p->flags) {
187 case ISO_SUSP_CFLAG_CURRENT:
188 /* Inserting Current */
189 wlen = 1;
190 break;
191
192 case ISO_SUSP_CFLAG_PARENT:
193 /* Inserting Parent */
194 wlen = 2;
195 break;
196
197 case ISO_SUSP_CFLAG_HOST:
198 /* Inserting hostname i.e. "kurt.tools.de" */
199 inbuf = hostname;
200 wlen = hostnamelen;
201 break;
202
203 case ISO_SUSP_CFLAG_CONTINUE:
204 cont = 1;
205 /* fall thru */
206 case 0:
207 /* Inserting component */
208 wlen = isonum_711(p->h.length) - 5;
209 inbuf = (char *)p + 5;
210 break;
211
212 default:
213 printf("RRIP with incorrect NM flags?\n");
214 wlen = ana->maxlen + 1;
215 break;
216 }
217
218 if ((*ana->outlen += wlen) > ana->maxlen) {
219 /* treat as no name field */
220 ana->fields &= ~ISO_SUSP_ALTNAME;
221 ana->outbuf -= *ana->outlen - wlen;
222 *ana->outlen = 0;
223 return 0;
224 }
225
226 bcopy(inbuf,ana->outbuf,wlen);
227 ana->outbuf += wlen;
228
229 if (!cont) {
230 ana->fields &= ~ISO_SUSP_ALTNAME;
231 return ISO_SUSP_ALTNAME;
232 }
233 return 0;
234 }
235
236 static void
cd9660_rrip_defname(isodir,ana)237 cd9660_rrip_defname(isodir,ana)
238 struct iso_directory_record *isodir;
239 ISO_RRIP_ANALYZE *ana;
240 {
241 strcpy(ana->outbuf,"..");
242 switch (*isodir->name) {
243 default:
244 isofntrans(isodir->name,isonum_711(isodir->name_len),
245 ana->outbuf,ana->outlen,
246 1,isonum_711(isodir->flags)&4);
247 break;
248 case 0:
249 *ana->outlen = 1;
250 break;
251 case 1:
252 *ana->outlen = 2;
253 break;
254 }
255 }
256
257 /*
258 * Parent or Child Link
259 */
260 static int
cd9660_rrip_pclink(p,ana)261 cd9660_rrip_pclink(p,ana)
262 ISO_RRIP_CLINK *p;
263 ISO_RRIP_ANALYZE *ana;
264 {
265 *ana->inump = isonum_733(p->dir_loc) << ana->imp->im_bshift;
266 ana->fields &= ~(ISO_SUSP_CLINK|ISO_SUSP_PLINK);
267 return *p->h.type == 'C' ? ISO_SUSP_CLINK : ISO_SUSP_PLINK;
268 }
269
270 /*
271 * Relocated directory
272 */
273 static int
cd9660_rrip_reldir(p,ana)274 cd9660_rrip_reldir(p,ana)
275 ISO_RRIP_RELDIR *p;
276 ISO_RRIP_ANALYZE *ana;
277 {
278 /* special hack to make caller aware of RE field */
279 *ana->outlen = 0;
280 ana->fields = 0;
281 return ISO_SUSP_RELDIR|ISO_SUSP_ALTNAME|ISO_SUSP_CLINK|ISO_SUSP_PLINK;
282 }
283
284 static int
cd9660_rrip_tstamp(p,ana)285 cd9660_rrip_tstamp(p,ana)
286 ISO_RRIP_TSTAMP *p;
287 ISO_RRIP_ANALYZE *ana;
288 {
289 u_char *ptime;
290
291 ptime = p->time;
292
293 /* Check a format of time stamp (7bytes/17bytes) */
294 if (!(*p->flags&ISO_SUSP_TSTAMP_FORM17)) {
295 if (*p->flags&ISO_SUSP_TSTAMP_CREAT)
296 ptime += 7;
297
298 if (*p->flags&ISO_SUSP_TSTAMP_MODIFY) {
299 cd9660_tstamp_conv7(ptime,&ana->inop->inode.iso_mtime);
300 ptime += 7;
301 } else
302 bzero(&ana->inop->inode.iso_mtime,sizeof(struct timespec));
303
304 if (*p->flags&ISO_SUSP_TSTAMP_ACCESS) {
305 cd9660_tstamp_conv7(ptime,&ana->inop->inode.iso_atime);
306 ptime += 7;
307 } else
308 ana->inop->inode.iso_atime = ana->inop->inode.iso_mtime;
309
310 if (*p->flags&ISO_SUSP_TSTAMP_ATTR)
311 cd9660_tstamp_conv7(ptime,&ana->inop->inode.iso_ctime);
312 else
313 ana->inop->inode.iso_ctime = ana->inop->inode.iso_mtime;
314
315 } else {
316 if (*p->flags&ISO_SUSP_TSTAMP_CREAT)
317 ptime += 17;
318
319 if (*p->flags&ISO_SUSP_TSTAMP_MODIFY) {
320 cd9660_tstamp_conv17(ptime,&ana->inop->inode.iso_mtime);
321 ptime += 17;
322 } else
323 bzero(&ana->inop->inode.iso_mtime,sizeof(struct timespec));
324
325 if (*p->flags&ISO_SUSP_TSTAMP_ACCESS) {
326 cd9660_tstamp_conv17(ptime,&ana->inop->inode.iso_atime);
327 ptime += 17;
328 } else
329 ana->inop->inode.iso_atime = ana->inop->inode.iso_mtime;
330
331 if (*p->flags&ISO_SUSP_TSTAMP_ATTR)
332 cd9660_tstamp_conv17(ptime,&ana->inop->inode.iso_ctime);
333 else
334 ana->inop->inode.iso_ctime = ana->inop->inode.iso_mtime;
335
336 }
337 ana->fields &= ~ISO_SUSP_TSTAMP;
338 return ISO_SUSP_TSTAMP;
339 }
340
341 static void
cd9660_rrip_deftstamp(isodir,ana)342 cd9660_rrip_deftstamp(isodir,ana)
343 struct iso_directory_record *isodir;
344 ISO_RRIP_ANALYZE *ana;
345 {
346 cd9660_deftstamp(isodir,ana->inop,NULL);
347 }
348
349 /*
350 * POSIX device modes
351 */
352 static int
cd9660_rrip_device(p,ana)353 cd9660_rrip_device(p,ana)
354 ISO_RRIP_DEVICE *p;
355 ISO_RRIP_ANALYZE *ana;
356 {
357 u_int high, low;
358
359 high = isonum_733(p->dev_t_high);
360 low = isonum_733(p->dev_t_low);
361
362 if (high == 0)
363 ana->inop->inode.iso_rdev = makedev(major(low), minor(low));
364 else
365 ana->inop->inode.iso_rdev = makedev(high, minor(low));
366 ana->fields &= ~ISO_SUSP_DEVICE;
367 return ISO_SUSP_DEVICE;
368 }
369
370 /*
371 * Flag indicating
372 */
373 static int
cd9660_rrip_idflag(p,ana)374 cd9660_rrip_idflag(p,ana)
375 ISO_RRIP_IDFLAG *p;
376 ISO_RRIP_ANALYZE *ana;
377 {
378 ana->fields &= isonum_711(p->flags)|~0xff; /* don't touch high bits */
379 /* special handling of RE field */
380 if (ana->fields&ISO_SUSP_RELDIR)
381 return cd9660_rrip_reldir(p,ana);
382
383 return ISO_SUSP_IDFLAG;
384 }
385
386 /*
387 * Continuation pointer
388 */
389 static int
cd9660_rrip_cont(p,ana)390 cd9660_rrip_cont(p,ana)
391 ISO_RRIP_CONT *p;
392 ISO_RRIP_ANALYZE *ana;
393 {
394 ana->iso_ce_blk = isonum_733(p->location);
395 ana->iso_ce_off = isonum_733(p->offset);
396 ana->iso_ce_len = isonum_733(p->length);
397 return ISO_SUSP_CONT;
398 }
399
400 /*
401 * System Use end
402 */
403 static int
cd9660_rrip_stop(p,ana)404 cd9660_rrip_stop(p,ana)
405 ISO_SUSP_HEADER *p;
406 ISO_RRIP_ANALYZE *ana;
407 {
408 return ISO_SUSP_STOP;
409 }
410
411 /*
412 * Extension reference
413 */
414 static int
cd9660_rrip_extref(p,ana)415 cd9660_rrip_extref(p,ana)
416 ISO_RRIP_EXTREF *p;
417 ISO_RRIP_ANALYZE *ana;
418 {
419 if (isonum_711(p->len_id) != 10
420 || bcmp((char *)p + 8,"RRIP_1991A",10)
421 || isonum_711(p->version) != 1)
422 return 0;
423 ana->fields &= ~ISO_SUSP_EXTREF;
424 return ISO_SUSP_EXTREF;
425 }
426
427 typedef struct {
428 char type[2];
429 int (*func)();
430 void (*func2)();
431 int result;
432 } RRIP_TABLE;
433
434 static int
cd9660_rrip_loop(isodir,ana,table)435 cd9660_rrip_loop(isodir,ana,table)
436 struct iso_directory_record *isodir;
437 ISO_RRIP_ANALYZE *ana;
438 RRIP_TABLE *table;
439 {
440 register RRIP_TABLE *ptable;
441 register ISO_SUSP_HEADER *phead;
442 register ISO_SUSP_HEADER *pend;
443 struct buf *bp = NULL;
444 int i;
445 char *pwhead;
446 int result;
447
448 /*
449 * Note: If name length is odd,
450 * it will be padding 1 byte after the name
451 */
452 pwhead = isodir->name + isonum_711(isodir->name_len);
453 if (!(isonum_711(isodir->name_len)&1))
454 pwhead++;
455
456 /* If it's not the '.' entry of the root dir obey SP field */
457 if (*isodir->name != 0
458 || isonum_733(isodir->extent) != ana->imp->root_extent)
459 pwhead += ana->imp->rr_skip;
460 else
461 pwhead += ana->imp->rr_skip0;
462
463 phead = (ISO_SUSP_HEADER *)pwhead;
464 pend = (ISO_SUSP_HEADER *)((char *)isodir + isonum_711(isodir->length));
465
466 result = 0;
467 while (1) {
468 ana->iso_ce_len = 0;
469 /*
470 * Note: "pend" should be more than one SUSP header
471 */
472 while (pend >= phead + 1) {
473 if (isonum_711(phead->version) == 1) {
474 for (ptable = table; ptable->func; ptable++) {
475 if (*phead->type == *ptable->type
476 && phead->type[1] == ptable->type[1]) {
477 result |= ptable->func(phead,ana);
478 break;
479 }
480 }
481 if (!ana->fields)
482 break;
483 }
484 if (result&ISO_SUSP_STOP) {
485 result &= ~ISO_SUSP_STOP;
486 break;
487 }
488 /* plausibility check */
489 if (isonum_711(phead->length) < sizeof(*phead))
490 break;
491 /*
492 * move to next SUSP
493 * Hopefully this works with newer versions, too
494 */
495 phead = (ISO_SUSP_HEADER *)((char *)phead + isonum_711(phead->length));
496 }
497
498 if (ana->fields && ana->iso_ce_len) {
499 if (ana->iso_ce_blk >= ana->imp->volume_space_size
500 || ana->iso_ce_off + ana->iso_ce_len > ana->imp->logical_block_size
501 || bread(ana->imp->im_devvp,
502 ana->iso_ce_blk <<
503 (ana->imp->im_bshift - DEV_BSHIFT),
504 ana->imp->logical_block_size, NOCRED, &bp))
505 /* what to do now? */
506 break;
507 phead = (ISO_SUSP_HEADER *)(bp->b_data + ana->iso_ce_off);
508 pend = (ISO_SUSP_HEADER *) ((char *)phead + ana->iso_ce_len);
509 } else
510 break;
511 }
512 if (bp)
513 brelse(bp);
514 /*
515 * If we don't find the Basic SUSP stuffs, just set default value
516 * (attribute/time stamp)
517 */
518 for (ptable = table; ptable->func2; ptable++)
519 if (!(ptable->result&result))
520 ptable->func2(isodir,ana);
521
522 return result;
523 }
524
525 /*
526 * Get Attributes.
527 */
528 static RRIP_TABLE rrip_table_analyze[] = {
529 { "PX", cd9660_rrip_attr, cd9660_rrip_defattr, ISO_SUSP_ATTR },
530 { "TF", cd9660_rrip_tstamp, cd9660_rrip_deftstamp, ISO_SUSP_TSTAMP },
531 { "PN", cd9660_rrip_device, 0, ISO_SUSP_DEVICE },
532 { "RR", cd9660_rrip_idflag, 0, ISO_SUSP_IDFLAG },
533 { "CE", cd9660_rrip_cont, 0, ISO_SUSP_CONT },
534 { "ST", cd9660_rrip_stop, 0, ISO_SUSP_STOP },
535 { "", 0, 0, 0 }
536 };
537
538 int
cd9660_rrip_analyze(isodir,inop,imp)539 cd9660_rrip_analyze(isodir,inop,imp)
540 struct iso_directory_record *isodir;
541 struct iso_node *inop;
542 struct iso_mnt *imp;
543 {
544 ISO_RRIP_ANALYZE analyze;
545
546 analyze.inop = inop;
547 analyze.imp = imp;
548 analyze.fields = ISO_SUSP_ATTR|ISO_SUSP_TSTAMP|ISO_SUSP_DEVICE;
549
550 return cd9660_rrip_loop(isodir,&analyze,rrip_table_analyze);
551 }
552
553 /*
554 * Get Alternate Name.
555 */
556 static RRIP_TABLE rrip_table_getname[] = {
557 { "NM", cd9660_rrip_altname, cd9660_rrip_defname, ISO_SUSP_ALTNAME },
558 { "CL", cd9660_rrip_pclink, 0, ISO_SUSP_CLINK|ISO_SUSP_PLINK },
559 { "PL", cd9660_rrip_pclink, 0, ISO_SUSP_CLINK|ISO_SUSP_PLINK },
560 { "RE", cd9660_rrip_reldir, 0, ISO_SUSP_RELDIR },
561 { "RR", cd9660_rrip_idflag, 0, ISO_SUSP_IDFLAG },
562 { "CE", cd9660_rrip_cont, 0, ISO_SUSP_CONT },
563 { "ST", cd9660_rrip_stop, 0, ISO_SUSP_STOP },
564 { "", 0, 0, 0 }
565 };
566
567 int
cd9660_rrip_getname(isodir,outbuf,outlen,inump,imp)568 cd9660_rrip_getname(isodir,outbuf,outlen,inump,imp)
569 struct iso_directory_record *isodir;
570 char *outbuf;
571 u_short *outlen;
572 ino_t *inump;
573 struct iso_mnt *imp;
574 {
575 ISO_RRIP_ANALYZE analyze;
576 RRIP_TABLE *tab;
577
578 analyze.outbuf = outbuf;
579 analyze.outlen = outlen;
580 analyze.maxlen = NAME_MAX;
581 analyze.inump = inump;
582 analyze.imp = imp;
583 analyze.fields = ISO_SUSP_ALTNAME|ISO_SUSP_RELDIR|ISO_SUSP_CLINK|ISO_SUSP_PLINK;
584 *outlen = 0;
585
586 tab = rrip_table_getname;
587 if (*isodir->name == 0
588 || *isodir->name == 1) {
589 cd9660_rrip_defname(isodir,&analyze);
590
591 analyze.fields &= ~ISO_SUSP_ALTNAME;
592 tab++;
593 }
594
595 return cd9660_rrip_loop(isodir,&analyze,tab);
596 }
597
598 /*
599 * Get Symbolic Link.
600 */
601 static RRIP_TABLE rrip_table_getsymname[] = {
602 { "SL", cd9660_rrip_slink, 0, ISO_SUSP_SLINK },
603 { "RR", cd9660_rrip_idflag, 0, ISO_SUSP_IDFLAG },
604 { "CE", cd9660_rrip_cont, 0, ISO_SUSP_CONT },
605 { "ST", cd9660_rrip_stop, 0, ISO_SUSP_STOP },
606 { "", 0, 0, 0 }
607 };
608
609 int
cd9660_rrip_getsymname(isodir,outbuf,outlen,imp)610 cd9660_rrip_getsymname(isodir,outbuf,outlen,imp)
611 struct iso_directory_record *isodir;
612 char *outbuf;
613 u_short *outlen;
614 struct iso_mnt *imp;
615 {
616 ISO_RRIP_ANALYZE analyze;
617
618 analyze.outbuf = outbuf;
619 analyze.outlen = outlen;
620 *outlen = 0;
621 analyze.maxlen = MAXPATHLEN;
622 analyze.cont = 1; /* don't start with a slash */
623 analyze.imp = imp;
624 analyze.fields = ISO_SUSP_SLINK;
625
626 return (cd9660_rrip_loop(isodir,&analyze,rrip_table_getsymname)&ISO_SUSP_SLINK);
627 }
628
629 static RRIP_TABLE rrip_table_extref[] = {
630 { "ER", cd9660_rrip_extref, 0, ISO_SUSP_EXTREF },
631 { "CE", cd9660_rrip_cont, 0, ISO_SUSP_CONT },
632 { "ST", cd9660_rrip_stop, 0, ISO_SUSP_STOP },
633 { "", 0, 0, 0 }
634 };
635
636 /*
637 * Check for Rock Ridge Extension and return offset of its fields.
638 * Note: We insist on the ER field.
639 */
640 int
cd9660_rrip_offset(isodir,imp)641 cd9660_rrip_offset(isodir,imp)
642 struct iso_directory_record *isodir;
643 struct iso_mnt *imp;
644 {
645 ISO_RRIP_OFFSET *p;
646 ISO_RRIP_ANALYZE analyze;
647
648 imp->rr_skip0 = 0;
649 p = (ISO_RRIP_OFFSET *)(isodir->name + 1);
650 if (bcmp(p,"SP\7\1\276\357",6)) {
651 /* Maybe, it's a CDROM XA disc? */
652 imp->rr_skip0 = 15;
653 p = (ISO_RRIP_OFFSET *)((char *)p + 15);
654 if (bcmp(p,"SP\7\1\276\357",6))
655 return -1;
656 }
657
658 analyze.imp = imp;
659 analyze.fields = ISO_SUSP_EXTREF;
660 if (!(cd9660_rrip_loop(isodir,&analyze,rrip_table_extref)&ISO_SUSP_EXTREF))
661 return -1;
662
663 return isonum_711(p->skip);
664 }
665