|  | /* Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved. | 
|  | * Portions Copyright © 1997-1999 Vita Nuova Limited | 
|  | * Portions Copyright © 2000-2007 Vita Nuova Holdings Limited | 
|  | *                                (www.vitanuova.com) | 
|  | * Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others | 
|  | * | 
|  | * Modified for the Akaros operating system: | 
|  | * Copyright (c) 2013-2014 The Regents of the University of California | 
|  | * Copyright (c) 2013-2015 Google Inc. | 
|  | * | 
|  | * Permission is hereby granted, free of charge, to any person obtaining a copy | 
|  | * of this software and associated documentation files (the "Software"), to deal | 
|  | * in the Software without restriction, including without limitation the rights | 
|  | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | 
|  | * copies of the Software, and to permit persons to whom the Software is | 
|  | * furnished to do so, subject to the following conditions: | 
|  | * | 
|  | * The above copyright notice and this permission notice shall be included in | 
|  | * all copies or substantial portions of the Software. | 
|  | * | 
|  | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
|  | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
|  | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE | 
|  | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | 
|  | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | 
|  | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | 
|  | * SOFTWARE. */ | 
|  |  | 
|  | #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 <net/ip.h> | 
|  | #include <smallidpool.h> | 
|  |  | 
|  | struct dev mntdevtab; | 
|  |  | 
|  | static char *devname(void) | 
|  | { | 
|  | return mntdevtab.name; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * 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); | 
|  | size_t mntrdwr(int unused_int, struct chan *, void *, size_t, off64_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); | 
|  |  | 
|  | 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);  */ | 
|  | } | 
|  |  | 
|  | /* | 
|  | * 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]; | 
|  |  | 
|  | /* make sure no one else does this until we've established ourselves */ | 
|  | qlock(&c->umqlock); | 
|  | 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(EFAIL, "bad iounit in version call"); | 
|  | if (strncmp(v, VERSION9P, strlen(VERSION9P)) != 0) | 
|  | error(EFAIL, "bad 9P version specification"); | 
|  |  | 
|  | m = c->mux; | 
|  |  | 
|  | if (m != NULL) { | 
|  | qunlock(&c->umqlock); | 
|  | poperror(); | 
|  |  | 
|  | strlcpy(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(EFAIL, buf); | 
|  | } | 
|  | if (returnlen > 0) { | 
|  | if (returnlen < k) | 
|  | error(ENAMETOOLONG, ERROR_FIXME); | 
|  | 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(EFAIL, "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(EFAIL, "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(EFAIL, "EOF receiving fversion reply"); | 
|  |  | 
|  | spin_lock(&c->lock); | 
|  | c->offset += k; | 
|  | spin_unlock(&c->lock); | 
|  |  | 
|  | l = convM2S(msg, k, &f); | 
|  | if (l != k) | 
|  | error(EFAIL, "bad fversion conversion on reply"); | 
|  | if (f.type != Rversion) { | 
|  | if (f.type == Rerror) | 
|  | error(EFAIL, f.ename); | 
|  | error(EFAIL, "unexpected reply type in fversion"); | 
|  | } | 
|  | if (f.msize > msize) | 
|  | error(EFAIL, "server tries to increase msize in fversion"); | 
|  | if (f.msize < 256 || f.msize > 1024 * 1024) | 
|  | error(EFAIL, "nonsense value of msize in fversion"); | 
|  | if (strncmp(f.version, v, strlen(f.version)) != 0) | 
|  | error(EFAIL, "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(ENAMETOOLONG, ERROR_FIXME); | 
|  | 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(EINVAL, ERROR_FIXME); | 
|  | } | 
|  |  | 
|  | 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; | 
|  | /* This assumes we're called from a syscall, which should always be | 
|  | * true. */ | 
|  | if (!current_kthread->sysc) | 
|  | warn("Kthread %s didn't have a syscall, current is %s", | 
|  | current_kthread->name, current ? current->progname : NULL); | 
|  | r->request.uname = current->user.name; | 
|  | 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 = O_RDWR; | 
|  |  | 
|  | 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 mntparam *params = (struct mntparam *)muxattach; | 
|  |  | 
|  | c = params->chan; | 
|  |  | 
|  | m = c->mux; | 
|  |  | 
|  | if (m == NULL) { | 
|  | mntversion(c, NULL, 0, 0); | 
|  | m = c->mux; | 
|  | if (m == NULL) | 
|  | error(EINVAL, ERROR_FIXME); | 
|  | } | 
|  |  | 
|  | 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 (params->authchan == NULL) | 
|  | r->request.afid = NOFID; | 
|  | else | 
|  | r->request.afid = params->authchan->fid; | 
|  | /* This assumes we're called from a syscall, which should always be | 
|  | * true. */ | 
|  | if (!current_kthread->sysc) | 
|  | warn("Kthread %s didn't have a syscall, current is %s", | 
|  | current_kthread->name, current ? current->progname : NULL); | 
|  | r->request.uname = current->user.name; | 
|  | r->request.aname = params->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 */ | 
|  | return c; | 
|  | } | 
|  |  | 
|  | struct chan *mntchan(void) | 
|  | { | 
|  | struct chan *c; | 
|  |  | 
|  | c = devattach(devname(), 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, | 
|  | unsigned 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(EFAIL, "devmnt: too many name elements"); | 
|  | alloc = 0; | 
|  | wq = kzmalloc(sizeof(struct walkqid) + nname * sizeof(struct qid), | 
|  | MEM_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(EFAIL, "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 size_t mntstat(struct chan *c, uint8_t *dp, size_t n) | 
|  | { | 
|  | ERRSTACK(1); | 
|  | struct mnt *m; | 
|  | struct mntrpc *r; | 
|  |  | 
|  | if (n < BIT16SZ) | 
|  | error(EINVAL, ERROR_FIXME); | 
|  | 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_to_9p_accmode(omode); | 
|  | 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); | 
|  |  | 
|  | 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, | 
|  | char *ext) | 
|  | { | 
|  | /* TODO: support extensions for e.g. symlinks */ | 
|  | if (perm & DMSYMLINK) | 
|  | error(EINVAL, "#%s doesn't support symlinks", devname()); | 
|  | 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 size_t mntwstat(struct chan *c, uint8_t *dp, size_t 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 size_t mntread(struct chan *c, void *buf, size_t n, off64_t off) | 
|  | { | 
|  | uint8_t *p, *e; | 
|  | int nc, dirlen; | 
|  | int numdirent = 0; | 
|  |  | 
|  |  | 
|  | p = buf; | 
|  |  | 
|  | n = mntrdwr(Tread, c, buf, n, off); | 
|  |  | 
|  | if (c->qid.type & QTDIR) { | 
|  | 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 size_t mntwrite(struct chan *c, void *buf, size_t n, off64_t off) | 
|  | { | 
|  | return mntrdwr(Twrite, c, buf, n, off); | 
|  | } | 
|  |  | 
|  | size_t mntrdwr(int type, struct chan *c, void *buf, size_t n, off64_t off) | 
|  | { | 
|  | ERRSTACK(1); | 
|  | struct mnt *m; | 
|  | struct mntrpc *r;	/* TO DO: volatile struct { Mntrpc *r; } r; */ | 
|  | char *uba; | 
|  | uint32_t cnt, nr, nreq; | 
|  |  | 
|  | m = mntchk(c); | 
|  | uba = buf; | 
|  | cnt = 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); | 
|  |  | 
|  | 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); | 
|  |  | 
|  | error(errno, &r->reply.ename[5]); | 
|  | } else | 
|  | error(EFAIL, r->reply.ename); | 
|  | case Rflush: | 
|  | error(EINTR, ERROR_FIXME); | 
|  | 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; | 
|  | warn("mnt: mismatch from %s %s rep %p tag %d fid %d T%d R%d rp %d\n", | 
|  | sn, cn, r, r->request.tag, r->request.fid, r->request.type, | 
|  | r->reply.type, r->reply.tag); | 
|  | error(EPROTO, ERROR_FIXME); | 
|  | } | 
|  | } | 
|  |  | 
|  | static bool kth_proc_is_dying(struct kthread *kth) | 
|  | { | 
|  | return kth->proc ? proc_is_dying(kth->proc) : false; | 
|  | } | 
|  |  | 
|  | void mountio(struct mnt *m, struct mntrpc *r) | 
|  | { | 
|  | ERRSTACK(1); | 
|  | int n; | 
|  |  | 
|  | while (waserror()) { | 
|  | if (m->rip == current_kthread) | 
|  | mntgate(m); | 
|  | /* Syscall aborts are like Plan 9 Eintr.  For those, we need to | 
|  | * change the old request to a flush (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. | 
|  | * | 
|  | * This all means that regular aborts cannot break us out of | 
|  | * here!  We can consider that policy in the future, if we need | 
|  | * to.  Regardless, if the process is dying, we really do need | 
|  | * to abort.  We might not always have a process (RKM | 
|  | * chan_release), but in that case we're fine | 
|  | * - we're not preventing a process from dying. */ | 
|  | if ((get_errno() != EINTR) || | 
|  | kth_proc_is_dying(current_kthread)) { | 
|  | /* all other errors or dying, bail out! */ | 
|  | mntflushfree(m, r); | 
|  | nexterror(); | 
|  | } | 
|  | /* try again.  this is where you can get the "rpc tags" errstr. | 
|  | */ | 
|  | 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(EIO, ERROR_FIXME); | 
|  | /*	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_kthread; | 
|  | spin_unlock(&m->lock); | 
|  | while (r->done == 0) { | 
|  | if (mntrpcread(m, r) < 0) | 
|  | error(EIO, ERROR_FIXME); | 
|  | 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; | 
|  | } | 
|  |  | 
|  | /* TODO: this should use a qio helper directly.  qputback should have | 
|  | * the qlocked, but I guess we assume we're the only one using it. */ | 
|  |  | 
|  | /* hang the data off of the fcall struct */ | 
|  | l = &r->b; | 
|  | *l = NULL; | 
|  | do { | 
|  | b = qget(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 = block_alloc(i - len, MEM_WAIT); | 
|  | 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, MEM_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, MEM_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) | 
|  | { | 
|  | /* TODO: We used to use the device's char (dc), instead of the type. | 
|  | * not sure about the effects one way or the other.  This might be the | 
|  | * type[2] and dev[4] in a D (struct dir, see 9p's stat | 
|  | * (http://man.cat-v.org/plan_9/5/stat).  In which case, those should be | 
|  | * for the kernel's use.  Hopefully our kernel. */ | 
|  | dirbuf += BIT16SZ;	/* skip count */ | 
|  | PBIT16(dirbuf, c->type); | 
|  | 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 = { | 
|  | .name = "mnt", | 
|  |  | 
|  | .reset = devreset, | 
|  | .init = mntinit, | 
|  | .shutdown = devshutdown, | 
|  | .attach = mntattach, | 
|  | .walk = mntwalk, | 
|  | .stat = mntstat, | 
|  | .open = mntopen, | 
|  | .create = mntcreate, | 
|  | .close = mntclose, | 
|  | .read = mntread, | 
|  | .bread = devbread, | 
|  | .write = mntwrite, | 
|  | .bwrite = devbwrite, | 
|  | .remove = mntremove, | 
|  | .wstat = mntwstat, | 
|  | .power = devpower, | 
|  | .chaninfo = devchaninfo, | 
|  | }; |