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