tFile system stuff. - 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 d3df308747ee4d1fcc063a348dcf1146b390bda7
 (DIR) parent e97ceade5e1bba5787e39429384336fa37797906
 (HTM) Author: rsc <devnull@localhost>
       Date:   Sat,  6 Dec 2003 18:08:52 +0000
       
       File system stuff.
       
       Diffstat:
         A include/fs.h                        |      41 +++++++++++++++++++++++++++++++
         A include/mux.h                       |      54 +++++++++++++++++++++++++++++++
         A src/cmd/9p.c                        |     191 +++++++++++++++++++++++++++++++
         A src/cmd/9pserve.c                   |     597 +++++++++++++++++++++++++++++++
         A src/lib9/convD2M.c                  |      95 ++++++++++++++++++++++++++++++
         A src/lib9/convM2D.c                  |      94 +++++++++++++++++++++++++++++++
         A src/lib9/convM2S.c                  |     315 +++++++++++++++++++++++++++++++
         A src/lib9/convS2M.c                  |     386 +++++++++++++++++++++++++++++++
         A src/lib9/fcallfmt.c                 |     234 +++++++++++++++++++++++++++++++
         A src/lib9/read9pmsg.c                |      31 +++++++++++++++++++++++++++++++
         A src/libfs/COPYRIGHT                 |      27 +++++++++++++++++++++++++++
         A src/libfs/close.c                   |      26 ++++++++++++++++++++++++++
         A src/libfs/create.c                  |      25 +++++++++++++++++++++++++
         A src/libfs/dirread.c                 |     100 +++++++++++++++++++++++++++++++
         A src/libfs/fs.c                      |     276 ++++++++++++++++++++++++++++++
         A src/libfs/fsimpl.h                  |      41 +++++++++++++++++++++++++++++++
         A src/libfs/mkfile                    |      22 ++++++++++++++++++++++
         A src/libfs/open.c                    |      24 ++++++++++++++++++++++++
         A src/libfs/read.c                    |      48 +++++++++++++++++++++++++++++++
         A src/libfs/stat.c                    |      54 +++++++++++++++++++++++++++++++
         A src/libfs/walk.c                    |      73 +++++++++++++++++++++++++++++++
         A src/libfs/write.c                   |      46 +++++++++++++++++++++++++++++++
         A src/libfs/wstat.c                   |      49 +++++++++++++++++++++++++++++++
         A src/libmux/COPYRIGHT                |      27 +++++++++++++++++++++++++++
         A src/libmux/io.c                     |     136 +++++++++++++++++++++++++++++++
         A src/libmux/mkfile                   |      16 ++++++++++++++++
         A src/libmux/mux.c                    |     152 +++++++++++++++++++++++++++++++
         A src/libmux/queue.c                  |     109 +++++++++++++++++++++++++++++++
         A src/libmux/thread.c                 |      27 +++++++++++++++++++++++++++
       
       29 files changed, 3316 insertions(+), 0 deletions(-)
       ---
 (DIR) diff --git a/include/fs.h b/include/fs.h
       t@@ -0,0 +1,41 @@
       +#ifndef _FS_H_
       +#define _FS_H_ 1
       +#ifdef __cplusplus
       +extern "C" {
       +#endif
       +
       +/*
       + * Simple user-level 9P client.
       + */
       +
       +typedef struct Fsys Fsys;
       +typedef struct Fid Fid;
       +
       +Fsys *fsinit(int);
       +Fsys *fsmount(int);
       +
       +int fsversion(Fsys*, int, char*, int);
       +Fid *fsauth(Fsys*, char*);
       +Fid *fsattach(Fsys*, Fid*, char*, char*);
       +Fid *fsopen(Fsys*, char*, int);
       +int fsopenfd(Fsys*, char*, int);
       +long fsread(Fid*, void*, long);
       +long fsreadn(Fid*, void*, long);
       +long fswrite(Fid*, void*, long);
       +void fsclose(Fid*);
       +void fsunmount(Fsys*);
       +int fsrpc(Fsys*, Fcall*, Fcall*, void**);
       +Fid *fswalk(Fid*, char*);
       +struct Dir;        /* in case there's no lib9.h */
       +long fsdirread(Fid*, struct Dir**);
       +long fsdirreadall(Fid*, struct Dir**);
       +struct Dir *fsdirstat(Fsys*, char*);
       +struct Dir *fsdirfstat(Fid*);
       +int fsdirwstat(Fsys*, char*, struct Dir*);
       +int fsdirfwstat(Fid*, struct Dir*);
       +Fid *fsroot(Fsys*);
       +
       +#ifdef __cplusplus
       +}
       +#endif
       +#endif
 (DIR) diff --git a/include/mux.h b/include/mux.h
       t@@ -0,0 +1,54 @@
       +typedef struct Mux Mux;
       +typedef struct Muxrpc Muxrpc;
       +typedef struct Muxqueue Muxqueue;
       +
       +struct Muxrpc
       +{
       +        Muxrpc *next;
       +        Muxrpc *prev;
       +        Rendez r;
       +        uint tag;
       +        void *p;
       +};
       +
       +struct Mux
       +{
       +        uint mintag;        /* to be filled by client */
       +        uint maxtag;
       +        int (*send)(Mux*, void*);
       +        void *(*recv)(Mux*);
       +        int (*gettag)(Mux*, void*);
       +        int (*settag)(Mux*, void*, uint);
       +        void *aux;        /* for private use by client */
       +
       +/* private */
       +        QLock lk;
       +        QLock inlk;
       +        QLock outlk;
       +        Rendez tagrend;
       +        Rendez rpcfork;
       +        Muxqueue *readq;
       +        Muxqueue *writeq;
       +        uint nwait;
       +        uint mwait;
       +        uint freetag;
       +        Muxrpc **wait;
       +        uint muxer;
       +        Muxrpc sleep;
       +};
       +
       +void        muxinit(Mux*);
       +void*        muxrpc(Mux*, void*);
       +void        muxthreads(Mux*);
       +
       +/* private */
       +int        _muxsend(Mux*, void*);
       +void*        _muxrecv(Mux*);
       +void        _muxsendproc(void*);
       +void        _muxrecvproc(void*);
       +Muxqueue *_muxqalloc(void);
       +int _muxqsend(Muxqueue*, void*);
       +void *_muxqrecv(Muxqueue*);
       +void _muxqhangup(Muxqueue*);
       +void *_muxnbqrecv(Muxqueue*);
       +
 (DIR) diff --git a/src/cmd/9p.c b/src/cmd/9p.c
       t@@ -0,0 +1,191 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <fcall.h>
       +#include <fs.h>
       +
       +char *addr;
       +
       +void
       +usage(void)
       +{
       +        fprint(2, "usage: 9p [-a address] cmd args...\n");
       +        fprint(2, "possible cmds:\n");
       +        fprint(2, "        read name\n");
       +        fprint(2, "        write name\n");
       +        fprint(2, "        stat name\n");
       +//        fprint(2, "        ls name\n");
       +        fprint(2, "without -a, name elem/path means /path on server unix!$ns/elem\n");
       +        exits("usage");
       +}
       +
       +void xread(int, char**);
       +void xwrite(int, char**);
       +void xstat(int, char**);
       +void xls(int, char**);
       +
       +struct {
       +        char *s;
       +        void (*f)(int, char**);
       +} cmds[] = {
       +        "read", xread,
       +        "write", xwrite,
       +        "stat", xstat,
       +//        "ls", xls,
       +};
       +
       +void
       +main(int argc, char **argv)
       +{
       +        char *cmd;
       +        int i;
       +
       +        ARGBEGIN{
       +        case 'a':
       +                addr = EARGF(usage());
       +                break;
       +        default:
       +                usage();
       +        }ARGEND
       +
       +        if(argc < 1)
       +                usage();
       +
       +        cmd = argv[0];
       +        for(i=0; i<nelem(cmds); i++){
       +                if(strcmp(cmds[i].s, cmd) == 0){
       +                        cmds[i].f(argc, argv);
       +                        exits(0);
       +                }
       +        }
       +        usage();        
       +}
       +
       +Fsys*
       +xparse(char *name, char **path)
       +{
       +        int fd;
       +        char *ns;
       +        char *p;
       +        Fsys *fs;
       +
       +        if(addr == nil){
       +                p = strchr(name, '/');
       +                if(p == nil)
       +                        p = name+strlen(name);
       +                else
       +                        *p++ = 0;
       +                *path = p;
       +                if(*name == 0)
       +                        usage();
       +                ns = getenv("ns");
       +                if(ns == nil)
       +                        sysfatal("ns not set");
       +                addr = smprint("unix!%s/%s", ns, name);
       +                if(addr == nil)
       +                        sysfatal("out of memory");
       +        }else
       +                *path = name;
       +
       +        fprint(2, "dial %s...", addr);
       +        if((fd = dial(addr, nil, nil, nil)) < 0)
       +                sysfatal("dial: %r");
       +        if((fs = fsmount(fd)) == nil)
       +                sysfatal("fsmount: %r");
       +        return fs;
       +}
       +
       +Fid*
       +xwalk(char *name)
       +{
       +        Fid *fid;
       +        Fsys *fs;
       +
       +        fs = xparse(name, &name);
       +        fid = fswalk(fsroot(fs), name);
       +        if(fid == nil)
       +                sysfatal("fswalk %s: %r", name);
       +        return fid;
       +}
       +
       +Fid*
       +xopen(char *name, int mode)
       +{
       +        Fid *fid;
       +        Fsys *fs;
       +
       +        fs = xparse(name, &name);
       +        fid = fsopen(fs, name, mode);
       +        if(fid == nil)
       +                sysfatal("fsopen %s: %r", name);
       +        return fid;
       +}
       +
       +void
       +xread(int argc, char **argv)
       +{
       +        char buf[1024];
       +        int n;
       +        Fid *fid;
       +
       +        ARGBEGIN{
       +        default:
       +                usage();
       +        }ARGEND
       +
       +        if(argc != 1)
       +                usage();
       +
       +        fid = xopen(argv[0], OREAD);
       +        while((n = fsread(fid, buf, sizeof buf)) > 0)
       +                write(1, buf, n);
       +        if(n < 0)
       +                sysfatal("read error: %r");
       +        exits(0);        
       +}
       +
       +void
       +xwrite(int argc, char **argv)
       +{
       +        char buf[1024];
       +        int n;
       +        Fid *fid;
       +
       +        ARGBEGIN{
       +        default:
       +                usage();
       +        }ARGEND
       +
       +        if(argc != 1)
       +                usage();
       +
       +        fid = xopen(argv[0], OWRITE|OTRUNC);
       +        while((n = read(0, buf, sizeof buf)) > 0)
       +                if(fswrite(fid, buf, n) != n)
       +                        sysfatal("write error: %r");
       +        if(n < 0)
       +                sysfatal("read error: %r");
       +        exits(0);        
       +}
       +
       +void
       +xstat(int argc, char **argv)
       +{
       +        Dir *d;
       +        Fid *fid;
       +
       +        ARGBEGIN{
       +        default:
       +                usage();
       +        }ARGEND
       +
       +        if(argc != 1)
       +                usage();
       +
       +        fid = xwalk(argv[0]);
       +        if((d = fsdirfstat(fid)) < 0)
       +                sysfatal("dirfstat: %r");
       +        fmtinstall('D', dirfmt);
       +        fmtinstall('M', dirmodefmt);
       +        print("%D\n", d);
       +        exits(0);
       +}
 (DIR) diff --git a/src/cmd/9pserve.c b/src/cmd/9pserve.c
       t@@ -0,0 +1,597 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <fcall.h>
       +#include <thread.h>
       +
       +enum
       +{
       +        STACK = 32768,
       +        NHASH = 31,
       +        MAXMSG = 64,        /* per connection */
       +};
       +
       +typedef struct Hash Hash;
       +typedef struct Fid Fid;
       +typedef struct Msg Msg;
       +typedef struct Conn Conn;
       +typedef struct Queue Queue;
       +
       +struct Hash
       +{
       +        Hash *next;
       +        uint n;
       +        void *v;
       +};
       +
       +struct Fid
       +{
       +        int fid;
       +        int ref;
       +        int cfid;
       +        Fid *next;
       +};
       +
       +struct Msg
       +{
       +        Conn *c;
       +        int internal;
       +        int ref;
       +        int ctag;
       +        int tag;
       +        Fcall tx;
       +        Fcall rx;
       +        Fid *fid;
       +        Fid *newfid;
       +        Fid *afid;
       +        Msg *oldm;
       +        Msg *next;
       +        uchar *tpkt;
       +        uchar *rpkt;
       +};
       +
       +struct Conn
       +{
       +        int fd;
       +        int nmsg;
       +        int nfid;
       +        Channel *inc;
       +        Channel *internal;
       +        int inputstalled;
       +        char dir[40];
       +        Hash *tag[NHASH];
       +        Hash *fid[NHASH];
       +        Queue *outq;
       +        Queue *inq;
       +};
       +
       +char *addr;
       +int afd;
       +char adir[40];
       +int isunix;
       +Queue *outq;
       +Queue *inq;
       +
       +void *gethash(Hash**, uint);
       +int puthash(Hash**, uint, void*);
       +int delhash(Hash**, uint, void*);
       +Msg *mread9p(int);
       +int mwrite9p(int, Msg*);
       +uchar *read9ppkt(int);
       +int write9ppkt(int, uchar*);
       +Msg *msgnew(void);
       +void msgput(Msg*);
       +Msg *msgget(int);
       +Fid *fidnew(int);
       +void fidput(Fid*);
       +void *emalloc(int);
       +void *erealloc(void*, int);
       +int sendq(Queue*, void*);
       +void *recvq(Queue*);
       +void selectthread(void*);
       +void connthread(void*);
       +void listenthread(void*);
       +void rewritehdr(Fcall*, uchar*);
       +int tlisten(char*, char*);
       +int taccept(int, char*);
       +
       +void
       +usage(void)
       +{
       +        fprint(2, "usage: 9pserve [-u] address\n");
       +        fprint(2, "\treads/writes 9P messages on stdin/stdout\n");
       +        exits("usage");
       +}
       +
       +void
       +threadmain(int argc, char **argv)
       +{
       +        ARGBEGIN{
       +        default:
       +                usage();
       +        case 'u':
       +                isunix = 1;
       +                break;
       +        }ARGEND
       +
       +        if(argc != 1)
       +                usage();
       +
       +        if((afd = announce(addr, adir)) < 0)
       +                sysfatal("announce %s: %r", addr);
       +
       +        threadcreateidle(selectthread, nil, STACK);
       +}
       +
       +void
       +listenthread(void *arg)
       +{
       +        Conn *c;
       +
       +        USED(arg);
       +        for(;;){
       +                c = malloc(sizeof(Conn));
       +                if(c == nil){
       +                        fprint(2, "out of memory\n");
       +                        sleep(60*1000);
       +                        continue;
       +                }
       +                c->fd = tlisten(adir, c->dir);
       +                if(c->fd < 0){
       +                        fprint(2, "listen: %r\n");
       +                        close(afd);
       +                        free(c);
       +                        return;
       +                }
       +                threadcreate(connthread, c, STACK);
       +        }        
       +}
       +
       +void
       +err(Msg *m, char *ename)
       +{
       +        int n, nn;
       +
       +        m->rx.type = Rerror;
       +        m->rx.ename = ename;
       +        m->rx.tag = m->ctag;
       +        n = sizeS2M(&m->rx);
       +        m->rpkt = emalloc(n);
       +        nn = convS2M(&m->rx, m->rpkt, n);
       +        if(nn != n)
       +                sysfatal("sizeS2M + convS2M disagree");
       +        sendq(m->c->outq, m);
       +}
       +
       +void
       +connthread(void *arg)
       +{
       +        int i, fd;
       +        Conn *c;
       +        Hash *h;
       +        Msg *m, *om;
       +        Fid *f;
       +
       +        c = arg;
       +        fd = taccept(c->fd, c->dir);
       +        if(fd < 0){
       +                fprint(2, "accept %s: %r\n", c->dir);
       +                goto out;
       +        }
       +        close(c->fd);
       +        c->fd = fd;
       +        while((m = mread9p(c->fd)) != nil){
       +                m->c = c;
       +                c->nmsg++;
       +                if(puthash(c->tag, m->tx.tag, m) < 0){
       +                        err(m, "duplicate tag");
       +                        continue;
       +                }
       +                switch(m->tx.type){
       +                case Tflush:
       +                        if((m->oldm = gethash(c->tag, m->tx.oldtag)) == nil){
       +                                m->rx.tag = Rflush;
       +                                sendq(c->outq, m);
       +                                continue;
       +                        }
       +                        break;
       +                case Tattach:
       +                        m->fid = fidnew(m->tx.fid);
       +                        if(puthash(c->fid, m->tx.fid, m->fid) < 0){
       +                                err(m, "duplicate fid");
       +                                continue;
       +                        }
       +                        m->fid->ref++;
       +                        break;
       +                case Twalk:
       +                        if((m->fid = gethash(c->fid, m->tx.fid)) == nil){
       +                                err(m, "unknown fid");
       +                                continue;
       +                        }
       +                        if(m->tx.newfid == m->tx.fid){
       +                                m->fid->ref++;
       +                                m->newfid = m->fid;
       +                        }else{
       +                                m->newfid = fidnew(m->tx.newfid);
       +                                if(puthash(c->fid, m->tx.newfid, m->newfid) < 0){
       +                                        err(m, "duplicate fid");
       +                                        continue;
       +                                }
       +                                m->newfid->ref++;
       +                        }
       +                        break;
       +                case Tauth:
       +                        if((m->afid = gethash(c->fid, m->tx.afid)) == nil){
       +                                err(m, "unknown fid");
       +                                continue;
       +                        }
       +                        m->fid = fidnew(m->tx.fid);
       +                        if(puthash(c->fid, m->tx.fid, m->fid) < 0){
       +                                err(m, "duplicate fid");
       +                                continue;
       +                        }
       +                        m->fid->ref++;
       +                        break;
       +                case Topen:
       +                case Tclunk:
       +                case Tread:
       +                case Twrite:
       +                case Tstat:
       +                case Twstat:
       +                        if((m->fid = gethash(c->fid, m->tx.fid)) == nil){
       +                                err(m, "unknown fid");
       +                                continue;
       +                        }
       +                        m->fid->ref++;
       +                        break;
       +                }
       +
       +                /* have everything - translate and send */
       +                m->c = c;
       +                m->ctag = m->tx.tag;
       +                m->tx.tag = m->tag;
       +                if(m->fid)
       +                        m->tx.fid = m->fid->fid;
       +                if(m->newfid)
       +                        m->tx.newfid = m->newfid->fid;
       +                if(m->afid)
       +                        m->tx.afid = m->afid->fid;
       +                if(m->oldm)
       +                        m->tx.oldtag = m->oldm->tag;
       +                rewritehdr(&m->tx, m->tpkt);
       +                sendq(outq, m);
       +                while(c->nmsg >= MAXMSG){
       +                        c->inputstalled = 1;
       +                        recvp(c->inc);
       +                }
       +        }
       +
       +        /* flush all outstanding messages */
       +        for(i=0; i<NHASH; i++){
       +                for(h=c->tag[i]; h; h=h->next){
       +                        om = h->v;
       +                        m = msgnew();
       +                        m->internal = 1;
       +                        m->c = c;
       +                        m->tx.type = Tflush;
       +                        m->tx.tag = m->tag;
       +                        m->tx.oldtag = om->tag;
       +                        m->oldm = om;
       +                        om->ref++;
       +                        sendq(outq, m);
       +                        recvp(c->internal);
       +                }
       +        }
       +
       +        /* clunk all outstanding fids */
       +        for(i=0; i<NHASH; i++){
       +                for(h=c->fid[i]; h; h=h->next){
       +                        f = h->v;
       +                        m = msgnew();
       +                        m->internal = 1;
       +                        m->c = c;
       +                        m->tx.type = Tclunk;
       +                        m->tx.tag = m->tag;
       +                        m->tx.fid = f->fid;
       +                        m->fid = f;
       +                        f->ref++;
       +                        sendq(outq, m);
       +                        recvp(c->internal);
       +                }
       +        }
       +
       +out:
       +        assert(c->nmsg == 0);
       +        assert(c->nfid == 0);
       +        close(c->fd);
       +        free(c);
       +}
       +
       +void
       +connoutthread(void *arg)
       +{
       +        int err;
       +        Conn *c;
       +        Msg *m, *om;
       +
       +        c = arg;
       +        while((m = recvq(c->outq)) != nil){
       +                err = m->tx.type+1 != m->rx.type;
       +                switch(m->tx.type){
       +                case Tflush:
       +                        om = m->oldm;
       +                        if(delhash(om->c->tag, om->ctag, om) == 0)
       +                                msgput(om);
       +                        break;
       +                case Tclunk:
       +                        if(delhash(m->c->fid, m->fid->cfid, m->fid) == 0)
       +                                fidput(m->fid);
       +                        break;
       +                case Tauth:
       +                        if(err)
       +                                if(delhash(m->c->fid, m->afid->cfid, m->fid) == 0)
       +                                        fidput(m->fid);
       +                case Tattach:
       +                        if(err)
       +                                if(delhash(m->c->fid, m->fid->cfid, m->fid) == 0)
       +                                        fidput(m->fid);
       +                        break;
       +                case Twalk:
       +                        if(err && m->tx.fid != m->tx.newfid)
       +                                if(delhash(m->c->fid, m->newfid->cfid, m->newfid) == 0)
       +                                        fidput(m->newfid);
       +                        break;
       +                }
       +                if(mwrite9p(c->fd, m) < 0)
       +                        fprint(2, "write error: %r\n");
       +                if(delhash(m->c->tag, m->ctag, m) == 0)
       +                        msgput(m);
       +                msgput(m);
       +                if(c->inputstalled && c->nmsg < MAXMSG)
       +                        nbsendp(c->inc, 0);
       +        }
       +}
       +
       +void
       +outputthread(void *arg)
       +{
       +        Msg *m;
       +
       +        USED(arg);
       +
       +        while((m = recvq(outq)) != nil){
       +                if(mwrite9p(1, m) < 0)
       +                        sysfatal("output error: %r");
       +                msgput(m);
       +        }
       +}        
       +
       +void
       +inputthread(void *arg)
       +{
       +        uchar *pkt;
       +        int n, nn, tag;
       +        Msg *m;
       +
       +        while((pkt = read9ppkt(0)) != nil){
       +                n = GBIT32(pkt);
       +                if(n < 7){
       +                        fprint(2, "short 9P packet\n");
       +                        free(pkt);
       +                        continue;
       +                }
       +                tag = GBIT16(pkt+5);
       +                if((m = msgget(tag)) == nil){
       +                        fprint(2, "unexpected 9P response tag %d\n", tag);
       +                        free(pkt);
       +                        msgput(m);
       +                        continue;
       +                }
       +                if((nn = convM2S(pkt, n, &m->rx)) != n){
       +                        fprint(2, "bad packet - convM2S %d but %d\n", nn, n);
       +                        free(pkt);
       +                        msgput(m);
       +                        continue;
       +                }
       +                m->rpkt = pkt;
       +                m->rx.tag = m->ctag;
       +                rewritehdr(&m->rx, m->rpkt);
       +                sendq(m->c->outq, m);
       +        }
       +}
       +
       +void*
       +gethash(Hash **ht, uint n)
       +{
       +        Hash *h;
       +
       +        for(h=ht[n%NHASH]; h; h=h->next)
       +                if(h->n == n)
       +                        return h->v;
       +        return nil;
       +}
       +
       +int
       +delhash(Hash **ht, uint n, void *v)
       +{
       +        Hash *h, **l;
       +
       +        for(l=&ht[n%NHASH]; h=*l; l=&h->next)
       +                if(h->n == n){
       +                        if(h->v != v)
       +                                fprint(2, "hash error\n");
       +                        *l = h->next;
       +                        free(h);
       +                        return 0;
       +                }
       +        return -1;
       +}
       +
       +int
       +puthash(Hash **ht, uint n, void *v)
       +{
       +        Hash *h;
       +
       +        if(gethash(ht, n))
       +                return -1;
       +        h = emalloc(sizeof(Hash));
       +        h->next = ht[n%NHASH];
       +        h->n = n;
       +        h->v = v;
       +        ht[n%NHASH] = h;
       +        return 0;
       +}
       +
       +Fid **fidtab;
       +int nfidtab;
       +Fid *freefid;
       +
       +Fid*
       +fidnew(int cfid)
       +{
       +        Fid *f;
       +
       +        if(freefid == nil){
       +                fidtab = erealloc(fidtab, nfidtab*sizeof(fidtab[0]));
       +                fidtab[nfidtab] = emalloc(sizeof(Fid));
       +                freefid = fidtab[nfidtab++];
       +        }
       +        f = freefid;
       +        freefid = f->next;
       +        f->cfid = f->cfid;
       +        f->ref = 1;
       +        return f;
       +}
       +
       +void
       +fidput(Fid *f)
       +{
       +        assert(f->ref > 0);
       +        if(--f->ref > 0)
       +                return;
       +        f->next = freefid;
       +        f->cfid = -1;
       +        freefid = f;
       +}
       +
       +Msg **msgtab;
       +int nmsgtab;
       +Msg *freemsg;
       +
       +Msg*
       +msgnew(void)
       +{
       +        Msg *m;
       +
       +        if(freemsg == nil){
       +                msgtab = erealloc(msgtab, nmsgtab*sizeof(msgtab[0]));
       +                msgtab[nmsgtab] = emalloc(sizeof(Msg));
       +                freemsg = msgtab[nmsgtab++];
       +        }
       +        m = freemsg;
       +        freemsg = m->next;
       +        m->ref = 1;
       +        return m;
       +}
       +
       +void
       +msgput(Msg *m)
       +{
       +        assert(m->ref > 0);
       +        if(--m->ref > 0)
       +                return;
       +        m->next = freemsg;
       +        freemsg = m;
       +}
       +
       +void*
       +emalloc(int n)
       +{
       +        void *v;
       +
       +        v = mallocz(n, 1);
       +        if(v == nil)
       +                sysfatal("out of memory");
       +        return v;
       +}
       +
       +void*
       +erealloc(void *v, int n)
       +{
       +        v = realloc(v, n);
       +        if(v == nil)
       +                sysfatal("out of memory");
       +        return v;
       +}
       +
       +typedef struct Qel Qel;
       +struct Qel
       +{
       +        Qel *next;
       +        void *p;
       +};
       +
       +struct Queue
       +{
       +        int hungup;
       +        QLock lk;
       +        Rendez r;
       +        Qel *head;
       +        Qel *tail;
       +};
       +
       +Queue*
       +qalloc(void)
       +{
       +        Queue *q;
       +
       +        q = mallocz(sizeof(Queue), 1);
       +        if(q == nil)
       +                return nil;
       +        q->r.l = &q->lk;
       +        return q;
       +}
       +
       +int
       +sendq(Queue *q, void *p)
       +{
       +        Qel *e;
       +
       +        e = emalloc(sizeof(Qel));
       +        qlock(&q->lk);
       +        if(q->hungup){
       +                werrstr("hungup queue");
       +                qunlock(&q->lk);
       +                return -1;
       +        }
       +        e->p = p;
       +        e->next = nil;
       +        if(q->head == nil)
       +                q->head = e;
       +        else
       +                q->tail->next = e;
       +        q->tail = e;
       +        rwakeup(&q->r);
       +        qunlock(&q->lk);
       +        return 0;
       +}
       +
       +void*
       +recvq(Queue *q)
       +{
       +        void *p;
       +        Qel *e;
       +
       +        qlock(&q->lk);
       +        while(q->head == nil && !q->hungup)
       +                rsleep(&q->r);
       +        if(q->hungup){
       +                qunlock(&q->lk);
       +                return nil;
       +        }
       +        e = q->head;
       +        q->head = e->next;
       +        qunlock(&q->lk);
       +        p = e->p;
       +        free(e);
       +        return p;
       +}
 (DIR) diff --git a/src/lib9/convD2M.c b/src/lib9/convD2M.c
       t@@ -0,0 +1,95 @@
       +#include        <u.h>
       +#include        <libc.h>
       +#include        <fcall.h>
       +
       +uint
       +sizeD2M(Dir *d)
       +{
       +        char *sv[4];
       +        int i, ns;
       +
       +        sv[0] = d->name;
       +        sv[1] = d->uid;
       +        sv[2] = d->gid;
       +        sv[3] = d->muid;
       +
       +        ns = 0;
       +        for(i = 0; i < 4; i++)
       +                if(sv[i])
       +                        ns += strlen(sv[i]);
       +
       +        return STATFIXLEN + ns;
       +}
       +
       +uint
       +convD2M(Dir *d, uchar *buf, uint nbuf)
       +{
       +        uchar *p, *ebuf;
       +        char *sv[4];
       +        int i, ns, nsv[4], ss;
       +
       +        if(nbuf < BIT16SZ)
       +                return 0;
       +
       +        p = buf;
       +        ebuf = buf + nbuf;
       +
       +        sv[0] = d->name;
       +        sv[1] = d->uid;
       +        sv[2] = d->gid;
       +        sv[3] = d->muid;
       +
       +        ns = 0;
       +        for(i = 0; i < 4; i++){
       +                if(sv[i])
       +                        nsv[i] = strlen(sv[i]);
       +                else
       +                        nsv[i] = 0;
       +                ns += nsv[i];
       +        }
       +
       +        ss = STATFIXLEN + ns;
       +
       +        /* set size befor erroring, so user can know how much is needed */
       +        /* note that length excludes count field itself */
       +        PBIT16(p, ss-BIT16SZ);
       +        p += BIT16SZ;
       +
       +        if(ss > nbuf)
       +                return BIT16SZ;
       +
       +        PBIT16(p, d->type);
       +        p += BIT16SZ;
       +        PBIT32(p, d->dev);
       +        p += BIT32SZ;
       +        PBIT8(p, d->qid.type);
       +        p += BIT8SZ;
       +        PBIT32(p, d->qid.vers);
       +        p += BIT32SZ;
       +        PBIT64(p, d->qid.path);
       +        p += BIT64SZ;
       +        PBIT32(p, d->mode);
       +        p += BIT32SZ;
       +        PBIT32(p, d->atime);
       +        p += BIT32SZ;
       +        PBIT32(p, d->mtime);
       +        p += BIT32SZ;
       +        PBIT64(p, d->length);
       +        p += BIT64SZ;
       +
       +        for(i = 0; i < 4; i++){
       +                ns = nsv[i];
       +                if(p + ns + BIT16SZ > ebuf)
       +                        return 0;
       +                PBIT16(p, ns);
       +                p += BIT16SZ;
       +                if(ns)
       +                        memmove(p, sv[i], ns);
       +                p += ns;
       +        }
       +
       +        if(ss != p - buf)
       +                return 0;
       +
       +        return p - buf;
       +}
 (DIR) diff --git a/src/lib9/convM2D.c b/src/lib9/convM2D.c
       t@@ -0,0 +1,94 @@
       +#include        <u.h>
       +#include        <libc.h>
       +#include        <fcall.h>
       +
       +int
       +statcheck(uchar *buf, uint nbuf)
       +{
       +        uchar *ebuf;
       +        int i;
       +
       +        ebuf = buf + nbuf;
       +
       +        if(nbuf < STATFIXLEN || nbuf != BIT16SZ + GBIT16(buf))
       +                return -1;
       +
       +        buf += STATFIXLEN - 4 * BIT16SZ;
       +
       +        for(i = 0; i < 4; i++){
       +                if(buf + BIT16SZ > ebuf)
       +                        return -1;
       +                buf += BIT16SZ + GBIT16(buf);
       +        }
       +
       +        if(buf != ebuf)
       +                return -1;
       +
       +        return 0;
       +}
       +
       +static char nullstring[] = "";
       +
       +uint
       +convM2D(uchar *buf, uint nbuf, Dir *d, char *strs)
       +{
       +        uchar *p, *ebuf;
       +        char *sv[4];
       +        int i, ns;
       +
       +        if(nbuf < STATFIXLEN)
       +                return 0; 
       +
       +        p = buf;
       +        ebuf = buf + nbuf;
       +
       +        p += BIT16SZ;        /* ignore size */
       +        d->type = GBIT16(p);
       +        p += BIT16SZ;
       +        d->dev = GBIT32(p);
       +        p += BIT32SZ;
       +        d->qid.type = GBIT8(p);
       +        p += BIT8SZ;
       +        d->qid.vers = GBIT32(p);
       +        p += BIT32SZ;
       +        d->qid.path = GBIT64(p);
       +        p += BIT64SZ;
       +        d->mode = GBIT32(p);
       +        p += BIT32SZ;
       +        d->atime = GBIT32(p);
       +        p += BIT32SZ;
       +        d->mtime = GBIT32(p);
       +        p += BIT32SZ;
       +        d->length = GBIT64(p);
       +        p += BIT64SZ;
       +
       +        for(i = 0; i < 4; i++){
       +                if(p + BIT16SZ > ebuf)
       +                        return 0;
       +                ns = GBIT16(p);
       +                p += BIT16SZ;
       +                if(p + ns > ebuf)
       +                        return 0;
       +                if(strs){
       +                        sv[i] = strs;
       +                        memmove(strs, p, ns);
       +                        strs += ns;
       +                        *strs++ = '\0';
       +                }
       +                p += ns;
       +        }
       +
       +        if(strs){
       +                d->name = sv[0];
       +                d->uid = sv[1];
       +                d->gid = sv[2];
       +                d->muid = sv[3];
       +        }else{
       +                d->name = nullstring;
       +                d->uid = nullstring;
       +                d->gid = nullstring;
       +                d->muid = nullstring;
       +        }
       +        
       +        return p - buf;
       +}
 (DIR) diff --git a/src/lib9/convM2S.c b/src/lib9/convM2S.c
       t@@ -0,0 +1,315 @@
       +#include        <u.h>
       +#include        <libc.h>
       +#include        <fcall.h>
       +
       +static
       +uchar*
       +gstring(uchar *p, uchar *ep, char **s)
       +{
       +        uint n;
       +
       +        if(p+BIT16SZ > ep)
       +                return nil;
       +        n = GBIT16(p);
       +        p += BIT16SZ - 1;
       +        if(p+n+1 > ep)
       +                return nil;
       +        /* move it down, on top of count, to make room for '\0' */
       +        memmove(p, p + 1, n);
       +        p[n] = '\0';
       +        *s = (char*)p;
       +        p += n+1;
       +        return p;
       +}
       +
       +static
       +uchar*
       +gqid(uchar *p, uchar *ep, Qid *q)
       +{
       +        if(p+QIDSZ > ep)
       +                return nil;
       +        q->type = GBIT8(p);
       +        p += BIT8SZ;
       +        q->vers = GBIT32(p);
       +        p += BIT32SZ;
       +        q->path = GBIT64(p);
       +        p += BIT64SZ;
       +        return p;
       +}
       +
       +/*
       + * no syntactic checks.
       + * three causes for error:
       + *  1. message size field is incorrect
       + *  2. input buffer too short for its own data (counts too long, etc.)
       + *  3. too many names or qids
       + * gqid() and gstring() return nil if they would reach beyond buffer.
       + * main switch statement checks range and also can fall through
       + * to test at end of routine.
       + */
       +uint
       +convM2S(uchar *ap, uint nap, Fcall *f)
       +{
       +        uchar *p, *ep;
       +        uint i, size;
       +
       +        p = ap;
       +        ep = p + nap;
       +
       +        if(p+BIT32SZ+BIT8SZ+BIT16SZ > ep)
       +                return 0;
       +        size = GBIT32(p);
       +        p += BIT32SZ;
       +
       +        if(size < BIT32SZ+BIT8SZ+BIT16SZ)
       +                return 0;
       +
       +        f->type = GBIT8(p);
       +        p += BIT8SZ;
       +        f->tag = GBIT16(p);
       +        p += BIT16SZ;
       +
       +        switch(f->type)
       +        {
       +        default:
       +                return 0;
       +
       +        case Tversion:
       +                if(p+BIT32SZ > ep)
       +                        return 0;
       +                f->msize = GBIT32(p);
       +                p += BIT32SZ;
       +                p = gstring(p, ep, &f->version);
       +                break;
       +
       +        case Tflush:
       +                if(p+BIT16SZ > ep)
       +                        return 0;
       +                f->oldtag = GBIT16(p);
       +                p += BIT16SZ;
       +                break;
       +
       +        case Tauth:
       +                if(p+BIT32SZ > ep)
       +                        return 0;
       +                f->afid = GBIT32(p);
       +                p += BIT32SZ;
       +                p = gstring(p, ep, &f->uname);
       +                if(p == nil)
       +                        break;
       +                p = gstring(p, ep, &f->aname);
       +                if(p == nil)
       +                        break;
       +                break;
       +
       +        case Tattach:
       +                if(p+BIT32SZ > ep)
       +                        return 0;
       +                f->fid = GBIT32(p);
       +                p += BIT32SZ;
       +                if(p+BIT32SZ > ep)
       +                        return 0;
       +                f->afid = GBIT32(p);
       +                p += BIT32SZ;
       +                p = gstring(p, ep, &f->uname);
       +                if(p == nil)
       +                        break;
       +                p = gstring(p, ep, &f->aname);
       +                if(p == nil)
       +                        break;
       +                break;
       +
       +        case Twalk:
       +                if(p+BIT32SZ+BIT32SZ+BIT16SZ > ep)
       +                        return 0;
       +                f->fid = GBIT32(p);
       +                p += BIT32SZ;
       +                f->newfid = GBIT32(p);
       +                p += BIT32SZ;
       +                f->nwname = GBIT16(p);
       +                p += BIT16SZ;
       +                if(f->nwname > MAXWELEM)
       +                        return 0;
       +                for(i=0; i<f->nwname; i++){
       +                        p = gstring(p, ep, &f->wname[i]);
       +                        if(p == nil)
       +                                break;
       +                }
       +                break;
       +
       +        case Topen:
       +                if(p+BIT32SZ+BIT8SZ > ep)
       +                        return 0;
       +                f->fid = GBIT32(p);
       +                p += BIT32SZ;
       +                f->mode = GBIT8(p);
       +                p += BIT8SZ;
       +                break;
       +
       +        case Tcreate:
       +                if(p+BIT32SZ > ep)
       +                        return 0;
       +                f->fid = GBIT32(p);
       +                p += BIT32SZ;
       +                p = gstring(p, ep, &f->name);
       +                if(p == nil)
       +                        break;
       +                if(p+BIT32SZ+BIT8SZ > ep)
       +                        return 0;
       +                f->perm = GBIT32(p);
       +                p += BIT32SZ;
       +                f->mode = GBIT8(p);
       +                p += BIT8SZ;
       +                break;
       +
       +        case Tread:
       +                if(p+BIT32SZ+BIT64SZ+BIT32SZ > ep)
       +                        return 0;
       +                f->fid = GBIT32(p);
       +                p += BIT32SZ;
       +                f->offset = GBIT64(p);
       +                p += BIT64SZ;
       +                f->count = GBIT32(p);
       +                p += BIT32SZ;
       +                break;
       +
       +        case Twrite:
       +                if(p+BIT32SZ+BIT64SZ+BIT32SZ > ep)
       +                        return 0;
       +                f->fid = GBIT32(p);
       +                p += BIT32SZ;
       +                f->offset = GBIT64(p);
       +                p += BIT64SZ;
       +                f->count = GBIT32(p);
       +                p += BIT32SZ;
       +                if(p+f->count > ep)
       +                        return 0;
       +                f->data = (char*)p;
       +                p += f->count;
       +                break;
       +
       +        case Tclunk:
       +        case Tremove:
       +                if(p+BIT32SZ > ep)
       +                        return 0;
       +                f->fid = GBIT32(p);
       +                p += BIT32SZ;
       +                break;
       +
       +        case Tstat:
       +                if(p+BIT32SZ > ep)
       +                        return 0;
       +                f->fid = GBIT32(p);
       +                p += BIT32SZ;
       +                break;
       +
       +        case Twstat:
       +                if(p+BIT32SZ+BIT16SZ > ep)
       +                        return 0;
       +                f->fid = GBIT32(p);
       +                p += BIT32SZ;
       +                f->nstat = GBIT16(p);
       +                p += BIT16SZ;
       +                if(p+f->nstat > ep)
       +                        return 0;
       +                f->stat = p;
       +                p += f->nstat;
       +                break;
       +
       +/*
       + */
       +        case Rversion:
       +                if(p+BIT32SZ > ep)
       +                        return 0;
       +                f->msize = GBIT32(p);
       +                p += BIT32SZ;
       +                p = gstring(p, ep, &f->version);
       +                break;
       +
       +        case Rerror:
       +                p = gstring(p, ep, &f->ename);
       +                break;
       +
       +        case Rflush:
       +                break;
       +
       +        case Rauth:
       +                p = gqid(p, ep, &f->aqid);
       +                if(p == nil)
       +                        break;
       +                break;
       +
       +        case Rattach:
       +                p = gqid(p, ep, &f->qid);
       +                if(p == nil)
       +                        break;
       +                break;
       +
       +        case Rwalk:
       +                if(p+BIT16SZ > ep)
       +                        return 0;
       +                f->nwqid = GBIT16(p);
       +                p += BIT16SZ;
       +                if(f->nwqid > MAXWELEM)
       +                        return 0;
       +                for(i=0; i<f->nwqid; i++){
       +                        p = gqid(p, ep, &f->wqid[i]);
       +                        if(p == nil)
       +                                break;
       +                }
       +                break;
       +
       +        case Ropen:
       +        case Rcreate:
       +                p = gqid(p, ep, &f->qid);
       +                if(p == nil)
       +                        break;
       +                if(p+BIT32SZ > ep)
       +                        return 0;
       +                f->iounit = GBIT32(p);
       +                p += BIT32SZ;
       +                break;
       +
       +        case Rread:
       +                if(p+BIT32SZ > ep)
       +                        return 0;
       +                f->count = GBIT32(p);
       +                p += BIT32SZ;
       +                if(p+f->count > ep)
       +                        return 0;
       +                f->data = (char*)p;
       +                p += f->count;
       +                break;
       +
       +        case Rwrite:
       +                if(p+BIT32SZ > ep)
       +                        return 0;
       +                f->count = GBIT32(p);
       +                p += BIT32SZ;
       +                break;
       +
       +        case Rclunk:
       +        case Rremove:
       +                break;
       +
       +        case Rstat:
       +                if(p+BIT16SZ > ep)
       +                        return 0;
       +                f->nstat = GBIT16(p);
       +                p += BIT16SZ;
       +                if(p+f->nstat > ep)
       +                        return 0;
       +                f->stat = p;
       +                p += f->nstat;
       +                break;
       +
       +        case Rwstat:
       +                break;
       +        }
       +
       +        if(p==nil || p>ep)
       +                return 0;
       +        if(ap+size == p)
       +                return size;
       +        return 0;
       +}
 (DIR) diff --git a/src/lib9/convS2M.c b/src/lib9/convS2M.c
       t@@ -0,0 +1,386 @@
       +#include        <u.h>
       +#include        <libc.h>
       +#include        <fcall.h>
       +
       +static
       +uchar*
       +pstring(uchar *p, char *s)
       +{
       +        uint n;
       +
       +        if(s == nil){
       +                PBIT16(p, 0);
       +                p += BIT16SZ;
       +                return p;
       +        }
       +
       +        n = strlen(s);
       +        PBIT16(p, n);
       +        p += BIT16SZ;
       +        memmove(p, s, n);
       +        p += n;
       +        return p;
       +}
       +
       +static
       +uchar*
       +pqid(uchar *p, Qid *q)
       +{
       +        PBIT8(p, q->type);
       +        p += BIT8SZ;
       +        PBIT32(p, q->vers);
       +        p += BIT32SZ;
       +        PBIT64(p, q->path);
       +        p += BIT64SZ;
       +        return p;
       +}
       +
       +static
       +uint
       +stringsz(char *s)
       +{
       +        if(s == nil)
       +                return BIT16SZ;
       +
       +        return BIT16SZ+strlen(s);
       +}
       +
       +uint
       +sizeS2M(Fcall *f)
       +{
       +        uint n;
       +        int i;
       +
       +        n = 0;
       +        n += BIT32SZ;        /* size */
       +        n += BIT8SZ;        /* type */
       +        n += BIT16SZ;        /* tag */
       +
       +        switch(f->type)
       +        {
       +        default:
       +                return 0;
       +
       +        case Tversion:
       +                n += BIT32SZ;
       +                n += stringsz(f->version);
       +                break;
       +
       +        case Tflush:
       +                n += BIT16SZ;
       +                break;
       +
       +        case Tauth:
       +                n += BIT32SZ;
       +                n += stringsz(f->uname);
       +                n += stringsz(f->aname);
       +                break;
       +
       +        case Tattach:
       +                n += BIT32SZ;
       +                n += BIT32SZ;
       +                n += stringsz(f->uname);
       +                n += stringsz(f->aname);
       +                break;
       +
       +        case Twalk:
       +                n += BIT32SZ;
       +                n += BIT32SZ;
       +                n += BIT16SZ;
       +                for(i=0; i<f->nwname; i++)
       +                        n += stringsz(f->wname[i]);
       +                break;
       +
       +        case Topen:
       +                n += BIT32SZ;
       +                n += BIT8SZ;
       +                break;
       +
       +        case Tcreate:
       +                n += BIT32SZ;
       +                n += stringsz(f->name);
       +                n += BIT32SZ;
       +                n += BIT8SZ;
       +                break;
       +
       +        case Tread:
       +                n += BIT32SZ;
       +                n += BIT64SZ;
       +                n += BIT32SZ;
       +                break;
       +
       +        case Twrite:
       +                n += BIT32SZ;
       +                n += BIT64SZ;
       +                n += BIT32SZ;
       +                n += f->count;
       +                break;
       +
       +        case Tclunk:
       +        case Tremove:
       +                n += BIT32SZ;
       +                break;
       +
       +        case Tstat:
       +                n += BIT32SZ;
       +                break;
       +
       +        case Twstat:
       +                n += BIT32SZ;
       +                n += BIT16SZ;
       +                n += f->nstat;
       +                break;
       +/*
       + */
       +
       +        case Rversion:
       +                n += BIT32SZ;
       +                n += stringsz(f->version);
       +                break;
       +
       +        case Rerror:
       +                n += stringsz(f->ename);
       +                break;
       +
       +        case Rflush:
       +                break;
       +
       +        case Rauth:
       +                n += QIDSZ;
       +                break;
       +
       +        case Rattach:
       +                n += QIDSZ;
       +                break;
       +
       +        case Rwalk:
       +                n += BIT16SZ;
       +                n += f->nwqid*QIDSZ;
       +                break;
       +
       +        case Ropen:
       +        case Rcreate:
       +                n += QIDSZ;
       +                n += BIT32SZ;
       +                break;
       +
       +        case Rread:
       +                n += BIT32SZ;
       +                n += f->count;
       +                break;
       +
       +        case Rwrite:
       +                n += BIT32SZ;
       +                break;
       +
       +        case Rclunk:
       +                break;
       +
       +        case Rremove:
       +                break;
       +
       +        case Rstat:
       +                n += BIT16SZ;
       +                n += f->nstat;
       +                break;
       +
       +        case Rwstat:
       +                break;
       +        }
       +        return n;
       +}
       +
       +uint
       +convS2M(Fcall *f, uchar *ap, uint nap)
       +{
       +        uchar *p;
       +        uint i, size;
       +
       +        size = sizeS2M(f);
       +        if(size == 0)
       +                return 0;
       +        if(size > nap)
       +                return 0;
       +
       +        p = (uchar*)ap;
       +
       +        PBIT32(p, size);
       +        p += BIT32SZ;
       +        PBIT8(p, f->type);
       +        p += BIT8SZ;
       +        PBIT16(p, f->tag);
       +        p += BIT16SZ;
       +
       +        switch(f->type)
       +        {
       +        default:
       +                return 0;
       +
       +        case Tversion:
       +                PBIT32(p, f->msize);
       +                p += BIT32SZ;
       +                p = pstring(p, f->version);
       +                break;
       +
       +        case Tflush:
       +                PBIT16(p, f->oldtag);
       +                p += BIT16SZ;
       +                break;
       +
       +        case Tauth:
       +                PBIT32(p, f->afid);
       +                p += BIT32SZ;
       +                p  = pstring(p, f->uname);
       +                p  = pstring(p, f->aname);
       +                break;
       +
       +        case Tattach:
       +                PBIT32(p, f->fid);
       +                p += BIT32SZ;
       +                PBIT32(p, f->afid);
       +                p += BIT32SZ;
       +                p  = pstring(p, f->uname);
       +                p  = pstring(p, f->aname);
       +                break;
       +
       +        case Twalk:
       +                PBIT32(p, f->fid);
       +                p += BIT32SZ;
       +                PBIT32(p, f->newfid);
       +                p += BIT32SZ;
       +                PBIT16(p, f->nwname);
       +                p += BIT16SZ;
       +                if(f->nwname > MAXWELEM)
       +                        return 0;
       +                for(i=0; i<f->nwname; i++)
       +                        p = pstring(p, f->wname[i]);
       +                break;
       +
       +        case Topen:
       +                PBIT32(p, f->fid);
       +                p += BIT32SZ;
       +                PBIT8(p, f->mode);
       +                p += BIT8SZ;
       +                break;
       +
       +        case Tcreate:
       +                PBIT32(p, f->fid);
       +                p += BIT32SZ;
       +                p = pstring(p, f->name);
       +                PBIT32(p, f->perm);
       +                p += BIT32SZ;
       +                PBIT8(p, f->mode);
       +                p += BIT8SZ;
       +                break;
       +
       +        case Tread:
       +                PBIT32(p, f->fid);
       +                p += BIT32SZ;
       +                PBIT64(p, f->offset);
       +                p += BIT64SZ;
       +                PBIT32(p, f->count);
       +                p += BIT32SZ;
       +                break;
       +
       +        case Twrite:
       +                PBIT32(p, f->fid);
       +                p += BIT32SZ;
       +                PBIT64(p, f->offset);
       +                p += BIT64SZ;
       +                PBIT32(p, f->count);
       +                p += BIT32SZ;
       +                memmove(p, f->data, f->count);
       +                p += f->count;
       +                break;
       +
       +        case Tclunk:
       +        case Tremove:
       +                PBIT32(p, f->fid);
       +                p += BIT32SZ;
       +                break;
       +
       +        case Tstat:
       +                PBIT32(p, f->fid);
       +                p += BIT32SZ;
       +                break;
       +
       +        case Twstat:
       +                PBIT32(p, f->fid);
       +                p += BIT32SZ;
       +                PBIT16(p, f->nstat);
       +                p += BIT16SZ;
       +                memmove(p, f->stat, f->nstat);
       +                p += f->nstat;
       +                break;
       +/*
       + */
       +
       +        case Rversion:
       +                PBIT32(p, f->msize);
       +                p += BIT32SZ;
       +                p = pstring(p, f->version);
       +                break;
       +
       +        case Rerror:
       +                p = pstring(p, f->ename);
       +                break;
       +
       +        case Rflush:
       +                break;
       +
       +        case Rauth:
       +                p = pqid(p, &f->aqid);
       +                break;
       +
       +        case Rattach:
       +                p = pqid(p, &f->qid);
       +                break;
       +
       +        case Rwalk:
       +                PBIT16(p, f->nwqid);
       +                p += BIT16SZ;
       +                if(f->nwqid > MAXWELEM)
       +                        return 0;
       +                for(i=0; i<f->nwqid; i++)
       +                        p = pqid(p, &f->wqid[i]);
       +                break;
       +
       +        case Ropen:
       +        case Rcreate:
       +                p = pqid(p, &f->qid);
       +                PBIT32(p, f->iounit);
       +                p += BIT32SZ;
       +                break;
       +
       +        case Rread:
       +                PBIT32(p, f->count);
       +                p += BIT32SZ;
       +                memmove(p, f->data, f->count);
       +                p += f->count;
       +                break;
       +
       +        case Rwrite:
       +                PBIT32(p, f->count);
       +                p += BIT32SZ;
       +                break;
       +
       +        case Rclunk:
       +                break;
       +
       +        case Rremove:
       +                break;
       +
       +        case Rstat:
       +                PBIT16(p, f->nstat);
       +                p += BIT16SZ;
       +                memmove(p, f->stat, f->nstat);
       +                p += f->nstat;
       +                break;
       +
       +        case Rwstat:
       +                break;
       +        }
       +        if(size != p-ap)
       +                return 0;
       +        return size;
       +}
 (DIR) diff --git a/src/lib9/fcallfmt.c b/src/lib9/fcallfmt.c
       t@@ -0,0 +1,234 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <fcall.h>
       +
       +static uint dumpsome(char*, char*, char*, long);
       +static void fdirconv(char*, char*, Dir*);
       +static char *qidtype(char*, uchar);
       +
       +#define        QIDFMT        "(%.16llux %lud %s)"
       +
       +int
       +fcallfmt(Fmt *fmt)
       +{
       +        Fcall *f;
       +        int fid, type, tag, i;
       +        char buf[512], tmp[200];
       +        char *p, *e;
       +        Dir *d;
       +        Qid *q;
       +
       +        e = buf+sizeof(buf);
       +        f = va_arg(fmt->args, Fcall*);
       +        type = f->type;
       +        fid = f->fid;
       +        tag = f->tag;
       +        switch(type){
       +        case Tversion:        /* 100 */
       +                seprint(buf, e, "Tversion tag %ud msize %ud version '%s'", tag, f->msize, f->version);
       +                break;
       +        case Rversion:
       +                seprint(buf, e, "Rversion tag %ud msize %ud version '%s'", tag, f->msize, f->version);
       +                break;
       +        case Tauth:        /* 102 */
       +                seprint(buf, e, "Tauth tag %ud afid %d uname %s aname %s", tag,
       +                        f->afid, f->uname, f->aname);
       +                break;
       +        case Rauth:
       +                seprint(buf, e, "Rauth tag %ud qid " QIDFMT, tag,
       +                        f->aqid.path, f->aqid.vers, qidtype(tmp, f->aqid.type));
       +                break;
       +        case Tattach:        /* 104 */
       +                seprint(buf, e, "Tattach tag %ud fid %d afid %d uname %s aname %s", tag,
       +                        fid, f->afid, f->uname, f->aname);
       +                break;
       +        case Rattach:
       +                seprint(buf, e, "Rattach tag %ud qid " QIDFMT, tag,
       +                        f->qid.path, f->qid.vers, qidtype(tmp, f->qid.type));
       +                break;
       +        case Rerror:        /* 107; 106 (Terror) illegal */
       +                seprint(buf, e, "Rerror tag %ud ename %s", tag, f->ename);
       +                break;
       +        case Tflush:        /* 108 */
       +                seprint(buf, e, "Tflush tag %ud oldtag %ud", tag, f->oldtag);
       +                break;
       +        case Rflush:
       +                seprint(buf, e, "Rflush tag %ud", tag);
       +                break;
       +        case Twalk:        /* 110 */
       +                p = seprint(buf, e, "Twalk tag %ud fid %d newfid %d nwname %d ", tag, fid, f->newfid, f->nwname);
       +                if(f->nwname <= MAXWELEM)
       +                        for(i=0; i<f->nwname; i++)
       +                                p = seprint(p, e, "%d:%s ", i, f->wname[i]);
       +                break;
       +        case Rwalk:
       +                p = seprint(buf, e, "Rwalk tag %ud nwqid %ud ", tag, f->nwqid);
       +                if(f->nwqid <= MAXWELEM)
       +                        for(i=0; i<f->nwqid; i++){
       +                                q = &f->wqid[i];
       +                                p = seprint(p, e, "%d:" QIDFMT " ", i,
       +                                        q->path, q->vers, qidtype(tmp, q->type));
       +                        }
       +                break;
       +        case Topen:        /* 112 */
       +                seprint(buf, e, "Topen tag %ud fid %ud mode %d", tag, fid, f->mode);
       +                break;
       +        case Ropen:
       +                seprint(buf, e, "Ropen tag %ud qid " QIDFMT " iounit %ud ", tag,
       +                        f->qid.path, f->qid.vers, qidtype(tmp, f->qid.type), f->iounit);
       +                break;
       +        case Tcreate:        /* 114 */
       +                seprint(buf, e, "Tcreate tag %ud fid %ud name %s perm %M mode %d", tag, fid, f->name, (ulong)f->perm, f->mode);
       +                break;
       +        case Rcreate:
       +                seprint(buf, e, "Rcreate tag %ud qid " QIDFMT " iounit %ud ", tag,
       +                        f->qid.path, f->qid.vers, qidtype(tmp, f->qid.type), f->iounit);
       +                break;
       +        case Tread:        /* 116 */
       +                seprint(buf, e, "Tread tag %ud fid %d offset %lld count %ud",
       +                        tag, fid, f->offset, f->count);
       +                break;
       +        case Rread:
       +                p = seprint(buf, e, "Rread tag %ud count %ud ", tag, f->count);
       +                        dumpsome(p, e, f->data, f->count);
       +                break;
       +        case Twrite:        /* 118 */
       +                p = seprint(buf, e, "Twrite tag %ud fid %d offset %lld count %ud ",
       +                        tag, fid, f->offset, f->count);
       +                dumpsome(p, e, f->data, f->count);
       +                break;
       +        case Rwrite:
       +                seprint(buf, e, "Rwrite tag %ud count %ud", tag, f->count);
       +                break;
       +        case Tclunk:        /* 120 */
       +                seprint(buf, e, "Tclunk tag %ud fid %ud", tag, fid);
       +                break;
       +        case Rclunk:
       +                seprint(buf, e, "Rclunk tag %ud", tag);
       +                break;
       +        case Tremove:        /* 122 */
       +                seprint(buf, e, "Tremove tag %ud fid %ud", tag, fid);
       +                break;
       +        case Rremove:
       +                seprint(buf, e, "Rremove tag %ud", tag);
       +                break;
       +        case Tstat:        /* 124 */
       +                seprint(buf, e, "Tstat tag %ud fid %ud", tag, fid);
       +                break;
       +        case Rstat:
       +                p = seprint(buf, e, "Rstat tag %ud ", tag);
       +                if(f->nstat > sizeof tmp)
       +                        seprint(p, e, " stat(%d bytes)", f->nstat);
       +                else{
       +                        d = (Dir*)tmp;
       +                        convM2D(f->stat, f->nstat, d, (char*)(d+1));
       +                        seprint(p, e, " stat ");
       +                        fdirconv(p+6, e, d);
       +                }
       +                break;
       +        case Twstat:        /* 126 */
       +                p = seprint(buf, e, "Twstat tag %ud fid %ud", tag, fid);
       +                if(f->nstat > sizeof tmp)
       +                        seprint(p, e, " stat(%d bytes)", f->nstat);
       +                else{
       +                        d = (Dir*)tmp;
       +                        convM2D(f->stat, f->nstat, d, (char*)(d+1));
       +                        seprint(p, e, " stat ");
       +                        fdirconv(p+6, e, d);
       +                }
       +                break;
       +        case Rwstat:
       +                seprint(buf, e, "Rwstat tag %ud", tag);
       +                break;
       +        default:
       +                seprint(buf, e,  "unknown type %d", type);
       +        }
       +        return fmtstrcpy(fmt, buf);
       +}
       +
       +static char*
       +qidtype(char *s, uchar t)
       +{
       +        char *p;
       +
       +        p = s;
       +        if(t & QTDIR)
       +                *p++ = 'd';
       +        if(t & QTAPPEND)
       +                *p++ = 'a';
       +        if(t & QTEXCL)
       +                *p++ = 'l';
       +        if(t & QTAUTH)
       +                *p++ = 'A';
       +        *p = '\0';
       +        return s;
       +}
       +
       +int
       +dirfmt(Fmt *fmt)
       +{
       +        char buf[160];
       +
       +        fdirconv(buf, buf+sizeof buf, va_arg(fmt->args, Dir*));
       +        return fmtstrcpy(fmt, buf);
       +}
       +
       +static void
       +fdirconv(char *buf, char *e, Dir *d)
       +{
       +        char tmp[16];
       +
       +        seprint(buf, e, "'%s' '%s' '%s' '%s' "
       +                "q " QIDFMT " m %#luo "
       +                "at %ld mt %ld l %lld "
       +                "t %d d %d",
       +                        d->name, d->uid, d->gid, d->muid,
       +                        d->qid.path, d->qid.vers, qidtype(tmp, d->qid.type), d->mode,
       +                        d->atime, d->mtime, d->length,
       +                        d->type, d->dev);
       +}
       +
       +/*
       + * dump out count (or DUMPL, if count is bigger) bytes from
       + * buf to ans, as a string if they are all printable,
       + * else as a series of hex bytes
       + */
       +#define DUMPL 64
       +
       +static uint
       +dumpsome(char *ans, char *e, char *buf, long count)
       +{
       +        int i, printable;
       +        char *p;
       +
       +        if(buf == nil){
       +                seprint(ans, e, "<no data>");
       +                return strlen(ans);
       +        }
       +        printable = 1;
       +        if(count > DUMPL)
       +                count = DUMPL;
       +        for(i=0; i<count && printable; i++)
       +                if((buf[i]<32 && buf[i] !='\n' && buf[i] !='\t') || (uchar)buf[i]>127)
       +                        printable = 0;
       +        p = ans;
       +        *p++ = '\'';
       +        if(printable){
       +                if(count > e-p-2)
       +                        count = e-p-2;
       +                memmove(p, buf, count);
       +                p += count;
       +        }else{
       +                if(2*count > e-p-2)
       +                        count = (e-p-2)/2;
       +                for(i=0; i<count; i++){
       +                        if(i>0 && i%4==0)
       +                                *p++ = ' ';
       +                        sprint(p, "%2.2ux", buf[i]);
       +                        p += 2;
       +                }
       +        }
       +        *p++ = '\'';
       +        *p = 0;
       +        return p - ans;
       +}
 (DIR) diff --git a/src/lib9/read9pmsg.c b/src/lib9/read9pmsg.c
       t@@ -0,0 +1,31 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <fcall.h>
       +
       +int
       +read9pmsg(int fd, void *abuf, uint n)
       +{
       +        int m, len;
       +        uchar *buf;
       +
       +        buf = abuf;
       +
       +        /* read count */
       +        m = readn(fd, buf, BIT32SZ);
       +        if(m != BIT32SZ){
       +                if(m < 0)
       +                        return -1;
       +                return 0;
       +        }
       +
       +        len = GBIT32(buf);
       +        if(len <= BIT32SZ || len > n){
       +                werrstr("bad length in 9P2000 message header");
       +                return -1;
       +        }
       +        len -= BIT32SZ;
       +        m = readn(fd, buf+BIT32SZ, len);
       +        if(m < len)
       +                return 0;
       +        return BIT32SZ+m;
       +}
 (DIR) diff --git a/src/libfs/COPYRIGHT b/src/libfs/COPYRIGHT
       t@@ -0,0 +1,27 @@
       +
       +This software was developed as part of a project at MIT:
       +        /sys/src/libfs/* except dirread.c
       +        /sys/include/fs.h
       +
       +Copyright (c) 2003 Russ Cox,
       +                   Massachusetts Institute of Technology
       +
       +Permission is hereby granted, free of charge, to any person obtaining
       +a copy of this software and associated documentation files (the
       +"Software"), to deal in the Software without restriction, including
       +without limitation the rights to use, copy, modify, merge, publish,
       +distribute, sublicense, and/or sell copies of the Software, and to
       +permit persons to whom the Software is furnished to do so, subject to
       +the following conditions:
       +
       +The above copyright notice and this permission notice shall be
       +included in all copies or substantial portions of the Software.
       +
       +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
       +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
       +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
       +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
       +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
       +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
       +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
       +
 (DIR) diff --git a/src/libfs/close.c b/src/libfs/close.c
       t@@ -0,0 +1,26 @@
       +/* Copyright (C) 2003 Russ Cox, Massachusetts Institute of Technology */
       +/* See COPYRIGHT */
       +
       +#include <u.h>
       +#include <libc.h>
       +#include <fcall.h>
       +#include <fs.h>
       +#include "fsimpl.h"
       +
       +static void
       +fidclunk(Fid *fid)
       +{
       +        Fcall tx, rx;
       +
       +        tx.type = Tclunk;
       +        tx.fid = fid->fid;
       +        fsrpc(fid->fs, &tx, &rx, 0);
       +        _fsputfid(fid);
       +}
       +
       +void
       +fsclose(Fid *fid)
       +{
       +        /* maybe someday there will be a ref count */
       +        fidclunk(fid);
       +}
 (DIR) diff --git a/src/libfs/create.c b/src/libfs/create.c
       t@@ -0,0 +1,25 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <fcall.h>
       +#include <fs.h>
       +#include "fsimpl.h"
       +
       +Fid*
       +fscreate(Fsys *fs, char *name, int mode, ulong perm)
       +{
       +        Fid *fid;
       +        Fcall tx, rx;
       +
       +        if((fid = fswalk(fs->root, name)) == nil)
       +                return nil;
       +        tx.type = Tcreate;
       +        tx.fid = fid->fid;
       +        tx.mode = mode;
       +        tx.perm = perm;
       +        if(fsrpc(fs, &tx, &rx, 0) < 0){
       +                fsclose(fid);
       +                return nil;
       +        }
       +        fid->mode = mode;
       +        return fid;
       +}
 (DIR) diff --git a/src/libfs/dirread.c b/src/libfs/dirread.c
       t@@ -0,0 +1,100 @@
       +/* Mostly copied from Plan 9's libc. */
       +
       +#include <u.h>
       +#include <libc.h>
       +#include <fcall.h>
       +#include <fs.h>
       +
       +static
       +long
       +dirpackage(uchar *buf, long ts, Dir **d)
       +{
       +        char *s;
       +        long ss, i, n, nn, m;
       +
       +        *d = nil;
       +        if(ts <= 0)
       +                return 0;
       +
       +        /*
       +         * first find number of all stats, check they look like stats, & size all associated strings
       +         */
       +        ss = 0;
       +        n = 0;
       +        for(i = 0; i < ts; i += m){
       +                m = BIT16SZ + GBIT16(&buf[i]);
       +                if(statcheck(&buf[i], m) < 0)
       +                        break;
       +                ss += m;
       +                n++;
       +        }
       +
       +        if(i != ts)
       +                return -1;
       +
       +        *d = malloc(n * sizeof(Dir) + ss);
       +        if(*d == nil)
       +                return -1;
       +
       +        /*
       +         * then convert all buffers
       +         */
       +        s = (char*)*d + n * sizeof(Dir);
       +        nn = 0;
       +        for(i = 0; i < ts; i += m){
       +                m = BIT16SZ + GBIT16((uchar*)&buf[i]);
       +                if(nn >= n || convM2D(&buf[i], m, *d + nn, s) != m){
       +                        free(*d);
       +                        *d = nil;
       +                        return -1;
       +                }
       +                nn++;
       +                s += m;
       +        }
       +
       +        return nn;
       +}
       +
       +long
       +fsdirread(Fid *fid, Dir **d)
       +{
       +        uchar *buf;
       +        long ts;
       +
       +        buf = malloc(DIRMAX);
       +        if(buf == nil)
       +                return -1;
       +        ts = fsread(fid, buf, DIRMAX);
       +        if(ts >= 0)
       +                ts = dirpackage(buf, ts, d);
       +        free(buf);
       +        return ts;
       +}
       +
       +long
       +fsdirreadall(Fid *fid, Dir **d)
       +{
       +        uchar *buf, *nbuf;
       +        long n, ts;
       +
       +        buf = nil;
       +        ts = 0;
       +        for(;;){
       +                nbuf = realloc(buf, ts+DIRMAX);
       +                if(nbuf == nil){
       +                        free(buf);
       +                        return -1;
       +                }
       +                buf = nbuf;
       +                n = fsread(fid, buf+ts, DIRMAX);
       +                if(n <= 0)
       +                        break;
       +                ts += n;
       +        }
       +        if(ts >= 0)
       +                ts = dirpackage(buf, ts, d);
       +        free(buf);
       +        if(ts == 0 && n < 0)
       +                return -1;
       +        return ts;
       +}
 (DIR) diff --git a/src/libfs/fs.c b/src/libfs/fs.c
       t@@ -0,0 +1,276 @@
       +/* Copyright (C) 2003 Russ Cox, Massachusetts Institute of Technology */
       +/* See COPYRIGHT */
       +
       +#include <u.h>
       +#include <libc.h>
       +#include <fcall.h>
       +#include <fs.h>
       +#include "fsimpl.h"
       +
       +static int _fssend(Mux*, void*);
       +static void *_fsrecv(Mux*);
       +static int _fsgettag(Mux*, void*);
       +static int _fssettag(Mux*, void*, uint);
       +
       +enum
       +{
       +        Fidchunk = 32
       +};
       +
       +Fsys*
       +fsinit(int fd)
       +{
       +        Fsys *fs;
       +
       +        fs = mallocz(sizeof(Fsys), 1);
       +        if(fs == nil)
       +                return nil;
       +        fs->fd = fd;
       +        fs->ref = 1;
       +        fs->mux.aux = fs;
       +        fs->mux.mintag = 0;
       +        fs->mux.maxtag = 256;
       +        fs->mux.send = _fssend;
       +        fs->mux.recv = _fsrecv;
       +        fs->mux.gettag = _fsgettag;
       +        fs->mux.settag = _fssettag;
       +        muxinit(&fs->mux);
       +        return fs;
       +}
       +
       +Fid*
       +fsroot(Fsys *fs)
       +{
       +        /* N.B. no incref */
       +        return fs->root;
       +}
       +
       +Fsys*
       +fsmount(int fd)
       +{
       +        int n;
       +        char *user;
       +        Fsys *fs;
       +
       +        fs = fsinit(fd);
       +        if(fs == nil)
       +                return nil;
       +        strcpy(fs->version, "9P2000");
       +        if((n = fsversion(fs, 8192, fs->version, sizeof fs->version)) < 0){
       +        Error:
       +                fsunmount(fs);
       +                return nil;
       +        }
       +        fs->msize = n;
       +
       +        user = getuser();
       +        if((fs->root = fsattach(fs, nil, getuser(), "")) == nil)
       +                goto Error;
       +        return fs;
       +}
       +
       +void
       +fsunmount(Fsys *fs)
       +{
       +        _fsdecref(fs);
       +}
       +
       +void
       +_fsdecref(Fsys *fs)
       +{
       +        Fid *f, *next;
       +
       +        qlock(&fs->lk);
       +        if(--fs->ref == 0){
       +                close(fs->fd);
       +                for(f=fs->freefid; f; f=next){
       +                        next = f->next;
       +                        if(f->fid%Fidchunk == 0)
       +                                free(f);
       +                }
       +                free(fs);
       +        }
       +        qunlock(&fs->lk);
       +}
       +
       +int
       +fsversion(Fsys *fs, int msize, char *version, int nversion)
       +{
       +        void *freep;
       +        Fcall tx, rx;
       +
       +        tx.type = Tversion;
       +        tx.version = version;
       +        tx.msize = msize;
       +
       +        if(fsrpc(fs, &tx, &rx, &freep) < 0)
       +                return -1;
       +        strecpy(version, version+nversion, rx.version);
       +        free(freep);
       +        return rx.msize;
       +}
       +
       +Fid*
       +fsattach(Fsys *fs, Fid *afid, char *user, char *aname)
       +{
       +        Fcall tx, rx;
       +        Fid *fid;
       +
       +        if((fid = _fsgetfid(fs)) == nil)
       +                return nil;
       +
       +        tx.type = Tattach;
       +        tx.afid = afid ? afid->fid : NOFID;
       +        tx.fid = fid->fid;
       +        tx.uname = user;
       +        tx.aname = aname;
       +
       +        if(fsrpc(fs, &tx, &rx, 0) < 0){
       +                _fsputfid(fid);
       +                return nil;
       +        }
       +        fid->qid = rx.qid;
       +        return fid;
       +}
       +
       +int
       +fsrpc(Fsys *fs, Fcall *tx, Fcall *rx, void **freep)
       +{
       +        int n, nn;
       +        void *tpkt, *rpkt;
       +
       +        n = sizeS2M(tx);
       +        tpkt = malloc(n);
       +        if(tpkt == nil)
       +                return -1;
       +        nn = convS2M(tx, tpkt, n);
       +        if(nn != n){
       +                free(tpkt);
       +                werrstr("libfs: sizeS2M convS2M mismatch");
       +                fprint(2, "%r\n");
       +                return -1;
       +        }
       +        rpkt = muxrpc(&fs->mux, tpkt);
       +        free(tpkt);
       +        if(rpkt == nil)
       +                return -1;
       +        n = GBIT32((uchar*)rpkt);
       +        nn = convM2S(rpkt, n, rx);
       +        if(nn != n){
       +                free(rpkt);
       +                werrstr("libfs: convM2S packet size mismatch");
       +                fprint(2, "%r\n");
       +                return -1;
       +        }
       +        if(rx->type == Rerror){
       +                werrstr("%s", rx->ename);
       +                free(rpkt);
       +                return -1;
       +        }
       +        if(rx->type != tx->type+1){
       +                werrstr("packet type mismatch -- tx %d rx %d",
       +                        tx->type, rx->type);
       +                free(rpkt);
       +                return -1;
       +        }
       +        if(freep)
       +                *freep = rpkt;
       +        else
       +                free(rpkt);
       +        return 0;
       +}
       +
       +Fid*
       +_fsgetfid(Fsys *fs)
       +{
       +        int i;
       +        Fid *f;
       +
       +        qlock(&fs->lk);
       +        if(fs->freefid == nil){
       +                f = malloc(sizeof(Fid)*Fidchunk);
       +                if(f == nil){
       +                        qunlock(&fs->lk);
       +                        return nil;
       +                }
       +                for(i=0; i<Fidchunk; i++){
       +                        f[i].fid = fs->nextfid++;
       +                        f[i].next = &f[i+1];
       +                        f[i].fs = fs;
       +                        fs->ref++;
       +                }
       +                f[i-1].next = nil;
       +                fs->freefid = f;
       +        }
       +        f = fs->freefid;
       +        fs->freefid = f->next;
       +        qunlock(&fs->lk);
       +        return f;
       +}
       +
       +void
       +_fsputfid(Fid *f)
       +{
       +        Fsys *fs;
       +
       +        fs = f->fs;
       +        qlock(&fs->lk);
       +        f->next = fs->freefid;
       +        fs->freefid = f;
       +        qunlock(&fs->lk);
       +        _fsdecref(fs);
       +}
       +
       +static int
       +_fsgettag(Mux *mux, void *pkt)
       +{
       +        return GBIT16((uchar*)pkt+5);
       +}
       +
       +static int
       +_fssettag(Mux *mux, void *pkt, uint tag)
       +{
       +        PBIT16((uchar*)pkt+5, tag);
       +        return 0;
       +}
       +
       +static int
       +_fssend(Mux *mux, void *pkt)
       +{
       +        Fsys *fs;
       +
       +        fs = mux->aux;
       +        return write(fs->fd, pkt, GBIT32((uchar*)pkt));
       +}
       +
       +static void*
       +_fsrecv(Mux *mux)
       +{
       +        uchar *pkt;
       +        uchar buf[4];
       +        int n;
       +        Fsys *fs;
       +
       +        fs = mux->aux;
       +        n = readn(fs->fd, buf, 4);
       +        if(n != 4)
       +                return nil;
       +        n = GBIT32(buf);
       +        pkt = malloc(n+4);
       +        if(pkt == nil){
       +                fprint(2, "libfs out of memory reading 9p packet; here comes trouble\n");
       +                return nil;
       +        }
       +        PBIT32(buf, n);
       +        if(readn(fs->fd, pkt+4, n-4) != n-4){
       +                free(pkt);
       +                return nil;
       +        }
       +#if 0
       +        if(pkt[4] == Ropenfd){
       +                /* do unix socket crap */
       +                sysfatal("no socket crap implemented");
       +        }
       +#endif
       +        return pkt;
       +}
 (DIR) diff --git a/src/libfs/fsimpl.h b/src/libfs/fsimpl.h
       t@@ -0,0 +1,41 @@
       +/* Copyright (C) 2003 Russ Cox, Massachusetts Institute of Technology */
       +/* See COPYRIGHT */
       +
       +typedef struct Queue Queue;
       +Queue *_fsqalloc(void);
       +int _fsqsend(Queue*, void*);
       +void *_fsqrecv(Queue*);
       +void _fsqhangup(Queue*);
       +void *_fsnbqrecv(Queue*);
       +
       +#include <mux.h>
       +struct Fsys
       +{
       +        char version[20];
       +        int msize;
       +        QLock lk;
       +        int fd;
       +        int ref;
       +        Mux mux;
       +        Fid *root;
       +        Queue *txq;
       +        Queue *rxq;
       +        Fid *freefid;
       +        int nextfid;
       +};
       +
       +struct Fid
       +{
       +        int fid;
       +        int mode;
       +        Fid *next;
       +        QLock lk;
       +        Fsys *fs;
       +        Qid qid;
       +        vlong offset;
       +};
       +
       +void _fsdecref(Fsys*);
       +void _fsputfid(Fid*);
       +Fid *_fsgetfid(Fsys*);
       +
 (DIR) diff --git a/src/libfs/mkfile b/src/libfs/mkfile
       t@@ -0,0 +1,22 @@
       +PLAN9=../..
       +<$PLAN9/src/mkhdr
       +
       +LIB=libfs.a
       +
       +OFILES=\
       +        close.$O\
       +        create.$O\
       +        dirread.$O\
       +        fs.$O\
       +        open.$O\
       +        read.$O\
       +        stat.$O\
       +        walk.$O\
       +        write.$O\
       +        wstat.$O\
       +
       +HFILES=\
       +        $PLAN9/include/fs.h\
       +        $PLAN9/include/mux.h\
       +
       +<$PLAN9/src/mksyslib
 (DIR) diff --git a/src/libfs/open.c b/src/libfs/open.c
       t@@ -0,0 +1,24 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <fcall.h>
       +#include <fs.h>
       +#include "fsimpl.h"
       +
       +Fid*
       +fsopen(Fsys *fs, char *name, int mode)
       +{
       +        Fid *fid;
       +        Fcall tx, rx;
       +
       +        if((fid = fswalk(fs->root, name)) == nil)
       +                return nil;
       +        tx.type = Topen;
       +        tx.fid = fid->fid;
       +        tx.mode = mode;
       +        if(fsrpc(fs, &tx, &rx, 0) < 0){
       +                fsclose(fid);
       +                return nil;
       +        }
       +        fid->mode = mode;
       +        return fid;
       +}
 (DIR) diff --git a/src/libfs/read.c b/src/libfs/read.c
       t@@ -0,0 +1,48 @@
       +/* Copyright (C) 2003 Russ Cox, Massachusetts Institute of Technology */
       +/* See COPYRIGHT */
       +
       +#include <u.h>
       +#include <libc.h>
       +#include <fcall.h>
       +#include <fs.h>
       +#include "fsimpl.h"
       +
       +long
       +fspread(Fid *fid, void *buf, long n, vlong offset)
       +{
       +        Fcall tx, rx;
       +        void *freep;
       +
       +        tx.type = Tread;
       +        if(offset == -1){
       +                qlock(&fid->lk);
       +                tx.offset = fid->offset;
       +                qunlock(&fid->lk);
       +        }else
       +                tx.offset = offset;
       +        tx.count = n;
       +
       +        fsrpc(fid->fs, &tx, &rx, &freep);
       +        if(rx.type == Rerror){
       +                werrstr("%s", rx.ename);
       +                free(freep);
       +                return -1;
       +        }
       +        if(rx.count){
       +                memmove(buf, rx.data, rx.count);
       +                if(offset == -1){
       +                        qlock(&fid->lk);
       +                        tx.offset += n;
       +                        qunlock(&fid->lk);
       +                }
       +        }
       +        free(freep);
       +        
       +        return rx.count;
       +}
       +
       +long
       +fsread(Fid *fid, void *buf, long n)
       +{
       +        return fspread(fid, buf, n, -1);
       +}
 (DIR) diff --git a/src/libfs/stat.c b/src/libfs/stat.c
       t@@ -0,0 +1,54 @@
       +/* Copyright (C) 2003 Russ Cox, Massachusetts Institute of Technology */
       +/* See COPYRIGHT */
       +
       +#include <u.h>
       +#include <libc.h>
       +#include <fcall.h>
       +#include <fs.h>
       +#include "fsimpl.h"
       +
       +Dir*
       +fsdirstat(Fsys *fs, char *name)
       +{
       +        Dir *d;
       +        Fid *fid;
       +
       +        if((fid = fswalk(fs->root, name)) == nil)
       +                return nil;
       +        
       +        d = fsdirfstat(fid);
       +        fsclose(fid);
       +        return d;
       +}
       +
       +Dir*
       +fsdirfstat(Fid *fid)
       +{
       +        Dir *d;
       +        Fsys *fs;
       +        Fcall tx, rx;
       +        void *freep;
       +        int n;
       +
       +        fs = fid->fs;
       +        tx.type = Tstat;
       +        tx.fid = fid->fid;
       +
       +        if(fsrpc(fs, &tx, &rx, &freep) < 0)
       +                return nil;
       +
       +        d = malloc(sizeof(Dir)+rx.nstat);
       +        if(d == nil){
       +                free(freep);
       +                return nil;
       +        }
       +        n = convM2D(rx.stat, rx.nstat, d, (char*)&d[1]);
       +        free(freep);
       +        if(n != rx.nstat){
       +                free(d);
       +                werrstr("rx.nstat and convM2D disagree about dir length");
       +                return nil;
       +        }
       +        return d;
       +}
       +
 (DIR) diff --git a/src/libfs/walk.c b/src/libfs/walk.c
       t@@ -0,0 +1,73 @@
       +/* Copyright (C) 2003 Russ Cox, Massachusetts Institute of Technology */
       +/* See COPYRIGHT */
       +
       +#include <u.h>
       +#include <libc.h>
       +#include <fcall.h>
       +#include <fs.h>
       +#include "fsimpl.h"
       +
       +Fid*
       +fswalk(Fid *fid, char *oname)
       +{
       +        char *freep, *name;
       +        int i, nwalk;
       +        char *p;
       +        Fid *wfid;
       +        Fcall tx, rx;
       +
       +        freep = nil;
       +        name = oname;
       +        if(name){
       +                freep = malloc(strlen(name)+1);
       +                if(freep == nil)
       +                        return nil;
       +                strcpy(freep, name);
       +                name = freep;
       +        }
       +
       +        if((wfid = _fsgetfid(fid->fs)) == nil){
       +                free(freep);
       +                return nil;
       +        }
       +
       +        nwalk = 0;
       +        do{
       +                /* collect names */
       +                for(i=0; name && *name && i < MAXWELEM; ){
       +                        p = name;
       +                        name = strchr(name, '/');
       +                        if(name)
       +                                *name++ = 0;
       +                        if(*p == 0)
       +                                continue;
       +                        tx.wname[i++] = p;
       +                }
       +
       +                /* do a walk */
       +                tx.type = Twalk;
       +                tx.fid = nwalk ? wfid->fid : fid->fid;
       +                tx.newfid = wfid->fid;
       +                tx.nwname = i;
       +                if(fsrpc(fid->fs, &tx, &rx, 0) < 0){
       +                Error:
       +                        free(freep);
       +                        if(nwalk)
       +                                fsclose(wfid);
       +                        else
       +                                _fsputfid(wfid);
       +                        return nil;
       +                }
       +                if(rx.nwqid != tx.nwname){
       +                        /* XXX lame error */
       +                        werrstr("file '%s' not found", oname);
       +                        goto Error;
       +                }
       +                if(rx.nwqid == 0)
       +                        wfid->qid = fid->qid;
       +                else
       +                        wfid->qid = rx.wqid[rx.nwqid-1];
       +                nwalk++;
       +        }while(name && *name);
       +        return wfid;
       +}
 (DIR) diff --git a/src/libfs/write.c b/src/libfs/write.c
       t@@ -0,0 +1,46 @@
       +/* Copyright (C) 2003 Russ Cox, Massachusetts Institute of Technology */
       +/* See COPYRIGHT */
       +
       +#include <u.h>
       +#include <libc.h>
       +#include <fcall.h>
       +#include <fs.h>
       +#include "fsimpl.h"
       +
       +long
       +fspwrite(Fid *fd, void *buf, long n, vlong offset)
       +{
       +        Fcall tx, rx;
       +        void *freep;
       +
       +        tx.type = Tread;
       +        if(offset == -1){
       +                qlock(&fd->lk);
       +                tx.offset = fd->offset;
       +                fd->offset += n;
       +                qunlock(&fd->lk);
       +        }else
       +                tx.offset = offset;
       +        tx.count = n;
       +        tx.data = buf;
       +
       +        fsrpc(fd->fs, &tx, &rx, &freep);
       +        if(rx.type == Rerror){
       +                if(offset == -1){
       +                        qlock(&fd->lk);
       +                        fd->offset -= n;
       +                        qunlock(&fd->lk);
       +                }
       +                werrstr("%s", rx.ename);
       +                free(freep);
       +                return -1;
       +        }
       +        free(freep);
       +        return rx.count;
       +}
       +
       +long
       +fswrite(Fid *fd, void *buf, long n)
       +{
       +        return fspwrite(fd, buf, n, -1);
       +}
 (DIR) diff --git a/src/libfs/wstat.c b/src/libfs/wstat.c
       t@@ -0,0 +1,49 @@
       +/* Copyright (C) 2003 Russ Cox, Massachusetts Institute of Technology */
       +/* See COPYRIGHT */
       +
       +#include <u.h>
       +#include <libc.h>
       +#include <fcall.h>
       +#include <fs.h>
       +#include "fsimpl.h"
       +
       +int
       +fsdirwstat(Fsys *fs, char *name, Dir *d)
       +{
       +        int n;
       +        Fid *fid;
       +
       +        if((fid = fswalk(fs->root, name)) == nil)
       +                return -1;
       +        
       +        n = fsdirfwstat(fid, d);
       +        fsclose(fid);
       +        return n;
       +}
       +
       +int
       +fsdirfwstat(Fid *fid, Dir *d)
       +{
       +        char *a;
       +        int n, nn;
       +        Fcall tx, rx;
       +
       +        n = sizeD2M(d);
       +        a = malloc(n);
       +        if(a == nil)
       +                return -1;
       +        nn = convD2M(d, a, n);
       +        if(n != nn){
       +                werrstr("convD2M and sizeD2M disagree");
       +                free(a);
       +                return -1;
       +        }
       +
       +        tx.type = Twstat;
       +        tx.fid = fid->fid;
       +        tx.stat = a;
       +        tx.nstat = n;
       +        n = fsrpc(fid->fs, &tx, &rx, 0);
       +        free(a);
       +        return n;
       +}
 (DIR) diff --git a/src/libmux/COPYRIGHT b/src/libmux/COPYRIGHT
       t@@ -0,0 +1,27 @@
       +
       +This software was developed as part of a project at MIT:
       +        /sys/src/libmux/*
       +        /sys/include/mux.h
       +
       +Copyright (c) 2003 Russ Cox,
       +                   Massachusetts Institute of Technology
       +
       +Permission is hereby granted, free of charge, to any person obtaining
       +a copy of this software and associated documentation files (the
       +"Software"), to deal in the Software without restriction, including
       +without limitation the rights to use, copy, modify, merge, publish,
       +distribute, sublicense, and/or sell copies of the Software, and to
       +permit persons to whom the Software is furnished to do so, subject to
       +the following conditions:
       +
       +The above copyright notice and this permission notice shall be
       +included in all copies or substantial portions of the Software.
       +
       +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
       +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
       +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
       +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
       +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
       +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
       +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
       +
 (DIR) diff --git a/src/libmux/io.c b/src/libmux/io.c
       t@@ -0,0 +1,136 @@
       +/* Copyright (C) 2003 Russ Cox, Massachusetts Institute of Technology */
       +/* See COPYRIGHT */
       +
       +#include <u.h>
       +#include <libc.h>
       +#include <mux.h>
       +
       +/*
       + * If you fork off two procs running muxrecvproc and muxsendproc,
       + * then muxrecv/muxsend (and thus muxrpc) will never block except on 
       + * rendevouses, which is nice when it's running in one thread of many.
       + */
       +void
       +_muxrecvproc(void *v)
       +{
       +        void *p;
       +        Mux *mux;
       +        Muxqueue *q;
       +
       +        mux = v;
       +        q = _muxqalloc();
       +
       +        qlock(&mux->lk);
       +        mux->readq = q;
       +        qlock(&mux->inlk);
       +        rwakeup(&mux->rpcfork);
       +        qunlock(&mux->lk);
       +
       +        while((p = mux->recv(mux)) != nil)
       +                if(_muxqsend(q, p) < 0){
       +                        free(p);
       +                        break;
       +                }
       +        qunlock(&mux->inlk);
       +        qlock(&mux->lk);
       +        _muxqhangup(q);
       +        while((p = _muxnbqrecv(q)) != nil)
       +                free(p);
       +        free(q);
       +        mux->readq = nil;
       +        rwakeup(&mux->rpcfork);
       +        qunlock(&mux->lk);
       +}
       +
       +void
       +_muxsendproc(void *v)
       +{
       +        Muxqueue *q;
       +        void *p;
       +        Mux *mux;
       +
       +        mux = v;
       +        q = _muxqalloc();
       +
       +        qlock(&mux->lk);
       +        mux->writeq = q;
       +        qlock(&mux->outlk);
       +        rwakeup(&mux->rpcfork);
       +        qunlock(&mux->lk);
       +
       +        while((p = _muxqrecv(q)) != nil)
       +                if(mux->send(mux, p) < 0)
       +                        break;
       +        qunlock(&mux->outlk);
       +        qlock(&mux->lk);
       +        _muxqhangup(q);
       +        while((p = _muxnbqrecv(q)) != nil)
       +                free(p);
       +        free(q);
       +        mux->writeq = nil;
       +        rwakeup(&mux->rpcfork);
       +        qunlock(&mux->lk);
       +        return;
       +}
       +
       +void*
       +_muxrecv(Mux *mux)
       +{
       +        void *p;
       +
       +        qlock(&mux->lk);
       +/*
       +        if(mux->state != VtStateConnected){
       +                werrstr("not connected");
       +                qunlock(&mux->lk);
       +                return nil;
       +        }
       +*/
       +        if(mux->readq){
       +                qunlock(&mux->lk);
       +                return _muxqrecv(mux->readq);
       +        }
       +
       +        qlock(&mux->inlk);
       +        qunlock(&mux->lk);
       +        p = mux->recv(mux);
       +        qunlock(&mux->inlk);
       +/*
       +        if(!p)
       +                vthangup(mux);
       +*/
       +        return p;
       +}
       +
       +int
       +_muxsend(Mux *mux, void *p)
       +{
       +        qlock(&mux->lk);
       +/*
       +        if(mux->state != VtStateConnected){
       +                packetfree(p);
       +                werrstr("not connected");
       +                qunlock(&mux->lk);
       +                return -1;
       +        }
       +*/
       +        if(mux->writeq){
       +                qunlock(&mux->lk);
       +                if(_muxqsend(mux->writeq, p) < 0){
       +                        free(p);
       +                        return -1;
       +                }
       +                return 0;
       +        }
       +
       +        qlock(&mux->outlk);
       +        qunlock(&mux->lk);
       +        if(mux->send(mux, p) < 0){
       +                qunlock(&mux->outlk);
       +                /* vthangup(mux); */
       +                return -1;        
       +        }
       +        qunlock(&mux->outlk);
       +        return 0;
       +}
       +
 (DIR) diff --git a/src/libmux/mkfile b/src/libmux/mkfile
       t@@ -0,0 +1,16 @@
       +PLAN9=../..
       +<$PLAN9/src/mkhdr
       +
       +LIB=libmux.a
       +
       +OFILES=\
       +        io.$O\
       +        mux.$O\
       +        queue.$O\
       +        thread.$O\
       +
       +HFILES=\
       +        $PLAN9/include/mux.h\
       +
       +<$PLAN9/src/mksyslib
       +
 (DIR) diff --git a/src/libmux/mux.c b/src/libmux/mux.c
       t@@ -0,0 +1,152 @@
       +/* Copyright (C) 2003 Russ Cox, Massachusetts Institute of Technology */
       +/* See COPYRIGHT */
       +
       +/*
       + * Generic RPC packet multiplexor.  Inspired by but not derived from
       + * Plan 9 kernel.  Originally developed as part of Tra, later used in
       + * libnventi, and then finally split out into a generic library.
       + */
       +
       +#include <u.h>
       +#include <libc.h>
       +#include <mux.h>
       +
       +static int gettag(Mux*, Muxrpc*);
       +static void puttag(Mux*, Muxrpc*);
       +static void enqueue(Mux*, Muxrpc*);
       +static void dequeue(Mux*, Muxrpc*);
       +
       +void
       +muxinit(Mux *mux)
       +{
       +        mux->tagrend.l = &mux->lk;
       +        mux->sleep.next = &mux->sleep;
       +        mux->sleep.prev = &mux->sleep;
       +}
       +
       +void*
       +muxrpc(Mux *mux, void *tx)
       +{
       +        uint tag;
       +        Muxrpc *r, *r2;
       +        void *p;
       +
       +        /* must malloc because stack could be private */
       +        r = mallocz(sizeof(Muxrpc), 1);
       +        if(r == nil)
       +                return nil;
       +        r->r.l = &mux->lk;
       +
       +        /* assign the tag */
       +        tag = gettag(mux, r);
       +        if(mux->settag(mux, tx, tag) < 0){
       +                puttag(mux, r);
       +                free(r);
       +                return nil;
       +        }
       +
       +        /* send the packet */
       +        if(_muxsend(mux, tx) < 0){
       +                puttag(mux, r);
       +                free(r);
       +                return nil;
       +        }
       +
       +        /* add ourselves to sleep queue */
       +        qlock(&mux->lk);
       +        enqueue(mux, r);
       +
       +        /* wait for our packet */
       +        while(mux->muxer && !r->p)
       +                rsleep(&r->r);
       +
       +        /* if not done, there's no muxer: start muxing */
       +        if(!r->p){
       +                if(mux->muxer)
       +                        abort();
       +                mux->muxer = 1;
       +                while(!r->p){
       +                        qunlock(&mux->lk);
       +                        p = _muxrecv(mux);
       +                        if(p)
       +                                tag = mux->gettag(mux, p);
       +                        else
       +                                tag = ~0;
       +                        qlock(&mux->lk);
       +                        if(p == nil){        /* eof -- just give up and pass the buck */
       +                                dequeue(mux, r);
       +                                break;
       +                        }
       +                        /* hand packet to correct sleeper */
       +                        if(tag < 0 || tag >= mux->mwait){
       +                                fprint(2, "%s: bad rpc tag %ux\n", argv0, tag);
       +                                /* must leak packet! don't know how to free it! */
       +                                continue;
       +                        }
       +                        r2 = mux->wait[tag];
       +                        r2->p = p;
       +                        rwakeup(&r2->r);
       +                }
       +                mux->muxer = 0;
       +
       +                /* if there is anyone else sleeping, wake them to mux */
       +                if(mux->sleep.next != &mux->sleep)
       +                        rwakeup(&mux->sleep.next->r);
       +        }
       +        p = r->p;
       +        puttag(mux, r);
       +        free(r);
       +        qunlock(&mux->lk);
       +        return p;
       +}
       +
       +static void
       +enqueue(Mux *mux, Muxrpc *r)
       +{
       +        r->next = mux->sleep.next;
       +        r->prev = &mux->sleep;
       +        r->next->prev = r;
       +        r->prev->next = r;
       +}
       +
       +static void
       +dequeue(Mux *mux, Muxrpc *r)
       +{
       +        r->next->prev = r->prev;
       +        r->prev->next = r->next;
       +        r->prev = nil;
       +        r->next = nil;
       +}
       +
       +static int 
       +gettag(Mux *mux, Muxrpc *r)
       +{
       +        int i;
       +
       +Again:
       +        while(mux->nwait == mux->mwait)
       +                rsleep(&mux->tagrend);
       +        i=mux->freetag;
       +        if(mux->wait[i] == 0)
       +                goto Found;
       +        for(i=0; i<mux->mwait; i++)
       +                if(mux->wait[i] == 0){
       +                Found:
       +                        mux->nwait++;
       +                        mux->wait[i] = r;
       +                        r->tag = i;
       +                        return i;
       +                }
       +        fprint(2, "libfs: nwait botch\n");
       +        goto Again;
       +}
       +
       +static void
       +puttag(Mux *mux, Muxrpc *r)
       +{
       +        assert(mux->wait[r->tag] == r);
       +        mux->wait[r->tag] = nil;
       +        mux->nwait--;
       +        mux->freetag = r->tag;
       +        rwakeup(&mux->tagrend);
       +}
 (DIR) diff --git a/src/libmux/queue.c b/src/libmux/queue.c
       t@@ -0,0 +1,109 @@
       +/* Copyright (C) 2003 Russ Cox, Massachusetts Institute of Technology */
       +/* See COPYRIGHT */
       +
       +#include <u.h>
       +#include <libc.h>
       +#include <mux.h>
       +
       +typedef struct Qel Qel;
       +struct Qel
       +{
       +        Qel *next;
       +        void *p;
       +};
       +
       +struct Muxqueue
       +{
       +        int hungup;
       +        QLock lk;
       +        Rendez r;
       +        Qel *head;
       +        Qel *tail;
       +};
       +
       +Muxqueue*
       +_muxqalloc(void)
       +{
       +        Muxqueue *q;
       +
       +        q = mallocz(sizeof(Muxqueue), 1);
       +        if(q == nil)
       +                return nil;
       +        q->r.l = &q->lk;
       +        return q;
       +}
       +
       +int
       +_muxqsend(Muxqueue *q, void *p)
       +{
       +        Qel *e;
       +
       +        e = malloc(sizeof(Qel));
       +        if(e == nil)
       +                return -1;
       +        qlock(&q->lk);
       +        if(q->hungup){
       +                werrstr("hungup queue");
       +                qunlock(&q->lk);
       +                return -1;
       +        }
       +        e->p = p;
       +        e->next = nil;
       +        if(q->head == nil)
       +                q->head = e;
       +        else
       +                q->tail->next = e;
       +        q->tail = e;
       +        rwakeup(&q->r);
       +        qunlock(&q->lk);
       +        return 0;
       +}
       +
       +void*
       +_muxqrecv(Muxqueue *q)
       +{
       +        void *p;
       +        Qel *e;
       +
       +        qlock(&q->lk);
       +        while(q->head == nil && !q->hungup)
       +                rsleep(&q->r);
       +        if(q->hungup){
       +                qunlock(&q->lk);
       +                return nil;
       +        }
       +        e = q->head;
       +        q->head = e->next;
       +        qunlock(&q->lk);
       +        p = e->p;
       +        free(e);
       +        return p;
       +}
       +
       +void*
       +_muxnbqrecv(Muxqueue *q)
       +{
       +        void *p;
       +        Qel *e;
       +
       +        qlock(&q->lk);
       +        if(q->head == nil){
       +                qunlock(&q->lk);
       +                return nil;
       +        }
       +        e = q->head;
       +        q->head = e->next;
       +        qunlock(&q->lk);
       +        p = e->p;
       +        free(e);
       +        return p;
       +}
       +
       +void
       +_muxqhangup(Muxqueue *q)
       +{
       +        qlock(&q->lk);
       +        q->hungup = 1;
       +        rwakeupall(&q->r);
       +        qunlock(&q->lk);
       +}
 (DIR) diff --git a/src/libmux/thread.c b/src/libmux/thread.c
       t@@ -0,0 +1,27 @@
       +/* Copyright (C) 2003 Russ Cox, Massachusetts Institute of Technology */
       +/* See COPYRIGHT */
       +
       +#include <u.h>
       +#include <libc.h>
       +#include <thread.h>
       +#include <mux.h>
       +
       +enum
       +{
       +        STACK = 32768
       +};
       +
       +void
       +muxthreads(Mux *mux)
       +{
       +        proccreate(_muxrecvproc, mux, STACK);
       +        qlock(&mux->lk);
       +        while(!mux->writeq)
       +                rsleep(&mux->rpcfork);
       +        qunlock(&mux->lk);
       +        proccreate(_muxsendproc, mux, STACK);
       +        qlock(&mux->lk);
       +        while(!mux->writeq)
       +                rsleep(&mux->rpcfork);
       +        qunlock(&mux->lk);
       +}