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