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