Commit Diff


commit - 29e9b5683ec8d610140da9118e8f004f74bc6c77
commit + 78a779a3834cf39d7c0bcd93a15824b29df947a3
blob - /dev/null
blob + a64cedfddc662f3bb81a9ca91845cde4bec55f4a (mode 644)
--- /dev/null
+++ src/cmd/9660srv/9660srv.c
@@ -0,0 +1,896 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <fcall.h>
+#include "dat.h"
+#include "fns.h"
+#include "iso9660.h"
+
+static void	ireset(void);
+static int	iattach(Xfile*);
+static void	iclone(Xfile*, Xfile*);
+static void	iwalkup(Xfile*);
+static void	iwalk(Xfile*, char*);
+static void	iopen(Xfile*, int);
+static void	icreate(Xfile*, char*, long, int);
+static long	ireaddir(Xfile*, uchar*, long, long);
+static long	iread(Xfile*, char*, vlong, long);
+static long	iwrite(Xfile*, char*, vlong, long);
+static void	iclunk(Xfile*);
+static void	iremove(Xfile*);
+static void	istat(Xfile*, Dir*);
+static void	iwstat(Xfile*, Dir*);
+
+static char*	nstr(uchar*, int);
+static char*	rdate(uchar*, int);
+static int	getcontin(Xdata*, uchar*, uchar**);
+static int	getdrec(Xfile*, void*);
+static void	ungetdrec(Xfile*);
+static int	opendotdot(Xfile*, Xfile*);
+static int	showdrec(int, int, void*);
+static long	gtime(uchar*);
+static long	l16(void*);
+static long	l32(void*);
+static void	newdrec(Xfile*, Drec*);
+static int	rzdir(Xfs*, Dir*, int, Drec*);
+
+Xfsub	isosub =
+{
+	ireset, iattach, iclone, iwalkup, iwalk, iopen, icreate,
+	ireaddir, iread, iwrite, iclunk, iremove, istat, iwstat
+};
+
+static void
+ireset(void)
+{}
+
+static int
+iattach(Xfile *root)
+{
+	Xfs *cd = root->xf;
+	Iobuf *p; Voldesc *v; Isofile *fp; Drec *dp;
+	int fmt, blksize, i, n, l, haveplan9;
+	Iobuf *dirp;
+	uchar dbuf[256];
+	Drec *rd = (Drec *)dbuf;
+	uchar *q, *s;
+
+	dirp = nil;
+	blksize = 0;
+	fmt = 0;
+	dp = nil;
+	haveplan9 = 0;
+	for(i=VOLDESC;i<VOLDESC+100; i++){	/* +100 for sanity */
+		p = getbuf(cd->d, i);
+		v = (Voldesc*)(p->iobuf);
+		if(memcmp(v->byte, "\01CD001\01", 7) == 0){		/* iso */
+			if(dirp)
+				putbuf(dirp);
+			dirp = p;
+			fmt = 'z';
+			dp = (Drec*)v->z.desc.rootdir;
+			blksize = l16(v->z.desc.blksize);
+			chat("iso, blksize=%d...", blksize);
+
+			v = (Voldesc*)(dirp->iobuf);
+			haveplan9 = (strncmp((char*)v->z.boot.sysid, "PLAN 9", 6)==0);
+			if(haveplan9){
+				if(noplan9) {
+					chat("ignoring plan9");
+					haveplan9 = 0;
+				} else {
+					fmt = '9';
+					chat("plan9 iso...");
+				}
+			}
+			continue;
+		}
+
+		if(memcmp(&v->byte[8], "\01CDROM\01", 7) == 0){	/* high sierra */
+			if(dirp)
+				putbuf(dirp);
+			dirp = p;
+			fmt = 'r';
+			dp = (Drec*)v->r.desc.rootdir;
+			blksize = l16(v->r.desc.blksize);
+			chat("high sierra, blksize=%d...", blksize);
+			continue;
+		}
+
+		if(haveplan9==0 && !nojoliet
+		&& memcmp(v->byte, "\02CD001\01", 7) == 0){
+chat("%d %d\n", haveplan9, nojoliet);
+			/*
+			 * The right thing to do is walk the escape sequences looking
+			 * for one of 25 2F 4[035], but Microsoft seems to not honor
+			 * the format, which makes it hard to walk over.
+			 */
+			q = v->z.desc.escapes;
+			if(q[0] == 0x25 && q[1] == 0x2F && (q[2] == 0x40 || q[2] == 0x43 || q[2] == 0x45)){	/* Joliet, it appears */
+				if(dirp)
+					putbuf(dirp);
+				dirp = p;
+				fmt = 'J';
+				dp = (Drec*)v->z.desc.rootdir;
+				if(blksize != l16(v->z.desc.blksize))
+					fprint(2, "warning: suspicious Joliet blocksize\n");
+				chat("joliet...");
+				continue;
+			}
+		}
+		putbuf(p);
+		if(v->byte[0] == 0xFF)
+			break;
+	}
+
+	if(fmt == 0){
+		if(dirp)
+			putbuf(dirp);
+		return -1;
+	}
+	assert(dirp != nil);
+
+	if(chatty)
+		showdrec(2, fmt, dp);
+	if(blksize > Sectorsize){
+		chat("blksize too big...");
+		putbuf(dirp);
+		return -1;
+	}
+	if(waserror()){
+		putbuf(dirp);
+		nexterror();
+	}
+	root->len = sizeof(Isofile) - sizeof(Drec) + dp->reclen;
+	root->ptr = fp = ealloc(root->len);
+
+	if(haveplan9)
+		root->xf->isplan9 = 1;
+
+	fp->fmt = fmt;
+	fp->blksize = blksize;
+	fp->offset = 0;
+	fp->doffset = 0;
+	memmove(&fp->d, dp, dp->reclen);
+	root->qid.path = l32(dp->addr);
+	root->qid.type = QTDIR;
+	putbuf(dirp);
+	poperror();
+	if(getdrec(root, rd) >= 0){
+		n = rd->reclen-(34+rd->namelen);
+		s = (uchar*)rd->name + rd->namelen;
+		if((uintptr)s & 1){
+			s++;
+			n--;
+		}
+		if(n >= 7 && s[0] == 'S' && s[1] == 'P' && s[2] == 7 &&
+		   s[3] == 1 && s[4] == 0xBE && s[5] == 0xEF){
+			root->xf->issusp = 1;
+			root->xf->suspoff = s[6];
+			n -= root->xf->suspoff;
+			s += root->xf->suspoff;
+			for(; n >= 4; s += l, n -= l){
+				l = s[2];
+				if(s[0] == 'E' && s[1] == 'R'){
+					if(!norock && s[4] == 10 && memcmp(s+8, "RRIP_1991A", 10) == 0)
+						root->xf->isrock = 1;
+					break;
+				} else if(s[0] == 'C' && s[1] == 'E' && s[2] >= 28){
+					n = getcontin(root->xf->d, s, &s);
+					continue;
+				} else if(s[0] == 'R' && s[1] == 'R'){
+					if(!norock)
+						root->xf->isrock = 1;
+					break;
+				} else if(s[0] == 'S' && s[1] == 'T')
+					break;
+			}
+		}
+	}
+	if(root->xf->isrock)
+		chat("Rock Ridge...");
+	fp->offset = 0;
+	fp->doffset = 0;
+	return 0;
+}
+
+static void
+iclone(Xfile *of, Xfile *nf)
+{
+	USED(of);
+	USED(nf);
+}
+
+static void
+iwalkup(Xfile *f)
+{
+	long paddr;
+	uchar dbuf[256];
+	Drec *d = (Drec *)dbuf;
+	Xfile pf, ppf;
+	Isofile piso, ppiso;
+
+	memset(&pf, 0, sizeof pf);
+	memset(&ppf, 0, sizeof ppf);
+	pf.ptr = &piso;
+	ppf.ptr = &ppiso;
+	if(opendotdot(f, &pf) < 0)
+		error("can't open pf");
+	paddr = l32(pf.ptr->d.addr);
+	if(l32(f->ptr->d.addr) == paddr)
+		return;
+	if(opendotdot(&pf, &ppf) < 0)
+		error("can't open ppf");
+	while(getdrec(&ppf, d) >= 0){
+		if(l32(d->addr) == paddr){
+			newdrec(f, d);
+			f->qid.path = paddr;
+			f->qid.type = QTDIR;
+			return;
+		}
+	}
+	error("can't find addr of ..");
+}
+
+static int
+casestrcmp(int isplan9, char *a, char *b)
+{
+	int ca, cb;
+
+	if(isplan9)
+		return strcmp(a, b);
+	for(;;) {
+		ca = *a++;
+		cb = *b++;
+		if(ca >= 'A' && ca <= 'Z')
+			ca += 'a' - 'A';
+		if(cb >= 'A' && cb <= 'Z')
+			cb += 'a' - 'A';
+		if(ca != cb) {
+			if(ca > cb)
+				return 1;
+			return -1;
+		}
+		if(ca == 0)
+			return 0;
+	}
+}
+
+static void
+iwalk(Xfile *f, char *name)
+{
+	Isofile *ip = f->ptr;
+	uchar dbuf[256];
+	char nbuf[4*Maxname];
+	Drec *d = (Drec*)dbuf;
+	Dir dir;
+	char *p;
+	int len, vers, dvers;
+
+	vers = -1;
+	if(p = strchr(name, ';')) {	/* assign = */
+		len = p-name;
+		if(len >= Maxname)
+			len = Maxname-1;
+		memmove(nbuf, name, len);
+		vers = strtoul(p+1, 0, 10);
+		name = nbuf;
+	}
+/*
+	len = strlen(name);
+	if(len >= Maxname){
+		len = Maxname-1;
+		if(name != nbuf){
+			memmove(nbuf, name, len);
+			name = nbuf;
+		}
+		name[len] = 0;
+	}
+*/
+
+	chat("%d \"%s\"...", strlen(name), name);
+	ip->offset = 0;
+	setnames(&dir, nbuf);
+	while(getdrec(f, d) >= 0) {
+		dvers = rzdir(f->xf, &dir, ip->fmt, d);
+		if(casestrcmp(f->xf->isplan9||f->xf->isrock, name, dir.name) != 0)
+			continue;
+		newdrec(f, d);
+		f->qid.path = dir.qid.path;
+		f->qid.type = dir.qid.type;
+		USED(dvers);
+		return;
+	}
+	USED(vers);
+	error(Enonexist);
+}
+
+static void
+iopen(Xfile *f, int mode)
+{
+	mode &= ~OCEXEC;
+	if(mode != OREAD && mode != OEXEC)
+		error(Eperm);
+	f->ptr->offset = 0;
+	f->ptr->doffset = 0;
+}
+
+static void
+icreate(Xfile *f, char *name, long perm, int mode)
+{
+	USED(f);
+	USED(name);
+	USED(perm);
+	USED(mode);
+	error(Eperm);
+}
+
+static long
+ireaddir(Xfile *f, uchar *buf, long offset, long count)
+{
+	Isofile *ip = f->ptr;
+	Dir d;
+	char names[4*Maxname];
+	uchar dbuf[256];
+	Drec *drec = (Drec *)dbuf;
+	int n, rcnt;
+
+	if(offset==0){
+		ip->offset = 0;
+		ip->doffset = 0;
+	}else if(offset != ip->doffset)
+		error("seek in directory not allowed");
+
+	rcnt = 0;
+	setnames(&d, names);
+	while(rcnt < count && getdrec(f, drec) >= 0){
+		if(drec->namelen == 1){
+			if(drec->name[0] == 0)
+				continue;
+			if(drec->name[0] == 1)
+				continue;
+		}
+		rzdir(f->xf, &d, ip->fmt, drec);
+		d.qid.vers = f->qid.vers;
+		if((n = convD2M(&d, buf+rcnt, count-rcnt)) <= BIT16SZ){
+			ungetdrec(f);
+			break;
+		}
+		rcnt += n;
+	}
+	ip->doffset += rcnt;
+	return rcnt;
+}
+
+static long
+iread(Xfile *f, char *buf, vlong offset, long count)
+{
+	int n, o, rcnt = 0;
+	long size;
+	vlong addr;
+	Isofile *ip = f->ptr;
+	Iobuf *p;
+
+	size = l32(ip->d.size);
+	if(offset >= size)
+		return 0;
+	if(offset+count > size)
+		count = size - offset;
+	addr = ((vlong)l32(ip->d.addr) + ip->d.attrlen)*ip->blksize + offset;
+	o = addr % Sectorsize;
+	addr /= Sectorsize;
+	/*chat("d.addr=%ld, addr=%lld, o=%d...", l32(ip->d.addr), addr, o);*/
+	n = Sectorsize - o;
+
+	while(count > 0){
+		if(n > count)
+			n = count;
+		p = getbuf(f->xf->d, addr);
+		memmove(&buf[rcnt], &p->iobuf[o], n);
+		putbuf(p);
+		count -= n;
+		rcnt += n;
+		++addr;
+		o = 0;
+		n = Sectorsize;
+	}
+	return rcnt;
+}
+
+static long
+iwrite(Xfile *f, char *buf, vlong offset, long count)
+{
+	USED(f);
+	USED(buf);
+	USED(offset);
+	USED(count);
+	error(Eperm);
+	return 0;
+}
+
+static void
+iclunk(Xfile *f)
+{
+	USED(f);
+}
+
+static void
+iremove(Xfile *f)
+{
+	USED(f);
+	error(Eperm);
+}
+
+static void
+istat(Xfile *f, Dir *d)
+{
+	Isofile *ip = f->ptr;
+
+	rzdir(f->xf, d, ip->fmt, &ip->d);
+	d->qid.vers = f->qid.vers;
+	if(d->qid.path==f->xf->rootqid.path){
+		d->qid.path = 0;
+		d->qid.type = QTDIR;
+	}
+}
+
+static void
+iwstat(Xfile *f, Dir *d)
+{
+	USED(f);
+	USED(d);
+	error(Eperm);
+}
+
+static int
+showdrec(int fd, int fmt, void *x)
+{
+	Drec *d = (Drec *)x;
+	int namelen;
+	int syslen;
+
+	if(d->reclen == 0)
+		return 0;
+	fprint(fd, "%d %d %ld %ld ",
+		d->reclen, d->attrlen, l32(d->addr), l32(d->size));
+	fprint(fd, "%s 0x%2.2x %d %d %ld ",
+		rdate(d->date, fmt), (fmt=='z' ? d->flags : d->r_flags),
+		d->unitsize, d->gapsize, l16(d->vseqno));
+	fprint(fd, "%d %s", d->namelen, nstr(d->name, d->namelen));
+	if(fmt != 'J'){
+		namelen = d->namelen + (1-(d->namelen&1));
+		syslen = d->reclen - 33 - namelen;
+		if(syslen != 0)
+			fprint(fd, " %s", nstr(&d->name[namelen], syslen));
+	}
+	fprint(fd, "\n");
+	return d->reclen + (d->reclen&1);
+}
+
+static void
+newdrec(Xfile *f, Drec *dp)
+{
+	Isofile *x = f->ptr;
+	Isofile *n;
+	int len;
+
+	len = sizeof(Isofile) - sizeof(Drec) + dp->reclen;
+	n = ealloc(len);
+	n->fmt = x->fmt;
+	n->blksize = x->blksize;
+	n->offset = 0;
+	n->doffset = 0;
+	memmove(&n->d, dp, dp->reclen);
+	free(x);
+	f->ptr = n;
+	f->len = len;
+}
+
+static void
+ungetdrec(Xfile *f)
+{
+	Isofile *ip = f->ptr;
+
+	if(ip->offset >= ip->odelta){
+		ip->offset -= ip->odelta;
+		ip->odelta = 0;
+	}
+}
+
+static int
+getdrec(Xfile *f, void *buf)
+{
+	Isofile *ip = f->ptr;
+	int len = 0, boff = 0;
+	ulong size;
+	vlong addr;
+	Iobuf *p = 0;
+
+	if(!ip)
+		return -1;
+	size = l32(ip->d.size);
+	while(ip->offset < size){
+		addr = (l32(ip->d.addr)+ip->d.attrlen)*ip->blksize + ip->offset;
+		boff = addr % Sectorsize;
+		if(boff > Sectorsize-34){
+			ip->offset += Sectorsize-boff;
+			continue;
+		}
+		p = getbuf(f->xf->d, addr/Sectorsize);
+		len = p->iobuf[boff];
+		if(len >= 34)
+			break;
+		putbuf(p);
+		p = 0;
+		ip->offset += Sectorsize-boff;
+	}
+	if(p) {
+		memmove(buf, &p->iobuf[boff], len);
+		putbuf(p);
+		ip->odelta = len + (len&1);
+		ip->offset += ip->odelta;
+		return 0;
+	}
+	return -1;
+}
+
+static int
+opendotdot(Xfile *f, Xfile *pf)
+{
+	uchar dbuf[256];
+	Drec *d = (Drec *)dbuf;
+	Isofile *ip = f->ptr, *pip = pf->ptr;
+
+	ip->offset = 0;
+	if(getdrec(f, d) < 0){
+		chat("opendotdot: getdrec(.) failed...");
+		return -1;
+	}
+	if(d->namelen != 1 || d->name[0] != 0){
+		chat("opendotdot: no . entry...");
+		return -1;
+	}
+	if(l32(d->addr) != l32(ip->d.addr)){
+		chat("opendotdot: bad . address...");
+		return -1;
+	}
+	if(getdrec(f, d) < 0){
+		chat("opendotdot: getdrec(..) failed...");
+		return -1;
+	}
+	if(d->namelen != 1 || d->name[0] != 1){
+		chat("opendotdot: no .. entry...");
+		return -1;
+	}
+
+	pf->xf = f->xf;
+	pip->fmt = ip->fmt;
+	pip->blksize = ip->blksize;
+	pip->offset = 0;
+	pip->doffset = 0;
+	pip->d = *d;
+	return 0;
+}
+
+enum {
+	Hname = 1,
+	Hmode = 2,
+};
+
+static int
+rzdir(Xfs *fs, Dir *d, int fmt, Drec *dp)
+{
+	int n, flags, i, j, lj, nl, vers, sysl, mode, l, have;
+	uchar *s;
+	char *p;
+	char buf[Maxname+UTFmax+1];
+	uchar *q;
+	Rune r;
+	enum { ONAMELEN = 28 };	/* old Plan 9 directory name length */
+
+	have = 0;
+	flags = 0;
+	vers = -1;
+	d->qid.path = l32(dp->addr);
+	d->qid.type = 0;
+	d->qid.vers = 0;
+	n = dp->namelen;
+	memset(d->name, 0, Maxname);
+	if(n == 1) {
+		switch(dp->name[0]){
+		case 1:
+			d->name[1] = '.';
+			/* fall through */
+		case 0:
+			d->name[0] = '.';
+			have = Hname;
+			break;
+		default:
+			d->name[0] = tolower(dp->name[0]);
+		}
+	} else {
+		if(fmt == 'J'){	/* Joliet, 16-bit Unicode */
+			q = (uchar*)dp->name;
+			for(i=j=lj=0; i<n && j<Maxname; i+=2){
+				lj = j;
+				r = (q[i]<<8)|q[i+1];
+				j += runetochar(buf+j, &r);
+			}
+			if(j >= Maxname)
+				j = lj;
+			memmove(d->name, buf, j);
+		}else{
+			if(n >= Maxname)
+				n = Maxname-1;
+			for(i=0; i<n; i++)
+				d->name[i] = tolower(dp->name[i]);
+		}
+	}
+
+	sysl = dp->reclen-(34+dp->namelen);
+	s = (uchar*)dp->name + dp->namelen;
+	if(((uintptr)s) & 1) {
+		s++;
+		sysl--;
+	}
+	if(fs->isplan9 && sysl > 0) {
+		/*
+		 * get gid, uid, mode and possibly name
+		 * from plan9 directory extension
+		 */
+		nl = *s;
+		if(nl >= ONAMELEN)
+			nl = ONAMELEN-1;
+		if(nl) {
+			memset(d->name, 0, ONAMELEN);
+			memmove(d->name, s+1, nl);
+		}
+		s += 1 + *s;
+		nl = *s;
+		if(nl >= ONAMELEN)
+			nl = ONAMELEN-1;
+		memset(d->uid, 0, ONAMELEN);
+		memmove(d->uid, s+1, nl);
+		s += 1 + *s;
+		nl = *s;
+		if(nl >= ONAMELEN)
+			nl = ONAMELEN-1;
+		memset(d->gid, 0, ONAMELEN);
+		memmove(d->gid, s+1, nl);
+		s += 1 + *s;
+		if(((uintptr)s) & 1)
+			s++;
+		d->mode = l32(s);
+		if(d->mode & DMDIR)
+			d->qid.type |= QTDIR;
+	} else {
+		d->mode = 0444;
+		switch(fmt) {
+		case 'z':
+			if(fs->isrock)
+				strcpy(d->gid, "ridge");
+			else
+				strcpy(d->gid, "iso9660");
+			flags = dp->flags;
+			break;
+		case 'r':
+			strcpy(d->gid, "sierra");
+			flags = dp->r_flags;
+			break;
+		case 'J':
+			strcpy(d->gid, "joliet");
+			flags = dp->flags;
+			break;
+		case '9':
+			strcpy(d->gid, "plan9");
+			flags = dp->flags;
+			break;
+		}
+		if(flags & 0x02){
+			d->qid.type |= QTDIR;
+			d->mode |= DMDIR|0111;
+		}
+		strcpy(d->uid, "cdrom");
+		if(fmt!='9' && !(d->mode&DMDIR)){
+			/*
+			 * ISO 9660 actually requires that you always have a . and a ;,
+			 * even if there is no version and no extension.  Very few writers
+			 * do this.  If the version is present, we use it for qid.vers.
+			 * If there is no extension but there is a dot, we strip it off.
+			 * (VMS heads couldn't comprehend the dot as a file name character
+			 * rather than as just a separator between name and extension.)
+			 *
+			 * We don't do this for directory names because directories are
+			 * not allowed to have extensions and versions.
+			 */
+			if((p=strchr(d->name, ';')) != nil){
+				vers = strtoul(p+1, 0, 0);
+				d->qid.vers = vers;
+				*p = '\0';
+			}
+			if((p=strchr(d->name, '.')) != nil && *(p+1)=='\0')
+				*p = '\0';
+		}
+		if(fs->issusp){
+			nl = 0;
+			s += fs->suspoff;
+			sysl -= fs->suspoff;
+			for(; sysl >= 4 && have != (Hname|Hmode); sysl -= l, s += l){
+				if(s[0] == 0 && ((uintptr)s & 1)){
+					/* MacOS pads individual entries, contrary to spec */
+					s++;
+					sysl--;
+				}
+				l = s[2];
+				if(s[0] == 'P' && s[1] == 'X' && s[3] == 1){
+					/* posix file attributes */
+					mode = l32(s+4);
+					d->mode = mode & 0777;
+					if((mode & 0170000) == 040000){
+						d->mode |= DMDIR;
+						d->qid.type |= QTDIR;
+					}
+					have |= Hmode;
+				} else if(s[0] == 'N' && s[1] == 'M' && s[3] == 1){
+					/* alternative name */
+					if((s[4] & ~1) == 0){
+						i = nl+l-5;
+						if(i >= Maxname)
+							i = Maxname-1;
+						if((i -= nl) > 0){
+							memmove(d->name+nl, s+5, i);
+							nl += i;
+						}
+						if(s[4] == 0)
+							have |= Hname;
+					}
+				} else if(s[0] == 'C' && s[1] == 'E' && s[2] >= 28){
+					sysl = getcontin(fs->d, s, &s);
+					continue;
+				} else if(s[0] == 'S' && s[1] == 'T')
+					break;
+			}
+		}
+	}
+	d->length = 0;
+	if((d->mode & DMDIR) == 0)
+		d->length = l32(dp->size);
+	d->type = 0;
+	d->dev = 0;
+	d->atime = gtime(dp->date);
+	d->mtime = d->atime;
+	return vers;
+}
+
+static int
+getcontin(Xdata *dev, uchar *p, uchar **s)
+{
+	long bn, off, len;
+	Iobuf *b;
+
+	bn = l32(p+4);
+	off = l32(p+12);
+	len = l32(p+20);
+	chat("getcontin %d...", bn);
+	b = getbuf(dev, bn);
+	if(b == 0){
+		*s = 0;
+		return 0;
+	}
+	*s = b->iobuf+off;
+	putbuf(b);
+	return len;
+}
+
+static char *
+nstr(uchar *p, int n)
+{
+	static char buf[132];
+	char *q = buf;
+
+	while(--n >= 0){
+		if(*p == '\\')
+			*q++ = '\\';
+		if(' ' <= *p && *p <= '~')
+			*q++ = *p++;
+		else
+			q += sprint(q, "\\%2.2ux", *p++);
+	}
+	*q = 0;
+	return buf;
+}
+
+static char *
+rdate(uchar *p, int fmt)
+{
+	static char buf[64];
+	int htz, s, n;
+
+	n = sprint(buf, "%2.2d.%2.2d.%2.2d %2.2d:%2.2d:%2.2d",
+		p[0], p[1], p[2], p[3], p[4], p[5]);
+	if(fmt == 'z'){
+		htz = p[6];
+		if(htz >= 128){
+			htz = 256-htz;
+			s = '-';
+		}else
+			s = '+';
+		sprint(&buf[n], " (%c%.1f)", s, (float)htz/2);
+	}
+	return buf;
+}
+
+static char
+dmsize[12] =
+{
+	31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
+};
+
+#define dysize mydysize
+
+static int
+dysize(int y)
+{
+
+	if((y%4) == 0)
+		return 366;
+	return 365;
+}
+
+static long
+gtime(uchar *p)	/* yMdhmsz */
+{
+	long t;
+	int i, y, M, d, h, m, s, tz;
+
+	y=p[0]; M=p[1]; d=p[2];
+	h=p[3]; m=p[4]; s=p[5]; tz=p[6];
+	USED(tz);
+	y += 1900;
+	if (y < 1970)
+		return 0;
+	if (M < 1 || M > 12)
+		return 0;
+	if (d < 1 || d > dmsize[M-1])
+		if (!(M == 2 && d == 29 && dysize(y) == 366))
+			return 0;
+	if (h > 23)
+		return 0;
+	if (m > 59)
+		return 0;
+	if (s > 59)
+		return 0;
+	t = 0;
+	for(i=1970; i<y; i++)
+		t += dysize(i);
+	if (dysize(y)==366 && M >= 3)
+		t++;
+	while(--M)
+		t += dmsize[M-1];
+	t += d-1;
+	t = 24*t + h;
+	t = 60*t + m;
+	t = 60*t + s;
+	return t;
+}
+
+#define	p	((uchar*)arg)
+
+static long
+l16(void *arg)
+{
+	long v;
+
+	v = ((long)p[1]<<8)|p[0];
+	if (v >= 0x8000L)
+		v -= 0x10000L;
+	return v;
+}
+
+static long
+l32(void *arg)
+{
+	return ((((((long)p[3]<<8)|p[2])<<8)|p[1])<<8)|p[0];
+}
+
+#undef	p
blob - /dev/null
blob + bee14db6114889970a4ada7e100eca690c28b9e2 (mode 644)
--- /dev/null
+++ src/cmd/9660srv/dat.h
@@ -0,0 +1,118 @@
+typedef	struct Ioclust	Ioclust;
+typedef	struct Iobuf	Iobuf;
+typedef	struct Isofile	Isofile;
+typedef struct Xdata	Xdata;
+typedef struct Xfile	Xfile;
+typedef struct Xfs	Xfs;
+typedef struct Xfsub	Xfsub;
+
+#pragma incomplete Isofile
+
+enum
+{
+	Sectorsize = 2048,
+	Maxname = 256,
+};
+
+struct Iobuf
+{
+	Ioclust* clust;
+	long	addr;
+	uchar*	iobuf;
+};
+
+struct Ioclust
+{
+	long	addr;			/* in sectors; good to 8TB */
+	Xdata*	dev;
+	Ioclust* next;
+	Ioclust* prev;
+	int	busy;
+	int	nbuf;
+	Iobuf*	buf;
+	uchar*	iobuf;
+};
+
+struct Xdata
+{
+	Xdata*	next;
+	char*	name;		/* of underlying file */
+	Qid	qid;
+	short	type;
+	short	fdev;
+	int	ref;		/* attach count */
+	int	dev;		/* for read/write */
+};
+
+struct Xfsub
+{
+	void	(*reset)(void);
+	int	(*attach)(Xfile*);
+	void	(*clone)(Xfile*, Xfile*);
+	void	(*walkup)(Xfile*);
+	void	(*walk)(Xfile*, char*);
+	void	(*open)(Xfile*, int);
+	void	(*create)(Xfile*, char*, long, int);
+	long	(*readdir)(Xfile*, uchar*, long, long);
+	long	(*read)(Xfile*, char*, vlong, long);
+	long	(*write)(Xfile*, char*, vlong, long);
+	void	(*clunk)(Xfile*);
+	void	(*remove)(Xfile*);
+	void	(*stat)(Xfile*, Dir*);
+	void	(*wstat)(Xfile*, Dir*);
+};
+
+struct Xfs
+{
+	Xdata*	d;		/* how to get the bits */
+	Xfsub*	s;		/* how to use them */
+	int	ref;
+	int	issusp;	/* follows system use sharing protocol */
+	long	suspoff;	/* if so, offset at which SUSP area begins */
+	int	isrock;	/* Rock Ridge format */
+	int	isplan9;	/* has Plan 9-specific directory info */
+	Qid	rootqid;
+	Isofile*	ptr;		/* private data */
+};
+
+struct Xfile
+{
+	Xfile*	next;		/* in fid hash bucket */
+	Xfs*	xf;
+	long	fid;
+	ulong	flags;
+	Qid	qid;
+	int	len;		/* of private data */
+	Isofile*	ptr;
+};
+
+enum
+{
+	Asis,
+	Clean,
+	Clunk
+};
+
+enum
+{
+	Oread = 1,
+	Owrite = 2,
+	Orclose = 4,
+	Omodes = 3,
+};
+
+extern char	Enonexist[];	/* file does not exist */
+extern char	Eperm[];	/* permission denied */
+extern char	Enofile[];	/* no file system specified */
+extern char	Eauth[];	/* authentication failed */
+
+extern char	*srvname;
+extern char	*deffile;
+extern int	chatty;
+extern jmp_buf	err_lab[];
+extern int	nerr_lab;
+extern char	err_msg[];
+
+extern int nojoliet;
+extern int noplan9;
+extern int norock;
blob - /dev/null
blob + e89f8d1c19074a605bdd044b386232457721ca3d (mode 644)
--- /dev/null
+++ src/cmd/9660srv/data.c
@@ -0,0 +1,22 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <fcall.h>
+#include "dat.h"
+#include "fns.h"
+
+char	Enonexist[] =	"file does not exist";
+char	Eperm[] =	"permission denied";
+char	Enofile[] =	"no file system specified";
+char	Eauth[] =	"authentication failed";
+
+char	*srvname = "9660";
+char	*deffile = 0;
+
+extern Xfsub	isosub;
+
+Xfsub*	xsublist[] =
+{
+	&isosub,
+	0
+};
blob - /dev/null
blob + 1bae3b507186c6a8fe61d58b28f265cf7b4483b6 (mode 644)
--- /dev/null
+++ src/cmd/9660srv/fns.h
@@ -0,0 +1,17 @@
+void	chat(char*, ...);
+void*	ealloc(long);
+void	error(char*);
+Iobuf*	getbuf(Xdata*, ulong);
+Xdata*	getxdata(char*);
+void	iobuf_init(void);
+void	nexterror(void);
+void	panic(int, char*, ...);
+void	purgebuf(Xdata*);
+void	putbuf(Iobuf*);
+void	refxfs(Xfs*, int);
+void	showdir(int, Dir*);
+Xfile*	xfile(int, int);
+void setnames(Dir*, char*);
+
+#define	waserror()	(++nerr_lab, setjmp(err_lab[nerr_lab-1]))
+#define	poperror()	(--nerr_lab)
blob - /dev/null
blob + 30eafb3a41b66cf4550319e264d13d1db6abaac2 (mode 644)
--- /dev/null
+++ src/cmd/9660srv/iobuf.c
@@ -0,0 +1,177 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <fcall.h>
+#include "dat.h"
+#include "fns.h"
+
+/*
+ * We used to use 100 i/o buffers of size 2kb (Sectorsize).
+ * Unfortunately, reading 2kb at a time often hopping around
+ * the disk doesn't let us get near the disk bandwidth.
+ *
+ * Based on a trace of iobuf address accesses taken while
+ * tarring up a Plan 9 distribution CD, we now use 16 128kb
+ * buffers.  This works for ISO9660 because data is required
+ * to be laid out contiguously; effectively we're doing agressive
+ * readahead.  Because the buffers are so big and the typical 
+ * disk accesses so concentrated, it's okay that we have so few
+ * of them.
+ *
+ * If this is used to access multiple discs at once, it's not clear
+ * how gracefully the scheme degrades, but I'm not convinced
+ * it's worth worrying about.		-rsc
+ */
+
+/* trying a larger value to get greater throughput - geoff */
+#define	BUFPERCLUST	256 /* sectors/cluster; was 64, 64*Sectorsize = 128kb */
+#define	NCLUST		16
+
+int nclust = NCLUST;
+
+static Ioclust*	iohead;
+static Ioclust*	iotail;
+
+static Ioclust*	getclust(Xdata*, long);
+static void	putclust(Ioclust*);
+static void	xread(Ioclust*);
+
+void
+iobuf_init(void)
+{
+	int i, j, n;
+	Ioclust *c;
+	Iobuf *b;
+	uchar *mem;
+
+	n = nclust*sizeof(Ioclust) +
+		nclust*BUFPERCLUST*(sizeof(Iobuf)+Sectorsize);
+	mem = sbrk(n);
+	if(mem == (void*)-1)
+		panic(0, "iobuf_init");
+	memset(mem, 0, n);
+
+	for(i=0; i<nclust; i++){
+		c = (Ioclust*)mem;
+		mem += sizeof(Ioclust);
+		c->addr = -1;
+		c->prev = iotail;
+		if(iotail)
+			iotail->next = c;
+		iotail = c;
+		if(iohead == nil)
+			iohead = c;
+
+		c->buf = (Iobuf*)mem;
+		mem += BUFPERCLUST*sizeof(Iobuf);
+		c->iobuf = mem;
+		mem += BUFPERCLUST*Sectorsize;
+		for(j=0; j<BUFPERCLUST; j++){
+			b = &c->buf[j];
+			b->clust = c;
+			b->addr = -1;
+			b->iobuf = c->iobuf+j*Sectorsize;
+		}
+	}
+}
+
+void
+purgebuf(Xdata *dev)
+{
+	Ioclust *p;
+
+	for(p=iohead; p!=nil; p=p->next)
+		if(p->dev == dev){
+			p->addr = -1;
+			p->busy = 0;
+		}
+}
+
+static Ioclust*
+getclust(Xdata *dev, long addr)
+{
+	Ioclust *c, *f;
+
+	f = nil;
+	for(c=iohead; c; c=c->next){
+		if(!c->busy)
+			f = c;
+		if(c->addr == addr && c->dev == dev){
+			c->busy++;
+			return c;
+		}
+	}
+
+	if(f == nil)
+		panic(0, "out of buffers");
+
+	f->addr = addr;
+	f->dev = dev;
+	f->busy++;
+	if(waserror()){
+		f->addr = -1;	/* stop caching */
+		putclust(f);
+		nexterror();
+	}
+	xread(f);
+	poperror();
+	return f;
+}
+
+static void
+putclust(Ioclust *c)
+{
+	if(c->busy <= 0)
+		panic(0, "putbuf");
+	c->busy--;
+
+	/* Link onto head for LRU */
+	if(c == iohead)
+		return;
+	c->prev->next = c->next;
+
+	if(c->next)
+		c->next->prev = c->prev;
+	else
+		iotail = c->prev;
+
+	c->prev = nil;
+	c->next = iohead;
+	iohead->prev = c;
+	iohead = c;
+}
+
+Iobuf*
+getbuf(Xdata *dev, ulong addr)
+{
+	int off;
+	Ioclust *c;
+
+	off = addr%BUFPERCLUST;
+	c = getclust(dev, addr - off);
+	if(c->nbuf < off){
+		c->busy--;
+		error("I/O read error");
+	}
+	return &c->buf[off];
+}
+
+void
+putbuf(Iobuf *b)
+{
+	putclust(b->clust);
+}
+
+static void
+xread(Ioclust *c)
+{
+	int n;
+	Xdata *dev;
+
+	dev = c->dev;
+	seek(dev->dev, (vlong)c->addr * Sectorsize, 0);
+	n = readn(dev->dev, c->iobuf, BUFPERCLUST*Sectorsize);
+	if(n < Sectorsize)
+		error("I/O read error");
+	c->nbuf = n/Sectorsize;
+}
blob - /dev/null
blob + 58bcb9ff0cdeaa3b325c676b885ca210beb58cb6 (mode 644)
--- /dev/null
+++ src/cmd/9660srv/iso9660.h
@@ -0,0 +1,142 @@
+#define	VOLDESC	16	/* sector number */
+
+/*
+ * L means little-endian, M means big-endian, and LM means little-endian
+ * then again big-endian.
+ */
+typedef uchar		Byte2L[2];
+typedef uchar		Byte2M[2];
+typedef uchar		Byte4LM[4];
+typedef uchar		Byte4L[4];
+typedef uchar		Byte4M[4];
+typedef uchar		Byte8LM[8];
+typedef union Drec	Drec;
+typedef union Voldesc	Voldesc;
+
+enum
+{
+	Boot		= 0,
+	Primary		= 1,
+	Supplementary	= 2,
+	Partition	= 3,
+	Terminator	= 255
+};
+
+union	Voldesc
+{			/* volume descriptor */
+	uchar	byte[Sectorsize];
+	union {			/* for CD001, the ECMA standard */
+		struct
+		{
+			uchar	type;
+			uchar	stdid[5];
+			uchar	version;
+			uchar	unused;
+			uchar	sysid[32];
+			uchar	bootid[32];
+			uchar	data[1977];
+		} boot;
+		struct
+		{
+			uchar	type;
+			uchar	stdid[5];
+			uchar	version;
+			uchar	flags;
+			uchar	sysid[32];
+			uchar	volid[32];
+			Byte8LM	partloc;
+			Byte8LM	size;
+			uchar	escapes[32];
+			Byte4LM	vsetsize;
+			Byte4LM	vseqno;
+			Byte4LM	blksize;
+			Byte8LM	ptabsize;
+			Byte4L	lptable;
+			Byte4L	optlptable;
+			Byte4M	mptable;
+			Byte4M	optmptable;
+			uchar	rootdir[34];
+			uchar	volsetid[128];
+			uchar	pubid[128];
+			uchar	prepid[128];
+			uchar	appid[128];
+			uchar	copyright[37];
+			uchar	abstract[37];
+			uchar	bibliography[37];
+			uchar	cdate[17];
+			uchar	mdate[17];
+			uchar	expdate[17];
+			uchar	effdate[17];
+			uchar	fsversion;
+			uchar	unused3[1];
+			uchar	appuse[512];
+			uchar	unused4[653];
+		} desc;
+	} z;
+	union
+	{			/* for CDROM, the `High Sierra' standard */
+		struct
+		{
+			Byte8LM	number;
+			uchar	type;
+			uchar	stdid[5];
+			uchar	version;
+			uchar	flags;
+			uchar	sysid[32];
+			uchar	volid[32];
+			Byte8LM	partloc;
+			Byte8LM	size;
+			uchar	escapes[32];
+			Byte4LM	vsetsize;
+			Byte4LM	vseqno;
+			Byte4LM	blksize;
+			uchar	quux[40];
+			uchar	rootdir[34];
+			uchar	volsetid[128];
+			uchar	pubid[128];
+			uchar	prepid[128];
+			uchar	appid[128];
+			uchar	copyright[32];
+			uchar	abstract[32];
+			uchar	cdate[16];
+			uchar	mdate[16];
+			uchar	expdate[16];
+			uchar	effdate[16];
+			uchar	fsversion;
+		} desc;
+	} r;
+};
+
+union	Drec
+{
+	struct
+	{
+		uchar	reclen;
+		uchar	attrlen;
+		Byte8LM	addr;
+		Byte8LM	size;
+		uchar	date[6];
+		uchar	tzone;		/* flags in high sierra */
+		uchar	flags;		/* ? in high sierra */
+		uchar	unitsize;	/* ? in high sierra */
+		uchar	gapsize;	/* ? in high sierra */
+		Byte4LM	vseqno;		/* ? in high sierra */
+		uchar	namelen;
+		uchar	name[1];
+	};
+	struct
+	{
+		uchar	r_pad[24];
+		uchar	r_flags;
+	};
+};
+
+struct	Isofile
+{
+	short	fmt;		/* 'z' if iso, 'r' if high sierra */
+	short	blksize;
+	long	offset;		/* true offset when reading directory */
+	long odelta;	/* true size of directory just read */
+	long	doffset;	/* plan9 offset when reading directory */
+	Drec	d;
+};
blob - /dev/null
blob + c3e5d9c92881ca32f1cdfd94b5574f67c4937092 (mode 644)
--- /dev/null
+++ src/cmd/9660srv/main.c
@@ -0,0 +1,576 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <fcall.h>
+#include "dat.h"
+#include "fns.h"
+
+enum
+{
+	Maxfdata	= 8192,
+	Maxiosize	= IOHDRSZ+Maxfdata,
+};
+
+void io(int);
+void rversion(void);
+void	rattach(void);
+void	rauth(void);
+void	rclunk(void);
+void	rcreate(void);
+void	rflush(void);
+void	ropen(void);
+void	rread(void);
+void	rremove(void);
+void	rsession(void);
+void	rstat(void);
+void	rwalk(void);
+void	rwrite(void);
+void	rwstat(void);
+
+static int	openflags(int);
+static void	usage(void);
+
+#define Reqsize (sizeof(Fcall)+Maxfdata)
+
+Fcall *req;
+Fcall *rep;
+
+uchar mdata[Maxiosize];
+char fdata[Maxfdata];
+uchar statbuf[STATMAX];
+int errno;
+
+
+extern Xfsub	*xsublist[];
+extern int	nclust;
+
+jmp_buf	err_lab[16];
+int	nerr_lab;
+char	err_msg[ERRMAX];
+
+int	chatty;
+int	nojoliet;
+int	noplan9;
+int norock;
+
+void	(*fcalls[])(void) = {
+	[Tversion]	rversion,
+	[Tflush]	rflush,
+	[Tauth]	rauth,
+	[Tattach]	rattach,
+	[Twalk]		rwalk,
+	[Topen]		ropen,
+	[Tcreate]	rcreate,
+	[Tread]		rread,
+	[Twrite]	rwrite,
+	[Tclunk]	rclunk,
+	[Tremove]	rremove,
+	[Tstat]		rstat,
+	[Twstat]	rwstat,
+};
+
+void
+main(int argc, char **argv)
+{
+	int srvfd, pipefd[2], stdio;
+	Xfsub **xs;
+	char *mtpt;
+
+	stdio = 0;
+	mtpt = nil;
+	ARGBEGIN {
+	case '9':
+		noplan9 = 1;
+		break;
+	case 'c':
+		nclust = atoi(EARGF(usage()));
+		if (nclust <= 0)
+			sysfatal("nclust %d non-positive", nclust);
+		break;
+	case 'f':
+		deffile = EARGF(usage());
+		break;
+	case 'r':
+		norock = 1;
+		break;
+	case 's':
+		stdio = 1;
+		break;
+	case 'v':
+		chatty = 1;
+		break;
+	case 'J':
+		nojoliet = 1;
+		break;
+	case 'm':
+		mtpt = EARGF(usage());
+		break;
+	default:
+		usage();
+	} ARGEND
+
+	switch(argc) {
+	case 0:
+		break;
+	case 1:
+		srvname = argv[0];
+		break;
+	default:
+		usage();
+	}
+
+	iobuf_init();
+	for(xs=xsublist; *xs; xs++)
+		(*(*xs)->reset)();
+
+	if(stdio) {
+		pipefd[0] = 0;
+		pipefd[1] = 1;
+	} else {
+		close(0);
+		close(1);
+		open("/dev/null", OREAD);
+		open("/dev/null", OWRITE);
+		if(pipe(pipefd) < 0)
+			panic(1, "pipe");
+		
+		if(post9pservice(pipefd[0], srvname, mtpt) < 0)
+			sysfatal("post9pservice: %r");
+		close(pipefd[0]);
+	}
+	srvfd = pipefd[1];
+	
+	switch(rfork(RFNOWAIT|RFNOTEG|RFFDG|RFPROC)){
+	case -1:
+		panic(1, "fork");
+	default:
+		_exits(0);
+	case 0:
+		break;
+	}
+
+	io(srvfd);
+	exits(0);
+}
+
+void
+io(int srvfd)
+{
+	int n, pid;
+	Fcall xreq, xrep;
+
+	req = &xreq;
+	rep = &xrep;
+	pid = getpid();
+	fmtinstall('F', fcallfmt);
+
+	for(;;){
+		/*
+		 * reading from a pipe or a network device
+		 * will give an error after a few eof reads.
+		 * however, we cannot tell the difference
+		 * between a zero-length read and an interrupt
+		 * on the processes writing to us,
+		 * so we wait for the error.
+		 */
+		n = read9pmsg(srvfd, mdata, sizeof mdata);
+		if(n < 0)
+			break;
+		if(n == 0)
+			continue;
+		if(convM2S(mdata, n, req) == 0)
+			continue;
+
+		if(chatty)
+			fprint(2, "9660srv %d:<-%F\n", pid, req);
+
+		errno = 0;
+		if(!waserror()){
+			err_msg[0] = 0;
+			if(req->type >= nelem(fcalls) || !fcalls[req->type])
+				error("bad fcall type");
+			(*fcalls[req->type])();
+			poperror();
+		}
+
+		if(err_msg[0]){
+			rep->type = Rerror;
+			rep->ename = err_msg;
+		}else{
+			rep->type = req->type + 1;
+			rep->fid = req->fid;
+		}
+		rep->tag = req->tag;
+
+		if(chatty)
+			fprint(2, "9660srv %d:->%F\n", pid, rep);
+		n = convS2M(rep, mdata, sizeof mdata);
+		if(n == 0)
+			panic(1, "convS2M error on write");
+		if(write(srvfd, mdata, n) != n)
+			panic(1, "mount write");
+		if(nerr_lab != 0)
+			panic(0, "err stack %d");
+	}
+	chat("server shut down");
+}
+
+static void
+usage(void)
+{
+	fprint(2, "usage: %s [-v] [-9Jr] [-s] [-f devicefile] [srvname]\n", argv0);
+	exits("usage");
+}
+
+void
+error(char *p)
+{
+	strecpy(err_msg, err_msg+sizeof err_msg, p);
+	nexterror();
+}
+
+void
+nexterror(void)
+{
+	longjmp(err_lab[--nerr_lab], 1);
+}
+
+void*
+ealloc(long n)
+{
+	void *p;
+
+	p = malloc(n);
+	if(p == 0)
+		error("no memory");
+	return p;
+}
+
+void
+setnames(Dir *d, char *n)
+{
+	d->name = n;
+	d->uid = n+Maxname;
+	d->gid = n+Maxname*2;
+	d->muid = n+Maxname*3;
+
+	d->name[0] = '\0';
+	d->uid[0] = '\0';
+	d->gid[0] = '\0';
+	d->muid[0] = '\0';
+}
+
+void
+rversion(void)
+{
+	if(req->msize > Maxiosize)
+		rep->msize = Maxiosize;
+	else
+		rep->msize = req->msize;
+	rep->version = "9P2000";
+}
+
+void
+rauth(void)
+{
+	error("9660srv: authentication not required");
+}
+
+void
+rflush(void)
+{
+}
+
+void
+rattach(void)
+{
+	Xfs *xf;
+	Xfile *root;
+	Xfsub **xs;
+
+	chat("attach(fid=%d,uname=\"%s\",aname=\"%s\")...",
+		req->fid, req->uname, req->aname);
+
+	if(waserror()){
+		xfile(req->fid, Clunk);
+		nexterror();
+	}
+	root = xfile(req->fid, Clean);
+	root->qid = (Qid){0, 0, QTDIR};
+	root->xf = xf = ealloc(sizeof(Xfs));
+	memset(xf, 0, sizeof(Xfs));
+	xf->ref = 1;
+	xf->d = getxdata(req->aname);
+
+	for(xs=xsublist; *xs; xs++)
+		if((*(*xs)->attach)(root) >= 0){
+			poperror();
+			xf->s = *xs;
+			xf->rootqid = root->qid;
+			rep->qid = root->qid;
+			return;
+		}
+	error("unknown format");
+}
+
+Xfile*
+doclone(Xfile *of, int newfid)
+{
+	Xfile *nf, *next;
+
+	nf = xfile(newfid, Clean);
+	if(waserror()){
+		xfile(newfid, Clunk);
+		nexterror();
+	}
+	next = nf->next;
+	*nf = *of;
+	nf->next = next;
+	nf->fid = newfid;
+	refxfs(nf->xf, 1);
+	if(nf->len){
+		nf->ptr = ealloc(nf->len);
+		memmove(nf->ptr, of->ptr, nf->len);
+	}else
+		nf->ptr = of->ptr;
+	(*of->xf->s->clone)(of, nf);
+	poperror();
+	return nf;
+}
+
+void
+rwalk(void)
+{
+	Xfile *f, *nf;
+	Isofile *oldptr;
+	int oldlen;
+	Qid oldqid;
+
+	rep->nwqid = 0;
+	nf = nil;
+	f = xfile(req->fid, Asis);
+	if(req->fid != req->newfid)
+		f = nf = doclone(f, req->newfid);
+
+	/* save old state in case of error */
+	oldqid = f->qid;
+	oldlen = f->len;
+	oldptr = f->ptr;
+	if(oldlen){
+		oldptr = ealloc(oldlen);
+		memmove(oldptr, f->ptr, oldlen);
+	}
+
+	if(waserror()){
+		if(nf != nil)
+			xfile(req->newfid, Clunk);
+		if(rep->nwqid == req->nwname){
+			if(oldlen)
+				free(oldptr);
+		}else{
+			/* restore previous state */
+			f->qid = oldqid;
+			if(f->len)
+				free(f->ptr);
+			f->ptr = oldptr;
+			f->len = oldlen;
+		}
+		if(rep->nwqid==req->nwname || rep->nwqid > 0){
+			err_msg[0] = '\0';
+			return;
+		}
+		nexterror();
+	}
+
+	for(rep->nwqid=0; rep->nwqid < req->nwname && rep->nwqid < MAXWELEM; rep->nwqid++){
+		chat("\twalking %s\n", req->wname[rep->nwqid]);
+		if(!(f->qid.type & QTDIR)){
+			chat("\tnot dir: type=%#x\n", f->qid.type);
+			error("walk in non-directory");
+		}
+
+		if(strcmp(req->wname[rep->nwqid], "..")==0){
+			if(f->qid.path != f->xf->rootqid.path)
+				(*f->xf->s->walkup)(f);
+		}else
+			(*f->xf->s->walk)(f, req->wname[rep->nwqid]);
+		rep->wqid[rep->nwqid] = f->qid;
+	}
+	poperror();
+	if(oldlen)
+		free(oldptr);
+}
+
+void
+ropen(void)
+{
+	Xfile *f;
+
+	f = xfile(req->fid, Asis);
+	if(f->flags&Omodes)
+		error("open on open file");
+	if(req->mode&ORCLOSE)
+		error("no removes");
+	(*f->xf->s->open)(f, req->mode);
+	f->flags = openflags(req->mode);
+	rep->qid = f->qid;
+	rep->iounit = 0;
+}
+
+void
+rcreate(void)
+{
+	error("no creates");
+/*
+	Xfile *f;
+
+	if(strcmp(req->name, ".") == 0 || strcmp(req->name, "..") == 0)
+		error("create . or ..");
+	f = xfile(req->fid, Asis);
+	if(f->flags&Omodes)
+		error("create on open file");
+	if(!(f->qid.path&CHDIR))
+		error("create in non-directory");
+	(*f->xf->s->create)(f, req->name, req->perm, req->mode);
+	chat("f->qid=0x%8.8lux...", f->qid.path);
+	f->flags = openflags(req->mode);
+	rep->qid = f->qid;
+*/
+}
+
+void
+rread(void)
+{
+	Xfile *f;
+
+	f=xfile(req->fid, Asis);
+	if (!(f->flags&Oread))
+		error("file not opened for reading");
+	if(f->qid.type & QTDIR)
+		rep->count = (*f->xf->s->readdir)(f, (uchar*)fdata, req->offset, req->count);
+	else
+		rep->count = (*f->xf->s->read)(f, fdata, req->offset, req->count);
+	rep->data = fdata;
+}
+
+void
+rwrite(void)
+{
+	Xfile *f;
+
+	f=xfile(req->fid, Asis);
+	if(!(f->flags&Owrite))
+		error("file not opened for writing");
+	rep->count = (*f->xf->s->write)(f, req->data, req->offset, req->count);
+}
+
+void
+rclunk(void)
+{
+	Xfile *f;
+
+	if(!waserror()){
+		f = xfile(req->fid, Asis);
+		(*f->xf->s->clunk)(f);
+		poperror();
+	}
+	xfile(req->fid, Clunk);
+}
+
+void
+rremove(void)
+{
+	error("no removes");
+}
+
+void
+rstat(void)
+{
+	Xfile *f;
+	Dir dir;
+
+	chat("stat(fid=%d)...", req->fid);
+	f=xfile(req->fid, Asis);
+	setnames(&dir, fdata);
+	(*f->xf->s->stat)(f, &dir);
+	if(chatty)
+		showdir(2, &dir);
+	rep->nstat = convD2M(&dir, statbuf, sizeof statbuf);
+	rep->stat = statbuf;
+}
+
+void
+rwstat(void)
+{
+	error("no wstat");
+}
+
+static int
+openflags(int mode)
+{
+	int flags = 0;
+
+	switch(mode & ~(OTRUNC|OCEXEC|ORCLOSE)){
+	case OREAD:
+	case OEXEC:
+		flags = Oread; break;
+	case OWRITE:
+		flags = Owrite; break;
+	case ORDWR:
+		flags = Oread|Owrite; break;
+	}
+	if(mode & ORCLOSE)
+		flags |= Orclose;
+	return flags;
+}
+
+void
+showdir(int fd, Dir *s)
+{
+	char a_time[32], m_time[32];
+	char *p;
+
+	strcpy(a_time, ctime(s->atime));
+	if(p=strchr(a_time, '\n'))	/* assign = */
+		*p = 0;
+	strcpy(m_time, ctime(s->mtime));
+	if(p=strchr(m_time, '\n'))	/* assign = */
+		*p = 0;
+	fprint(fd, "name=\"%s\" qid=(0x%llux,%lud) type=%d dev=%d \
+mode=0x%8.8lux=0%luo atime=%s mtime=%s length=%lld uid=\"%s\" gid=\"%s\"...",
+		s->name, s->qid.path, s->qid.vers, s->type, s->dev,
+		s->mode, s->mode,
+		a_time, m_time, s->length, s->uid, s->gid);
+}
+
+#define	SIZE	1024
+
+void
+chat(char *fmt, ...)
+{
+	va_list arg;
+
+	if(chatty){
+		va_start(arg, fmt);
+		vfprint(2, fmt, arg);
+		va_end(arg);
+	}
+}
+
+void
+panic(int rflag, char *fmt, ...)
+{
+	va_list arg;
+	char buf[SIZE]; int n;
+
+	n = sprint(buf, "%s %d: ", argv0, getpid());
+	va_start(arg, fmt);
+	vseprint(buf+n, buf+SIZE, fmt, arg);
+	va_end(arg);
+	fprint(2, (rflag ? "%s: %r\n" : "%s\n"), buf);
+	if(chatty){
+		fprint(2, "abort\n");
+		abort();
+	}
+	exits("panic");
+}
blob - /dev/null
blob + 0e478011fe71b964909076b68bf9b9374db5b1dd (mode 644)
--- /dev/null
+++ src/cmd/9660srv/mkfile
@@ -0,0 +1,17 @@
+<$PLAN9/src/mkhdr
+
+TARG=9660srv
+
+OFILES=\
+	main.$O\
+	9660srv.$O\
+	xfile.$O\
+	iobuf.$O\
+	data.$O\
+
+HFILES=dat.h fns.h
+
+<$PLAN9/src/mkone
+
+9660srv.$O: iso9660.h
+
blob - /dev/null
blob + c46adf21e82877b3c95176b611856bba830429d0 (mode 644)
--- /dev/null
+++ src/cmd/9660srv/xfile.c
@@ -0,0 +1,170 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <fcall.h>
+#include "dat.h"
+#include "fns.h"
+
+static Xfile*	clean(Xfile*);
+
+#define	FIDMOD	127	/* prime */
+
+static Xdata*	xhead;
+static Xfile*	xfiles[FIDMOD];
+static Xfile*	freelist;
+
+Xdata*
+getxdata(char *name)
+{
+	int fd;
+	Dir *dir;
+	Xdata *xf, *fxf;
+	int flag;
+
+	if(name[0] == 0)
+		name = deffile;
+	if(name == 0)
+		error(Enofile);
+	flag = (access(name, 6) == 0) ? ORDWR : OREAD;
+	fd = open(name, flag);
+	if(fd < 0)
+		error(Enonexist);
+	dir = nil;
+	if(waserror()){
+		close(fd);
+		free(dir);
+		nexterror();
+	}
+	if((dir = dirfstat(fd)) == nil)
+		error("I/O error");
+	if((dir->qid.type & ~QTTMP) != QTFILE)
+		error("attach name not a plain file");
+	for(fxf=0,xf=xhead; xf; xf=xf->next){
+		if(xf->name == 0){
+			if(fxf == 0)
+				fxf = xf;
+			continue;
+		}
+		if(xf->qid.path != dir->qid.path || xf->qid.vers != dir->qid.vers)
+			continue;
+		if(xf->type != dir->type || xf->fdev != dir->dev)
+			continue;
+		xf->ref++;
+		chat("incref=%d, \"%s\", dev=%d...", xf->ref, xf->name, xf->dev);
+		close(fd);
+		poperror();
+		free(dir);
+		return xf;
+	}
+	if(fxf==0){
+		fxf = ealloc(sizeof(Xfs));
+		fxf->next = xhead;
+		xhead = fxf;
+	}
+	chat("alloc \"%s\", dev=%d...", name, fd);
+	fxf->ref = 1;
+	fxf->name = strcpy(ealloc(strlen(name)+1), name);
+	fxf->qid = dir->qid;
+	fxf->type = dir->type;
+	fxf->fdev = dir->dev;
+	fxf->dev = fd;
+	free(dir);
+	poperror();
+	return fxf;
+}
+
+static void
+putxdata(Xdata *d)
+{
+	if(d->ref <= 0)
+		panic(0, "putxdata");
+	d->ref--;
+	chat("decref=%d, \"%s\", dev=%d...", d->ref, d->name, d->dev);
+	if(d->ref == 0){
+		chat("purgebuf...");
+		purgebuf(d);
+		close(d->dev);
+		free(d->name);
+		d->name = 0;
+	}
+}
+
+void
+refxfs(Xfs *xf, int delta)
+{
+	xf->ref += delta;
+	if(xf->ref == 0){
+		if(xf->d)
+			putxdata(xf->d);
+		if(xf->ptr)
+			free(xf->ptr);
+		free(xf);
+	}
+}
+
+Xfile*
+xfile(int fid, int flag)
+{
+	int k = fid%FIDMOD;
+	Xfile **hp=&xfiles[k], *f, *pf;
+
+	for(f=*hp,pf=0; f; pf=f,f=f->next)
+		if(f->fid == fid)
+			break;
+	if(f && pf){
+		pf->next = f->next;
+		f->next = *hp;
+		*hp = f;
+	}
+	switch(flag){
+	default:
+		panic(0, "xfile");
+	case Asis:
+		if(f == 0)
+			error("unassigned fid");
+		return f;
+	case Clean:
+		break;
+	case Clunk:
+		if(f){
+			*hp = f->next;
+			clean(f);
+			f->next = freelist;
+			freelist = f;
+		}
+		return 0;
+	}
+	if(f)
+		return clean(f);
+	if(f = freelist)	/* assign = */
+		freelist = f->next;
+	else
+		f = ealloc(sizeof(Xfile));
+	f->next = *hp;
+	*hp = f;
+	f->xf = 0;
+	f->fid = fid;
+	f->flags = 0;
+	f->qid = (Qid){0,0,0};
+	f->len = 0;
+	f->ptr = 0;
+	return f;
+}
+
+static Xfile *
+clean(Xfile *f)
+{
+	if(f->xf){
+		refxfs(f->xf, -1);
+		f->xf = 0;
+	}
+	if(f->len){
+		free(f->ptr);
+		f->len = 0;
+	}
+	f->ptr = 0;
+	f->flags = 0;
+	f->qid = (Qid){0,0,0};
+	return f;
+}
+