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