aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorrsc <devnull@localhost>2003-12-06 18:08:52 +0000
committerrsc <devnull@localhost>2003-12-06 18:08:52 +0000
commitd3df308747ee4d1fcc063a348dcf1146b390bda7 (patch)
treea204b027256ec29b110caaa86100cbd701808b5b /src
parente97ceade5e1bba5787e39429384336fa37797906 (diff)
downloadplan9port-d3df308747ee4d1fcc063a348dcf1146b390bda7.tar.gz
plan9port-d3df308747ee4d1fcc063a348dcf1146b390bda7.tar.bz2
plan9port-d3df308747ee4d1fcc063a348dcf1146b390bda7.zip
File system stuff.
Diffstat (limited to 'src')
-rw-r--r--src/cmd/9p.c191
-rw-r--r--src/cmd/9pserve.c597
-rw-r--r--src/lib9/convD2M.c95
-rw-r--r--src/lib9/convM2D.c94
-rw-r--r--src/lib9/convM2S.c315
-rw-r--r--src/lib9/convS2M.c386
-rw-r--r--src/lib9/fcallfmt.c234
-rw-r--r--src/lib9/read9pmsg.c31
-rw-r--r--src/libfs/COPYRIGHT27
-rw-r--r--src/libfs/close.c26
-rw-r--r--src/libfs/create.c25
-rw-r--r--src/libfs/dirread.c100
-rw-r--r--src/libfs/fs.c276
-rw-r--r--src/libfs/fsimpl.h41
-rw-r--r--src/libfs/mkfile22
-rw-r--r--src/libfs/open.c24
-rw-r--r--src/libfs/read.c48
-rw-r--r--src/libfs/stat.c54
-rw-r--r--src/libfs/walk.c73
-rw-r--r--src/libfs/write.c46
-rw-r--r--src/libfs/wstat.c49
-rw-r--r--src/libmux/COPYRIGHT27
-rw-r--r--src/libmux/io.c136
-rw-r--r--src/libmux/mkfile16
-rw-r--r--src/libmux/mux.c152
-rw-r--r--src/libmux/queue.c109
-rw-r--r--src/libmux/thread.c27
27 files changed, 3221 insertions, 0 deletions
diff --git a/src/cmd/9p.c b/src/cmd/9p.c
new file mode 100644
index 00000000..7c018c34
--- /dev/null
+++ b/src/cmd/9p.c
@@ -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);
+}
diff --git a/src/cmd/9pserve.c b/src/cmd/9pserve.c
new file mode 100644
index 00000000..40db17e5
--- /dev/null
+++ b/src/cmd/9pserve.c
@@ -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;
+}
diff --git a/src/lib9/convD2M.c b/src/lib9/convD2M.c
new file mode 100644
index 00000000..5acee7e5
--- /dev/null
+++ b/src/lib9/convD2M.c
@@ -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;
+}
diff --git a/src/lib9/convM2D.c b/src/lib9/convM2D.c
new file mode 100644
index 00000000..6f4b4bd9
--- /dev/null
+++ b/src/lib9/convM2D.c
@@ -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;
+}
diff --git a/src/lib9/convM2S.c b/src/lib9/convM2S.c
new file mode 100644
index 00000000..fcdcd42d
--- /dev/null
+++ b/src/lib9/convM2S.c
@@ -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;
+}
diff --git a/src/lib9/convS2M.c b/src/lib9/convS2M.c
new file mode 100644
index 00000000..9acdcfa5
--- /dev/null
+++ b/src/lib9/convS2M.c
@@ -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;
+}
diff --git a/src/lib9/fcallfmt.c b/src/lib9/fcallfmt.c
new file mode 100644
index 00000000..4eef88de
--- /dev/null
+++ b/src/lib9/fcallfmt.c
@@ -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;
+}
diff --git a/src/lib9/read9pmsg.c b/src/lib9/read9pmsg.c
new file mode 100644
index 00000000..9e90ec5d
--- /dev/null
+++ b/src/lib9/read9pmsg.c
@@ -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;
+}
diff --git a/src/libfs/COPYRIGHT b/src/libfs/COPYRIGHT
new file mode 100644
index 00000000..de348d12
--- /dev/null
+++ b/src/libfs/COPYRIGHT
@@ -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.
+
diff --git a/src/libfs/close.c b/src/libfs/close.c
new file mode 100644
index 00000000..23388ae3
--- /dev/null
+++ b/src/libfs/close.c
@@ -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);
+}
diff --git a/src/libfs/create.c b/src/libfs/create.c
new file mode 100644
index 00000000..ef52a2a8
--- /dev/null
+++ b/src/libfs/create.c
@@ -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;
+}
diff --git a/src/libfs/dirread.c b/src/libfs/dirread.c
new file mode 100644
index 00000000..0ca40645
--- /dev/null
+++ b/src/libfs/dirread.c
@@ -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;
+}
diff --git a/src/libfs/fs.c b/src/libfs/fs.c
new file mode 100644
index 00000000..985071c1
--- /dev/null
+++ b/src/libfs/fs.c
@@ -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;
+}
diff --git a/src/libfs/fsimpl.h b/src/libfs/fsimpl.h
new file mode 100644
index 00000000..fbcc3777
--- /dev/null
+++ b/src/libfs/fsimpl.h
@@ -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*);
+
diff --git a/src/libfs/mkfile b/src/libfs/mkfile
new file mode 100644
index 00000000..acfb0ae5
--- /dev/null
+++ b/src/libfs/mkfile
@@ -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
diff --git a/src/libfs/open.c b/src/libfs/open.c
new file mode 100644
index 00000000..458c5dac
--- /dev/null
+++ b/src/libfs/open.c
@@ -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;
+}
diff --git a/src/libfs/read.c b/src/libfs/read.c
new file mode 100644
index 00000000..ca6c628c
--- /dev/null
+++ b/src/libfs/read.c
@@ -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);
+}
diff --git a/src/libfs/stat.c b/src/libfs/stat.c
new file mode 100644
index 00000000..e55e0b12
--- /dev/null
+++ b/src/libfs/stat.c
@@ -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;
+}
+
diff --git a/src/libfs/walk.c b/src/libfs/walk.c
new file mode 100644
index 00000000..ad4eddc7
--- /dev/null
+++ b/src/libfs/walk.c
@@ -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;
+}
diff --git a/src/libfs/write.c b/src/libfs/write.c
new file mode 100644
index 00000000..96ecfa86
--- /dev/null
+++ b/src/libfs/write.c
@@ -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);
+}
diff --git a/src/libfs/wstat.c b/src/libfs/wstat.c
new file mode 100644
index 00000000..90ae0e0f
--- /dev/null
+++ b/src/libfs/wstat.c
@@ -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;
+}
diff --git a/src/libmux/COPYRIGHT b/src/libmux/COPYRIGHT
new file mode 100644
index 00000000..d9679c3a
--- /dev/null
+++ b/src/libmux/COPYRIGHT
@@ -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.
+
diff --git a/src/libmux/io.c b/src/libmux/io.c
new file mode 100644
index 00000000..3d932b1a
--- /dev/null
+++ b/src/libmux/io.c
@@ -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;
+}
+
diff --git a/src/libmux/mkfile b/src/libmux/mkfile
new file mode 100644
index 00000000..71d62a43
--- /dev/null
+++ b/src/libmux/mkfile
@@ -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
+
diff --git a/src/libmux/mux.c b/src/libmux/mux.c
new file mode 100644
index 00000000..0d33498e
--- /dev/null
+++ b/src/libmux/mux.c
@@ -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);
+}
diff --git a/src/libmux/queue.c b/src/libmux/queue.c
new file mode 100644
index 00000000..072f1860
--- /dev/null
+++ b/src/libmux/queue.c
@@ -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);
+}
diff --git a/src/libmux/thread.c b/src/libmux/thread.c
new file mode 100644
index 00000000..1c643e06
--- /dev/null
+++ b/src/libmux/thread.c
@@ -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);
+}