diff options
author | rsc <devnull@localhost> | 2004-03-21 04:33:13 +0000 |
---|---|---|
committer | rsc <devnull@localhost> | 2004-03-21 04:33:13 +0000 |
commit | 2277c5d7bbe1f9595fad512d8f790708473a9bf1 (patch) | |
tree | 4d653e13906f1971d3170dba6dbe0fbf92eb48d6 /src/lib9p/srv.c | |
parent | a770daa795754cb600ad3fab2fdd2961147006c4 (diff) | |
download | plan9port-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.c | 837 |
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; +} + |