diff options
Diffstat (limited to 'net/9p/conv.c')
-rw-r--r-- | net/9p/conv.c | 903 |
1 files changed, 903 insertions, 0 deletions
diff --git a/net/9p/conv.c b/net/9p/conv.c new file mode 100644 index 0000000..3745117 --- /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); + } +} + +/** + * p9_deserialize_stat - decode a received metadata structure + * @buf: buffer to deserialize + * @buflen: length of received buffer + * @stat: metadata structure to decode into + * @dotu: non-zero if 9P2000.u + * + * Note: stat will point to the buf region. + */ + +int +p9_deserialize_stat(void *buf, u32 buflen, struct p9_stat *stat, + int dotu) +{ + struct cbuf buffer; + struct cbuf *bufp = &buffer; + unsigned char *p; + + buf_init(bufp, buf, buflen); + p = bufp->p; + buf_get_stat(bufp, stat, dotu); + + if (buf_check_overflow(bufp)) + return 0; + else + return bufp->p - p; +} +EXPORT_SYMBOL(p9_deserialize_stat); + +/** + * deserialize_fcall - unmarshal a response + * @buf: recieved buffer + * @buflen: length of received buffer + * @rcall: fcall structure to populate + * @rcalllen: length of fcall structure to populate + * @dotu: non-zero if 9P2000.u + * + */ + +int +p9_deserialize_fcall(void *buf, u32 buflen, struct p9_fcall *rcall, + int dotu) +{ + + struct cbuf buffer; + struct cbuf *bufp = &buffer; + int i = 0; + + buf_init(bufp, buf, buflen); + + rcall->size = buf_get_int32(bufp); + rcall->id = buf_get_int8(bufp); + rcall->tag = buf_get_int16(bufp); + + P9_DPRINTK(P9_DEBUG_CONV, "size %d id %d tag %d\n", rcall->size, + rcall->id, rcall->tag); + + switch (rcall->id) { + default: + P9_EPRINTK(KERN_ERR, "unknown message type: %d\n", rcall->id); + return -EPROTO; + case P9_RVERSION: + rcall->params.rversion.msize = buf_get_int32(bufp); + buf_get_str(bufp, &rcall->params.rversion.version); + break; + case P9_RFLUSH: + break; + case P9_RATTACH: + rcall->params.rattach.qid.type = buf_get_int8(bufp); + rcall->params.rattach.qid.version = buf_get_int32(bufp); + rcall->params.rattach.qid.path = buf_get_int64(bufp); + break; + case P9_RWALK: + rcall->params.rwalk.nwqid = buf_get_int16(bufp); + if (rcall->params.rwalk.nwqid > P9_MAXWELEM) { + P9_EPRINTK(KERN_ERR, + "Rwalk with more than %d qids: %d\n", + P9_MAXWELEM, rcall->params.rwalk.nwqid); + return -EPROTO; + } + + for (i = 0; i < rcall->params.rwalk.nwqid; i++) + buf_get_qid(bufp, &rcall->params.rwalk.wqids[i]); + break; + case P9_ROPEN: + buf_get_qid(bufp, &rcall->params.ropen.qid); + rcall->params.ropen.iounit = buf_get_int32(bufp); + break; + case P9_RCREATE: + buf_get_qid(bufp, &rcall->params.rcreate.qid); + rcall->params.rcreate.iounit = buf_get_int32(bufp); + break; + case P9_RREAD: + rcall->params.rread.count = buf_get_int32(bufp); + rcall->params.rread.data = bufp->p; + buf_check_size(bufp, rcall->params.rread.count); + break; + case P9_RWRITE: + rcall->params.rwrite.count = buf_get_int32(bufp); + break; + case P9_RCLUNK: + break; + case P9_RREMOVE: + break; + case P9_RSTAT: + buf_get_int16(bufp); + buf_get_stat(bufp, &rcall->params.rstat.stat, dotu); + break; + case P9_RWSTAT: + break; + case P9_RERROR: + buf_get_str(bufp, &rcall->params.rerror.error); + if (dotu) + rcall->params.rerror.errno = buf_get_int16(bufp); + break; + } + + if (buf_check_overflow(bufp)) { + P9_DPRINTK(P9_DEBUG_ERROR, "buffer overflow\n"); + return -EIO; + } + + return bufp->p - bufp->sp; +} +EXPORT_SYMBOL(p9_deserialize_fcall); + +static inline void p9_put_int8(struct cbuf *bufp, u8 val, u8 * p) +{ + *p = val; + buf_put_int8(bufp, val); +} + +static inline void p9_put_int16(struct cbuf *bufp, u16 val, u16 * p) +{ + *p = val; + buf_put_int16(bufp, val); +} + +static inline void p9_put_int32(struct cbuf *bufp, u32 val, u32 * p) +{ + *p = val; + buf_put_int32(bufp, val); +} + +static inline void p9_put_int64(struct cbuf *bufp, u64 val, u64 * p) +{ + *p = val; + buf_put_int64(bufp, val); +} + +static void +p9_put_str(struct cbuf *bufp, char *data, struct p9_str *str) +{ + int len; + char *s; + + if (data) + len = strlen(data); + else + len = 0; + + s = buf_put_stringn(bufp, data, len); + if (str) { + str->len = len; + str->str = s; + } +} + +static int +p9_put_data(struct cbuf *bufp, const char *data, int count, + unsigned char **pdata) +{ + *pdata = buf_alloc(bufp, count); + memmove(*pdata, data, count); + return count; +} + +static int +p9_put_user_data(struct cbuf *bufp, const char __user *data, int count, + unsigned char **pdata) +{ + *pdata = buf_alloc(bufp, count); + return copy_from_user(*pdata, data, count); +} + +static void +p9_put_wstat(struct cbuf *bufp, struct p9_wstat *wstat, + struct p9_stat *stat, int statsz, int dotu) +{ + p9_put_int16(bufp, statsz, &stat->size); + p9_put_int16(bufp, wstat->type, &stat->type); + p9_put_int32(bufp, wstat->dev, &stat->dev); + p9_put_int8(bufp, wstat->qid.type, &stat->qid.type); + p9_put_int32(bufp, wstat->qid.version, &stat->qid.version); + p9_put_int64(bufp, wstat->qid.path, &stat->qid.path); + p9_put_int32(bufp, wstat->mode, &stat->mode); + p9_put_int32(bufp, wstat->atime, &stat->atime); + p9_put_int32(bufp, wstat->mtime, &stat->mtime); + p9_put_int64(bufp, wstat->length, &stat->length); + + p9_put_str(bufp, wstat->name, &stat->name); + p9_put_str(bufp, wstat->uid, &stat->uid); + p9_put_str(bufp, wstat->gid, &stat->gid); + p9_put_str(bufp, wstat->muid, &stat->muid); + + if (dotu) { + p9_put_str(bufp, wstat->extension, &stat->extension); + p9_put_int32(bufp, wstat->n_uid, &stat->n_uid); + p9_put_int32(bufp, wstat->n_gid, &stat->n_gid); + p9_put_int32(bufp, wstat->n_muid, &stat->n_muid); + } +} + +static struct p9_fcall * +p9_create_common(struct cbuf *bufp, u32 size, u8 id) +{ + struct p9_fcall *fc; + + size += 4 + 1 + 2; /* size[4] id[1] tag[2] */ + fc = kmalloc(sizeof(struct p9_fcall) + size, GFP_KERNEL); + if (!fc) + return ERR_PTR(-ENOMEM); + + fc->sdata = (char *)fc + sizeof(*fc); + + buf_init(bufp, (char *)fc->sdata, size); + p9_put_int32(bufp, size, &fc->size); + p9_put_int8(bufp, id, &fc->id); + p9_put_int16(bufp, P9_NOTAG, &fc->tag); + + return fc; +} + +void p9_set_tag(struct p9_fcall *fc, u16 tag) +{ + fc->tag = tag; + *(__le16 *) (fc->sdata + 5) = cpu_to_le16(tag); +} +EXPORT_SYMBOL(p9_set_tag); + +struct p9_fcall *p9_create_tversion(u32 msize, char *version) +{ + int size; + struct p9_fcall *fc; + struct cbuf buffer; + struct cbuf *bufp = &buffer; + + size = 4 + 2 + strlen(version); /* msize[4] version[s] */ + fc = p9_create_common(bufp, size, P9_TVERSION); + if (IS_ERR(fc)) + goto error; + + p9_put_int32(bufp, msize, &fc->params.tversion.msize); + p9_put_str(bufp, version, &fc->params.tversion.version); + + if (buf_check_overflow(bufp)) { + kfree(fc); + fc = ERR_PTR(-ENOMEM); + } +error: + return fc; +} +EXPORT_SYMBOL(p9_create_tversion); + +struct p9_fcall *p9_create_tauth(u32 afid, char *uname, char *aname) +{ + int size; + struct p9_fcall *fc; + struct cbuf buffer; + struct cbuf *bufp = &buffer; + + /* afid[4] uname[s] aname[s] */ + size = 4 + 2 + strlen(uname) + 2 + strlen(aname); + fc = p9_create_common(bufp, size, P9_TAUTH); + if (IS_ERR(fc)) + goto error; + + p9_put_int32(bufp, afid, &fc->params.tauth.afid); + p9_put_str(bufp, uname, &fc->params.tauth.uname); + p9_put_str(bufp, aname, &fc->params.tauth.aname); + + if (buf_check_overflow(bufp)) { + kfree(fc); + fc = ERR_PTR(-ENOMEM); + } +error: + return fc; +} +EXPORT_SYMBOL(p9_create_tauth); + +struct p9_fcall * +p9_create_tattach(u32 fid, u32 afid, char *uname, char *aname) +{ + int size; + struct p9_fcall *fc; + struct cbuf buffer; + struct cbuf *bufp = &buffer; + + /* fid[4] afid[4] uname[s] aname[s] */ + size = 4 + 4 + 2 + strlen(uname) + 2 + strlen(aname); + fc = p9_create_common(bufp, size, P9_TATTACH); + if (IS_ERR(fc)) + goto error; + + p9_put_int32(bufp, fid, &fc->params.tattach.fid); + p9_put_int32(bufp, afid, &fc->params.tattach.afid); + p9_put_str(bufp, uname, &fc->params.tattach.uname); + p9_put_str(bufp, aname, &fc->params.tattach.aname); + +error: + return fc; +} +EXPORT_SYMBOL(p9_create_tattach); + +struct p9_fcall *p9_create_tflush(u16 oldtag) +{ + int size; + struct p9_fcall *fc; + struct cbuf buffer; + struct cbuf *bufp = &buffer; + + size = 2; /* oldtag[2] */ + fc = p9_create_common(bufp, size, P9_TFLUSH); + if (IS_ERR(fc)) + goto error; + + p9_put_int16(bufp, oldtag, &fc->params.tflush.oldtag); + + if (buf_check_overflow(bufp)) { + kfree(fc); + fc = ERR_PTR(-ENOMEM); + } +error: + return fc; +} +EXPORT_SYMBOL(p9_create_tflush); + +struct p9_fcall *p9_create_twalk(u32 fid, u32 newfid, u16 nwname, + char **wnames) +{ + int i, size; + struct p9_fcall *fc; + struct cbuf buffer; + struct cbuf *bufp = &buffer; + + if (nwname > P9_MAXWELEM) { + P9_DPRINTK(P9_DEBUG_ERROR, "nwname > %d\n", P9_MAXWELEM); + return NULL; + } + + size = 4 + 4 + 2; /* fid[4] newfid[4] nwname[2] ... */ + for (i = 0; i < nwname; i++) { + size += 2 + strlen(wnames[i]); /* wname[s] */ + } + + fc = p9_create_common(bufp, size, P9_TWALK); + if (IS_ERR(fc)) + goto error; + + p9_put_int32(bufp, fid, &fc->params.twalk.fid); + p9_put_int32(bufp, newfid, &fc->params.twalk.newfid); + p9_put_int16(bufp, nwname, &fc->params.twalk.nwname); + for (i = 0; i < nwname; i++) { + p9_put_str(bufp, wnames[i], &fc->params.twalk.wnames[i]); + } + + if (buf_check_overflow(bufp)) { + kfree(fc); + fc = ERR_PTR(-ENOMEM); + } +error: + return fc; +} +EXPORT_SYMBOL(p9_create_twalk); + +struct p9_fcall *p9_create_topen(u32 fid, u8 mode) +{ + int size; + struct p9_fcall *fc; + struct cbuf buffer; + struct cbuf *bufp = &buffer; + + size = 4 + 1; /* fid[4] mode[1] */ + fc = p9_create_common(bufp, size, P9_TOPEN); + if (IS_ERR(fc)) + goto error; + + p9_put_int32(bufp, fid, &fc->params.topen.fid); + p9_put_int8(bufp, mode, &fc->params.topen.mode); + + if (buf_check_overflow(bufp)) { + kfree(fc); + fc = ERR_PTR(-ENOMEM); + } +error: + return fc; +} +EXPORT_SYMBOL(p9_create_topen); + +struct p9_fcall *p9_create_tcreate(u32 fid, char *name, u32 perm, u8 mode, + char *extension, int dotu) +{ + int size; + struct p9_fcall *fc; + struct cbuf buffer; + struct cbuf *bufp = &buffer; + + /* fid[4] name[s] perm[4] mode[1] */ + size = 4 + 2 + strlen(name) + 4 + 1; + if (dotu) { + size += 2 + /* extension[s] */ + (extension == NULL ? 0 : strlen(extension)); + } + + fc = p9_create_common(bufp, size, P9_TCREATE); + if (IS_ERR(fc)) + goto error; + + p9_put_int32(bufp, fid, &fc->params.tcreate.fid); + p9_put_str(bufp, name, &fc->params.tcreate.name); + p9_put_int32(bufp, perm, &fc->params.tcreate.perm); + p9_put_int8(bufp, mode, &fc->params.tcreate.mode); + if (dotu) + p9_put_str(bufp, extension, &fc->params.tcreate.extension); + + if (buf_check_overflow(bufp)) { + kfree(fc); + fc = ERR_PTR(-ENOMEM); + } +error: + return fc; +} +EXPORT_SYMBOL(p9_create_tcreate); + +struct p9_fcall *p9_create_tread(u32 fid, u64 offset, u32 count) +{ + int size; + struct p9_fcall *fc; + struct cbuf buffer; + struct cbuf *bufp = &buffer; + + size = 4 + 8 + 4; /* fid[4] offset[8] count[4] */ + fc = p9_create_common(bufp, size, P9_TREAD); + if (IS_ERR(fc)) + goto error; + + p9_put_int32(bufp, fid, &fc->params.tread.fid); + p9_put_int64(bufp, offset, &fc->params.tread.offset); + p9_put_int32(bufp, count, &fc->params.tread.count); + + if (buf_check_overflow(bufp)) { + kfree(fc); + fc = ERR_PTR(-ENOMEM); + } +error: + return fc; +} +EXPORT_SYMBOL(p9_create_tread); + +struct p9_fcall *p9_create_twrite(u32 fid, u64 offset, u32 count, + const char *data) +{ + int size, err; + struct p9_fcall *fc; + struct cbuf buffer; + struct cbuf *bufp = &buffer; + + /* fid[4] offset[8] count[4] data[count] */ + size = 4 + 8 + 4 + count; + fc = p9_create_common(bufp, size, P9_TWRITE); + if (IS_ERR(fc)) + goto error; + + p9_put_int32(bufp, fid, &fc->params.twrite.fid); + p9_put_int64(bufp, offset, &fc->params.twrite.offset); + p9_put_int32(bufp, count, &fc->params.twrite.count); + err = p9_put_data(bufp, data, count, &fc->params.twrite.data); + if (err) { + kfree(fc); + fc = ERR_PTR(err); + } + + if (buf_check_overflow(bufp)) { + kfree(fc); + fc = ERR_PTR(-ENOMEM); + } +error: + return fc; +} +EXPORT_SYMBOL(p9_create_twrite); + +struct p9_fcall *p9_create_twrite_u(u32 fid, u64 offset, u32 count, + const char __user *data) +{ + int size, err; + struct p9_fcall *fc; + struct cbuf buffer; + struct cbuf *bufp = &buffer; + + /* fid[4] offset[8] count[4] data[count] */ + size = 4 + 8 + 4 + count; + fc = p9_create_common(bufp, size, P9_TWRITE); + if (IS_ERR(fc)) + goto error; + + p9_put_int32(bufp, fid, &fc->params.twrite.fid); + p9_put_int64(bufp, offset, &fc->params.twrite.offset); + p9_put_int32(bufp, count, &fc->params.twrite.count); + err = p9_put_user_data(bufp, data, count, &fc->params.twrite.data); + if (err) { + kfree(fc); + fc = ERR_PTR(err); + } + + if (buf_check_overflow(bufp)) { + kfree(fc); + fc = ERR_PTR(-ENOMEM); + } +error: + return fc; +} +EXPORT_SYMBOL(p9_create_twrite_u); + +struct p9_fcall *p9_create_tclunk(u32 fid) +{ + int size; + struct p9_fcall *fc; + struct cbuf buffer; + struct cbuf *bufp = &buffer; + + size = 4; /* fid[4] */ + fc = p9_create_common(bufp, size, P9_TCLUNK); + if (IS_ERR(fc)) + goto error; + + p9_put_int32(bufp, fid, &fc->params.tclunk.fid); + + if (buf_check_overflow(bufp)) { + kfree(fc); + fc = ERR_PTR(-ENOMEM); + } +error: + return fc; +} +EXPORT_SYMBOL(p9_create_tclunk); + +struct p9_fcall *p9_create_tremove(u32 fid) +{ + int size; + struct p9_fcall *fc; + struct cbuf buffer; + struct cbuf *bufp = &buffer; + + size = 4; /* fid[4] */ + fc = p9_create_common(bufp, size, P9_TREMOVE); + if (IS_ERR(fc)) + goto error; + + p9_put_int32(bufp, fid, &fc->params.tremove.fid); + + if (buf_check_overflow(bufp)) { + kfree(fc); + fc = ERR_PTR(-ENOMEM); + } +error: + return fc; +} +EXPORT_SYMBOL(p9_create_tremove); + +struct p9_fcall *p9_create_tstat(u32 fid) +{ + int size; + struct p9_fcall *fc; + struct cbuf buffer; + struct cbuf *bufp = &buffer; + + size = 4; /* fid[4] */ + fc = p9_create_common(bufp, size, P9_TSTAT); + if (IS_ERR(fc)) + goto error; + + p9_put_int32(bufp, fid, &fc->params.tstat.fid); + + if (buf_check_overflow(bufp)) { + kfree(fc); + fc = ERR_PTR(-ENOMEM); + } +error: + return fc; +} +EXPORT_SYMBOL(p9_create_tstat); + +struct p9_fcall *p9_create_twstat(u32 fid, struct p9_wstat *wstat, + int dotu) +{ + int size, statsz; + struct p9_fcall *fc; + struct cbuf buffer; + struct cbuf *bufp = &buffer; + + statsz = p9_size_wstat(wstat, dotu); + size = 4 + 2 + 2 + statsz; /* fid[4] stat[n] */ + fc = p9_create_common(bufp, size, P9_TWSTAT); + if (IS_ERR(fc)) + goto error; + + p9_put_int32(bufp, fid, &fc->params.twstat.fid); + buf_put_int16(bufp, statsz + 2); + p9_put_wstat(bufp, wstat, &fc->params.twstat.stat, statsz, dotu); + + if (buf_check_overflow(bufp)) { + kfree(fc); + fc = ERR_PTR(-ENOMEM); + } +error: + return fc; +} +EXPORT_SYMBOL(p9_create_twstat); |