aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/9660srv
diff options
context:
space:
mode:
authorRuss Cox <rsc@swtch.com>2008-07-20 03:23:19 -0400
committerRuss Cox <rsc@swtch.com>2008-07-20 03:23:19 -0400
commit78a779a3834cf39d7c0bcd93a15824b29df947a3 (patch)
tree7ebe47a874a3864f8e990567554b0df956590650 /src/cmd/9660srv
parent29e9b5683ec8d610140da9118e8f004f74bc6c77 (diff)
downloadplan9port-78a779a3834cf39d7c0bcd93a15824b29df947a3.tar.gz
plan9port-78a779a3834cf39d7c0bcd93a15824b29df947a3.tar.bz2
plan9port-78a779a3834cf39d7c0bcd93a15824b29df947a3.zip
9660srv: import from Plan 9
Diffstat (limited to 'src/cmd/9660srv')
-rw-r--r--src/cmd/9660srv/9660srv.c896
-rw-r--r--src/cmd/9660srv/dat.h118
-rw-r--r--src/cmd/9660srv/data.c22
-rw-r--r--src/cmd/9660srv/fns.h17
-rw-r--r--src/cmd/9660srv/iobuf.c177
-rw-r--r--src/cmd/9660srv/iso9660.h142
-rw-r--r--src/cmd/9660srv/main.c576
-rw-r--r--src/cmd/9660srv/mkfile17
-rw-r--r--src/cmd/9660srv/xfile.c170
9 files changed, 2135 insertions, 0 deletions
diff --git a/src/cmd/9660srv/9660srv.c b/src/cmd/9660srv/9660srv.c
new file mode 100644
index 00000000..a64cedfd
--- /dev/null
+++ b/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
diff --git a/src/cmd/9660srv/dat.h b/src/cmd/9660srv/dat.h
new file mode 100644
index 00000000..bee14db6
--- /dev/null
+++ b/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;
diff --git a/src/cmd/9660srv/data.c b/src/cmd/9660srv/data.c
new file mode 100644
index 00000000..e89f8d1c
--- /dev/null
+++ b/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
+};
diff --git a/src/cmd/9660srv/fns.h b/src/cmd/9660srv/fns.h
new file mode 100644
index 00000000..1bae3b50
--- /dev/null
+++ b/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)
diff --git a/src/cmd/9660srv/iobuf.c b/src/cmd/9660srv/iobuf.c
new file mode 100644
index 00000000..30eafb3a
--- /dev/null
+++ b/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;
+}
diff --git a/src/cmd/9660srv/iso9660.h b/src/cmd/9660srv/iso9660.h
new file mode 100644
index 00000000..58bcb9ff
--- /dev/null
+++ b/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;
+};
diff --git a/src/cmd/9660srv/main.c b/src/cmd/9660srv/main.c
new file mode 100644
index 00000000..c3e5d9c9
--- /dev/null
+++ b/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");
+}
diff --git a/src/cmd/9660srv/mkfile b/src/cmd/9660srv/mkfile
new file mode 100644
index 00000000..0e478011
--- /dev/null
+++ b/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
+
diff --git a/src/cmd/9660srv/xfile.c b/src/cmd/9660srv/xfile.c
new file mode 100644
index 00000000..c46adf21
--- /dev/null
+++ b/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;
+}
+