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