xref: /plan9/sys/src/cmd/9660srv/9660srv.c (revision 1f3c807ea9d1aebcf3c35268748c0042242aa4b7)
13e12c5d1SDavid du Colombier #include <u.h>
23e12c5d1SDavid du Colombier #include <libc.h>
3219b2ee8SDavid du Colombier #include <auth.h>
43e12c5d1SDavid du Colombier #include <fcall.h>
53e12c5d1SDavid du Colombier #include "dat.h"
63e12c5d1SDavid du Colombier #include "fns.h"
73e12c5d1SDavid du Colombier #include "iso9660.h"
83e12c5d1SDavid du Colombier 
93e12c5d1SDavid du Colombier static void	ireset(void);
103e12c5d1SDavid du Colombier static int	iattach(Xfile*);
113e12c5d1SDavid du Colombier static void	iclone(Xfile*, Xfile*);
123e12c5d1SDavid du Colombier static void	iwalkup(Xfile*);
133e12c5d1SDavid du Colombier static void	iwalk(Xfile*, char*);
143e12c5d1SDavid du Colombier static void	iopen(Xfile*, int);
153e12c5d1SDavid du Colombier static void	icreate(Xfile*, char*, long, int);
169a747e4fSDavid du Colombier static long	ireaddir(Xfile*, uchar*, long, long);
177dd7cddfSDavid du Colombier static long	iread(Xfile*, char*, long, long);
187dd7cddfSDavid du Colombier static long	iwrite(Xfile*, char*, long, long);
193e12c5d1SDavid du Colombier static void	iclunk(Xfile*);
203e12c5d1SDavid du Colombier static void	iremove(Xfile*);
213e12c5d1SDavid du Colombier static void	istat(Xfile*, Dir*);
223e12c5d1SDavid du Colombier static void	iwstat(Xfile*, Dir*);
233e12c5d1SDavid du Colombier 
247dd7cddfSDavid du Colombier static char*	nstr(uchar*, int);
257dd7cddfSDavid du Colombier static char*	rdate(uchar*, int);
2659cc4ca5SDavid du Colombier static int	getcontin(Xdata*, uchar*, uchar**);
273e12c5d1SDavid du Colombier static int	getdrec(Xfile*, void*);
289a747e4fSDavid du Colombier static void	ungetdrec(Xfile*);
293e12c5d1SDavid du Colombier static int	opendotdot(Xfile*, Xfile*);
303e12c5d1SDavid du Colombier static int	showdrec(int, int, void*);
317dd7cddfSDavid du Colombier static long	gtime(uchar*);
323e12c5d1SDavid du Colombier static long	l16(void*);
333e12c5d1SDavid du Colombier static long	l32(void*);
343e12c5d1SDavid du Colombier static void	newdrec(Xfile*, Drec*);
3559cc4ca5SDavid du Colombier static int	rzdir(Xfs*, Dir*, int, Drec*);
363e12c5d1SDavid du Colombier 
37bd389b36SDavid du Colombier Xfsub	isosub =
38bd389b36SDavid du Colombier {
393e12c5d1SDavid du Colombier 	ireset, iattach, iclone, iwalkup, iwalk, iopen, icreate,
403e12c5d1SDavid du Colombier 	ireaddir, iread, iwrite, iclunk, iremove, istat, iwstat
413e12c5d1SDavid du Colombier };
423e12c5d1SDavid du Colombier 
433e12c5d1SDavid du Colombier static void
443e12c5d1SDavid du Colombier ireset(void)
453e12c5d1SDavid du Colombier {}
463e12c5d1SDavid du Colombier 
473e12c5d1SDavid du Colombier static int
483e12c5d1SDavid du Colombier iattach(Xfile *root)
493e12c5d1SDavid du Colombier {
503e12c5d1SDavid du Colombier 	Xfs *cd = root->xf;
513e12c5d1SDavid du Colombier 	Iobuf *p; Voldesc *v; Isofile *fp; Drec *dp;
5259cc4ca5SDavid du Colombier 	int fmt, blksize, i, n, l, haveplan9;
537dd7cddfSDavid du Colombier 	Iobuf *dirp;
5459cc4ca5SDavid du Colombier 	uchar dbuf[256];
5559cc4ca5SDavid du Colombier 	Drec *rd = (Drec *)dbuf;
5659cc4ca5SDavid du Colombier 	uchar *q, *s;
573e12c5d1SDavid du Colombier 
587dd7cddfSDavid du Colombier 	dirp = nil;
597dd7cddfSDavid du Colombier 	blksize = 0;
607dd7cddfSDavid du Colombier 	fmt = 0;
617dd7cddfSDavid du Colombier 	dp = nil;
627dd7cddfSDavid du Colombier 	haveplan9 = 0;
637dd7cddfSDavid du Colombier 	for(i=VOLDESC;i<VOLDESC+100; i++){	/* +100 for sanity */
647dd7cddfSDavid du Colombier 		p = getbuf(cd->d, i);
653e12c5d1SDavid du Colombier 		v = (Voldesc*)(p->iobuf);
663e12c5d1SDavid du Colombier 		if(memcmp(v->byte, "\01CD001\01", 7) == 0){		/* iso */
677dd7cddfSDavid du Colombier 			if(dirp)
687dd7cddfSDavid du Colombier 				putbuf(dirp);
697dd7cddfSDavid du Colombier 			dirp = p;
703e12c5d1SDavid du Colombier 			fmt = 'z';
713e12c5d1SDavid du Colombier 			dp = (Drec*)v->z.desc.rootdir;
723e12c5d1SDavid du Colombier 			blksize = l16(v->z.desc.blksize);
733e12c5d1SDavid du Colombier 			chat("iso, blksize=%d...", blksize);
747dd7cddfSDavid du Colombier 
757dd7cddfSDavid du Colombier 			v = (Voldesc*)(dirp->iobuf);
767dd7cddfSDavid du Colombier 			haveplan9 = (strncmp((char*)v->z.boot.sysid, "PLAN 9", 6)==0);
7759cc4ca5SDavid du Colombier 			if(haveplan9){
787dd7cddfSDavid du Colombier 				if(noplan9) {
7959cc4ca5SDavid du Colombier 					chat("ignoring plan9");
807dd7cddfSDavid du Colombier 					haveplan9 = 0;
817dd7cddfSDavid du Colombier 				} else {
827dd7cddfSDavid du Colombier 					fmt = '9';
837dd7cddfSDavid du Colombier 					chat("plan9 iso...");
847dd7cddfSDavid du Colombier 				}
8559cc4ca5SDavid du Colombier 			}
867dd7cddfSDavid du Colombier 			continue;
877dd7cddfSDavid du Colombier 		}
887dd7cddfSDavid du Colombier 
897dd7cddfSDavid du Colombier 		if(memcmp(&v->byte[8], "\01CDROM\01", 7) == 0){	/* high sierra */
907dd7cddfSDavid du Colombier 			if(dirp)
917dd7cddfSDavid du Colombier 				putbuf(dirp);
927dd7cddfSDavid du Colombier 			dirp = p;
933e12c5d1SDavid du Colombier 			fmt = 'r';
943e12c5d1SDavid du Colombier 			dp = (Drec*)v->r.desc.rootdir;
953e12c5d1SDavid du Colombier 			blksize = l16(v->r.desc.blksize);
963e12c5d1SDavid du Colombier 			chat("high sierra, blksize=%d...", blksize);
977dd7cddfSDavid du Colombier 			continue;
987dd7cddfSDavid du Colombier 		}
997dd7cddfSDavid du Colombier 
1007dd7cddfSDavid du Colombier 		if(haveplan9==0 && !nojoliet
1017dd7cddfSDavid du Colombier 		&& memcmp(v->byte, "\02CD001\01", 7) == 0){
1027dd7cddfSDavid du Colombier chat("%d %d\n", haveplan9, nojoliet);
1037dd7cddfSDavid du Colombier 			/*
1047dd7cddfSDavid du Colombier 			 * The right thing to do is walk the escape sequences looking
1057dd7cddfSDavid du Colombier 			 * for one of 25 2F 4[035], but Microsoft seems to not honor
1067dd7cddfSDavid du Colombier 			 * the format, which makes it hard to walk over.
1077dd7cddfSDavid du Colombier 			 */
1087dd7cddfSDavid du Colombier 			q = v->z.desc.escapes;
1097dd7cddfSDavid du Colombier 			if(q[0] == 0x25 && q[1] == 0x2F && (q[2] == 0x40 || q[2] == 0x43 || q[2] == 0x45)){	/* Joliet, it appears */
1107dd7cddfSDavid du Colombier 				if(dirp)
1117dd7cddfSDavid du Colombier 					putbuf(dirp);
1127dd7cddfSDavid du Colombier 				dirp = p;
1137dd7cddfSDavid du Colombier 				fmt = 'J';
1147dd7cddfSDavid du Colombier 				dp = (Drec*)v->z.desc.rootdir;
1157dd7cddfSDavid du Colombier 				if(blksize != l16(v->z.desc.blksize))
1167dd7cddfSDavid du Colombier 					fprint(2, "warning: suspicious Joliet blocksize\n");
1177dd7cddfSDavid du Colombier 				chat("joliet...");
1187dd7cddfSDavid du Colombier 				continue;
1197dd7cddfSDavid du Colombier 			}
1207dd7cddfSDavid du Colombier 		}
1213e12c5d1SDavid du Colombier 		putbuf(p);
1227dd7cddfSDavid du Colombier 		if(v->byte[0] == 0xFF)
1237dd7cddfSDavid du Colombier 			break;
1247dd7cddfSDavid du Colombier 	}
1257dd7cddfSDavid du Colombier 
1267dd7cddfSDavid du Colombier 	if(fmt == 0){
1277dd7cddfSDavid du Colombier 		if(dirp)
1287dd7cddfSDavid du Colombier 			putbuf(dirp);
1293e12c5d1SDavid du Colombier 		return -1;
1303e12c5d1SDavid du Colombier 	}
1317dd7cddfSDavid du Colombier 	assert(dirp != nil);
1327dd7cddfSDavid du Colombier 
1333e12c5d1SDavid du Colombier 	if(chatty)
1343e12c5d1SDavid du Colombier 		showdrec(2, fmt, dp);
1353e12c5d1SDavid du Colombier 	if(blksize > Sectorsize){
1363e12c5d1SDavid du Colombier 		chat("blksize too big...");
1377dd7cddfSDavid du Colombier 		putbuf(dirp);
1383e12c5d1SDavid du Colombier 		return -1;
1393e12c5d1SDavid du Colombier 	}
1403e12c5d1SDavid du Colombier 	if(waserror()){
1417dd7cddfSDavid du Colombier 		putbuf(dirp);
1423e12c5d1SDavid du Colombier 		nexterror();
1433e12c5d1SDavid du Colombier 	}
1443e12c5d1SDavid du Colombier 	root->len = sizeof(Isofile) - sizeof(Drec) + dp->reclen;
1453e12c5d1SDavid du Colombier 	root->ptr = fp = ealloc(root->len);
1467dd7cddfSDavid du Colombier 
1477dd7cddfSDavid du Colombier 	if(haveplan9)
1487dd7cddfSDavid du Colombier 		root->xf->isplan9 = 1;
1497dd7cddfSDavid du Colombier 
1503e12c5d1SDavid du Colombier 	fp->fmt = fmt;
1513e12c5d1SDavid du Colombier 	fp->blksize = blksize;
1523e12c5d1SDavid du Colombier 	fp->offset = 0;
1533e12c5d1SDavid du Colombier 	fp->doffset = 0;
1543e12c5d1SDavid du Colombier 	memmove(&fp->d, dp, dp->reclen);
1559a747e4fSDavid du Colombier 	root->qid.path = l32(dp->addr);
1569a747e4fSDavid du Colombier 	root->qid.type = QTDIR;
1577dd7cddfSDavid du Colombier 	putbuf(dirp);
1583e12c5d1SDavid du Colombier 	poperror();
15959cc4ca5SDavid du Colombier 	if(getdrec(root, rd) >= 0){
16059cc4ca5SDavid du Colombier 		n = rd->reclen-(34+rd->namelen);
16159cc4ca5SDavid du Colombier 		s = (uchar*)rd->name + rd->namelen;
16259cc4ca5SDavid du Colombier 		if((ulong)s & 1){
16359cc4ca5SDavid du Colombier 			s++;
16459cc4ca5SDavid du Colombier 			n--;
16559cc4ca5SDavid du Colombier 		}
16659cc4ca5SDavid du Colombier 		if(n >= 7 && s[0] == 'S' && s[1] == 'P' && s[2] == 7 &&
16759cc4ca5SDavid du Colombier 		   s[3] == 1 && s[4] == 0xBE && s[5] == 0xEF){
16859cc4ca5SDavid du Colombier 			root->xf->issusp = 1;
16959cc4ca5SDavid du Colombier 			root->xf->suspoff = s[6];
17059cc4ca5SDavid du Colombier 			n -= root->xf->suspoff;
17159cc4ca5SDavid du Colombier 			s += root->xf->suspoff;
17259cc4ca5SDavid du Colombier 			for(; n >= 4; s += l, n -= l){
17359cc4ca5SDavid du Colombier 				l = s[2];
17459cc4ca5SDavid du Colombier 				if(s[0] == 'E' && s[1] == 'R'){
17559cc4ca5SDavid du Colombier 					if(!norock && s[4] == 10 && memcmp(s+8, "RRIP_1991A", 10) == 0)
17659cc4ca5SDavid du Colombier 						root->xf->isrock = 1;
17759cc4ca5SDavid du Colombier 					break;
17859cc4ca5SDavid du Colombier 				} else if(s[0] == 'C' && s[1] == 'E' && s[2] >= 28){
17959cc4ca5SDavid du Colombier 					n = getcontin(root->xf->d, s, &s);
18059cc4ca5SDavid du Colombier 					continue;
18159cc4ca5SDavid du Colombier 				} else if(s[0] == 'R' && s[1] == 'R'){
18259cc4ca5SDavid du Colombier 					if(!norock)
18359cc4ca5SDavid du Colombier 						root->xf->isrock = 1;
18459cc4ca5SDavid du Colombier 					break;
18559cc4ca5SDavid du Colombier 				} else if(s[0] == 'S' && s[1] == 'T')
18659cc4ca5SDavid du Colombier 					break;
18759cc4ca5SDavid du Colombier 			}
18859cc4ca5SDavid du Colombier 		}
18959cc4ca5SDavid du Colombier 	}
19059cc4ca5SDavid du Colombier 	if(root->xf->isrock)
19159cc4ca5SDavid du Colombier 		chat("Rock Ridge...");
19259cc4ca5SDavid du Colombier 	fp->offset = 0;
19359cc4ca5SDavid du Colombier 	fp->doffset = 0;
1943e12c5d1SDavid du Colombier 	return 0;
1953e12c5d1SDavid du Colombier }
1963e12c5d1SDavid du Colombier 
1973e12c5d1SDavid du Colombier static void
1983e12c5d1SDavid du Colombier iclone(Xfile *of, Xfile *nf)
1993e12c5d1SDavid du Colombier {
2003e12c5d1SDavid du Colombier 	USED(of, nf);
2013e12c5d1SDavid du Colombier }
2023e12c5d1SDavid du Colombier 
2033e12c5d1SDavid du Colombier static void
2043e12c5d1SDavid du Colombier iwalkup(Xfile *f)
2053e12c5d1SDavid du Colombier {
2063e12c5d1SDavid du Colombier 	long paddr;
2073e12c5d1SDavid du Colombier 	uchar dbuf[256];
2083e12c5d1SDavid du Colombier 	Drec *d = (Drec *)dbuf;
2093e12c5d1SDavid du Colombier 	Xfile pf, ppf;
2103e12c5d1SDavid du Colombier 	Isofile piso, ppiso;
2113e12c5d1SDavid du Colombier 
2123e12c5d1SDavid du Colombier 	memset(&pf, 0, sizeof pf);
2133e12c5d1SDavid du Colombier 	memset(&ppf, 0, sizeof ppf);
2143e12c5d1SDavid du Colombier 	pf.ptr = &piso;
2153e12c5d1SDavid du Colombier 	ppf.ptr = &ppiso;
2163e12c5d1SDavid du Colombier 	if(opendotdot(f, &pf) < 0)
2173e12c5d1SDavid du Colombier 		error("can't open pf");
2189a747e4fSDavid du Colombier 	paddr = l32(pf.ptr->d.addr);
2199a747e4fSDavid du Colombier 	if(l32(f->ptr->d.addr) == paddr)
2203e12c5d1SDavid du Colombier 		return;
2213e12c5d1SDavid du Colombier 	if(opendotdot(&pf, &ppf) < 0)
2223e12c5d1SDavid du Colombier 		error("can't open ppf");
2233e12c5d1SDavid du Colombier 	while(getdrec(&ppf, d) >= 0){
2243e12c5d1SDavid du Colombier 		if(l32(d->addr) == paddr){
2253e12c5d1SDavid du Colombier 			newdrec(f, d);
2269a747e4fSDavid du Colombier 			f->qid.path = paddr;
2279a747e4fSDavid du Colombier 			f->qid.type = QTDIR;
2283e12c5d1SDavid du Colombier 			return;
2293e12c5d1SDavid du Colombier 		}
2303e12c5d1SDavid du Colombier 	}
2313e12c5d1SDavid du Colombier 	error("can't find addr of ..");
2323e12c5d1SDavid du Colombier }
2333e12c5d1SDavid du Colombier 
2347dd7cddfSDavid du Colombier static int
2357dd7cddfSDavid du Colombier casestrcmp(int isplan9, char *a, char *b)
2367dd7cddfSDavid du Colombier {
2377dd7cddfSDavid du Colombier 	int ca, cb;
2387dd7cddfSDavid du Colombier 
2397dd7cddfSDavid du Colombier 	if(isplan9)
2407dd7cddfSDavid du Colombier 		return strcmp(a, b);
2417dd7cddfSDavid du Colombier 	for(;;) {
2427dd7cddfSDavid du Colombier 		ca = *a++;
2437dd7cddfSDavid du Colombier 		cb = *b++;
2447dd7cddfSDavid du Colombier 		if(ca >= 'A' && ca <= 'Z')
2457dd7cddfSDavid du Colombier 			ca += 'a' - 'A';
2467dd7cddfSDavid du Colombier 		if(cb >= 'A' && cb <= 'Z')
2477dd7cddfSDavid du Colombier 			cb += 'a' - 'A';
2487dd7cddfSDavid du Colombier 		if(ca != cb) {
2497dd7cddfSDavid du Colombier 			if(ca > cb)
2507dd7cddfSDavid du Colombier 				return 1;
2517dd7cddfSDavid du Colombier 			return -1;
2527dd7cddfSDavid du Colombier 		}
2537dd7cddfSDavid du Colombier 		if(ca == 0)
2547dd7cddfSDavid du Colombier 			return 0;
2557dd7cddfSDavid du Colombier 	}
2567dd7cddfSDavid du Colombier }
2577dd7cddfSDavid du Colombier 
2583e12c5d1SDavid du Colombier static void
2593e12c5d1SDavid du Colombier iwalk(Xfile *f, char *name)
2603e12c5d1SDavid du Colombier {
2613e12c5d1SDavid du Colombier 	Isofile *ip = f->ptr;
2623e12c5d1SDavid du Colombier 	uchar dbuf[256];
2639a747e4fSDavid du Colombier 	char nbuf[4*Maxname];
2643e12c5d1SDavid du Colombier 	Drec *d = (Drec*)dbuf;
2653e12c5d1SDavid du Colombier 	Dir dir;
2663e12c5d1SDavid du Colombier 	char *p;
267bd389b36SDavid du Colombier 	int len, vers, dvers;
2683e12c5d1SDavid du Colombier 
269bd389b36SDavid du Colombier 	vers = -1;
270bd389b36SDavid du Colombier 	if(p = strchr(name, ';')) {	/* assign = */
271bd389b36SDavid du Colombier 		len = p-name;
2729a747e4fSDavid du Colombier 		if(len >= Maxname)
2739a747e4fSDavid du Colombier 			len = Maxname-1;
274bd389b36SDavid du Colombier 		memmove(nbuf, name, len);
275bd389b36SDavid du Colombier 		vers = strtoul(p+1, 0, 10);
2763e12c5d1SDavid du Colombier 		name = nbuf;
2773e12c5d1SDavid du Colombier 	}
2789a747e4fSDavid du Colombier /*
279bd389b36SDavid du Colombier 	len = strlen(name);
2809a747e4fSDavid du Colombier 	if(len >= Maxname){
2819a747e4fSDavid du Colombier 		len = Maxname-1;
2829a747e4fSDavid du Colombier 		if(name != nbuf){
2839a747e4fSDavid du Colombier 			memmove(nbuf, name, len);
2849a747e4fSDavid du Colombier 			name = nbuf;
2859a747e4fSDavid du Colombier 		}
286bd389b36SDavid du Colombier 		name[len] = 0;
2879a747e4fSDavid du Colombier 	}
2889a747e4fSDavid du Colombier */
289bd389b36SDavid du Colombier 
2909a747e4fSDavid du Colombier 	chat("%d \"%s\"...", strlen(name), name);
2913e12c5d1SDavid du Colombier 	ip->offset = 0;
2929a747e4fSDavid du Colombier 	setnames(&dir, nbuf);
293bd389b36SDavid du Colombier 	while(getdrec(f, d) >= 0) {
29459cc4ca5SDavid du Colombier 		dvers = rzdir(f->xf, &dir, ip->fmt, d);
29559cc4ca5SDavid du Colombier 		if(casestrcmp(f->xf->isplan9||f->xf->isrock, name, dir.name) != 0)
2963e12c5d1SDavid du Colombier 			continue;
2973e12c5d1SDavid du Colombier 		newdrec(f, d);
2983e12c5d1SDavid du Colombier 		f->qid.path = dir.qid.path;
2999a747e4fSDavid du Colombier 		f->qid.type = dir.qid.type;
300bd389b36SDavid du Colombier 		USED(dvers);
3013e12c5d1SDavid du Colombier 		return;
3023e12c5d1SDavid du Colombier 	}
303bd389b36SDavid du Colombier 	USED(vers);
3043e12c5d1SDavid du Colombier 	error(Enonexist);
3053e12c5d1SDavid du Colombier }
3063e12c5d1SDavid du Colombier 
3073e12c5d1SDavid du Colombier static void
3083e12c5d1SDavid du Colombier iopen(Xfile *f, int mode)
3093e12c5d1SDavid du Colombier {
3103e12c5d1SDavid du Colombier 	mode &= ~OCEXEC;
3113e12c5d1SDavid du Colombier 	if(mode != OREAD && mode != OEXEC)
3123e12c5d1SDavid du Colombier 		error(Eperm);
3139a747e4fSDavid du Colombier 	f->ptr->offset = 0;
3149a747e4fSDavid du Colombier 	f->ptr->doffset = 0;
3153e12c5d1SDavid du Colombier }
3163e12c5d1SDavid du Colombier 
3173e12c5d1SDavid du Colombier static void
3183e12c5d1SDavid du Colombier icreate(Xfile *f, char *name, long perm, int mode)
3193e12c5d1SDavid du Colombier {
3203e12c5d1SDavid du Colombier 	USED(f, name, perm, mode);
3213e12c5d1SDavid du Colombier 	error(Eperm);
3223e12c5d1SDavid du Colombier }
3233e12c5d1SDavid du Colombier 
3243e12c5d1SDavid du Colombier static long
3259a747e4fSDavid du Colombier ireaddir(Xfile *f, uchar *buf, long offset, long count)
3263e12c5d1SDavid du Colombier {
3273e12c5d1SDavid du Colombier 	Isofile *ip = f->ptr;
3283e12c5d1SDavid du Colombier 	Dir d;
3299a747e4fSDavid du Colombier 	char names[4*Maxname];
3303e12c5d1SDavid du Colombier 	uchar dbuf[256];
3313e12c5d1SDavid du Colombier 	Drec *drec = (Drec *)dbuf;
3329a747e4fSDavid du Colombier 	int n, rcnt;
3333e12c5d1SDavid du Colombier 
3349a747e4fSDavid du Colombier 	if(offset==0){
3353e12c5d1SDavid du Colombier 		ip->offset = 0;
3363e12c5d1SDavid du Colombier 		ip->doffset = 0;
3379a747e4fSDavid du Colombier 	}else if(offset != ip->doffset)
3389a747e4fSDavid du Colombier 		error("seek in directory not allowed");
3399a747e4fSDavid du Colombier 
3409a747e4fSDavid du Colombier 	rcnt = 0;
3419a747e4fSDavid du Colombier 	setnames(&d, names);
3423e12c5d1SDavid du Colombier 	while(rcnt < count && getdrec(f, drec) >= 0){
3433e12c5d1SDavid du Colombier 		if(drec->namelen == 1){
3443e12c5d1SDavid du Colombier 			if(drec->name[0] == 0)
3453e12c5d1SDavid du Colombier 				continue;
3463e12c5d1SDavid du Colombier 			if(drec->name[0] == 1)
3473e12c5d1SDavid du Colombier 				continue;
3483e12c5d1SDavid du Colombier 		}
34959cc4ca5SDavid du Colombier 		rzdir(f->xf, &d, ip->fmt, drec);
3503e12c5d1SDavid du Colombier 		d.qid.vers = f->qid.vers;
3519a747e4fSDavid du Colombier 		if((n = convD2M(&d, buf+rcnt, count-rcnt)) <= BIT16SZ){
3529a747e4fSDavid du Colombier 			ungetdrec(f);
3539a747e4fSDavid du Colombier 			break;
3549a747e4fSDavid du Colombier 		}
3559a747e4fSDavid du Colombier 		rcnt += n;
3563e12c5d1SDavid du Colombier 	}
3573e12c5d1SDavid du Colombier 	ip->doffset += rcnt;
3583e12c5d1SDavid du Colombier 	return rcnt;
3593e12c5d1SDavid du Colombier }
3603e12c5d1SDavid du Colombier 
3613e12c5d1SDavid du Colombier static long
3623e12c5d1SDavid du Colombier iread(Xfile *f, char *buf, long offset, long count)
3633e12c5d1SDavid du Colombier {
3643e12c5d1SDavid du Colombier 	Isofile *ip = f->ptr;
3653e12c5d1SDavid du Colombier 	long size, addr, o, n;
3663e12c5d1SDavid du Colombier 	int rcnt = 0;
3673e12c5d1SDavid du Colombier 	Iobuf *p;
3683e12c5d1SDavid du Colombier 
3693e12c5d1SDavid du Colombier 	size = l32(ip->d.size);
3703e12c5d1SDavid du Colombier 	if(offset >= size)
3713e12c5d1SDavid du Colombier 		return 0;
3723e12c5d1SDavid du Colombier 	if(offset+count > size)
3733e12c5d1SDavid du Colombier 		count = size - offset;
3743e12c5d1SDavid du Colombier 	addr = (l32(ip->d.addr)+ip->d.attrlen)*ip->blksize + offset;
3757dd7cddfSDavid du Colombier 	o = (ulong)addr % Sectorsize;
3767dd7cddfSDavid du Colombier 	addr = (ulong)addr / Sectorsize;
3773e12c5d1SDavid du Colombier 	/*chat("d.addr=0x%x, addr=0x%x, o=0x%x...", l32(ip->d.addr), addr, o);*/
3783e12c5d1SDavid du Colombier 	n = Sectorsize - o;
3793e12c5d1SDavid du Colombier 
3803e12c5d1SDavid du Colombier 	while(count > 0){
3813e12c5d1SDavid du Colombier 		if(n > count)
3823e12c5d1SDavid du Colombier 			n = count;
3833e12c5d1SDavid du Colombier 		p = getbuf(f->xf->d, addr);
3843e12c5d1SDavid du Colombier 		memmove(&buf[rcnt], &p->iobuf[o], n);
3853e12c5d1SDavid du Colombier 		putbuf(p);
3863e12c5d1SDavid du Colombier 		count -= n;
3873e12c5d1SDavid du Colombier 		rcnt += n;
3883e12c5d1SDavid du Colombier 		++addr;
3893e12c5d1SDavid du Colombier 		o = 0;
3903e12c5d1SDavid du Colombier 		n = Sectorsize;
3913e12c5d1SDavid du Colombier 	}
3923e12c5d1SDavid du Colombier 	return rcnt;
3933e12c5d1SDavid du Colombier }
3943e12c5d1SDavid du Colombier 
3953e12c5d1SDavid du Colombier static long
3967dd7cddfSDavid du Colombier iwrite(Xfile *f, char *buf, long offset, long count)
3973e12c5d1SDavid du Colombier {
3983e12c5d1SDavid du Colombier 	USED(f, buf, offset, count);
3993e12c5d1SDavid du Colombier 	error(Eperm);
4003e12c5d1SDavid du Colombier 	return 0;
4013e12c5d1SDavid du Colombier }
4023e12c5d1SDavid du Colombier 
4033e12c5d1SDavid du Colombier static void
4043e12c5d1SDavid du Colombier iclunk(Xfile *f)
4053e12c5d1SDavid du Colombier {
4063e12c5d1SDavid du Colombier 	USED(f);
4073e12c5d1SDavid du Colombier }
4083e12c5d1SDavid du Colombier 
4093e12c5d1SDavid du Colombier static void
4103e12c5d1SDavid du Colombier iremove(Xfile *f)
4113e12c5d1SDavid du Colombier {
4123e12c5d1SDavid du Colombier 	USED(f);
4133e12c5d1SDavid du Colombier 	error(Eperm);
4143e12c5d1SDavid du Colombier }
4153e12c5d1SDavid du Colombier 
4163e12c5d1SDavid du Colombier static void
4173e12c5d1SDavid du Colombier istat(Xfile *f, Dir *d)
4183e12c5d1SDavid du Colombier {
4193e12c5d1SDavid du Colombier 	Isofile *ip = f->ptr;
4203e12c5d1SDavid du Colombier 
42159cc4ca5SDavid du Colombier 	rzdir(f->xf, d, ip->fmt, &ip->d);
4223e12c5d1SDavid du Colombier 	d->qid.vers = f->qid.vers;
4239a747e4fSDavid du Colombier 	if(d->qid.path==f->xf->rootqid.path){
4249a747e4fSDavid du Colombier 		d->qid.path = 0;
4259a747e4fSDavid du Colombier 		d->qid.type = QTDIR;
4269a747e4fSDavid du Colombier 	}
4273e12c5d1SDavid du Colombier }
4283e12c5d1SDavid du Colombier 
4293e12c5d1SDavid du Colombier static void
4303e12c5d1SDavid du Colombier iwstat(Xfile *f, Dir *d)
4313e12c5d1SDavid du Colombier {
4323e12c5d1SDavid du Colombier 	USED(f, d);
4333e12c5d1SDavid du Colombier 	error(Eperm);
4343e12c5d1SDavid du Colombier }
4353e12c5d1SDavid du Colombier 
4363e12c5d1SDavid du Colombier static int
4373e12c5d1SDavid du Colombier showdrec(int fd, int fmt, void *x)
4383e12c5d1SDavid du Colombier {
4393e12c5d1SDavid du Colombier 	Drec *d = (Drec *)x;
4407dd7cddfSDavid du Colombier 	int namelen;
4417dd7cddfSDavid du Colombier 	int syslen;
4423e12c5d1SDavid du Colombier 
4433e12c5d1SDavid du Colombier 	if(d->reclen == 0)
4443e12c5d1SDavid du Colombier 		return 0;
4457dd7cddfSDavid du Colombier 	fprint(fd, "%d %d %ld %ld ",
4463e12c5d1SDavid du Colombier 		d->reclen, d->attrlen, l32(d->addr), l32(d->size));
4477dd7cddfSDavid du Colombier 	fprint(fd, "%s 0x%2.2x %d %d %ld ",
4483e12c5d1SDavid du Colombier 		rdate(d->date, fmt), (fmt=='z' ? d->flags : d->r_flags),
4493e12c5d1SDavid du Colombier 		d->unitsize, d->gapsize, l16(d->vseqno));
4503e12c5d1SDavid du Colombier 	fprint(fd, "%d %s", d->namelen, nstr(d->name, d->namelen));
4517dd7cddfSDavid du Colombier 	if(fmt != 'J'){
4523e12c5d1SDavid du Colombier 		namelen = d->namelen + (1-(d->namelen&1));
4533e12c5d1SDavid du Colombier 		syslen = d->reclen - 33 - namelen;
4543e12c5d1SDavid du Colombier 		if(syslen != 0)
4553e12c5d1SDavid du Colombier 			fprint(fd, " %s", nstr(&d->name[namelen], syslen));
4567dd7cddfSDavid du Colombier 	}
4573e12c5d1SDavid du Colombier 	fprint(fd, "\n");
4583e12c5d1SDavid du Colombier 	return d->reclen + (d->reclen&1);
4593e12c5d1SDavid du Colombier }
4603e12c5d1SDavid du Colombier 
4613e12c5d1SDavid du Colombier static void
4623e12c5d1SDavid du Colombier newdrec(Xfile *f, Drec *dp)
4633e12c5d1SDavid du Colombier {
4643e12c5d1SDavid du Colombier 	Isofile *x = f->ptr;
4653e12c5d1SDavid du Colombier 	Isofile *n;
4663e12c5d1SDavid du Colombier 	int len;
4673e12c5d1SDavid du Colombier 
4683e12c5d1SDavid du Colombier 	len = sizeof(Isofile) - sizeof(Drec) + dp->reclen;
4693e12c5d1SDavid du Colombier 	n = ealloc(len);
4703e12c5d1SDavid du Colombier 	n->fmt = x->fmt;
4713e12c5d1SDavid du Colombier 	n->blksize = x->blksize;
4723e12c5d1SDavid du Colombier 	n->offset = 0;
4733e12c5d1SDavid du Colombier 	n->doffset = 0;
4743e12c5d1SDavid du Colombier 	memmove(&n->d, dp, dp->reclen);
4753e12c5d1SDavid du Colombier 	free(x);
4763e12c5d1SDavid du Colombier 	f->ptr = n;
4773e12c5d1SDavid du Colombier 	f->len = len;
4783e12c5d1SDavid du Colombier }
4793e12c5d1SDavid du Colombier 
4809a747e4fSDavid du Colombier static void
4819a747e4fSDavid du Colombier ungetdrec(Xfile *f)
4829a747e4fSDavid du Colombier {
4839a747e4fSDavid du Colombier 	Isofile *ip = f->ptr;
4849a747e4fSDavid du Colombier 
4859a747e4fSDavid du Colombier 	if(ip->offset >= ip->odelta){
4869a747e4fSDavid du Colombier 		ip->offset -= ip->odelta;
4879a747e4fSDavid du Colombier 		ip->odelta = 0;
4889a747e4fSDavid du Colombier 	}
4899a747e4fSDavid du Colombier }
4909a747e4fSDavid du Colombier 
4913e12c5d1SDavid du Colombier static int
4923e12c5d1SDavid du Colombier getdrec(Xfile *f, void *buf)
4933e12c5d1SDavid du Colombier {
4943e12c5d1SDavid du Colombier 	Isofile *ip = f->ptr;
4953e12c5d1SDavid du Colombier 	int len = 0, boff = 0;
4963e12c5d1SDavid du Colombier 	long size, addr;
4973e12c5d1SDavid du Colombier 	Iobuf *p = 0;
4983e12c5d1SDavid du Colombier 
4993e12c5d1SDavid du Colombier 	if(!ip)
5003e12c5d1SDavid du Colombier 		return -1;
5013e12c5d1SDavid du Colombier 	size = l32(ip->d.size);
5023e12c5d1SDavid du Colombier 	while(ip->offset<size){
5033e12c5d1SDavid du Colombier 		addr = (l32(ip->d.addr)+ip->d.attrlen)*ip->blksize + ip->offset;
5047dd7cddfSDavid du Colombier 		boff = (ulong)addr % Sectorsize;
5053e12c5d1SDavid du Colombier 		if(boff > Sectorsize-34){
5063e12c5d1SDavid du Colombier 			ip->offset += Sectorsize-boff;
5073e12c5d1SDavid du Colombier 			continue;
5083e12c5d1SDavid du Colombier 		}
5097dd7cddfSDavid du Colombier 		p = getbuf(f->xf->d, (ulong)addr/Sectorsize);
5103e12c5d1SDavid du Colombier 		len = p->iobuf[boff];
5113e12c5d1SDavid du Colombier 		if(len >= 34)
5123e12c5d1SDavid du Colombier 			break;
5133e12c5d1SDavid du Colombier 		putbuf(p);
5143e12c5d1SDavid du Colombier 		p = 0;
5153e12c5d1SDavid du Colombier 		ip->offset += Sectorsize-boff;
5163e12c5d1SDavid du Colombier 	}
5173e12c5d1SDavid du Colombier 	if(p) {
5183e12c5d1SDavid du Colombier 		memmove(buf, &p->iobuf[boff], len);
5193e12c5d1SDavid du Colombier 		putbuf(p);
5209a747e4fSDavid du Colombier 		ip->odelta = len + (len&1);
5219a747e4fSDavid du Colombier 		ip->offset += ip->odelta;
5223e12c5d1SDavid du Colombier 	}
523bd389b36SDavid du Colombier 	if(p)
524bd389b36SDavid du Colombier 		return 0;
525bd389b36SDavid du Colombier 	return -1;
5263e12c5d1SDavid du Colombier }
5273e12c5d1SDavid du Colombier 
5283e12c5d1SDavid du Colombier static int
5293e12c5d1SDavid du Colombier opendotdot(Xfile *f, Xfile *pf)
5303e12c5d1SDavid du Colombier {
5313e12c5d1SDavid du Colombier 	uchar dbuf[256];
5323e12c5d1SDavid du Colombier 	Drec *d = (Drec *)dbuf;
5333e12c5d1SDavid du Colombier 	Isofile *ip = f->ptr, *pip = pf->ptr;
5343e12c5d1SDavid du Colombier 
5353e12c5d1SDavid du Colombier 	ip->offset = 0;
5363e12c5d1SDavid du Colombier 	if(getdrec(f, d) < 0){
5373e12c5d1SDavid du Colombier 		chat("opendotdot: getdrec(.) failed...");
5383e12c5d1SDavid du Colombier 		return -1;
5393e12c5d1SDavid du Colombier 	}
5403e12c5d1SDavid du Colombier 	if(d->namelen != 1 || d->name[0] != 0){
5413e12c5d1SDavid du Colombier 		chat("opendotdot: no . entry...");
5423e12c5d1SDavid du Colombier 		return -1;
5433e12c5d1SDavid du Colombier 	}
5443e12c5d1SDavid du Colombier 	if(l32(d->addr) != l32(ip->d.addr)){
5453e12c5d1SDavid du Colombier 		chat("opendotdot: bad . address...");
5463e12c5d1SDavid du Colombier 		return -1;
5473e12c5d1SDavid du Colombier 	}
5483e12c5d1SDavid du Colombier 	if(getdrec(f, d) < 0){
5493e12c5d1SDavid du Colombier 		chat("opendotdot: getdrec(..) failed...");
5503e12c5d1SDavid du Colombier 		return -1;
5513e12c5d1SDavid du Colombier 	}
5523e12c5d1SDavid du Colombier 	if(d->namelen != 1 || d->name[0] != 1){
5533e12c5d1SDavid du Colombier 		chat("opendotdot: no .. entry...");
5543e12c5d1SDavid du Colombier 		return -1;
5553e12c5d1SDavid du Colombier 	}
5563e12c5d1SDavid du Colombier 
5573e12c5d1SDavid du Colombier 	pf->xf = f->xf;
5583e12c5d1SDavid du Colombier 	pip->fmt = ip->fmt;
5593e12c5d1SDavid du Colombier 	pip->blksize = ip->blksize;
5603e12c5d1SDavid du Colombier 	pip->offset = 0;
5613e12c5d1SDavid du Colombier 	pip->doffset = 0;
5623e12c5d1SDavid du Colombier 	pip->d = *d;
5633e12c5d1SDavid du Colombier 	return 0;
5643e12c5d1SDavid du Colombier }
5653e12c5d1SDavid du Colombier 
56659cc4ca5SDavid du Colombier enum {
56759cc4ca5SDavid du Colombier 	Hname = 1,
56859cc4ca5SDavid du Colombier 	Hmode = 2,
56959cc4ca5SDavid du Colombier };
57059cc4ca5SDavid du Colombier 
571bd389b36SDavid du Colombier static int
57259cc4ca5SDavid du Colombier rzdir(Xfs *fs, Dir *d, int fmt, Drec *dp)
5733e12c5d1SDavid du Colombier {
57459cc4ca5SDavid du Colombier 	int n, flags, i, j, lj, nl, vers, sysl, mode, l, have;
5753e12c5d1SDavid du Colombier 	uchar *s;
576bd389b36SDavid du Colombier 	char *p;
5779a747e4fSDavid du Colombier 	char buf[Maxname+UTFmax+1];
5787dd7cddfSDavid du Colombier 	uchar *q;
5797dd7cddfSDavid du Colombier 	Rune r;
5809a747e4fSDavid du Colombier 	enum { ONAMELEN = 28 };	/* old Plan 9 directory name length */
5813e12c5d1SDavid du Colombier 
58259cc4ca5SDavid du Colombier 	have = 0;
583bd389b36SDavid du Colombier 	flags = 0;
584bd389b36SDavid du Colombier 	vers = -1;
5853e12c5d1SDavid du Colombier 	d->qid.path = l32(dp->addr);
5869a747e4fSDavid du Colombier 	d->qid.type = 0;
5873e12c5d1SDavid du Colombier 	d->qid.vers = 0;
588bd389b36SDavid du Colombier 	n = dp->namelen;
5899a747e4fSDavid du Colombier 	memset(d->name, 0, Maxname);
5903e12c5d1SDavid du Colombier 	if(n == 1) {
5913e12c5d1SDavid du Colombier 		switch(dp->name[0]){
5923e12c5d1SDavid du Colombier 		case 1:
5933e12c5d1SDavid du Colombier 			d->name[1] = '.';
5943e12c5d1SDavid du Colombier 			/* fall through */
5953e12c5d1SDavid du Colombier 		case 0:
5963e12c5d1SDavid du Colombier 			d->name[0] = '.';
59759cc4ca5SDavid du Colombier 			have = Hname;
5983e12c5d1SDavid du Colombier 			break;
5993e12c5d1SDavid du Colombier 		default:
6003e12c5d1SDavid du Colombier 			d->name[0] = tolower(dp->name[0]);
6013e12c5d1SDavid du Colombier 		}
6023e12c5d1SDavid du Colombier 	} else {
6037dd7cddfSDavid du Colombier 		if(fmt == 'J'){	/* Joliet, 16-bit Unicode */
6047dd7cddfSDavid du Colombier 			q = (uchar*)dp->name;
6059a747e4fSDavid du Colombier 			for(i=j=lj=0; i<n && j<Maxname; i+=2){
6067dd7cddfSDavid du Colombier 				lj = j;
6077dd7cddfSDavid du Colombier 				r = (q[i]<<8)|q[i+1];
6087dd7cddfSDavid du Colombier 				j += runetochar(buf+j, &r);
6097dd7cddfSDavid du Colombier 			}
6109a747e4fSDavid du Colombier 			if(j >= Maxname)
6117dd7cddfSDavid du Colombier 				j = lj;
6127dd7cddfSDavid du Colombier 			memmove(d->name, buf, j);
6137dd7cddfSDavid du Colombier 		}else{
6149a747e4fSDavid du Colombier 			if(n >= Maxname)
6159a747e4fSDavid du Colombier 				n = Maxname-1;
6163e12c5d1SDavid du Colombier 			for(i=0; i<n; i++)
6173e12c5d1SDavid du Colombier 				d->name[i] = tolower(dp->name[i]);
6183e12c5d1SDavid du Colombier 		}
6197dd7cddfSDavid du Colombier 	}
6203e12c5d1SDavid du Colombier 
62159cc4ca5SDavid du Colombier 	sysl = dp->reclen-(34+dp->namelen);
62259cc4ca5SDavid du Colombier 	s = (uchar*)dp->name + dp->namelen;
62359cc4ca5SDavid du Colombier 	if(((ulong)s) & 1) {
62459cc4ca5SDavid du Colombier 		s++;
62559cc4ca5SDavid du Colombier 		sysl--;
62659cc4ca5SDavid du Colombier 	}
62759cc4ca5SDavid du Colombier 	if(fs->isplan9 && sysl > 0) {
628bd389b36SDavid du Colombier 		/*
629bd389b36SDavid du Colombier 		 * get gid, uid, mode and possibly name
630bd389b36SDavid du Colombier 		 * from plan9 directory extension
631bd389b36SDavid du Colombier 		 */
6323e12c5d1SDavid du Colombier 		nl = *s;
6339a747e4fSDavid du Colombier 		if(nl >= ONAMELEN)
6349a747e4fSDavid du Colombier 			nl = ONAMELEN-1;
6353e12c5d1SDavid du Colombier 		if(nl) {
6369a747e4fSDavid du Colombier 			memset(d->name, 0, ONAMELEN);
6373e12c5d1SDavid du Colombier 			memmove(d->name, s+1, nl);
6383e12c5d1SDavid du Colombier 		}
6393e12c5d1SDavid du Colombier 		s += 1 + *s;
6403e12c5d1SDavid du Colombier 		nl = *s;
6419a747e4fSDavid du Colombier 		if(nl >= ONAMELEN)
6429a747e4fSDavid du Colombier 			nl = ONAMELEN-1;
6439a747e4fSDavid du Colombier 		memset(d->uid, 0, ONAMELEN);
6443e12c5d1SDavid du Colombier 		memmove(d->uid, s+1, nl);
6453e12c5d1SDavid du Colombier 		s += 1 + *s;
6463e12c5d1SDavid du Colombier 		nl = *s;
6479a747e4fSDavid du Colombier 		if(nl >= ONAMELEN)
6489a747e4fSDavid du Colombier 			nl = ONAMELEN-1;
6499a747e4fSDavid du Colombier 		memset(d->gid, 0, ONAMELEN);
6503e12c5d1SDavid du Colombier 		memmove(d->gid, s+1, nl);
6513e12c5d1SDavid du Colombier 		s += 1 + *s;
6523e12c5d1SDavid du Colombier 		if(((ulong)s) & 1)
6533e12c5d1SDavid du Colombier 			s++;
6543e12c5d1SDavid du Colombier 		d->mode = l32(s);
6559a747e4fSDavid du Colombier 		if(d->mode & DMDIR)
6569a747e4fSDavid du Colombier 			d->qid.type |= QTDIR;
6573e12c5d1SDavid du Colombier 	} else {
6583e12c5d1SDavid du Colombier 		d->mode = 0444;
6593e12c5d1SDavid du Colombier 		switch(fmt) {
6603e12c5d1SDavid du Colombier 		case 'z':
66159cc4ca5SDavid du Colombier 			if(fs->isrock)
66259cc4ca5SDavid du Colombier 				strcpy(d->gid, "ridge");
66359cc4ca5SDavid du Colombier 			else
6647dd7cddfSDavid du Colombier 				strcpy(d->gid, "iso9660");
6653e12c5d1SDavid du Colombier 			flags = dp->flags;
6663e12c5d1SDavid du Colombier 			break;
6673e12c5d1SDavid du Colombier 		case 'r':
6683e12c5d1SDavid du Colombier 			strcpy(d->gid, "sierra");
6693e12c5d1SDavid du Colombier 			flags = dp->r_flags;
6703e12c5d1SDavid du Colombier 			break;
6717dd7cddfSDavid du Colombier 		case 'J':
6727dd7cddfSDavid du Colombier 			strcpy(d->gid, "joliet");
6737dd7cddfSDavid du Colombier 			flags = dp->flags;
6747dd7cddfSDavid du Colombier 			break;
6757dd7cddfSDavid du Colombier 		case '9':
6767dd7cddfSDavid du Colombier 			strcpy(d->gid, "plan9");
6777dd7cddfSDavid du Colombier 			flags = dp->flags;
6787dd7cddfSDavid du Colombier 			break;
6793e12c5d1SDavid du Colombier 		}
6803e12c5d1SDavid du Colombier 		if(flags & 0x02){
6819a747e4fSDavid du Colombier 			d->qid.type |= QTDIR;
6829a747e4fSDavid du Colombier 			d->mode |= DMDIR|0111;
6833e12c5d1SDavid du Colombier 		}
6843e12c5d1SDavid du Colombier 		strcpy(d->uid, "cdrom");
6859a747e4fSDavid du Colombier 		if(fmt!='9' && !(d->mode&DMDIR)){
6869a747e4fSDavid du Colombier 			/*
6879a747e4fSDavid du Colombier 			 * ISO 9660 actually requires that you always have a . and a ;,
6889a747e4fSDavid du Colombier 			 * even if there is no version and no extension.  Very few writers
6899a747e4fSDavid du Colombier 			 * do this.  If the version is present, we use it for qid.vers.
6909a747e4fSDavid du Colombier 			 * If there is no extension but there is a dot, we strip it off.
6919a747e4fSDavid du Colombier 			 * (VMS heads couldn't comprehend the dot as a file name character
6929a747e4fSDavid du Colombier 			 * rather than as just a separator between name and extension.)
6939a747e4fSDavid du Colombier 			 *
6949a747e4fSDavid du Colombier 			 * We don't do this for directory names because directories are
6959a747e4fSDavid du Colombier 			 * not allowed to have extensions and versions.
6969a747e4fSDavid du Colombier 			 */
6979a747e4fSDavid du Colombier 			if((p=strchr(d->name, ';')) != nil){
6989a747e4fSDavid du Colombier 				vers = strtoul(p+1, 0, 0);
6999a747e4fSDavid du Colombier 				d->qid.vers = vers;
7009a747e4fSDavid du Colombier 				*p = '\0';
7019a747e4fSDavid du Colombier 			}
7029a747e4fSDavid du Colombier 			if((p=strchr(d->name, '.')) != nil && *(p+1)=='\0')
7039a747e4fSDavid du Colombier 				*p = '\0';
704bd389b36SDavid du Colombier 		}
70559cc4ca5SDavid du Colombier 		if(fs->issusp){
70659cc4ca5SDavid du Colombier 			nl = 0;
70759cc4ca5SDavid du Colombier 			s += fs->suspoff;
70859cc4ca5SDavid du Colombier 			sysl -= fs->suspoff;
70959cc4ca5SDavid du Colombier 			for(; sysl >= 4 && have != (Hname|Hmode); sysl -= l, s += l){
710*1f3c807eSDavid du Colombier 				if(s[0] == 0 && ((ulong)s & 1)){
711*1f3c807eSDavid du Colombier 					/* MacOS pads individual entries, contrary to spec */
712*1f3c807eSDavid du Colombier 					s++;
713*1f3c807eSDavid du Colombier 					sysl--;
714*1f3c807eSDavid du Colombier 				}
71559cc4ca5SDavid du Colombier 				l = s[2];
71659cc4ca5SDavid du Colombier 				if(s[0] == 'P' && s[1] == 'X' && s[3] == 1){
71759cc4ca5SDavid du Colombier 					/* posix file attributes */
71859cc4ca5SDavid du Colombier 					mode = l32(s+4);
71959cc4ca5SDavid du Colombier 					d->mode = mode & 0777;
72059cc4ca5SDavid du Colombier 					if((mode & 0170000) == 040000){
7219a747e4fSDavid du Colombier 						d->mode |= DMDIR;
7229a747e4fSDavid du Colombier 						d->qid.type |= QTDIR;
72359cc4ca5SDavid du Colombier 					}
72459cc4ca5SDavid du Colombier 					have |= Hmode;
72559cc4ca5SDavid du Colombier 				} else if(s[0] == 'N' && s[1] == 'M' && s[3] == 1){
72659cc4ca5SDavid du Colombier 					/* alternative name */
72759cc4ca5SDavid du Colombier 					if((s[4] & ~1) == 0){
72859cc4ca5SDavid du Colombier 						i = nl+l-5;
7299a747e4fSDavid du Colombier 						if(i >= Maxname)
7309a747e4fSDavid du Colombier 							i = Maxname-1;
73159cc4ca5SDavid du Colombier 						if((i -= nl) > 0){
73259cc4ca5SDavid du Colombier 							memmove(d->name+nl, s+5, i);
73359cc4ca5SDavid du Colombier 							nl += i;
73459cc4ca5SDavid du Colombier 						}
73559cc4ca5SDavid du Colombier 						if(s[4] == 0)
73659cc4ca5SDavid du Colombier 							have |= Hname;
73759cc4ca5SDavid du Colombier 					}
73859cc4ca5SDavid du Colombier 				} else if(s[0] == 'C' && s[1] == 'E' && s[2] >= 28){
73959cc4ca5SDavid du Colombier 					sysl = getcontin(fs->d, s, &s);
74059cc4ca5SDavid du Colombier 					continue;
74159cc4ca5SDavid du Colombier 				} else if(s[0] == 'S' && s[1] == 'T')
74259cc4ca5SDavid du Colombier 					break;
74359cc4ca5SDavid du Colombier 			}
74459cc4ca5SDavid du Colombier 		}
7453e12c5d1SDavid du Colombier 	}
7463e12c5d1SDavid du Colombier 	d->length = 0;
7479a747e4fSDavid du Colombier 	if((d->mode & DMDIR) == 0)
7483e12c5d1SDavid du Colombier 		d->length = l32(dp->size);
7493e12c5d1SDavid du Colombier 	d->type = 0;
7503e12c5d1SDavid du Colombier 	d->dev = 0;
7513e12c5d1SDavid du Colombier 	d->atime = gtime(dp->date);
7523e12c5d1SDavid du Colombier 	d->mtime = d->atime;
753bd389b36SDavid du Colombier 	return vers;
7543e12c5d1SDavid du Colombier }
7553e12c5d1SDavid du Colombier 
75659cc4ca5SDavid du Colombier static int
75759cc4ca5SDavid du Colombier getcontin(Xdata *dev, uchar *p, uchar **s)
75859cc4ca5SDavid du Colombier {
75959cc4ca5SDavid du Colombier 	long bn, off, len;
76059cc4ca5SDavid du Colombier 	Iobuf *b;
76159cc4ca5SDavid du Colombier 
76259cc4ca5SDavid du Colombier 	bn = l32(p+4);
76359cc4ca5SDavid du Colombier 	off = l32(p+12);
76459cc4ca5SDavid du Colombier 	len = l32(p+20);
76559cc4ca5SDavid du Colombier 	chat("getcontin %d...", bn);
76659cc4ca5SDavid du Colombier 	b = getbuf(dev, bn);
76759cc4ca5SDavid du Colombier 	if(b == 0){
76859cc4ca5SDavid du Colombier 		*s = 0;
76959cc4ca5SDavid du Colombier 		return 0;
77059cc4ca5SDavid du Colombier 	}
77159cc4ca5SDavid du Colombier 	*s = b->iobuf+off;
77259cc4ca5SDavid du Colombier 	putbuf(b);
77359cc4ca5SDavid du Colombier 	return len;
77459cc4ca5SDavid du Colombier }
77559cc4ca5SDavid du Colombier 
7763e12c5d1SDavid du Colombier static char *
7773e12c5d1SDavid du Colombier nstr(uchar *p, int n)
7783e12c5d1SDavid du Colombier {
7793e12c5d1SDavid du Colombier 	static char buf[132];
7803e12c5d1SDavid du Colombier 	char *q = buf;
781bd389b36SDavid du Colombier 
7823e12c5d1SDavid du Colombier 	while(--n >= 0){
7833e12c5d1SDavid du Colombier 		if(*p == '\\')
7843e12c5d1SDavid du Colombier 			*q++ = '\\';
7853e12c5d1SDavid du Colombier 		if(' ' <= *p && *p <= '~')
7863e12c5d1SDavid du Colombier 			*q++ = *p++;
7873e12c5d1SDavid du Colombier 		else
7883e12c5d1SDavid du Colombier 			q += sprint(q, "\\%2.2ux", *p++);
7893e12c5d1SDavid du Colombier 	}
7903e12c5d1SDavid du Colombier 	*q = 0;
7913e12c5d1SDavid du Colombier 	return buf;
7923e12c5d1SDavid du Colombier }
7933e12c5d1SDavid du Colombier 
7943e12c5d1SDavid du Colombier static char *
7953e12c5d1SDavid du Colombier rdate(uchar *p, int fmt)
7963e12c5d1SDavid du Colombier {
7973e12c5d1SDavid du Colombier 	static char buf[64];
7983e12c5d1SDavid du Colombier 	int htz, s, n;
7993e12c5d1SDavid du Colombier 
8003e12c5d1SDavid du Colombier 	n = sprint(buf, "%2.2d.%2.2d.%2.2d %2.2d:%2.2d:%2.2d",
8013e12c5d1SDavid du Colombier 		p[0], p[1], p[2], p[3], p[4], p[5]);
8023e12c5d1SDavid du Colombier 	if(fmt == 'z'){
8033e12c5d1SDavid du Colombier 		htz = p[6];
8043e12c5d1SDavid du Colombier 		if(htz >= 128){
8053e12c5d1SDavid du Colombier 			htz = 256-htz;
8063e12c5d1SDavid du Colombier 			s = '-';
8073e12c5d1SDavid du Colombier 		}else
8083e12c5d1SDavid du Colombier 			s = '+';
8093e12c5d1SDavid du Colombier 		sprint(&buf[n], " (%c%.1f)", s, (float)htz/2);
8103e12c5d1SDavid du Colombier 	}
8113e12c5d1SDavid du Colombier 	return buf;
8123e12c5d1SDavid du Colombier }
8133e12c5d1SDavid du Colombier 
814bd389b36SDavid du Colombier static char
815bd389b36SDavid du Colombier dmsize[12] =
816bd389b36SDavid du Colombier {
8173e12c5d1SDavid du Colombier 	31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
8183e12c5d1SDavid du Colombier };
819bd389b36SDavid du Colombier 
8203e12c5d1SDavid du Colombier static int
8213e12c5d1SDavid du Colombier dysize(int y)
8223e12c5d1SDavid du Colombier {
8233e12c5d1SDavid du Colombier 
8243e12c5d1SDavid du Colombier 	if((y%4) == 0)
8253e12c5d1SDavid du Colombier 		return 366;
8263e12c5d1SDavid du Colombier 	return 365;
8273e12c5d1SDavid du Colombier }
828bd389b36SDavid du Colombier 
8293e12c5d1SDavid du Colombier static long
8303e12c5d1SDavid du Colombier gtime(uchar *p)	/* yMdhmsz */
8313e12c5d1SDavid du Colombier {
8323e12c5d1SDavid du Colombier 	long t;
8333e12c5d1SDavid du Colombier 	int i, y, M, d, h, m, s, tz;
8343e12c5d1SDavid du Colombier 	y=p[0]; M=p[1]; d=p[2];
8353e12c5d1SDavid du Colombier 	h=p[3]; m=p[4]; s=p[5]; tz=p[6];
8363e12c5d1SDavid du Colombier 	USED(tz);
8373e12c5d1SDavid du Colombier 	if (y < 70)
8383e12c5d1SDavid du Colombier 		return 0;
8393e12c5d1SDavid du Colombier 	if (M < 1 || M > 12)
8403e12c5d1SDavid du Colombier 		return 0;
8413e12c5d1SDavid du Colombier 	if (d < 1 || d > dmsize[M-1])
8423e12c5d1SDavid du Colombier 		return 0;
8433e12c5d1SDavid du Colombier 	if (h > 23)
8443e12c5d1SDavid du Colombier 		return 0;
8453e12c5d1SDavid du Colombier 	if (m > 59)
8463e12c5d1SDavid du Colombier 		return 0;
8473e12c5d1SDavid du Colombier 	if (s > 59)
8483e12c5d1SDavid du Colombier 		return 0;
8493e12c5d1SDavid du Colombier 	y += 1900;
8503e12c5d1SDavid du Colombier 	t = 0;
8513e12c5d1SDavid du Colombier 	for(i=1970; i<y; i++)
8523e12c5d1SDavid du Colombier 		t += dysize(i);
8533e12c5d1SDavid du Colombier 	if (dysize(y)==366 && M >= 3)
8543e12c5d1SDavid du Colombier 		t++;
8553e12c5d1SDavid du Colombier 	while(--M)
8563e12c5d1SDavid du Colombier 		t += dmsize[M-1];
8573e12c5d1SDavid du Colombier 	t += d-1;
8583e12c5d1SDavid du Colombier 	t = 24*t + h;
8593e12c5d1SDavid du Colombier 	t = 60*t + m;
8603e12c5d1SDavid du Colombier 	t = 60*t + s;
8613e12c5d1SDavid du Colombier 	return t;
8623e12c5d1SDavid du Colombier }
8633e12c5d1SDavid du Colombier 
8643e12c5d1SDavid du Colombier #define	p	((uchar*)arg)
8653e12c5d1SDavid du Colombier 
8663e12c5d1SDavid du Colombier static long
8673e12c5d1SDavid du Colombier l16(void *arg)
8683e12c5d1SDavid du Colombier {
8693e12c5d1SDavid du Colombier 	long v;
8703e12c5d1SDavid du Colombier 
8713e12c5d1SDavid du Colombier 	v = ((long)p[1]<<8)|p[0];
8723e12c5d1SDavid du Colombier 	if (v >= 0x8000L)
8733e12c5d1SDavid du Colombier 		v -= 0x10000L;
8743e12c5d1SDavid du Colombier 	return v;
8753e12c5d1SDavid du Colombier }
8763e12c5d1SDavid du Colombier 
8773e12c5d1SDavid du Colombier static long
8783e12c5d1SDavid du Colombier l32(void *arg)
8793e12c5d1SDavid du Colombier {
8803e12c5d1SDavid du Colombier 	return ((((((long)p[3]<<8)|p[2])<<8)|p[1])<<8)|p[0];
8813e12c5d1SDavid du Colombier }
8823e12c5d1SDavid du Colombier 
8833e12c5d1SDavid du Colombier #undef	p
884