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