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