123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215 |
- #include "u.h"
- #include "../port/lib.h"
- #include "mem.h"
- #include "dat.h"
- #include "fns.h"
- #include "../port/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;
- 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
- mntreset(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);
- /* We can't install %M since eipfmt does and is used in the kernel [sape] */
- 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);
- oo = c->offset;
- c->offset += k;
- unlock(c);
- l = devtab[c->type]->write(c, msg, k, oo);
- if(l < k){
- lock(c);
- c->offset -= k - l;
- unlock(c);
- 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);
- c->offset += k;
- unlock(c);
- 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);
- m = mntalloc.mntfree;
- if(m != 0)
- mntalloc.mntfree = m->list;
- else {
- m = malloc(sizeof(Mnt));
- if(m == 0) {
- unlock(&mntalloc);
- 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);
- poperror(); /* msg */
- free(msg);
- lock(m);
- m->queue = 0;
- m->rip = 0;
- c->flag |= CMSG;
- c->mux = m;
- m->c = c;
- unlock(m);
- 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->user;
- r->request.aname = spec;
- mountrpc(m, r);
- c->qid = r->reply.aqid;
- c->mchan = m->c;
- incref(m->c);
- 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->user;
- r->request.aname = bogus.spec;
- mountrpc(m, r);
- c->qid = r->reply.qid;
- c->mchan = m->c;
- incref(m->c);
- 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);
- c->dev = mntalloc.id++;
- unlock(&mntalloc);
- if(c->mchan)
- panic("mntchan non-zero %p", c->mchan);
- return c;
- }
- static Walkqid*
- mntwalk(Chan *c, Chan *nc, char **name, int nname)
- {
- int i, alloc;
- 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);
- }
- 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 >= 1<<(8*BIT16SZ))
- error("returned stat buffer count too large");
- if(r->reply.nstat > n){
- /*
- * 12/31/2002 RSC
- *
- * This should be nstat-2, which is the first two
- * bytes of the stat buffer. But dirstat and dirfstat
- * depended on getting the full nstat (they didn't
- * add BIT16SZ themselves). I fixed dirstat and dirfstat
- * but am leaving this unchanged for now. After a
- * few months, once enough of the relevant binaries
- * have been recompiled for other reasons, we can
- * change this to nstat-2. Devstat gets this right
- * (via convD2M).
- */
- /* 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);
- r = mntralloc(c, m->msize);
- if(waserror()) {
- mntfree(r);
- nexterror();
- }
- r->request.type = type;
- r->request.fid = c->fid;
- r->request.mode = omode;
- if(type == Tcreate){
- r->request.perm = perm;
- r->request.name = name;
- }
- mountrpc(m, r);
- c->qid = r->reply.qid;
- c->offset = 0;
- c->mode = openmode(omode);
- c->iounit = r->reply.iounit;
- if(c->iounit == 0 || c->iounit > m->msize-IOHDRSZ)
- c->iounit = m->msize-IOHDRSZ;
- c->flag |= COPEN;
- poperror();
- mntfree(r);
- if(c->flag & CCACHE)
- copen(c);
- return c;
- }
- static Chan*
- mntopen(Chan *c, int omode)
- {
- return mntopencreate(Topen, c, nil, omode, 0);
- }
- static void
- mntcreate(Chan *c, char *name, int omode, ulong perm)
- {
- mntopencreate(Tcreate, c, name, omode, perm);
- }
- static void
- mntclunk(Chan *c, int t)
- {
- Mnt *m;
- Mntrpc *r;
- m = mntchk(c);
- r = mntralloc(c, m->msize);
- if(waserror()){
- mntfree(r);
- nexterror();
- }
- r->request.type = t;
- r->request.fid = c->fid;
- mountrpc(m, r);
- mntfree(r);
- poperror();
- }
- void
- muxclose(Mnt *m)
- {
- Mntrpc *q, *r;
- for(q = m->queue; q; q = r) {
- r = q->list;
- mntfree(q);
- }
- m->id = 0;
- free(m->version);
- m->version = nil;
- mntpntfree(m);
- }
- void
- mntpntfree(Mnt *m)
- {
- Mnt *f, **l;
- Queue *q;
- lock(&mntalloc);
- l = &mntalloc.list;
- for(f = *l; f; f = f->list) {
- if(f == m) {
- *l = m->list;
- break;
- }
- l = &f->list;
- }
- m->list = mntalloc.mntfree;
- mntalloc.mntfree = m;
- q = m->q;
- unlock(&mntalloc);
- qfree(q);
- }
- static void
- mntclose(Chan *c)
- {
- mntclunk(c, Tclunk);
- }
- static void
- mntremove(Chan *c)
- {
- mntclunk(c, Tremove);
- }
- static int
- mntwstat(Chan *c, uchar *dp, int n)
- {
- Mnt *m;
- Mntrpc *r;
- m = mntchk(c);
- r = mntralloc(c, m->msize);
- if(waserror()) {
- mntfree(r);
- nexterror();
- }
- r->request.type = Twstat;
- r->request.fid = c->fid;
- r->request.nstat = n;
- r->request.stat = dp;
- mountrpc(m, r);
- poperror();
- mntfree(r);
- return n;
- }
- static long
- mntread(Chan *c, void *buf, long n, vlong off)
- {
- uchar *p, *e;
- int nc, cache, isdir, dirlen;
- isdir = 0;
- cache = c->flag & CCACHE;
- if(c->qid.type & QTDIR) {
- cache = 0;
- isdir = 1;
- }
- p = buf;
- if(cache) {
- nc = cread(c, buf, n, off);
- if(nc > 0) {
- n -= nc;
- if(n == 0)
- return nc;
- p += nc;
- off += nc;
- }
- n = mntrdwr(Tread, c, p, n, off);
- cupdate(c, p, n, off);
- return n + nc;
- }
- n = mntrdwr(Tread, c, buf, n, off);
- if(isdir) {
- for(e = &p[n]; p+BIT16SZ < e; p += dirlen){
- dirlen = BIT16SZ+GBIT16(p);
- if(p+dirlen > e)
- break;
- validstat(p, dirlen);
- mntdirfix(p, c);
- }
- if(p != e)
- error(Esbadstat);
- }
- return n;
- }
- static long
- mntwrite(Chan *c, void *buf, long n, vlong off)
- {
- return mntrdwr(Twrite, c, buf, n, off);
- }
- long
- mntrdwr(int type, Chan *c, void *buf, long n, vlong off)
- {
- Mnt *m;
- Mntrpc *r;
- char *uba;
- int cache;
- ulong cnt, nr, nreq;
- m = mntchk(c);
- uba = buf;
- cnt = 0;
- cache = c->flag & CCACHE;
- if(c->qid.type & QTDIR)
- cache = 0;
- for(;;) {
- r = mntralloc(c, m->msize);
- if(waserror()) {
- mntfree(r);
- nexterror();
- }
- r->request.type = type;
- r->request.fid = c->fid;
- r->request.offset = off;
- r->request.data = uba;
- nr = n;
- if(nr > m->msize-IOHDRSZ)
- nr = m->msize-IOHDRSZ;
- r->request.count = nr;
- mountrpc(m, r);
- nreq = r->request.count;
- nr = r->reply.count;
- if(nr > nreq)
- nr = nreq;
- if(type == Tread)
- r->b = bl2mem((uchar*)uba, r->b, nr);
- else if(cache)
- cwrite(c, (uchar*)uba, nr, off);
- poperror();
- mntfree(r);
- off += nr;
- uba += nr;
- cnt += nr;
- n -= nr;
- if(nr != nreq || n == 0 || up->nnote)
- break;
- }
- return cnt;
- }
- void
- mountrpc(Mnt *m, Mntrpc *r)
- {
- char *sn, *cn;
- int t;
- r->reply.tag = 0;
- r->reply.type = Tmax; /* can't ever be a valid message type */
- mountio(m, r);
- t = r->reply.type;
- switch(t) {
- case Rerror:
- error(r->reply.ename);
- case Rflush:
- error(Eintr);
- default:
- if(t == r->request.type+1)
- break;
- sn = "?";
- if(m->c->name != nil)
- sn = m->c->name->s;
- cn = "?";
- if(r->c != nil && r->c->name != nil)
- cn = r->c->name->s;
- print("mnt: proc %s %lud: mismatch from %s %s rep 0x%lux tag %d fid %d T%d R%d rp %d\n",
- up->text, up->pid, sn, cn,
- r, r->request.tag, r->request.fid, r->request.type,
- r->reply.type, r->reply.tag);
- error(Emountrpc);
- }
- }
- void
- mountio(Mnt *m, Mntrpc *r)
- {
- int n;
- while(waserror()) {
- if(m->rip == up)
- mntgate(m);
- if(strcmp(up->errstr, Eintr) != 0){
- mntflushfree(m, r);
- nexterror();
- }
- r = mntflushalloc(r, m->msize);
- }
- lock(m);
- r->m = m;
- r->list = m->queue;
- m->queue = r;
- unlock(m);
- /* Transmit a file system rpc */
- if(m->msize == 0)
- panic("msize");
- n = convS2M(&r->request, r->rpc, m->msize);
- if(n < 0)
- panic("bad message type in mountio");
- if(devtab[m->c->type]->write(m->c, r->rpc, n, 0) != n)
- error(Emountrpc);
- r->stime = fastticks(nil);
- r->reqlen = n;
- /* Gate readers onto the mount point one at a time */
- for(;;) {
- lock(m);
- if(m->rip == 0)
- break;
- unlock(m);
- sleep(&r->r, rpcattn, r);
- if(r->done){
- poperror();
- mntflushfree(m, r);
- return;
- }
- }
- m->rip = up;
- unlock(m);
- while(r->done == 0) {
- if(mntrpcread(m, r) < 0)
- error(Emountrpc);
- mountmux(m, r);
- }
- mntgate(m);
- poperror();
- mntflushfree(m, r);
- }
- static int
- doread(Mnt *m, int len)
- {
- Block *b;
- while(qlen(m->q) < len){
- b = devtab[m->c->type]->bread(m->c, m->msize, 0);
- if(b == nil)
- return -1;
- if(BLEN(b) == 0){
- freeblist(b);
- return -1;
- }
- qaddlist(m->q, b);
- }
- return 0;
- }
- int
- mntrpcread(Mnt *m, Mntrpc *r)
- {
- int i, t, len, hlen;
- Block *b, **l, *nb;
- r->reply.type = 0;
- r->reply.tag = 0;
- /* read at least length, type, and tag and pullup to a single block */
- if(doread(m, BIT32SZ+BIT8SZ+BIT16SZ) < 0)
- return -1;
- nb = pullupqueue(m->q, BIT32SZ+BIT8SZ+BIT16SZ);
- /* read in the rest of the message, avoid rediculous (for now) message sizes */
- len = GBIT32(nb->rp);
- if(len > m->msize){
- qdiscard(m->q, qlen(m->q));
- return -1;
- }
- if(doread(m, len) < 0)
- return -1;
- /* pullup the header (i.e. everything except data) */
- t = nb->rp[BIT32SZ];
- switch(t){
- case Rread:
- hlen = BIT32SZ+BIT8SZ+BIT16SZ+BIT32SZ;
- break;
- default:
- hlen = len;
- break;
- }
- nb = pullupqueue(m->q, hlen);
- if(convM2S(nb->rp, len, &r->reply) <= 0){
- /* bad message, dump it */
- print("mntrpcread: convM2S failed\n");
- qdiscard(m->q, len);
- return -1;
- }
- /* hang the data off of the fcall struct */
- l = &r->b;
- *l = nil;
- do {
- b = qremove(m->q);
- if(hlen > 0){
- b->rp += hlen;
- len -= hlen;
- hlen = 0;
- }
- i = BLEN(b);
- if(i <= len){
- len -= i;
- *l = b;
- l = &(b->next);
- } else {
- /* split block and put unused bit back */
- nb = allocb(i-len);
- memmove(nb->wp, b->rp+len, i-len);
- b->wp = b->rp+len;
- nb->wp += i-len;
- qputback(m->q, nb);
- *l = b;
- return 0;
- }
- }while(len > 0);
- return 0;
- }
- void
- mntgate(Mnt *m)
- {
- Mntrpc *q;
- lock(m);
- m->rip = 0;
- for(q = m->queue; q; q = q->list) {
- if(q->done == 0)
- if(wakeup(&q->r))
- break;
- }
- unlock(m);
- }
- void
- mountmux(Mnt *m, Mntrpc *r)
- {
- Mntrpc **l, *q;
- lock(m);
- l = &m->queue;
- for(q = *l; q; q = q->list) {
- /* look for a reply to a message */
- if(q->request.tag == r->reply.tag) {
- *l = q->list;
- if(q != r) {
- /*
- * Completed someone else.
- * Trade pointers to receive buffer.
- */
- q->reply = r->reply;
- q->b = r->b;
- r->b = nil;
- }
- q->done = 1;
- unlock(m);
- if(mntstats != nil)
- (*mntstats)(q->request.type,
- m->c, q->stime,
- q->reqlen + r->replen);
- if(q != r)
- wakeup(&q->r);
- return;
- }
- l = &q->list;
- }
- unlock(m);
- print("unexpected reply tag %ud; type %d\n", r->reply.tag, r->reply.type);
- }
- /*
- * Create a new flush request and chain the previous
- * requests from it
- */
- Mntrpc*
- mntflushalloc(Mntrpc *r, ulong iounit)
- {
- Mntrpc *fr;
- fr = mntralloc(0, iounit);
- fr->request.type = Tflush;
- if(r->request.type == Tflush)
- fr->request.oldtag = r->request.oldtag;
- else
- fr->request.oldtag = r->request.tag;
- fr->flushed = r;
- return fr;
- }
- /*
- * Free a chain of flushes. Remove each unanswered
- * flush and the original message from the unanswered
- * request queue. Mark the original message as done
- * and if it hasn't been answered set the reply to to
- * Rflush.
- */
- void
- mntflushfree(Mnt *m, Mntrpc *r)
- {
- Mntrpc *fr;
- while(r){
- fr = r->flushed;
- if(!r->done){
- r->reply.type = Rflush;
- mntqrm(m, r);
- }
- if(fr)
- mntfree(r);
- r = fr;
- }
- }
- int
- alloctag(void)
- {
- int i, j;
- ulong v;
- for(i = 0; i < NMASK; i++){
- v = mntalloc.tagmask[i];
- if(v == ~0UL)
- continue;
- for(j = 0; j < 1<<TAGSHIFT; j++)
- if((v & (1<<j)) == 0){
- mntalloc.tagmask[i] |= 1<<j;
- return (i<<TAGSHIFT) + j;
- }
- }
- panic("no friggin tags left");
- return NOTAG;
- }
- void
- freetag(int t)
- {
- mntalloc.tagmask[t>>TAGSHIFT] &= ~(1<<(t&TAGMASK));
- }
- Mntrpc*
- mntralloc(Chan *c, ulong msize)
- {
- Mntrpc *new;
- lock(&mntalloc);
- new = mntalloc.rpcfree;
- if(new == nil){
- new = malloc(sizeof(Mntrpc));
- if(new == nil) {
- unlock(&mntalloc);
- exhausted("mount rpc header");
- }
- /*
- * The header is split from the data buffer as
- * mountmux may swap the buffer with another header.
- */
- new->rpc = mallocz(msize, 0);
- if(new->rpc == nil){
- free(new);
- unlock(&mntalloc);
- exhausted("mount rpc buffer");
- }
- new->rpclen = msize;
- new->request.tag = alloctag();
- }
- else {
- mntalloc.rpcfree = new->list;
- mntalloc.nrpcfree--;
- if(new->rpclen < msize){
- free(new->rpc);
- new->rpc = mallocz(msize, 0);
- if(new->rpc == nil){
- free(new);
- mntalloc.nrpcused--;
- unlock(&mntalloc);
- exhausted("mount rpc buffer");
- }
- new->rpclen = msize;
- }
- }
- mntalloc.nrpcused++;
- unlock(&mntalloc);
- new->c = c;
- new->done = 0;
- new->flushed = nil;
- new->b = nil;
- return new;
- }
- void
- mntfree(Mntrpc *r)
- {
- if(r->b != nil)
- freeblist(r->b);
- lock(&mntalloc);
- if(mntalloc.nrpcfree >= 10){
- free(r->rpc);
- free(r);
- freetag(r->request.tag);
- }
- else{
- r->list = mntalloc.rpcfree;
- mntalloc.rpcfree = r;
- mntalloc.nrpcfree++;
- }
- mntalloc.nrpcused--;
- unlock(&mntalloc);
- }
- void
- mntqrm(Mnt *m, Mntrpc *r)
- {
- Mntrpc **l, *f;
- lock(m);
- r->done = 1;
- l = &m->queue;
- for(f = *l; f; f = f->list) {
- if(f == r) {
- *l = r->list;
- break;
- }
- l = &f->list;
- }
- unlock(m);
- }
- Mnt*
- mntchk(Chan *c)
- {
- Mnt *m;
- /* This routine is mostly vestiges of prior lives; now it's just sanity checking */
- if(c->mchan == nil)
- panic("mntchk 1: nil mchan c %s\n", c2name(c));
- m = c->mchan->mux;
- if(m == nil)
- print("mntchk 2: nil mux c %s c->mchan %s \n", c2name(c), c2name(c->mchan));
- /*
- * Was it closed and reused (was error(Eshutdown); now, it can't happen)
- */
- if(m->id == 0 || m->id >= c->dev)
- panic("mntchk 3: can't happen");
- return m;
- }
- /*
- * Rewrite channel type and dev for in-flight data to
- * reflect local values. These entries are known to be
- * the first two in the Dir encoding after the count.
- */
- void
- mntdirfix(uchar *dirbuf, Chan *c)
- {
- uint r;
- r = devtab[c->type]->dc;
- dirbuf += BIT16SZ; /* skip count */
- PBIT16(dirbuf, r);
- dirbuf += BIT16SZ;
- PBIT32(dirbuf, c->dev);
- }
- int
- rpcattn(void *v)
- {
- Mntrpc *r;
- r = v;
- return r->done || r->m->rip == 0;
- }
- Dev mntdevtab = {
- 'M',
- "mnt",
- mntreset,
- devinit,
- devshutdown,
- mntattach,
- mntwalk,
- mntstat,
- mntopen,
- mntcreate,
- mntclose,
- mntread,
- devbread,
- mntwrite,
- devbwrite,
- mntremove,
- mntwstat,
- };
|