aboutsummaryrefslogtreecommitdiff
path: root/src/lib9p/srv.c
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/srv.c
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/srv.c')
-rw-r--r--src/lib9p/srv.c837
1 files changed, 837 insertions, 0 deletions
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;
+}
+