]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/port/devmnt.c
devmnt: abandon fid on botched Tclunk or Tremove
[plan9front.git] / sys / src / 9 / port / devmnt.c
1 #include        "u.h"
2 #include        "../port/lib.h"
3 #include        "mem.h"
4 #include        "dat.h"
5 #include        "fns.h"
6 #include        "../port/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* z;              /* Place to hang out */
29         uchar*  rpc;            /* I/O Data buffer */
30         uint    rpclen;         /* len of buffer */
31         Block*  b;              /* reply blocks */
32         uvlong  stime;          /* start time for mnt statistics */
33         ulong   reqlen;         /* request length for mnt statistics */
34         ulong   replen;         /* reply length for mnt statistics */
35         Mntrpc* flushed;        /* message this one flushes */
36         char    done;           /* Rpc completed */
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;
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    mntqrm(Mnt*, Mntrpc*);
65 Mntrpc* mntralloc(Chan*, ulong);
66 long    mntrdwr(int, Chan*, void*, long, vlong);
67 int     mntrpcread(Mnt*, Mntrpc*);
68 void    mountio(Mnt*, Mntrpc*);
69 void    mountmux(Mnt*, Mntrpc*);
70 void    mountrpc(Mnt*, Mntrpc*);
71 int     rpcattn(void*);
72 Chan*   mntchan(void);
73
74 char    Esbadstat[] = "invalid directory entry received from server";
75 char    Enoversion[] = "version not established for mount channel";
76
77 void    (*mntstats)(int, Chan*, uvlong, ulong);
78
79
80 static void
81 mntreset(void)
82 {
83         mntalloc.id = 1;
84         mntalloc.tagmask[0] = 1;                        /* don't allow 0 as a tag */
85         mntalloc.tagmask[NMASK-1] = 0x80000000UL;       /* don't allow NOTAG */
86         fmtinstall('F', fcallfmt);
87         fmtinstall('D', dirfmt);
88 /* We can't install %M since eipfmt does and is used in the kernel [sape] */
89
90         cinit();
91 }
92
93 /*
94  * Version is not multiplexed: message sent only once per connection.
95  */
96 long
97 mntversion(Chan *c, char *version, int msize, int returnlen)
98 {
99         Fcall f;
100         uchar *msg;
101         Mnt *m;
102         char *v;
103         Queue *q;
104         long k, l;
105         uvlong oo;
106         char buf[128];
107
108         eqlock(&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);
165         oo = c->offset;
166         c->offset += k;
167         unlock(c);
168
169         l = devtab[c->type]->write(c, msg, k, oo);
170
171         if(l < k){
172                 lock(c);
173                 c->offset -= k - l;
174                 unlock(c);
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);
184         c->offset += k;
185         unlock(c);
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         if(returnlen > 0 && returnlen < k)
203                 error(Eshort);
204
205         v = nil;
206         kstrdup(&v, f.version);
207         q = qopen(10*MAXRPC, 0, nil, nil);
208         if(q == nil){
209                 free(v);
210                 exhausted("mount queues");
211         }
212
213         /* now build Mnt associated with this connection */
214         lock(&mntalloc);
215         m = mntalloc.mntfree;
216         if(m != 0)
217                 mntalloc.mntfree = m->list;
218         else {
219                 m = malloc(sizeof(Mnt));
220                 if(m == 0) {
221                         qfree(q);
222                         free(v);
223                         unlock(&mntalloc);
224                         exhausted("mount devices");
225                 }
226         }
227         m->list = mntalloc.list;
228         mntalloc.list = m;
229         m->version = v;
230         m->id = mntalloc.id++;
231         m->q = q;
232         m->msize = f.msize;
233         unlock(&mntalloc);
234
235         if(returnlen > 0)
236                 memmove(version, f.version, k); /* length was checked above */
237
238         poperror();     /* msg */
239         free(msg);
240
241         lock(m);
242         m->queue = 0;
243         m->rip = 0;
244
245         c->flag |= CMSG;
246         c->mux = m;
247         m->c = c;
248         unlock(m);
249
250         poperror();     /* c */
251         qunlock(&c->umqlock);
252
253         return k;
254 }
255
256 Chan*
257 mntauth(Chan *c, char *spec)
258 {
259         Mnt *m;
260         Mntrpc *r;
261
262         m = c->mux;
263
264         if(m == nil){
265                 mntversion(c, VERSION9P, MAXRPC, 0);
266                 m = c->mux;
267                 if(m == nil)
268                         error(Enoversion);
269         }
270
271         c = mntchan();
272         if(waserror()) {
273                 /* Close must not be called since it will
274                  * call mnt recursively
275                  */
276                 chanfree(c);
277                 nexterror();
278         }
279
280         r = mntralloc(0, m->msize);
281
282         if(waserror()) {
283                 mntfree(r);
284                 nexterror();
285         }
286
287         r->request.type = Tauth;
288         r->request.afid = c->fid;
289         r->request.uname = up->user;
290         r->request.aname = spec;
291         mountrpc(m, r);
292
293         c->qid = r->reply.aqid;
294         c->mchan = m->c;
295         incref(m->c);
296         c->mqid = c->qid;
297         c->mode = ORDWR;
298
299         poperror();     /* r */
300         mntfree(r);
301
302         poperror();     /* c */
303
304         return c;
305
306 }
307
308 static Chan*
309 mntattach(char *muxattach)
310 {
311         Mnt *m;
312         Chan *c;
313         Mntrpc *r;
314         struct bogus{
315                 Chan    *chan;
316                 Chan    *authchan;
317                 char    *spec;
318                 int     flags;
319         }bogus;
320
321         bogus = *((struct bogus *)muxattach);
322         c = bogus.chan;
323
324         m = c->mux;
325
326         if(m == nil){
327                 mntversion(c, nil, 0, 0);
328                 m = c->mux;
329                 if(m == nil)
330                         error(Enoversion);
331         }
332
333         c = mntchan();
334         if(waserror()) {
335                 /* Close must not be called since it will
336                  * call mnt recursively
337                  */
338                 chanfree(c);
339                 nexterror();
340         }
341
342         r = mntralloc(0, m->msize);
343
344         if(waserror()) {
345                 mntfree(r);
346                 nexterror();
347         }
348
349         r->request.type = Tattach;
350         r->request.fid = c->fid;
351         if(bogus.authchan == nil)
352                 r->request.afid = NOFID;
353         else
354                 r->request.afid = bogus.authchan->fid;
355         r->request.uname = up->user;
356         r->request.aname = bogus.spec;
357         mountrpc(m, r);
358
359         c->qid = r->reply.qid;
360         c->mchan = m->c;
361         incref(m->c);
362         c->mqid = c->qid;
363
364         poperror();     /* r */
365         mntfree(r);
366
367         poperror();     /* c */
368
369         if(bogus.flags&MCACHE)
370                 c->flag |= CCACHE;
371         return c;
372 }
373
374 Chan*
375 mntchan(void)
376 {
377         Chan *c;
378
379         c = devattach('M', 0);
380         lock(&mntalloc);
381         c->dev = mntalloc.id++;
382         unlock(&mntalloc);
383
384         if(c->mchan)
385                 panic("mntchan non-zero %p", c->mchan);
386         return c;
387 }
388
389 static Walkqid*
390 mntwalk(Chan *c, Chan *nc, char **name, int nname)
391 {
392         int i, alloc;
393         Mnt *m;
394         Mntrpc *r;
395         Walkqid *wq;
396
397         if(nc != nil)
398                 print("mntwalk: nc != nil\n");
399         if(nname > MAXWELEM)
400                 error("devmnt: too many name elements");
401         alloc = 0;
402         wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid));
403         if(waserror()){
404                 if(alloc && wq->clone!=nil)
405                         cclose(wq->clone);
406                 free(wq);
407                 return nil;
408         }
409
410         alloc = 0;
411         m = mntchk(c);
412         r = mntralloc(c, m->msize);
413         if(nc == nil){
414                 nc = devclone(c);
415                 /*
416                  * Until the other side accepts this fid, we can't mntclose it.
417                  * Therefore set type to 0 for now; rootclose is known to be safe.
418                  */
419                 nc->type = 0;
420                 nc->flag |= (c->flag & CCACHE);
421                 alloc = 1;
422         }
423         wq->clone = nc;
424
425         if(waserror()) {
426                 mntfree(r);
427                 nexterror();
428         }
429         r->request.type = Twalk;
430         r->request.fid = c->fid;
431         r->request.newfid = nc->fid;
432         r->request.nwname = nname;
433         memmove(r->request.wname, name, nname*sizeof(char*));
434
435         mountrpc(m, r);
436
437         if(r->reply.nwqid > nname)
438                 error("too many QIDs returned by walk");
439         if(r->reply.nwqid < nname){
440                 if(alloc)
441                         cclose(nc);
442                 wq->clone = nil;
443                 if(r->reply.nwqid == 0){
444                         free(wq);
445                         wq = nil;
446                         goto Return;
447                 }
448         }
449
450         /* move new fid onto mnt device and update its qid */
451         if(wq->clone != nil){
452                 if(wq->clone != c){
453                         wq->clone->type = c->type;
454                         wq->clone->mchan = c->mchan;
455                         incref(c->mchan);
456                 }
457                 if(r->reply.nwqid > 0)
458                         wq->clone->qid = r->reply.wqid[r->reply.nwqid-1];
459         }
460         wq->nqid = r->reply.nwqid;
461         for(i=0; i<wq->nqid; i++)
462                 wq->qid[i] = r->reply.wqid[i];
463
464     Return:
465         poperror();
466         mntfree(r);
467         poperror();
468         return wq;
469 }
470
471 static int
472 mntstat(Chan *c, uchar *dp, int n)
473 {
474         Mnt *m;
475         Mntrpc *r;
476
477         if(n < BIT16SZ)
478                 error(Eshortstat);
479         m = mntchk(c);
480         r = mntralloc(c, m->msize);
481         if(waserror()) {
482                 mntfree(r);
483                 nexterror();
484         }
485         r->request.type = Tstat;
486         r->request.fid = c->fid;
487         mountrpc(m, r);
488
489         if(r->reply.nstat > n){
490                 n = BIT16SZ;
491                 PBIT16((uchar*)dp, r->reply.nstat-2);
492         }else{
493                 n = r->reply.nstat;
494                 memmove(dp, r->reply.stat, n);
495                 validstat(dp, n);
496                 mntdirfix(dp, c);
497         }
498         poperror();
499         mntfree(r);
500         return n;
501 }
502
503 static Chan*
504 mntopencreate(int type, Chan *c, char *name, int omode, ulong perm)
505 {
506         Mnt *m;
507         Mntrpc *r;
508
509         m = mntchk(c);
510         r = mntralloc(c, m->msize);
511         if(waserror()) {
512                 mntfree(r);
513                 nexterror();
514         }
515         r->request.type = type;
516         r->request.fid = c->fid;
517         r->request.mode = omode;
518         if(type == Tcreate){
519                 r->request.perm = perm;
520                 r->request.name = name;
521         }
522         mountrpc(m, r);
523
524         c->qid = r->reply.qid;
525         c->offset = 0;
526         c->mode = openmode(omode);
527         c->iounit = r->reply.iounit;
528         if(c->iounit == 0 || c->iounit > m->msize-IOHDRSZ)
529                 c->iounit = m->msize-IOHDRSZ;
530         c->flag |= COPEN;
531         poperror();
532         mntfree(r);
533
534         if(c->flag & CCACHE)
535                 copen(c);
536
537         return c;
538 }
539
540 static Chan*
541 mntopen(Chan *c, int omode)
542 {
543         return mntopencreate(Topen, c, nil, omode, 0);
544 }
545
546 static Chan*
547 mntcreate(Chan *c, char *name, int omode, ulong perm)
548 {
549         return mntopencreate(Tcreate, c, name, omode, perm);
550 }
551
552 static void
553 mntclunk(Chan *c, int t)
554 {
555         Mnt *m;
556         Mntrpc *r;
557
558         m = mntchk(c);
559         r = mntralloc(c, m->msize);
560         if(waserror()){
561                 mntfree(r);
562                 nexterror();
563         }
564
565         r->request.type = t;
566         r->request.fid = c->fid;
567         mountrpc(m, r);
568         mntfree(r);
569         poperror();
570 }
571
572 void
573 muxclose(Mnt *m)
574 {
575         Mnt *f, **l;
576         Mntrpc *r;
577
578         while((r = m->queue) != nil){
579                 m->queue = r->list;
580                 mntfree(r);
581         }
582         m->id = 0;
583         free(m->version);
584         m->version = nil;
585         qfree(m->q);
586         m->q = nil;
587
588         lock(&mntalloc);
589         l = &mntalloc.list;
590         for(f = *l; f; f = f->list) {
591                 if(f == m) {
592                         *l = m->list;
593                         break;
594                 }
595                 l = &f->list;
596         }
597         m->list = mntalloc.mntfree;
598         mntalloc.mntfree = m;
599         unlock(&mntalloc);
600 }
601
602 static void
603 mntclose(Chan *c)
604 {
605         mntclunk(c, Tclunk);
606 }
607
608 static void
609 mntremove(Chan *c)
610 {
611         mntclunk(c, Tremove);
612 }
613
614 static int
615 mntwstat(Chan *c, uchar *dp, int n)
616 {
617         Mnt *m;
618         Mntrpc *r;
619
620         m = mntchk(c);
621         r = mntralloc(c, m->msize);
622         if(waserror()) {
623                 mntfree(r);
624                 nexterror();
625         }
626         r->request.type = Twstat;
627         r->request.fid = c->fid;
628         r->request.nstat = n;
629         r->request.stat = dp;
630         mountrpc(m, r);
631         poperror();
632         mntfree(r);
633         return n;
634 }
635
636 static long
637 mntread(Chan *c, void *buf, long n, vlong off)
638 {
639         uchar *p, *e;
640         int nc, cache, isdir, dirlen;
641
642         isdir = 0;
643         cache = c->flag & CCACHE;
644         if(c->qid.type & QTDIR) {
645                 cache = 0;
646                 isdir = 1;
647         }
648
649         p = buf;
650         if(cache) {
651                 nc = cread(c, buf, n, off);
652                 if(nc > 0) {
653                         n -= nc;
654                         if(n == 0)
655                                 return nc;
656                         p += nc;
657                         off += nc;
658                 }
659                 n = mntrdwr(Tread, c, p, n, off);
660                 cupdate(c, p, n, off);
661                 return n + nc;
662         }
663
664         n = mntrdwr(Tread, c, buf, n, off);
665         if(isdir) {
666                 for(e = &p[n]; p+BIT16SZ < e; p += dirlen){
667                         dirlen = BIT16SZ+GBIT16(p);
668                         if(p+dirlen > e)
669                                 break;
670                         validstat(p, dirlen);
671                         mntdirfix(p, c);
672                 }
673                 if(p != e)
674                         error(Esbadstat);
675         }
676         return n;
677 }
678
679 static long
680 mntwrite(Chan *c, void *buf, long n, vlong off)
681 {
682         return mntrdwr(Twrite, c, buf, n, off);
683 }
684
685 long
686 mntrdwr(int type, Chan *c, void *buf, long n, vlong off)
687 {
688         Mnt *m;
689         Mntrpc *r;
690         char *uba;
691         int cache;
692         ulong cnt, nr, nreq;
693
694         m = mntchk(c);
695         uba = buf;
696         cnt = 0;
697         cache = c->flag & CCACHE;
698         if(c->qid.type & QTDIR)
699                 cache = 0;
700         for(;;) {
701                 r = mntralloc(c, m->msize);
702                 if(waserror()) {
703                         mntfree(r);
704                         nexterror();
705                 }
706                 r->request.type = type;
707                 r->request.fid = c->fid;
708                 r->request.offset = off;
709                 r->request.data = uba;
710                 nr = n;
711                 if(nr > m->msize-IOHDRSZ)
712                         nr = m->msize-IOHDRSZ;
713                 r->request.count = nr;
714                 mountrpc(m, r);
715                 nreq = r->request.count;
716                 nr = r->reply.count;
717                 if(nr > nreq)
718                         nr = nreq;
719
720                 if(type == Tread)
721                         r->b = bl2mem((uchar*)uba, r->b, nr);
722                 else if(cache)
723                         cwrite(c, (uchar*)uba, nr, off);
724
725                 poperror();
726                 mntfree(r);
727                 off += nr;
728                 uba += nr;
729                 cnt += nr;
730                 n -= nr;
731                 if(nr != nreq || n == 0 || up->nnote)
732                         break;
733         }
734         return cnt;
735 }
736
737 void
738 mountrpc(Mnt *m, Mntrpc *r)
739 {
740         char *sn, *cn;
741         int t;
742
743         r->reply.tag = 0;
744         r->reply.type = Tmax;   /* can't ever be a valid message type */
745
746         mountio(m, r);
747
748         t = r->reply.type;
749         switch(t) {
750         case Rerror:
751                 error(r->reply.ename);
752         case Rflush:
753                 error(Eintr);
754         default:
755                 if(t == r->request.type+1)
756                         break;
757                 sn = "?";
758                 if(m->c->path != nil)
759                         sn = m->c->path->s;
760                 cn = "?";
761                 if(r->c != nil && r->c->path != nil)
762                         cn = r->c->path->s;
763                 print("mnt: proc %s %lud: mismatch from %s %s rep %#p tag %d fid %d T%d R%d rp %d\n",
764                         up->text, up->pid, sn, cn,
765                         r, r->request.tag, r->request.fid, r->request.type,
766                         r->reply.type, r->reply.tag);
767                 error(Emountrpc);
768         }
769 }
770
771 void
772 mountio(Mnt *m, Mntrpc *r)
773 {
774         int n;
775
776         while(waserror()) {
777                 if(m->rip == up)
778                         mntgate(m);
779                 if(strcmp(up->errstr, Eintr) != 0){
780                         switch(r->request.type){
781                         case Tremove:
782                         case Tclunk:
783                                 /* botch, abandon fid */ 
784                                 if(strcmp(up->errstr, Ehungup) != 0)
785                                         r->c->fid = 0;
786                         }
787                         mntflushfree(m, r);
788                         nexterror();
789                 }
790                 r = mntflushalloc(r, m->msize);
791         }
792
793         lock(m);
794         r->z = &up->sleep;
795         r->m = m;
796         r->list = m->queue;
797         m->queue = r;
798         unlock(m);
799
800         /* Transmit a file system rpc */
801         if(m->msize == 0)
802                 panic("msize");
803         n = convS2M(&r->request, r->rpc, m->msize);
804         if(n <= 0){
805                 print("mountio: proc %s %lud: convS2M returned %d for tag %d fid %d T%d\n",
806                         up->text, up->pid, n, r->request.tag, r->request.fid, r->request.type);
807                 error(Emountrpc);
808         }
809                 
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);
818                 if(m->rip == 0)
819                         break;
820                 unlock(m);
821                 sleep(r->z, rpcattn, r);
822                 if(r->done){
823                         poperror();
824                         mntflushfree(m, r);
825                         return;
826                 }
827         }
828         m->rip = up;
829         unlock(m);
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);
936         m->rip = 0;
937         for(q = m->queue; q; q = q->list) {
938                 if(q->done == 0)
939                 if(wakeup(q->z))
940                         break;
941         }
942         unlock(m);
943 }
944
945 void
946 mountmux(Mnt *m, Mntrpc *r)
947 {
948         Mntrpc **l, *q;
949         Rendez *z;
950
951         lock(m);
952         l = &m->queue;
953         for(q = *l; q; q = q->list) {
954                 /* look for a reply to a message */
955                 if(q->request.tag == r->reply.tag) {
956                         *l = q->list;
957                         if(mntstats != nil)
958                                 (*mntstats)(q->request.type,
959                                         m->c, q->stime,
960                                         q->reqlen + r->replen);
961                         if(q != r) {
962                                 /*
963                                  * Completed someone else.
964                                  * Trade pointers to receive buffer.
965                                  */
966                                 q->reply = r->reply;
967                                 q->b = r->b;
968                                 r->b = nil;
969                                 z = q->z;
970                         } else
971                                 z = nil;
972                         q->done = 1;    /* hands off */
973                         if(z != nil)
974                                 wakeup(z);
975                         unlock(m);
976                         return;
977                 }
978                 l = &q->list;
979         }
980         unlock(m);
981         print("unexpected reply tag %ud; type %d\n", r->reply.tag, r->reply.type);
982 }
983
984 /*
985  * Create a new flush request and chain the previous
986  * requests from it
987  */
988 Mntrpc*
989 mntflushalloc(Mntrpc *r, ulong iounit)
990 {
991         Mntrpc *fr;
992
993         fr = mntralloc(0, iounit);
994
995         fr->request.type = Tflush;
996         if(r->request.type == Tflush)
997                 fr->request.oldtag = r->request.oldtag;
998         else
999                 fr->request.oldtag = r->request.tag;
1000         fr->flushed = r;
1001
1002         return fr;
1003 }
1004
1005 /*
1006  *  Free a chain of flushes.  Remove each unanswered
1007  *  flush and the original message from the unanswered
1008  *  request queue.  Mark the original message as done
1009  *  and if it hasn't been answered set the reply to to
1010  *  Rflush.
1011  */
1012 void
1013 mntflushfree(Mnt *m, Mntrpc *r)
1014 {
1015         Mntrpc *fr;
1016
1017         while(r){
1018                 fr = r->flushed;
1019                 if(!r->done){
1020                         r->reply.type = Rflush;
1021                         mntqrm(m, r);
1022                 }
1023                 if(fr)
1024                         mntfree(r);
1025                 r = fr;
1026         }
1027 }
1028
1029 int
1030 alloctag(void)
1031 {
1032         int i, j;
1033         ulong v;
1034
1035         for(i = 0; i < NMASK; i++){
1036                 v = mntalloc.tagmask[i];
1037                 if(v == ~0UL)
1038                         continue;
1039                 for(j = 0; j < 1<<TAGSHIFT; j++)
1040                         if((v & (1<<j)) == 0){
1041                                 mntalloc.tagmask[i] |= 1<<j;
1042                                 return (i<<TAGSHIFT) + j;
1043                         }
1044         }
1045         panic("no friggin tags left");
1046         return NOTAG;
1047 }
1048
1049 void
1050 freetag(int t)
1051 {
1052         mntalloc.tagmask[t>>TAGSHIFT] &= ~(1<<(t&TAGMASK));
1053 }
1054
1055 Mntrpc*
1056 mntralloc(Chan *c, ulong msize)
1057 {
1058         Mntrpc *new;
1059
1060         lock(&mntalloc);
1061         new = mntalloc.rpcfree;
1062         if(new == nil){
1063                 new = malloc(sizeof(Mntrpc));
1064                 if(new == nil) {
1065                         unlock(&mntalloc);
1066                         exhausted("mount rpc header");
1067                 }
1068                 /*
1069                  * The header is split from the data buffer as
1070                  * mountmux may swap the buffer with another header.
1071                  */
1072                 new->rpc = mallocz(msize, 0);
1073                 if(new->rpc == nil){
1074                         free(new);
1075                         unlock(&mntalloc);
1076                         exhausted("mount rpc buffer");
1077                 }
1078                 new->rpclen = msize;
1079                 new->request.tag = alloctag();
1080         }
1081         else {
1082                 mntalloc.rpcfree = new->list;
1083                 mntalloc.nrpcfree--;
1084                 if(new->rpclen < msize){
1085                         free(new->rpc);
1086                         new->rpc = mallocz(msize, 0);
1087                         if(new->rpc == nil){
1088                                 free(new);
1089                                 mntalloc.nrpcused--;
1090                                 unlock(&mntalloc);
1091                                 exhausted("mount rpc buffer");
1092                         }
1093                         new->rpclen = msize;
1094                 }
1095         }
1096         mntalloc.nrpcused++;
1097         unlock(&mntalloc);
1098         new->c = c;
1099         new->done = 0;
1100         new->flushed = nil;
1101         new->b = nil;
1102         return new;
1103 }
1104
1105 void
1106 mntfree(Mntrpc *r)
1107 {
1108         if(r->b != nil)
1109                 freeblist(r->b);
1110         lock(&mntalloc);
1111         if(mntalloc.nrpcfree >= 10){
1112                 free(r->rpc);
1113                 freetag(r->request.tag);
1114                 free(r);
1115         }
1116         else{
1117                 r->list = mntalloc.rpcfree;
1118                 mntalloc.rpcfree = r;
1119                 mntalloc.nrpcfree++;
1120         }
1121         mntalloc.nrpcused--;
1122         unlock(&mntalloc);
1123 }
1124
1125 void
1126 mntqrm(Mnt *m, Mntrpc *r)
1127 {
1128         Mntrpc **l, *f;
1129
1130         lock(m);
1131         r->done = 1;
1132
1133         l = &m->queue;
1134         for(f = *l; f; f = f->list) {
1135                 if(f == r) {
1136                         *l = r->list;
1137                         break;
1138                 }
1139                 l = &f->list;
1140         }
1141         unlock(m);
1142 }
1143
1144 Mnt*
1145 mntchk(Chan *c)
1146 {
1147         Mnt *m;
1148
1149         /* This routine is mostly vestiges of prior lives; now it's just sanity checking */
1150
1151         if(c->mchan == nil)
1152                 panic("mntchk 1: nil mchan c %s", chanpath(c));
1153
1154         m = c->mchan->mux;
1155
1156         if(m == nil)
1157                 print("mntchk 2: nil mux c %s c->mchan %s \n", chanpath(c), chanpath(c->mchan));
1158
1159         /*
1160          * Was it closed and reused (was error(Eshutdown); now, it cannot happen)
1161          */
1162         if(m->id == 0 || m->id >= c->dev)
1163                 panic("mntchk 3: can't happen");
1164
1165         return m;
1166 }
1167
1168 /*
1169  * Rewrite channel type and dev for in-flight data to
1170  * reflect local values.  These entries are known to be
1171  * the first two in the Dir encoding after the count.
1172  */
1173 void
1174 mntdirfix(uchar *dirbuf, Chan *c)
1175 {
1176         uint r;
1177
1178         r = devtab[c->type]->dc;
1179         dirbuf += BIT16SZ;      /* skip count */
1180         PBIT16(dirbuf, r);
1181         dirbuf += BIT16SZ;
1182         PBIT32(dirbuf, c->dev);
1183 }
1184
1185 int
1186 rpcattn(void *v)
1187 {
1188         Mntrpc *r;
1189
1190         r = v;
1191         return r->done || r->m->rip == 0;
1192 }
1193
1194 Dev mntdevtab = {
1195         'M',
1196         "mnt",
1197
1198         mntreset,
1199         devinit,
1200         devshutdown,
1201         mntattach,
1202         mntwalk,
1203         mntstat,
1204         mntopen,
1205         mntcreate,
1206         mntclose,
1207         mntread,
1208         devbread,
1209         mntwrite,
1210         devbwrite,
1211         mntremove,
1212         mntwstat,
1213 };