t9660srv: import from Plan 9 - plan9port - [fork] Plan 9 from user space
 (HTM) git clone git://src.adamsgaard.dk/plan9port
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit 78a779a3834cf39d7c0bcd93a15824b29df947a3
 (DIR) parent 29e9b5683ec8d610140da9118e8f004f74bc6c77
 (HTM) Author: Russ Cox <rsc@swtch.com>
       Date:   Sun, 20 Jul 2008 03:23:19 -0400
       
       9660srv: import from Plan 9
       
       Diffstat:
         A src/cmd/9660srv/9660srv.c           |     896 +++++++++++++++++++++++++++++++
         A src/cmd/9660srv/dat.h               |     118 +++++++++++++++++++++++++++++++
         A src/cmd/9660srv/data.c              |      22 ++++++++++++++++++++++
         A src/cmd/9660srv/fns.h               |      17 +++++++++++++++++
         A src/cmd/9660srv/iobuf.c             |     177 +++++++++++++++++++++++++++++++
         A src/cmd/9660srv/iso9660.h           |     142 +++++++++++++++++++++++++++++++
         A src/cmd/9660srv/main.c              |     576 +++++++++++++++++++++++++++++++
         A src/cmd/9660srv/mkfile              |      17 +++++++++++++++++
         A src/cmd/9660srv/xfile.c             |     170 +++++++++++++++++++++++++++++++
       
       9 files changed, 2135 insertions(+), 0 deletions(-)
       ---
 (DIR) diff --git a/src/cmd/9660srv/9660srv.c b/src/cmd/9660srv/9660srv.c
       t@@ -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
 (DIR) diff --git a/src/cmd/9660srv/dat.h b/src/cmd/9660srv/dat.h
       t@@ -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;
 (DIR) diff --git a/src/cmd/9660srv/data.c b/src/cmd/9660srv/data.c
       t@@ -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
       +};
 (DIR) diff --git a/src/cmd/9660srv/fns.h b/src/cmd/9660srv/fns.h
       t@@ -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)
 (DIR) diff --git a/src/cmd/9660srv/iobuf.c b/src/cmd/9660srv/iobuf.c
       t@@ -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;
       +}
 (DIR) diff --git a/src/cmd/9660srv/iso9660.h b/src/cmd/9660srv/iso9660.h
       t@@ -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;
       +};
 (DIR) diff --git a/src/cmd/9660srv/main.c b/src/cmd/9660srv/main.c
       t@@ -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");
       +}
 (DIR) diff --git a/src/cmd/9660srv/mkfile b/src/cmd/9660srv/mkfile
       t@@ -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
       +
 (DIR) diff --git a/src/cmd/9660srv/xfile.c b/src/cmd/9660srv/xfile.c
       t@@ -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;
       +}
       +