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 #ifdef CONFIG_NET_9P_DEBUG
506 if ((p9_debug_level&P9_DEBUG_FCALL) == P9_DEBUG_FCALL) {
509 p9_printfcall(buf, sizeof(buf), req->rc, c->dotu);
510 printk(KERN_NOTICE ">>> %p %s\n", c, buf);
514 if (req->rc->id == P9_RERROR) {
515 int ecode = req->rc->params.rerror.errno;
516 struct p9_str *ename = &req->rc->params.rerror.error;
518 P9_DPRINTK(P9_DEBUG_MUX, "Rerror %.*s\n", ename->len,
525 err = p9_errstr2errno(ename->str, ename->len);
527 /* string match failed */
529 PRINT_FCALL_ERROR("unknown error", req->rc);
539 P9_DPRINTK(P9_DEBUG_9P, "returning %d\n", err);
543 static struct p9_fid *p9_fid_create(struct p9_client *clnt)
548 P9_DPRINTK(P9_DEBUG_9P, "clnt %p\n", clnt);
549 fid = kmalloc(sizeof(struct p9_fid), GFP_KERNEL);
551 return ERR_PTR(-ENOMEM);
553 fid->fid = p9_idpool_get(clnt->fidpool);
559 memset(&fid->qid, 0, sizeof(struct p9_qid));
562 fid->uid = current->fsuid;
566 spin_lock(&clnt->lock);
567 list_add(&fid->flist, &clnt->fidlist);
568 spin_unlock(&clnt->lock);
577 static void p9_fid_destroy(struct p9_fid *fid)
579 struct p9_client *clnt;
581 P9_DPRINTK(P9_DEBUG_9P, "fid %d\n", fid->fid);
583 p9_idpool_put(fid->fid, clnt->fidpool);
584 spin_lock(&clnt->lock);
585 list_del(&fid->flist);
586 spin_unlock(&clnt->lock);
590 static int p9_client_version(struct p9_client *clnt)
593 struct p9_fcall *tc, *rc;
594 struct p9_str *version;
596 P9_DPRINTK(P9_DEBUG_9P, "%p\n", clnt);
601 tc = p9_create_tversion(clnt->msize,
602 clnt->dotu ? "9P2000.u" : "9P2000");
609 err = p9_client_rpc(clnt, tc, &rc);
613 version = &rc->params.rversion.version;
614 if (version->len == 8 && !memcmp(version->str, "9P2000.u", 8))
616 else if (version->len == 6 && !memcmp(version->str, "9P2000", 6))
623 if (rc->params.rversion.msize < clnt->msize)
624 clnt->msize = rc->params.rversion.msize;
632 EXPORT_SYMBOL(p9_client_auth);
634 struct p9_client *p9_client_create(const char *dev_name, char *options)
637 struct p9_client *clnt;
640 clnt = kmalloc(sizeof(struct p9_client), GFP_KERNEL);
642 return ERR_PTR(-ENOMEM);
644 clnt->trans_mod = NULL;
646 spin_lock_init(&clnt->lock);
647 INIT_LIST_HEAD(&clnt->fidlist);
648 clnt->fidpool = p9_idpool_create();
649 if (IS_ERR(clnt->fidpool)) {
650 err = PTR_ERR(clnt->fidpool);
651 clnt->fidpool = NULL;
657 err = parse_opts(options, clnt);
661 if (clnt->trans_mod == NULL) {
662 err = -EPROTONOSUPPORT;
663 P9_DPRINTK(P9_DEBUG_ERROR,
664 "No transport defined or default transport\n");
668 P9_DPRINTK(P9_DEBUG_9P, "clnt %p trans %p msize %d dotu %d\n",
669 clnt, clnt->trans_mod, clnt->msize, clnt->dotu);
671 err = clnt->trans_mod->create(clnt, dev_name, options);
675 if ((clnt->msize+P9_IOHDRSZ) > clnt->trans_mod->maxsize)
676 clnt->msize = clnt->trans_mod->maxsize-P9_IOHDRSZ;
678 err = p9_client_version(clnt);
685 p9_client_destroy(clnt);
688 EXPORT_SYMBOL(p9_client_create);
690 void p9_client_destroy(struct p9_client *clnt)
692 struct p9_fid *fid, *fidptr;
694 P9_DPRINTK(P9_DEBUG_9P, "clnt %p\n", clnt);
697 clnt->trans_mod->close(clnt);
699 v9fs_put_trans(clnt->trans_mod);
701 list_for_each_entry_safe(fid, fidptr, &clnt->fidlist, flist)
705 p9_idpool_destroy(clnt->fidpool);
707 p9_tag_cleanup(clnt);
711 EXPORT_SYMBOL(p9_client_destroy);
713 void p9_client_disconnect(struct p9_client *clnt)
715 P9_DPRINTK(P9_DEBUG_9P, "clnt %p\n", clnt);
716 clnt->status = Disconnected;
718 EXPORT_SYMBOL(p9_client_disconnect);
720 struct p9_fid *p9_client_attach(struct p9_client *clnt, struct p9_fid *afid,
721 char *uname, u32 n_uname, char *aname)
724 struct p9_fcall *tc, *rc;
727 P9_DPRINTK(P9_DEBUG_9P, "clnt %p afid %d uname %s aname %s\n",
728 clnt, afid?afid->fid:-1, uname, aname);
733 fid = p9_fid_create(clnt);
740 tc = p9_create_tattach(fid->fid, afid?afid->fid:P9_NOFID, uname, aname,
741 n_uname, clnt->dotu);
748 err = p9_client_rpc(clnt, tc, &rc);
752 memmove(&fid->qid, &rc->params.rattach.qid, sizeof(struct p9_qid));
764 EXPORT_SYMBOL(p9_client_attach);
766 struct p9_fid *p9_client_auth(struct p9_client *clnt, char *uname,
767 u32 n_uname, char *aname)
770 struct p9_fcall *tc, *rc;
773 P9_DPRINTK(P9_DEBUG_9P, "clnt %p uname %s aname %s\n", clnt, uname,
779 fid = p9_fid_create(clnt);
786 tc = p9_create_tauth(fid->fid, uname, aname, n_uname, clnt->dotu);
793 err = p9_client_rpc(clnt, tc, &rc);
797 memmove(&fid->qid, &rc->params.rauth.qid, sizeof(struct p9_qid));
809 EXPORT_SYMBOL(p9_client_auth);
811 struct p9_fid *p9_client_walk(struct p9_fid *oldfid, int nwname, char **wnames,
815 struct p9_fcall *tc, *rc;
816 struct p9_client *clnt;
819 P9_DPRINTK(P9_DEBUG_9P, "fid %d nwname %d wname[0] %s\n",
820 oldfid->fid, nwname, wnames?wnames[0]:NULL);
826 fid = p9_fid_create(clnt);
833 fid->uid = oldfid->uid;
837 tc = p9_create_twalk(oldfid->fid, fid->fid, nwname, wnames);
844 err = p9_client_rpc(clnt, tc, &rc);
846 if (rc && rc->id == P9_RWALK)
852 if (rc->params.rwalk.nwqid != nwname) {
859 &rc->params.rwalk.wqids[rc->params.rwalk.nwqid - 1],
860 sizeof(struct p9_qid));
862 fid->qid = oldfid->qid;
872 tc = p9_create_tclunk(fid->fid);
879 p9_client_rpc(clnt, tc, &rc);
884 if (fid && (fid != oldfid))
889 EXPORT_SYMBOL(p9_client_walk);
891 int p9_client_open(struct p9_fid *fid, int mode)
894 struct p9_fcall *tc, *rc;
895 struct p9_client *clnt;
897 P9_DPRINTK(P9_DEBUG_9P, "fid %d mode %d\n", fid->fid, mode);
906 tc = p9_create_topen(fid->fid, mode);
913 err = p9_client_rpc(clnt, tc, &rc);
918 fid->iounit = rc->params.ropen.iounit;
925 EXPORT_SYMBOL(p9_client_open);
927 int p9_client_fcreate(struct p9_fid *fid, char *name, u32 perm, int mode,
931 struct p9_fcall *tc, *rc;
932 struct p9_client *clnt;
934 P9_DPRINTK(P9_DEBUG_9P, "fid %d name %s perm %d mode %d\n", fid->fid,
944 tc = p9_create_tcreate(fid->fid, name, perm, mode, extension,
952 err = p9_client_rpc(clnt, tc, &rc);
957 fid->iounit = rc->params.ropen.iounit;
964 EXPORT_SYMBOL(p9_client_fcreate);
966 int p9_client_clunk(struct p9_fid *fid)
969 struct p9_fcall *tc, *rc;
970 struct p9_client *clnt;
972 P9_DPRINTK(P9_DEBUG_9P, "fid %d\n", fid->fid);
978 tc = p9_create_tclunk(fid->fid);
985 err = p9_client_rpc(clnt, tc, &rc);
996 EXPORT_SYMBOL(p9_client_clunk);
998 int p9_client_remove(struct p9_fid *fid)
1001 struct p9_fcall *tc, *rc;
1002 struct p9_client *clnt;
1004 P9_DPRINTK(P9_DEBUG_9P, "fid %d\n", fid->fid);
1010 tc = p9_create_tremove(fid->fid);
1017 err = p9_client_rpc(clnt, tc, &rc);
1021 p9_fid_destroy(fid);
1028 EXPORT_SYMBOL(p9_client_remove);
1031 p9_client_read(struct p9_fid *fid, char *data, char __user *udata, u64 offset,
1034 int err, n, rsize, total;
1035 struct p9_fcall *tc, *rc;
1036 struct p9_client *clnt;
1038 P9_DPRINTK(P9_DEBUG_9P, "fid %d offset %llu %d\n", fid->fid,
1039 (long long unsigned) offset, count);
1046 rsize = fid->iounit;
1047 if (!rsize || rsize > clnt->msize-P9_IOHDRSZ)
1048 rsize = clnt->msize - P9_IOHDRSZ;
1054 tc = p9_create_tread(fid->fid, offset, rsize);
1061 err = p9_client_rpc(clnt, tc, &rc);
1065 n = rc->params.rread.count;
1070 memmove(data, rc->params.rread.data, n);
1075 err = copy_to_user(udata, rc->params.rread.data, n);
1090 } while (count > 0 && n == rsize);
1099 EXPORT_SYMBOL(p9_client_read);
1102 p9_client_write(struct p9_fid *fid, char *data, const char __user *udata,
1103 u64 offset, u32 count)
1105 int err, n, rsize, total;
1106 struct p9_fcall *tc, *rc;
1107 struct p9_client *clnt;
1109 P9_DPRINTK(P9_DEBUG_9P, "fid %d offset %llu count %d\n", fid->fid,
1110 (long long unsigned) offset, count);
1117 rsize = fid->iounit;
1118 if (!rsize || rsize > clnt->msize-P9_IOHDRSZ)
1119 rsize = clnt->msize - P9_IOHDRSZ;
1126 tc = p9_create_twrite(fid->fid, offset, rsize, data);
1128 tc = p9_create_twrite_u(fid->fid, offset, rsize, udata);
1135 err = p9_client_rpc(clnt, tc, &rc);
1139 n = rc->params.rread.count;
1153 } while (count > 0);
1162 EXPORT_SYMBOL(p9_client_write);
1164 static struct p9_stat *p9_clone_stat(struct p9_stat *st, int dotu)
1168 struct p9_stat *ret;
1170 n = sizeof(struct p9_stat) + st->name.len + st->uid.len + st->gid.len +
1174 n += st->extension.len;
1176 ret = kmalloc(n, GFP_KERNEL);
1178 return ERR_PTR(-ENOMEM);
1180 memmove(ret, st, sizeof(struct p9_stat));
1181 p = ((char *) ret) + sizeof(struct p9_stat);
1182 memmove(p, st->name.str, st->name.len);
1185 memmove(p, st->uid.str, st->uid.len);
1188 memmove(p, st->gid.str, st->gid.len);
1191 memmove(p, st->muid.str, st->muid.len);
1196 memmove(p, st->extension.str, st->extension.len);
1197 ret->extension.str = p;
1198 p += st->extension.len;
1204 struct p9_stat *p9_client_stat(struct p9_fid *fid)
1207 struct p9_fcall *tc, *rc;
1208 struct p9_client *clnt;
1209 struct p9_stat *ret;
1211 P9_DPRINTK(P9_DEBUG_9P, "fid %d\n", fid->fid);
1218 tc = p9_create_tstat(fid->fid);
1225 err = p9_client_rpc(clnt, tc, &rc);
1229 ret = p9_clone_stat(&rc->params.rstat.stat, clnt->dotu);
1244 return ERR_PTR(err);
1246 EXPORT_SYMBOL(p9_client_stat);
1248 int p9_client_wstat(struct p9_fid *fid, struct p9_wstat *wst)
1251 struct p9_fcall *tc, *rc;
1252 struct p9_client *clnt;
1254 P9_DPRINTK(P9_DEBUG_9P, "fid %d\n", fid->fid);
1260 tc = p9_create_twstat(fid->fid, wst, clnt->dotu);
1267 err = p9_client_rpc(clnt, tc, &rc);
1274 EXPORT_SYMBOL(p9_client_wstat);