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