]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/port/devmnt.c
devmnt: add debug print for convS2M failing, suspecting this causing zero length...
[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                         mntflushfree(m, r);
781                         nexterror();
782                 }
783                 r = mntflushalloc(r, m->msize);
784         }
785
786         lock(m);
787         r->z = &up->sleep;
788         r->m = m;
789         r->list = m->queue;
790         m->queue = r;
791         unlock(m);
792
793         /* Transmit a file system rpc */
794         if(m->msize == 0)
795                 panic("msize");
796         n = convS2M(&r->request, r->rpc, m->msize);
797         if(n <= 0){
798                 print("mountio: proc %s %lud: convS2M returned %d for tag %d fid %d T%d\n",
799                         up->text, up->pid, n, r->request.tag, r->request.fid, r->request.type);
800                 error(Emountrpc);
801         }
802                 
803         if(devtab[m->c->type]->write(m->c, r->rpc, n, 0) != n)
804                 error(Emountrpc);
805         r->stime = fastticks(nil);
806         r->reqlen = n;
807
808         /* Gate readers onto the mount point one at a time */
809         for(;;) {
810                 lock(m);
811                 if(m->rip == 0)
812                         break;
813                 unlock(m);
814                 sleep(r->z, rpcattn, r);
815                 if(r->done){
816                         poperror();
817                         mntflushfree(m, r);
818                         return;
819                 }
820         }
821         m->rip = up;
822         unlock(m);
823         while(r->done == 0) {
824                 if(mntrpcread(m, r) < 0)
825                         error(Emountrpc);
826                 mountmux(m, r);
827         }
828         mntgate(m);
829         poperror();
830         mntflushfree(m, r);
831 }
832
833 static int
834 doread(Mnt *m, int len)
835 {
836         Block *b;
837
838         while(qlen(m->q) < len){
839                 b = devtab[m->c->type]->bread(m->c, m->msize, 0);
840                 if(b == nil)
841                         return -1;
842                 if(blocklen(b) == 0){
843                         freeblist(b);
844                         return -1;
845                 }
846                 qaddlist(m->q, b);
847         }
848         return 0;
849 }
850
851 int
852 mntrpcread(Mnt *m, Mntrpc *r)
853 {
854         int i, t, len, hlen;
855         Block *b, **l, *nb;
856
857         r->reply.type = 0;
858         r->reply.tag = 0;
859
860         /* read at least length, type, and tag and pullup to a single block */
861         if(doread(m, BIT32SZ+BIT8SZ+BIT16SZ) < 0)
862                 return -1;
863         nb = pullupqueue(m->q, BIT32SZ+BIT8SZ+BIT16SZ);
864
865         /* read in the rest of the message, avoid ridiculous (for now) message sizes */
866         len = GBIT32(nb->rp);
867         if(len > m->msize){
868                 qdiscard(m->q, qlen(m->q));
869                 return -1;
870         }
871         if(doread(m, len) < 0)
872                 return -1;
873
874         /* pullup the header (i.e. everything except data) */
875         t = nb->rp[BIT32SZ];
876         switch(t){
877         case Rread:
878                 hlen = BIT32SZ+BIT8SZ+BIT16SZ+BIT32SZ;
879                 break;
880         default:
881                 hlen = len;
882                 break;
883         }
884         nb = pullupqueue(m->q, hlen);
885
886         if(convM2S(nb->rp, len, &r->reply) <= 0){
887                 /* bad message, dump it */
888                 print("mntrpcread: convM2S failed\n");
889                 qdiscard(m->q, len);
890                 return -1;
891         }
892
893         /* hang the data off of the fcall struct */
894         l = &r->b;
895         *l = nil;
896         do {
897                 b = qremove(m->q);
898                 if(hlen > 0){
899                         b->rp += hlen;
900                         len -= hlen;
901                         hlen = 0;
902                 }
903                 i = BLEN(b);
904                 if(i <= len){
905                         len -= i;
906                         *l = b;
907                         l = &(b->next);
908                 } else {
909                         /* split block and put unused bit back */
910                         nb = allocb(i-len);
911                         memmove(nb->wp, b->rp+len, i-len);
912                         b->wp = b->rp+len;
913                         nb->wp += i-len;
914                         qputback(m->q, nb);
915                         *l = b;
916                         return 0;
917                 }
918         }while(len > 0);
919
920         return 0;
921 }
922
923 void
924 mntgate(Mnt *m)
925 {
926         Mntrpc *q;
927
928         lock(m);
929         m->rip = 0;
930         for(q = m->queue; q; q = q->list) {
931                 if(q->done == 0)
932                 if(wakeup(q->z))
933                         break;
934         }
935         unlock(m);
936 }
937
938 void
939 mountmux(Mnt *m, Mntrpc *r)
940 {
941         Mntrpc **l, *q;
942         Rendez *z;
943
944         lock(m);
945         l = &m->queue;
946         for(q = *l; q; q = q->list) {
947                 /* look for a reply to a message */
948                 if(q->request.tag == r->reply.tag) {
949                         *l = q->list;
950                         if(mntstats != nil)
951                                 (*mntstats)(q->request.type,
952                                         m->c, q->stime,
953                                         q->reqlen + r->replen);
954                         if(q != r) {
955                                 /*
956                                  * Completed someone else.
957                                  * Trade pointers to receive buffer.
958                                  */
959                                 q->reply = r->reply;
960                                 q->b = r->b;
961                                 r->b = nil;
962                                 z = q->z;
963                         } else
964                                 z = nil;
965                         q->done = 1;    /* hands off */
966                         if(z != nil)
967                                 wakeup(z);
968                         unlock(m);
969                         return;
970                 }
971                 l = &q->list;
972         }
973         unlock(m);
974         print("unexpected reply tag %ud; type %d\n", r->reply.tag, r->reply.type);
975 }
976
977 /*
978  * Create a new flush request and chain the previous
979  * requests from it
980  */
981 Mntrpc*
982 mntflushalloc(Mntrpc *r, ulong iounit)
983 {
984         Mntrpc *fr;
985
986         fr = mntralloc(0, iounit);
987
988         fr->request.type = Tflush;
989         if(r->request.type == Tflush)
990                 fr->request.oldtag = r->request.oldtag;
991         else
992                 fr->request.oldtag = r->request.tag;
993         fr->flushed = r;
994
995         return fr;
996 }
997
998 /*
999  *  Free a chain of flushes.  Remove each unanswered
1000  *  flush and the original message from the unanswered
1001  *  request queue.  Mark the original message as done
1002  *  and if it hasn't been answered set the reply to to
1003  *  Rflush.
1004  */
1005 void
1006 mntflushfree(Mnt *m, Mntrpc *r)
1007 {
1008         Mntrpc *fr;
1009
1010         while(r){
1011                 fr = r->flushed;
1012                 if(!r->done){
1013                         r->reply.type = Rflush;
1014                         mntqrm(m, r);
1015                 }
1016                 if(fr)
1017                         mntfree(r);
1018                 r = fr;
1019         }
1020 }
1021
1022 int
1023 alloctag(void)
1024 {
1025         int i, j;
1026         ulong v;
1027
1028         for(i = 0; i < NMASK; i++){
1029                 v = mntalloc.tagmask[i];
1030                 if(v == ~0UL)
1031                         continue;
1032                 for(j = 0; j < 1<<TAGSHIFT; j++)
1033                         if((v & (1<<j)) == 0){
1034                                 mntalloc.tagmask[i] |= 1<<j;
1035                                 return (i<<TAGSHIFT) + j;
1036                         }
1037         }
1038         panic("no friggin tags left");
1039         return NOTAG;
1040 }
1041
1042 void
1043 freetag(int t)
1044 {
1045         mntalloc.tagmask[t>>TAGSHIFT] &= ~(1<<(t&TAGMASK));
1046 }
1047
1048 Mntrpc*
1049 mntralloc(Chan *c, ulong msize)
1050 {
1051         Mntrpc *new;
1052
1053         lock(&mntalloc);
1054         new = mntalloc.rpcfree;
1055         if(new == nil){
1056                 new = malloc(sizeof(Mntrpc));
1057                 if(new == nil) {
1058                         unlock(&mntalloc);
1059                         exhausted("mount rpc header");
1060                 }
1061                 /*
1062                  * The header is split from the data buffer as
1063                  * mountmux may swap the buffer with another header.
1064                  */
1065                 new->rpc = mallocz(msize, 0);
1066                 if(new->rpc == nil){
1067                         free(new);
1068                         unlock(&mntalloc);
1069                         exhausted("mount rpc buffer");
1070                 }
1071                 new->rpclen = msize;
1072                 new->request.tag = alloctag();
1073         }
1074         else {
1075                 mntalloc.rpcfree = new->list;
1076                 mntalloc.nrpcfree--;
1077                 if(new->rpclen < msize){
1078                         free(new->rpc);
1079                         new->rpc = mallocz(msize, 0);
1080                         if(new->rpc == nil){
1081                                 free(new);
1082                                 mntalloc.nrpcused--;
1083                                 unlock(&mntalloc);
1084                                 exhausted("mount rpc buffer");
1085                         }
1086                         new->rpclen = msize;
1087                 }
1088         }
1089         mntalloc.nrpcused++;
1090         unlock(&mntalloc);
1091         new->c = c;
1092         new->done = 0;
1093         new->flushed = nil;
1094         new->b = nil;
1095         return new;
1096 }
1097
1098 void
1099 mntfree(Mntrpc *r)
1100 {
1101         if(r->b != nil)
1102                 freeblist(r->b);
1103         lock(&mntalloc);
1104         if(mntalloc.nrpcfree >= 10){
1105                 free(r->rpc);
1106                 freetag(r->request.tag);
1107                 free(r);
1108         }
1109         else{
1110                 r->list = mntalloc.rpcfree;
1111                 mntalloc.rpcfree = r;
1112                 mntalloc.nrpcfree++;
1113         }
1114         mntalloc.nrpcused--;
1115         unlock(&mntalloc);
1116 }
1117
1118 void
1119 mntqrm(Mnt *m, Mntrpc *r)
1120 {
1121         Mntrpc **l, *f;
1122
1123         lock(m);
1124         r->done = 1;
1125
1126         l = &m->queue;
1127         for(f = *l; f; f = f->list) {
1128                 if(f == r) {
1129                         *l = r->list;
1130                         break;
1131                 }
1132                 l = &f->list;
1133         }
1134         unlock(m);
1135 }
1136
1137 Mnt*
1138 mntchk(Chan *c)
1139 {
1140         Mnt *m;
1141
1142         /* This routine is mostly vestiges of prior lives; now it's just sanity checking */
1143
1144         if(c->mchan == nil)
1145                 panic("mntchk 1: nil mchan c %s", chanpath(c));
1146
1147         m = c->mchan->mux;
1148
1149         if(m == nil)
1150                 print("mntchk 2: nil mux c %s c->mchan %s \n", chanpath(c), chanpath(c->mchan));
1151
1152         /*
1153          * Was it closed and reused (was error(Eshutdown); now, it cannot happen)
1154          */
1155         if(m->id == 0 || m->id >= c->dev)
1156                 panic("mntchk 3: can't happen");
1157
1158         return m;
1159 }
1160
1161 /*
1162  * Rewrite channel type and dev for in-flight data to
1163  * reflect local values.  These entries are known to be
1164  * the first two in the Dir encoding after the count.
1165  */
1166 void
1167 mntdirfix(uchar *dirbuf, Chan *c)
1168 {
1169         uint r;
1170
1171         r = devtab[c->type]->dc;
1172         dirbuf += BIT16SZ;      /* skip count */
1173         PBIT16(dirbuf, r);
1174         dirbuf += BIT16SZ;
1175         PBIT32(dirbuf, c->dev);
1176 }
1177
1178 int
1179 rpcattn(void *v)
1180 {
1181         Mntrpc *r;
1182
1183         r = v;
1184         return r->done || r->m->rip == 0;
1185 }
1186
1187 Dev mntdevtab = {
1188         'M',
1189         "mnt",
1190
1191         mntreset,
1192         devinit,
1193         devshutdown,
1194         mntattach,
1195         mntwalk,
1196         mntstat,
1197         mntopen,
1198         mntcreate,
1199         mntclose,
1200         mntread,
1201         devbread,
1202         mntwrite,
1203         devbwrite,
1204         mntremove,
1205         mntwstat,
1206 };