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