1 #include "u.h"
2 #include "lib.h"
3 #include "dat.h"
4 #include "fns.h"
5 #include "error.h"
6
7 /*
8 * References are managed as follows:
9 * The channel to the server - a network connection or pipe - has one
10 * reference for every Chan open on the server. The server channel has
11 * c->mux set to the Mnt used for muxing control to that server. Mnts
12 * have no reference count; they go away when c goes away.
13 * Each channel derived from the mount point has mchan set to c,
14 * and increfs/decrefs mchan to manage references on the server
15 * connection.
16 */
17
18 #define MAXRPC (IOHDRSZ+8192)
19
20 struct Mntrpc
21 {
22 Chan* c; /* Channel for whom we are working */
23 Mntrpc* list; /* Free/pending list */
24 Fcall request; /* Outgoing file system protocol message */
25 Fcall reply; /* Incoming reply */
26 Mnt* m; /* Mount device during rpc */
27 Rendez r; /* Place to hang out */
28 uchar* rpc; /* I/O Data buffer */
29 uint rpclen; /* len of buffer */
30 Block *b; /* reply blocks */
31 char done; /* Rpc completed */
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 };
37
38 enum
39 {
40 TAGSHIFT = 5, /* ulong has to be 32 bits */
41 TAGMASK = (1<<TAGSHIFT)-1,
42 NMASK = (64*1024)>>TAGSHIFT,
43 };
44
45 struct Mntalloc
46 {
47 Lock lk;
48 Mnt* list; /* Mount devices in use */
49 Mnt* mntfree; /* Free list */
50 Mntrpc* rpcfree;
51 int nrpcfree;
52 int nrpcused;
53 ulong id;
54 ulong tagmask[NMASK];
55 }mntalloc;
56
57 void mattach(Mnt*, Chan*, char*);
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
mntreset(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
mntversion(Chan * c,char * version,int msize,int returnlen)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 qlock(&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->ref.lk);
165 oo = c->offset;
166 c->offset += k;
167 unlock(&c->ref.lk);
168
169 l = devtab[c->type]->write(c, msg, k, oo);
170
171 if(l < k){
172 lock(&c->ref.lk);
173 c->offset -= k - l;
174 unlock(&c->ref.lk);
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->ref.lk);
184 c->offset += k;
185 unlock(&c->ref.lk);
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.lk);
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.lk);
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, 0, nil);
221 m->msize = f.msize;
222 unlock(&mntalloc.lk);
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->lk);
234 m->queue = 0;
235 m->rip = 0;
236
237 c->flag |= CMSG;
238 c->mux = m;
239 m->c = c;
240 unlock(&m->lk);
241
242 poperror(); /* c */
243 qunlock(&c->umqlock);
244
245 return k;
246 }
247
248 Chan*
mntauth(Chan * c,char * spec)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->ref);
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*
mntattach(char * muxattach)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->ref);
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*
mntchan(void)367 mntchan(void)
368 {
369 Chan *c;
370
371 c = devattach('M', 0);
372 lock(&mntalloc.lk);
373 c->dev = mntalloc.id++;
374 unlock(&mntalloc.lk);
375
376 if(c->mchan)
377 panic("mntchan non-zero %p", c->mchan);
378 return c;
379 }
380
381 static Walkqid*
mntwalk(Chan * c,Chan * nc,char ** name,int nname)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 alloc = 1;
413 }
414 wq->clone = nc;
415
416 if(waserror()) {
417 mntfree(r);
418 nexterror();
419 }
420 r->request.type = Twalk;
421 r->request.fid = c->fid;
422 r->request.newfid = nc->fid;
423 r->request.nwname = nname;
424 memmove(r->request.wname, name, nname*sizeof(char*));
425
426 mountrpc(m, r);
427
428 if(r->reply.nwqid > nname)
429 error("too many QIDs returned by walk");
430 if(r->reply.nwqid < nname){
431 if(alloc)
432 cclose(nc);
433 wq->clone = nil;
434 if(r->reply.nwqid == 0){
435 free(wq);
436 wq = nil;
437 goto Return;
438 }
439 }
440
441 /* move new fid onto mnt device and update its qid */
442 if(wq->clone != nil){
443 if(wq->clone != c){
444 wq->clone->type = c->type;
445 wq->clone->mchan = c->mchan;
446 incref(&c->mchan->ref);
447 }
448 if(r->reply.nwqid > 0)
449 wq->clone->qid = r->reply.wqid[r->reply.nwqid-1];
450 }
451 wq->nqid = r->reply.nwqid;
452 for(i=0; i<wq->nqid; i++)
453 wq->qid[i] = r->reply.wqid[i];
454
455 Return:
456 poperror();
457 mntfree(r);
458 poperror();
459 return wq;
460 }
461
462 static int
mntstat(Chan * c,uchar * dp,int n)463 mntstat(Chan *c, uchar *dp, int n)
464 {
465 Mnt *m;
466 Mntrpc *r;
467
468 if(n < BIT16SZ)
469 error(Eshortstat);
470 m = mntchk(c);
471 r = mntralloc(c, m->msize);
472 if(waserror()) {
473 mntfree(r);
474 nexterror();
475 }
476 r->request.type = Tstat;
477 r->request.fid = c->fid;
478 mountrpc(m, r);
479
480 /* r->reply.nstat is 16 bits
481 if(r->reply.nstat >= 1<<16)
482 error("returned stat buffer count too large");
483 */
484
485 if(r->reply.nstat > n){
486 /*
487 * 12/31/2002 RSC
488 *
489 * This should be nstat-2, which is the first two
490 * bytes of the stat buffer. But dirstat and dirfstat
491 * depended on getting the full nstat (they didn't
492 * add BIT16SZ themselves). I fixed dirstat and dirfstat
493 * but am leaving this unchanged for now. After a
494 * few months, once enough of the relevant binaries
495 * have been recompiled for other reasons, we can
496 * change this to nstat-2. Devstat gets this right
497 * (via convD2M).
498 */
499 /* doesn't fit; just patch the count and return */
500 PBIT16((uchar*)dp, r->reply.nstat);
501 n = BIT16SZ;
502 }else{
503 n = r->reply.nstat;
504 memmove(dp, r->reply.stat, n);
505 validstat(dp, n);
506 mntdirfix(dp, c);
507 }
508 poperror();
509 mntfree(r);
510 return n;
511 }
512
513 static Chan*
mntopencreate(int type,Chan * c,char * name,int omode,ulong perm)514 mntopencreate(int type, Chan *c, char *name, int omode, ulong perm)
515 {
516 Mnt *m;
517 Mntrpc *r;
518
519 m = mntchk(c);
520 r = mntralloc(c, m->msize);
521 if(waserror()) {
522 mntfree(r);
523 nexterror();
524 }
525 r->request.type = type;
526 r->request.fid = c->fid;
527 r->request.mode = omode;
528 if(type == Tcreate){
529 r->request.perm = perm;
530 r->request.name = name;
531 }
532 mountrpc(m, r);
533
534 c->qid = r->reply.qid;
535 c->offset = 0;
536 c->mode = openmode(omode);
537 c->iounit = r->reply.iounit;
538 if(c->iounit == 0 || c->iounit > m->msize-IOHDRSZ)
539 c->iounit = m->msize-IOHDRSZ;
540 c->flag |= COPEN;
541 poperror();
542 mntfree(r);
543
544 if(c->flag & CCACHE)
545 copen(c);
546
547 return c;
548 }
549
550 static Chan*
mntopen(Chan * c,int omode)551 mntopen(Chan *c, int omode)
552 {
553 return mntopencreate(Topen, c, nil, omode, 0);
554 }
555
556 static void
mntcreate(Chan * c,char * name,int omode,ulong perm)557 mntcreate(Chan *c, char *name, int omode, ulong perm)
558 {
559 mntopencreate(Tcreate, c, name, omode, perm);
560 }
561
562 static void
mntclunk(Chan * c,int t)563 mntclunk(Chan *c, int t)
564 {
565 Mnt *m;
566 Mntrpc *r;
567
568 m = mntchk(c);
569 r = mntralloc(c, m->msize);
570 if(waserror()){
571 mntfree(r);
572 nexterror();
573 }
574
575 r->request.type = t;
576 r->request.fid = c->fid;
577 mountrpc(m, r);
578 mntfree(r);
579 poperror();
580 }
581
582 void
muxclose(Mnt * m)583 muxclose(Mnt *m)
584 {
585 Mntrpc *q, *r;
586
587 for(q = m->queue; q; q = r) {
588 r = q->list;
589 mntfree(q);
590 }
591 m->id = 0;
592 free(m->version);
593 m->version = nil;
594 mntpntfree(m);
595 }
596
597 void
mntpntfree(Mnt * m)598 mntpntfree(Mnt *m)
599 {
600 Mnt *f, **l;
601 Queue *q;
602
603 lock(&mntalloc.lk);
604 l = &mntalloc.list;
605 for(f = *l; f; f = f->list) {
606 if(f == m) {
607 *l = m->list;
608 break;
609 }
610 l = &f->list;
611 }
612 m->list = mntalloc.mntfree;
613 mntalloc.mntfree = m;
614 q = m->q;
615 unlock(&mntalloc.lk);
616
617 qfree(q);
618 }
619
620 static void
mntclose(Chan * c)621 mntclose(Chan *c)
622 {
623 mntclunk(c, Tclunk);
624 }
625
626 static void
mntremove(Chan * c)627 mntremove(Chan *c)
628 {
629 mntclunk(c, Tremove);
630 }
631
632 static int
mntwstat(Chan * c,uchar * dp,int n)633 mntwstat(Chan *c, uchar *dp, int n)
634 {
635 Mnt *m;
636 Mntrpc *r;
637
638 m = mntchk(c);
639 r = mntralloc(c, m->msize);
640 if(waserror()) {
641 mntfree(r);
642 nexterror();
643 }
644 r->request.type = Twstat;
645 r->request.fid = c->fid;
646 r->request.nstat = n;
647 r->request.stat = dp;
648 mountrpc(m, r);
649 poperror();
650 mntfree(r);
651 return n;
652 }
653
654 static long
mntread(Chan * c,void * buf,long n,vlong off)655 mntread(Chan *c, void *buf, long n, vlong off)
656 {
657 uchar *p, *e;
658 int nc, cache, isdir, dirlen;
659
660 isdir = 0;
661 cache = c->flag & CCACHE;
662 if(c->qid.type & QTDIR) {
663 cache = 0;
664 isdir = 1;
665 }
666
667 p = buf;
668 if(cache) {
669 nc = cread(c, buf, n, off);
670 if(nc > 0) {
671 n -= nc;
672 if(n == 0)
673 return nc;
674 p += nc;
675 off += nc;
676 }
677 n = mntrdwr(Tread, c, p, n, off);
678 cupdate(c, p, n, off);
679 return n + nc;
680 }
681
682 n = mntrdwr(Tread, c, buf, n, off);
683 if(isdir) {
684 for(e = &p[n]; p+BIT16SZ < e; p += dirlen){
685 dirlen = BIT16SZ+GBIT16(p);
686 if(p+dirlen > e)
687 break;
688 validstat(p, dirlen);
689 mntdirfix(p, c);
690 }
691 if(p != e)
692 error(Esbadstat);
693 }
694 return n;
695 }
696
697 static long
mntwrite(Chan * c,void * buf,long n,vlong off)698 mntwrite(Chan *c, void *buf, long n, vlong off)
699 {
700 return mntrdwr(Twrite, c, buf, n, off);
701 }
702
703 long
mntrdwr(int type,Chan * c,void * buf,long n,vlong off)704 mntrdwr(int type, Chan *c, void *buf, long n, vlong off)
705 {
706 Mnt *m;
707 Mntrpc *r;
708 char *uba;
709 int cache;
710 ulong cnt, nr, nreq;
711
712 m = mntchk(c);
713 uba = buf;
714 cnt = 0;
715 cache = c->flag & CCACHE;
716 if(c->qid.type & QTDIR)
717 cache = 0;
718 for(;;) {
719 r = mntralloc(c, m->msize);
720 if(waserror()) {
721 mntfree(r);
722 nexterror();
723 }
724 r->request.type = type;
725 r->request.fid = c->fid;
726 r->request.offset = off;
727 r->request.data = uba;
728 nr = n;
729 if(nr > m->msize-IOHDRSZ)
730 nr = m->msize-IOHDRSZ;
731 r->request.count = nr;
732 mountrpc(m, r);
733 nreq = r->request.count;
734 nr = r->reply.count;
735 if(nr > nreq)
736 nr = nreq;
737
738 if(type == Tread)
739 r->b = bl2mem((uchar*)uba, r->b, nr);
740 else if(cache)
741 cwrite(c, (uchar*)uba, nr, off);
742
743 poperror();
744 mntfree(r);
745 off += nr;
746 uba += nr;
747 cnt += nr;
748 n -= nr;
749 if(nr != nreq || n == 0)
750 break;
751 }
752 return cnt;
753 }
754
755 void
mountrpc(Mnt * m,Mntrpc * r)756 mountrpc(Mnt *m, Mntrpc *r)
757 {
758 char *sn, *cn;
759 int t;
760
761 r->reply.tag = 0;
762 r->reply.type = Tmax; /* can't ever be a valid message type */
763
764 mountio(m, r);
765
766 t = r->reply.type;
767 switch(t) {
768 case Rerror:
769 error(r->reply.ename);
770 case Rflush:
771 error(Eintr);
772 default:
773 if(t == r->request.type+1)
774 break;
775 sn = "?";
776 if(m->c->name != nil)
777 sn = m->c->name->s;
778 cn = "?";
779 if(r->c != nil && r->c->name != nil)
780 cn = r->c->name->s;
781 print("mnt: proc %lud: mismatch from %s %s rep 0x%lux tag %d fid %d T%d R%d rp %d\n",
782 up->pid, sn, cn,
783 r, r->request.tag, r->request.fid, r->request.type,
784 r->reply.type, r->reply.tag);
785 error(Emountrpc);
786 }
787 }
788
789 void
mountio(Mnt * m,Mntrpc * r)790 mountio(Mnt *m, Mntrpc *r)
791 {
792 int n;
793
794 while(waserror()) {
795 if(m->rip == up)
796 mntgate(m);
797 if(strcmp(up->errstr, Eintr) != 0){
798 mntflushfree(m, r);
799 nexterror();
800 }
801 r = mntflushalloc(r, m->msize);
802 }
803
804 lock(&m->lk);
805 r->m = m;
806 r->list = m->queue;
807 m->queue = r;
808 unlock(&m->lk);
809
810 /* Transmit a file system rpc */
811 if(m->msize == 0)
812 panic("msize");
813 n = convS2M(&r->request, r->rpc, m->msize);
814 if(n < 0)
815 panic("bad message type in mountio");
816 if(devtab[m->c->type]->write(m->c, r->rpc, n, 0) != n)
817 error(Emountrpc);
818 r->stime = fastticks(nil);
819 r->reqlen = n;
820
821 /* Gate readers onto the mount point one at a time */
822 for(;;) {
823 lock(&m->lk);
824 if(m->rip == 0)
825 break;
826 unlock(&m->lk);
827 sleep(&r->r, rpcattn, r);
828 if(r->done){
829 poperror();
830 mntflushfree(m, r);
831 return;
832 }
833 }
834 m->rip = up;
835 unlock(&m->lk);
836 while(r->done == 0) {
837 if(mntrpcread(m, r) < 0)
838 error(Emountrpc);
839 mountmux(m, r);
840 }
841 mntgate(m);
842 poperror();
843 mntflushfree(m, r);
844 }
845
846 static int
doread(Mnt * m,int len)847 doread(Mnt *m, int len)
848 {
849 Block *b;
850
851 while(qlen(m->q) < len){
852 b = devtab[m->c->type]->bread(m->c, m->msize, 0);
853 if(b == nil)
854 return -1;
855 if(BLEN(b) == 0){
856 freeblist(b);
857 return -1;
858 }
859 qaddlist(m->q, b);
860 }
861 return 0;
862 }
863
864 int
mntrpcread(Mnt * m,Mntrpc * r)865 mntrpcread(Mnt *m, Mntrpc *r)
866 {
867 int i, t, len, hlen;
868 Block *b, **l, *nb;
869
870 r->reply.type = 0;
871 r->reply.tag = 0;
872
873 /* read at least length, type, and tag and pullup to a single block */
874 if(doread(m, BIT32SZ+BIT8SZ+BIT16SZ) < 0)
875 return -1;
876 nb = pullupqueue(m->q, BIT32SZ+BIT8SZ+BIT16SZ);
877
878 /* read in the rest of the message, avoid rediculous (for now) message sizes */
879 len = GBIT32(nb->rp);
880 if(len > m->msize){
881 qdiscard(m->q, qlen(m->q));
882 return -1;
883 }
884 if(doread(m, len) < 0)
885 return -1;
886
887 /* pullup the header (i.e. everything except data) */
888 t = nb->rp[BIT32SZ];
889 switch(t){
890 case Rread:
891 hlen = BIT32SZ+BIT8SZ+BIT16SZ+BIT32SZ;
892 break;
893 default:
894 hlen = len;
895 break;
896 }
897 nb = pullupqueue(m->q, hlen);
898
899 if(convM2S(nb->rp, len, &r->reply) <= 0){
900 /* bad message, dump it */
901 print("mntrpcread: convM2S failed\n");
902 qdiscard(m->q, len);
903 return -1;
904 }
905
906 /* hang the data off of the fcall struct */
907 l = &r->b;
908 *l = nil;
909 do {
910 b = qremove(m->q);
911 if(hlen > 0){
912 b->rp += hlen;
913 len -= hlen;
914 hlen = 0;
915 }
916 i = BLEN(b);
917 if(i <= len){
918 len -= i;
919 *l = b;
920 l = &(b->next);
921 } else {
922 /* split block and put unused bit back */
923 nb = allocb(i-len);
924 memmove(nb->wp, b->rp+len, i-len);
925 b->wp = b->rp+len;
926 nb->wp += i-len;
927 qputback(m->q, nb);
928 *l = b;
929 return 0;
930 }
931 }while(len > 0);
932
933 return 0;
934 }
935
936 void
mntgate(Mnt * m)937 mntgate(Mnt *m)
938 {
939 Mntrpc *q;
940
941 lock(&m->lk);
942 m->rip = 0;
943 for(q = m->queue; q; q = q->list) {
944 if(q->done == 0)
945 if(wakeup(&q->r))
946 break;
947 }
948 unlock(&m->lk);
949 }
950
951 void
mountmux(Mnt * m,Mntrpc * r)952 mountmux(Mnt *m, Mntrpc *r)
953 {
954 Mntrpc **l, *q;
955
956 lock(&m->lk);
957 l = &m->queue;
958 for(q = *l; q; q = q->list) {
959 /* look for a reply to a message */
960 if(q->request.tag == r->reply.tag) {
961 *l = q->list;
962 if(q != r) {
963 /*
964 * Completed someone else.
965 * Trade pointers to receive buffer.
966 */
967 q->reply = r->reply;
968 q->b = r->b;
969 r->b = nil;
970 }
971 q->done = 1;
972 unlock(&m->lk);
973 if(mntstats != 0)
974 (*mntstats)(q->request.type,
975 m->c, q->stime,
976 q->reqlen + r->replen);
977 if(q != r)
978 wakeup(&q->r);
979 return;
980 }
981 l = &q->list;
982 }
983 unlock(&m->lk);
984 print("unexpected reply tag %ud; type %d\n", r->reply.tag, r->reply.type);
985 }
986
987 /*
988 * Create a new flush request and chain the previous
989 * requests from it
990 */
991 Mntrpc*
mntflushalloc(Mntrpc * r,ulong iounit)992 mntflushalloc(Mntrpc *r, ulong iounit)
993 {
994 Mntrpc *fr;
995
996 fr = mntralloc(0, iounit);
997
998 fr->request.type = Tflush;
999 if(r->request.type == Tflush)
1000 fr->request.oldtag = r->request.oldtag;
1001 else
1002 fr->request.oldtag = r->request.tag;
1003 fr->flushed = r;
1004
1005 return fr;
1006 }
1007
1008 /*
1009 * Free a chain of flushes. Remove each unanswered
1010 * flush and the original message from the unanswered
1011 * request queue. Mark the original message as done
1012 * and if it hasn't been answered set the reply to to
1013 * Rflush.
1014 */
1015 void
mntflushfree(Mnt * m,Mntrpc * r)1016 mntflushfree(Mnt *m, Mntrpc *r)
1017 {
1018 Mntrpc *fr;
1019
1020 while(r){
1021 fr = r->flushed;
1022 if(!r->done){
1023 r->reply.type = Rflush;
1024 mntqrm(m, r);
1025 }
1026 if(fr)
1027 mntfree(r);
1028 r = fr;
1029 }
1030 }
1031
1032 int
alloctag(void)1033 alloctag(void)
1034 {
1035 int i, j;
1036 ulong v;
1037
1038 for(i = 0; i < NMASK; i++){
1039 v = mntalloc.tagmask[i];
1040 if(v == ~0)
1041 continue;
1042 for(j = 0; j < 1<<TAGSHIFT; j++)
1043 if((v & (1<<j)) == 0){
1044 mntalloc.tagmask[i] |= 1<<j;
1045 return (i<<TAGSHIFT) + j;
1046 }
1047 }
1048 panic("no friggin tags left");
1049 return NOTAG;
1050 }
1051
1052 void
freetag(int t)1053 freetag(int t)
1054 {
1055 mntalloc.tagmask[t>>TAGSHIFT] &= ~(1<<(t&TAGMASK));
1056 }
1057
1058 Mntrpc*
mntralloc(Chan * c,ulong msize)1059 mntralloc(Chan *c, ulong msize)
1060 {
1061 Mntrpc *new;
1062
1063 lock(&mntalloc.lk);
1064 new = mntalloc.rpcfree;
1065 if(new == nil){
1066 new = malloc(sizeof(Mntrpc));
1067 if(new == nil) {
1068 unlock(&mntalloc.lk);
1069 exhausted("mount rpc header");
1070 }
1071 /*
1072 * The header is split from the data buffer as
1073 * mountmux may swap the buffer with another header.
1074 */
1075 new->rpc = mallocz(msize, 0);
1076 if(new->rpc == nil){
1077 free(new);
1078 unlock(&mntalloc.lk);
1079 exhausted("mount rpc buffer");
1080 }
1081 new->rpclen = msize;
1082 new->request.tag = alloctag();
1083 }
1084 else {
1085 mntalloc.rpcfree = new->list;
1086 mntalloc.nrpcfree--;
1087 if(new->rpclen < msize){
1088 free(new->rpc);
1089 new->rpc = mallocz(msize, 0);
1090 if(new->rpc == nil){
1091 free(new);
1092 mntalloc.nrpcused--;
1093 unlock(&mntalloc.lk);
1094 exhausted("mount rpc buffer");
1095 }
1096 new->rpclen = msize;
1097 }
1098 }
1099 mntalloc.nrpcused++;
1100 unlock(&mntalloc.lk);
1101 new->c = c;
1102 new->done = 0;
1103 new->flushed = nil;
1104 new->b = nil;
1105 return new;
1106 }
1107
1108 void
mntfree(Mntrpc * r)1109 mntfree(Mntrpc *r)
1110 {
1111 if(r->b != nil)
1112 freeblist(r->b);
1113 lock(&mntalloc.lk);
1114 if(mntalloc.nrpcfree >= 10){
1115 free(r->rpc);
1116 free(r);
1117 freetag(r->request.tag);
1118 }
1119 else{
1120 r->list = mntalloc.rpcfree;
1121 mntalloc.rpcfree = r;
1122 mntalloc.nrpcfree++;
1123 }
1124 mntalloc.nrpcused--;
1125 unlock(&mntalloc.lk);
1126 }
1127
1128 void
mntqrm(Mnt * m,Mntrpc * r)1129 mntqrm(Mnt *m, Mntrpc *r)
1130 {
1131 Mntrpc **l, *f;
1132
1133 lock(&m->lk);
1134 r->done = 1;
1135
1136 l = &m->queue;
1137 for(f = *l; f; f = f->list) {
1138 if(f == r) {
1139 *l = r->list;
1140 break;
1141 }
1142 l = &f->list;
1143 }
1144 unlock(&m->lk);
1145 }
1146
1147 Mnt*
mntchk(Chan * c)1148 mntchk(Chan *c)
1149 {
1150 Mnt *m;
1151
1152 /* This routine is mostly vestiges of prior lives; now it's just sanity checking */
1153
1154 if(c->mchan == nil)
1155 panic("mntchk 1: nil mchan c %s\n", c2name(c));
1156
1157 m = c->mchan->mux;
1158
1159 if(m == nil)
1160 print("mntchk 2: nil mux c %s c->mchan %s \n", c2name(c), c2name(c->mchan));
1161
1162 /*
1163 * Was it closed and reused (was error(Eshutdown); now, it can't happen)
1164 */
1165 if(m->id == 0 || m->id >= c->dev)
1166 panic("mntchk 3: can't happen");
1167
1168 return m;
1169 }
1170
1171 /*
1172 * Rewrite channel type and dev for in-flight data to
1173 * reflect local values. These entries are known to be
1174 * the first two in the Dir encoding after the count.
1175 */
1176 void
mntdirfix(uchar * dirbuf,Chan * c)1177 mntdirfix(uchar *dirbuf, Chan *c)
1178 {
1179 uint r;
1180
1181 r = devtab[c->type]->dc;
1182 dirbuf += BIT16SZ; /* skip count */
1183 PBIT16(dirbuf, r);
1184 dirbuf += BIT16SZ;
1185 PBIT32(dirbuf, c->dev);
1186 }
1187
1188 int
rpcattn(void * v)1189 rpcattn(void *v)
1190 {
1191 Mntrpc *r;
1192
1193 r = v;
1194 return r->done || r->m->rip == 0;
1195 }
1196
1197 Dev mntdevtab = {
1198 'M',
1199 "mnt",
1200
1201 mntreset,
1202 devinit,
1203 devshutdown,
1204 mntattach,
1205 mntwalk,
1206 mntstat,
1207 mntopen,
1208 mntcreate,
1209 mntclose,
1210 mntread,
1211 devbread,
1212 mntwrite,
1213 devbwrite,
1214 mntremove,
1215 mntwstat,
1216 };
1217