xref: /plan9/sys/src/cmd/unix/drawterm/kern/devmnt.c (revision 96cbc34f1b36a29efdcfd47b10e70703a690febc)
1 #include	"u.h"
2 #include	"lib.h"
3 #include	"dat.h"
4 #include	"fns.h"
5 #include	"error.h"
6 
7 /*
8  * References are managed as follows:
9  * The channel to the server - a network connection or pipe - has one
10  * reference for every Chan open on the server.  The server channel has
11  * c->mux set to the Mnt used for muxing control to that server.  Mnts
12  * have no reference count; they go away when c goes away.
13  * Each channel derived from the mount point has mchan set to c,
14  * and increfs/decrefs mchan to manage references on the server
15  * connection.
16  */
17 
18 #define MAXRPC (IOHDRSZ+8192)
19 
20 struct Mntrpc
21 {
22 	Chan*	c;		/* Channel for whom we are working */
23 	Mntrpc*	list;		/* Free/pending list */
24 	Fcall	request;	/* Outgoing file system protocol message */
25 	Fcall 	reply;		/* Incoming reply */
26 	Mnt*	m;		/* Mount device during rpc */
27 	Rendez	r;		/* Place to hang out */
28 	uchar*	rpc;		/* I/O Data buffer */
29 	uint		rpclen;	/* len of buffer */
30 	Block	*b;		/* reply blocks */
31 	char	done;		/* Rpc completed */
32 	uvlong	stime;		/* start time for mnt statistics */
33 	ulong	reqlen;		/* request length for mnt statistics */
34 	ulong	replen;		/* reply length for mnt statistics */
35 	Mntrpc*	flushed;	/* message this one flushes */
36 };
37 
38 enum
39 {
40 	TAGSHIFT = 5,			/* ulong has to be 32 bits */
41 	TAGMASK = (1<<TAGSHIFT)-1,
42 	NMASK = (64*1024)>>TAGSHIFT,
43 };
44 
45 struct Mntalloc
46 {
47 	Lock lk;
48 	Mnt*	list;		/* Mount devices in use */
49 	Mnt*	mntfree;	/* Free list */
50 	Mntrpc*	rpcfree;
51 	int	nrpcfree;
52 	int	nrpcused;
53 	ulong	id;
54 	ulong	tagmask[NMASK];
55 }mntalloc;
56 
57 void	mattach(Mnt*, Chan*, char*);
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->ref.lk);
165 	oo = c->offset;
166 	c->offset += k;
167 	unlock(&c->ref.lk);
168 
169 	l = devtab[c->type]->write(c, msg, k, oo);
170 
171 	if(l < k){
172 		lock(&c->ref.lk);
173 		c->offset -= k - l;
174 		unlock(&c->ref.lk);
175 		error("short write in fversion");
176 	}
177 
178 	/* message sent; receive and decode reply */
179 	k = devtab[c->type]->read(c, msg, 8192+IOHDRSZ, c->offset);
180 	if(k <= 0)
181 		error("EOF receiving fversion reply");
182 
183 	lock(&c->ref.lk);
184 	c->offset += k;
185 	unlock(&c->ref.lk);
186 
187 	l = convM2S(msg, k, &f);
188 	if(l != k)
189 		error("bad fversion conversion on reply");
190 	if(f.type != Rversion){
191 		if(f.type == Rerror)
192 			error(f.ename);
193 		error("unexpected reply type in fversion");
194 	}
195 	if(f.msize > msize)
196 		error("server tries to increase msize in fversion");
197 	if(f.msize<256 || f.msize>1024*1024)
198 		error("nonsense value of msize in fversion");
199 	k = strlen(f.version);
200 	if(strncmp(f.version, v, k) != 0)
201 		error("bad 9P version returned from server");
202 
203 	/* now build Mnt associated with this connection */
204 	lock(&mntalloc.lk);
205 	m = mntalloc.mntfree;
206 	if(m != 0)
207 		mntalloc.mntfree = m->list;
208 	else {
209 		m = malloc(sizeof(Mnt));
210 		if(m == 0) {
211 			unlock(&mntalloc.lk);
212 			exhausted("mount devices");
213 		}
214 	}
215 	m->list = mntalloc.list;
216 	mntalloc.list = m;
217 	m->version = nil;
218 	kstrdup(&m->version, f.version);
219 	m->id = mntalloc.id++;
220 	m->q = qopen(10*MAXRPC, 0, 0, nil);
221 	m->msize = f.msize;
222 	unlock(&mntalloc.lk);
223 
224 	if(returnlen > 0){
225 		if(returnlen < k)
226 			error(Eshort);
227 		memmove(version, f.version, k);
228 	}
229 
230 	poperror();	/* msg */
231 	free(msg);
232 
233 	lock(&m->lk);
234 	m->queue = 0;
235 	m->rip = 0;
236 
237 	c->flag |= CMSG;
238 	c->mux = m;
239 	m->c = c;
240 	unlock(&m->lk);
241 
242 	poperror();	/* c */
243 	qunlock(&c->umqlock);
244 
245 	return k;
246 }
247 
248 Chan*
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->ref);
288 	c->mqid = c->qid;
289 	c->mode = ORDWR;
290 
291 	poperror();	/* r */
292 	mntfree(r);
293 
294 	poperror();	/* c */
295 
296 	return c;
297 
298 }
299 
300 static Chan*
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->ref);
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.lk);
373 	c->dev = mntalloc.id++;
374 	unlock(&mntalloc.lk);
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 
416 	if(waserror()) {
417 		mntfree(r);
418 		nexterror();
419 	}
420 	r->request.type = Twalk;
421 	r->request.fid = c->fid;
422 	r->request.newfid = nc->fid;
423 	r->request.nwname = nname;
424 	memmove(r->request.wname, name, nname*sizeof(char*));
425 
426 	mountrpc(m, r);
427 
428 	if(r->reply.nwqid > nname)
429 		error("too many QIDs returned by walk");
430 	if(r->reply.nwqid < nname){
431 		if(alloc)
432 			cclose(nc);
433 		wq->clone = nil;
434 		if(r->reply.nwqid == 0){
435 			free(wq);
436 			wq = nil;
437 			goto Return;
438 		}
439 	}
440 
441 	/* move new fid onto mnt device and update its qid */
442 	if(wq->clone != nil){
443 		if(wq->clone != c){
444 			wq->clone->type = c->type;
445 			wq->clone->mchan = c->mchan;
446 			incref(&c->mchan->ref);
447 		}
448 		if(r->reply.nwqid > 0)
449 			wq->clone->qid = r->reply.wqid[r->reply.nwqid-1];
450 	}
451 	wq->nqid = r->reply.nwqid;
452 	for(i=0; i<wq->nqid; i++)
453 		wq->qid[i] = r->reply.wqid[i];
454 
455     Return:
456 	poperror();
457 	mntfree(r);
458 	poperror();
459 	return wq;
460 }
461 
462 static int
mntstat(Chan * c,uchar * dp,int n)463 mntstat(Chan *c, uchar *dp, int n)
464 {
465 	Mnt *m;
466 	Mntrpc *r;
467 
468 	if(n < BIT16SZ)
469 		error(Eshortstat);
470 	m = mntchk(c);
471 	r = mntralloc(c, m->msize);
472 	if(waserror()) {
473 		mntfree(r);
474 		nexterror();
475 	}
476 	r->request.type = Tstat;
477 	r->request.fid = c->fid;
478 	mountrpc(m, r);
479 
480 /* r->reply.nstat is 16 bits
481 	if(r->reply.nstat >= 1<<16)
482 		error("returned stat buffer count too large");
483 */
484 
485 	if(r->reply.nstat > n){
486 		/*
487 		 * 12/31/2002 RSC
488 		 *
489 		 * This should be nstat-2, which is the first two
490 		 * bytes of the stat buffer.  But dirstat and dirfstat
491 		 * depended on getting the full nstat (they didn't
492 		 * add BIT16SZ themselves).  I fixed dirstat and dirfstat
493 		 * but am leaving this unchanged for now.  After a
494 		 * few months, once enough of the relevant binaries
495 		 * have been recompiled for other reasons, we can
496 		 * change this to nstat-2.  Devstat gets this right
497 		 * (via convD2M).
498 		 */
499 		/* doesn't fit; just patch the count and return */
500 		PBIT16((uchar*)dp, r->reply.nstat);
501 		n = BIT16SZ;
502 	}else{
503 		n = r->reply.nstat;
504 		memmove(dp, r->reply.stat, n);
505 		validstat(dp, n);
506 		mntdirfix(dp, c);
507 	}
508 	poperror();
509 	mntfree(r);
510 	return n;
511 }
512 
513 static Chan*
mntopencreate(int type,Chan * c,char * name,int omode,ulong perm)514 mntopencreate(int type, Chan *c, char *name, int omode, ulong perm)
515 {
516 	Mnt *m;
517 	Mntrpc *r;
518 
519 	m = mntchk(c);
520 	r = mntralloc(c, m->msize);
521 	if(waserror()) {
522 		mntfree(r);
523 		nexterror();
524 	}
525 	r->request.type = type;
526 	r->request.fid = c->fid;
527 	r->request.mode = omode;
528 	if(type == Tcreate){
529 		r->request.perm = perm;
530 		r->request.name = name;
531 	}
532 	mountrpc(m, r);
533 
534 	c->qid = r->reply.qid;
535 	c->offset = 0;
536 	c->mode = openmode(omode);
537 	c->iounit = r->reply.iounit;
538 	if(c->iounit == 0 || c->iounit > m->msize-IOHDRSZ)
539 		c->iounit = m->msize-IOHDRSZ;
540 	c->flag |= COPEN;
541 	poperror();
542 	mntfree(r);
543 
544 	if(c->flag & CCACHE)
545 		copen(c);
546 
547 	return c;
548 }
549 
550 static Chan*
mntopen(Chan * c,int omode)551 mntopen(Chan *c, int omode)
552 {
553 	return mntopencreate(Topen, c, nil, omode, 0);
554 }
555 
556 static void
mntcreate(Chan * c,char * name,int omode,ulong perm)557 mntcreate(Chan *c, char *name, int omode, ulong perm)
558 {
559 	mntopencreate(Tcreate, c, name, omode, perm);
560 }
561 
562 static void
mntclunk(Chan * c,int t)563 mntclunk(Chan *c, int t)
564 {
565 	Mnt *m;
566 	Mntrpc *r;
567 
568 	m = mntchk(c);
569 	r = mntralloc(c, m->msize);
570 	if(waserror()){
571 		mntfree(r);
572 		nexterror();
573 	}
574 
575 	r->request.type = t;
576 	r->request.fid = c->fid;
577 	mountrpc(m, r);
578 	mntfree(r);
579 	poperror();
580 }
581 
582 void
muxclose(Mnt * m)583 muxclose(Mnt *m)
584 {
585 	Mntrpc *q, *r;
586 
587 	for(q = m->queue; q; q = r) {
588 		r = q->list;
589 		mntfree(q);
590 	}
591 	m->id = 0;
592 	free(m->version);
593 	m->version = nil;
594 	mntpntfree(m);
595 }
596 
597 void
mntpntfree(Mnt * m)598 mntpntfree(Mnt *m)
599 {
600 	Mnt *f, **l;
601 	Queue *q;
602 
603 	lock(&mntalloc.lk);
604 	l = &mntalloc.list;
605 	for(f = *l; f; f = f->list) {
606 		if(f == m) {
607 			*l = m->list;
608 			break;
609 		}
610 		l = &f->list;
611 	}
612 	m->list = mntalloc.mntfree;
613 	mntalloc.mntfree = m;
614 	q = m->q;
615 	unlock(&mntalloc.lk);
616 
617 	qfree(q);
618 }
619 
620 static void
mntclose(Chan * c)621 mntclose(Chan *c)
622 {
623 	mntclunk(c, Tclunk);
624 }
625 
626 static void
mntremove(Chan * c)627 mntremove(Chan *c)
628 {
629 	mntclunk(c, Tremove);
630 }
631 
632 static int
mntwstat(Chan * c,uchar * dp,int n)633 mntwstat(Chan *c, uchar *dp, int n)
634 {
635 	Mnt *m;
636 	Mntrpc *r;
637 
638 	m = mntchk(c);
639 	r = mntralloc(c, m->msize);
640 	if(waserror()) {
641 		mntfree(r);
642 		nexterror();
643 	}
644 	r->request.type = Twstat;
645 	r->request.fid = c->fid;
646 	r->request.nstat = n;
647 	r->request.stat = dp;
648 	mountrpc(m, r);
649 	poperror();
650 	mntfree(r);
651 	return n;
652 }
653 
654 static long
mntread(Chan * c,void * buf,long n,vlong off)655 mntread(Chan *c, void *buf, long n, vlong off)
656 {
657 	uchar *p, *e;
658 	int nc, cache, isdir, dirlen;
659 
660 	isdir = 0;
661 	cache = c->flag & CCACHE;
662 	if(c->qid.type & QTDIR) {
663 		cache = 0;
664 		isdir = 1;
665 	}
666 
667 	p = buf;
668 	if(cache) {
669 		nc = cread(c, buf, n, off);
670 		if(nc > 0) {
671 			n -= nc;
672 			if(n == 0)
673 				return nc;
674 			p += nc;
675 			off += nc;
676 		}
677 		n = mntrdwr(Tread, c, p, n, off);
678 		cupdate(c, p, n, off);
679 		return n + nc;
680 	}
681 
682 	n = mntrdwr(Tread, c, buf, n, off);
683 	if(isdir) {
684 		for(e = &p[n]; p+BIT16SZ < e; p += dirlen){
685 			dirlen = BIT16SZ+GBIT16(p);
686 			if(p+dirlen > e)
687 				break;
688 			validstat(p, dirlen);
689 			mntdirfix(p, c);
690 		}
691 		if(p != e)
692 			error(Esbadstat);
693 	}
694 	return n;
695 }
696 
697 static long
mntwrite(Chan * c,void * buf,long n,vlong off)698 mntwrite(Chan *c, void *buf, long n, vlong off)
699 {
700 	return mntrdwr(Twrite, c, buf, n, off);
701 }
702 
703 long
mntrdwr(int type,Chan * c,void * buf,long n,vlong off)704 mntrdwr(int type, Chan *c, void *buf, long n, vlong off)
705 {
706 	Mnt *m;
707  	Mntrpc *r;
708 	char *uba;
709 	int cache;
710 	ulong cnt, nr, nreq;
711 
712 	m = mntchk(c);
713 	uba = buf;
714 	cnt = 0;
715 	cache = c->flag & CCACHE;
716 	if(c->qid.type & QTDIR)
717 		cache = 0;
718 	for(;;) {
719 		r = mntralloc(c, m->msize);
720 		if(waserror()) {
721 			mntfree(r);
722 			nexterror();
723 		}
724 		r->request.type = type;
725 		r->request.fid = c->fid;
726 		r->request.offset = off;
727 		r->request.data = uba;
728 		nr = n;
729 		if(nr > m->msize-IOHDRSZ)
730 			nr = m->msize-IOHDRSZ;
731 		r->request.count = nr;
732 		mountrpc(m, r);
733 		nreq = r->request.count;
734 		nr = r->reply.count;
735 		if(nr > nreq)
736 			nr = nreq;
737 
738 		if(type == Tread)
739 			r->b = bl2mem((uchar*)uba, r->b, nr);
740 		else if(cache)
741 			cwrite(c, (uchar*)uba, nr, off);
742 
743 		poperror();
744 		mntfree(r);
745 		off += nr;
746 		uba += nr;
747 		cnt += nr;
748 		n -= nr;
749 		if(nr != nreq || n == 0)
750 			break;
751 	}
752 	return cnt;
753 }
754 
755 void
mountrpc(Mnt * m,Mntrpc * r)756 mountrpc(Mnt *m, Mntrpc *r)
757 {
758 	char *sn, *cn;
759 	int t;
760 
761 	r->reply.tag = 0;
762 	r->reply.type = Tmax;	/* can't ever be a valid message type */
763 
764 	mountio(m, r);
765 
766 	t = r->reply.type;
767 	switch(t) {
768 	case Rerror:
769 		error(r->reply.ename);
770 	case Rflush:
771 		error(Eintr);
772 	default:
773 		if(t == r->request.type+1)
774 			break;
775 		sn = "?";
776 		if(m->c->name != nil)
777 			sn = m->c->name->s;
778 		cn = "?";
779 		if(r->c != nil && r->c->name != nil)
780 			cn = r->c->name->s;
781 		print("mnt: proc %lud: mismatch from %s %s rep 0x%lux tag %d fid %d T%d R%d rp %d\n",
782 			up->pid, sn, cn,
783 			r, r->request.tag, r->request.fid, r->request.type,
784 			r->reply.type, r->reply.tag);
785 		error(Emountrpc);
786 	}
787 }
788 
789 void
mountio(Mnt * m,Mntrpc * r)790 mountio(Mnt *m, Mntrpc *r)
791 {
792 	int n;
793 
794 	while(waserror()) {
795 		if(m->rip == up)
796 			mntgate(m);
797 		if(strcmp(up->errstr, Eintr) != 0){
798 			mntflushfree(m, r);
799 			nexterror();
800 		}
801 		r = mntflushalloc(r, m->msize);
802 	}
803 
804 	lock(&m->lk);
805 	r->m = m;
806 	r->list = m->queue;
807 	m->queue = r;
808 	unlock(&m->lk);
809 
810 	/* Transmit a file system rpc */
811 	if(m->msize == 0)
812 		panic("msize");
813 	n = convS2M(&r->request, r->rpc, m->msize);
814 	if(n < 0)
815 		panic("bad message type in mountio");
816 	if(devtab[m->c->type]->write(m->c, r->rpc, n, 0) != n)
817 		error(Emountrpc);
818 	r->stime = fastticks(nil);
819 	r->reqlen = n;
820 
821 	/* Gate readers onto the mount point one at a time */
822 	for(;;) {
823 		lock(&m->lk);
824 		if(m->rip == 0)
825 			break;
826 		unlock(&m->lk);
827 		sleep(&r->r, rpcattn, r);
828 		if(r->done){
829 			poperror();
830 			mntflushfree(m, r);
831 			return;
832 		}
833 	}
834 	m->rip = up;
835 	unlock(&m->lk);
836 	while(r->done == 0) {
837 		if(mntrpcread(m, r) < 0)
838 			error(Emountrpc);
839 		mountmux(m, r);
840 	}
841 	mntgate(m);
842 	poperror();
843 	mntflushfree(m, r);
844 }
845 
846 static int
doread(Mnt * m,int len)847 doread(Mnt *m, int len)
848 {
849 	Block *b;
850 
851 	while(qlen(m->q) < len){
852 		b = devtab[m->c->type]->bread(m->c, m->msize, 0);
853 		if(b == nil)
854 			return -1;
855 		if(BLEN(b) == 0){
856 			freeblist(b);
857 			return -1;
858 		}
859 		qaddlist(m->q, b);
860 	}
861 	return 0;
862 }
863 
864 int
mntrpcread(Mnt * m,Mntrpc * r)865 mntrpcread(Mnt *m, Mntrpc *r)
866 {
867 	int i, t, len, hlen;
868 	Block *b, **l, *nb;
869 
870 	r->reply.type = 0;
871 	r->reply.tag = 0;
872 
873 	/* read at least length, type, and tag and pullup to a single block */
874 	if(doread(m, BIT32SZ+BIT8SZ+BIT16SZ) < 0)
875 		return -1;
876 	nb = pullupqueue(m->q, BIT32SZ+BIT8SZ+BIT16SZ);
877 
878 	/* read in the rest of the message, avoid rediculous (for now) message sizes */
879 	len = GBIT32(nb->rp);
880 	if(len > m->msize){
881 		qdiscard(m->q, qlen(m->q));
882 		return -1;
883 	}
884 	if(doread(m, len) < 0)
885 		return -1;
886 
887 	/* pullup the header (i.e. everything except data) */
888 	t = nb->rp[BIT32SZ];
889 	switch(t){
890 	case Rread:
891 		hlen = BIT32SZ+BIT8SZ+BIT16SZ+BIT32SZ;
892 		break;
893 	default:
894 		hlen = len;
895 		break;
896 	}
897 	nb = pullupqueue(m->q, hlen);
898 
899 	if(convM2S(nb->rp, len, &r->reply) <= 0){
900 		/* bad message, dump it */
901 		print("mntrpcread: convM2S failed\n");
902 		qdiscard(m->q, len);
903 		return -1;
904 	}
905 
906 	/* hang the data off of the fcall struct */
907 	l = &r->b;
908 	*l = nil;
909 	do {
910 		b = qremove(m->q);
911 		if(hlen > 0){
912 			b->rp += hlen;
913 			len -= hlen;
914 			hlen = 0;
915 		}
916 		i = BLEN(b);
917 		if(i <= len){
918 			len -= i;
919 			*l = b;
920 			l = &(b->next);
921 		} else {
922 			/* split block and put unused bit back */
923 			nb = allocb(i-len);
924 			memmove(nb->wp, b->rp+len, i-len);
925 			b->wp = b->rp+len;
926 			nb->wp += i-len;
927 			qputback(m->q, nb);
928 			*l = b;
929 			return 0;
930 		}
931 	}while(len > 0);
932 
933 	return 0;
934 }
935 
936 void
mntgate(Mnt * m)937 mntgate(Mnt *m)
938 {
939 	Mntrpc *q;
940 
941 	lock(&m->lk);
942 	m->rip = 0;
943 	for(q = m->queue; q; q = q->list) {
944 		if(q->done == 0)
945 		if(wakeup(&q->r))
946 			break;
947 	}
948 	unlock(&m->lk);
949 }
950 
951 void
mountmux(Mnt * m,Mntrpc * r)952 mountmux(Mnt *m, Mntrpc *r)
953 {
954 	Mntrpc **l, *q;
955 
956 	lock(&m->lk);
957 	l = &m->queue;
958 	for(q = *l; q; q = q->list) {
959 		/* look for a reply to a message */
960 		if(q->request.tag == r->reply.tag) {
961 			*l = q->list;
962 			if(q != r) {
963 				/*
964 				 * Completed someone else.
965 				 * Trade pointers to receive buffer.
966 				 */
967 				q->reply = r->reply;
968 				q->b = r->b;
969 				r->b = nil;
970 			}
971 			q->done = 1;
972 			unlock(&m->lk);
973 			if(mntstats != 0)
974 				(*mntstats)(q->request.type,
975 					m->c, q->stime,
976 					q->reqlen + r->replen);
977 			if(q != r)
978 				wakeup(&q->r);
979 			return;
980 		}
981 		l = &q->list;
982 	}
983 	unlock(&m->lk);
984 	print("unexpected reply tag %ud; type %d\n", r->reply.tag, r->reply.type);
985 }
986 
987 /*
988  * Create a new flush request and chain the previous
989  * requests from it
990  */
991 Mntrpc*
mntflushalloc(Mntrpc * r,ulong iounit)992 mntflushalloc(Mntrpc *r, ulong iounit)
993 {
994 	Mntrpc *fr;
995 
996 	fr = mntralloc(0, iounit);
997 
998 	fr->request.type = Tflush;
999 	if(r->request.type == Tflush)
1000 		fr->request.oldtag = r->request.oldtag;
1001 	else
1002 		fr->request.oldtag = r->request.tag;
1003 	fr->flushed = r;
1004 
1005 	return fr;
1006 }
1007 
1008 /*
1009  *  Free a chain of flushes.  Remove each unanswered
1010  *  flush and the original message from the unanswered
1011  *  request queue.  Mark the original message as done
1012  *  and if it hasn't been answered set the reply to to
1013  *  Rflush.
1014  */
1015 void
mntflushfree(Mnt * m,Mntrpc * r)1016 mntflushfree(Mnt *m, Mntrpc *r)
1017 {
1018 	Mntrpc *fr;
1019 
1020 	while(r){
1021 		fr = r->flushed;
1022 		if(!r->done){
1023 			r->reply.type = Rflush;
1024 			mntqrm(m, r);
1025 		}
1026 		if(fr)
1027 			mntfree(r);
1028 		r = fr;
1029 	}
1030 }
1031 
1032 int
alloctag(void)1033 alloctag(void)
1034 {
1035 	int i, j;
1036 	ulong v;
1037 
1038 	for(i = 0; i < NMASK; i++){
1039 		v = mntalloc.tagmask[i];
1040 		if(v == ~0)
1041 			continue;
1042 		for(j = 0; j < 1<<TAGSHIFT; j++)
1043 			if((v & (1<<j)) == 0){
1044 				mntalloc.tagmask[i] |= 1<<j;
1045 				return (i<<TAGSHIFT) + j;
1046 			}
1047 	}
1048 	panic("no friggin tags left");
1049 	return NOTAG;
1050 }
1051 
1052 void
freetag(int t)1053 freetag(int t)
1054 {
1055 	mntalloc.tagmask[t>>TAGSHIFT] &= ~(1<<(t&TAGMASK));
1056 }
1057 
1058 Mntrpc*
mntralloc(Chan * c,ulong msize)1059 mntralloc(Chan *c, ulong msize)
1060 {
1061 	Mntrpc *new;
1062 
1063 	lock(&mntalloc.lk);
1064 	new = mntalloc.rpcfree;
1065 	if(new == nil){
1066 		new = malloc(sizeof(Mntrpc));
1067 		if(new == nil) {
1068 			unlock(&mntalloc.lk);
1069 			exhausted("mount rpc header");
1070 		}
1071 		/*
1072 		 * The header is split from the data buffer as
1073 		 * mountmux may swap the buffer with another header.
1074 		 */
1075 		new->rpc = mallocz(msize, 0);
1076 		if(new->rpc == nil){
1077 			free(new);
1078 			unlock(&mntalloc.lk);
1079 			exhausted("mount rpc buffer");
1080 		}
1081 		new->rpclen = msize;
1082 		new->request.tag = alloctag();
1083 	}
1084 	else {
1085 		mntalloc.rpcfree = new->list;
1086 		mntalloc.nrpcfree--;
1087 		if(new->rpclen < msize){
1088 			free(new->rpc);
1089 			new->rpc = mallocz(msize, 0);
1090 			if(new->rpc == nil){
1091 				free(new);
1092 				mntalloc.nrpcused--;
1093 				unlock(&mntalloc.lk);
1094 				exhausted("mount rpc buffer");
1095 			}
1096 			new->rpclen = msize;
1097 		}
1098 	}
1099 	mntalloc.nrpcused++;
1100 	unlock(&mntalloc.lk);
1101 	new->c = c;
1102 	new->done = 0;
1103 	new->flushed = nil;
1104 	new->b = nil;
1105 	return new;
1106 }
1107 
1108 void
mntfree(Mntrpc * r)1109 mntfree(Mntrpc *r)
1110 {
1111 	if(r->b != nil)
1112 		freeblist(r->b);
1113 	lock(&mntalloc.lk);
1114 	if(mntalloc.nrpcfree >= 10){
1115 		free(r->rpc);
1116 		free(r);
1117 		freetag(r->request.tag);
1118 	}
1119 	else{
1120 		r->list = mntalloc.rpcfree;
1121 		mntalloc.rpcfree = r;
1122 		mntalloc.nrpcfree++;
1123 	}
1124 	mntalloc.nrpcused--;
1125 	unlock(&mntalloc.lk);
1126 }
1127 
1128 void
mntqrm(Mnt * m,Mntrpc * r)1129 mntqrm(Mnt *m, Mntrpc *r)
1130 {
1131 	Mntrpc **l, *f;
1132 
1133 	lock(&m->lk);
1134 	r->done = 1;
1135 
1136 	l = &m->queue;
1137 	for(f = *l; f; f = f->list) {
1138 		if(f == r) {
1139 			*l = r->list;
1140 			break;
1141 		}
1142 		l = &f->list;
1143 	}
1144 	unlock(&m->lk);
1145 }
1146 
1147 Mnt*
mntchk(Chan * c)1148 mntchk(Chan *c)
1149 {
1150 	Mnt *m;
1151 
1152 	/* This routine is mostly vestiges of prior lives; now it's just sanity checking */
1153 
1154 	if(c->mchan == nil)
1155 		panic("mntchk 1: nil mchan c %s\n", c2name(c));
1156 
1157 	m = c->mchan->mux;
1158 
1159 	if(m == nil)
1160 		print("mntchk 2: nil mux c %s c->mchan %s \n", c2name(c), c2name(c->mchan));
1161 
1162 	/*
1163 	 * Was it closed and reused (was error(Eshutdown); now, it can't happen)
1164 	 */
1165 	if(m->id == 0 || m->id >= c->dev)
1166 		panic("mntchk 3: can't happen");
1167 
1168 	return m;
1169 }
1170 
1171 /*
1172  * Rewrite channel type and dev for in-flight data to
1173  * reflect local values.  These entries are known to be
1174  * the first two in the Dir encoding after the count.
1175  */
1176 void
mntdirfix(uchar * dirbuf,Chan * c)1177 mntdirfix(uchar *dirbuf, Chan *c)
1178 {
1179 	uint r;
1180 
1181 	r = devtab[c->type]->dc;
1182 	dirbuf += BIT16SZ;	/* skip count */
1183 	PBIT16(dirbuf, r);
1184 	dirbuf += BIT16SZ;
1185 	PBIT32(dirbuf, c->dev);
1186 }
1187 
1188 int
rpcattn(void * v)1189 rpcattn(void *v)
1190 {
1191 	Mntrpc *r;
1192 
1193 	r = v;
1194 	return r->done || r->m->rip == 0;
1195 }
1196 
1197 Dev mntdevtab = {
1198 	'M',
1199 	"mnt",
1200 
1201 	mntreset,
1202 	devinit,
1203 	devshutdown,
1204 	mntattach,
1205 	mntwalk,
1206 	mntstat,
1207 	mntopen,
1208 	mntcreate,
1209 	mntclose,
1210 	mntread,
1211 	devbread,
1212 	mntwrite,
1213 	devbwrite,
1214 	mntremove,
1215 	mntwstat,
1216 };
1217