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
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);
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*
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);
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);
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);
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*
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 nc->flag |= c->flag&CCACHE;
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
mntstat(Chan * c,uchar * dp,int n)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*
mntopencreate(int type,Chan * c,char * name,int omode,ulong perm)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*
mntopen(Chan * c,int omode)533 mntopen(Chan *c, int omode)
534 {
535 return mntopencreate(Topen, c, nil, omode, 0);
536 }
537
538 static void
mntcreate(Chan * c,char * name,int omode,ulong perm)539 mntcreate(Chan *c, char *name, int omode, ulong perm)
540 {
541 mntopencreate(Tcreate, c, name, omode, perm);
542 }
543
544 static void
mntclunk(Chan * c,int t)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
muxclose(Mnt * m)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
mntpntfree(Mnt * m)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
mntclose(Chan * c)603 mntclose(Chan *c)
604 {
605 mntclunk(c, Tclunk);
606 }
607
608 static void
mntremove(Chan * c)609 mntremove(Chan *c)
610 {
611 mntclunk(c, Tremove);
612 }
613
614 static int
mntwstat(Chan * c,uchar * dp,int n)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
mntread(Chan * c,void * buf,long n,vlong off)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
mntwrite(Chan * c,void * buf,long n,vlong off)680 mntwrite(Chan *c, void *buf, long n, vlong off)
681 {
682 return mntrdwr(Twrite, c, buf, n, off);
683 }
684
685 long
mntrdwr(int type,Chan * c,void * buf,long n,vlong off)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
mountrpc(Mnt * m,Mntrpc * r)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
mountio(Mnt * m,Mntrpc * r)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
doread(Mnt * m,int len)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
mntrpcread(Mnt * m,Mntrpc * r)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
mntgate(Mnt * m)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
mountmux(Mnt * m,Mntrpc * r)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*
mntflushalloc(Mntrpc * r,ulong iounit)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
mntflushfree(Mnt * m,Mntrpc * r)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
alloctag(void)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
freetag(int t)1035 freetag(int t)
1036 {
1037 mntalloc.tagmask[t>>TAGSHIFT] &= ~(1<<(t&TAGMASK));
1038 }
1039
1040 Mntrpc*
mntralloc(Chan * c,ulong msize)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
mntfree(Mntrpc * r)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
mntqrm(Mnt * m,Mntrpc * r)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*
mntchk(Chan * c)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\n", 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
mntdirfix(uchar * dirbuf,Chan * c)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
rpcattn(void * v)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 };
1199