#include <linux/uaccess.h>
#include <net/9p/9p.h>
#include <linux/parser.h>
-#include <net/9p/transport.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);
+#include <net/9p/transport.h>
/*
* Client Option Parsing (code inspired by NFS code)
Opt_err,
};
-static match_table_t tokens = {
+static const match_table_t tokens = {
{Opt_msize, "msize=%u"},
{Opt_legacy, "noextend"},
{Opt_trans, "trans=%s"},
{Opt_err, NULL},
};
+static int
+p9_client_rpc(struct p9_client *c, struct p9_fcall *tc, struct p9_fcall **rc);
+
/**
* v9fs_parse_options - parse mount options into session structure
* @options: options string passed from mount
return ret;
}
+/**
+ * p9_tag_alloc - lookup/allocate a request by tag
+ * @c: client session to lookup tag within
+ * @tag: numeric id for transaction
+ *
+ * this is a simple array lookup, but will grow the
+ * request_slots as necessary to accomodate transaction
+ * ids which did not previously have a slot.
+ *
+ * this code relies on the client spinlock to manage locks, its
+ * possible we should switch to something else, but I'd rather
+ * stick with something low-overhead for the common case.
+ *
+ */
+
+struct p9_req_t *p9_tag_alloc(struct p9_client *c, u16 tag)
+{
+ unsigned long flags;
+ int row, col;
+
+ /* This looks up the original request by tag so we know which
+ * buffer to read the data into */
+ tag++;
+
+ if (tag >= c->max_tag) {
+ spin_lock_irqsave(&c->lock, flags);
+ /* check again since original check was outside of lock */
+ while (tag >= c->max_tag) {
+ row = (tag / P9_ROW_MAXTAG);
+ c->reqs[row] = kcalloc(P9_ROW_MAXTAG,
+ sizeof(struct p9_req_t), GFP_ATOMIC);
+
+ if (!c->reqs[row]) {
+ printk(KERN_ERR "Couldn't grow tag array\n");
+ BUG();
+ }
+ for (col = 0; col < P9_ROW_MAXTAG; col++) {
+ c->reqs[row][col].status = REQ_STATUS_IDLE;
+ c->reqs[row][col].flush_tag = P9_NOTAG;
+ c->reqs[row][col].wq = kmalloc(
+ sizeof(wait_queue_head_t), GFP_ATOMIC);
+ if (!c->reqs[row][col].wq) {
+ printk(KERN_ERR
+ "Couldn't grow tag array\n");
+ BUG();
+ }
+ init_waitqueue_head(c->reqs[row][col].wq);
+ }
+ c->max_tag += P9_ROW_MAXTAG;
+ }
+ spin_unlock_irqrestore(&c->lock, flags);
+ }
+ row = tag / P9_ROW_MAXTAG;
+ col = tag % P9_ROW_MAXTAG;
+
+ c->reqs[row][col].status = REQ_STATUS_ALLOC;
+ c->reqs[row][col].flush_tag = P9_NOTAG;
+
+ return &c->reqs[row][col];
+}
+EXPORT_SYMBOL(p9_tag_alloc);
/**
- * p9_client_rpc - sends 9P request and waits until a response is available.
- * The function can be interrupted.
- * @c: client data
- * @tc: request to be sent
- * @rc: pointer where a pointer to the response is stored
+ * p9_tag_lookup - lookup a request by tag
+ * @c: client session to lookup tag within
+ * @tag: numeric id for transaction
+ *
*/
-int
-p9_client_rpc(struct p9_client *c, struct p9_fcall *tc,
- struct p9_fcall **rc)
+
+struct p9_req_t *p9_tag_lookup(struct p9_client *c, u16 tag)
+{
+ int row, col;
+
+ /* This looks up the original request by tag so we know which
+ * buffer to read the data into */
+ tag++;
+
+ BUG_ON(tag >= c->max_tag);
+
+ row = tag / P9_ROW_MAXTAG;
+ col = tag % P9_ROW_MAXTAG;
+
+ return &c->reqs[row][col];
+}
+EXPORT_SYMBOL(p9_tag_lookup);
+
+/**
+ * p9_tag_init - setup tags structure and contents
+ * @tags: tags structure from the client struct
+ *
+ * This initializes the tags structure for each client instance.
+ *
+ */
+
+static int p9_tag_init(struct p9_client *c)
+{
+ int err = 0;
+
+ c->tagpool = p9_idpool_create();
+ if (IS_ERR(c->tagpool)) {
+ err = PTR_ERR(c->tagpool);
+ c->tagpool = NULL;
+ goto error;
+ }
+
+ p9_idpool_get(c->tagpool); /* reserve tag 0 */
+
+ c->max_tag = 0;
+error:
+ return err;
+}
+
+/**
+ * p9_tag_cleanup - cleans up tags structure and reclaims resources
+ * @tags: tags structure from the client struct
+ *
+ * This frees resources associated with the tags structure
+ *
+ */
+static void p9_tag_cleanup(struct p9_client *c)
+{
+ int row, col;
+
+ /* check to insure all requests are idle */
+ for (row = 0; row < (c->max_tag/P9_ROW_MAXTAG); row++) {
+ for (col = 0; col < P9_ROW_MAXTAG; col++) {
+ if (c->reqs[row][col].status != REQ_STATUS_IDLE) {
+ P9_DPRINTK(P9_DEBUG_MUX,
+ "Attempting to cleanup non-free tag %d,%d\n",
+ row, col);
+ /* TODO: delay execution of cleanup */
+ return;
+ }
+ }
+ }
+
+ if (c->tagpool)
+ p9_idpool_destroy(c->tagpool);
+
+ /* free requests associated with tags */
+ for (row = 0; row < (c->max_tag/P9_ROW_MAXTAG); row++) {
+ for (col = 0; col < P9_ROW_MAXTAG; col++)
+ kfree(c->reqs[row][col].wq);
+ kfree(c->reqs[row]);
+ }
+ c->max_tag = 0;
+}
+
+/**
+ * p9_client_flush - flush (cancel) a request
+ * c: client state
+ * req: request to cancel
+ *
+ * This sents a flush for a particular requests and links
+ * the flush request to the original request. The current
+ * code only supports a single flush request although the protocol
+ * allows for multiple flush requests to be sent for a single request.
+ *
+ */
+
+static int p9_client_flush(struct p9_client *c, struct p9_req_t *req)
+{
+ struct p9_fcall *tc, *rc = NULL;
+ int err;
+
+ P9_DPRINTK(P9_DEBUG_9P, "client %p tag %d\n", c, req->tc->tag);
+
+ tc = p9_create_tflush(req->tc->tag);
+ if (IS_ERR(tc))
+ return PTR_ERR(tc);
+
+ err = p9_client_rpc(c, tc, &rc);
+
+ /* we don't free anything here because RPC isn't complete */
+
+ return err;
+}
+
+/**
+ * p9_free_req - free a request and clean-up as necessary
+ * c: client state
+ * r: request to release
+ *
+ */
+
+void p9_free_req(struct p9_client *c, struct p9_req_t *r)
+{
+ r->flush_tag = P9_NOTAG;
+ r->status = REQ_STATUS_IDLE;
+ if (r->tc->tag != P9_NOTAG && p9_idpool_check(r->tc->tag, c->tagpool))
+ p9_idpool_put(r->tc->tag, c->tagpool);
+
+ /* if this was a flush request we have to free response fcall */
+ if (r->tc->id == P9_TFLUSH) {
+ kfree(r->tc);
+ kfree(r->rc);
+ }
+}
+
+/**
+ * p9_client_cb - call back from transport to client
+ * c: client state
+ * req: request received
+ *
+ */
+void p9_client_cb(struct p9_client *c, struct p9_req_t *req)
+{
+ struct p9_req_t *other_req;
+ unsigned long flags;
+
+ P9_DPRINTK(P9_DEBUG_MUX, ": %d\n", req->tc->tag);
+
+ if (req->status == REQ_STATUS_ERROR)
+ wake_up(req->wq);
+
+ if (req->tc->id == P9_TFLUSH) { /* flush receive path */
+ P9_DPRINTK(P9_DEBUG_MUX, "flush: %d\n", req->tc->tag);
+ spin_lock_irqsave(&c->lock, flags);
+ other_req = p9_tag_lookup(c, req->tc->params.tflush.oldtag);
+ if (other_req->flush_tag != req->tc->tag) /* stale flush */
+ spin_unlock_irqrestore(&c->lock, flags);
+ else {
+ BUG_ON(other_req->status != REQ_STATUS_FLSH);
+ other_req->status = REQ_STATUS_FLSHD;
+ spin_unlock_irqrestore(&c->lock, flags);
+ wake_up(other_req->wq);
+ }
+ p9_free_req(c, req);
+ } else { /* normal receive path */
+ P9_DPRINTK(P9_DEBUG_MUX, "normal: %d\n", req->tc->tag);
+ spin_lock_irqsave(&c->lock, flags);
+ if (req->status != REQ_STATUS_FLSHD)
+ req->status = REQ_STATUS_RCVD;
+ req->flush_tag = P9_NOTAG;
+ spin_unlock_irqrestore(&c->lock, flags);
+ wake_up(req->wq);
+ P9_DPRINTK(P9_DEBUG_MUX, "wakeup: %d\n", req->tc->tag);
+ }
+}
+EXPORT_SYMBOL(p9_client_cb);
+
+/**
+ * p9_client_rpc - issue a request and wait for a response
+ * @c: client session
+ * @tc: &p9_fcall request to transmit
+ * @rc: &p9_fcall to put reponse into
+ *
+ * Returns 0 on success, error code on failure
+ */
+
+static int
+p9_client_rpc(struct p9_client *c, struct p9_fcall *tc, struct p9_fcall **rc)
+{
+ int tag, err, size;
+ char *rdata;
+ struct p9_req_t *req;
+ unsigned long flags;
+ int sigpending;
+ int flushed = 0;
+
+ P9_DPRINTK(P9_DEBUG_9P, "client %p tc %p rc %p\n", c, tc, rc);
+
+ if (c->status != Connected)
+ return -EIO;
+
+ if (signal_pending(current)) {
+ sigpending = 1;
+ clear_thread_flag(TIF_SIGPENDING);
+ } else
+ sigpending = 0;
+
+ tag = P9_NOTAG;
+ if (tc->id != P9_TVERSION) {
+ tag = p9_idpool_get(c->tagpool);
+ if (tag < 0)
+ return -ENOMEM;
+ }
+
+ req = p9_tag_alloc(c, tag);
+
+ /* if this is a flush request, backlink flush request now to
+ * avoid race conditions later. */
+ if (tc->id == P9_TFLUSH) {
+ struct p9_req_t *other_req =
+ p9_tag_lookup(c, tc->params.tflush.oldtag);
+ if (other_req->status == REQ_STATUS_FLSH)
+ other_req->flush_tag = tag;
+ }
+
+ p9_set_tag(tc, tag);
+
+ /*
+ * if client passed in a pre-allocated response fcall struct
+ * then we just use that, otherwise we allocate one.
+ */
+
+ if (rc == NULL)
+ req->rc = NULL;
+ else
+ req->rc = *rc;
+ if (req->rc == NULL) {
+ req->rc = kmalloc(sizeof(struct p9_fcall) + c->msize,
+ GFP_KERNEL);
+ if (!req->rc) {
+ err = -ENOMEM;
+ p9_idpool_put(tag, c->tagpool);
+ p9_free_req(c, req);
+ goto reterr;
+ }
+ *rc = req->rc;
+ }
+
+ rdata = (char *)req->rc+sizeof(struct p9_fcall);
+
+ req->tc = tc;
+ P9_DPRINTK(P9_DEBUG_9P, "request: tc: %p rc: %p\n", req->tc, req->rc);
+
+ err = c->trans_mod->request(c, req);
+ if (err < 0) {
+ c->status = Disconnected;
+ goto reterr;
+ }
+
+ /* if it was a flush we just transmitted, return our tag */
+ if (tc->id == P9_TFLUSH)
+ return 0;
+again:
+ P9_DPRINTK(P9_DEBUG_9P, "wait %p tag: %d\n", req->wq, tag);
+ err = wait_event_interruptible(*req->wq,
+ req->status >= REQ_STATUS_RCVD);
+ P9_DPRINTK(P9_DEBUG_9P, "wait %p tag: %d returned %d (flushed=%d)\n",
+ req->wq, tag, err, flushed);
+
+ if (req->status == REQ_STATUS_ERROR) {
+ P9_DPRINTK(P9_DEBUG_9P, "req_status error %d\n", req->t_err);
+ err = req->t_err;
+ } else if (err == -ERESTARTSYS && flushed) {
+ P9_DPRINTK(P9_DEBUG_9P, "flushed - going again\n");
+ goto again;
+ } else if (req->status == REQ_STATUS_FLSHD) {
+ P9_DPRINTK(P9_DEBUG_9P, "flushed - erestartsys\n");
+ err = -ERESTARTSYS;
+ }
+
+ if ((err == -ERESTARTSYS) && (c->status == Connected) && (!flushed)) {
+ P9_DPRINTK(P9_DEBUG_9P, "flushing\n");
+ spin_lock_irqsave(&c->lock, flags);
+ if (req->status == REQ_STATUS_SENT)
+ req->status = REQ_STATUS_FLSH;
+ spin_unlock_irqrestore(&c->lock, flags);
+ sigpending = 1;
+ flushed = 1;
+ clear_thread_flag(TIF_SIGPENDING);
+
+ if (c->trans_mod->cancel(c, req)) {
+ err = p9_client_flush(c, req);
+ if (err == 0)
+ goto again;
+ }
+ }
+
+ if (sigpending) {
+ spin_lock_irqsave(¤t->sighand->siglock, flags);
+ recalc_sigpending();
+ spin_unlock_irqrestore(¤t->sighand->siglock, flags);
+ }
+
+ if (err < 0)
+ goto reterr;
+
+ size = le32_to_cpu(*(__le32 *) rdata);
+
+ err = p9_deserialize_fcall(rdata, size, req->rc, c->dotu);
+ if (err < 0) {
+ P9_DPRINTK(P9_DEBUG_9P,
+ "9p debug: client rpc deserialize returned %d\n", err);
+ goto reterr;
+ }
+
+#ifdef CONFIG_NET_9P_DEBUG
+ if ((p9_debug_level&P9_DEBUG_FCALL) == P9_DEBUG_FCALL) {
+ char buf[150];
+
+ p9_printfcall(buf, sizeof(buf), req->rc, c->dotu);
+ printk(KERN_NOTICE ">>> %p %s\n", c, buf);
+ }
+#endif
+
+ if (req->rc->id == P9_RERROR) {
+ int ecode = req->rc->params.rerror.errno;
+ struct p9_str *ename = &req->rc->params.rerror.error;
+
+ P9_DPRINTK(P9_DEBUG_MUX, "Rerror %.*s\n", ename->len,
+ ename->str);
+
+ if (c->dotu)
+ err = -ecode;
+
+ if (!err) {
+ err = p9_errstr2errno(ename->str, ename->len);
+
+ /* string match failed */
+ if (!err) {
+ PRINT_FCALL_ERROR("unknown error", req->rc);
+ err = -ESERVERFAULT;
+ }
+ }
+ } else
+ err = 0;
+
+reterr:
+ p9_free_req(c, req);
+
+ P9_DPRINTK(P9_DEBUG_9P, "returning %d\n", err);
+ return err;
+}
+
+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)
{
- return c->trans->rpc(c->trans, tc, rc);
+ 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);
}
struct p9_client *p9_client_create(const char *dev_name, char *options)
goto error;
}
+ p9_tag_init(clnt);
+
err = parse_opts(options, clnt);
if (err < 0)
goto error;
clnt, clnt->trans_mod, clnt->msize, clnt->dotu);
- clnt->trans = clnt->trans_mod->create(dev_name, options, clnt->msize,
- clnt->dotu);
- if (IS_ERR(clnt->trans)) {
- err = PTR_ERR(clnt->trans);
- clnt->trans = NULL;
+ err = clnt->trans_mod->create(clnt, dev_name, options);
+ if (err)
goto error;
- }
if ((clnt->msize+P9_IOHDRSZ) > clnt->trans_mod->maxsize)
clnt->msize = clnt->trans_mod->maxsize-P9_IOHDRSZ;
P9_DPRINTK(P9_DEBUG_9P, "clnt %p\n", clnt);
- if (clnt->trans) {
- clnt->trans->close(clnt->trans);
- kfree(clnt->trans);
- clnt->trans = NULL;
- }
+ if (clnt->trans_mod)
+ clnt->trans_mod->close(clnt);
v9fs_put_trans(clnt->trans_mod);
if (clnt->fidpool)
p9_idpool_destroy(clnt->fidpool);
+ p9_tag_cleanup(clnt);
+
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;
+ clnt->status = Disconnected;
}
EXPORT_SYMBOL(p9_client_disconnect);
}
EXPORT_SYMBOL(p9_client_remove);
-int p9_client_read(struct p9_fid *fid, char *data, u64 offset, u32 count)
+int
+p9_client_read(struct p9_fid *fid, char *data, char __user *udata, u64 offset,
+ u32 count)
{
int err, n, rsize, total;
struct p9_fcall *tc, *rc;
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_client_rpc(clnt, 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;
+ if (data) {
+ memmove(data, rc->params.rread.data, n);
+ data += n;
}
- err = p9_client_rpc(clnt, 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;
+ if (udata) {
+ err = copy_to_user(udata, rc->params.rread.data, n);
+ if (err) {
+ err = -EFAULT;
+ goto error;
+ }
+ udata += n;
}
count -= n;
- data += n;
offset += n;
total += n;
kfree(tc);
kfree(rc);
return err;
}
-EXPORT_SYMBOL(p9_client_uread);
+EXPORT_SYMBOL(p9_client_read);
int
-p9_client_uwrite(struct p9_fid *fid, const char __user *data, u64 offset,
- u32 count)
+p9_client_write(struct p9_fid *fid, char *data, const char __user *udata,
+ u64 offset, u32 count)
{
int err, n, rsize, total;
struct p9_fcall *tc, *rc;
if (count < rsize)
rsize = count;
- tc = p9_create_twrite_u(fid->fid, offset, rsize, data);
+ if (data)
+ tc = p9_create_twrite(fid->fid, offset, rsize, data);
+ else
+ tc = p9_create_twrite_u(fid->fid, offset, rsize, udata);
if (IS_ERR(tc)) {
err = PTR_ERR(tc);
tc = NULL;
n = rc->params.rread.count;
count -= n;
- data += n;
+
+ if (data)
+ data += n;
+ else
+ udata += n;
+
offset += n;
total += n;
kfree(tc);
kfree(rc);
return err;
}
-EXPORT_SYMBOL(p9_client_uwrite);
+EXPORT_SYMBOL(p9_client_write);
int p9_client_readn(struct p9_fid *fid, char *data, u64 offset, u32 count)
{
n = 0;
total = 0;
while (count) {
- n = p9_client_read(fid, data, offset, count);
+ n = p9_client_read(fid, data, NULL, offset, count);
if (n <= 0)
break;
}
EXPORT_SYMBOL(p9_client_readn);
+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);
+ ret->name.str = p;
+ p += st->name.len;
+ memmove(p, st->uid.str, st->uid.len);
+ ret->uid.str = p;
+ p += st->uid.len;
+ memmove(p, st->gid.str, st->gid.len);
+ ret->gid.str = p;
+ p += st->gid.len;
+ memmove(p, st->muid.str, st->muid.len);
+ ret->muid.str = p;
+ p += st->muid.len;
+
+ if (dotu) {
+ memmove(p, st->extension.str, st->extension.len);
+ ret->extension.str = p;
+ p += st->extension.len;
+ }
+
+ return ret;
+}
+
struct p9_stat *p9_client_stat(struct p9_fid *fid)
{
int err;
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);
- ret->name.str = p;
- p += st->name.len;
- memmove(p, st->uid.str, st->uid.len);
- ret->uid.str = p;
- p += st->uid.len;
- memmove(p, st->gid.str, st->gid.len);
- ret->gid.str = p;
- p += st->gid.len;
- memmove(p, st->muid.str, st->muid.len);
- ret->muid.str = p;
- p += st->muid.len;
-
- if (dotu) {
- memmove(p, st->extension.str, st->extension.len);
- ret->extension.str = p;
- 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);
-}