vx32

Local 9vx git repository for patches.
git clone git://r-36.net/vx32
Log | Files | Refs

devmnt.c (21327B)


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