devmnt.c - vx32 - Local 9vx git repository for patches.
 (HTM) git clone git://r-36.net/vx32
 (DIR) Log
 (DIR) Files
 (DIR) Refs
       ---
       devmnt.c (21327B)
       ---
            1 #include        "u.h"
            2 #include        "lib.h"
            3 #include        "mem.h"
            4 #include        "dat.h"
            5 #include        "fns.h"
            6 #include        "error.h"
            7 
            8 /*
            9  * References are managed as follows:
           10  * The channel to the server - a network connection or pipe - has one
           11  * reference for every Chan open on the server.  The server channel has
           12  * c->mux set to the Mnt used for muxing control to that server.  Mnts
           13  * have no reference count; they go away when c goes away.
           14  * Each channel derived from the mount point has mchan set to c,
           15  * and increfs/decrefs mchan to manage references on the server
           16  * connection.
           17  */
           18 
           19 #define MAXRPC (IOHDRSZ+8192)
           20 
           21 struct Mntrpc
           22 {
           23         Chan*        c;                /* Channel for whom we are working */
           24         Mntrpc*        list;                /* Free/pending list */
           25         Fcall        request;        /* Outgoing file system protocol message */
           26         Fcall         reply;                /* Incoming reply */
           27         Mnt*        m;                /* Mount device during rpc */
           28         Rendez        r;                /* Place to hang out */
           29         uchar*        rpc;                /* I/O Data buffer */
           30         uint        rpclen;                /* len of buffer */
           31         Block        *b;                /* reply blocks */
           32         char        done;                /* Rpc completed */
           33         uvlong        stime;                /* start time for mnt statistics */
           34         ulong        reqlen;                /* request length for mnt statistics */
           35         ulong        replen;                /* reply length for mnt statistics */
           36         Mntrpc*        flushed;        /* message this one flushes */
           37 };
           38 
           39 enum
           40 {
           41         TAGSHIFT = 5,                        /* ulong has to be 32 bits */
           42         TAGMASK = (1<<TAGSHIFT)-1,
           43         NMASK = (64*1024)>>TAGSHIFT,
           44 };
           45 
           46 struct Mntalloc
           47 {
           48         Lock lk;
           49         Mnt*        list;                /* Mount devices in use */
           50         Mnt*        mntfree;        /* Free list */
           51         Mntrpc*        rpcfree;
           52         int        nrpcfree;
           53         int        nrpcused;
           54         ulong        id;
           55         ulong        tagmask[NMASK];
           56 }mntalloc;
           57 
           58 Mnt*        mntchk(Chan*);
           59 void        mntdirfix(uchar*, Chan*);
           60 Mntrpc*        mntflushalloc(Mntrpc*, ulong);
           61 void        mntflushfree(Mnt*, Mntrpc*);
           62 void        mntfree(Mntrpc*);
           63 void        mntgate(Mnt*);
           64 void        mntpntfree(Mnt*);
           65 void        mntqrm(Mnt*, Mntrpc*);
           66 Mntrpc*        mntralloc(Chan*, ulong);
           67 long        mntrdwr(int, Chan*, void*, long, vlong);
           68 int        mntrpcread(Mnt*, Mntrpc*);
           69 void        mountio(Mnt*, Mntrpc*);
           70 void        mountmux(Mnt*, Mntrpc*);
           71 void        mountrpc(Mnt*, Mntrpc*);
           72 int        rpcattn(void*);
           73 Chan*        mntchan(void);
           74 
           75 char        Esbadstat[] = "invalid directory entry received from server";
           76 char        Enoversion[] = "version not established for mount channel";
           77 
           78 
           79 void (*mntstats)(int, Chan*, uvlong, ulong);
           80 
           81 static void
           82 mntreset(void)
           83 {
           84         mntalloc.id = 1;
           85         mntalloc.tagmask[0] = 1;                        /* don't allow 0 as a tag */
           86         mntalloc.tagmask[NMASK-1] = 0x80000000UL;        /* don't allow NOTAG */
           87         fmtinstall('F', fcallfmt);
           88         fmtinstall('D', dirfmt);
           89 /* We can't install %M since eipfmt does and is used in the kernel [sape] */
           90 
           91         cinit();
           92 }
           93 
           94 /*
           95  * Version is not multiplexed: message sent only once per connection.
           96  */
           97 long
           98 mntversion(Chan *c, char *version, int msize, int returnlen)
           99 {
          100         Fcall f;
          101         uchar *msg;
          102         Mnt *m;
          103         char *v;
          104         long k, l;
          105         uvlong oo;
          106         char buf[128];
          107 
          108         qlock(&c->umqlock);        /* make sure no one else does this until we've established ourselves */
          109         if(waserror()){
          110                 qunlock(&c->umqlock);
          111                 nexterror();
          112         }
          113 
          114         /* defaults */
          115         if(msize == 0)
          116                 msize = MAXRPC;
          117         if(msize > c->iounit && c->iounit != 0)
          118                 msize = c->iounit;
          119         v = version;
          120         if(v == nil || v[0] == '\0')
          121                 v = VERSION9P;
          122 
          123         /* validity */
          124         if(msize < 0)
          125                 error("bad iounit in version call");
          126         if(strncmp(v, VERSION9P, strlen(VERSION9P)) != 0)
          127                 error("bad 9P version specification");
          128 
          129         m = c->mux;
          130 
          131         if(m != nil){
          132                 qunlock(&c->umqlock);
          133                 poperror();
          134 
          135                 strecpy(buf, buf+sizeof buf, m->version);
          136                 k = strlen(buf);
          137                 if(strncmp(buf, v, k) != 0){
          138                         snprint(buf, sizeof buf, "incompatible 9P versions %s %s", m->version, v);
          139                         error(buf);
          140                 }
          141                 if(returnlen > 0){
          142                         if(returnlen < k)
          143                                 error(Eshort);
          144                         memmove(version, buf, k);
          145                 }
          146                 return k;
          147         }
          148 
          149         f.type = Tversion;
          150         f.tag = NOTAG;
          151         f.msize = msize;
          152         f.version = v;
          153         msg = malloc(8192+IOHDRSZ);
          154         if(msg == nil)
          155                 exhausted("version memory");
          156         if(waserror()){
          157                 free(msg);
          158                 nexterror();
          159         }
          160         k = convS2M(&f, msg, 8192+IOHDRSZ);
          161         if(k == 0)
          162                 error("bad fversion conversion on send");
          163 
          164         lock(&c->ref.lk);
          165         oo = c->offset;
          166         c->offset += k;
          167         unlock(&c->ref.lk);
          168 
          169         l = devtab[c->type]->write(c, msg, k, oo);
          170 
          171         if(l < k){
          172                 lock(&c->ref.lk);
          173                 c->offset -= k - l;
          174                 unlock(&c->ref.lk);
          175                 error("short write in fversion");
          176         }
          177 
          178         /* message sent; receive and decode reply */
          179         k = devtab[c->type]->read(c, msg, 8192+IOHDRSZ, c->offset);
          180         if(k <= 0)
          181                 error("EOF receiving fversion reply");
          182 
          183         lock(&c->ref.lk);
          184         c->offset += k;
          185         unlock(&c->ref.lk);
          186 
          187         l = convM2S(msg, k, &f);
          188         if(l != k)
          189                 error("bad fversion conversion on reply");
          190         if(f.type != Rversion){
          191                 if(f.type == Rerror)
          192                         error(f.ename);
          193                 error("unexpected reply type in fversion");
          194         }
          195         if(f.msize > msize)
          196                 error("server tries to increase msize in fversion");
          197         if(f.msize<256 || f.msize>1024*1024)
          198                 error("nonsense value of msize in fversion");
          199         k = strlen(f.version);
          200         if(strncmp(f.version, v, k) != 0)
          201                 error("bad 9P version returned from server");
          202 
          203         /* now build Mnt associated with this connection */
          204         lock(&mntalloc.lk);
          205         m = mntalloc.mntfree;
          206         if(m != 0)
          207                 mntalloc.mntfree = m->list;
          208         else {
          209                 m = malloc(sizeof(Mnt));
          210                 if(m == 0) {
          211                         unlock(&mntalloc.lk);
          212                         exhausted("mount devices");
          213                 }
          214         }
          215         m->list = mntalloc.list;
          216         mntalloc.list = m;
          217         m->version = nil;
          218         kstrdup(&m->version, f.version);
          219         m->id = mntalloc.id++;
          220         m->q = qopen(10*MAXRPC, 0, nil, nil);
          221         m->msize = f.msize;
          222         unlock(&mntalloc.lk);
          223 
          224         if(returnlen > 0){
          225                 if(returnlen < k)
          226                         error(Eshort);
          227                 memmove(version, f.version, k);
          228         }
          229 
          230         poperror();        /* msg */
          231         free(msg);
          232 
          233         lock(&m->lk);
          234         m->queue = 0;
          235         m->rip = 0;
          236 
          237         c->flag |= CMSG;
          238         c->mux = m;
          239         m->c = c;
          240         unlock(&m->lk);
          241 
          242         poperror();        /* c */
          243         qunlock(&c->umqlock);
          244 
          245         return k;
          246 }
          247 
          248 Chan*
          249 mntauth(Chan *c, char *spec)
          250 {
          251         Mnt *m;
          252         Mntrpc *r;
          253 
          254         m = c->mux;
          255 
          256         if(m == nil){
          257                 mntversion(c, VERSION9P, MAXRPC, 0);
          258                 m = c->mux;
          259                 if(m == nil)
          260                         error(Enoversion);
          261         }
          262 
          263         c = mntchan();
          264         if(waserror()) {
          265                 /* Close must not be called since it will
          266                  * call mnt recursively
          267                  */
          268                 chanfree(c);
          269                 nexterror();
          270         }
          271 
          272         r = mntralloc(0, m->msize);
          273 
          274         if(waserror()) {
          275                 mntfree(r);
          276                 nexterror();
          277         }
          278 
          279         r->request.type = Tauth;
          280         r->request.afid = c->fid;
          281         r->request.uname = up->user;
          282         r->request.aname = spec;
          283         mountrpc(m, r);
          284 
          285         c->qid = r->reply.aqid;
          286         c->mchan = m->c;
          287         incref(&m->c->ref);
          288         c->mqid = c->qid;
          289         c->mode = ORDWR;
          290 
          291         poperror();        /* r */
          292         mntfree(r);
          293 
          294         poperror();        /* c */
          295 
          296         return c;
          297 
          298 }
          299 
          300 static Chan*
          301 mntattach(char *muxattach)
          302 {
          303         Mnt *m;
          304         Chan *c;
          305         Mntrpc *r;
          306         struct bogus{
          307                 Chan        *chan;
          308                 Chan        *authchan;
          309                 char        *spec;
          310                 int        flags;
          311         }bogus;
          312 
          313         bogus = *((struct bogus *)muxattach);
          314         c = bogus.chan;
          315 
          316         { // Plan 9 VX addition
          317                 extern Dev mntloopdevtab;
          318                 Chan *mc;
          319                 if(devtab[c->type] == &mntloopdevtab){
          320                         if(bogus.authchan || (bogus.spec && bogus.spec[0]))
          321                                 error(Ebadarg);
          322                         mc = c->aux;
          323                         incref(&mc->ref);
          324                         return mc;
          325                 }
          326         }
          327 
          328 
          329         m = c->mux;
          330 
          331         if(m == nil){
          332                 mntversion(c, nil, 0, 0);
          333                 m = c->mux;
          334                 if(m == nil)
          335                         error(Enoversion);
          336         }
          337 
          338         c = mntchan();
          339         if(waserror()) {
          340                 /* Close must not be called since it will
          341                  * call mnt recursively
          342                  */
          343                 chanfree(c);
          344                 nexterror();
          345         }
          346 
          347         r = mntralloc(0, m->msize);
          348 
          349         if(waserror()) {
          350                 mntfree(r);
          351                 nexterror();
          352         }
          353 
          354         r->request.type = Tattach;
          355         r->request.fid = c->fid;
          356         if(bogus.authchan == nil)
          357                 r->request.afid = NOFID;
          358         else
          359                 r->request.afid = bogus.authchan->fid;
          360         r->request.uname = up->user;
          361         r->request.aname = bogus.spec;
          362         mountrpc(m, r);
          363 
          364         c->qid = r->reply.qid;
          365         c->mchan = m->c;
          366         incref(&m->c->ref);
          367         c->mqid = c->qid;
          368 
          369         poperror();        /* r */
          370         mntfree(r);
          371 
          372         poperror();        /* c */
          373 
          374         if(bogus.flags&MCACHE)
          375                 c->flag |= CCACHE;
          376         return c;
          377 }
          378 
          379 Chan*
          380 mntchan(void)
          381 {
          382         Chan *c;
          383 
          384         c = devattach('M', 0);
          385         lock(&mntalloc.lk);
          386         c->dev = mntalloc.id++;
          387         unlock(&mntalloc.lk);
          388 
          389         if(c->mchan)
          390                 panic("mntchan non-zero %p", c->mchan);
          391         return c;
          392 }
          393 
          394 static Walkqid*
          395 mntwalk(Chan *c, Chan *nc, char **name, int nname)
          396 {
          397         int i, alloc;
          398         Mnt *m;
          399         Mntrpc *r;
          400         Walkqid *wq;
          401 
          402         if(nc != nil)
          403                 print("mntwalk: nc != nil\n");
          404         if(nname > MAXWELEM)
          405                 error("devmnt: too many name elements");
          406         alloc = 0;
          407         wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid));
          408         if(waserror()){
          409                 if(alloc && wq->clone!=nil)
          410                         cclose(wq->clone);
          411                 free(wq);
          412                 return nil;
          413         }
          414 
          415         alloc = 0;
          416         m = mntchk(c);
          417         r = mntralloc(c, m->msize);
          418         if(nc == nil){
          419                 nc = devclone(c);
          420                 /*
          421                  * Until the other side accepts this fid, we can't mntclose it.
          422                  * Therefore set type to 0 for now; rootclose is known to be safe.
          423                  */
          424                 nc->type = 0;
          425                 alloc = 1;
          426         }
          427         wq->clone = nc;
          428 
          429         if(waserror()) {
          430                 mntfree(r);
          431                 nexterror();
          432         }
          433         r->request.type = Twalk;
          434         r->request.fid = c->fid;
          435         r->request.newfid = nc->fid;
          436         r->request.nwname = nname;
          437         memmove(r->request.wname, name, nname*sizeof(char*));
          438 
          439         mountrpc(m, r);
          440 
          441         if(r->reply.nwqid > nname)
          442                 error("too many QIDs returned by walk");
          443         if(r->reply.nwqid < nname){
          444                 if(alloc)
          445                         cclose(nc);
          446                 wq->clone = nil;
          447                 if(r->reply.nwqid == 0){
          448                         free(wq);
          449                         wq = nil;
          450                         goto Return;
          451                 }
          452         }
          453 
          454         /* move new fid onto mnt device and update its qid */
          455         if(wq->clone != nil){
          456                 if(wq->clone != c){
          457                         wq->clone->type = c->type;
          458                         wq->clone->mchan = c->mchan;
          459                         incref(&c->mchan->ref);
          460                 }
          461                 if(r->reply.nwqid > 0)
          462                         wq->clone->qid = r->reply.wqid[r->reply.nwqid-1];
          463         }
          464         wq->nqid = r->reply.nwqid;
          465         for(i=0; i<wq->nqid; i++)
          466                 wq->qid[i] = r->reply.wqid[i];
          467 
          468     Return:
          469         poperror();
          470         mntfree(r);
          471         poperror();
          472         return wq;
          473 }
          474 
          475 static int
          476 mntstat(Chan *c, uchar *dp, int n)
          477 {
          478         Mnt *m;
          479         Mntrpc *r;
          480 
          481         if(n < BIT16SZ)
          482                 error(Eshortstat);
          483         m = mntchk(c);
          484         r = mntralloc(c, m->msize);
          485         if(waserror()) {
          486                 mntfree(r);
          487                 nexterror();
          488         }
          489         r->request.type = Tstat;
          490         r->request.fid = c->fid;
          491         mountrpc(m, r);
          492 
          493         if(r->reply.nstat > n){
          494                 n = BIT16SZ;
          495                 PBIT16((uchar*)dp, r->reply.nstat-2);
          496         }else{
          497                 n = r->reply.nstat;
          498                 memmove(dp, r->reply.stat, n);
          499                 validstat(dp, n);
          500                 mntdirfix(dp, c);
          501         }
          502         poperror();
          503         mntfree(r);
          504         return n;
          505 }
          506 
          507 static Chan*
          508 mntopencreate(int type, Chan *c, char *name, int omode, ulong perm)
          509 {
          510         Mnt *m;
          511         Mntrpc *r;
          512 
          513         m = mntchk(c);
          514         r = mntralloc(c, m->msize);
          515         if(waserror()) {
          516                 mntfree(r);
          517                 nexterror();
          518         }
          519         r->request.type = type;
          520         r->request.fid = c->fid;
          521         r->request.mode = omode;
          522         if(type == Tcreate){
          523                 r->request.perm = perm;
          524                 r->request.name = name;
          525         }
          526         mountrpc(m, r);
          527 
          528         c->qid = r->reply.qid;
          529         c->offset = 0;
          530         c->mode = openmode(omode);
          531         c->iounit = r->reply.iounit;
          532         if(c->iounit == 0 || c->iounit > m->msize-IOHDRSZ)
          533                 c->iounit = m->msize-IOHDRSZ;
          534         c->flag |= COPEN;
          535         poperror();
          536         mntfree(r);
          537 
          538         if(c->flag & CCACHE)
          539                 copen(c);
          540 
          541         return c;
          542 }
          543 
          544 static Chan*
          545 mntopen(Chan *c, int omode)
          546 {
          547         return mntopencreate(Topen, c, nil, omode, 0);
          548 }
          549 
          550 static void
          551 mntcreate(Chan *c, char *name, int omode, ulong perm)
          552 {
          553         mntopencreate(Tcreate, c, name, omode, perm);
          554 }
          555 
          556 static void
          557 mntclunk(Chan *c, int t)
          558 {
          559         Mnt *m;
          560         Mntrpc *r;
          561 
          562         m = mntchk(c);
          563         r = mntralloc(c, m->msize);
          564         if(waserror()){
          565                 mntfree(r);
          566                 nexterror();
          567         }
          568 
          569         r->request.type = t;
          570         r->request.fid = c->fid;
          571         mountrpc(m, r);
          572         mntfree(r);
          573         poperror();
          574 }
          575 
          576 void
          577 muxclose(Mnt *m)
          578 {
          579         Mntrpc *q, *r;
          580 
          581         for(q = m->queue; q; q = r) {
          582                 r = q->list;
          583                 mntfree(q);
          584         }
          585         m->id = 0;
          586         free(m->version);
          587         m->version = nil;
          588         mntpntfree(m);
          589 }
          590 
          591 void
          592 mntpntfree(Mnt *m)
          593 {
          594         Mnt *f, **l;
          595         Queue *q;
          596 
          597         lock(&mntalloc.lk);
          598         l = &mntalloc.list;
          599         for(f = *l; f; f = f->list) {
          600                 if(f == m) {
          601                         *l = m->list;
          602                         break;
          603                 }
          604                 l = &f->list;
          605         }
          606         m->list = mntalloc.mntfree;
          607         mntalloc.mntfree = m;
          608         q = m->q;
          609         unlock(&mntalloc.lk);
          610 
          611         qfree(q);
          612 }
          613 
          614 static void
          615 mntclose(Chan *c)
          616 {
          617         mntclunk(c, Tclunk);
          618 }
          619 
          620 static void
          621 mntremove(Chan *c)
          622 {
          623         mntclunk(c, Tremove);
          624 }
          625 
          626 static int
          627 mntwstat(Chan *c, uchar *dp, int n)
          628 {
          629         Mnt *m;
          630         Mntrpc *r;
          631 
          632         m = mntchk(c);
          633         r = mntralloc(c, m->msize);
          634         if(waserror()) {
          635                 mntfree(r);
          636                 nexterror();
          637         }
          638         r->request.type = Twstat;
          639         r->request.fid = c->fid;
          640         r->request.nstat = n;
          641         r->request.stat = dp;
          642         mountrpc(m, r);
          643         poperror();
          644         mntfree(r);
          645         return n;
          646 }
          647 
          648 static long
          649 mntread(Chan *c, void *buf, long n, vlong off)
          650 {
          651         uchar *p, *e;
          652         int nc, cache, isdir, dirlen;
          653 
          654         isdir = 0;
          655         cache = c->flag & CCACHE;
          656         if(c->qid.type & QTDIR) {
          657                 cache = 0;
          658                 isdir = 1;
          659         }
          660 
          661         p = buf;
          662         if(cache) {
          663                 nc = cread(c, buf, n, off);
          664                 if(nc > 0) {
          665                         n -= nc;
          666                         if(n == 0)
          667                                 return nc;
          668                         p += nc;
          669                         off += nc;
          670                 }
          671                 n = mntrdwr(Tread, c, p, n, off);
          672                 cupdate(c, p, n, off);
          673                 return n + nc;
          674         }
          675 
          676         n = mntrdwr(Tread, c, buf, n, off);
          677         if(isdir) {
          678                 for(e = &p[n]; p+BIT16SZ < e; p += dirlen){
          679                         dirlen = BIT16SZ+GBIT16(p);
          680                         if(p+dirlen > e)
          681                                 break;
          682                         validstat(p, dirlen);
          683                         mntdirfix(p, c);
          684                 }
          685                 if(p != e)
          686                         error(Esbadstat);
          687         }
          688         return n;
          689 }
          690 
          691 static long
          692 mntwrite(Chan *c, void *buf, long n, vlong off)
          693 {
          694         return mntrdwr(Twrite, c, buf, n, off);
          695 }
          696 
          697 long
          698 mntrdwr(int type, Chan *c, void *buf, long n, vlong off)
          699 {
          700         Mnt *m;
          701          Mntrpc *r;
          702         char *uba;
          703         int cache;
          704         ulong cnt, nr, nreq;
          705 
          706         m = mntchk(c);
          707         uba = buf;
          708         cnt = 0;
          709         cache = c->flag & CCACHE;
          710         if(c->qid.type & QTDIR)
          711                 cache = 0;
          712         for(;;) {
          713                 r = mntralloc(c, m->msize);
          714                 if(waserror()) {
          715                         mntfree(r);
          716                         nexterror();
          717                 }
          718                 r->request.type = type;
          719                 r->request.fid = c->fid;
          720                 r->request.offset = off;
          721                 r->request.data = uba;
          722                 nr = n;
          723                 if(nr > m->msize-IOHDRSZ)
          724                         nr = m->msize-IOHDRSZ;
          725                 r->request.count = nr;
          726                 mountrpc(m, r);
          727                 nreq = r->request.count;
          728                 nr = r->reply.count;
          729                 if(nr > nreq)
          730                         nr = nreq;
          731 
          732                 if(type == Tread)
          733                         r->b = bl2mem((uchar*)uba, r->b, nr);
          734                 else if(cache)
          735                         cwrite(c, (uchar*)uba, nr, off);
          736 
          737                 poperror();
          738                 mntfree(r);
          739                 off += nr;
          740                 uba += nr;
          741                 cnt += nr;
          742                 n -= nr;
          743                 if(nr != nreq || n == 0 || up->nnote)
          744                         break;
          745         }
          746         return cnt;
          747 }
          748 
          749 void
          750 mountrpc(Mnt *m, Mntrpc *r)
          751 {
          752         char *sn, *cn;
          753         int t;
          754 
          755         r->reply.tag = 0;
          756         r->reply.type = Tmax;        /* can't ever be a valid message type */
          757 
          758         mountio(m, r);
          759 
          760         t = r->reply.type;
          761         switch(t) {
          762         case Rerror:
          763                 error(r->reply.ename);
          764         case Rflush:
          765                 error(Eintr);
          766         default:
          767                 if(t == r->request.type+1)
          768                         break;
          769                 sn = "?";
          770                 if(m->c->path != nil)
          771                         sn = m->c->path->s;
          772                 cn = "?";
          773                 if(r->c != nil && r->c->path != nil)
          774                         cn = r->c->path->s;
          775                 print("mnt: proc %s %lud: mismatch from %s %s rep %#p tag %d fid %d T%d R%d rp %d\n",
          776                         up->text, up->pid, sn, cn,
          777                         r, r->request.tag, r->request.fid, r->request.type,
          778                         r->reply.type, r->reply.tag);
          779                 error(Emountrpc);
          780         }
          781 }
          782 
          783 void
          784 mountio(Mnt *m, Mntrpc *r)
          785 {
          786         int n;
          787 
          788         while(waserror()) {
          789                 if(m->rip == up)
          790                         mntgate(m);
          791                 if(strcmp(up->errstr, Eintr) != 0){
          792                         mntflushfree(m, r);
          793                         nexterror();
          794                 }
          795                 r = mntflushalloc(r, m->msize);
          796         }
          797 
          798         lock(&m->lk);
          799         r->m = m;
          800         r->list = m->queue;
          801         m->queue = r;
          802         unlock(&m->lk);
          803 
          804         /* Transmit a file system rpc */
          805         if(m->msize == 0)
          806                 panic("msize");
          807         n = convS2M(&r->request, r->rpc, m->msize);
          808         if(n < 0)
          809                 panic("bad message type in mountio");
          810         if(devtab[m->c->type]->write(m->c, r->rpc, n, 0) != n)
          811                 error(Emountrpc);
          812         r->stime = fastticks(nil);
          813         r->reqlen = n;
          814 
          815         /* Gate readers onto the mount point one at a time */
          816         for(;;) {
          817                 lock(&m->lk);
          818                 if(m->rip == 0)
          819                         break;
          820                 unlock(&m->lk);
          821                 sleep(&r->r, rpcattn, r);
          822                 if(r->done){
          823                         poperror();
          824                         mntflushfree(m, r);
          825                         return;
          826                 }
          827         }
          828         m->rip = up;
          829         unlock(&m->lk);
          830         while(r->done == 0) {
          831                 if(mntrpcread(m, r) < 0)
          832                         error(Emountrpc);
          833                 mountmux(m, r);
          834         }
          835         mntgate(m);
          836         poperror();
          837         mntflushfree(m, r);
          838 }
          839 
          840 static int
          841 doread(Mnt *m, int len)
          842 {
          843         Block *b;
          844 
          845         while(qlen(m->q) < len){
          846                 b = devtab[m->c->type]->bread(m->c, m->msize, 0);
          847                 if(b == nil)
          848                         return -1;
          849                 if(blocklen(b) == 0){
          850                         freeblist(b);
          851                         return -1;
          852                 }
          853                 qaddlist(m->q, b);
          854         }
          855         return 0;
          856 }
          857 
          858 int
          859 mntrpcread(Mnt *m, Mntrpc *r)
          860 {
          861         int i, t, len, hlen;
          862         Block *b, **l, *nb;
          863 
          864         r->reply.type = 0;
          865         r->reply.tag = 0;
          866 
          867         /* read at least length, type, and tag and pullup to a single block */
          868         if(doread(m, BIT32SZ+BIT8SZ+BIT16SZ) < 0)
          869                 return -1;
          870         nb = pullupqueue(m->q, BIT32SZ+BIT8SZ+BIT16SZ);
          871 
          872         /* read in the rest of the message, avoid ridiculous (for now) message sizes */
          873         len = GBIT32(nb->rp);
          874         if(len > m->msize){
          875                 qdiscard(m->q, qlen(m->q));
          876                 return -1;
          877         }
          878         if(doread(m, len) < 0)
          879                 return -1;
          880 
          881         /* pullup the header (i.e. everything except data) */
          882         t = nb->rp[BIT32SZ];
          883         switch(t){
          884         case Rread:
          885                 hlen = BIT32SZ+BIT8SZ+BIT16SZ+BIT32SZ;
          886                 break;
          887         default:
          888                 hlen = len;
          889                 break;
          890         }
          891         nb = pullupqueue(m->q, hlen);
          892 
          893         if(convM2S(nb->rp, len, &r->reply) <= 0){
          894                 /* bad message, dump it */
          895                 print("mntrpcread: convM2S failed\n");
          896                 qdiscard(m->q, len);
          897                 return -1;
          898         }
          899 
          900         /* hang the data off of the fcall struct */
          901         l = &r->b;
          902         *l = nil;
          903         do {
          904                 b = qremove(m->q);
          905                 if(hlen > 0){
          906                         b->rp += hlen;
          907                         len -= hlen;
          908                         hlen = 0;
          909                 }
          910                 i = BLEN(b);
          911                 if(i <= len){
          912                         len -= i;
          913                         *l = b;
          914                         l = &(b->next);
          915                 } else {
          916                         /* split block and put unused bit back */
          917                         nb = allocb(i-len);
          918                         memmove(nb->wp, b->rp+len, i-len);
          919                         b->wp = b->rp+len;
          920                         nb->wp += i-len;
          921                         qputback(m->q, nb);
          922                         *l = b;
          923                         return 0;
          924                 }
          925         }while(len > 0);
          926 
          927         return 0;
          928 }
          929 
          930 void
          931 mntgate(Mnt *m)
          932 {
          933         Mntrpc *q;
          934 
          935         lock(&m->lk);
          936         m->rip = 0;
          937         for(q = m->queue; q; q = q->list) {
          938                 if(q->done == 0)
          939                 if(wakeup(&q->r))
          940                         break;
          941         }
          942         unlock(&m->lk);
          943 }
          944 
          945 void
          946 mountmux(Mnt *m, Mntrpc *r)
          947 {
          948         Mntrpc **l, *q;
          949 
          950         lock(&m->lk);
          951         l = &m->queue;
          952         for(q = *l; q; q = q->list) {
          953                 /* look for a reply to a message */
          954                 if(q->request.tag == r->reply.tag) {
          955                         *l = q->list;
          956                         if(q != r) {
          957                                 /*
          958                                  * Completed someone else.
          959                                  * Trade pointers to receive buffer.
          960                                  */
          961                                 q->reply = r->reply;
          962                                 q->b = r->b;
          963                                 r->b = nil;
          964                         }
          965                         q->done = 1;
          966                         unlock(&m->lk);
          967                         if(mntstats != nil)
          968                                 (*mntstats)(q->request.type,
          969                                         m->c, q->stime,
          970                                         q->reqlen + r->replen);
          971                         if(q != r)
          972                                 wakeup(&q->r);
          973                         return;
          974                 }
          975                 l = &q->list;
          976         }
          977         unlock(&m->lk);
          978         print("unexpected reply tag %ud; type %d\n", r->reply.tag, r->reply.type);
          979 }
          980 
          981 /*
          982  * Create a new flush request and chain the previous
          983  * requests from it
          984  */
          985 Mntrpc*
          986 mntflushalloc(Mntrpc *r, ulong iounit)
          987 {
          988         Mntrpc *fr;
          989 
          990         fr = mntralloc(0, iounit);
          991 
          992         fr->request.type = Tflush;
          993         if(r->request.type == Tflush)
          994                 fr->request.oldtag = r->request.oldtag;
          995         else
          996                 fr->request.oldtag = r->request.tag;
          997         fr->flushed = r;
          998 
          999         return fr;
         1000 }
         1001 
         1002 /*
         1003  *  Free a chain of flushes.  Remove each unanswered
         1004  *  flush and the original message from the unanswered
         1005  *  request queue.  Mark the original message as done
         1006  *  and if it hasn't been answered set the reply to to
         1007  *  Rflush.
         1008  */
         1009 void
         1010 mntflushfree(Mnt *m, Mntrpc *r)
         1011 {
         1012         Mntrpc *fr;
         1013 
         1014         while(r){
         1015                 fr = r->flushed;
         1016                 if(!r->done){
         1017                         r->reply.type = Rflush;
         1018                         mntqrm(m, r);
         1019                 }
         1020                 if(fr)
         1021                         mntfree(r);
         1022                 r = fr;
         1023         }
         1024 }
         1025 
         1026 int
         1027 alloctag(void)
         1028 {
         1029         int i, j;
         1030         ulong v;
         1031 
         1032         for(i = 0; i < NMASK; i++){
         1033                 v = mntalloc.tagmask[i];
         1034                 if(v == ~0UL)
         1035                         continue;
         1036                 for(j = 0; j < 1<<TAGSHIFT; j++)
         1037                         if((v & (1<<j)) == 0){
         1038                                 mntalloc.tagmask[i] |= 1<<j;
         1039                                 return (i<<TAGSHIFT) + j;
         1040                         }
         1041         }
         1042         panic("no friggin tags left");
         1043         return NOTAG;
         1044 }
         1045 
         1046 void
         1047 freetag(int t)
         1048 {
         1049         mntalloc.tagmask[t>>TAGSHIFT] &= ~(1<<(t&TAGMASK));
         1050 }
         1051 
         1052 Mntrpc*
         1053 mntralloc(Chan *c, ulong msize)
         1054 {
         1055         Mntrpc *new;
         1056 
         1057         lock(&mntalloc.lk);
         1058         new = mntalloc.rpcfree;
         1059         if(new == nil){
         1060                 new = malloc(sizeof(Mntrpc));
         1061                 if(new == nil) {
         1062                         unlock(&mntalloc.lk);
         1063                         exhausted("mount rpc header");
         1064                 }
         1065                 /*
         1066                  * The header is split from the data buffer as
         1067                  * mountmux may swap the buffer with another header.
         1068                  */
         1069                 new->rpc = mallocz(msize, 0);
         1070                 if(new->rpc == nil){
         1071                         free(new);
         1072                         unlock(&mntalloc.lk);
         1073                         exhausted("mount rpc buffer");
         1074                 }
         1075                 new->rpclen = msize;
         1076                 new->request.tag = alloctag();
         1077         }
         1078         else {
         1079                 mntalloc.rpcfree = new->list;
         1080                 mntalloc.nrpcfree--;
         1081                 if(new->rpclen < msize){
         1082                         free(new->rpc);
         1083                         new->rpc = mallocz(msize, 0);
         1084                         if(new->rpc == nil){
         1085                                 free(new);
         1086                                 mntalloc.nrpcused--;
         1087                                 unlock(&mntalloc.lk);
         1088                                 exhausted("mount rpc buffer");
         1089                         }
         1090                         new->rpclen = msize;
         1091                 }
         1092         }
         1093         mntalloc.nrpcused++;
         1094         unlock(&mntalloc.lk);
         1095         new->c = c;
         1096         new->done = 0;
         1097         new->flushed = nil;
         1098         new->b = nil;
         1099         return new;
         1100 }
         1101 
         1102 void
         1103 mntfree(Mntrpc *r)
         1104 {
         1105         if(r->b != nil)
         1106                 freeblist(r->b);
         1107         lock(&mntalloc.lk);
         1108         if(mntalloc.nrpcfree >= 10){
         1109                 free(r->rpc);
         1110                 freetag(r->request.tag);
         1111                 free(r);
         1112         }
         1113         else{
         1114                 r->list = mntalloc.rpcfree;
         1115                 mntalloc.rpcfree = r;
         1116                 mntalloc.nrpcfree++;
         1117         }
         1118         mntalloc.nrpcused--;
         1119         unlock(&mntalloc.lk);
         1120 }
         1121 
         1122 void
         1123 mntqrm(Mnt *m, Mntrpc *r)
         1124 {
         1125         Mntrpc **l, *f;
         1126 
         1127         lock(&m->lk);
         1128         r->done = 1;
         1129 
         1130         l = &m->queue;
         1131         for(f = *l; f; f = f->list) {
         1132                 if(f == r) {
         1133                         *l = r->list;
         1134                         break;
         1135                 }
         1136                 l = &f->list;
         1137         }
         1138         unlock(&m->lk);
         1139 }
         1140 
         1141 Mnt*
         1142 mntchk(Chan *c)
         1143 {
         1144         Mnt *m;
         1145 
         1146         /* This routine is mostly vestiges of prior lives; now it's just sanity checking */
         1147 
         1148         if(c->mchan == nil)
         1149                 panic("mntchk 1: nil mchan c %s\n", chanpath(c));
         1150 
         1151         m = c->mchan->mux;
         1152 
         1153         if(m == nil)
         1154                 print("mntchk 2: nil mux c %s c->mchan %s \n", chanpath(c), chanpath(c->mchan));
         1155 
         1156         /*
         1157          * Was it closed and reused (was error(Eshutdown); now, it cannot happen)
         1158          */
         1159         if(m->id == 0 || m->id >= c->dev)
         1160                 panic("mntchk 3: can't happen");
         1161 
         1162         return m;
         1163 }
         1164 
         1165 /*
         1166  * Rewrite channel type and dev for in-flight data to
         1167  * reflect local values.  These entries are known to be
         1168  * the first two in the Dir encoding after the count.
         1169  */
         1170 void
         1171 mntdirfix(uchar *dirbuf, Chan *c)
         1172 {
         1173         uint r;
         1174 
         1175         r = devtab[c->type]->dc;
         1176         dirbuf += BIT16SZ;        /* skip count */
         1177         PBIT16(dirbuf, r);
         1178         dirbuf += BIT16SZ;
         1179         PBIT32(dirbuf, c->dev);
         1180 }
         1181 
         1182 int
         1183 rpcattn(void *v)
         1184 {
         1185         Mntrpc *r;
         1186 
         1187         r = v;
         1188         return r->done || r->m->rip == 0;
         1189 }
         1190 
         1191 Dev mntdevtab = {
         1192         'M',
         1193         "mnt",
         1194 
         1195         mntreset,
         1196         devinit,
         1197         devshutdown,
         1198         mntattach,
         1199         mntwalk,
         1200         mntstat,
         1201         mntopen,
         1202         mntcreate,
         1203         mntclose,
         1204         mntread,
         1205         devbread,
         1206         mntwrite,
         1207         devbwrite,
         1208         mntremove,
         1209         mntwstat,
         1210 };