| // INFERNO | 
 | #include <vfs.h> | 
 | #include <kfs.h> | 
 | #include <slab.h> | 
 | #include <kmalloc.h> | 
 | #include <kref.h> | 
 | #include <string.h> | 
 | #include <stdio.h> | 
 | #include <assert.h> | 
 | #include <error.h> | 
 | #include <cpio.h> | 
 | #include <pmap.h> | 
 | #include <smp.h> | 
 | #include <ip.h> | 
 | #include <smallidpool.h> | 
 |  | 
 | /* | 
 |  * References are managed as follows: | 
 |  * The channel to the server - a network connection or pipe - has one | 
 |  * reference for every Chan open on the server.  The server channel has | 
 |  * c->mux set to the Mnt used for muxing control to that server.  Mnts | 
 |  * have no reference count; they go away when c goes away. | 
 |  * Each channel derived from the mount point has mchan set to c, | 
 |  * and increfs/decrefs mchan to manage references on the server | 
 |  * connection. | 
 |  */ | 
 |  | 
 | #define MAXRPC (IOHDRSZ+8192) | 
 | #define MAXTAG MAX_U16_POOL_SZ | 
 |  | 
 | static __inline int isxdigit(int c) | 
 | { | 
 | 	if ((c >= '0') && (c <= '9')) | 
 | 		return 1; | 
 | 	if ((c >= 'a') && (c <= 'f')) | 
 | 		return 1; | 
 | 	if ((c >= 'A') && (c <= 'F')) | 
 | 		return 1; | 
 | 	return 0; | 
 | } | 
 |  | 
 | struct mntrpc { | 
 | 	struct chan *c;				/* Channel for whom we are working */ | 
 | 	struct mntrpc *list;		/* Free/pending list */ | 
 | 	struct fcall request;		/* Outgoing file system protocol message */ | 
 | 	struct fcall reply;			/* Incoming reply */ | 
 | 	struct mnt *m;				/* Mount device during rpc */ | 
 | 	struct rendez r;			/* Place to hang out */ | 
 | 	uint8_t *rpc;				/* I/O Data buffer */ | 
 | 	unsigned int rpclen;		/* len of buffer */ | 
 | 	struct block *b;			/* reply blocks */ | 
 | 	char done;					/* Rpc completed */ | 
 | 	uint64_t stime;				/* start time for mnt statistics */ | 
 | 	uint32_t reqlen;			/* request length for mnt statistics */ | 
 | 	uint32_t replen;			/* reply length for mnt statistics */ | 
 | 	struct mntrpc *flushed;		/* message this one flushes */ | 
 | }; | 
 |  | 
 | /* Our TRUNC and remove on close differ from 9ps, so we'll need to translate. | 
 |  * I got these flags from http://man.cat-v.org/plan_9/5/open */ | 
 | #define MNT_9P_OPEN_OTRUNC		0x10 | 
 | #define MNT_9P_OPEN_ORCLOSE		0x40 | 
 |  | 
 | struct Mntalloc { | 
 | 	spinlock_t l; | 
 | 	struct mnt *list;			/* Mount devices in use */ | 
 | 	struct mnt *mntfree;		/* Free list */ | 
 | 	struct mntrpc *rpcfree; | 
 | 	int nrpcfree; | 
 | 	int nrpcused; | 
 | 	uint32_t id; | 
 | 	struct u16_pool *tags; | 
 | } mntalloc; | 
 |  | 
 | void mattach(struct mnt *, struct chan *, char *unused_char_p_t); | 
 | struct mnt *mntchk(struct chan *); | 
 | void mntdirfix(uint8_t * unused_uint8_p_t, struct chan *); | 
 | struct mntrpc *mntflushalloc(struct mntrpc *, uint32_t); | 
 | void mntflushfree(struct mnt *, struct mntrpc *); | 
 | void mntfree(struct mntrpc *); | 
 | void mntgate(struct mnt *); | 
 | void mntpntfree(struct mnt *); | 
 | void mntqrm(struct mnt *, struct mntrpc *); | 
 | struct mntrpc *mntralloc(struct chan *, uint32_t); | 
 | long mntrdwr(int unused_int, struct chan *, void *, long, int64_t); | 
 | int mntrpcread(struct mnt *, struct mntrpc *); | 
 | void mountio(struct mnt *, struct mntrpc *); | 
 | void mountmux(struct mnt *, struct mntrpc *); | 
 | void mountrpc(struct mnt *, struct mntrpc *); | 
 | int rpcattn(void *); | 
 | struct chan *mntchan(void); | 
 |  | 
 | char Esbadstat[] = "invalid directory entry received from server"; | 
 | char Enoversion[] = "version not established for mount channel"; | 
 |  | 
 | void (*mntstats) (int unused_int, struct chan *, uint64_t, uint32_t); | 
 |  | 
 | static void mntinit(void) | 
 | { | 
 | 	mntalloc.id = 1; | 
 | 	mntalloc.tags = create_u16_pool(MAXTAG); | 
 | 	(void) get_u16(mntalloc.tags);	/* don't allow 0 as a tag */ | 
 | 	//fmtinstall('F', fcallfmt); | 
 | /*	fmtinstall('D', dirfmt); */ | 
 | /*	fmtinstall('M', dirmodefmt);  */ | 
 |  | 
 | 	cinit(); | 
 | } | 
 |  | 
 | /* | 
 |  * Version is not multiplexed: message sent only once per connection. | 
 |  */ | 
 | long mntversion(struct chan *c, char *version, int msize, int returnlen) | 
 | { | 
 | 	ERRSTACK(2); | 
 | 	struct fcall f; | 
 | 	uint8_t *msg; | 
 | 	struct mnt *m; | 
 | 	char *v; | 
 | 	long k, l; | 
 | 	uint64_t oo; | 
 | 	char buf[128]; | 
 |  | 
 | 	qlock(&c->umqlock);	/* make sure no one else does this until we've established ourselves */ | 
 | 	if (waserror()) { | 
 | 		qunlock(&c->umqlock); | 
 | 		nexterror(); | 
 | 	} | 
 |  | 
 | 	/* defaults */ | 
 | 	if (msize == 0) | 
 | 		msize = MAXRPC; | 
 | 	if (msize > c->iounit && c->iounit != 0) | 
 | 		msize = c->iounit; | 
 | 	v = version; | 
 | 	if (v == NULL || v[0] == '\0') | 
 | 		v = VERSION9P; | 
 |  | 
 | 	/* validity */ | 
 | 	if (msize < 0) | 
 | 		error("bad iounit in version call"); | 
 | 	if (strncmp(v, VERSION9P, strlen(VERSION9P)) != 0) | 
 | 		error("bad 9P version specification"); | 
 |  | 
 | 	m = c->mux; | 
 |  | 
 | 	if (m != NULL) { | 
 | 		qunlock(&c->umqlock); | 
 | 		poperror(); | 
 |  | 
 | 		strncpy(buf, m->version, sizeof buf); | 
 | 		k = strlen(buf); | 
 | 		if (strncmp(buf, v, k) != 0) { | 
 | 			snprintf(buf, sizeof buf, "incompatible 9P versions %s %s", | 
 | 					 m->version, v); | 
 | 			error(buf); | 
 | 		} | 
 | 		if (returnlen > 0) { | 
 | 			if (returnlen < k) | 
 | 				error(Eshort); | 
 | 			memmove(version, buf, k); | 
 | 		} | 
 | 		return k; | 
 | 	} | 
 |  | 
 | 	f.type = Tversion; | 
 | 	f.tag = NOTAG; | 
 | 	f.msize = msize; | 
 | 	f.version = v; | 
 | 	msg = kzmalloc(8192 + IOHDRSZ, 0); | 
 | 	if (msg == NULL) | 
 | 		exhausted("version memory"); | 
 | 	if (waserror()) { | 
 | 		kfree(msg); | 
 | 		nexterror(); | 
 | 	} | 
 | 	k = convS2M(&f, msg, 8192 + IOHDRSZ); | 
 | 	if (k == 0) | 
 | 		error("bad fversion conversion on send"); | 
 |  | 
 | 	spin_lock(&c->lock); | 
 | 	oo = c->offset; | 
 | 	c->offset += k; | 
 | 	spin_unlock(&c->lock); | 
 |  | 
 | 	l = devtab[c->type].write(c, msg, k, oo); | 
 |  | 
 | 	if (l < k) { | 
 | 		spin_lock(&c->lock); | 
 | 		c->offset -= k - l; | 
 | 		spin_unlock(&c->lock); | 
 | 		error("short write in fversion"); | 
 | 	} | 
 |  | 
 | 	/* message sent; receive and decode reply */ | 
 | 	k = devtab[c->type].read(c, msg, 8192 + IOHDRSZ, c->offset); | 
 | 	if (k <= 0) | 
 | 		error("EOF receiving fversion reply"); | 
 |  | 
 | 	spin_lock(&c->lock); | 
 | 	c->offset += k; | 
 | 	spin_unlock(&c->lock); | 
 |  | 
 | 	l = convM2S(msg, k, &f); | 
 | 	if (l != k) | 
 | 		error("bad fversion conversion on reply"); | 
 | 	if (f.type != Rversion) { | 
 | 		if (f.type == Rerror) | 
 | 			error(f.ename); | 
 | 		error("unexpected reply type in fversion"); | 
 | 	} | 
 | 	if (f.msize > msize) | 
 | 		error("server tries to increase msize in fversion"); | 
 | 	if (f.msize < 256 || f.msize > 1024 * 1024) | 
 | 		error("nonsense value of msize in fversion"); | 
 | 	if (strncmp(f.version, v, strlen(f.version)) != 0) | 
 | 		error("bad 9P version returned from server"); | 
 |  | 
 | 	/* now build Mnt associated with this connection */ | 
 | 	spin_lock(&mntalloc.l); | 
 | 	m = mntalloc.mntfree; | 
 | 	if (m != 0) | 
 | 		mntalloc.mntfree = m->list; | 
 | 	else { | 
 | 		m = kzmalloc(sizeof(struct mnt), 0); | 
 | 		if (m == 0) { | 
 | 			spin_unlock(&mntalloc.l); | 
 | 			exhausted("mount devices"); | 
 | 		} | 
 | 	} | 
 | 	m->list = mntalloc.list; | 
 | 	mntalloc.list = m; | 
 | 	m->version = NULL; | 
 | 	kstrdup(&m->version, f.version); | 
 | 	m->id = mntalloc.id++; | 
 | 	m->q = qopen(10 * MAXRPC, 0, NULL, NULL); | 
 | 	m->msize = f.msize; | 
 | 	spin_unlock(&mntalloc.l); | 
 |  | 
 | 	poperror();	/* msg */ | 
 | 	kfree(msg); | 
 |  | 
 | 	spin_lock(&m->lock); | 
 | 	m->queue = 0; | 
 | 	m->rip = 0; | 
 |  | 
 | 	c->flag |= CMSG; | 
 | 	c->mux = m; | 
 | 	m->c = c; | 
 | 	spin_unlock(&m->lock); | 
 |  | 
 | 	poperror();	/* c */ | 
 | 	qunlock(&c->umqlock); | 
 | 	k = strlen(f.version); | 
 | 	if (returnlen > 0) { | 
 | 		if (returnlen < k) | 
 | 			error(Eshort); | 
 | 		memmove(version, f.version, k); | 
 | 	} | 
 |  | 
 | 	return k; | 
 | } | 
 |  | 
 | struct chan *mntauth(struct chan *c, char *spec) | 
 | { | 
 | 	ERRSTACK(2); | 
 | 	struct mnt *m; | 
 | 	struct mntrpc *r; | 
 |  | 
 | 	m = c->mux; | 
 |  | 
 | 	if (m == NULL) { | 
 | 		mntversion(c, VERSION9P, MAXRPC, 0); | 
 | 		m = c->mux; | 
 | 		if (m == NULL) | 
 | 			error(Enoversion); | 
 | 	} | 
 |  | 
 | 	c = mntchan(); | 
 | 	if (waserror()) { | 
 | 		/* Close must not be called since it will | 
 | 		 * call mnt recursively | 
 | 		 */ | 
 | 		chanfree(c); | 
 | 		nexterror(); | 
 | 	} | 
 |  | 
 | 	r = mntralloc(0, m->msize); | 
 |  | 
 | 	if (waserror()) { | 
 | 		mntfree(r); | 
 | 		nexterror(); | 
 | 	} | 
 |  | 
 | 	r->request.type = Tauth; | 
 | 	r->request.afid = c->fid; | 
 | 	r->request.uname = current->user; | 
 | 	r->request.aname = spec; | 
 | 	mountrpc(m, r); | 
 |  | 
 | 	c->qid = r->reply.aqid; | 
 | 	c->mchan = m->c; | 
 | 	chan_incref(m->c); | 
 | 	c->mqid = c->qid; | 
 | 	c->mode = ORDWR; | 
 |  | 
 | 	poperror();	/* r */ | 
 | 	mntfree(r); | 
 |  | 
 | 	poperror();	/* c */ | 
 |  | 
 | 	return c; | 
 |  | 
 | } | 
 |  | 
 | static struct chan *mntattach(char *muxattach) | 
 | { | 
 | 	ERRSTACK(2); | 
 | 	struct mnt *m; | 
 | 	struct chan *c; | 
 | 	struct mntrpc *r; | 
 | 	struct bogus { | 
 | 		struct chan *chan; | 
 | 		struct chan *authchan; | 
 | 		char *spec; | 
 | 		int flags; | 
 | 	} bogus; | 
 |  | 
 | 	bogus = *((struct bogus *)muxattach); | 
 | 	c = bogus.chan; | 
 |  | 
 | 	m = c->mux; | 
 |  | 
 | 	if (m == NULL) { | 
 | 		mntversion(c, NULL, 0, 0); | 
 | 		m = c->mux; | 
 | 		if (m == NULL) | 
 | 			error(Enoversion); | 
 | 	} | 
 |  | 
 | 	c = mntchan(); | 
 | 	if (waserror()) { | 
 | 		/* Close must not be called since it will | 
 | 		 * call mnt recursively | 
 | 		 */ | 
 | 		chanfree(c); | 
 | 		nexterror(); | 
 | 	} | 
 |  | 
 | 	r = mntralloc(0, m->msize); | 
 |  | 
 | 	if (waserror()) { | 
 | 		mntfree(r); | 
 | 		nexterror(); | 
 | 	} | 
 |  | 
 | 	r->request.type = Tattach; | 
 | 	r->request.fid = c->fid; | 
 | 	if (bogus.authchan == NULL) | 
 | 		r->request.afid = NOFID; | 
 | 	else | 
 | 		r->request.afid = bogus.authchan->fid; | 
 | 	r->request.uname = current->user; | 
 | 	r->request.aname = bogus.spec; | 
 | 	mountrpc(m, r); | 
 |  | 
 | 	c->qid = r->reply.qid; | 
 | 	c->mchan = m->c; | 
 | 	chan_incref(m->c); | 
 | 	c->mqid = c->qid; | 
 |  | 
 | 	poperror();	/* r */ | 
 | 	mntfree(r); | 
 |  | 
 | 	poperror();	/* c */ | 
 |  | 
 | 	if (bogus.flags & MCACHE) | 
 | 		c->flag |= CCACHE; | 
 | 	return c; | 
 | } | 
 |  | 
 | struct chan *mntchan(void) | 
 | { | 
 | 	struct chan *c; | 
 |  | 
 | 	c = devattach('M', 0); | 
 | 	spin_lock(&mntalloc.l); | 
 | 	c->dev = mntalloc.id++; | 
 | 	spin_unlock(&mntalloc.l); | 
 |  | 
 | 	if (c->mchan) | 
 | 		panic("mntchan non-zero %p", c->mchan); | 
 | 	return c; | 
 | } | 
 |  | 
 | static struct walkqid *mntwalk(struct chan *c, struct chan *nc, char **name, | 
 | 							   int nname) | 
 | { | 
 | 	ERRSTACK(2); | 
 | 	volatile int alloc; | 
 | 	int i; | 
 | 	struct mnt *m; | 
 | 	struct mntrpc *r; | 
 | 	struct walkqid *wq; | 
 |  | 
 | 	if (nc != NULL) | 
 | 		printd("mntwalk: nc != NULL\n"); | 
 | 	if (nname > MAXWELEM) | 
 | 		error("devmnt: too many name elements"); | 
 | 	alloc = 0; | 
 | 	wq = kzmalloc(sizeof(struct walkqid) + nname * sizeof(struct qid), | 
 | 				  KMALLOC_WAIT); | 
 | 	if (waserror()) { | 
 | 		if (alloc && wq->clone != NULL) | 
 | 			cclose(wq->clone); | 
 | 		kfree(wq); | 
 | 		poperror(); | 
 | 		return NULL; | 
 | 	} | 
 |  | 
 | 	alloc = 0; | 
 | 	m = mntchk(c); | 
 | 	r = mntralloc(c, m->msize); | 
 | 	if (nc == NULL) { | 
 | 		nc = devclone(c); | 
 | 		/* Until the other side accepts this fid, we can't mntclose it. | 
 | 		 * Therefore set type to -1 for now.  inferno was setting this to 0, | 
 | 		 * assuming it was devroot.  lining up with chanrelease and newchan */ | 
 | 		nc->type = -1; | 
 | 		alloc = 1; | 
 | 	} | 
 | 	wq->clone = nc; | 
 |  | 
 | 	if (waserror()) { | 
 | 		mntfree(r); | 
 | 		nexterror(); | 
 | 	} | 
 | 	r->request.type = Twalk; | 
 | 	r->request.fid = c->fid; | 
 | 	r->request.newfid = nc->fid; | 
 | 	r->request.nwname = nname; | 
 | 	memmove(r->request.wname, name, nname * sizeof(char *)); | 
 |  | 
 | 	mountrpc(m, r); | 
 |  | 
 | 	if (r->reply.nwqid > nname) | 
 | 		error("too many QIDs returned by walk"); | 
 | 	if (r->reply.nwqid < nname) { | 
 | 		if (alloc) | 
 | 			cclose(nc); | 
 | 		wq->clone = NULL; | 
 | 		if (r->reply.nwqid == 0) { | 
 | 			kfree(wq); | 
 | 			wq = NULL; | 
 | 			goto Return; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	/* move new fid onto mnt device and update its qid */ | 
 | 	if (wq->clone != NULL) { | 
 | 		if (wq->clone != c) { | 
 | 			wq->clone->type = c->type; | 
 | 			wq->clone->mchan = c->mchan; | 
 | 			chan_incref(c->mchan); | 
 | 		} | 
 | 		if (r->reply.nwqid > 0) | 
 | 			wq->clone->qid = r->reply.wqid[r->reply.nwqid - 1]; | 
 | 	} | 
 | 	wq->nqid = r->reply.nwqid; | 
 | 	for (i = 0; i < wq->nqid; i++) | 
 | 		wq->qid[i] = r->reply.wqid[i]; | 
 |  | 
 | Return: | 
 | 	poperror(); | 
 | 	mntfree(r); | 
 | 	poperror(); | 
 | 	return wq; | 
 | } | 
 |  | 
 | static int mntstat(struct chan *c, uint8_t * dp, int n) | 
 | { | 
 | 	ERRSTACK(1); | 
 | 	struct mnt *m; | 
 | 	struct mntrpc *r; | 
 |  | 
 | 	if (n < BIT16SZ) | 
 | 		error(Eshortstat); | 
 | 	m = mntchk(c); | 
 | 	r = mntralloc(c, m->msize); | 
 | 	if (waserror()) { | 
 | 		mntfree(r); | 
 | 		nexterror(); | 
 | 	} | 
 | 	r->request.type = Tstat; | 
 | 	r->request.fid = c->fid; | 
 | 	mountrpc(m, r); | 
 |  | 
 | 	if (r->reply.nstat > n) { | 
 | 		/* doesn't fit; just patch the count and return */ | 
 | 		PBIT16((uint8_t *) dp, r->reply.nstat); | 
 | 		n = BIT16SZ; | 
 | 	} else { | 
 | 		n = r->reply.nstat; | 
 | 		memmove(dp, r->reply.stat, n); | 
 | 		validstat(dp, n, 0); | 
 | 		mntdirfix(dp, c); | 
 | 	} | 
 | 	poperror(); | 
 | 	mntfree(r); | 
 | 	return n; | 
 | } | 
 |  | 
 | static struct chan *mntopencreate(int type, struct chan *c, char *name, | 
 | 								  int omode, uint32_t perm) | 
 | { | 
 | 	ERRSTACK(1); | 
 | 	struct mnt *m; | 
 | 	struct mntrpc *r; | 
 |  | 
 | 	m = mntchk(c); | 
 | 	r = mntralloc(c, m->msize); | 
 | 	if (waserror()) { | 
 | 		mntfree(r); | 
 | 		nexterror(); | 
 | 	} | 
 | 	r->request.type = type; | 
 | 	r->request.fid = c->fid; | 
 | 	r->request.mode = omode & O_ACCMODE;	/* not using openmode, want O_EXEC */ | 
 | 	if (omode & O_TRUNC) | 
 | 		r->request.mode |= MNT_9P_OPEN_OTRUNC; | 
 | 	if (omode & O_REMCLO) | 
 | 		r->request.mode |= MNT_9P_OPEN_ORCLOSE; | 
 | 	if (type == Tcreate) { | 
 | 		r->request.perm = perm; | 
 | 		r->request.name = name; | 
 | 	} | 
 | 	mountrpc(m, r); | 
 |  | 
 | 	c->qid = r->reply.qid; | 
 | 	c->offset = 0; | 
 | 	c->mode = openmode(omode); | 
 | 	c->iounit = r->reply.iounit; | 
 | 	if (c->iounit == 0 || c->iounit > m->msize - IOHDRSZ) | 
 | 		c->iounit = m->msize - IOHDRSZ; | 
 | 	c->flag |= COPEN; | 
 | 	poperror(); | 
 | 	mntfree(r); | 
 |  | 
 | 	if (c->flag & CCACHE) | 
 | 		copen(c); | 
 |  | 
 | 	return c; | 
 | } | 
 |  | 
 | static struct chan *mntopen(struct chan *c, int omode) | 
 | { | 
 | 	return mntopencreate(Topen, c, NULL, omode, 0); | 
 | } | 
 |  | 
 | static void mntcreate(struct chan *c, char *name, int omode, uint32_t perm) | 
 | { | 
 | 	mntopencreate(Tcreate, c, name, omode, perm); | 
 | } | 
 |  | 
 | static void mntclunk(struct chan *c, int t) | 
 | { | 
 | 	ERRSTACK(1); | 
 | 	struct mnt *m; | 
 | 	struct mntrpc *r; | 
 |  | 
 | 	m = mntchk(c); | 
 | 	r = mntralloc(c, m->msize); | 
 | 	if (waserror()) { | 
 | 		mntfree(r); | 
 | 		nexterror(); | 
 | 	} | 
 |  | 
 | 	r->request.type = t; | 
 | 	r->request.fid = c->fid; | 
 | 	mountrpc(m, r); | 
 | 	mntfree(r); | 
 | 	poperror(); | 
 | } | 
 |  | 
 | void muxclose(struct mnt *m) | 
 | { | 
 | 	struct mntrpc *q, *r; | 
 |  | 
 | 	for (q = m->queue; q; q = r) { | 
 | 		r = q->list; | 
 | 		mntfree(q); | 
 | 	} | 
 | 	m->id = 0; | 
 | 	kfree(m->version); | 
 | 	m->version = NULL; | 
 | 	mntpntfree(m); | 
 | } | 
 |  | 
 | void mntpntfree(struct mnt *m) | 
 | { | 
 | 	struct mnt *f, **l; | 
 | 	struct queue *q; | 
 |  | 
 | 	spin_lock(&mntalloc.l); | 
 | 	l = &mntalloc.list; | 
 | 	for (f = *l; f; f = f->list) { | 
 | 		if (f == m) { | 
 | 			*l = m->list; | 
 | 			break; | 
 | 		} | 
 | 		l = &f->list; | 
 | 	} | 
 | 	m->list = mntalloc.mntfree; | 
 | 	mntalloc.mntfree = m; | 
 | 	q = m->q; | 
 | 	spin_unlock(&mntalloc.l); | 
 |  | 
 | 	qfree(q); | 
 | } | 
 |  | 
 | static void mntclose(struct chan *c) | 
 | { | 
 | 	mntclunk(c, Tclunk); | 
 | } | 
 |  | 
 | static void mntremove(struct chan *c) | 
 | { | 
 | 	mntclunk(c, Tremove); | 
 | } | 
 |  | 
 | static int mntwstat(struct chan *c, uint8_t * dp, int n) | 
 | { | 
 | 	ERRSTACK(1); | 
 | 	struct mnt *m; | 
 | 	struct mntrpc *r; | 
 |  | 
 | 	m = mntchk(c); | 
 | 	r = mntralloc(c, m->msize); | 
 | 	if (waserror()) { | 
 | 		mntfree(r); | 
 | 		nexterror(); | 
 | 	} | 
 | 	r->request.type = Twstat; | 
 | 	r->request.fid = c->fid; | 
 | 	r->request.nstat = n; | 
 | 	r->request.stat = dp; | 
 | 	mountrpc(m, r); | 
 | 	poperror(); | 
 | 	mntfree(r); | 
 | 	return n; | 
 | } | 
 |  | 
 | /* the servers should either return units of whole directory entries | 
 |  * OR support seeking to an arbitrary place. One or other. | 
 |  * Both are fine, but at least one is a minimum. | 
 |  * If the return a partial result, but more than one result, | 
 |  * we'll return a shorter read and the next offset will be aligned | 
 |  */ | 
 | static long mntread(struct chan *c, void *buf, long n, int64_t off) | 
 | { | 
 | 	uint8_t *p, *e; | 
 | 	int nc, cache, isdir, dirlen; | 
 | 	int numdirent = 0; | 
 |  | 
 | 	isdir = 0; | 
 | 	cache = c->flag & CCACHE; | 
 | 	if (c->qid.type & QTDIR) { | 
 | 		cache = 0; | 
 | 		isdir = 1; | 
 | 	} | 
 |  | 
 | 	p = buf; | 
 | 	if (cache) { | 
 | 		nc = cread(c, buf, n, off); | 
 | 		if (nc > 0) { | 
 | 			n -= nc; | 
 | 			if (n == 0) | 
 | 				return nc; | 
 | 			p += nc; | 
 | 			off += nc; | 
 | 		} | 
 | 		n = mntrdwr(Tread, c, p, n, off); | 
 | 		cupdate(c, p, n, off); | 
 | 		return n + nc; | 
 | 	} | 
 |  | 
 | 	n = mntrdwr(Tread, c, buf, n, off); | 
 |  | 
 | 	if (isdir) { | 
 | 		for (e = &p[n]; p + BIT16SZ < e; p += dirlen) { | 
 | 			dirlen = BIT16SZ + GBIT16(p); | 
 | 			if (p + dirlen > e){ | 
 | 				break; | 
 | 			} | 
 | 			validstat(p, dirlen, 0); | 
 | 			mntdirfix(p, c); | 
 | 			numdirent += dirlen; | 
 | 		} | 
 | 		if (p != e) { | 
 | 			//error(Esbadstat); | 
 | 			/* not really. Maybe the server supports | 
 | 			 * arbitrary seek like go9p now does. | 
 | 			 */ | 
 | 			n = numdirent; | 
 | 		} | 
 | 	} | 
 | 	return n; | 
 | } | 
 |  | 
 | static long mntwrite(struct chan *c, void *buf, long n, int64_t off) | 
 | { | 
 | 	return mntrdwr(Twrite, c, buf, n, off); | 
 | } | 
 |  | 
 | long mntrdwr(int type, struct chan *c, void *buf, long n, int64_t off) | 
 | { | 
 | 	ERRSTACK(1); | 
 | 	struct mnt *m; | 
 | 	struct mntrpc *r;			/* TO DO: volatile struct { Mntrpc *r; } r; */ | 
 | 	char *uba; | 
 | 	int cache; | 
 | 	uint32_t cnt, nr, nreq; | 
 |  | 
 | 	m = mntchk(c); | 
 | 	uba = buf; | 
 | 	cnt = 0; | 
 | 	cache = c->flag & CCACHE; | 
 | 	if (c->qid.type & QTDIR) | 
 | 		cache = 0; | 
 | 	for (;;) { | 
 | 		r = mntralloc(c, m->msize); | 
 | 		if (waserror()) { | 
 | 			mntfree(r); | 
 | 			nexterror(); | 
 | 		} | 
 | 		r->request.type = type; | 
 | 		r->request.fid = c->fid; | 
 | 		r->request.offset = off; | 
 | 		r->request.data = uba; | 
 | 		nr = n; | 
 | 		if (nr > m->msize - IOHDRSZ) | 
 | 			nr = m->msize - IOHDRSZ; | 
 | 		r->request.count = nr; | 
 | 		mountrpc(m, r); | 
 | 		nreq = r->request.count; | 
 | 		nr = r->reply.count; | 
 | 		if (nr > nreq) | 
 | 			nr = nreq; | 
 |  | 
 | 		if (type == Tread) | 
 | 			r->b = bl2mem((uint8_t *) uba, r->b, nr); | 
 | 		else if (cache) | 
 | 			cwrite(c, (uint8_t *) uba, nr, off); | 
 |  | 
 | 		poperror(); | 
 | 		mntfree(r); | 
 | 		off += nr; | 
 | 		uba += nr; | 
 | 		cnt += nr; | 
 | 		n -= nr; | 
 | 		if (nr != nreq || n == 0 /*|| current->killed */ ) | 
 | 			break; | 
 | 	} | 
 | 	return cnt; | 
 | } | 
 |  | 
 | void mountrpc(struct mnt *m, struct mntrpc *r) | 
 | { | 
 | 	char *sn, *cn; | 
 | 	int t; | 
 | 	char *e; | 
 |  | 
 | 	r->reply.tag = 0; | 
 | 	r->reply.type = Tmax;	/* can't ever be a valid message type */ | 
 |  | 
 | 	mountio(m, r); | 
 |  | 
 | 	t = r->reply.type; | 
 | 	switch (t) { | 
 | 		case Rerror: | 
 | 			/* in Akaros mode, first four characters | 
 | 			 * are errno. | 
 | 			 */ | 
 | 			e = r->reply.ename; | 
 | 			/* If it is in the format "XXXX <at least one char>" */ | 
 | 			if ((strlen(e) > 5) && isxdigit(e[0]) && | 
 | 				isxdigit(e[1]) && | 
 | 				isxdigit(e[2]) && | 
 | 				isxdigit(e[3])) { | 
 | 				int errno = strtoul(e, NULL, 16); | 
 | 				set_errno(errno); | 
 | 				error(&r->reply.ename[5]); | 
 | 			} else | 
 | 				error(r->reply.ename); | 
 | 		case Rflush: | 
 | 			error(Eintr); | 
 | 		default: | 
 | 			if (t == r->request.type + 1) | 
 | 				break; | 
 | 			sn = "?"; | 
 | 			if (m->c->name != NULL) | 
 | 				sn = m->c->name->s; | 
 | 			cn = "?"; | 
 | 			if (r->c != NULL && r->c->name != NULL) | 
 | 				cn = r->c->name->s; | 
 | 			printd | 
 | 				("mnt: proc %s %lu: mismatch from %s %s rep 0x%p tag %d fid %d T%d R%d rp %d\n", | 
 | 				 "current->text", "current->pid", sn, cn, r, r->request.tag, | 
 | 				 r->request.fid, r->request.type, r->reply.type, r->reply.tag); | 
 | 			error(Emountrpc); | 
 | 	} | 
 | } | 
 |  | 
 | void mountio(struct mnt *m, struct mntrpc *r) | 
 | { | 
 | 	ERRSTACK(1); | 
 | 	int n; | 
 |  | 
 | 	while (waserror()) { | 
 | 		if (m->rip == current) | 
 | 			mntgate(m); | 
 | 		/* Syscall aborts are like Plan 9 Eintr.  For those, we need to change | 
 | 		 * the old request to a flsh (mntflushalloc) and try again.  We'll | 
 | 		 * always try to flush, and you can't get out until the flush either | 
 | 		 * succeeds or errors out with a non-abort/Eintr error. */ | 
 | 		if (strcmp(current_errstr(), "syscall aborted") && | 
 | 		    strcmp(current_errstr(), Eintr)) { | 
 | 			/* all other errors (not abort or Eintr) */ | 
 | 			mntflushfree(m, r); | 
 | 			nexterror(); | 
 | 		} | 
 | 		r = mntflushalloc(r, m->msize); | 
 | 		/* need one for every waserror call (so this plus one outside) */ | 
 | 		poperror(); | 
 | 	} | 
 |  | 
 | 	spin_lock(&m->lock); | 
 | 	r->m = m; | 
 | 	r->list = m->queue; | 
 | 	m->queue = r; | 
 | 	spin_unlock(&m->lock); | 
 |  | 
 | 	/* Transmit a file system rpc */ | 
 | 	if (m->msize == 0) | 
 | 		panic("msize"); | 
 | 	n = convS2M(&r->request, r->rpc, m->msize); | 
 | 	if (n < 0) | 
 | 		panic("bad message type in mountio"); | 
 | 	if (devtab[m->c->type].write(m->c, r->rpc, n, 0) != n) | 
 | 		error(Emountrpc); | 
 | /*	r->stime = fastticks(NULL); */ | 
 | 	r->reqlen = n; | 
 |  | 
 | 	/* Gate readers onto the mount point one at a time */ | 
 | 	for (;;) { | 
 | 		spin_lock(&m->lock); | 
 | 		if (m->rip == 0) | 
 | 			break; | 
 | 		spin_unlock(&m->lock); | 
 | 		rendez_sleep(&r->r, rpcattn, r); | 
 | 		if (r->done) { | 
 | 			poperror(); | 
 | 			mntflushfree(m, r); | 
 | 			return; | 
 | 		} | 
 | 	} | 
 | 	m->rip = current; | 
 | 	spin_unlock(&m->lock); | 
 | 	while (r->done == 0) { | 
 | 		if (mntrpcread(m, r) < 0) | 
 | 			error(Emountrpc); | 
 | 		mountmux(m, r); | 
 | 	} | 
 | 	mntgate(m); | 
 | 	poperror(); | 
 | 	mntflushfree(m, r); | 
 | } | 
 |  | 
 | static int doread(struct mnt *m, int len) | 
 | { | 
 | 	struct block *b; | 
 |  | 
 | 	while (qlen(m->q) < len) { | 
 | 		b = devtab[m->c->type].bread(m->c, m->msize, 0); | 
 | 		if (b == NULL) | 
 | 			return -1; | 
 | 		if (blocklen(b) == 0) { | 
 | 			freeblist(b); | 
 | 			return -1; | 
 | 		} | 
 | 		qaddlist(m->q, b); | 
 | 	} | 
 | 	return 0; | 
 | } | 
 |  | 
 | int mntrpcread(struct mnt *m, struct mntrpc *r) | 
 | { | 
 | 	int i, t, len, hlen; | 
 | 	struct block *b, **l, *nb; | 
 |  | 
 | 	r->reply.type = 0; | 
 | 	r->reply.tag = 0; | 
 |  | 
 | 	/* read at least length, type, and tag and pullup to a single block */ | 
 | 	if (doread(m, BIT32SZ + BIT8SZ + BIT16SZ) < 0) | 
 | 		return -1; | 
 | 	nb = pullupqueue(m->q, BIT32SZ + BIT8SZ + BIT16SZ); | 
 |  | 
 | 	/* read in the rest of the message, avoid ridiculous (for now) message sizes */ | 
 | 	len = GBIT32(nb->rp); | 
 | 	if (len > m->msize) { | 
 | 		qdiscard(m->q, qlen(m->q)); | 
 | 		return -1; | 
 | 	} | 
 | 	if (doread(m, len) < 0) | 
 | 		return -1; | 
 |  | 
 | 	/* pullup the header (i.e. everything except data) */ | 
 | 	t = nb->rp[BIT32SZ]; | 
 | 	switch (t) { | 
 | 		case Rread: | 
 | 			hlen = BIT32SZ + BIT8SZ + BIT16SZ + BIT32SZ; | 
 | 			break; | 
 | 		default: | 
 | 			hlen = len; | 
 | 			break; | 
 | 	} | 
 | 	nb = pullupqueue(m->q, hlen); | 
 |  | 
 | 	if (convM2S(nb->rp, len, &r->reply) <= 0) { | 
 | 		/* bad message, dump it */ | 
 | 		printd("mntrpcread: convM2S failed\n"); | 
 | 		qdiscard(m->q, len); | 
 | 		return -1; | 
 | 	} | 
 |  | 
 | 	/* hang the data off of the fcall struct */ | 
 | 	l = &r->b; | 
 | 	*l = NULL; | 
 | 	do { | 
 | 		b = qremove(m->q); | 
 | 		/* TODO: have better block helpers for this and the memmove below */ | 
 | 		b = linearizeblock(b); | 
 | 		if (hlen > 0) { | 
 | 			b->rp += hlen; | 
 | 			len -= hlen; | 
 | 			hlen = 0; | 
 | 		} | 
 | 		i = BLEN(b); | 
 | 		if (i <= len) { | 
 | 			len -= i; | 
 | 			*l = b; | 
 | 			l = &(b->next); | 
 | 		} else { | 
 | 			/* split block and put unused bit back */ | 
 | 			nb = allocb(i - len); | 
 | 			memmove(nb->wp, b->rp + len, i - len); | 
 | 			b->wp = b->rp + len; | 
 | 			nb->wp += i - len; | 
 | 			qputback(m->q, nb); | 
 | 			*l = b; | 
 | 			return 0; | 
 | 		} | 
 | 	} while (len > 0); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | void mntgate(struct mnt *m) | 
 | { | 
 | 	struct mntrpc *q; | 
 |  | 
 | 	spin_lock(&m->lock); | 
 | 	m->rip = 0; | 
 | 	for (q = m->queue; q; q = q->list) { | 
 | 		if (q->done == 0) | 
 | 			if (rendez_wakeup(&q->r)) | 
 | 				break; | 
 | 	} | 
 | 	spin_unlock(&m->lock); | 
 | } | 
 |  | 
 | void mountmux(struct mnt *m, struct mntrpc *r) | 
 | { | 
 | 	struct mntrpc **l, *q; | 
 |  | 
 | 	spin_lock(&m->lock); | 
 | 	l = &m->queue; | 
 | 	for (q = *l; q; q = q->list) { | 
 | 		/* look for a reply to a message */ | 
 | 		if (q->request.tag == r->reply.tag) { | 
 | 			*l = q->list; | 
 | 			if (q != r) { | 
 | 				/* | 
 | 				 * Completed someone else. | 
 | 				 * Trade pointers to receive buffer. | 
 | 				 */ | 
 | 				q->reply = r->reply; | 
 | 				q->b = r->b; | 
 | 				r->b = NULL; | 
 | 			} | 
 | 			q->done = 1; | 
 | 			spin_unlock(&m->lock); | 
 | 			if (mntstats != NULL) | 
 | 				(*mntstats) (q->request.type, | 
 | 							 m->c, q->stime, q->reqlen + r->replen); | 
 | 			if (q != r) | 
 | 				rendez_wakeup(&q->r); | 
 | 			return; | 
 | 		} | 
 | 		l = &q->list; | 
 | 	} | 
 | 	spin_unlock(&m->lock); | 
 | 	if (r->reply.type == Rerror) { | 
 | 		printd("unexpected reply tag %u; type %d (error %q)\n", r->reply.tag, | 
 | 			   r->reply.type, r->reply.ename); | 
 | 	} else { | 
 | 		printd("unexpected reply tag %u; type %d\n", r->reply.tag, | 
 | 			   r->reply.type); | 
 | 	} | 
 | } | 
 |  | 
 | /* | 
 |  * Create a new flush request and chain the previous | 
 |  * requests from it | 
 |  */ | 
 | struct mntrpc *mntflushalloc(struct mntrpc *r, uint32_t iounit) | 
 | { | 
 | 	struct mntrpc *fr; | 
 |  | 
 | 	fr = mntralloc(0, iounit); | 
 |  | 
 | 	fr->request.type = Tflush; | 
 | 	if (r->request.type == Tflush) | 
 | 		fr->request.oldtag = r->request.oldtag; | 
 | 	else | 
 | 		fr->request.oldtag = r->request.tag; | 
 | 	fr->flushed = r; | 
 |  | 
 | 	return fr; | 
 | } | 
 |  | 
 | /* | 
 |  *  Free a chain of flushes.  Remove each unanswered | 
 |  *  flush and the original message from the unanswered | 
 |  *  request queue.  Mark the original message as done | 
 |  *  and if it hasn't been answered set the reply to to | 
 |  *  Rflush. | 
 |  */ | 
 | void mntflushfree(struct mnt *m, struct mntrpc *r) | 
 | { | 
 | 	struct mntrpc *fr; | 
 |  | 
 | 	while (r) { | 
 | 		fr = r->flushed; | 
 | 		if (!r->done) { | 
 | 			r->reply.type = Rflush; | 
 | 			mntqrm(m, r); | 
 | 		} | 
 | 		if (fr) | 
 | 			mntfree(r); | 
 | 		r = fr; | 
 | 	} | 
 | } | 
 |  | 
 | static int alloctag(void) | 
 | { | 
 | 	return get_u16(mntalloc.tags); | 
 | } | 
 |  | 
 | static void freetag(int t) | 
 | { | 
 | 	put_u16(mntalloc.tags, t); | 
 | } | 
 |  | 
 | struct mntrpc *mntralloc(struct chan *c, uint32_t msize) | 
 | { | 
 | 	struct mntrpc *new; | 
 |  | 
 | 	spin_lock(&mntalloc.l); | 
 | 	new = mntalloc.rpcfree; | 
 | 	if (new == NULL) { | 
 | 		new = kzmalloc(sizeof(struct mntrpc), 0); | 
 | 		if (new == NULL) { | 
 | 			spin_unlock(&mntalloc.l); | 
 | 			exhausted("mount rpc header"); | 
 | 		} | 
 | 		rendez_init(&new->r); | 
 | 		/* | 
 | 		 * The header is split from the data buffer as | 
 | 		 * mountmux may swap the buffer with another header. | 
 | 		 */ | 
 | 		new->rpc = kzmalloc(msize, KMALLOC_WAIT); | 
 | 		if (new->rpc == NULL) { | 
 | 			kfree(new); | 
 | 			spin_unlock(&mntalloc.l); | 
 | 			exhausted("mount rpc buffer"); | 
 | 		} | 
 | 		new->rpclen = msize; | 
 | 		new->request.tag = alloctag(); | 
 | 		if (new->request.tag == NOTAG) { | 
 | 			kfree(new); | 
 | 			spin_unlock(&mntalloc.l); | 
 | 			exhausted("rpc tags"); | 
 | 		} | 
 | 	} else { | 
 | 		mntalloc.rpcfree = new->list; | 
 | 		mntalloc.nrpcfree--; | 
 | 		if (new->rpclen < msize) { | 
 | 			kfree(new->rpc); | 
 | 			new->rpc = kzmalloc(msize, KMALLOC_WAIT); | 
 | 			if (new->rpc == NULL) { | 
 | 				kfree(new); | 
 | 				mntalloc.nrpcused--; | 
 | 				spin_unlock(&mntalloc.l); | 
 | 				exhausted("mount rpc buffer"); | 
 | 			} | 
 | 			new->rpclen = msize; | 
 | 		} | 
 | 	} | 
 | 	mntalloc.nrpcused++; | 
 | 	spin_unlock(&mntalloc.l); | 
 | 	new->c = c; | 
 | 	new->done = 0; | 
 | 	new->flushed = NULL; | 
 | 	new->b = NULL; | 
 | 	return new; | 
 | } | 
 |  | 
 | void mntfree(struct mntrpc *r) | 
 | { | 
 | 	if (r->b != NULL) | 
 | 		freeblist(r->b); | 
 | 	spin_lock(&mntalloc.l); | 
 | 	if (mntalloc.nrpcfree >= 10) { | 
 | 		kfree(r->rpc); | 
 | 		freetag(r->request.tag); | 
 | 		kfree(r); | 
 | 	} else { | 
 | 		r->list = mntalloc.rpcfree; | 
 | 		mntalloc.rpcfree = r; | 
 | 		mntalloc.nrpcfree++; | 
 | 	} | 
 | 	mntalloc.nrpcused--; | 
 | 	spin_unlock(&mntalloc.l); | 
 | } | 
 |  | 
 | void mntqrm(struct mnt *m, struct mntrpc *r) | 
 | { | 
 | 	struct mntrpc **l, *f; | 
 |  | 
 | 	spin_lock(&m->lock); | 
 | 	r->done = 1; | 
 |  | 
 | 	l = &m->queue; | 
 | 	for (f = *l; f; f = f->list) { | 
 | 		if (f == r) { | 
 | 			*l = r->list; | 
 | 			break; | 
 | 		} | 
 | 		l = &f->list; | 
 | 	} | 
 | 	spin_unlock(&m->lock); | 
 | } | 
 |  | 
 | struct mnt *mntchk(struct chan *c) | 
 | { | 
 | 	struct mnt *m; | 
 |  | 
 | 	/* This routine is mostly vestiges of prior lives; now it's just sanity checking */ | 
 |  | 
 | 	if (c->mchan == NULL) | 
 | 		panic("mntchk 1: NULL mchan c %s\n", /*c2name(c) */ "channame?"); | 
 |  | 
 | 	m = c->mchan->mux; | 
 |  | 
 | 	if (m == NULL) | 
 | 		printd("mntchk 2: NULL mux c %s c->mchan %s \n", c2name(c), | 
 | 			   c2name(c->mchan)); | 
 |  | 
 | 	/* | 
 | 	 * Was it closed and reused (was error(Eshutdown); now, it can't happen) | 
 | 	 */ | 
 | 	if (m->id == 0 || m->id >= c->dev) | 
 | 		panic("mntchk 3: can't happen"); | 
 |  | 
 | 	return m; | 
 | } | 
 |  | 
 | /* | 
 |  * Rewrite channel type and dev for in-flight data to | 
 |  * reflect local values.  These entries are known to be | 
 |  * the first two in the Dir encoding after the count. | 
 |  */ | 
 | void mntdirfix(uint8_t * dirbuf, struct chan *c) | 
 | { | 
 | 	unsigned int r; | 
 |  | 
 | 	r = devtab[c->type].dc; | 
 | 	dirbuf += BIT16SZ;	/* skip count */ | 
 | 	PBIT16(dirbuf, r); | 
 | 	dirbuf += BIT16SZ; | 
 | 	PBIT32(dirbuf, c->dev); | 
 | } | 
 |  | 
 | int rpcattn(void *v) | 
 | { | 
 | 	struct mntrpc *r; | 
 |  | 
 | 	r = v; | 
 | 	return r->done || r->m->rip == 0; | 
 | } | 
 |  | 
 | struct dev mntdevtab __devtab = { | 
 | 	'M', | 
 | 	"mnt", | 
 |  | 
 | 	devreset, | 
 | 	mntinit, | 
 | 	devshutdown, | 
 | 	mntattach, | 
 | 	mntwalk, | 
 | 	mntstat, | 
 | 	mntopen, | 
 | 	mntcreate, | 
 | 	mntclose, | 
 | 	mntread, | 
 | 	devbread, | 
 | 	mntwrite, | 
 | 	devbwrite, | 
 | 	mntremove, | 
 | 	mntwstat, | 
 | 	devpower, | 
 | 	devchaninfo, | 
 | }; |