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