6 * Copyright (C) 2008 by Eric Van Hensbergen <ericvh@gmail.com>
7 * Copyright (C) 2007 by Latchesar Ionkov <lucho@ionkov.net>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2
11 * as published by the Free Software Foundation.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to:
20 * Free Software Foundation
21 * 51 Franklin Street, Fifth Floor
22 * Boston, MA 02111-1301 USA
26 #include <linux/module.h>
27 #include <linux/errno.h>
29 #include <linux/poll.h>
30 #include <linux/idr.h>
31 #include <linux/mutex.h>
32 #include <linux/sched.h>
33 #include <linux/uaccess.h>
34 #include <net/9p/9p.h>
35 #include <linux/parser.h>
36 #include <net/9p/client.h>
37 #include <net/9p/transport.h>
40 * Client Option Parsing (code inspired by NFS code)
41 * - a little lazy - parse all client options
51 static const match_table_t tokens = {
52 {Opt_msize, "msize=%u"},
53 {Opt_legacy, "noextend"},
54 {Opt_trans, "trans=%s"},
59 p9_client_rpc(struct p9_client *c, struct p9_fcall *tc, struct p9_fcall **rc);
62 * v9fs_parse_options - parse mount options into session structure
63 * @options: options string passed from mount
64 * @v9ses: existing v9fs session information
66 * Return 0 upon success, -ERRNO upon failure
69 static int parse_opts(char *opts, struct p9_client *clnt)
73 substring_t args[MAX_OPT_ARGS];
83 options = kstrdup(opts, GFP_KERNEL);
85 P9_DPRINTK(P9_DEBUG_ERROR,
86 "failed to allocate copy of option string\n");
90 while ((p = strsep(&options, ",")) != NULL) {
94 token = match_token(p, tokens, args);
95 if (token < Opt_trans) {
96 int r = match_int(&args[0], &option);
98 P9_DPRINTK(P9_DEBUG_ERROR,
99 "integer field, but no integer?\n");
106 clnt->msize = option;
109 clnt->trans_mod = v9fs_get_trans_by_name(&args[0]);
119 if (!clnt->trans_mod)
120 clnt->trans_mod = v9fs_get_default_trans();
127 * p9_tag_alloc - lookup/allocate a request by tag
128 * @c: client session to lookup tag within
129 * @tag: numeric id for transaction
131 * this is a simple array lookup, but will grow the
132 * request_slots as necessary to accomodate transaction
133 * ids which did not previously have a slot.
135 * this code relies on the client spinlock to manage locks, its
136 * possible we should switch to something else, but I'd rather
137 * stick with something low-overhead for the common case.
141 struct p9_req_t *p9_tag_alloc(struct p9_client *c, u16 tag)
146 /* This looks up the original request by tag so we know which
147 * buffer to read the data into */
150 if (tag >= c->max_tag) {
151 spin_lock_irqsave(&c->lock, flags);
152 /* check again since original check was outside of lock */
153 while (tag >= c->max_tag) {
154 row = (tag / P9_ROW_MAXTAG);
155 c->reqs[row] = kcalloc(P9_ROW_MAXTAG,
156 sizeof(struct p9_req_t), GFP_ATOMIC);
159 printk(KERN_ERR "Couldn't grow tag array\n");
162 for (col = 0; col < P9_ROW_MAXTAG; col++) {
163 c->reqs[row][col].status = REQ_STATUS_IDLE;
164 c->reqs[row][col].flush_tag = P9_NOTAG;
165 c->reqs[row][col].wq = kmalloc(
166 sizeof(wait_queue_head_t), GFP_ATOMIC);
167 if (!c->reqs[row][col].wq) {
169 "Couldn't grow tag array\n");
172 init_waitqueue_head(c->reqs[row][col].wq);
174 c->max_tag += P9_ROW_MAXTAG;
176 spin_unlock_irqrestore(&c->lock, flags);
178 row = tag / P9_ROW_MAXTAG;
179 col = tag % P9_ROW_MAXTAG;
181 c->reqs[row][col].status = REQ_STATUS_ALLOC;
182 c->reqs[row][col].flush_tag = P9_NOTAG;
184 return &c->reqs[row][col];
186 EXPORT_SYMBOL(p9_tag_alloc);
189 * p9_tag_lookup - lookup a request by tag
190 * @c: client session to lookup tag within
191 * @tag: numeric id for transaction
195 struct p9_req_t *p9_tag_lookup(struct p9_client *c, u16 tag)
199 /* This looks up the original request by tag so we know which
200 * buffer to read the data into */
203 BUG_ON(tag >= c->max_tag);
205 row = tag / P9_ROW_MAXTAG;
206 col = tag % P9_ROW_MAXTAG;
208 return &c->reqs[row][col];
210 EXPORT_SYMBOL(p9_tag_lookup);
213 * p9_tag_init - setup tags structure and contents
214 * @tags: tags structure from the client struct
216 * This initializes the tags structure for each client instance.
220 static int p9_tag_init(struct p9_client *c)
224 c->tagpool = p9_idpool_create();
225 if (IS_ERR(c->tagpool)) {
226 err = PTR_ERR(c->tagpool);
231 p9_idpool_get(c->tagpool); /* reserve tag 0 */
239 * p9_tag_cleanup - cleans up tags structure and reclaims resources
240 * @tags: tags structure from the client struct
242 * This frees resources associated with the tags structure
245 static void p9_tag_cleanup(struct p9_client *c)
249 /* check to insure all requests are idle */
250 for (row = 0; row < (c->max_tag/P9_ROW_MAXTAG); row++) {
251 for (col = 0; col < P9_ROW_MAXTAG; col++) {
252 if (c->reqs[row][col].status != REQ_STATUS_IDLE) {
253 P9_DPRINTK(P9_DEBUG_MUX,
254 "Attempting to cleanup non-free tag %d,%d\n",
256 /* TODO: delay execution of cleanup */
263 p9_idpool_destroy(c->tagpool);
265 /* free requests associated with tags */
266 for (row = 0; row < (c->max_tag/P9_ROW_MAXTAG); row++) {
267 for (col = 0; col < P9_ROW_MAXTAG; col++)
268 kfree(c->reqs[row][col].wq);
275 * p9_client_flush - flush (cancel) a request
277 * req: request to cancel
279 * This sents a flush for a particular requests and links
280 * the flush request to the original request. The current
281 * code only supports a single flush request although the protocol
282 * allows for multiple flush requests to be sent for a single request.
286 static int p9_client_flush(struct p9_client *c, struct p9_req_t *req)
288 struct p9_fcall *tc, *rc = NULL;
291 P9_DPRINTK(P9_DEBUG_9P, "client %p tag %d\n", c, req->tc->tag);
293 tc = p9_create_tflush(req->tc->tag);
297 err = p9_client_rpc(c, tc, &rc);
299 /* we don't free anything here because RPC isn't complete */
305 * p9_free_req - free a request and clean-up as necessary
307 * r: request to release
311 void p9_free_req(struct p9_client *c, struct p9_req_t *r)
313 r->flush_tag = P9_NOTAG;
314 r->status = REQ_STATUS_IDLE;
315 if (r->tc->tag != P9_NOTAG && p9_idpool_check(r->tc->tag, c->tagpool))
316 p9_idpool_put(r->tc->tag, c->tagpool);
318 /* if this was a flush request we have to free response fcall */
319 if (r->tc->id == P9_TFLUSH) {
326 * p9_client_cb - call back from transport to client
328 * req: request received
331 void p9_client_cb(struct p9_client *c, struct p9_req_t *req)
333 struct p9_req_t *other_req;
336 P9_DPRINTK(P9_DEBUG_MUX, ": %d\n", req->tc->tag);
338 if (req->status == REQ_STATUS_ERROR)
341 if (req->tc->id == P9_TFLUSH) { /* flush receive path */
342 P9_DPRINTK(P9_DEBUG_MUX, "flush: %d\n", req->tc->tag);
343 spin_lock_irqsave(&c->lock, flags);
344 other_req = p9_tag_lookup(c, req->tc->params.tflush.oldtag);
345 if (other_req->flush_tag != req->tc->tag) /* stale flush */
346 spin_unlock_irqrestore(&c->lock, flags);
348 BUG_ON(other_req->status != REQ_STATUS_FLSH);
349 other_req->status = REQ_STATUS_FLSHD;
350 spin_unlock_irqrestore(&c->lock, flags);
351 wake_up(other_req->wq);
354 } else { /* normal receive path */
355 P9_DPRINTK(P9_DEBUG_MUX, "normal: %d\n", req->tc->tag);
356 spin_lock_irqsave(&c->lock, flags);
357 if (req->status != REQ_STATUS_FLSHD)
358 req->status = REQ_STATUS_RCVD;
359 req->flush_tag = P9_NOTAG;
360 spin_unlock_irqrestore(&c->lock, flags);
362 P9_DPRINTK(P9_DEBUG_MUX, "wakeup: %d\n", req->tc->tag);
365 EXPORT_SYMBOL(p9_client_cb);
368 * p9_client_rpc - issue a request and wait for a response
370 * @tc: &p9_fcall request to transmit
371 * @rc: &p9_fcall to put reponse into
373 * Returns 0 on success, error code on failure
377 p9_client_rpc(struct p9_client *c, struct p9_fcall *tc, struct p9_fcall **rc)
381 struct p9_req_t *req;
386 P9_DPRINTK(P9_DEBUG_9P, "client %p tc %p rc %p\n", c, tc, rc);
388 if (c->status != Connected)
391 if (signal_pending(current)) {
393 clear_thread_flag(TIF_SIGPENDING);
398 if (tc->id != P9_TVERSION) {
399 tag = p9_idpool_get(c->tagpool);
404 req = p9_tag_alloc(c, tag);
406 /* if this is a flush request, backlink flush request now to
407 * avoid race conditions later. */
408 if (tc->id == P9_TFLUSH) {
409 struct p9_req_t *other_req =
410 p9_tag_lookup(c, tc->params.tflush.oldtag);
411 if (other_req->status == REQ_STATUS_FLSH)
412 other_req->flush_tag = tag;
418 * if client passed in a pre-allocated response fcall struct
419 * then we just use that, otherwise we allocate one.
426 if (req->rc == NULL) {
427 req->rc = kmalloc(sizeof(struct p9_fcall) + c->msize,
431 p9_idpool_put(tag, c->tagpool);
438 rdata = (char *)req->rc+sizeof(struct p9_fcall);
441 P9_DPRINTK(P9_DEBUG_9P, "request: tc: %p rc: %p\n", req->tc, req->rc);
443 err = c->trans_mod->request(c, req);
445 c->status = Disconnected;
449 /* if it was a flush we just transmitted, return our tag */
450 if (tc->id == P9_TFLUSH)
453 P9_DPRINTK(P9_DEBUG_9P, "wait %p tag: %d\n", req->wq, tag);
454 err = wait_event_interruptible(*req->wq,
455 req->status >= REQ_STATUS_RCVD);
456 P9_DPRINTK(P9_DEBUG_9P, "wait %p tag: %d returned %d (flushed=%d)\n",
457 req->wq, tag, err, flushed);
459 if (req->status == REQ_STATUS_ERROR) {
460 P9_DPRINTK(P9_DEBUG_9P, "req_status error %d\n", req->t_err);
462 } else if (err == -ERESTARTSYS && flushed) {
463 P9_DPRINTK(P9_DEBUG_9P, "flushed - going again\n");
465 } else if (req->status == REQ_STATUS_FLSHD) {
466 P9_DPRINTK(P9_DEBUG_9P, "flushed - erestartsys\n");
470 if ((err == -ERESTARTSYS) && (c->status == Connected) && (!flushed)) {
471 P9_DPRINTK(P9_DEBUG_9P, "flushing\n");
472 spin_lock_irqsave(&c->lock, flags);
473 if (req->status == REQ_STATUS_SENT)
474 req->status = REQ_STATUS_FLSH;
475 spin_unlock_irqrestore(&c->lock, flags);
478 clear_thread_flag(TIF_SIGPENDING);
480 if (c->trans_mod->cancel(c, req)) {
481 err = p9_client_flush(c, req);
488 spin_lock_irqsave(¤t->sighand->siglock, flags);
490 spin_unlock_irqrestore(¤t->sighand->siglock, flags);
496 size = le32_to_cpu(*(__le32 *) rdata);
498 err = p9_deserialize_fcall(rdata, size, req->rc, c->dotu);
500 P9_DPRINTK(P9_DEBUG_9P,
501 "9p debug: client rpc deserialize returned %d\n", err);
505 if (req->rc->id == P9_RERROR) {
506 int ecode = req->rc->params.rerror.errno;
507 struct p9_str *ename = &req->rc->params.rerror.error;
509 P9_DPRINTK(P9_DEBUG_MUX, "Rerror %.*s\n", ename->len,
516 err = p9_errstr2errno(ename->str, ename->len);
518 /* string match failed */
520 PRINT_FCALL_ERROR("unknown error", req->rc);
530 P9_DPRINTK(P9_DEBUG_9P, "returning %d\n", err);
534 static struct p9_fid *p9_fid_create(struct p9_client *clnt)
539 P9_DPRINTK(P9_DEBUG_9P, "clnt %p\n", clnt);
540 fid = kmalloc(sizeof(struct p9_fid), GFP_KERNEL);
542 return ERR_PTR(-ENOMEM);
544 fid->fid = p9_idpool_get(clnt->fidpool);
550 memset(&fid->qid, 0, sizeof(struct p9_qid));
553 fid->uid = current->fsuid;
557 spin_lock(&clnt->lock);
558 list_add(&fid->flist, &clnt->fidlist);
559 spin_unlock(&clnt->lock);
568 static void p9_fid_destroy(struct p9_fid *fid)
570 struct p9_client *clnt;
572 P9_DPRINTK(P9_DEBUG_9P, "fid %d\n", fid->fid);
574 p9_idpool_put(fid->fid, clnt->fidpool);
575 spin_lock(&clnt->lock);
576 list_del(&fid->flist);
577 spin_unlock(&clnt->lock);
581 static int p9_client_version(struct p9_client *clnt)
584 struct p9_fcall *tc, *rc;
585 struct p9_str *version;
587 P9_DPRINTK(P9_DEBUG_9P, "%p\n", clnt);
592 tc = p9_create_tversion(clnt->msize,
593 clnt->dotu ? "9P2000.u" : "9P2000");
600 err = p9_client_rpc(clnt, tc, &rc);
604 version = &rc->params.rversion.version;
605 if (version->len == 8 && !memcmp(version->str, "9P2000.u", 8))
607 else if (version->len == 6 && !memcmp(version->str, "9P2000", 6))
614 if (rc->params.rversion.msize < clnt->msize)
615 clnt->msize = rc->params.rversion.msize;
623 EXPORT_SYMBOL(p9_client_auth);
625 struct p9_client *p9_client_create(const char *dev_name, char *options)
628 struct p9_client *clnt;
631 clnt = kmalloc(sizeof(struct p9_client), GFP_KERNEL);
633 return ERR_PTR(-ENOMEM);
635 clnt->trans_mod = NULL;
637 spin_lock_init(&clnt->lock);
638 INIT_LIST_HEAD(&clnt->fidlist);
639 clnt->fidpool = p9_idpool_create();
640 if (IS_ERR(clnt->fidpool)) {
641 err = PTR_ERR(clnt->fidpool);
642 clnt->fidpool = NULL;
648 err = parse_opts(options, clnt);
652 if (clnt->trans_mod == NULL) {
653 err = -EPROTONOSUPPORT;
654 P9_DPRINTK(P9_DEBUG_ERROR,
655 "No transport defined or default transport\n");
659 P9_DPRINTK(P9_DEBUG_9P, "clnt %p trans %p msize %d dotu %d\n",
660 clnt, clnt->trans_mod, clnt->msize, clnt->dotu);
662 err = clnt->trans_mod->create(clnt, dev_name, options);
666 if ((clnt->msize+P9_IOHDRSZ) > clnt->trans_mod->maxsize)
667 clnt->msize = clnt->trans_mod->maxsize-P9_IOHDRSZ;
669 err = p9_client_version(clnt);
676 p9_client_destroy(clnt);
679 EXPORT_SYMBOL(p9_client_create);
681 void p9_client_destroy(struct p9_client *clnt)
683 struct p9_fid *fid, *fidptr;
685 P9_DPRINTK(P9_DEBUG_9P, "clnt %p\n", clnt);
688 clnt->trans_mod->close(clnt);
690 v9fs_put_trans(clnt->trans_mod);
692 list_for_each_entry_safe(fid, fidptr, &clnt->fidlist, flist)
696 p9_idpool_destroy(clnt->fidpool);
698 p9_tag_cleanup(clnt);
702 EXPORT_SYMBOL(p9_client_destroy);
704 void p9_client_disconnect(struct p9_client *clnt)
706 P9_DPRINTK(P9_DEBUG_9P, "clnt %p\n", clnt);
707 clnt->status = Disconnected;
709 EXPORT_SYMBOL(p9_client_disconnect);
711 struct p9_fid *p9_client_attach(struct p9_client *clnt, struct p9_fid *afid,
712 char *uname, u32 n_uname, char *aname)
715 struct p9_fcall *tc, *rc;
718 P9_DPRINTK(P9_DEBUG_9P, "clnt %p afid %d uname %s aname %s\n",
719 clnt, afid?afid->fid:-1, uname, aname);
724 fid = p9_fid_create(clnt);
731 tc = p9_create_tattach(fid->fid, afid?afid->fid:P9_NOFID, uname, aname,
732 n_uname, clnt->dotu);
739 err = p9_client_rpc(clnt, tc, &rc);
743 memmove(&fid->qid, &rc->params.rattach.qid, sizeof(struct p9_qid));
755 EXPORT_SYMBOL(p9_client_attach);
757 struct p9_fid *p9_client_auth(struct p9_client *clnt, char *uname,
758 u32 n_uname, char *aname)
761 struct p9_fcall *tc, *rc;
764 P9_DPRINTK(P9_DEBUG_9P, "clnt %p uname %s aname %s\n", clnt, uname,
770 fid = p9_fid_create(clnt);
777 tc = p9_create_tauth(fid->fid, uname, aname, n_uname, clnt->dotu);
784 err = p9_client_rpc(clnt, tc, &rc);
788 memmove(&fid->qid, &rc->params.rauth.qid, sizeof(struct p9_qid));
800 EXPORT_SYMBOL(p9_client_auth);
802 struct p9_fid *p9_client_walk(struct p9_fid *oldfid, int nwname, char **wnames,
806 struct p9_fcall *tc, *rc;
807 struct p9_client *clnt;
810 P9_DPRINTK(P9_DEBUG_9P, "fid %d nwname %d wname[0] %s\n",
811 oldfid->fid, nwname, wnames?wnames[0]:NULL);
817 fid = p9_fid_create(clnt);
824 fid->uid = oldfid->uid;
828 tc = p9_create_twalk(oldfid->fid, fid->fid, nwname, wnames);
835 err = p9_client_rpc(clnt, tc, &rc);
837 if (rc && rc->id == P9_RWALK)
843 if (rc->params.rwalk.nwqid != nwname) {
850 &rc->params.rwalk.wqids[rc->params.rwalk.nwqid - 1],
851 sizeof(struct p9_qid));
853 fid->qid = oldfid->qid;
863 tc = p9_create_tclunk(fid->fid);
870 p9_client_rpc(clnt, tc, &rc);
875 if (fid && (fid != oldfid))
880 EXPORT_SYMBOL(p9_client_walk);
882 int p9_client_open(struct p9_fid *fid, int mode)
885 struct p9_fcall *tc, *rc;
886 struct p9_client *clnt;
888 P9_DPRINTK(P9_DEBUG_9P, "fid %d mode %d\n", fid->fid, mode);
897 tc = p9_create_topen(fid->fid, mode);
904 err = p9_client_rpc(clnt, tc, &rc);
909 fid->iounit = rc->params.ropen.iounit;
916 EXPORT_SYMBOL(p9_client_open);
918 int p9_client_fcreate(struct p9_fid *fid, char *name, u32 perm, int mode,
922 struct p9_fcall *tc, *rc;
923 struct p9_client *clnt;
925 P9_DPRINTK(P9_DEBUG_9P, "fid %d name %s perm %d mode %d\n", fid->fid,
935 tc = p9_create_tcreate(fid->fid, name, perm, mode, extension,
943 err = p9_client_rpc(clnt, tc, &rc);
948 fid->iounit = rc->params.ropen.iounit;
955 EXPORT_SYMBOL(p9_client_fcreate);
957 int p9_client_clunk(struct p9_fid *fid)
960 struct p9_fcall *tc, *rc;
961 struct p9_client *clnt;
963 P9_DPRINTK(P9_DEBUG_9P, "fid %d\n", fid->fid);
969 tc = p9_create_tclunk(fid->fid);
976 err = p9_client_rpc(clnt, tc, &rc);
987 EXPORT_SYMBOL(p9_client_clunk);
989 int p9_client_remove(struct p9_fid *fid)
992 struct p9_fcall *tc, *rc;
993 struct p9_client *clnt;
995 P9_DPRINTK(P9_DEBUG_9P, "fid %d\n", fid->fid);
1001 tc = p9_create_tremove(fid->fid);
1008 err = p9_client_rpc(clnt, tc, &rc);
1012 p9_fid_destroy(fid);
1019 EXPORT_SYMBOL(p9_client_remove);
1022 p9_client_read(struct p9_fid *fid, char *data, char __user *udata, u64 offset,
1025 int err, n, rsize, total;
1026 struct p9_fcall *tc, *rc;
1027 struct p9_client *clnt;
1029 P9_DPRINTK(P9_DEBUG_9P, "fid %d offset %llu %d\n", fid->fid,
1030 (long long unsigned) offset, count);
1037 rsize = fid->iounit;
1038 if (!rsize || rsize > clnt->msize-P9_IOHDRSZ)
1039 rsize = clnt->msize - P9_IOHDRSZ;
1045 tc = p9_create_tread(fid->fid, offset, rsize);
1052 err = p9_client_rpc(clnt, tc, &rc);
1056 n = rc->params.rread.count;
1061 memmove(data, rc->params.rread.data, n);
1066 err = copy_to_user(udata, rc->params.rread.data, n);
1081 } while (count > 0 && n == rsize);
1090 EXPORT_SYMBOL(p9_client_read);
1093 p9_client_write(struct p9_fid *fid, char *data, const char __user *udata,
1094 u64 offset, u32 count)
1096 int err, n, rsize, total;
1097 struct p9_fcall *tc, *rc;
1098 struct p9_client *clnt;
1100 P9_DPRINTK(P9_DEBUG_9P, "fid %d offset %llu count %d\n", fid->fid,
1101 (long long unsigned) offset, count);
1108 rsize = fid->iounit;
1109 if (!rsize || rsize > clnt->msize-P9_IOHDRSZ)
1110 rsize = clnt->msize - P9_IOHDRSZ;
1117 tc = p9_create_twrite(fid->fid, offset, rsize, data);
1119 tc = p9_create_twrite_u(fid->fid, offset, rsize, udata);
1126 err = p9_client_rpc(clnt, tc, &rc);
1130 n = rc->params.rread.count;
1144 } while (count > 0);
1153 EXPORT_SYMBOL(p9_client_write);
1155 static struct p9_stat *p9_clone_stat(struct p9_stat *st, int dotu)
1159 struct p9_stat *ret;
1161 n = sizeof(struct p9_stat) + st->name.len + st->uid.len + st->gid.len +
1165 n += st->extension.len;
1167 ret = kmalloc(n, GFP_KERNEL);
1169 return ERR_PTR(-ENOMEM);
1171 memmove(ret, st, sizeof(struct p9_stat));
1172 p = ((char *) ret) + sizeof(struct p9_stat);
1173 memmove(p, st->name.str, st->name.len);
1176 memmove(p, st->uid.str, st->uid.len);
1179 memmove(p, st->gid.str, st->gid.len);
1182 memmove(p, st->muid.str, st->muid.len);
1187 memmove(p, st->extension.str, st->extension.len);
1188 ret->extension.str = p;
1189 p += st->extension.len;
1195 struct p9_stat *p9_client_stat(struct p9_fid *fid)
1198 struct p9_fcall *tc, *rc;
1199 struct p9_client *clnt;
1200 struct p9_stat *ret;
1202 P9_DPRINTK(P9_DEBUG_9P, "fid %d\n", fid->fid);
1209 tc = p9_create_tstat(fid->fid);
1216 err = p9_client_rpc(clnt, tc, &rc);
1220 ret = p9_clone_stat(&rc->params.rstat.stat, clnt->dotu);
1235 return ERR_PTR(err);
1237 EXPORT_SYMBOL(p9_client_stat);
1239 int p9_client_wstat(struct p9_fid *fid, struct p9_wstat *wst)
1242 struct p9_fcall *tc, *rc;
1243 struct p9_client *clnt;
1245 P9_DPRINTK(P9_DEBUG_9P, "fid %d\n", fid->fid);
1251 tc = p9_create_twstat(fid->fid, wst, clnt->dotu);
1258 err = p9_client_rpc(clnt, tc, &rc);
1265 EXPORT_SYMBOL(p9_client_wstat);