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