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