aboutsummaryrefslogtreecommitdiff
path: root/src/lib9p
diff options
context:
space:
mode:
authorrsc <devnull@localhost>2004-03-21 04:33:13 +0000
committerrsc <devnull@localhost>2004-03-21 04:33:13 +0000
commit2277c5d7bbe1f9595fad512d8f790708473a9bf1 (patch)
tree4d653e13906f1971d3170dba6dbe0fbf92eb48d6 /src/lib9p
parenta770daa795754cb600ad3fab2fdd2961147006c4 (diff)
downloadplan9port-2277c5d7bbe1f9595fad512d8f790708473a9bf1.tar.gz
plan9port-2277c5d7bbe1f9595fad512d8f790708473a9bf1.tar.bz2
plan9port-2277c5d7bbe1f9595fad512d8f790708473a9bf1.zip
Small tweaks
Lots of new code imported.
Diffstat (limited to 'src/lib9p')
-rw-r--r--src/lib9p/_post.c77
-rw-r--r--src/lib9p/dirread.c40
-rw-r--r--src/lib9p/fid.c81
-rw-r--r--src/lib9p/file.c372
-rw-r--r--src/lib9p/ftest.c29
-rw-r--r--src/lib9p/intmap.c166
-rw-r--r--src/lib9p/mem.c49
-rw-r--r--src/lib9p/mkfile20
-rw-r--r--src/lib9p/parse.c115
-rw-r--r--src/lib9p/post.c24
-rw-r--r--src/lib9p/post.h13
-rw-r--r--src/lib9p/ramfs.c163
-rw-r--r--src/lib9p/req.c112
-rw-r--r--src/lib9p/srv.c837
-rw-r--r--src/lib9p/tpost.c18
-rw-r--r--src/lib9p/uid.c34
-rw-r--r--src/lib9p/util.c25
17 files changed, 2175 insertions, 0 deletions
diff --git a/src/lib9p/_post.c b/src/lib9p/_post.c
new file mode 100644
index 00000000..e8313be1
--- /dev/null
+++ b/src/lib9p/_post.c
@@ -0,0 +1,77 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+#include <auth.h>
+#include "post.h"
+
+Postcrud*
+_post1(Srv *s, char *name, char *mtpt, int flag)
+{
+ Postcrud *p;
+
+ p = emalloc9p(sizeof *p);
+ if(!s->nopipe){
+ if(pipe(p->fd) < 0)
+ sysfatal("pipe: %r");
+ s->infd = s->outfd = p->fd[1];
+ s->srvfd = p->fd[0];
+ }
+ if(name)
+ if(postfd(name, s->srvfd) < 0)
+ sysfatal("postfd %s: %r", name);
+ p->s = s;
+ p->mtpt = mtpt;
+ p->flag = flag;
+ return p;
+}
+
+void
+_post2(void *v)
+{
+ Srv *s;
+
+ s = v;
+ rfork(RFNOTEG);
+ if(!s->leavefdsopen){
+ rendezvous((ulong)s, 0);
+ close(s->srvfd);
+ }
+ srv(s);
+}
+
+void
+_post3(Postcrud *p)
+{
+ /*
+ * Normally the server is posting as the last thing it does
+ * before exiting, so the correct thing to do is drop into
+ * a different fd space and close the 9P server half of the
+ * pipe before trying to mount the kernel half. This way,
+ * if the file server dies, we don't have a ref to the 9P server
+ * half of the pipe. Then killing the other procs will drop
+ * all the refs on the 9P server half, and the mount will fail.
+ * Otherwise the mount hangs forever.
+ *
+ * Libthread in general and acme win in particular make
+ * it hard to make this fd bookkeeping work out properly,
+ * so leaveinfdopen is a flag that win sets to opt out of this
+ * safety net.
+ */
+ if(!p->s->leavefdsopen){
+ rfork(RFFDG);
+ rendezvous((ulong)p->s, 0);
+ close(p->s->infd);
+ if(p->s->infd != p->s->outfd)
+ close(p->s->outfd);
+ }
+
+ if(p->mtpt){
+ if(amount(p->s->srvfd, p->mtpt, p->flag, "") == -1)
+ sysfatal("mount %s: %r", p->mtpt);
+ }else
+ close(p->s->srvfd);
+ free(p);
+}
+
diff --git a/src/lib9p/dirread.c b/src/lib9p/dirread.c
new file mode 100644
index 00000000..7fb88305
--- /dev/null
+++ b/src/lib9p/dirread.c
@@ -0,0 +1,40 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+
+void
+dirread9p(Req *r, Dirgen *gen, void *aux)
+{
+ int start;
+ uchar *p, *ep;
+ uint rv;
+ Dir d;
+
+ if(r->ifcall.offset == 0)
+ start = 0;
+ else
+ start = r->fid->dirindex;
+
+ p = (uchar*)r->ofcall.data;
+ ep = p+r->ifcall.count;
+
+ while(p < ep){
+ memset(&d, 0, sizeof d);
+ if((*gen)(start, &d, aux) < 0)
+ break;
+ rv = convD2M(&d, p, ep-p);
+ free(d.name);
+ free(d.muid);
+ free(d.uid);
+ free(d.gid);
+ if(rv <= BIT16SZ)
+ break;
+ p += rv;
+ start++;
+ }
+ r->fid->dirindex = start;
+ r->ofcall.count = p - (uchar*)r->ofcall.data;
+}
diff --git a/src/lib9p/fid.c b/src/lib9p/fid.c
new file mode 100644
index 00000000..2393f1c4
--- /dev/null
+++ b/src/lib9p/fid.c
@@ -0,0 +1,81 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <fcall.h>
+#include <thread.h>
+#include "9p.h"
+
+static void
+incfidref(void *v)
+{
+ Fid *f;
+
+ f = v;
+ if(f)
+ incref(&f->ref);
+}
+
+Fidpool*
+allocfidpool(void (*destroy)(Fid*))
+{
+ Fidpool *f;
+
+ f = emalloc9p(sizeof *f);
+ f->map = allocmap(incfidref);
+ f->destroy = destroy;
+ return f;
+}
+
+void
+freefidpool(Fidpool *p)
+{
+ freemap(p->map, (void(*)(void*))p->destroy);
+ free(p);
+}
+
+Fid*
+allocfid(Fidpool *pool, ulong fid)
+{
+ Fid *f;
+
+ f = emalloc9p(sizeof *f);
+ f->fid = fid;
+ f->omode = -1;
+ f->pool = pool;
+
+ incfidref(f);
+ incfidref(f);
+ if(caninsertkey(pool->map, fid, f) == 0){
+ closefid(f);
+ return nil;
+ }
+
+ return f;
+}
+
+Fid*
+lookupfid(Fidpool *pool, ulong fid)
+{
+ return lookupkey(pool->map, fid);
+}
+
+void
+closefid(Fid *f)
+{
+ if(decref(&f->ref) == 0) {
+ if(f->rdir)
+ closedirfile(f->rdir);
+ if(f->pool->destroy)
+ f->pool->destroy(f);
+ if(f->file)
+ closefile(f->file);
+ free(f->uid);
+ free(f);
+ }
+}
+
+Fid*
+removefid(Fidpool *pool, ulong fid)
+{
+ return deletekey(pool->map, fid);
+}
diff --git a/src/lib9p/file.c b/src/lib9p/file.c
new file mode 100644
index 00000000..79a8a11d
--- /dev/null
+++ b/src/lib9p/file.c
@@ -0,0 +1,372 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+
+/*
+ * To avoid deadlock, the following rules must be followed.
+ * Always lock child then parent, never parent then child.
+ * If holding the free file lock, do not lock any Files.
+ */
+struct Filelist {
+ File *f;
+ Filelist *link;
+};
+
+static QLock filelk;
+static File *freefilelist;
+
+static File*
+allocfile(void)
+{
+ int i, a;
+ File *f;
+ enum { N = 16 };
+
+ qlock(&filelk);
+ if(freefilelist == nil){
+ f = emalloc9p(N*sizeof(*f));
+ for(i=0; i<N-1; i++)
+ f[i].aux = &f[i+1];
+ f[N-1].aux = nil;
+ f[0].allocd = 1;
+ freefilelist = f;
+ }
+
+ f = freefilelist;
+ freefilelist = f->aux;
+ qunlock(&filelk);
+
+ a = f->allocd;
+ memset(f, 0, sizeof *f);
+ f->allocd = a;
+ return f;
+}
+
+static void
+freefile(File *f)
+{
+ Filelist *fl, *flnext;
+
+ for(fl=f->filelist; fl; fl=flnext){
+ flnext = fl->link;
+ assert(fl->f == nil);
+ free(fl);
+ }
+
+ free(f->dir.name);
+ free(f->dir.uid);
+ free(f->dir.gid);
+ free(f->dir.muid);
+ qlock(&filelk);
+ assert(f->ref.ref == 0);
+ f->aux = freefilelist;
+ freefilelist = f;
+ qunlock(&filelk);
+}
+
+void
+closefile(File *f)
+{
+ if(decref(&f->ref) == 0){
+ f->tree->destroy(f);
+ freefile(f);
+ }
+}
+
+static void
+nop(File *f)
+{
+ USED(f);
+}
+
+int
+removefile(File *f)
+{
+ File *fp;
+ Filelist *fl;
+
+ fp = f->parent;
+ if(fp == nil){
+ werrstr("no parent");
+ closefile(f);
+ return -1;
+ }
+
+ if(fp == f){
+ werrstr("cannot remove root");
+ closefile(f);
+ return -1;
+ }
+
+ wlock(&fp->rwlock);
+ wlock(&f->rwlock);
+ if(f->nchild != 0){
+ werrstr("has children");
+ wunlock(&f->rwlock);
+ wunlock(&fp->rwlock);
+ closefile(f);
+ return -1;
+ }
+
+ if(f->parent != fp){
+ werrstr("parent changed underfoot");
+ wunlock(&f->rwlock);
+ wunlock(&fp->rwlock);
+ closefile(f);
+ return -1;
+ }
+
+ for(fl=fp->filelist; fl; fl=fl->link)
+ if(fl->f == f)
+ break;
+ assert(fl != nil && fl->f == f);
+
+ fl->f = nil;
+ fp->nchild--;
+ f->parent = nil;
+ wunlock(&fp->rwlock);
+ wunlock(&f->rwlock);
+
+ closefile(fp); /* reference from child */
+ closefile(f); /* reference from tree */
+ closefile(f);
+ return 0;
+}
+
+File*
+createfile(File *fp, char *name, char *uid, ulong perm, void *aux)
+{
+ File *f;
+ Filelist *fl, *freel;
+ Tree *t;
+
+ if((fp->dir.qid.type&QTDIR) == 0){
+ werrstr("create in non-directory");
+ return nil;
+ }
+
+ freel = nil;
+ wlock(&fp->rwlock);
+ for(fl=fp->filelist; fl; fl=fl->link){
+ if(fl->f == nil)
+ freel = fl;
+ else if(strcmp(fl->f->dir.name, name) == 0){
+ wunlock(&fp->rwlock);
+ werrstr("file already exists");
+ return nil;
+ }
+ }
+
+ if(freel == nil){
+ freel = emalloc9p(sizeof *freel);
+ freel->link = fp->filelist;
+ fp->filelist = freel;
+ }
+
+ f = allocfile();
+ f->dir.name = estrdup9p(name);
+ f->dir.uid = estrdup9p(uid ? uid : fp->dir.uid);
+ f->dir.gid = estrdup9p(fp->dir.gid);
+ f->dir.muid = estrdup9p(uid ? uid : "unknown");
+ f->aux = aux;
+ f->dir.mode = perm;
+
+ t = fp->tree;
+ lock(&t->genlock);
+ f->dir.qid.path = t->qidgen++;
+ unlock(&t->genlock);
+ if(perm & DMDIR)
+ f->dir.qid.type |= QTDIR;
+ if(perm & DMAPPEND)
+ f->dir.qid.type |= QTAPPEND;
+ if(perm & DMEXCL)
+ f->dir.qid.type |= QTEXCL;
+
+ f->dir.mode = perm;
+ f->dir.atime = f->dir.mtime = time(0);
+ f->dir.length = 0;
+ f->parent = fp;
+ incref(&fp->ref);
+ f->tree = fp->tree;
+
+ incref(&f->ref); /* being returned */
+ incref(&f->ref); /* for the tree */
+ freel->f = f;
+ fp->nchild++;
+ wunlock(&fp->rwlock);
+
+ return f;
+}
+
+static File*
+walkfile1(File *dir, char *elem)
+{
+ File *fp;
+ Filelist *fl;
+
+ rlock(&dir->rwlock);
+ if(strcmp(elem, "..") == 0){
+ fp = dir->parent;
+ incref(&fp->ref);
+ runlock(&dir->rwlock);
+ closefile(dir);
+ return fp;
+ }
+
+ fp = nil;
+ for(fl=dir->filelist; fl; fl=fl->link)
+ if(fl->f && strcmp(fl->f->dir.name, elem)==0){
+ fp = fl->f;
+ incref(&fp->ref);
+ break;
+ }
+
+ runlock(&dir->rwlock);
+ closefile(dir);
+ return fp;
+}
+
+File*
+walkfile(File *f, char *path)
+{
+ char *os, *s, *nexts;
+ File *nf;
+
+ if(strchr(path, '/') == nil)
+ return walkfile1(f, path); /* avoid malloc */
+
+ os = s = estrdup9p(path);
+ incref(&f->ref);
+ for(; *s; s=nexts){
+ if(nexts = strchr(s, '/'))
+ *nexts++ = '\0';
+ else
+ nexts = s+strlen(s);
+ nf = walkfile1(f, s);
+ decref(&f->ref);
+ f = nf;
+ if(f == nil)
+ break;
+ }
+ free(os);
+ return f;
+}
+
+Tree*
+alloctree(char *uid, char *gid, ulong mode, void (*destroy)(File*))
+{
+ char *muid;
+ Tree *t;
+ File *f;
+
+ t = emalloc9p(sizeof *t);
+ f = allocfile();
+ f->dir.name = estrdup9p("/");
+ if(uid == nil){
+ if(uid = getuser())
+ uid = estrdup9p(uid);
+ }
+ if(uid == nil)
+ uid = estrdup9p("none");
+ else
+ uid = estrdup9p(uid);
+
+ if(gid == nil)
+ gid = estrdup9p(uid);
+ else
+ gid = estrdup9p(gid);
+
+ muid = estrdup9p(uid);
+
+ f->dir.qid = (Qid){0, 0, QTDIR};
+ f->dir.length = 0;
+ f->dir.atime = f->dir.mtime = time(0);
+ f->dir.mode = DMDIR | mode;
+ f->tree = t;
+ f->parent = f;
+ f->dir.uid = uid;
+ f->dir.gid = gid;
+ f->dir.muid = muid;
+
+ incref(&f->ref);
+ t->root = f;
+ t->qidgen = 0;
+ t->dirqidgen = 1;
+ if(destroy == nil)
+ destroy = nop;
+ t->destroy = destroy;
+
+ return t;
+}
+
+static void
+_freefiles(File *f)
+{
+ Filelist *fl, *flnext;
+
+ for(fl=f->filelist; fl; fl=flnext){
+ flnext = fl->link;
+ _freefiles(fl->f);
+ free(fl);
+ }
+
+ f->tree->destroy(f);
+ freefile(f);
+}
+
+void
+freetree(Tree *t)
+{
+ _freefiles(t->root);
+ free(t);
+}
+
+struct Readdir {
+ Filelist *fl;
+};
+
+Readdir*
+opendirfile(File *dir)
+{
+ Readdir *r;
+
+ rlock(&dir->rwlock);
+ if((dir->dir.mode & DMDIR)==0){
+ runlock(&dir->rwlock);
+ return nil;
+ }
+ r = emalloc9p(sizeof(*r));
+
+ /*
+ * This reference won't go away while we're using it
+ * since we are dir->rdir.
+ */
+ r->fl = dir->filelist;
+ runlock(&dir->rwlock);
+ return r;
+}
+
+long
+readdirfile(Readdir *r, uchar *buf, long n)
+{
+ long x, m;
+ Filelist *fl;
+
+ for(fl=r->fl, m=0; fl && m+2<=n; fl=fl->link, m+=x){
+ if(fl->f == nil)
+ x = 0;
+ else if((x=convD2M(&fl->f->dir, buf+m, n-m)) <= BIT16SZ)
+ break;
+ }
+ r->fl = fl;
+ return m;
+}
+
+void
+closedirfile(Readdir *r)
+{
+ free(r);
+}
diff --git a/src/lib9p/ftest.c b/src/lib9p/ftest.c
new file mode 100644
index 00000000..6692b7f1
--- /dev/null
+++ b/src/lib9p/ftest.c
@@ -0,0 +1,29 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <fcall.h>
+#include "9p.h"
+
+void
+main(void)
+{
+ Tree *t;
+ File *hello, *goodbye, *world;
+
+ t = mktree();
+
+ hello = fcreate(t->root, "hello", CHDIR|0777);
+ assert(hello != nil);
+
+ goodbye = fcreate(t->root, "goodbye", CHDIR|0777);
+ assert(goodbye != nil);
+
+ world = fcreate(hello, "world", 0666);
+ assert(world != nil);
+ world = fcreate(goodbye, "world", 0666);
+ assert(world != nil);
+ fdump(t->root, 0);
+
+ fremove(world);
+ fdump(t->root, 0);
+}
diff --git a/src/lib9p/intmap.c b/src/lib9p/intmap.c
new file mode 100644
index 00000000..48695675
--- /dev/null
+++ b/src/lib9p/intmap.c
@@ -0,0 +1,166 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+
+enum {
+ NHASH = 128
+};
+
+typedef struct Intlist Intlist;
+struct Intlist
+{
+ ulong id;
+ void* aux;
+ Intlist* link;
+};
+
+struct Intmap
+{
+ RWLock rwlock;
+ Intlist* hash[NHASH];
+ void (*inc)(void*);
+};
+
+static ulong
+hashid(ulong id)
+{
+ return id%NHASH;
+}
+
+static void
+nop(void *v)
+{
+ USED(v);
+}
+
+Intmap*
+allocmap(void (*inc)(void*))
+{
+ Intmap *m;
+
+ m = emalloc9p(sizeof(*m));
+ if(inc == nil)
+ inc = nop;
+ m->inc = inc;
+ return m;
+}
+
+void
+freemap(Intmap *map, void (*destroy)(void*))
+{
+ int i;
+ Intlist *p, *nlink;
+
+ if(destroy == nil)
+ destroy = nop;
+ for(i=0; i<NHASH; i++){
+ for(p=map->hash[i]; p; p=nlink){
+ nlink = p->link;
+ destroy(p->aux);
+ free(p);
+ }
+ }
+
+ free(map);
+}
+
+static Intlist**
+llookup(Intmap *map, ulong id)
+{
+ Intlist **lf;
+
+ for(lf=&map->hash[hashid(id)]; *lf; lf=&(*lf)->link)
+ if((*lf)->id == id)
+ break;
+ return lf;
+}
+
+/*
+ * The RWlock is used as expected except that we allow
+ * inc() to be called while holding it. This is because we're
+ * locking changes to the tree structure, not to the references.
+ * Inc() is expected to have its own locking.
+ */
+void*
+lookupkey(Intmap *map, ulong id)
+{
+ Intlist *f;
+ void *v;
+
+ rlock(&map->rwlock);
+ if(f = *llookup(map, id)){
+ v = f->aux;
+ map->inc(v);
+ }else
+ v = nil;
+ runlock(&map->rwlock);
+ return v;
+}
+
+void*
+insertkey(Intmap *map, ulong id, void *v)
+{
+ Intlist *f;
+ void *ov;
+ ulong h;
+
+ wlock(&map->rwlock);
+ if(f = *llookup(map, id)){
+ /* no decrement for ov because we're returning it */
+ ov = f->aux;
+ f->aux = v;
+ }else{
+ f = emalloc9p(sizeof(*f));
+ f->id = id;
+ f->aux = v;
+ h = hashid(id);
+ f->link = map->hash[h];
+ map->hash[h] = f;
+ ov = nil;
+ }
+ wunlock(&map->rwlock);
+ return ov;
+}
+
+int
+caninsertkey(Intmap *map, ulong id, void *v)
+{
+ Intlist *f;
+ int rv;
+ ulong h;
+
+ wlock(&map->rwlock);
+ if(*llookup(map, id))
+ rv = 0;
+ else{
+ f = emalloc9p(sizeof *f);
+ f->id = id;
+ f->aux = v;
+ h = hashid(id);
+ f->link = map->hash[h];
+ map->hash[h] = f;
+ rv = 1;
+ }
+ wunlock(&map->rwlock);
+ return rv;
+}
+
+void*
+deletekey(Intmap *map, ulong id)
+{
+ Intlist **lf, *f;
+ void *ov;
+
+ wlock(&map->rwlock);
+ if(f = *(lf = llookup(map, id))){
+ ov = f->aux;
+ *lf = f->link;
+ free(f);
+ }else
+ ov = nil;
+ wunlock(&map->rwlock);
+ return ov;
+}
diff --git a/src/lib9p/mem.c b/src/lib9p/mem.c
new file mode 100644
index 00000000..b4414951
--- /dev/null
+++ b/src/lib9p/mem.c
@@ -0,0 +1,49 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <fcall.h>
+#include <thread.h>
+#include "9p.h"
+
+void*
+emalloc9p(ulong sz)
+{
+ void *v;
+
+ if((v = malloc(sz)) == nil) {
+ fprint(2, "out of memory allocating %lud\n", sz);
+ exits("mem");
+ }
+ memset(v, 0, sz);
+ setmalloctag(v, getcallerpc(&sz));
+ return v;
+}
+
+void*
+erealloc9p(void *v, ulong sz)
+{
+ void *nv;
+
+ if((nv = realloc(v, sz)) == nil) {
+ fprint(2, "out of memory allocating %lud\n", sz);
+ exits("mem");
+ }
+ if(v == nil)
+ setmalloctag(nv, getcallerpc(&v));
+ setrealloctag(nv, getcallerpc(&v));
+ return nv;
+}
+
+char*
+estrdup9p(char *s)
+{
+ char *t;
+
+ if((t = strdup(s)) == nil) {
+ fprint(2, "out of memory in strdup(%.10s)\n", s);
+ exits("mem");
+ }
+ setmalloctag(t, getcallerpc(&s));
+ return t;
+}
+
diff --git a/src/lib9p/mkfile b/src/lib9p/mkfile
new file mode 100644
index 00000000..17a10a3c
--- /dev/null
+++ b/src/lib9p/mkfile
@@ -0,0 +1,20 @@
+PLAN9=../..
+<$PLAN9/src/mkhdr
+
+LIB=lib9p.a
+OFILES=\
+ _post.$O\
+ dirread.$O\
+ fid.$O\
+ file.$O\
+ intmap.$O\
+ mem.$O\
+ req.$O\
+ parse.$O\
+ post.$O\
+ srv.$O\
+ tpost.$O\
+ uid.$O\
+ util.$O\
+
+<$PLAN9/src/mksyslib
diff --git a/src/lib9p/parse.c b/src/lib9p/parse.c
new file mode 100644
index 00000000..753ae79d
--- /dev/null
+++ b/src/lib9p/parse.c
@@ -0,0 +1,115 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+
+/*
+ * Generous estimate of number of fields, including terminal nil pointer
+ */
+static int
+ncmdfield(char *p, int n)
+{
+ int white, nwhite;
+ char *ep;
+ int nf;
+
+ if(p == nil)
+ return 1;
+
+ nf = 0;
+ ep = p+n;
+ white = 1; /* first text will start field */
+ while(p < ep){
+ nwhite = (strchr(" \t\r\n", *p++ & 0xFF) != 0); /* UTF is irrelevant */
+ if(white && !nwhite) /* beginning of field */
+ nf++;
+ white = nwhite;
+ }
+ return nf+1; /* +1 for nil */
+}
+
+/*
+ * parse a command written to a device
+ */
+Cmdbuf*
+parsecmd(char *p, int n)
+{
+ Cmdbuf *cb;
+ int nf;
+ char *sp;
+
+ nf = ncmdfield(p, n);
+
+ /* allocate Cmdbuf plus string pointers plus copy of string including \0 */
+ sp = emalloc9p(sizeof(*cb) + nf * sizeof(char*) + n + 1);
+ cb = (Cmdbuf*)sp;
+ cb->f = (char**)(&cb[1]);
+ cb->buf = (char*)(&cb->f[nf]);
+
+ memmove(cb->buf, p, n);
+
+ /* dump new line and null terminate */
+ if(n > 0 && cb->buf[n-1] == '\n')
+ n--;
+ cb->buf[n] = '\0';
+
+ cb->nf = tokenize(cb->buf, cb->f, nf-1);
+ cb->f[cb->nf] = nil;
+
+ return cb;
+}
+
+/*
+ * Reconstruct original message, for error diagnostic
+ */
+void
+respondcmderror(Req *r, Cmdbuf *cb, char *fmt, ...)
+{
+ int i;
+ va_list arg;
+ char *p, *e;
+ char err[ERRMAX];
+
+ e = err+ERRMAX-10;
+ va_start(arg, fmt);
+ p = vseprint(err, e, fmt, arg);
+ va_end(arg);
+ p = seprint(p, e, ": \"");
+ for(i=0; i<cb->nf; i++){
+ if(i > 0)
+ p = seprint(p, e, " ");
+ p = seprint(p, e, "%q", cb->f[i]);
+ }
+ strcpy(p, "\"");
+ respond(r, err);
+}
+
+/*
+ * Look up entry in table
+ */
+Cmdtab*
+lookupcmd(Cmdbuf *cb, Cmdtab *ctab, int nctab)
+{
+ int i;
+ Cmdtab *ct;
+
+ if(cb->nf == 0){
+ werrstr("empty control message");
+ return nil;
+ }
+
+ for(ct = ctab, i=0; i<nctab; i++, ct++){
+ if(strcmp(ct->cmd, "*") !=0) /* wildcard always matches */
+ if(strcmp(ct->cmd, cb->f[0]) != 0)
+ continue;
+ if(ct->narg != 0 && ct->narg != cb->nf){
+ werrstr("bad # args to command");
+ return nil;
+ }
+ return ct;
+ }
+
+ werrstr("unknown control message");
+ return nil;
+}
diff --git a/src/lib9p/post.c b/src/lib9p/post.c
new file mode 100644
index 00000000..09296a88
--- /dev/null
+++ b/src/lib9p/post.c
@@ -0,0 +1,24 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+#include "post.h"
+
+void
+postmountsrv(Srv *s, char *name, char *mtpt, int flag)
+{
+ Postcrud *p;
+
+ p = _post1(s, name, mtpt, flag);
+ switch(rfork(RFPROC|RFNOTEG|RFNAMEG|RFMEM)){
+ case -1:
+ sysfatal("rfork: %r");
+ case 0:
+ _post2(s);
+ exits(nil);
+ default:
+ _post3(p);
+ }
+}
+
diff --git a/src/lib9p/post.h b/src/lib9p/post.h
new file mode 100644
index 00000000..069a7ce7
--- /dev/null
+++ b/src/lib9p/post.h
@@ -0,0 +1,13 @@
+typedef struct Postcrud Postcrud;
+struct Postcrud
+{
+ int fd[2];
+ Srv *s;
+ char *name;
+ char *mtpt;
+ int flag;
+};
+
+Postcrud *_post1(Srv*, char*, char*, int);
+void _post2(void*);
+void _post3(Postcrud*);
diff --git a/src/lib9p/ramfs.c b/src/lib9p/ramfs.c
new file mode 100644
index 00000000..a2f0b3d7
--- /dev/null
+++ b/src/lib9p/ramfs.c
@@ -0,0 +1,163 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+
+static char Ebad[] = "something bad happened";
+static char Enomem[] = "no memory";
+
+typedef struct Ramfile Ramfile;
+struct Ramfile {
+ char *data;
+ int ndata;
+};
+
+void
+fsread(Req *r)
+{
+ Ramfile *rf;
+ vlong offset;
+ long count;
+
+ rf = r->fid->file->aux;
+ offset = r->ifcall.offset;
+ count = r->ifcall.count;
+
+//print("read %ld %lld\n", *count, offset);
+ if(offset >= rf->ndata){
+ r->ofcall.count = 0;
+ respond(r, nil);
+ return;
+ }
+
+ if(offset+count >= rf->ndata)
+ count = rf->ndata - offset;
+
+ memmove(r->ofcall.data, rf->data+offset, count);
+ r->ofcall.count = count;
+ respond(r, nil);
+}
+
+void
+fswrite(Req *r)
+{
+ void *v;
+ Ramfile *rf;
+ vlong offset;
+ long count;
+
+ rf = r->fid->file->aux;
+ offset = r->ifcall.offset;
+ count = r->ifcall.count;
+
+ if(offset+count >= rf->ndata){
+ v = realloc(rf->data, offset+count);
+ if(v == nil){
+ respond(r, Enomem);
+ return;
+ }
+ rf->data = v;
+ rf->ndata = offset+count;
+ r->fid->file->length = rf->ndata;
+ }
+ memmove(rf->data+offset, r->ifcall.data, count);
+ r->ofcall.count = count;
+ respond(r, nil);
+}
+
+void
+fscreate(Req *r)
+{
+ Ramfile *rf;
+ File *f;
+
+ if(f = createfile(r->fid->file, r->ifcall.name, r->fid->uid, r->ifcall.perm, nil)){
+ rf = emalloc9p(sizeof *rf);
+ f->aux = rf;
+ r->fid->file = f;
+ r->ofcall.qid = f->qid;
+ respond(r, nil);
+ return;
+ }
+ respond(r, Ebad);
+}
+
+void
+fsopen(Req *r)
+{
+ Ramfile *rf;
+
+ rf = r->fid->file->aux;
+
+ if(rf && (r->ifcall.mode&OTRUNC)){
+ rf->ndata = 0;
+ r->fid->file->length = 0;
+ }
+
+ respond(r, nil);
+}
+
+void
+fsdestroyfile(File *f)
+{
+ Ramfile *rf;
+
+//fprint(2, "clunk\n");
+ rf = f->aux;
+ if(rf){
+ free(rf->data);
+ free(rf);
+ }
+}
+
+Srv fs = {
+ .open= fsopen,
+ .read= fsread,
+ .write= fswrite,
+ .create= fscreate,
+};
+
+void
+usage(void)
+{
+ fprint(2, "usage: ramfs [-D] [-s srvname] [-m mtpt]\n");
+ exits("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+ char *srvname = nil;
+ char *mtpt = nil;
+ Qid q;
+
+ fs.tree = alloctree(nil, nil, DMDIR|0777, fsdestroyfile);
+ q = fs.tree->root->qid;
+
+ ARGBEGIN{
+ case 'D':
+ chatty9p++;
+ break;
+ case 's':
+ srvname = EARGF(usage());
+ break;
+ case 'm':
+ mtpt = EARGF(usage());
+ break;
+ default:
+ usage();
+ }ARGEND;
+
+ if(argc)
+ usage();
+
+ if(chatty9p)
+ fprint(2, "ramsrv.nopipe %d srvname %s mtpt %s\n", fs.nopipe, srvname, mtpt);
+ if(srvname == nil && mtpt == nil)
+ sysfatal("you should at least specify a -s or -m option");
+
+ postmountsrv(&fs, srvname, mtpt, MREPL|MCREATE);
+ exits(0);
+}
diff --git a/src/lib9p/req.c b/src/lib9p/req.c
new file mode 100644
index 00000000..8e1aaab5
--- /dev/null
+++ b/src/lib9p/req.c
@@ -0,0 +1,112 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+
+static void
+increqref(void *v)
+{
+ Req *r;
+
+ r = v;
+ if(r){
+if(chatty9p > 1)
+ fprint(2, "increfreq %p %ld\n", r, r->ref.ref);
+ incref(&r->ref);
+ }
+}
+
+Reqpool*
+allocreqpool(void (*destroy)(Req*))
+{
+ Reqpool *f;
+
+ f = emalloc9p(sizeof *f);
+ f->map = allocmap(increqref);
+ f->destroy = destroy;
+ return f;
+}
+
+void
+freereqpool(Reqpool *p)
+{
+ freemap(p->map, (void(*)(void*))p->destroy);
+ free(p);
+}
+
+Req*
+allocreq(Reqpool *pool, ulong tag)
+{
+ Req *r;
+
+ r = emalloc9p(sizeof *r);
+ r->tag = tag;
+ r->pool = pool;
+
+ increqref(r);
+ increqref(r);
+ if(caninsertkey(pool->map, tag, r) == 0){
+ closereq(r);
+ return nil;
+ }
+
+ return r;
+}
+
+Req*
+lookupreq(Reqpool *pool, ulong tag)
+{
+if(chatty9p > 1)
+ fprint(2, "lookupreq %lud\n", tag);
+ return lookupkey(pool->map, tag);
+}
+
+void
+closereq(Req *r)
+{
+ int i;
+
+ if(r == nil)
+ return;
+
+if(chatty9p > 1)
+ fprint(2, "closereq %p %ld\n", r, r->ref.ref);
+
+ if(decref(&r->ref) == 0){
+ if(r->fid)
+ closefid(r->fid);
+ if(r->newfid)
+ closefid(r->newfid);
+ if(r->afid)
+ closefid(r->afid);
+ if(r->oldreq)
+ closereq(r->oldreq);
+ for(i=0; i<r->nflush; i++)
+ respond(r->flush[i], nil);
+ free(r->flush);
+ switch(r->ifcall.type){
+ case Tstat:
+ free(r->ofcall.stat);
+ free(r->d.name);
+ free(r->d.uid);
+ free(r->d.gid);
+ free(r->d.muid);
+ break;
+ }
+ if(r->pool->destroy)
+ r->pool->destroy(r);
+ free(r->buf);
+ free(r->rbuf);
+ free(r);
+ }
+}
+
+Req*
+removereq(Reqpool *pool, ulong tag)
+{
+if(chatty9p > 1)
+ fprint(2, "removereq %lud\n", tag);
+ return deletekey(pool->map, tag);
+}
diff --git a/src/lib9p/srv.c b/src/lib9p/srv.c
new file mode 100644
index 00000000..c5262f4c
--- /dev/null
+++ b/src/lib9p/srv.c
@@ -0,0 +1,837 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+
+// static char Ebadattach[] = "unknown specifier in attach";
+static char Ebadoffset[] = "bad offset";
+// static char Ebadcount[] = "bad count";
+static char Ebotch[] = "9P protocol botch";
+static char Ecreatenondir[] = "create in non-directory";
+static char Edupfid[] = "duplicate fid";
+static char Eduptag[] = "duplicate tag";
+static char Eisdir[] = "is a directory";
+static char Enocreate[] = "create prohibited";
+// static char Enomem[] = "out of memory";
+static char Enoremove[] = "remove prohibited";
+static char Enostat[] = "stat prohibited";
+static char Enotfound[] = "file not found";
+// static char Enowrite[] = "write prohibited";
+static char Enowstat[] = "wstat prohibited";
+static char Eperm[] = "permission denied";
+static char Eunknownfid[] = "unknown fid";
+static char Ebaddir[] = "bad directory in wstat";
+static char Ewalknodir[] = "walk in non-directory";
+
+static void
+setfcallerror(Fcall *f, char *err)
+{
+ f->ename = err;
+ f->type = Rerror;
+}
+
+static void
+changemsize(Srv *srv, int msize)
+{
+ if(srv->rbuf && srv->wbuf && srv->msize == msize)
+ return;
+ qlock(&srv->rlock);
+ qlock(&srv->wlock);
+ srv->msize = msize;
+ free(srv->rbuf);
+ free(srv->wbuf);
+ srv->rbuf = emalloc9p(msize);
+ srv->wbuf = emalloc9p(msize);
+ qunlock(&srv->rlock);
+ qunlock(&srv->wlock);
+}
+
+static Req*
+getreq(Srv *s)
+{
+ long n;
+ uchar *buf;
+ Fcall f;
+ Req *r;
+
+ qlock(&s->rlock);
+ if((n = read9pmsg(s->infd, s->rbuf, s->msize)) <= 0){
+ qunlock(&s->rlock);
+ return nil;
+ }
+
+ buf = emalloc9p(n);
+ memmove(buf, s->rbuf, n);
+ qunlock(&s->rlock);
+
+ if(convM2S(buf, n, &f) != n){
+ free(buf);
+ return nil;
+ }
+
+ if((r=allocreq(s->rpool, f.tag)) == nil){ /* duplicate tag: cons up a fake Req */
+ r = emalloc9p(sizeof *r);
+ incref(&r->ref);
+ r->tag = f.tag;
+ r->ifcall = f;
+ r->error = Eduptag;
+ r->buf = buf;
+ r->responded = 0;
+ r->type = 0;
+ r->srv = s;
+ r->pool = nil;
+if(chatty9p)
+ fprint(2, "<-%d- %F: dup tag\n", s->infd, &f);
+ return r;
+ }
+
+ r->srv = s;
+ r->responded = 0;
+ r->buf = buf;
+ r->ifcall = f;
+ memset(&r->ofcall, 0, sizeof r->ofcall);
+ r->type = r->ifcall.type;
+
+if(chatty9p)
+ if(r->error)
+ fprint(2, "<-%d- %F: %s\n", s->infd, &r->ifcall, r->error);
+ else
+ fprint(2, "<-%d- %F\n", s->infd, &r->ifcall);
+
+ return r;
+}
+
+static void
+filewalk(Req *r)
+{
+ int i;
+ File *f;
+
+ f = r->fid->file;
+ assert(f != nil);
+
+ incref(&f->ref);
+ for(i=0; i<r->ifcall.nwname; i++)
+ if(f = walkfile(f, r->ifcall.wname[i]))
+ r->ofcall.wqid[i] = f->dir.qid;
+ else
+ break;
+
+ r->ofcall.nwqid = i;
+ if(f){
+ r->newfid->file = f;
+ r->newfid->qid = r->newfid->file->dir.qid;
+ }
+ respond(r, nil);
+}
+
+void
+walkandclone(Req *r, char *(*walk1)(Fid*, char*, void*), char *(*clone)(Fid*, Fid*, void*), void *arg)
+{
+ int i;
+ char *e;
+
+ if(r->fid == r->newfid && r->ifcall.nwname > 1){
+ respond(r, "lib9p: unused documented feature not implemented");
+ return;
+ }
+
+ if(r->fid != r->newfid){
+ r->newfid->qid = r->fid->qid;
+ if(clone && (e = clone(r->fid, r->newfid, arg))){
+ respond(r, e);
+ return;
+ }
+ }
+
+ e = nil;
+ for(i=0; i<r->ifcall.nwname; i++){
+ if(e = walk1(r->newfid, r->ifcall.wname[i], arg))
+ break;
+ r->ofcall.wqid[i] = r->newfid->qid;
+ }
+
+ r->ofcall.nwqid = i;
+ if(e && i==0)
+ respond(r, e);
+ else
+ respond(r, nil);
+}
+
+static void
+sversion(Srv *srv, Req *r)
+{
+ USED(srv);
+
+ if(strncmp(r->ifcall.version, "9P", 2) != 0){
+ r->ofcall.version = "unknown";
+ respond(r, nil);
+ return;
+ }
+
+ r->ofcall.version = "9P2000";
+ r->ofcall.msize = r->ifcall.msize;
+ respond(r, nil);
+}
+static void
+rversion(Req *r, char *error)
+{
+ assert(error == nil);
+ changemsize(r->srv, r->ofcall.msize);
+}
+
+static void
+sauth(Srv *srv, Req *r)
+{
+ char e[ERRMAX];
+
+ if((r->afid = allocfid(srv->fpool, r->ifcall.afid)) == nil){
+ respond(r, Edupfid);
+ return;
+ }
+ if(srv->auth)
+ srv->auth(r);
+ else{
+ snprint(e, sizeof e, "%s: authentication not required", argv0);
+ respond(r, e);
+ }
+}
+static void
+rauth(Req *r, char *error)
+{
+ if(error && r->afid)
+ closefid(removefid(r->srv->fpool, r->afid->fid));
+}
+
+static void
+sattach(Srv *srv, Req *r)
+{
+ if((r->fid = allocfid(srv->fpool, r->ifcall.fid)) == nil){
+ respond(r, Edupfid);
+ return;
+ }
+ r->afid = nil;
+ if(r->ifcall.afid != NOFID && (r->afid = lookupfid(srv->fpool, r->ifcall.afid)) == nil){
+ respond(r, Eunknownfid);
+ return;
+ }
+ r->fid->uid = estrdup9p(r->ifcall.uname);
+ if(srv->tree){
+ r->fid->file = srv->tree->root;
+ /* BUG? incref(r->fid->file) ??? */
+ r->ofcall.qid = r->fid->file->dir.qid;
+ r->fid->qid = r->ofcall.qid;
+ }
+ if(srv->attach)
+ srv->attach(r);
+ else
+ respond(r, nil);
+ return;
+}
+static void
+rattach(Req *r, char *error)
+{
+ if(error && r->fid)
+ closefid(removefid(r->srv->fpool, r->fid->fid));
+}
+
+static void
+sflush(Srv *srv, Req *r)
+{
+ r->oldreq = lookupreq(srv->rpool, r->ifcall.oldtag);
+ if(r->oldreq == nil || r->oldreq == r)
+ respond(r, nil);
+ else if(srv->flush)
+ srv->flush(r);
+ else
+ respond(r, nil);
+}
+static int
+rflush(Req *r, char *error)
+{
+ Req *or;
+
+ assert(error == nil);
+ or = r->oldreq;
+ if(or){
+ qlock(&or->lk);
+ if(or->responded == 0){
+ or->flush = erealloc9p(or->flush, (or->nflush+1)*sizeof(or->flush[0]));
+ or->flush[or->nflush++] = r;
+ qunlock(&or->lk);
+ return -1; /* delay response until or is responded */
+ }
+ qunlock(&or->lk);
+ closereq(or);
+ }
+ r->oldreq = nil;
+ return 0;
+}
+
+static char*
+oldwalk1(Fid *fid, char *name, void *arg)
+{
+ char *e;
+ Qid qid;
+ Srv *srv;
+
+ srv = arg;
+ e = srv->walk1(fid, name, &qid);
+ if(e)
+ return e;
+ fid->qid = qid;
+ return nil;
+}
+
+static char*
+oldclone(Fid *fid, Fid *newfid, void *arg)
+{
+ Srv *srv;
+
+ srv = arg;
+ if(srv->clone == nil)
+ return nil;
+ return srv->clone(fid, newfid);
+}
+
+static void
+swalk(Srv *srv, Req *r)
+{
+ if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
+ respond(r, Eunknownfid);
+ return;
+ }
+ if(r->fid->omode != -1){
+ respond(r, "cannot clone open fid");
+ return;
+ }
+ if(r->ifcall.nwname && !(r->fid->qid.type&QTDIR)){
+ respond(r, Ewalknodir);
+ return;
+ }
+ if(r->ifcall.fid != r->ifcall.newfid){
+ if((r->newfid = allocfid(srv->fpool, r->ifcall.newfid)) == nil){
+ respond(r, Edupfid);
+ return;
+ }
+ r->newfid->uid = estrdup9p(r->fid->uid);
+ }else{
+ incref(&r->fid->ref);
+ r->newfid = r->fid;
+ }
+ if(r->fid->file){
+ filewalk(r);
+ }else if(srv->walk1)
+ walkandclone(r, oldwalk1, oldclone, srv);
+ else if(srv->walk)
+ srv->walk(r);
+ else
+ sysfatal("no walk function, no file trees");
+}
+static void
+rwalk(Req *r, char *error)
+{
+ if(error || r->ofcall.nwqid < r->ifcall.nwname){
+ if(r->ifcall.fid != r->ifcall.newfid && r->newfid)
+ closefid(removefid(r->srv->fpool, r->newfid->fid));
+ if (r->ofcall.nwqid==0){
+ if(error==nil && r->ifcall.nwname!=0)
+ r->error = Enotfound;
+ }else
+ r->error = nil; // No error on partial walks
+ }else{
+ if(r->ofcall.nwqid == 0){
+ /* Just a clone */
+ r->newfid->qid = r->fid->qid;
+ }else{
+ /* if file trees are in use, filewalk took care of the rest */
+ r->newfid->qid = r->ofcall.wqid[r->ofcall.nwqid-1];
+ }
+ }
+}
+
+static void
+sopen(Srv *srv, Req *r)
+{
+ int p;
+
+ if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
+ respond(r, Eunknownfid);
+ return;
+ }
+ if(r->fid->omode != -1){
+ respond(r, Ebotch);
+ return;
+ }
+ if((r->fid->qid.type&QTDIR) && (r->ifcall.mode&~ORCLOSE) != OREAD){
+ respond(r, Eisdir);
+ return;
+ }
+ r->ofcall.qid = r->fid->qid;
+ switch(r->ifcall.mode&3){
+ default:
+ assert(0);
+ case OREAD:
+ p = AREAD;
+ break;
+ case OWRITE:
+ p = AWRITE;
+ break;
+ case ORDWR:
+ p = AREAD|AWRITE;
+ break;
+ case OEXEC:
+ p = AEXEC;
+ break;
+ }
+ if(r->ifcall.mode&OTRUNC)
+ p |= AWRITE;
+ if((r->fid->qid.type&QTDIR) && p!=AREAD){
+ respond(r, Eperm);
+ return;
+ }
+ if(r->fid->file){
+ if(!hasperm(r->fid->file, r->fid->uid, p)){
+ respond(r, Eperm);
+ return;
+ }
+ /* BUG RACE */
+ if((r->ifcall.mode&ORCLOSE)
+ && !hasperm(r->fid->file->parent, r->fid->uid, AWRITE)){
+ respond(r, Eperm);
+ return;
+ }
+ r->ofcall.qid = r->fid->file->dir.qid;
+ if((r->ofcall.qid.type&QTDIR)
+ && (r->fid->rdir = opendirfile(r->fid->file)) == nil){
+ respond(r, "opendirfile failed");
+ return;
+ }
+ }
+ if(srv->open)
+ srv->open(r);
+ else
+ respond(r, nil);
+}
+static void
+ropen(Req *r, char *error)
+{
+ char errbuf[ERRMAX];
+ if(error)
+ return;
+ if(chatty9p){
+ snprint(errbuf, sizeof errbuf, "fid mode is 0x%ux\n", r->ifcall.mode);
+ write(2, errbuf, strlen(errbuf));
+ }
+ r->fid->omode = r->ifcall.mode;
+ r->fid->qid = r->ofcall.qid;
+ if(r->ofcall.qid.type&QTDIR)
+ r->fid->diroffset = 0;
+}
+
+static void
+screate(Srv *srv, Req *r)
+{
+ if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil)
+ respond(r, Eunknownfid);
+ else if(r->fid->omode != -1)
+ respond(r, Ebotch);
+ else if(!(r->fid->qid.type&QTDIR))
+ respond(r, Ecreatenondir);
+ else if(r->fid->file && !hasperm(r->fid->file, r->fid->uid, AWRITE))
+ respond(r, Eperm);
+ else if(srv->create)
+ srv->create(r);
+ else
+ respond(r, Enocreate);
+}
+static void
+rcreate(Req *r, char *error)
+{
+ if(error)
+ return;
+ r->fid->omode = r->ifcall.mode;
+ r->fid->qid = r->ofcall.qid;
+}
+
+static void
+sread(Srv *srv, Req *r)
+{
+ int o;
+
+ if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
+ respond(r, Eunknownfid);
+ return;
+ }
+ if(r->ifcall.count < 0){
+ respond(r, Ebotch);
+ return;
+ }
+ if(r->ifcall.offset < 0
+ || ((r->fid->qid.type&QTDIR) && r->ifcall.offset != 0 && r->ifcall.offset != r->fid->diroffset)){
+ respond(r, Ebadoffset);
+ return;
+ }
+
+ if(r->ifcall.count > srv->msize - IOHDRSZ)
+ r->ifcall.count = srv->msize - IOHDRSZ;
+ r->rbuf = emalloc9p(r->ifcall.count);
+ r->ofcall.data = r->rbuf;
+ o = r->fid->omode & 3;
+ if(o != OREAD && o != ORDWR && o != OEXEC){
+ respond(r, Ebotch);
+ return;
+ }
+ if((r->fid->qid.type&QTDIR) && r->fid->file){
+ r->ofcall.count = readdirfile(r->fid->rdir, r->rbuf, r->ifcall.count);
+ respond(r, nil);
+ return;
+ }
+ if(srv->read)
+ srv->read(r);
+ else
+ respond(r, "no srv->read");
+}
+static void
+rread(Req *r, char *error)
+{
+ if(error==nil && (r->fid->qid.type&QTDIR))
+ r->fid->diroffset += r->ofcall.count;
+}
+
+static void
+swrite(Srv *srv, Req *r)
+{
+ int o;
+ char e[ERRMAX];
+
+ if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
+ respond(r, Eunknownfid);
+ return;
+ }
+ if(r->ifcall.count < 0){
+ respond(r, Ebotch);
+ return;
+ }
+ if(r->ifcall.offset < 0){
+ respond(r, Ebotch);
+ return;
+ }
+ if(r->ifcall.count > srv->msize - IOHDRSZ)
+ r->ifcall.count = srv->msize - IOHDRSZ;
+ o = r->fid->omode & 3;
+ if(o != OWRITE && o != ORDWR){
+ snprint(e, sizeof e, "write on fid with open mode 0x%ux", r->fid->omode);
+ respond(r, e);
+ return;
+ }
+ if(srv->write)
+ srv->write(r);
+ else
+ respond(r, "no srv->write");
+}
+static void
+rwrite(Req *r, char *error)
+{
+ if(error)
+ return;
+ if(r->fid->file)
+ r->fid->file->dir.qid.vers++;
+}
+
+static void
+sclunk(Srv *srv, Req *r)
+{
+ if((r->fid = removefid(srv->fpool, r->ifcall.fid)) == nil)
+ respond(r, Eunknownfid);
+ else
+ respond(r, nil);
+}
+static void
+rclunk(Req *r, char *msg)
+{
+ USED(r);
+ USED(msg);
+}
+
+static void
+sremove(Srv *srv, Req *r)
+{
+ if((r->fid = removefid(srv->fpool, r->ifcall.fid)) == nil){
+ respond(r, Eunknownfid);
+ return;
+ }
+ /* BUG RACE */
+ if(r->fid->file && !hasperm(r->fid->file->parent, r->fid->uid, AWRITE)){
+ respond(r, Eperm);
+ return;
+ }
+ if(srv->remove)
+ srv->remove(r);
+ else
+ respond(r, r->fid->file ? nil : Enoremove);
+}
+static void
+rremove(Req *r, char *error, char *errbuf)
+{
+ if(error)
+ return;
+ if(r->fid->file){
+ if(removefile(r->fid->file) < 0){
+ snprint(errbuf, ERRMAX, "remove %s: %r",
+ r->fid->file->dir.name);
+ r->error = errbuf;
+ }
+ r->fid->file = nil;
+ }
+}
+
+static void
+sstat(Srv *srv, Req *r)
+{
+ if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
+ respond(r, Eunknownfid);
+ return;
+ }
+ if(r->fid->file){
+ r->d = r->fid->file->dir;
+ if(r->d.name)
+ r->d.name = estrdup9p(r->d.name);
+ if(r->d.uid)
+ r->d.uid = estrdup9p(r->d.uid);
+ if(r->d.gid)
+ r->d.gid = estrdup9p(r->d.gid);
+ if(r->d.muid)
+ r->d.muid = estrdup9p(r->d.muid);
+ }
+ if(srv->stat)
+ srv->stat(r);
+ else if(r->fid->file)
+ respond(r, nil);
+ else
+ respond(r, Enostat);
+}
+static void
+rstat(Req *r, char *error)
+{
+ int n;
+ uchar *statbuf;
+ uchar tmp[BIT16SZ];
+
+ if(error)
+ return;
+ if(convD2M(&r->d, tmp, BIT16SZ) != BIT16SZ){
+ r->error = "convD2M(_,_,BIT16SZ) did not return BIT16SZ";
+ return;
+ }
+ n = GBIT16(tmp)+BIT16SZ;
+ statbuf = emalloc9p(n);
+ if(statbuf == nil){
+ r->error = "out of memory";
+ return;
+ }
+ r->ofcall.nstat = convD2M(&r->d, statbuf, n);
+ r->ofcall.stat = statbuf; /* freed in closereq */
+ if(r->ofcall.nstat <= BIT16SZ){
+ r->error = "convD2M fails";
+ free(statbuf);
+ return;
+ }
+}
+
+static void
+swstat(Srv *srv, Req *r)
+{
+ if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
+ respond(r, Eunknownfid);
+ return;
+ }
+ if(srv->wstat == nil){
+ respond(r, Enowstat);
+ return;
+ }
+ if(convM2D(r->ifcall.stat, r->ifcall.nstat, &r->d, (char*)r->ifcall.stat) != r->ifcall.nstat){
+ respond(r, Ebaddir);
+ return;
+ }
+ if((ushort)~r->d.type){
+ respond(r, "wstat -- attempt to change type");
+ return;
+ }
+ if((uint)~r->d.dev){
+ respond(r, "wstat -- attempt to change dev");
+ return;
+ }
+ if((uchar)~r->d.qid.type || (ulong)~r->d.qid.vers || (uvlong)~r->d.qid.path){
+ respond(r, "wstat -- attempt to change qid");
+ return;
+ }
+ if(r->d.muid && r->d.muid[0]){
+ respond(r, "wstat -- attempt to change muid");
+ return;
+ }
+ if((ulong)~r->d.mode && ((r->d.mode&DMDIR)>>24) != (r->fid->qid.type&QTDIR)){
+ respond(r, "wstat -- attempt to change DMDIR bit");
+ return;
+ }
+ srv->wstat(r);
+}
+static void
+rwstat(Req *r, char *msg)
+{
+ USED(r);
+ USED(msg);
+}
+
+void
+srv(Srv *srv)
+{
+ Req *r;
+
+ fmtinstall('D', dirfmt);
+ fmtinstall('F', fcallfmt);
+
+ if(srv->fpool == nil)
+ srv->fpool = allocfidpool(srv->destroyfid);
+ if(srv->rpool == nil)
+ srv->rpool = allocreqpool(srv->destroyreq);
+ if(srv->msize == 0)
+ srv->msize = 8192+IOHDRSZ;
+
+ changemsize(srv, srv->msize);
+
+ srv->fpool->srv = srv;
+ srv->rpool->srv = srv;
+
+ while(r = getreq(srv)){
+ if(r->error){
+ respond(r, r->error);
+ continue;
+ }
+ switch(r->ifcall.type){
+ default:
+ respond(r, "unknown message");
+ break;
+ case Tversion: sversion(srv, r); break;
+ case Tauth: sauth(srv, r); break;
+ case Tattach: sattach(srv, r); break;
+ case Tflush: sflush(srv, r); break;
+ case Twalk: swalk(srv, r); break;
+ case Topen: sopen(srv, r); break;
+ case Tcreate: screate(srv, r); break;
+ case Tread: sread(srv, r); break;
+ case Twrite: swrite(srv, r); break;
+ case Tclunk: sclunk(srv, r); break;
+ case Tremove: sremove(srv, r); break;
+ case Tstat: sstat(srv, r); break;
+ case Twstat: swstat(srv, r); break;
+ }
+ }
+
+ if(srv->end)
+ srv->end(srv);
+}
+
+void
+respond(Req *r, char *error)
+{
+ int i, m, n;
+ char errbuf[ERRMAX];
+ Srv *srv;
+
+ srv = r->srv;
+ assert(srv != nil);
+
+ assert(r->responded == 0);
+ r->error = error;
+
+ switch(r->ifcall.type){
+ default:
+ assert(0);
+ /*
+ * Flush is special. If the handler says so, we return
+ * without further processing. Respond will be called
+ * again once it is safe.
+ */
+ case Tflush:
+ if(rflush(r, error)<0)
+ return;
+ break;
+ case Tversion: rversion(r, error); break;
+ case Tauth: rauth(r, error); break;
+ case Tattach: rattach(r, error); break;
+ case Twalk: rwalk(r, error); break;
+ case Topen: ropen(r, error); break;
+ case Tcreate: rcreate(r, error); break;
+ case Tread: rread(r, error); break;
+ case Twrite: rwrite(r, error); break;
+ case Tclunk: rclunk(r, error); break;
+ case Tremove: rremove(r, error, errbuf); break;
+ case Tstat: rstat(r, error); break;
+ case Twstat: rwstat(r, error); break;
+ }
+
+ r->ofcall.tag = r->ifcall.tag;
+ r->ofcall.type = r->ifcall.type+1;
+ if(r->error)
+ setfcallerror(&r->ofcall, r->error);
+
+if(chatty9p)
+ fprint(2, "-%d-> %F\n", srv->outfd, &r->ofcall);
+
+ qlock(&srv->wlock);
+ n = convS2M(&r->ofcall, srv->wbuf, srv->msize);
+ if(n <= 0){
+ fprint(2, "n = %d %F\n", n, &r->ofcall);
+ abort();
+ }
+ assert(n > 2);
+ if(r->pool) /* not a fake */
+ closereq(removereq(r->pool, r->ifcall.tag));
+ m = write(srv->outfd, srv->wbuf, n);
+ if(m != n)
+ sysfatal("lib9p srv: write %d returned %d on fd %d: %r", n, m, srv->outfd);
+ qunlock(&srv->wlock);
+
+ qlock(&r->lk); /* no one will add flushes now */
+ r->responded = 1;
+ qunlock(&r->lk);
+
+ for(i=0; i<r->nflush; i++)
+ respond(r->flush[i], nil);
+ free(r->flush);
+
+ if(r->pool)
+ closereq(r);
+ else
+ free(r);
+}
+
+int
+postfd(char *name, int pfd)
+{
+ int fd;
+ char buf[80];
+
+ snprint(buf, sizeof buf, "/srv/%s", name);
+ if(chatty9p)
+ fprint(2, "postfd %s\n", buf);
+ fd = create(buf, OWRITE|ORCLOSE|OCEXEC, 0600);
+ if(fd < 0){
+ if(chatty9p)
+ fprint(2, "create fails: %r\n");
+ return -1;
+ }
+ if(fprint(fd, "%d", pfd) < 0){
+ if(chatty9p)
+ fprint(2, "write fails: %r\n");
+ close(fd);
+ return -1;
+ }
+ if(chatty9p)
+ fprint(2, "postfd successful\n");
+ return 0;
+}
+
diff --git a/src/lib9p/tpost.c b/src/lib9p/tpost.c
new file mode 100644
index 00000000..68da19a7
--- /dev/null
+++ b/src/lib9p/tpost.c
@@ -0,0 +1,18 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+#include "post.h"
+
+void
+threadpostmountsrv(Srv *s, char *name, char *mtpt, int flag)
+{
+ Postcrud *p;
+
+ p = _post1(s, name, mtpt, flag);
+ if(procrfork(_post2, s, 32*1024, RFNAMEG|RFNOTEG) < 0)
+ sysfatal("procrfork: %r");
+ _post3(p);
+}
+
diff --git a/src/lib9p/uid.c b/src/lib9p/uid.c
new file mode 100644
index 00000000..84b98bde
--- /dev/null
+++ b/src/lib9p/uid.c
@@ -0,0 +1,34 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+
+/*
+ * simplistic permission checking. assume that
+ * each user is the leader of her own group.
+ */
+int
+hasperm(File *f, char *uid, int p)
+{
+ int m;
+
+ m = f->dir.mode & 7; /* other */
+ if((p & m) == p)
+ return 1;
+
+ if(strcmp(f->dir.uid, uid) == 0) {
+ m |= (f->dir.mode>>6) & 7;
+ if((p & m) == p)
+ return 1;
+ }
+
+ if(strcmp(f->dir.gid, uid) == 0) {
+ m |= (f->dir.mode>>3) & 7;
+ if((p & m) == p)
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/src/lib9p/util.c b/src/lib9p/util.c
new file mode 100644
index 00000000..17588a95
--- /dev/null
+++ b/src/lib9p/util.c
@@ -0,0 +1,25 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <fcall.h>
+#include <thread.h>
+#include "9p.h"
+
+void
+readbuf(Req *r, void *s, long n)
+{
+ r->ofcall.count = r->ifcall.count;
+ if(r->ifcall.offset >= n){
+ r->ofcall.count = 0;
+ return;
+ }
+ if(r->ifcall.offset+r->ofcall.count > n)
+ r->ofcall.count = n - r->ifcall.offset;
+ memmove(r->ofcall.data, (char*)s+r->ifcall.offset, r->ofcall.count);
+}
+
+void
+readstr(Req *r, char *s)
+{
+ readbuf(r, s, strlen(s));
+}