#include "dat.h"#include "fns.h"#include "error.h" /* * References are managed as follows: * The channel to the server - a network connection or pipe - has one * reference for every Chan open on the server. The server channel has * c->mux set to the Mnt used for muxing control to that server. Mnts * have no reference count; they go away when c goes away. * Each channel derived from the mount point has mchan set to c, * and increfs/decrefs mchan to manage references on the server * connection. */ #define MAXRPC (IOHDRSZ+8192) struct Mntrpc{ Chan* c; /* Channel for whom we are working */ Mntrpc* list; /* Free/pending list */ Fcall request; /* Outgoing file system protocol message */ Fcall reply; /* Incoming reply */ Mnt* m; /* Mount device during rpc */ Rendez r; /* Place to hang out */ uchar* rpc; /* I/O Data buffer */ uint rpclen; /* len of buffer */ Block *b; /* reply blocks */ char done; /* Rpc completed */ uvlong stime; /* start time for mnt statistics */ ulong reqlen; /* request length for mnt statistics */ ulong replen; /* reply length for mnt statistics */ Mntrpc* flushed; /* message this one flushes */}; enum{ TAGSHIFT = 5, /* ulong has to be 32 bits */ TAGMASK = (1<<TAGSHIFT)-1, NMASK = (64*1024)>>TAGSHIFT,}; struct Mntalloc{ Lock l; Mnt* list; /* Mount devices in use */ Mnt* mntfree; /* Free list */ Mntrpc* rpcfree; int nrpcfree; int nrpcused; ulong id; ulong tagmask[NMASK];}mntalloc; void mattach(Mnt*, Chan*, char*);Mnt* mntchk(Chan*);void mntdirfix(uchar*, Chan*);Mntrpc* mntflushalloc(Mntrpc*, ulong);void mntflushfree(Mnt*, Mntrpc*);void mntfree(Mntrpc*);void mntgate(Mnt*);void mntpntfree(Mnt*);void mntqrm(Mnt*, Mntrpc*);Mntrpc* mntralloc(Chan*, ulong);long mntrdwr(int, Chan*, void*, long, vlong);int mntrpcread(Mnt*, Mntrpc*);void mountio(Mnt*, Mntrpc*);void mountmux(Mnt*, Mntrpc*);void mountrpc(Mnt*, Mntrpc*);int rpcattn(void*);Chan* mntchan(void); char Esbadstat[] = "invalid directory entry received from server";char Enoversion[] = "version not established for mount channel"; void (*mntstats)(int, Chan*, uvlong, ulong); static voidmntinit(void){ mntalloc.id = 1; mntalloc.tagmask[0] = 1; /* don't allow 0 as a tag */ mntalloc.tagmask[NMASK-1] = 0x80000000UL; /* don't allow NOTAG */ fmtinstall('F', fcallfmt);/* fmtinstall('D', dirfmt); *//* fmtinstall('M', dirmodefmt); */ cinit();} /* * Version is not multiplexed: message sent only once per connection. */longmntversion(Chan *c, char *version, int msize, int returnlen){ Fcall f; uchar *msg; Mnt *m; char *v; long k, l; uvlong oo; char buf[128]; qlock(&c->umqlock); /* make sure no one else does this until we've established ourselves */ if(waserror()){ qunlock(&c->umqlock); nexterror(); } /* defaults */ if(msize == 0) msize = MAXRPC; if(msize > c->iounit && c->iounit != 0) msize = c->iounit; v = version; if(v == nil || v[0] == '\0') v = VERSION9P; /* validity */ if(msize < 0) error("bad iounit in version call"); if(strncmp(v, VERSION9P, strlen(VERSION9P)) != 0) error("bad 9P version specification"); m = c->mux; if(m != nil){ qunlock(&c->umqlock); poperror(); strecpy(buf, buf+sizeof buf, m->version); k = strlen(buf); if(strncmp(buf, v, k) != 0){ snprint(buf, sizeof buf, "incompatible 9P versions %s %s", m->version, v); error(buf); } if(returnlen > 0){ if(returnlen < k) error(Eshort); memmove(version, buf, k); } return k; } f.type = Tversion; f.tag = NOTAG; f.msize = msize; f.version = v; msg = malloc(8192+IOHDRSZ); if(msg == nil) exhausted("version memory"); if(waserror()){ free(msg); nexterror(); } k = convS2M(&f, msg, 8192+IOHDRSZ); if(k == 0) error("bad fversion conversion on send"); lock(&c->l); oo = c->offset; c->offset += k; unlock(&c->l); l = devtab[c->type]->write(c, msg, k, oo); if(l < k){ lock(&c->l); c->offset -= k - l; unlock(&c->l); error("short write in fversion"); } /* message sent; receive and decode reply */ k = devtab[c->type]->read(c, msg, 8192+IOHDRSZ, c->offset); if(k <= 0) error("EOF receiving fversion reply"); lock(&c->l); c->offset += k; unlock(&c->l); l = convM2S(msg, k, &f); if(l != k) error("bad fversion conversion on reply"); if(f.type != Rversion){ if(f.type == Rerror) error(f.ename); error("unexpected reply type in fversion"); } if(f.msize > msize) error("server tries to increase msize in fversion"); if(f.msize<256 || f.msize>1024*1024) error("nonsense value of msize in fversion"); if(strncmp(f.version, v, strlen(f.version)) != 0) error("bad 9P version returned from server"); /* now build Mnt associated with this connection */ lock(&mntalloc.l); m = mntalloc.mntfree; if(m != 0) mntalloc.mntfree = m->list; else { m = malloc(sizeof(Mnt)); if(m == 0) { unlock(&mntalloc.l); exhausted("mount devices"); } } m->list = mntalloc.list; mntalloc.list = m; m->version = nil; kstrdup(&m->version, f.version); m->id = mntalloc.id++; m->q = qopen(10*MAXRPC, 0, nil, nil); m->msize = f.msize; unlock(&mntalloc.l); poperror(); /* msg */ free(msg); lock(&m->l); m->queue = 0; m->rip = 0; c->flag |= CMSG; c->mux = m; m->c = c; unlock(&m->l); poperror(); /* c */ qunlock(&c->umqlock); k = strlen(f.version); if(returnlen > 0){ if(returnlen < k) error(Eshort); memmove(version, f.version, k); } return k;} Chan*mntauth(Chan *c, char *spec){ Mnt *m; Mntrpc *r; m = c->mux; if(m == nil){ mntversion(c, VERSION9P, MAXRPC, 0); m = c->mux; if(m == nil) error(Enoversion); } c = mntchan(); if(waserror()) { /* Close must not be called since it will * call mnt recursively */ chanfree(c); nexterror(); } r = mntralloc(0, m->msize); if(waserror()) { mntfree(r); nexterror(); } r->request.type = Tauth; r->request.afid = c->fid; r->request.uname = up->env->user; r->request.aname = spec; mountrpc(m, r); c->qid = r->reply.aqid; c->mchan = m->c; incref(&m->c->r); c->mqid = c->qid; c->mode = ORDWR; poperror(); /* r */ mntfree(r); poperror(); /* c */ return c; } static Chan*mntattach(char *muxattach){ Mnt *m; Chan *c; Mntrpc *r; struct bogus{ Chan *chan; Chan *authchan; char *spec; int flags; }bogus; bogus = *((struct bogus *)muxattach); c = bogus.chan; m = c->mux; if(m == nil){ mntversion(c, nil, 0, 0); m = c->mux; if(m == nil) error(Enoversion); } c = mntchan(); if(waserror()) { /* Close must not be called since it will * call mnt recursively */ chanfree(c); nexterror(); } r = mntralloc(0, m->msize); if(waserror()) { mntfree(r); nexterror(); } r->request.type = Tattach; r->request.fid = c->fid; if(bogus.authchan == nil) r->request.afid = NOFID; else r->request.afid = bogus.authchan->fid; r->request.uname = up->env->user; r->request.aname = bogus.spec; mountrpc(m, r); c->qid = r->reply.qid; c->mchan = m->c; incref(&m->c->r); c->mqid = c->qid; poperror(); /* r */ mntfree(r); poperror(); /* c */ if(bogus.flags&MCACHE) c->flag |= CCACHE; return c;} Chan*mntchan(void){ Chan *c; c = devattach('M', 0); lock(&mntalloc.l); c->dev = mntalloc.id++; unlock(&mntalloc.l); if(c->mchan) panic("mntchan non-zero %p", c->mchan); return c;} static Walkqid*mntwalk(Chan *c, Chan *nc, char **name, int nname){ volatile int alloc; int i; Mnt *m; Mntrpc *r; Walkqid *wq; if(nc != nil) print("mntwalk: nc != nil\n"); if(nname > MAXWELEM) error("devmnt: too many name elements"); alloc = 0; wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid)); if(waserror()){ if(alloc && wq->clone!=nil) cclose(wq->clone); free(wq); return nil; } alloc = 0; m = mntchk(c); r = mntralloc(c, m->msize); if(nc == nil){ nc = devclone(c); /* * Until the other side accepts this fid, we can't mntclose it. * Therefore set type to 0 for now; rootclose is known to be safe. */ nc->type = 0; alloc = 1; } wq->clone = nc; if(waserror()) { mntfree(r); nexterror(); } r->request.type = Twalk; r->request.fid = c->fid; r->request.newfid = nc->fid; r->request.nwname = nname; memmove(r->request.wname, name, nname*sizeof(char*)); mountrpc(m, r); if(r->reply.nwqid > nname) error("too many QIDs returned by walk"); if(r->reply.nwqid < nname){ if(alloc) cclose(nc); wq->clone = nil; if(r->reply.nwqid == 0){ free(wq); wq = nil; goto Return; } } /* move new fid onto mnt device and update its qid */ if(wq->clone != nil){ if(wq->clone != c){ wq->clone->type = c->type; wq->clone->mchan = c->mchan; incref(&c->mchan->r); } if(r->reply.nwqid > 0) wq->clone->qid = r->reply.wqid[r->reply.nwqid-1]; } wq->nqid = r->reply.nwqid; for(i=0; i<wq->nqid; i++) wq->qid[i] = r->reply.wqid[i]; Return: poperror(); mntfree(r); poperror(); return wq;} static intmntstat(Chan *c, uchar *dp, int n){ Mnt *m; Mntrpc *r; if(n < BIT16SZ) error(Eshortstat); m = mntchk(c); r = mntralloc(c, m->msize); if(waserror()) { mntfree(r); nexterror(); } r->request.type = Tstat; r->request.fid = c->fid; mountrpc(m, r); if(r->reply.nstat > n){ /* doesn't fit; just patch the count and return */ PBIT16((uchar*)dp, r->reply.nstat); n = BIT16SZ; }else{ n = r->reply.nstat; memmove(dp, r->reply.stat, n); validstat(dp, n); mntdirfix(dp, c); } poperror(); mntfree(r); return n;} static Chan*mntopencreate(int type, Chan *c, char *name, int omode, ulong perm){ Mnt *m; Mntrpc *r; m = mntchk(c);Lines 1–500 of 1203. Load more
#include "dat.h" #include "fns.h" #include "error.h" /* * References are managed as follows: * The channel to the server - a network connection or pipe - has one * reference for every Chan open on the server. The server channel has * c->mux set to the Mnt used for muxing control to that server. Mnts * have no reference count; they go away when c goes away. * Each channel derived from the mount point has mchan set to c, * and increfs/decrefs mchan to manage references on the server * connection. */ #define MAXRPC (IOHDRSZ+8192) struct Mntrpc { Chan* c; /* Channel for whom we are working */ Mntrpc* list; /* Free/pending list */ Fcall request; /* Outgoing file system protocol message */ Fcall reply; /* Incoming reply */ Mnt* m; /* Mount device during rpc */ Rendez r; /* Place to hang out */ uchar* rpc; /* I/O Data buffer */ uint rpclen; /* len of buffer */ Block *b; /* reply blocks */ char done; /* Rpc completed */ uvlong stime; /* start time for mnt statistics */ ulong reqlen; /* request length for mnt statistics */ ulong replen; /* reply length for mnt statistics */ Mntrpc* flushed; /* message this one flushes */ }; enum { TAGSHIFT = 5, /* ulong has to be 32 bits */ TAGMASK = (1<<TAGSHIFT)-1, NMASK = (64*1024)>>TAGSHIFT, }; struct Mntalloc { Lock l; Mnt* list; /* Mount devices in use */ Mnt* mntfree; /* Free list */ Mntrpc* rpcfree; int nrpcfree; int nrpcused; ulong id; ulong tagmask[NMASK]; }mntalloc; void mattach(Mnt*, Chan*, char*); Mnt* mntchk(Chan*); void mntdirfix(uchar*, Chan*); Mntrpc* mntflushalloc(Mntrpc*, ulong); void mntflushfree(Mnt*, Mntrpc*); void mntfree(Mntrpc*); void mntgate(Mnt*); void mntpntfree(Mnt*); void mntqrm(Mnt*, Mntrpc*); Mntrpc* mntralloc(Chan*, ulong); long mntrdwr(int, Chan*, void*, long, vlong); int mntrpcread(Mnt*, Mntrpc*); void mountio(Mnt*, Mntrpc*); void mountmux(Mnt*, Mntrpc*); void mountrpc(Mnt*, Mntrpc*); int rpcattn(void*); Chan* mntchan(void); char Esbadstat[] = "invalid directory entry received from server"; char Enoversion[] = "version not established for mount channel"; void (*mntstats)(int, Chan*, uvlong, ulong); static void mntinit(void) { mntalloc.id = 1; mntalloc.tagmask[0] = 1; /* don't allow 0 as a tag */ mntalloc.tagmask[NMASK-1] = 0x80000000UL; /* don't allow NOTAG */ fmtinstall('F', fcallfmt); /* fmtinstall('D', dirfmt); */ /* fmtinstall('M', dirmodefmt); */ cinit(); } /* * Version is not multiplexed: message sent only once per connection. */ long mntversion(Chan *c, char *version, int msize, int returnlen) { Fcall f; uchar *msg; Mnt *m; char *v; long k, l; uvlong oo; char buf[128]; qlock(&c->umqlock); /* make sure no one else does this until we've established ourselves */ if(waserror()){ qunlock(&c->umqlock); nexterror(); } /* defaults */ if(msize == 0) msize = MAXRPC; if(msize > c->iounit && c->iounit != 0) msize = c->iounit; v = version; if(v == nil || v[0] == '\0') v = VERSION9P; /* validity */ if(msize < 0) error("bad iounit in version call"); if(strncmp(v, VERSION9P, strlen(VERSION9P)) != 0) error("bad 9P version specification"); m = c->mux; if(m != nil){ qunlock(&c->umqlock); poperror(); strecpy(buf, buf+sizeof buf, m->version); k = strlen(buf); if(strncmp(buf, v, k) != 0){ snprint(buf, sizeof buf, "incompatible 9P versions %s %s", m->version, v); error(buf); } if(returnlen > 0){ if(returnlen < k) error(Eshort); memmove(version, buf, k); } return k; } f.type = Tversion; f.tag = NOTAG; f.msize = msize; f.version = v; msg = malloc(8192+IOHDRSZ); if(msg == nil) exhausted("version memory"); if(waserror()){ free(msg); nexterror(); } k = convS2M(&f, msg, 8192+IOHDRSZ); if(k == 0) error("bad fversion conversion on send"); lock(&c->l); oo = c->offset; c->offset += k; unlock(&c->l); l = devtab[c->type]->write(c, msg, k, oo); if(l < k){ lock(&c->l); c->offset -= k - l; unlock(&c->l); error("short write in fversion"); } /* message sent; receive and decode reply */ k = devtab[c->type]->read(c, msg, 8192+IOHDRSZ, c->offset); if(k <= 0) error("EOF receiving fversion reply"); lock(&c->l); c->offset += k; unlock(&c->l); l = convM2S(msg, k, &f); if(l != k) error("bad fversion conversion on reply"); if(f.type != Rversion){ if(f.type == Rerror) error(f.ename); error("unexpected reply type in fversion"); } if(f.msize > msize) error("server tries to increase msize in fversion"); if(f.msize<256 || f.msize>1024*1024) error("nonsense value of msize in fversion"); if(strncmp(f.version, v, strlen(f.version)) != 0) error("bad 9P version returned from server"); /* now build Mnt associated with this connection */ lock(&mntalloc.l); m = mntalloc.mntfree; if(m != 0) mntalloc.mntfree = m->list; else { m = malloc(sizeof(Mnt)); if(m == 0) { unlock(&mntalloc.l); exhausted("mount devices"); } } m->list = mntalloc.list; mntalloc.list = m; m->version = nil; kstrdup(&m->version, f.version); m->id = mntalloc.id++; m->q = qopen(10*MAXRPC, 0, nil, nil); m->msize = f.msize; unlock(&mntalloc.l); poperror(); /* msg */ free(msg); lock(&m->l); m->queue = 0; m->rip = 0; c->flag |= CMSG; c->mux = m; m->c = c; unlock(&m->l); poperror(); /* c */ qunlock(&c->umqlock); k = strlen(f.version); if(returnlen > 0){ if(returnlen < k) error(Eshort); memmove(version, f.version, k); } return k; } Chan* mntauth(Chan *c, char *spec) { Mnt *m; Mntrpc *r; m = c->mux; if(m == nil){ mntversion(c, VERSION9P, MAXRPC, 0); m = c->mux; if(m == nil) error(Enoversion); } c = mntchan(); if(waserror()) { /* Close must not be called since it will * call mnt recursively */ chanfree(c); nexterror(); } r = mntralloc(0, m->msize); if(waserror()) { mntfree(r); nexterror(); } r->request.type = Tauth; r->request.afid = c->fid; r->request.uname = up->env->user; r->request.aname = spec; mountrpc(m, r); c->qid = r->reply.aqid; c->mchan = m->c; incref(&m->c->r); c->mqid = c->qid; c->mode = ORDWR; poperror(); /* r */ mntfree(r); poperror(); /* c */ return c; } static Chan* mntattach(char *muxattach) { Mnt *m; Chan *c; Mntrpc *r; struct bogus{ Chan *chan; Chan *authchan; char *spec; int flags; }bogus; bogus = *((struct bogus *)muxattach); c = bogus.chan; m = c->mux; if(m == nil){ mntversion(c, nil, 0, 0); m = c->mux; if(m == nil) error(Enoversion); } c = mntchan(); if(waserror()) { /* Close must not be called since it will * call mnt recursively */ chanfree(c); nexterror(); } r = mntralloc(0, m->msize); if(waserror()) { mntfree(r); nexterror(); } r->request.type = Tattach; r->request.fid = c->fid; if(bogus.authchan == nil) r->request.afid = NOFID; else r->request.afid = bogus.authchan->fid; r->request.uname = up->env->user; r->request.aname = bogus.spec; mountrpc(m, r); c->qid = r->reply.qid; c->mchan = m->c; incref(&m->c->r); c->mqid = c->qid; poperror(); /* r */ mntfree(r); poperror(); /* c */ if(bogus.flags&MCACHE) c->flag |= CCACHE; return c; } Chan* mntchan(void) { Chan *c; c = devattach('M', 0); lock(&mntalloc.l); c->dev = mntalloc.id++; unlock(&mntalloc.l); if(c->mchan) panic("mntchan non-zero %p", c->mchan); return c; } static Walkqid* mntwalk(Chan *c, Chan *nc, char **name, int nname) { volatile int alloc; int i; Mnt *m; Mntrpc *r; Walkqid *wq; if(nc != nil) print("mntwalk: nc != nil\n"); if(nname > MAXWELEM) error("devmnt: too many name elements"); alloc = 0; wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid)); if(waserror()){ if(alloc && wq->clone!=nil) cclose(wq->clone); free(wq); return nil; } alloc = 0; m = mntchk(c); r = mntralloc(c, m->msize); if(nc == nil){ nc = devclone(c); /* * Until the other side accepts this fid, we can't mntclose it. * Therefore set type to 0 for now; rootclose is known to be safe. */ nc->type = 0; alloc = 1; } wq->clone = nc; if(waserror()) { mntfree(r); nexterror(); } r->request.type = Twalk; r->request.fid = c->fid; r->request.newfid = nc->fid; r->request.nwname = nname; memmove(r->request.wname, name, nname*sizeof(char*)); mountrpc(m, r); if(r->reply.nwqid > nname) error("too many QIDs returned by walk"); if(r->reply.nwqid < nname){ if(alloc) cclose(nc); wq->clone = nil; if(r->reply.nwqid == 0){ free(wq); wq = nil; goto Return; } } /* move new fid onto mnt device and update its qid */ if(wq->clone != nil){ if(wq->clone != c){ wq->clone->type = c->type; wq->clone->mchan = c->mchan; incref(&c->mchan->r); } if(r->reply.nwqid > 0) wq->clone->qid = r->reply.wqid[r->reply.nwqid-1]; } wq->nqid = r->reply.nwqid; for(i=0; i<wq->nqid; i++) wq->qid[i] = r->reply.wqid[i]; Return: poperror(); mntfree(r); poperror(); return wq; } static int mntstat(Chan *c, uchar *dp, int n) { Mnt *m; Mntrpc *r; if(n < BIT16SZ) error(Eshortstat); m = mntchk(c); r = mntralloc(c, m->msize); if(waserror()) { mntfree(r); nexterror(); } r->request.type = Tstat; r->request.fid = c->fid; mountrpc(m, r); if(r->reply.nstat > n){ /* doesn't fit; just patch the count and return */ PBIT16((uchar*)dp, r->reply.nstat); n = BIT16SZ; }else{ n = r->reply.nstat; memmove(dp, r->reply.stat, n); validstat(dp, n); mntdirfix(dp, c); } poperror(); mntfree(r); return n; } static Chan* mntopencreate(int type, Chan *c, char *name, int omode, ulong perm) { Mnt *m; Mntrpc *r; m = mntchk(c);