plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

9660srv.c (17394B)


      1 #include <u.h>
      2 #include <libc.h>
      3 #include <auth.h>
      4 #include <fcall.h>
      5 #include "dat.h"
      6 #include "fns.h"
      7 #include "iso9660.h"
      8 
      9 static void	ireset(void);
     10 static int	iattach(Xfile*);
     11 static void	iclone(Xfile*, Xfile*);
     12 static void	iwalkup(Xfile*);
     13 static void	iwalk(Xfile*, char*);
     14 static void	iopen(Xfile*, int);
     15 static void	icreate(Xfile*, char*, long, int);
     16 static long	ireaddir(Xfile*, uchar*, long, long);
     17 static long	iread(Xfile*, char*, vlong, long);
     18 static long	iwrite(Xfile*, char*, vlong, long);
     19 static void	iclunk(Xfile*);
     20 static void	iremove(Xfile*);
     21 static void	istat(Xfile*, Dir*);
     22 static void	iwstat(Xfile*, Dir*);
     23 
     24 static char*	nstr(uchar*, int);
     25 static char*	rdate(uchar*, int);
     26 static int	getcontin(Xdata*, uchar*, uchar**);
     27 static int	getdrec(Xfile*, void*);
     28 static void	ungetdrec(Xfile*);
     29 static int	opendotdot(Xfile*, Xfile*);
     30 static int	showdrec(int, int, void*);
     31 static long	gtime(uchar*);
     32 static long	l16(void*);
     33 static long	l32(void*);
     34 static void	newdrec(Xfile*, Drec*);
     35 static int	rzdir(Xfs*, Dir*, int, Drec*);
     36 
     37 Xfsub	isosub =
     38 {
     39 	ireset, iattach, iclone, iwalkup, iwalk, iopen, icreate,
     40 	ireaddir, iread, iwrite, iclunk, iremove, istat, iwstat
     41 };
     42 
     43 static void
     44 ireset(void)
     45 {}
     46 
     47 static int
     48 iattach(Xfile *root)
     49 {
     50 	Xfs *cd = root->xf;
     51 	Iobuf *p; Voldesc *v; Isofile *fp; Drec *dp;
     52 	int fmt, blksize, i, n, l, haveplan9;
     53 	Iobuf *dirp;
     54 	uchar dbuf[256];
     55 	Drec *rd = (Drec *)dbuf;
     56 	uchar *q, *s;
     57 
     58 	dirp = nil;
     59 	blksize = 0;
     60 	fmt = 0;
     61 	dp = nil;
     62 	haveplan9 = 0;
     63 	for(i=VOLDESC;i<VOLDESC+100; i++){	/* +100 for sanity */
     64 		p = getbuf(cd->d, i);
     65 		v = (Voldesc*)(p->iobuf);
     66 		if(memcmp(v->byte, "\01CD001\01", 7) == 0){		/* iso */
     67 			if(dirp)
     68 				putbuf(dirp);
     69 			dirp = p;
     70 			fmt = 'z';
     71 			dp = (Drec*)v->z.desc.rootdir;
     72 			blksize = l16(v->z.desc.blksize);
     73 			chat("iso, blksize=%d...", blksize);
     74 
     75 			v = (Voldesc*)(dirp->iobuf);
     76 			haveplan9 = (strncmp((char*)v->z.boot.sysid, "PLAN 9", 6)==0);
     77 			if(haveplan9){
     78 				if(noplan9) {
     79 					chat("ignoring plan9");
     80 					haveplan9 = 0;
     81 				} else {
     82 					fmt = '9';
     83 					chat("plan9 iso...");
     84 				}
     85 			}
     86 			continue;
     87 		}
     88 
     89 		if(memcmp(&v->byte[8], "\01CDROM\01", 7) == 0){	/* high sierra */
     90 			if(dirp)
     91 				putbuf(dirp);
     92 			dirp = p;
     93 			fmt = 'r';
     94 			dp = (Drec*)v->r.desc.rootdir;
     95 			blksize = l16(v->r.desc.blksize);
     96 			chat("high sierra, blksize=%d...", blksize);
     97 			continue;
     98 		}
     99 
    100 		if(haveplan9==0 && !nojoliet
    101 		&& memcmp(v->byte, "\02CD001\01", 7) == 0){
    102 chat("%d %d\n", haveplan9, nojoliet);
    103 			/*
    104 			 * The right thing to do is walk the escape sequences looking
    105 			 * for one of 25 2F 4[035], but Microsoft seems to not honor
    106 			 * the format, which makes it hard to walk over.
    107 			 */
    108 			q = v->z.desc.escapes;
    109 			if(q[0] == 0x25 && q[1] == 0x2F && (q[2] == 0x40 || q[2] == 0x43 || q[2] == 0x45)){	/* Joliet, it appears */
    110 				if(dirp)
    111 					putbuf(dirp);
    112 				dirp = p;
    113 				fmt = 'J';
    114 				dp = (Drec*)v->z.desc.rootdir;
    115 				if(blksize != l16(v->z.desc.blksize))
    116 					fprint(2, "warning: suspicious Joliet blocksize\n");
    117 				chat("joliet...");
    118 				continue;
    119 			}
    120 		}
    121 		putbuf(p);
    122 		if(v->byte[0] == 0xFF)
    123 			break;
    124 	}
    125 
    126 	if(fmt == 0){
    127 		if(dirp)
    128 			putbuf(dirp);
    129 		return -1;
    130 	}
    131 	assert(dirp != nil);
    132 
    133 	if(chatty)
    134 		showdrec(2, fmt, dp);
    135 	if(blksize > Sectorsize){
    136 		chat("blksize too big...");
    137 		putbuf(dirp);
    138 		return -1;
    139 	}
    140 	if(waserror()){
    141 		putbuf(dirp);
    142 		nexterror();
    143 	}
    144 	root->len = sizeof(Isofile) - sizeof(Drec) + dp->z.reclen;
    145 	root->ptr = fp = ealloc(root->len);
    146 
    147 	if(haveplan9)
    148 		root->xf->isplan9 = 1;
    149 
    150 	fp->fmt = fmt;
    151 	fp->blksize = blksize;
    152 	fp->offset = 0;
    153 	fp->doffset = 0;
    154 	memmove(&fp->d, dp, dp->z.reclen);
    155 	root->qid.path = l32(dp->z.addr);
    156 	root->qid.type = QTDIR;
    157 	putbuf(dirp);
    158 	poperror();
    159 	if(getdrec(root, rd) >= 0){
    160 		n = rd->z.reclen-(34+rd->z.namelen);
    161 		s = (uchar*)rd->z.name + rd->z.namelen;
    162 		if((uintptr)s & 1){
    163 			s++;
    164 			n--;
    165 		}
    166 		if(n >= 7 && s[0] == 'S' && s[1] == 'P' && s[2] == 7 &&
    167 		   s[3] == 1 && s[4] == 0xBE && s[5] == 0xEF){
    168 			root->xf->issusp = 1;
    169 			root->xf->suspoff = s[6];
    170 			n -= root->xf->suspoff;
    171 			s += root->xf->suspoff;
    172 			for(; n >= 4; s += l, n -= l){
    173 				l = s[2];
    174 				if(s[0] == 'E' && s[1] == 'R'){
    175 					if(!norock && s[4] == 10 && memcmp(s+8, "RRIP_1991A", 10) == 0)
    176 						root->xf->isrock = 1;
    177 					break;
    178 				} else if(s[0] == 'C' && s[1] == 'E' && s[2] >= 28){
    179 					n = getcontin(root->xf->d, s, &s);
    180 					continue;
    181 				} else if(s[0] == 'R' && s[1] == 'R'){
    182 					if(!norock)
    183 						root->xf->isrock = 1;
    184 					break;
    185 				} else if(s[0] == 'S' && s[1] == 'T')
    186 					break;
    187 			}
    188 		}
    189 	}
    190 	if(root->xf->isrock)
    191 		chat("Rock Ridge...");
    192 	fp->offset = 0;
    193 	fp->doffset = 0;
    194 	return 0;
    195 }
    196 
    197 static void
    198 iclone(Xfile *of, Xfile *nf)
    199 {
    200 	USED(of);
    201 	USED(nf);
    202 }
    203 
    204 static void
    205 iwalkup(Xfile *f)
    206 {
    207 	long paddr;
    208 	uchar dbuf[256];
    209 	Drec *d = (Drec *)dbuf;
    210 	Xfile pf, ppf;
    211 	Isofile piso, ppiso;
    212 
    213 	memset(&pf, 0, sizeof pf);
    214 	memset(&ppf, 0, sizeof ppf);
    215 	pf.ptr = &piso;
    216 	ppf.ptr = &ppiso;
    217 	if(opendotdot(f, &pf) < 0)
    218 		error("can't open pf");
    219 	paddr = l32(pf.ptr->d.z.addr);
    220 	if(l32(f->ptr->d.z.addr) == paddr)
    221 		return;
    222 	if(opendotdot(&pf, &ppf) < 0)
    223 		error("can't open ppf");
    224 	while(getdrec(&ppf, d) >= 0){
    225 		if(l32(d->z.addr) == paddr){
    226 			newdrec(f, d);
    227 			f->qid.path = paddr;
    228 			f->qid.type = QTDIR;
    229 			return;
    230 		}
    231 	}
    232 	error("can't find addr of ..");
    233 }
    234 
    235 static int
    236 casestrcmp(int isplan9, char *a, char *b)
    237 {
    238 	int ca, cb;
    239 
    240 	if(isplan9)
    241 		return strcmp(a, b);
    242 	for(;;) {
    243 		ca = *a++;
    244 		cb = *b++;
    245 		if(ca >= 'A' && ca <= 'Z')
    246 			ca += 'a' - 'A';
    247 		if(cb >= 'A' && cb <= 'Z')
    248 			cb += 'a' - 'A';
    249 		if(ca != cb) {
    250 			if(ca > cb)
    251 				return 1;
    252 			return -1;
    253 		}
    254 		if(ca == 0)
    255 			return 0;
    256 	}
    257 }
    258 
    259 static void
    260 iwalk(Xfile *f, char *name)
    261 {
    262 	Isofile *ip = f->ptr;
    263 	uchar dbuf[256];
    264 	char nbuf[4*Maxname];
    265 	Drec *d = (Drec*)dbuf;
    266 	Dir dir;
    267 	char *p;
    268 	int len, vers, dvers;
    269 
    270 	vers = -1;
    271 	if(p = strchr(name, ';')) {	/* assign = */
    272 		len = p-name;
    273 		if(len >= Maxname)
    274 			len = Maxname-1;
    275 		memmove(nbuf, name, len);
    276 		vers = strtoul(p+1, 0, 10);
    277 		name = nbuf;
    278 	}
    279 /*
    280 	len = strlen(name);
    281 	if(len >= Maxname){
    282 		len = Maxname-1;
    283 		if(name != nbuf){
    284 			memmove(nbuf, name, len);
    285 			name = nbuf;
    286 		}
    287 		name[len] = 0;
    288 	}
    289 */
    290 
    291 	chat("%d \"%s\"...", strlen(name), name);
    292 	ip->offset = 0;
    293 	setnames(&dir, nbuf);
    294 	while(getdrec(f, d) >= 0) {
    295 		dvers = rzdir(f->xf, &dir, ip->fmt, d);
    296 		if(casestrcmp(f->xf->isplan9||f->xf->isrock, name, dir.name) != 0)
    297 			continue;
    298 		newdrec(f, d);
    299 		f->qid.path = dir.qid.path;
    300 		f->qid.type = dir.qid.type;
    301 		USED(dvers);
    302 		return;
    303 	}
    304 	USED(vers);
    305 	error(Enonexist);
    306 }
    307 
    308 static void
    309 iopen(Xfile *f, int mode)
    310 {
    311 	mode &= ~OCEXEC;
    312 	if(mode != OREAD && mode != OEXEC)
    313 		error(Eperm);
    314 	f->ptr->offset = 0;
    315 	f->ptr->doffset = 0;
    316 }
    317 
    318 static void
    319 icreate(Xfile *f, char *name, long perm, int mode)
    320 {
    321 	USED(f);
    322 	USED(name);
    323 	USED(perm);
    324 	USED(mode);
    325 	error(Eperm);
    326 }
    327 
    328 static long
    329 ireaddir(Xfile *f, uchar *buf, long offset, long count)
    330 {
    331 	Isofile *ip = f->ptr;
    332 	Dir d;
    333 	char names[4*Maxname];
    334 	uchar dbuf[256];
    335 	Drec *drec = (Drec *)dbuf;
    336 	int n, rcnt;
    337 
    338 	if(offset==0){
    339 		ip->offset = 0;
    340 		ip->doffset = 0;
    341 	}else if(offset != ip->doffset)
    342 		error("seek in directory not allowed");
    343 
    344 	rcnt = 0;
    345 	setnames(&d, names);
    346 	while(rcnt < count && getdrec(f, drec) >= 0){
    347 		if(drec->z.namelen == 1){
    348 			if(drec->z.name[0] == 0)
    349 				continue;
    350 			if(drec->z.name[0] == 1)
    351 				continue;
    352 		}
    353 		rzdir(f->xf, &d, ip->fmt, drec);
    354 		d.qid.vers = f->qid.vers;
    355 		if((n = convD2M(&d, buf+rcnt, count-rcnt)) <= BIT16SZ){
    356 			ungetdrec(f);
    357 			break;
    358 		}
    359 		rcnt += n;
    360 	}
    361 	ip->doffset += rcnt;
    362 	return rcnt;
    363 }
    364 
    365 static long
    366 iread(Xfile *f, char *buf, vlong offset, long count)
    367 {
    368 	int n, o, rcnt = 0;
    369 	long size;
    370 	vlong addr;
    371 	Isofile *ip = f->ptr;
    372 	Iobuf *p;
    373 
    374 	size = l32(ip->d.z.size);
    375 	if(offset >= size)
    376 		return 0;
    377 	if(offset+count > size)
    378 		count = size - offset;
    379 	addr = ((vlong)l32(ip->d.z.addr) + ip->d.z.attrlen)*ip->blksize + offset;
    380 	o = addr % Sectorsize;
    381 	addr /= Sectorsize;
    382 	/*chat("d.addr=%ld, addr=%lld, o=%d...", l32(ip->d.z.addr), addr, o);*/
    383 	n = Sectorsize - o;
    384 
    385 	while(count > 0){
    386 		if(n > count)
    387 			n = count;
    388 		p = getbuf(f->xf->d, addr);
    389 		memmove(&buf[rcnt], &p->iobuf[o], n);
    390 		putbuf(p);
    391 		count -= n;
    392 		rcnt += n;
    393 		++addr;
    394 		o = 0;
    395 		n = Sectorsize;
    396 	}
    397 	return rcnt;
    398 }
    399 
    400 static long
    401 iwrite(Xfile *f, char *buf, vlong offset, long count)
    402 {
    403 	USED(f);
    404 	USED(buf);
    405 	USED(offset);
    406 	USED(count);
    407 	error(Eperm);
    408 	return 0;
    409 }
    410 
    411 static void
    412 iclunk(Xfile *f)
    413 {
    414 	USED(f);
    415 }
    416 
    417 static void
    418 iremove(Xfile *f)
    419 {
    420 	USED(f);
    421 	error(Eperm);
    422 }
    423 
    424 static void
    425 istat(Xfile *f, Dir *d)
    426 {
    427 	Isofile *ip = f->ptr;
    428 
    429 	rzdir(f->xf, d, ip->fmt, &ip->d);
    430 	d->qid.vers = f->qid.vers;
    431 	if(d->qid.path==f->xf->rootqid.path){
    432 		d->qid.path = 0;
    433 		d->qid.type = QTDIR;
    434 	}
    435 }
    436 
    437 static void
    438 iwstat(Xfile *f, Dir *d)
    439 {
    440 	USED(f);
    441 	USED(d);
    442 	error(Eperm);
    443 }
    444 
    445 static int
    446 showdrec(int fd, int fmt, void *x)
    447 {
    448 	Drec *d = (Drec *)x;
    449 	int namelen;
    450 	int syslen;
    451 
    452 	if(d->z.reclen == 0)
    453 		return 0;
    454 	fprint(fd, "%d %d %ld %ld ",
    455 		d->z.reclen, d->z.attrlen, l32(d->z.addr), l32(d->z.size));
    456 	fprint(fd, "%s 0x%2.2x %d %d %ld ",
    457 		rdate(d->z.date, fmt), (fmt=='z' ? d->z.flags : d->r.flags),
    458 		d->z.unitsize, d->z.gapsize, l16(d->z.vseqno));
    459 	fprint(fd, "%d %s", d->z.namelen, nstr(d->z.name, d->z.namelen));
    460 	if(fmt != 'J'){
    461 		namelen = d->z.namelen + (1-(d->z.namelen&1));
    462 		syslen = d->z.reclen - 33 - namelen;
    463 		if(syslen != 0)
    464 			fprint(fd, " %s", nstr(&d->z.name[namelen], syslen));
    465 	}
    466 	fprint(fd, "\n");
    467 	return d->z.reclen + (d->z.reclen&1);
    468 }
    469 
    470 static void
    471 newdrec(Xfile *f, Drec *dp)
    472 {
    473 	Isofile *x = f->ptr;
    474 	Isofile *n;
    475 	int len;
    476 
    477 	len = sizeof(Isofile) - sizeof(Drec) + dp->z.reclen;
    478 	n = ealloc(len);
    479 	n->fmt = x->fmt;
    480 	n->blksize = x->blksize;
    481 	n->offset = 0;
    482 	n->doffset = 0;
    483 	memmove(&n->d, dp, dp->z.reclen);
    484 	free(x);
    485 	f->ptr = n;
    486 	f->len = len;
    487 }
    488 
    489 static void
    490 ungetdrec(Xfile *f)
    491 {
    492 	Isofile *ip = f->ptr;
    493 
    494 	if(ip->offset >= ip->odelta){
    495 		ip->offset -= ip->odelta;
    496 		ip->odelta = 0;
    497 	}
    498 }
    499 
    500 static int
    501 getdrec(Xfile *f, void *buf)
    502 {
    503 	Isofile *ip = f->ptr;
    504 	int len = 0, boff = 0;
    505 	ulong size;
    506 	vlong addr;
    507 	Iobuf *p = 0;
    508 
    509 	if(!ip)
    510 		return -1;
    511 	size = l32(ip->d.z.size);
    512 	while(ip->offset < size){
    513 		addr = (l32(ip->d.z.addr)+ip->d.z.attrlen)*ip->blksize + ip->offset;
    514 		boff = addr % Sectorsize;
    515 		if(boff > Sectorsize-34){
    516 			ip->offset += Sectorsize-boff;
    517 			continue;
    518 		}
    519 		p = getbuf(f->xf->d, addr/Sectorsize);
    520 		len = p->iobuf[boff];
    521 		if(len >= 34)
    522 			break;
    523 		putbuf(p);
    524 		p = 0;
    525 		ip->offset += Sectorsize-boff;
    526 	}
    527 	if(p) {
    528 		memmove(buf, &p->iobuf[boff], len);
    529 		putbuf(p);
    530 		ip->odelta = len + (len&1);
    531 		ip->offset += ip->odelta;
    532 		return 0;
    533 	}
    534 	return -1;
    535 }
    536 
    537 static int
    538 opendotdot(Xfile *f, Xfile *pf)
    539 {
    540 	uchar dbuf[256];
    541 	Drec *d = (Drec *)dbuf;
    542 	Isofile *ip = f->ptr, *pip = pf->ptr;
    543 
    544 	ip->offset = 0;
    545 	if(getdrec(f, d) < 0){
    546 		chat("opendotdot: getdrec(.) failed...");
    547 		return -1;
    548 	}
    549 	if(d->z.namelen != 1 || d->z.name[0] != 0){
    550 		chat("opendotdot: no . entry...");
    551 		return -1;
    552 	}
    553 	if(l32(d->z.addr) != l32(ip->d.z.addr)){
    554 		chat("opendotdot: bad . address...");
    555 		return -1;
    556 	}
    557 	if(getdrec(f, d) < 0){
    558 		chat("opendotdot: getdrec(..) failed...");
    559 		return -1;
    560 	}
    561 	if(d->z.namelen != 1 || d->z.name[0] != 1){
    562 		chat("opendotdot: no .. entry...");
    563 		return -1;
    564 	}
    565 
    566 	pf->xf = f->xf;
    567 	pip->fmt = ip->fmt;
    568 	pip->blksize = ip->blksize;
    569 	pip->offset = 0;
    570 	pip->doffset = 0;
    571 	pip->d = *d;
    572 	return 0;
    573 }
    574 
    575 enum {
    576 	Hname = 1,
    577 	Hmode = 2,
    578 };
    579 
    580 static int
    581 rzdir(Xfs *fs, Dir *d, int fmt, Drec *dp)
    582 {
    583 	int n, flags, i, j, lj, nl, vers, sysl, mode, l, have;
    584 	uchar *s;
    585 	char *p;
    586 	char buf[Maxname+UTFmax+1];
    587 	uchar *q;
    588 	Rune r;
    589 	enum { ONAMELEN = 28 };	/* old Plan 9 directory name length */
    590 
    591 	have = 0;
    592 	flags = 0;
    593 	vers = -1;
    594 	d->qid.path = l32(dp->z.addr);
    595 	d->qid.type = 0;
    596 	d->qid.vers = 0;
    597 	n = dp->z.namelen;
    598 	memset(d->name, 0, Maxname);
    599 	if(n == 1) {
    600 		switch(dp->z.name[0]){
    601 		case 1:
    602 			d->name[1] = '.';
    603 			/* fall through */
    604 		case 0:
    605 			d->name[0] = '.';
    606 			have = Hname;
    607 			break;
    608 		default:
    609 			d->name[0] = tolower(dp->z.name[0]);
    610 		}
    611 	} else {
    612 		if(fmt == 'J'){	/* Joliet, 16-bit Unicode */
    613 			q = (uchar*)dp->z.name;
    614 			for(i=j=lj=0; i<n && j<Maxname; i+=2){
    615 				lj = j;
    616 				r = (q[i]<<8)|q[i+1];
    617 				j += runetochar(buf+j, &r);
    618 			}
    619 			if(j >= Maxname)
    620 				j = lj;
    621 			memmove(d->name, buf, j);
    622 		}else{
    623 			if(n >= Maxname)
    624 				n = Maxname-1;
    625 			for(i=0; i<n; i++)
    626 				d->name[i] = tolower(dp->z.name[i]);
    627 		}
    628 	}
    629 
    630 	sysl = dp->z.reclen-(34+dp->z.namelen);
    631 	s = (uchar*)dp->z.name + dp->z.namelen;
    632 	if(((uintptr)s) & 1) {
    633 		s++;
    634 		sysl--;
    635 	}
    636 	if(fs->isplan9 && sysl > 0) {
    637 		/*
    638 		 * get gid, uid, mode and possibly name
    639 		 * from plan9 directory extension
    640 		 */
    641 		nl = *s;
    642 		if(nl >= ONAMELEN)
    643 			nl = ONAMELEN-1;
    644 		if(nl) {
    645 			memset(d->name, 0, ONAMELEN);
    646 			memmove(d->name, s+1, nl);
    647 		}
    648 		s += 1 + *s;
    649 		nl = *s;
    650 		if(nl >= ONAMELEN)
    651 			nl = ONAMELEN-1;
    652 		memset(d->uid, 0, ONAMELEN);
    653 		memmove(d->uid, s+1, nl);
    654 		s += 1 + *s;
    655 		nl = *s;
    656 		if(nl >= ONAMELEN)
    657 			nl = ONAMELEN-1;
    658 		memset(d->gid, 0, ONAMELEN);
    659 		memmove(d->gid, s+1, nl);
    660 		s += 1 + *s;
    661 		if(((uintptr)s) & 1)
    662 			s++;
    663 		d->mode = l32(s);
    664 		if(d->mode & DMDIR)
    665 			d->qid.type |= QTDIR;
    666 	} else {
    667 		d->mode = 0444;
    668 		switch(fmt) {
    669 		case 'z':
    670 			if(fs->isrock)
    671 				strcpy(d->gid, "ridge");
    672 			else
    673 				strcpy(d->gid, "iso9660");
    674 			flags = dp->z.flags;
    675 			break;
    676 		case 'r':
    677 			strcpy(d->gid, "sierra");
    678 			flags = dp->r.flags;
    679 			break;
    680 		case 'J':
    681 			strcpy(d->gid, "joliet");
    682 			flags = dp->z.flags;
    683 			break;
    684 		case '9':
    685 			strcpy(d->gid, "plan9");
    686 			flags = dp->z.flags;
    687 			break;
    688 		}
    689 		if(flags & 0x02){
    690 			d->qid.type |= QTDIR;
    691 			d->mode |= DMDIR|0111;
    692 		}
    693 		strcpy(d->uid, "cdrom");
    694 		if(fmt!='9' && !(d->mode&DMDIR)){
    695 			/*
    696 			 * ISO 9660 actually requires that you always have a . and a ;,
    697 			 * even if there is no version and no extension.  Very few writers
    698 			 * do this.  If the version is present, we use it for qid.vers.
    699 			 * If there is no extension but there is a dot, we strip it off.
    700 			 * (VMS heads couldn't comprehend the dot as a file name character
    701 			 * rather than as just a separator between name and extension.)
    702 			 *
    703 			 * We don't do this for directory names because directories are
    704 			 * not allowed to have extensions and versions.
    705 			 */
    706 			if((p=strchr(d->name, ';')) != nil){
    707 				vers = strtoul(p+1, 0, 0);
    708 				d->qid.vers = vers;
    709 				*p = '\0';
    710 			}
    711 			if((p=strchr(d->name, '.')) != nil && *(p+1)=='\0')
    712 				*p = '\0';
    713 		}
    714 		if(fs->issusp){
    715 			nl = 0;
    716 			s += fs->suspoff;
    717 			sysl -= fs->suspoff;
    718 			for(; sysl >= 4 && have != (Hname|Hmode); sysl -= l, s += l){
    719 				if(s[0] == 0 && ((uintptr)s & 1)){
    720 					/* MacOS pads individual entries, contrary to spec */
    721 					s++;
    722 					sysl--;
    723 				}
    724 				l = s[2];
    725 				if(s[0] == 'P' && s[1] == 'X' && s[3] == 1){
    726 					/* posix file attributes */
    727 					mode = l32(s+4);
    728 					d->mode = mode & 0777;
    729 					if((mode & 0170000) == 040000){
    730 						d->mode |= DMDIR;
    731 						d->qid.type |= QTDIR;
    732 					}
    733 					have |= Hmode;
    734 				} else if(s[0] == 'N' && s[1] == 'M' && s[3] == 1){
    735 					/* alternative name */
    736 					if((s[4] & ~1) == 0){
    737 						i = nl+l-5;
    738 						if(i >= Maxname)
    739 							i = Maxname-1;
    740 						if((i -= nl) > 0){
    741 							memmove(d->name+nl, s+5, i);
    742 							nl += i;
    743 						}
    744 						if(s[4] == 0)
    745 							have |= Hname;
    746 					}
    747 				} else if(s[0] == 'C' && s[1] == 'E' && s[2] >= 28){
    748 					sysl = getcontin(fs->d, s, &s);
    749 					continue;
    750 				} else if(s[0] == 'S' && s[1] == 'T')
    751 					break;
    752 			}
    753 		}
    754 	}
    755 	d->length = 0;
    756 	if((d->mode & DMDIR) == 0)
    757 		d->length = l32(dp->z.size);
    758 	d->type = 0;
    759 	d->dev = 0;
    760 	d->atime = gtime(dp->z.date);
    761 	d->mtime = d->atime;
    762 	return vers;
    763 }
    764 
    765 static int
    766 getcontin(Xdata *dev, uchar *p, uchar **s)
    767 {
    768 	long bn, off, len;
    769 	Iobuf *b;
    770 
    771 	bn = l32(p+4);
    772 	off = l32(p+12);
    773 	len = l32(p+20);
    774 	chat("getcontin %d...", bn);
    775 	b = getbuf(dev, bn);
    776 	if(b == 0){
    777 		*s = 0;
    778 		return 0;
    779 	}
    780 	*s = b->iobuf+off;
    781 	putbuf(b);
    782 	return len;
    783 }
    784 
    785 static char *
    786 nstr(uchar *p, int n)
    787 {
    788 	static char buf[132];
    789 	char *q = buf;
    790 
    791 	while(--n >= 0){
    792 		if(*p == '\\')
    793 			*q++ = '\\';
    794 		if(' ' <= *p && *p <= '~')
    795 			*q++ = *p++;
    796 		else
    797 			q += sprint(q, "\\%2.2ux", *p++);
    798 	}
    799 	*q = 0;
    800 	return buf;
    801 }
    802 
    803 static char *
    804 rdate(uchar *p, int fmt)
    805 {
    806 	static char buf[64];
    807 	int htz, s, n;
    808 
    809 	n = sprint(buf, "%2.2d.%2.2d.%2.2d %2.2d:%2.2d:%2.2d",
    810 		p[0], p[1], p[2], p[3], p[4], p[5]);
    811 	if(fmt == 'z'){
    812 		htz = p[6];
    813 		if(htz >= 128){
    814 			htz = 256-htz;
    815 			s = '-';
    816 		}else
    817 			s = '+';
    818 		sprint(&buf[n], " (%c%.1f)", s, (float)htz/2);
    819 	}
    820 	return buf;
    821 }
    822 
    823 static char
    824 dmsize[12] =
    825 {
    826 	31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
    827 };
    828 
    829 #define dysize mydysize
    830 
    831 static int
    832 dysize(int y)
    833 {
    834 
    835 	if((y%4) == 0)
    836 		return 366;
    837 	return 365;
    838 }
    839 
    840 static long
    841 gtime(uchar *p)	/* yMdhmsz */
    842 {
    843 	long t;
    844 	int i, y, M, d, h, m, s, tz;
    845 
    846 	y=p[0]; M=p[1]; d=p[2];
    847 	h=p[3]; m=p[4]; s=p[5]; tz=p[6];
    848 	USED(tz);
    849 	y += 1900;
    850 	if (y < 1970)
    851 		return 0;
    852 	if (M < 1 || M > 12)
    853 		return 0;
    854 	if (d < 1 || d > dmsize[M-1])
    855 		if (!(M == 2 && d == 29 && dysize(y) == 366))
    856 			return 0;
    857 	if (h > 23)
    858 		return 0;
    859 	if (m > 59)
    860 		return 0;
    861 	if (s > 59)
    862 		return 0;
    863 	t = 0;
    864 	for(i=1970; i<y; i++)
    865 		t += dysize(i);
    866 	if (dysize(y)==366 && M >= 3)
    867 		t++;
    868 	while(--M)
    869 		t += dmsize[M-1];
    870 	t += d-1;
    871 	t = 24*t + h;
    872 	t = 60*t + m;
    873 	t = 60*t + s;
    874 	return t;
    875 }
    876 
    877 #define	p	((uchar*)arg)
    878 
    879 static long
    880 l16(void *arg)
    881 {
    882 	long v;
    883 
    884 	v = ((long)p[1]<<8)|p[0];
    885 	if (v >= 0x8000L)
    886 		v -= 0x10000L;
    887 	return v;
    888 }
    889 
    890 static long
    891 l32(void *arg)
    892 {
    893 	return ((((((long)p[3]<<8)|p[2])<<8)|p[1])<<8)|p[0];
    894 }
    895 
    896 #undef	p