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