diff options
Diffstat (limited to 'net/9p')
-rw-r--r-- | net/9p/Kconfig | 21 | ||||
-rw-r--r-- | net/9p/Makefile | 13 | ||||
-rw-r--r-- | net/9p/client.c | 965 | ||||
-rw-r--r-- | net/9p/conv.c | 903 | ||||
-rw-r--r-- | net/9p/error.c | 240 | ||||
-rw-r--r-- | net/9p/fcprint.c | 358 | ||||
-rw-r--r-- | net/9p/mod.c | 85 | ||||
-rw-r--r-- | net/9p/mux.c | 1054 | ||||
-rw-r--r-- | net/9p/sysctl.c | 86 | ||||
-rw-r--r-- | net/9p/trans_fd.c | 363 | ||||
-rw-r--r-- | net/9p/util.c | 125 |
11 files changed, 4213 insertions, 0 deletions
diff --git a/net/9p/Kconfig b/net/9p/Kconfig new file mode 100644 index 00000000000..66821cd64a7 --- /dev/null +++ b/net/9p/Kconfig @@ -0,0 +1,21 @@ +# +# 9P protocol configuration +# + +menuconfig NET_9P + depends on NET && EXPERIMENTAL + tristate "Plan 9 Resource Sharing Support (9P2000) (Experimental)" + help + If you say Y here, you will get experimental support for + Plan 9 resource sharing via the 9P2000 protocol. + + See <http://v9fs.sf.net> for more information. + + If unsure, say N. + +config NET_9P_DEBUG + bool "Debug information" + depends on NET_9P + help + Say Y if you want the 9P subsistem to log debug information. + diff --git a/net/9p/Makefile b/net/9p/Makefile new file mode 100644 index 00000000000..85b3a7838ac --- /dev/null +++ b/net/9p/Makefile @@ -0,0 +1,13 @@ +obj-$(CONFIG_NET_9P) := 9pnet.o + +9pnet-objs := \ + mod.o \ + trans_fd.o \ + mux.o \ + client.o \ + conv.o \ + error.o \ + fcprint.o \ + util.o \ + +9pnet-$(CONFIG_SYSCTL) += sysctl.o diff --git a/net/9p/client.c b/net/9p/client.c new file mode 100644 index 00000000000..cb170750337 --- /dev/null +++ b/net/9p/client.c @@ -0,0 +1,965 @@ +/* + * net/9p/clnt.c + * + * 9P Client + * + * Copyright (C) 2007 by Latchesar Ionkov <lucho@ionkov.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to: + * Free Software Foundation + * 51 Franklin Street, Fifth Floor + * Boston, MA 02111-1301 USA + * + */ + +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/idr.h> +#include <linux/mutex.h> +#include <linux/sched.h> +#include <linux/uaccess.h> +#include <net/9p/9p.h> +#include <net/9p/transport.h> +#include <net/9p/conn.h> +#include <net/9p/client.h> + +static struct p9_fid *p9_fid_create(struct p9_client *clnt); +static void p9_fid_destroy(struct p9_fid *fid); +static struct p9_stat *p9_clone_stat(struct p9_stat *st, int dotu); + +struct p9_client *p9_client_create(struct p9_transport *trans, int msize, + int dotu) +{ + int err, n; + struct p9_client *clnt; + struct p9_fcall *tc, *rc; + struct p9_str *version; + + err = 0; + tc = NULL; + rc = NULL; + clnt = kmalloc(sizeof(struct p9_client), GFP_KERNEL); + if (!clnt) + return ERR_PTR(-ENOMEM); + + P9_DPRINTK(P9_DEBUG_9P, "clnt %p trans %p msize %d dotu %d\n", + clnt, trans, msize, dotu); + spin_lock_init(&clnt->lock); + clnt->trans = trans; + clnt->msize = msize; + clnt->dotu = dotu; + INIT_LIST_HEAD(&clnt->fidlist); + clnt->fidpool = p9_idpool_create(); + if (!clnt->fidpool) { + err = PTR_ERR(clnt->fidpool); + clnt->fidpool = NULL; + goto error; + } + + clnt->conn = p9_conn_create(clnt->trans, clnt->msize, &clnt->dotu); + if (IS_ERR(clnt->conn)) { + err = PTR_ERR(clnt->conn); + clnt->conn = NULL; + goto error; + } + + tc = p9_create_tversion(clnt->msize, clnt->dotu?"9P2000.u":"9P2000"); + if (IS_ERR(tc)) { + err = PTR_ERR(tc); + tc = NULL; + goto error; + } + + err = p9_conn_rpc(clnt->conn, tc, &rc); + if (err) + goto error; + + version = &rc->params.rversion.version; + if (version->len == 8 && !memcmp(version->str, "9P2000.u", 8)) + clnt->dotu = 1; + else if (version->len == 6 && !memcmp(version->str, "9P2000", 6)) + clnt->dotu = 0; + else { + err = -EREMOTEIO; + goto error; + } + + n = rc->params.rversion.msize; + if (n < clnt->msize) + clnt->msize = n; + + kfree(tc); + kfree(rc); + return clnt; + +error: + kfree(tc); + kfree(rc); + p9_client_destroy(clnt); + return ERR_PTR(err); +} +EXPORT_SYMBOL(p9_client_create); + +void p9_client_destroy(struct p9_client *clnt) +{ + struct p9_fid *fid, *fidptr; + + P9_DPRINTK(P9_DEBUG_9P, "clnt %p\n", clnt); + if (clnt->conn) { + p9_conn_destroy(clnt->conn); + clnt->conn = NULL; + } + + if (clnt->trans) { + clnt->trans->close(clnt->trans); + kfree(clnt->trans); + clnt->trans = NULL; + } + + list_for_each_entry_safe(fid, fidptr, &clnt->fidlist, flist) + p9_fid_destroy(fid); + + if (clnt->fidpool) + p9_idpool_destroy(clnt->fidpool); + + kfree(clnt); +} +EXPORT_SYMBOL(p9_client_destroy); + +void p9_client_disconnect(struct p9_client *clnt) +{ + P9_DPRINTK(P9_DEBUG_9P, "clnt %p\n", clnt); + clnt->trans->status = Disconnected; + p9_conn_cancel(clnt->conn, -EIO); +} +EXPORT_SYMBOL(p9_client_disconnect); + +struct p9_fid *p9_client_attach(struct p9_client *clnt, struct p9_fid *afid, + char *uname, char *aname) +{ + int err; + struct p9_fcall *tc, *rc; + struct p9_fid *fid; + + P9_DPRINTK(P9_DEBUG_9P, "clnt %p afid %d uname %s aname %s\n", + clnt, afid?afid->fid:-1, uname, aname); + err = 0; + tc = NULL; + rc = NULL; + + fid = p9_fid_create(clnt); + if (IS_ERR(fid)) { + err = PTR_ERR(fid); + fid = NULL; + goto error; + } + + tc = p9_create_tattach(fid->fid, afid?afid->fid:P9_NOFID, uname, aname); + if (IS_ERR(tc)) { + err = PTR_ERR(tc); + tc = NULL; + goto error; + } + + err = p9_conn_rpc(clnt->conn, tc, &rc); + if (err) + goto error; + + memmove(&fid->qid, &rc->params.rattach.qid, sizeof(struct p9_qid)); + kfree(tc); + kfree(rc); + return fid; + +error: + kfree(tc); + kfree(rc); + if (fid) + p9_fid_destroy(fid); + return ERR_PTR(err); +} +EXPORT_SYMBOL(p9_client_attach); + +struct p9_fid *p9_client_auth(struct p9_client *clnt, char *uname, char *aname) +{ + int err; + struct p9_fcall *tc, *rc; + struct p9_fid *fid; + + P9_DPRINTK(P9_DEBUG_9P, "clnt %p uname %s aname %s\n", clnt, uname, + aname); + err = 0; + tc = NULL; + rc = NULL; + + fid = p9_fid_create(clnt); + if (IS_ERR(fid)) { + err = PTR_ERR(fid); + fid = NULL; + goto error; + } + + tc = p9_create_tauth(fid->fid, uname, aname); + if (IS_ERR(tc)) { + err = PTR_ERR(tc); + tc = NULL; + goto error; + } + + err = p9_conn_rpc(clnt->conn, tc, &rc); + if (err) + goto error; + + memmove(&fid->qid, &rc->params.rauth.qid, sizeof(struct p9_qid)); + kfree(tc); + kfree(rc); + return fid; + +error: + kfree(tc); + kfree(rc); + if (fid) + p9_fid_destroy(fid); + return ERR_PTR(err); +} +EXPORT_SYMBOL(p9_client_auth); + +struct p9_fid *p9_client_walk(struct p9_fid *oldfid, int nwname, char **wnames, + int clone) +{ + int err; + struct p9_fcall *tc, *rc; + struct p9_client *clnt; + struct p9_fid *fid; + + P9_DPRINTK(P9_DEBUG_9P, "fid %d nwname %d wname[0] %s\n", + oldfid->fid, nwname, wnames?wnames[0]:NULL); + err = 0; + tc = NULL; + rc = NULL; + clnt = oldfid->clnt; + if (clone) { + fid = p9_fid_create(clnt); + if (IS_ERR(fid)) { + err = PTR_ERR(fid); + fid = NULL; + goto error; + } + + fid->uid = oldfid->uid; + } else + fid = oldfid; + + tc = p9_create_twalk(oldfid->fid, fid->fid, nwname, wnames); + if (IS_ERR(tc)) { + err = PTR_ERR(tc); + tc = NULL; + goto error; + } + + err = p9_conn_rpc(clnt->conn, tc, &rc); + if (err) { + if (rc && rc->id == P9_RWALK) + goto clunk_fid; + else + goto error; + } + + if (rc->params.rwalk.nwqid != nwname) { + err = -ENOENT; + goto clunk_fid; + } + + if (nwname) + memmove(&fid->qid, + &rc->params.rwalk.wqids[rc->params.rwalk.nwqid - 1], + sizeof(struct p9_qid)); + else + fid->qid = oldfid->qid; + + kfree(tc); + kfree(rc); + return fid; + +clunk_fid: + kfree(tc); + kfree(rc); + rc = NULL; + tc = p9_create_tclunk(fid->fid); + if (IS_ERR(tc)) { + err = PTR_ERR(tc); + tc = NULL; + goto error; + } + + p9_conn_rpc(clnt->conn, tc, &rc); + +error: + kfree(tc); + kfree(rc); + if (fid && (fid != oldfid)) + p9_fid_destroy(fid); + + return ERR_PTR(err); +} +EXPORT_SYMBOL(p9_client_walk); + +int p9_client_open(struct p9_fid *fid, int mode) +{ + int err; + struct p9_fcall *tc, *rc; + struct p9_client *clnt; + + P9_DPRINTK(P9_DEBUG_9P, "fid %d mode %d\n", fid->fid, mode); + err = 0; + tc = NULL; + rc = NULL; + clnt = fid->clnt; + + if (fid->mode != -1) + return -EINVAL; + + tc = p9_create_topen(fid->fid, mode); + if (IS_ERR(tc)) { + err = PTR_ERR(tc); + tc = NULL; + goto done; + } + + err = p9_conn_rpc(clnt->conn, tc, &rc); + if (err) + goto done; + + fid->mode = mode; + fid->iounit = rc->params.ropen.iounit; + +done: + kfree(tc); + kfree(rc); + return err; +} +EXPORT_SYMBOL(p9_client_open); + +int p9_client_fcreate(struct p9_fid *fid, char *name, u32 perm, int mode, + char *extension) +{ + int err; + struct p9_fcall *tc, *rc; + struct p9_client *clnt; + + P9_DPRINTK(P9_DEBUG_9P, "fid %d name %s perm %d mode %d\n", fid->fid, + name, perm, mode); + err = 0; + tc = NULL; + rc = NULL; + clnt = fid->clnt; + + if (fid->mode != -1) + return -EINVAL; + + tc = p9_create_tcreate(fid->fid, name, perm, mode, extension, + clnt->dotu); + if (IS_ERR(tc)) { + err = PTR_ERR(tc); + tc = NULL; + goto done; + } + + err = p9_conn_rpc(clnt->conn, tc, &rc); + if (err) + goto done; + + fid->mode = mode; + fid->iounit = rc->params.ropen.iounit; + +done: + kfree(tc); + kfree(rc); + return err; +} +EXPORT_SYMBOL(p9_client_fcreate); + +int p9_client_clunk(struct p9_fid *fid) +{ + int err; + struct p9_fcall *tc, *rc; + struct p9_client *clnt; + + P9_DPRINTK(P9_DEBUG_9P, "fid %d\n", fid->fid); + err = 0; + tc = NULL; + rc = NULL; + clnt = fid->clnt; + + tc = p9_create_tclunk(fid->fid); + if (IS_ERR(tc)) { + err = PTR_ERR(tc); + tc = NULL; + goto done; + } + + err = p9_conn_rpc(clnt->conn, tc, &rc); + if (err) + goto done; + + p9_fid_destroy(fid); + +done: + kfree(tc); + kfree(rc); + return err; +} +EXPORT_SYMBOL(p9_client_clunk); + +int p9_client_remove(struct p9_fid *fid) +{ + int err; + struct p9_fcall *tc, *rc; + struct p9_client *clnt; + + P9_DPRINTK(P9_DEBUG_9P, "fid %d\n", fid->fid); + err = 0; + tc = NULL; + rc = NULL; + clnt = fid->clnt; + + tc = p9_create_tremove(fid->fid); + if (IS_ERR(tc)) { + err = PTR_ERR(tc); + tc = NULL; + goto done; + } + + err = p9_conn_rpc(clnt->conn, tc, &rc); + if (err) + goto done; + + p9_fid_destroy(fid); + +done: + kfree(tc); + kfree(rc); + return err; +} +EXPORT_SYMBOL(p9_client_remove); + +int p9_client_read(struct p9_fid *fid, char *data, u64 offset, u32 count) +{ + int err, n, rsize, total; + struct p9_fcall *tc, *rc; + struct p9_client *clnt; + + P9_DPRINTK(P9_DEBUG_9P, "fid %d offset %llu %d\n", fid->fid, + (long long unsigned) offset, count); + err = 0; + tc = NULL; + rc = NULL; + clnt = fid->clnt; + total = 0; + + rsize = fid->iounit; + if (!rsize || rsize > clnt->msize-P9_IOHDRSZ) + rsize = clnt->msize - P9_IOHDRSZ; + + do { + if (count < rsize) + rsize = count; + + tc = p9_create_tread(fid->fid, offset, rsize); + if (IS_ERR(tc)) { + err = PTR_ERR(tc); + tc = NULL; + goto error; + } + + err = p9_conn_rpc(clnt->conn, tc, &rc); + if (err) + goto error; + + n = rc->params.rread.count; + if (n > count) + n = count; + + memmove(data, rc->params.rread.data, n); + count -= n; + data += n; + offset += n; + total += n; + kfree(tc); + tc = NULL; + kfree(rc); + rc = NULL; + } while (count > 0 && n == rsize); + + return total; + +error: + kfree(tc); + kfree(rc); + return err; +} +EXPORT_SYMBOL(p9_client_read); + +int p9_client_write(struct p9_fid *fid, char *data, u64 offset, u32 count) +{ + int err, n, rsize, total; + struct p9_fcall *tc, *rc; + struct p9_client *clnt; + + P9_DPRINTK(P9_DEBUG_9P, "fid %d offset %llu count %d\n", fid->fid, + (long long unsigned) offset, count); + err = 0; + tc = NULL; + rc = NULL; + clnt = fid->clnt; + total = 0; + + rsize = fid->iounit; + if (!rsize || rsize > clnt->msize-P9_IOHDRSZ) + rsize = clnt->msize - P9_IOHDRSZ; + + do { + if (count < rsize) + rsize = count; + + tc = p9_create_twrite(fid->fid, offset, rsize, data); + if (IS_ERR(tc)) { + err = PTR_ERR(tc); + tc = NULL; + goto error; + } + + err = p9_conn_rpc(clnt->conn, tc, &rc); + if (err) + goto error; + + n = rc->params.rread.count; + count -= n; + data += n; + offset += n; + total += n; + kfree(tc); + tc = NULL; + kfree(rc); + rc = NULL; + } while (count > 0); + + return total; + +error: + kfree(tc); + kfree(rc); + return err; +} +EXPORT_SYMBOL(p9_client_write); + +int +p9_client_uread(struct p9_fid *fid, char __user *data, u64 offset, u32 count) +{ + int err, n, rsize, total; + struct p9_fcall *tc, *rc; + struct p9_client *clnt; + + P9_DPRINTK(P9_DEBUG_9P, "fid %d offset %llu count %d\n", fid->fid, + (long long unsigned) offset, count); + err = 0; + tc = NULL; + rc = NULL; + clnt = fid->clnt; + total = 0; + + rsize = fid->iounit; + if (!rsize || rsize > clnt->msize-P9_IOHDRSZ) + rsize = clnt->msize - P9_IOHDRSZ; + + do { + if (count < rsize) + rsize = count; + + tc = p9_create_tread(fid->fid, offset, rsize); + if (IS_ERR(tc)) { + err = PTR_ERR(tc); + tc = NULL; + goto error; + } + + err = p9_conn_rpc(clnt->conn, tc, &rc); + if (err) + goto error; + + n = rc->params.rread.count; + if (n > count) + n = count; + + err = copy_to_user(data, rc->params.rread.data, n); + if (err) { + err = -EFAULT; + goto error; + } + + count -= n; + data += n; + offset += n; + total += n; + kfree(tc); + tc = NULL; + kfree(rc); + rc = NULL; + } while (count > 0 && n == rsize); + + return total; + +error: + kfree(tc); + kfree(rc); + return err; +} +EXPORT_SYMBOL(p9_client_uread); + +int +p9_client_uwrite(struct p9_fid *fid, const char __user *data, u64 offset, + u32 count) +{ + int err, n, rsize, total; + struct p9_fcall *tc, *rc; + struct p9_client *clnt; + + P9_DPRINTK(P9_DEBUG_9P, "fid %d offset %llu count %d\n", fid->fid, + (long long unsigned) offset, count); + err = 0; + tc = NULL; + rc = NULL; + clnt = fid->clnt; + total = 0; + + rsize = fid->iounit; + if (!rsize || rsize > clnt->msize-P9_IOHDRSZ) + rsize = clnt->msize - P9_IOHDRSZ; + + do { + if (count < rsize) + rsize = count; + + tc = p9_create_twrite_u(fid->fid, offset, rsize, data); + if (IS_ERR(tc)) { + err = PTR_ERR(tc); + tc = NULL; + goto error; + } + + err = p9_conn_rpc(clnt->conn, tc, &rc); + if (err) + goto error; + + n = rc->params.rread.count; + count -= n; + data += n; + offset += n; + total += n; + kfree(tc); + tc = NULL; + kfree(rc); + rc = NULL; + } while (count > 0); + + return total; + +error: + kfree(tc); + kfree(rc); + return err; +} +EXPORT_SYMBOL(p9_client_uwrite); + +int p9_client_readn(struct p9_fid *fid, char *data, u64 offset, u32 count) +{ + int n, total; + + P9_DPRINTK(P9_DEBUG_9P, "fid %d offset %llu count %d\n", fid->fid, + (long long unsigned) offset, count); + n = 0; + total = 0; + while (count) { + n = p9_client_read(fid, data, offset, count); + if (n <= 0) + break; + + data += n; + offset += n; + count -= n; + total += n; + } + + if (n < 0) + total = n; + + return total; +} +EXPORT_SYMBOL(p9_client_readn); + +struct p9_stat *p9_client_stat(struct p9_fid *fid) +{ + int err; + struct p9_fcall *tc, *rc; + struct p9_client *clnt; + struct p9_stat *ret; + + P9_DPRINTK(P9_DEBUG_9P, "fid %d\n", fid->fid); + err = 0; + tc = NULL; + rc = NULL; + ret = NULL; + clnt = fid->clnt; + + tc = p9_create_tstat(fid->fid); + if (IS_ERR(tc)) { + err = PTR_ERR(tc); + tc = NULL; + goto error; + } + + err = p9_conn_rpc(clnt->conn, tc, &rc); + if (err) + goto error; + + ret = p9_clone_stat(&rc->params.rstat.stat, clnt->dotu); + if (IS_ERR(ret)) { + err = PTR_ERR(ret); + ret = NULL; + goto error; + } + + kfree(tc); + kfree(rc); + return ret; + +error: + kfree(tc); + kfree(rc); + kfree(ret); + return ERR_PTR(err); +} +EXPORT_SYMBOL(p9_client_stat); + +int p9_client_wstat(struct p9_fid *fid, struct p9_wstat *wst) +{ + int err; + struct p9_fcall *tc, *rc; + struct p9_client *clnt; + + P9_DPRINTK(P9_DEBUG_9P, "fid %d\n", fid->fid); + err = 0; + tc = NULL; + rc = NULL; + clnt = fid->clnt; + + tc = p9_create_twstat(fid->fid, wst, clnt->dotu); + if (IS_ERR(tc)) { + err = PTR_ERR(tc); + tc = NULL; + goto done; + } + + err = p9_conn_rpc(clnt->conn, tc, &rc); + +done: + kfree(tc); + kfree(rc); + return err; +} +EXPORT_SYMBOL(p9_client_wstat); + +struct p9_stat *p9_client_dirread(struct p9_fid *fid, u64 offset) +{ + int err, n, m; + struct p9_fcall *tc, *rc; + struct p9_client *clnt; + struct p9_stat st, *ret; + + P9_DPRINTK(P9_DEBUG_9P, "fid %d offset %llu\n", fid->fid, + (long long unsigned) offset); + err = 0; + tc = NULL; + rc = NULL; + ret = NULL; + clnt = fid->clnt; + + /* if the offset is below or above the current response, free it */ + if (offset < fid->rdir_fpos || (fid->rdir_fcall && + offset >= fid->rdir_fpos+fid->rdir_fcall->params.rread.count)) { + fid->rdir_pos = 0; + if (fid->rdir_fcall) + fid->rdir_fpos += fid->rdir_fcall->params.rread.count; + + kfree(fid->rdir_fcall); + fid->rdir_fcall = NULL; + if (offset < fid->rdir_fpos) + fid->rdir_fpos = 0; + } + + if (!fid->rdir_fcall) { + n = fid->iounit; + if (!n || n > clnt->msize-P9_IOHDRSZ) + n = clnt->msize - P9_IOHDRSZ; + + while (1) { + if (fid->rdir_fcall) { + fid->rdir_fpos += + fid->rdir_fcall->params.rread.count; + kfree(fid->rdir_fcall); + fid->rdir_fcall = NULL; + } + + tc = p9_create_tread(fid->fid, fid->rdir_fpos, n); + if (IS_ERR(tc)) { + err = PTR_ERR(tc); + tc = NULL; + goto error; + } + + err = p9_conn_rpc(clnt->conn, tc, &rc); + if (err) + goto error; + + n = rc->params.rread.count; + if (n == 0) + goto done; + + fid->rdir_fcall = rc; + rc = NULL; + if (offset >= fid->rdir_fpos && + offset < fid->rdir_fpos+n) + break; + } + + fid->rdir_pos = 0; + } + + m = offset - fid->rdir_fpos; + if (m < 0) + goto done; + + n = p9_deserialize_stat(fid->rdir_fcall->params.rread.data + m, + fid->rdir_fcall->params.rread.count - m, &st, clnt->dotu); + + if (!n) { + err = -EIO; + goto error; + } + + fid->rdir_pos += n; + st.size = n; + ret = p9_clone_stat(&st, clnt->dotu); + if (IS_ERR(ret)) { + err = PTR_ERR(ret); + ret = NULL; + goto error; + } + +done: + kfree(tc); + kfree(rc); + return ret; + +error: + kfree(tc); + kfree(rc); + kfree(ret); + return ERR_PTR(err); +} +EXPORT_SYMBOL(p9_client_dirread); + +static struct p9_stat *p9_clone_stat(struct p9_stat *st, int dotu) +{ + int n; + char *p; + struct p9_stat *ret; + + n = sizeof(struct p9_stat) + st->name.len + st->uid.len + st->gid.len + + st->muid.len; + + if (dotu) + n += st->extension.len; + + ret = kmalloc(n, GFP_KERNEL); + if (!ret) + return ERR_PTR(-ENOMEM); + + memmove(ret, st, sizeof(struct p9_stat)); + p = ((char *) ret) + sizeof(struct p9_stat); + memmove(p, st->name.str, st->name.len); + p += st->name.len; + memmove(p, st->uid.str, st->uid.len); + p += st->uid.len; + memmove(p, st->gid.str, st->gid.len); + p += st->gid.len; + memmove(p, st->muid.str, st->muid.len); + p += st->muid.len; + + if (dotu) { + memmove(p, st->extension.str, st->extension.len); + p += st->extension.len; + } + + return ret; +} + +static struct p9_fid *p9_fid_create(struct p9_client *clnt) +{ + int err; + struct p9_fid *fid; + + P9_DPRINTK(P9_DEBUG_9P, "clnt %p\n", clnt); + fid = kmalloc(sizeof(struct p9_fid), GFP_KERNEL); + if (!fid) + return ERR_PTR(-ENOMEM); + + fid->fid = p9_idpool_get(clnt->fidpool); + if (fid->fid < 0) { + err = -ENOSPC; + goto error; + } + + memset(&fid->qid, 0, sizeof(struct p9_qid)); + fid->mode = -1; + fid->rdir_fpos = 0; + fid->rdir_pos = 0; + fid->rdir_fcall = NULL; + fid->uid = current->fsuid; + fid->clnt = clnt; + fid->aux = NULL; + + spin_lock(&clnt->lock); + list_add(&fid->flist, &clnt->fidlist); + spin_unlock(&clnt->lock); + + return fid; + +error: + kfree(fid); + return ERR_PTR(err); +} + +static void p9_fid_destroy(struct p9_fid *fid) +{ + struct p9_client *clnt; + + P9_DPRINTK(P9_DEBUG_9P, "fid %d\n", fid->fid); + clnt = fid->clnt; + p9_idpool_put(fid->fid, clnt->fidpool); + spin_lock(&clnt->lock); + list_del(&fid->flist); + spin_unlock(&clnt->lock); + kfree(fid->rdir_fcall); + kfree(fid); +} diff --git a/net/9p/conv.c b/net/9p/conv.c new file mode 100644 index 00000000000..37451178e76 --- /dev/null +++ b/net/9p/conv.c @@ -0,0 +1,903 @@ +/* + * net/9p/conv.c + * + * 9P protocol conversion functions + * + * Copyright (C) 2004, 2005 by Latchesar Ionkov <lucho@ionkov.net> + * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com> + * Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to: + * Free Software Foundation + * 51 Franklin Street, Fifth Floor + * Boston, MA 02111-1301 USA + * + */ + +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/sched.h> +#include <linux/idr.h> +#include <linux/uaccess.h> +#include <net/9p/9p.h> + +/* + * Buffer to help with string parsing + */ +struct cbuf { + unsigned char *sp; + unsigned char *p; + unsigned char *ep; +}; + +static inline void buf_init(struct cbuf *buf, void *data, int datalen) +{ + buf->sp = buf->p = data; + buf->ep = data + datalen; +} + +static inline int buf_check_overflow(struct cbuf *buf) +{ + return buf->p > buf->ep; +} + +static int buf_check_size(struct cbuf *buf, int len) +{ + if (buf->p + len > buf->ep) { + if (buf->p < buf->ep) { + P9_EPRINTK(KERN_ERR, + "buffer overflow: want %d has %d\n", len, + (int)(buf->ep - buf->p)); + dump_stack(); + buf->p = buf->ep + 1; + } + + return 0; + } + + return 1; +} + +static void *buf_alloc(struct cbuf *buf, int len) +{ + void *ret = NULL; + + if (buf_check_size(buf, len)) { + ret = buf->p; + buf->p += len; + } + + return ret; +} + +static void buf_put_int8(struct cbuf *buf, u8 val) +{ + if (buf_check_size(buf, 1)) { + buf->p[0] = val; + buf->p++; + } +} + +static void buf_put_int16(struct cbuf *buf, u16 val) +{ + if (buf_check_size(buf, 2)) { + *(__le16 *) buf->p = cpu_to_le16(val); + buf->p += 2; + } +} + +static void buf_put_int32(struct cbuf *buf, u32 val) +{ + if (buf_check_size(buf, 4)) { + *(__le32 *)buf->p = cpu_to_le32(val); + buf->p += 4; + } +} + +static void buf_put_int64(struct cbuf *buf, u64 val) +{ + if (buf_check_size(buf, 8)) { + *(__le64 *)buf->p = cpu_to_le64(val); + buf->p += 8; + } +} + +static char *buf_put_stringn(struct cbuf *buf, const char *s, u16 slen) +{ + char *ret; + + ret = NULL; + if (buf_check_size(buf, slen + 2)) { + buf_put_int16(buf, slen); + ret = buf->p; + memcpy(buf->p, s, slen); + buf->p += slen; + } + + return ret; +} + +static inline void buf_put_string(struct cbuf *buf, const char *s) +{ + buf_put_stringn(buf, s, strlen(s)); +} + +static u8 buf_get_int8(struct cbuf *buf) +{ + u8 ret = 0; + + if (buf_check_size(buf, 1)) { + ret = buf->p[0]; + buf->p++; + } + + return ret; +} + +static u16 buf_get_int16(struct cbuf *buf) +{ + u16 ret = 0; + + if (buf_check_size(buf, 2)) { + ret = le16_to_cpu(*(__le16 *)buf->p); + buf->p += 2; + } + + return ret; +} + +static u32 buf_get_int32(struct cbuf *buf) +{ + u32 ret = 0; + + if (buf_check_size(buf, 4)) { + ret = le32_to_cpu(*(__le32 *)buf->p); + buf->p += 4; + } + + return ret; +} + +static u64 buf_get_int64(struct cbuf *buf) +{ + u64 ret = 0; + + if (buf_check_size(buf, 8)) { + ret = le64_to_cpu(*(__le64 *)buf->p); + buf->p += 8; + } + + return ret; +} + +static void buf_get_str(struct cbuf *buf, struct p9_str *vstr) +{ + vstr->len = buf_get_int16(buf); + if (!buf_check_overflow(buf) && buf_check_size(buf, vstr->len)) { + vstr->str = buf->p; + buf->p += vstr->len; + } else { + vstr->len = 0; + vstr->str = NULL; + } +} + +static void buf_get_qid(struct cbuf *bufp, struct p9_qid *qid) +{ + qid->type = buf_get_int8(bufp); + qid->version = buf_get_int32(bufp); + qid->path = buf_get_int64(bufp); +} + +/** + * p9_size_wstat - calculate the size of a variable length stat struct + * @stat: metadata (stat) structure + * @dotu: non-zero if 9P2000.u + * + */ + +static int p9_size_wstat(struct p9_wstat *wstat, int dotu) +{ + int size = 0; + + if (wstat == NULL) { + P9_EPRINTK(KERN_ERR, "p9_size_stat: got a NULL stat pointer\n"); + return 0; + } + + size = /* 2 + *//* size[2] */ + 2 + /* type[2] */ + 4 + /* dev[4] */ + 1 + /* qid.type[1] */ + 4 + /* qid.vers[4] */ + 8 + /* qid.path[8] */ + 4 + /* mode[4] */ + 4 + /* atime[4] */ + 4 + /* mtime[4] */ + 8 + /* length[8] */ + 8; /* minimum sum of string lengths */ + + if (wstat->name) + size += strlen(wstat->name); + if (wstat->uid) + size += strlen(wstat->uid); + if (wstat->gid) + size += strlen(wstat->gid); + if (wstat->muid) + size += strlen(wstat->muid); + + if (dotu) { + size += 4 + /* n_uid[4] */ + 4 + /* n_gid[4] */ + 4 + /* n_muid[4] */ + 2; /* string length of extension[4] */ + if (wstat->extension) + size += strlen(wstat->extension); + } + + return size; +} + +/** + * buf_get_stat - safely decode a recieved metadata (stat) structure + * @bufp: buffer to deserialize + * @stat: metadata (stat) structure + * @dotu: non-zero if 9P2000.u + * + */ + +static void +buf_get_stat(struct cbuf *bufp, struct p9_stat *stat, int dotu) +{ + stat->size = buf_get_int16(bufp); + stat->type = buf_get_int16(bufp); + stat->dev = buf_get_int32(bufp); + stat->qid.type = buf_get_int8(bufp); + stat->qid.version = buf_get_int32(bufp); + stat->qid.path = buf_get_int64(bufp); + stat->mode = buf_get_int32(bufp); + stat->atime = buf_get_int32(bufp); + stat->mtime = buf_get_int32(bufp); + stat->length = buf_get_int64(bufp); + buf_get_str(bufp, &stat->name); + buf_get_str(bufp, &stat->uid); + buf_get_str(bufp, &stat->gid); + buf_get_str(bufp, &stat->muid); + + if (dotu) { + buf_get_str(bufp, &stat->extension); + stat->n_uid = buf_get_int32(bufp); + stat->n_gid = buf_get_int32(bufp); + stat->n_muid = buf_get_int32(bufp); |