blob: f2d07ef9e6a4df8c62dc839923baf5b72d1042be [file] [log] [blame]
Latchesar Ionkovbd238fb2007-07-10 17:57:28 -05001/*
2 * net/9p/clnt.c
3 *
4 * 9P Client
5 *
Eric Van Hensbergen8a0dc952008-02-06 19:25:03 -06006 * Copyright (C) 2008 by Eric Van Hensbergen <ericvh@gmail.com>
Latchesar Ionkovbd238fb2007-07-10 17:57:28 -05007 * Copyright (C) 2007 by Latchesar Ionkov <lucho@ionkov.net>
8 *
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.
12 *
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.
17 *
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
23 *
24 */
25
26#include <linux/module.h>
27#include <linux/errno.h>
28#include <linux/fs.h>
Eric Van Hensbergen8a0dc952008-02-06 19:25:03 -060029#include <linux/poll.h>
Latchesar Ionkovbd238fb2007-07-10 17:57:28 -050030#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>
Eric Van Hensbergenfb0466c2007-10-17 14:31:07 -050035#include <linux/parser.h>
Latchesar Ionkovbd238fb2007-07-10 17:57:28 -050036#include <net/9p/client.h>
Eric Van Hensbergen8b81ef52008-10-13 18:45:25 -050037#include <net/9p/transport.h>
Latchesar Ionkovbd238fb2007-07-10 17:57:28 -050038
Eric Van Hensbergen8a0dc952008-02-06 19:25:03 -060039/*
40 * Client Option Parsing (code inspired by NFS code)
41 * - a little lazy - parse all client options
42 */
43
44enum {
45 Opt_msize,
46 Opt_trans,
47 Opt_legacy,
48 Opt_err,
49};
50
Steven Whitehousea447c092008-10-13 10:46:57 +010051static const match_table_t tokens = {
Eric Van Hensbergen8a0dc952008-02-06 19:25:03 -060052 {Opt_msize, "msize=%u"},
53 {Opt_legacy, "noextend"},
54 {Opt_trans, "trans=%s"},
55 {Opt_err, NULL},
56};
57
58/**
59 * v9fs_parse_options - parse mount options into session structure
60 * @options: options string passed from mount
61 * @v9ses: existing v9fs session information
62 *
Eric Van Hensbergenbb8ffdf2008-03-07 10:53:53 -060063 * Return 0 upon success, -ERRNO upon failure
Eric Van Hensbergen8a0dc952008-02-06 19:25:03 -060064 */
65
Eric Van Hensbergenbb8ffdf2008-03-07 10:53:53 -060066static int parse_opts(char *opts, struct p9_client *clnt)
Eric Van Hensbergen8a0dc952008-02-06 19:25:03 -060067{
Eric Van Hensbergenbb8ffdf2008-03-07 10:53:53 -060068 char *options;
Eric Van Hensbergen8a0dc952008-02-06 19:25:03 -060069 char *p;
70 substring_t args[MAX_OPT_ARGS];
71 int option;
Eric Van Hensbergenbb8ffdf2008-03-07 10:53:53 -060072 int ret = 0;
Eric Van Hensbergen8a0dc952008-02-06 19:25:03 -060073
Eric Van Hensbergen8a0dc952008-02-06 19:25:03 -060074 clnt->dotu = 1;
75 clnt->msize = 8192;
76
Eric Van Hensbergenbb8ffdf2008-03-07 10:53:53 -060077 if (!opts)
78 return 0;
79
80 options = kstrdup(opts, GFP_KERNEL);
81 if (!options) {
82 P9_DPRINTK(P9_DEBUG_ERROR,
83 "failed to allocate copy of option string\n");
84 return -ENOMEM;
85 }
Eric Van Hensbergen8a0dc952008-02-06 19:25:03 -060086
87 while ((p = strsep(&options, ",")) != NULL) {
88 int token;
89 if (!*p)
90 continue;
91 token = match_token(p, tokens, args);
92 if (token < Opt_trans) {
Eric Van Hensbergenbb8ffdf2008-03-07 10:53:53 -060093 int r = match_int(&args[0], &option);
94 if (r < 0) {
Eric Van Hensbergen8a0dc952008-02-06 19:25:03 -060095 P9_DPRINTK(P9_DEBUG_ERROR,
96 "integer field, but no integer?\n");
Eric Van Hensbergenbb8ffdf2008-03-07 10:53:53 -060097 ret = r;
Eric Van Hensbergen8a0dc952008-02-06 19:25:03 -060098 continue;
99 }
100 }
101 switch (token) {
102 case Opt_msize:
103 clnt->msize = option;
104 break;
105 case Opt_trans:
Tejun Heo72029fe2008-09-24 16:22:23 -0500106 clnt->trans_mod = v9fs_get_trans_by_name(&args[0]);
Eric Van Hensbergen8a0dc952008-02-06 19:25:03 -0600107 break;
108 case Opt_legacy:
109 clnt->dotu = 0;
110 break;
111 default:
112 continue;
113 }
114 }
Tejun Heo72029fe2008-09-24 16:22:23 -0500115
116 if (!clnt->trans_mod)
117 clnt->trans_mod = v9fs_get_default_trans();
118
Eric Van Hensbergenbb8ffdf2008-03-07 10:53:53 -0600119 kfree(options);
120 return ret;
Eric Van Hensbergen8a0dc952008-02-06 19:25:03 -0600121}
122
Eric Van Hensbergenfea511a2008-10-13 18:45:23 -0500123/**
124 * p9_tag_alloc - lookup/allocate a request by tag
125 * @c: client session to lookup tag within
126 * @tag: numeric id for transaction
127 *
128 * this is a simple array lookup, but will grow the
129 * request_slots as necessary to accomodate transaction
130 * ids which did not previously have a slot.
131 *
132 * this code relies on the client spinlock to manage locks, its
133 * possible we should switch to something else, but I'd rather
134 * stick with something low-overhead for the common case.
135 *
136 */
137
138struct p9_req_t *p9_tag_alloc(struct p9_client *c, u16 tag)
139{
140 unsigned long flags;
141 int row, col;
142
143 /* This looks up the original request by tag so we know which
144 * buffer to read the data into */
145 tag++;
146
147 if (tag >= c->max_tag) {
148 spin_lock_irqsave(&c->lock, flags);
149 /* check again since original check was outside of lock */
150 while (tag >= c->max_tag) {
151 row = (tag / P9_ROW_MAXTAG);
152 c->reqs[row] = kcalloc(P9_ROW_MAXTAG,
153 sizeof(struct p9_req_t), GFP_ATOMIC);
154
155 if (!c->reqs[row]) {
156 printk(KERN_ERR "Couldn't grow tag array\n");
157 BUG();
158 }
159 for (col = 0; col < P9_ROW_MAXTAG; col++) {
160 c->reqs[row][col].status = REQ_STATUS_IDLE;
161 c->reqs[row][col].flush_tag = P9_NOTAG;
162 c->reqs[row][col].wq = kmalloc(
163 sizeof(wait_queue_head_t), GFP_ATOMIC);
164 if (!c->reqs[row][col].wq) {
165 printk(KERN_ERR
166 "Couldn't grow tag array\n");
167 BUG();
168 }
169 init_waitqueue_head(c->reqs[row][col].wq);
170 }
171 c->max_tag += P9_ROW_MAXTAG;
172 }
173 spin_unlock_irqrestore(&c->lock, flags);
174 }
175 row = tag / P9_ROW_MAXTAG;
176 col = tag % P9_ROW_MAXTAG;
177
178 c->reqs[row][col].status = REQ_STATUS_ALLOC;
179 c->reqs[row][col].flush_tag = P9_NOTAG;
180
181 return &c->reqs[row][col];
182}
183EXPORT_SYMBOL(p9_tag_alloc);
184
185/**
186 * p9_tag_lookup - lookup a request by tag
187 * @c: client session to lookup tag within
188 * @tag: numeric id for transaction
189 *
190 */
191
192struct p9_req_t *p9_tag_lookup(struct p9_client *c, u16 tag)
193{
194 int row, col;
195
196 /* This looks up the original request by tag so we know which
197 * buffer to read the data into */
198 tag++;
199
200 BUG_ON(tag >= c->max_tag);
201
202 row = tag / P9_ROW_MAXTAG;
203 col = tag % P9_ROW_MAXTAG;
204
205 return &c->reqs[row][col];
206}
207EXPORT_SYMBOL(p9_tag_lookup);
208
209/**
210 * p9_tag_init - setup tags structure and contents
211 * @tags: tags structure from the client struct
212 *
213 * This initializes the tags structure for each client instance.
214 *
215 */
216
217static int p9_tag_init(struct p9_client *c)
218{
219 int err = 0;
220
221 c->tagpool = p9_idpool_create();
222 if (IS_ERR(c->tagpool)) {
223 err = PTR_ERR(c->tagpool);
224 c->tagpool = NULL;
225 goto error;
226 }
227
228 p9_idpool_get(c->tagpool); /* reserve tag 0 */
229
230 c->max_tag = 0;
231error:
232 return err;
233}
234
235/**
236 * p9_tag_cleanup - cleans up tags structure and reclaims resources
237 * @tags: tags structure from the client struct
238 *
239 * This frees resources associated with the tags structure
240 *
241 */
242static void p9_tag_cleanup(struct p9_client *c)
243{
244 int row, col;
245
246 /* check to insure all requests are idle */
247 for (row = 0; row < (c->max_tag/P9_ROW_MAXTAG); row++) {
248 for (col = 0; col < P9_ROW_MAXTAG; col++) {
249 if (c->reqs[row][col].status != REQ_STATUS_IDLE) {
250 P9_DPRINTK(P9_DEBUG_MUX,
251 "Attempting to cleanup non-free tag %d,%d\n",
252 row, col);
253 /* TODO: delay execution of cleanup */
254 return;
255 }
256 }
257 }
258
259 if (c->tagpool)
260 p9_idpool_destroy(c->tagpool);
261
262 /* free requests associated with tags */
263 for (row = 0; row < (c->max_tag/P9_ROW_MAXTAG); row++) {
264 for (col = 0; col < P9_ROW_MAXTAG; col++)
265 kfree(c->reqs[row][col].wq);
266 kfree(c->reqs[row]);
267 }
268 c->max_tag = 0;
269}
270
Eric Van Hensbergen673d62cd2008-10-13 18:45:22 -0500271/**
272 * p9_free_req - free a request and clean-up as necessary
273 * c: client state
274 * r: request to release
275 *
276 */
277
278void p9_free_req(struct p9_client *c, struct p9_req_t *r)
279{
280 r->flush_tag = P9_NOTAG;
281 r->status = REQ_STATUS_IDLE;
282 if (r->tc->tag != P9_NOTAG && p9_idpool_check(r->tc->tag, c->tagpool))
283 p9_idpool_put(r->tc->tag, c->tagpool);
284
285 /* if this was a flush request we have to free response fcall */
286 if (r->tc->id == P9_TFLUSH) {
287 kfree(r->tc);
288 kfree(r->rc);
289 }
290}
291
Eric Van Hensbergen5503ac52008-10-13 18:45:24 -0500292static struct p9_fid *p9_fid_create(struct p9_client *clnt)
293{
294 int err;
295 struct p9_fid *fid;
296
297 P9_DPRINTK(P9_DEBUG_9P, "clnt %p\n", clnt);
298 fid = kmalloc(sizeof(struct p9_fid), GFP_KERNEL);
299 if (!fid)
300 return ERR_PTR(-ENOMEM);
301
302 fid->fid = p9_idpool_get(clnt->fidpool);
303 if (fid->fid < 0) {
304 err = -ENOSPC;
305 goto error;
306 }
307
308 memset(&fid->qid, 0, sizeof(struct p9_qid));
309 fid->mode = -1;
310 fid->rdir_fpos = 0;
311 fid->rdir_pos = 0;
312 fid->rdir_fcall = NULL;
313 fid->uid = current->fsuid;
314 fid->clnt = clnt;
315 fid->aux = NULL;
316
317 spin_lock(&clnt->lock);
318 list_add(&fid->flist, &clnt->fidlist);
319 spin_unlock(&clnt->lock);
320
321 return fid;
322
323error:
324 kfree(fid);
325 return ERR_PTR(err);
326}
327
328static void p9_fid_destroy(struct p9_fid *fid)
329{
330 struct p9_client *clnt;
331
332 P9_DPRINTK(P9_DEBUG_9P, "fid %d\n", fid->fid);
333 clnt = fid->clnt;
334 p9_idpool_put(fid->fid, clnt->fidpool);
335 spin_lock(&clnt->lock);
336 list_del(&fid->flist);
337 spin_unlock(&clnt->lock);
338 kfree(fid->rdir_fcall);
339 kfree(fid);
340}
Eric Van Hensbergen8a0dc952008-02-06 19:25:03 -0600341
Eric Van Hensbergen043aba42008-02-06 19:25:09 -0600342/**
343 * p9_client_rpc - sends 9P request and waits until a response is available.
344 * The function can be interrupted.
345 * @c: client data
346 * @tc: request to be sent
347 * @rc: pointer where a pointer to the response is stored
348 */
349int
350p9_client_rpc(struct p9_client *c, struct p9_fcall *tc,
351 struct p9_fcall **rc)
352{
Eric Van Hensbergen8b81ef52008-10-13 18:45:25 -0500353 return c->trans_mod->rpc(c, tc, rc);
Eric Van Hensbergen043aba42008-02-06 19:25:09 -0600354}
355
Eric Van Hensbergen8a0dc952008-02-06 19:25:03 -0600356struct p9_client *p9_client_create(const char *dev_name, char *options)
Latchesar Ionkovbd238fb2007-07-10 17:57:28 -0500357{
358 int err, n;
359 struct p9_client *clnt;
360 struct p9_fcall *tc, *rc;
361 struct p9_str *version;
362
363 err = 0;
364 tc = NULL;
365 rc = NULL;
366 clnt = kmalloc(sizeof(struct p9_client), GFP_KERNEL);
367 if (!clnt)
368 return ERR_PTR(-ENOMEM);
369
Tejun Heo72029fe2008-09-24 16:22:23 -0500370 clnt->trans_mod = NULL;
Eric Van Hensbergenbb8ffdf2008-03-07 10:53:53 -0600371 clnt->trans = NULL;
Latchesar Ionkovbd238fb2007-07-10 17:57:28 -0500372 spin_lock_init(&clnt->lock);
Latchesar Ionkovbd238fb2007-07-10 17:57:28 -0500373 INIT_LIST_HEAD(&clnt->fidlist);
374 clnt->fidpool = p9_idpool_create();
Josef 'Jeff' Sipek728fc4ef2008-03-07 11:40:33 -0600375 if (IS_ERR(clnt->fidpool)) {
Latchesar Ionkovbd238fb2007-07-10 17:57:28 -0500376 err = PTR_ERR(clnt->fidpool);
377 clnt->fidpool = NULL;
378 goto error;
379 }
380
Eric Van Hensbergenfea511a2008-10-13 18:45:23 -0500381 p9_tag_init(clnt);
382
Eric Van Hensbergenbb8ffdf2008-03-07 10:53:53 -0600383 err = parse_opts(options, clnt);
384 if (err < 0)
385 goto error;
386
Eric Van Hensbergen8a0dc952008-02-06 19:25:03 -0600387 if (clnt->trans_mod == NULL) {
388 err = -EPROTONOSUPPORT;
389 P9_DPRINTK(P9_DEBUG_ERROR,
390 "No transport defined or default transport\n");
Latchesar Ionkovbd238fb2007-07-10 17:57:28 -0500391 goto error;
392 }
393
Eric Van Hensbergen8a0dc952008-02-06 19:25:03 -0600394 P9_DPRINTK(P9_DEBUG_9P, "clnt %p trans %p msize %d dotu %d\n",
395 clnt, clnt->trans_mod, clnt->msize, clnt->dotu);
396
397
Eric Van Hensbergen8b81ef52008-10-13 18:45:25 -0500398 err = clnt->trans_mod->create(clnt, dev_name, options);
399 if (err)
Eric Van Hensbergen8a0dc952008-02-06 19:25:03 -0600400 goto error;
Eric Van Hensbergen8a0dc952008-02-06 19:25:03 -0600401
402 if ((clnt->msize+P9_IOHDRSZ) > clnt->trans_mod->maxsize)
403 clnt->msize = clnt->trans_mod->maxsize-P9_IOHDRSZ;
404
Latchesar Ionkovbd238fb2007-07-10 17:57:28 -0500405 tc = p9_create_tversion(clnt->msize, clnt->dotu?"9P2000.u":"9P2000");
406 if (IS_ERR(tc)) {
407 err = PTR_ERR(tc);
408 tc = NULL;
409 goto error;
410 }
411
Eric Van Hensbergene2735b72008-02-06 19:25:58 -0600412 err = p9_client_rpc(clnt, tc, &rc);
Latchesar Ionkovbd238fb2007-07-10 17:57:28 -0500413 if (err)
414 goto error;
415
416 version = &rc->params.rversion.version;
417 if (version->len == 8 && !memcmp(version->str, "9P2000.u", 8))
418 clnt->dotu = 1;
419 else if (version->len == 6 && !memcmp(version->str, "9P2000", 6))
420 clnt->dotu = 0;
421 else {
422 err = -EREMOTEIO;
423 goto error;
424 }
425
426 n = rc->params.rversion.msize;
427 if (n < clnt->msize)
428 clnt->msize = n;
429
430 kfree(tc);
431 kfree(rc);
432 return clnt;
433
434error:
435 kfree(tc);
436 kfree(rc);
437 p9_client_destroy(clnt);
438 return ERR_PTR(err);
439}
440EXPORT_SYMBOL(p9_client_create);
441
442void p9_client_destroy(struct p9_client *clnt)
443{
444 struct p9_fid *fid, *fidptr;
445
446 P9_DPRINTK(P9_DEBUG_9P, "clnt %p\n", clnt);
Latchesar Ionkovbd238fb2007-07-10 17:57:28 -0500447
Eric Van Hensbergen8b81ef52008-10-13 18:45:25 -0500448 if (clnt->trans_mod)
449 clnt->trans_mod->close(clnt);
Latchesar Ionkovbd238fb2007-07-10 17:57:28 -0500450
Tejun Heo72029fe2008-09-24 16:22:23 -0500451 v9fs_put_trans(clnt->trans_mod);
452
Latchesar Ionkovbd238fb2007-07-10 17:57:28 -0500453 list_for_each_entry_safe(fid, fidptr, &clnt->fidlist, flist)
454 p9_fid_destroy(fid);
455
Eric Van Hensbergen0af88872007-07-13 16:47:58 -0500456 if (clnt->fidpool)
457 p9_idpool_destroy(clnt->fidpool);
458
Eric Van Hensbergenfea511a2008-10-13 18:45:23 -0500459 p9_tag_cleanup(clnt);
460
Latchesar Ionkovbd238fb2007-07-10 17:57:28 -0500461 kfree(clnt);
462}
463EXPORT_SYMBOL(p9_client_destroy);
464
465void p9_client_disconnect(struct p9_client *clnt)
466{
467 P9_DPRINTK(P9_DEBUG_9P, "clnt %p\n", clnt);
Eric Van Hensbergen8b81ef52008-10-13 18:45:25 -0500468 clnt->status = Disconnected;
Latchesar Ionkovbd238fb2007-07-10 17:57:28 -0500469}
470EXPORT_SYMBOL(p9_client_disconnect);
471
472struct p9_fid *p9_client_attach(struct p9_client *clnt, struct p9_fid *afid,
Latchesar Ionkovba176742007-10-17 14:31:07 -0500473 char *uname, u32 n_uname, char *aname)
Latchesar Ionkovbd238fb2007-07-10 17:57:28 -0500474{
475 int err;
476 struct p9_fcall *tc, *rc;
477 struct p9_fid *fid;
478
479 P9_DPRINTK(P9_DEBUG_9P, "clnt %p afid %d uname %s aname %s\n",
480 clnt, afid?afid->fid:-1, uname, aname);
481 err = 0;
482 tc = NULL;
483 rc = NULL;
484
485 fid = p9_fid_create(clnt);
486 if (IS_ERR(fid)) {
487 err = PTR_ERR(fid);
488 fid = NULL;
489 goto error;
490 }
491
Latchesar Ionkovba176742007-10-17 14:31:07 -0500492 tc = p9_create_tattach(fid->fid, afid?afid->fid:P9_NOFID, uname, aname,
493 n_uname, clnt->dotu);
Latchesar Ionkovbd238fb2007-07-10 17:57:28 -0500494 if (IS_ERR(tc)) {
495 err = PTR_ERR(tc);
496 tc = NULL;
497 goto error;
498 }
499
Eric Van Hensbergene2735b72008-02-06 19:25:58 -0600500 err = p9_client_rpc(clnt, tc, &rc);
Latchesar Ionkovbd238fb2007-07-10 17:57:28 -0500501 if (err)
502 goto error;
503
504 memmove(&fid->qid, &rc->params.rattach.qid, sizeof(struct p9_qid));
505 kfree(tc);
506 kfree(rc);
507 return fid;
508
509error:
510 kfree(tc);
511 kfree(rc);
512 if (fid)
513 p9_fid_destroy(fid);
514 return ERR_PTR(err);
515}
516EXPORT_SYMBOL(p9_client_attach);
517
Latchesar Ionkovba176742007-10-17 14:31:07 -0500518struct p9_fid *p9_client_auth(struct p9_client *clnt, char *uname,
519 u32 n_uname, char *aname)
Latchesar Ionkovbd238fb2007-07-10 17:57:28 -0500520{
521 int err;
522 struct p9_fcall *tc, *rc;
523 struct p9_fid *fid;
524
525 P9_DPRINTK(P9_DEBUG_9P, "clnt %p uname %s aname %s\n", clnt, uname,
526 aname);
527 err = 0;
528 tc = NULL;
529 rc = NULL;
530
531 fid = p9_fid_create(clnt);
532 if (IS_ERR(fid)) {
533 err = PTR_ERR(fid);
534 fid = NULL;
535 goto error;
536 }
537
Latchesar Ionkovba176742007-10-17 14:31:07 -0500538 tc = p9_create_tauth(fid->fid, uname, aname, n_uname, clnt->dotu);
Latchesar Ionkovbd238fb2007-07-10 17:57:28 -0500539 if (IS_ERR(tc)) {
540 err = PTR_ERR(tc);
541 tc = NULL;
542 goto error;
543 }
544
Eric Van Hensbergene2735b72008-02-06 19:25:58 -0600545 err = p9_client_rpc(clnt, tc, &rc);
Latchesar Ionkovbd238fb2007-07-10 17:57:28 -0500546 if (err)
547 goto error;
548
549 memmove(&fid->qid, &rc->params.rauth.qid, sizeof(struct p9_qid));
550 kfree(tc);
551 kfree(rc);
552 return fid;
553
554error:
555 kfree(tc);
556 kfree(rc);
557 if (fid)
558 p9_fid_destroy(fid);
559 return ERR_PTR(err);
560}
561EXPORT_SYMBOL(p9_client_auth);
562
563struct p9_fid *p9_client_walk(struct p9_fid *oldfid, int nwname, char **wnames,
564 int clone)
565{
566 int err;
567 struct p9_fcall *tc, *rc;
568 struct p9_client *clnt;
569 struct p9_fid *fid;
570
571 P9_DPRINTK(P9_DEBUG_9P, "fid %d nwname %d wname[0] %s\n",
572 oldfid->fid, nwname, wnames?wnames[0]:NULL);
573 err = 0;
574 tc = NULL;
575 rc = NULL;
576 clnt = oldfid->clnt;
577 if (clone) {
578 fid = p9_fid_create(clnt);
579 if (IS_ERR(fid)) {
580 err = PTR_ERR(fid);
581 fid = NULL;
582 goto error;
583 }
584
585 fid->uid = oldfid->uid;
586 } else
587 fid = oldfid;
588
589 tc = p9_create_twalk(oldfid->fid, fid->fid, nwname, wnames);
590 if (IS_ERR(tc)) {
591 err = PTR_ERR(tc);
592 tc = NULL;
593 goto error;
594 }
595
Eric Van Hensbergene2735b72008-02-06 19:25:58 -0600596 err = p9_client_rpc(clnt, tc, &rc);
Latchesar Ionkovbd238fb2007-07-10 17:57:28 -0500597 if (err) {
598 if (rc && rc->id == P9_RWALK)
599 goto clunk_fid;
600 else
601 goto error;
602 }
603
604 if (rc->params.rwalk.nwqid != nwname) {
605 err = -ENOENT;
606 goto clunk_fid;
607 }
608
609 if (nwname)
610 memmove(&fid->qid,
611 &rc->params.rwalk.wqids[rc->params.rwalk.nwqid - 1],
612 sizeof(struct p9_qid));
613 else
614 fid->qid = oldfid->qid;
615
616 kfree(tc);
617 kfree(rc);
618 return fid;
619
620clunk_fid:
621 kfree(tc);
622 kfree(rc);
623 rc = NULL;
624 tc = p9_create_tclunk(fid->fid);
625 if (IS_ERR(tc)) {
626 err = PTR_ERR(tc);
627 tc = NULL;
628 goto error;
629 }
630
Eric Van Hensbergene2735b72008-02-06 19:25:58 -0600631 p9_client_rpc(clnt, tc, &rc);
Latchesar Ionkovbd238fb2007-07-10 17:57:28 -0500632
633error:
634 kfree(tc);
635 kfree(rc);
636 if (fid && (fid != oldfid))
637 p9_fid_destroy(fid);
638
639 return ERR_PTR(err);
640}
641EXPORT_SYMBOL(p9_client_walk);
642
643int p9_client_open(struct p9_fid *fid, int mode)
644{
645 int err;
646 struct p9_fcall *tc, *rc;
647 struct p9_client *clnt;
648
649 P9_DPRINTK(P9_DEBUG_9P, "fid %d mode %d\n", fid->fid, mode);
650 err = 0;
651 tc = NULL;
652 rc = NULL;
653 clnt = fid->clnt;
654
655 if (fid->mode != -1)
656 return -EINVAL;
657
658 tc = p9_create_topen(fid->fid, mode);
659 if (IS_ERR(tc)) {
660 err = PTR_ERR(tc);
661 tc = NULL;
662 goto done;
663 }
664
Eric Van Hensbergene2735b72008-02-06 19:25:58 -0600665 err = p9_client_rpc(clnt, tc, &rc);
Latchesar Ionkovbd238fb2007-07-10 17:57:28 -0500666 if (err)
667 goto done;
668
669 fid->mode = mode;
670 fid->iounit = rc->params.ropen.iounit;
671
672done:
673 kfree(tc);
674 kfree(rc);
675 return err;
676}
677EXPORT_SYMBOL(p9_client_open);
678
679int p9_client_fcreate(struct p9_fid *fid, char *name, u32 perm, int mode,
680 char *extension)
681{
682 int err;
683 struct p9_fcall *tc, *rc;
684 struct p9_client *clnt;
685
686 P9_DPRINTK(P9_DEBUG_9P, "fid %d name %s perm %d mode %d\n", fid->fid,
687 name, perm, mode);
688 err = 0;
689 tc = NULL;
690 rc = NULL;
691 clnt = fid->clnt;
692
693 if (fid->mode != -1)
694 return -EINVAL;
695
696 tc = p9_create_tcreate(fid->fid, name, perm, mode, extension,
697 clnt->dotu);
698 if (IS_ERR(tc)) {
699 err = PTR_ERR(tc);
700 tc = NULL;
701 goto done;
702 }
703
Eric Van Hensbergene2735b72008-02-06 19:25:58 -0600704 err = p9_client_rpc(clnt, tc, &rc);
Latchesar Ionkovbd238fb2007-07-10 17:57:28 -0500705 if (err)
706 goto done;
707
708 fid->mode = mode;
709 fid->iounit = rc->params.ropen.iounit;
710
711done:
712 kfree(tc);
713 kfree(rc);
714 return err;
715}
716EXPORT_SYMBOL(p9_client_fcreate);
717
718int p9_client_clunk(struct p9_fid *fid)
719{
720 int err;
721 struct p9_fcall *tc, *rc;
722 struct p9_client *clnt;
723
724 P9_DPRINTK(P9_DEBUG_9P, "fid %d\n", fid->fid);
725 err = 0;
726 tc = NULL;
727 rc = NULL;
728 clnt = fid->clnt;
729
730 tc = p9_create_tclunk(fid->fid);
731 if (IS_ERR(tc)) {
732 err = PTR_ERR(tc);
733 tc = NULL;
734 goto done;
735 }
736
Eric Van Hensbergene2735b72008-02-06 19:25:58 -0600737 err = p9_client_rpc(clnt, tc, &rc);
Latchesar Ionkovbd238fb2007-07-10 17:57:28 -0500738 if (err)
739 goto done;
740
741 p9_fid_destroy(fid);
742
743done:
744 kfree(tc);
745 kfree(rc);
746 return err;
747}
748EXPORT_SYMBOL(p9_client_clunk);
749
750int p9_client_remove(struct p9_fid *fid)
751{
752 int err;
753 struct p9_fcall *tc, *rc;
754 struct p9_client *clnt;
755
756 P9_DPRINTK(P9_DEBUG_9P, "fid %d\n", fid->fid);
757 err = 0;
758 tc = NULL;
759 rc = NULL;
760 clnt = fid->clnt;
761
762 tc = p9_create_tremove(fid->fid);
763 if (IS_ERR(tc)) {
764 err = PTR_ERR(tc);
765 tc = NULL;
766 goto done;
767 }
768
Eric Van Hensbergene2735b72008-02-06 19:25:58 -0600769 err = p9_client_rpc(clnt, tc, &rc);
Latchesar Ionkovbd238fb2007-07-10 17:57:28 -0500770 if (err)
771 goto done;
772
773 p9_fid_destroy(fid);
774
775done:
776 kfree(tc);
777 kfree(rc);
778 return err;
779}
780EXPORT_SYMBOL(p9_client_remove);
781
782int p9_client_read(struct p9_fid *fid, char *data, u64 offset, u32 count)
783{
784 int err, n, rsize, total;
785 struct p9_fcall *tc, *rc;
786 struct p9_client *clnt;
787
788 P9_DPRINTK(P9_DEBUG_9P, "fid %d offset %llu %d\n", fid->fid,
789 (long long unsigned) offset, count);
790 err = 0;
791 tc = NULL;
792 rc = NULL;
793 clnt = fid->clnt;
794 total = 0;
795
796 rsize = fid->iounit;
797 if (!rsize || rsize > clnt->msize-P9_IOHDRSZ)
798 rsize = clnt->msize - P9_IOHDRSZ;
799
800 do {
801 if (count < rsize)
802 rsize = count;
803
804 tc = p9_create_tread(fid->fid, offset, rsize);
805 if (IS_ERR(tc)) {
806 err = PTR_ERR(tc);
807 tc = NULL;
808 goto error;
809 }
810
Eric Van Hensbergene2735b72008-02-06 19:25:58 -0600811 err = p9_client_rpc(clnt, tc, &rc);
Latchesar Ionkovbd238fb2007-07-10 17:57:28 -0500812 if (err)
813 goto error;
814
815 n = rc->params.rread.count;
816 if (n > count)
817 n = count;
818
819 memmove(data, rc->params.rread.data, n);
820 count -= n;
821 data += n;
822 offset += n;
823 total += n;
824 kfree(tc);
825 tc = NULL;
826 kfree(rc);
827 rc = NULL;
828 } while (count > 0 && n == rsize);
829
830 return total;
831
832error:
833 kfree(tc);
834 kfree(rc);
835 return err;
836}
837EXPORT_SYMBOL(p9_client_read);
838
839int p9_client_write(struct p9_fid *fid, char *data, u64 offset, u32 count)
840{
841 int err, n, rsize, total;
842 struct p9_fcall *tc, *rc;
843 struct p9_client *clnt;
844
845 P9_DPRINTK(P9_DEBUG_9P, "fid %d offset %llu count %d\n", fid->fid,
846 (long long unsigned) offset, count);
847 err = 0;
848 tc = NULL;
849 rc = NULL;
850 clnt = fid->clnt;
851 total = 0;
852
853 rsize = fid->iounit;
854 if (!rsize || rsize > clnt->msize-P9_IOHDRSZ)
855 rsize = clnt->msize - P9_IOHDRSZ;
856
857 do {
858 if (count < rsize)
859 rsize = count;
860
861 tc = p9_create_twrite(fid->fid, offset, rsize, data);
862 if (IS_ERR(tc)) {
863 err = PTR_ERR(tc);
864 tc = NULL;
865 goto error;
866 }
867
Eric Van Hensbergene2735b72008-02-06 19:25:58 -0600868 err = p9_client_rpc(clnt, tc, &rc);
Latchesar Ionkovbd238fb2007-07-10 17:57:28 -0500869 if (err)
870 goto error;
871
872 n = rc->params.rread.count;
873 count -= n;
874 data += n;
875 offset += n;
876 total += n;
877 kfree(tc);
878 tc = NULL;
879 kfree(rc);
880 rc = NULL;
881 } while (count > 0);
882
883 return total;
884
885error:
886 kfree(tc);
887 kfree(rc);
888 return err;
889}
890EXPORT_SYMBOL(p9_client_write);
891
892int
893p9_client_uread(struct p9_fid *fid, char __user *data, u64 offset, u32 count)
894{
895 int err, n, rsize, total;
896 struct p9_fcall *tc, *rc;
897 struct p9_client *clnt;
898
899 P9_DPRINTK(P9_DEBUG_9P, "fid %d offset %llu count %d\n", fid->fid,
900 (long long unsigned) offset, count);
901 err = 0;
902 tc = NULL;
903 rc = NULL;
904 clnt = fid->clnt;
905 total = 0;
906
907 rsize = fid->iounit;
908 if (!rsize || rsize > clnt->msize-P9_IOHDRSZ)
909 rsize = clnt->msize - P9_IOHDRSZ;
910
911 do {
912 if (count < rsize)
913 rsize = count;
914
915 tc = p9_create_tread(fid->fid, offset, rsize);
916 if (IS_ERR(tc)) {
917 err = PTR_ERR(tc);
918 tc = NULL;
919 goto error;
920 }
921
Eric Van Hensbergene2735b72008-02-06 19:25:58 -0600922 err = p9_client_rpc(clnt, tc, &rc);
Latchesar Ionkovbd238fb2007-07-10 17:57:28 -0500923 if (err)
924 goto error;
925
926 n = rc->params.rread.count;
927 if (n > count)
928 n = count;
929
930 err = copy_to_user(data, rc->params.rread.data, n);
931 if (err) {
932 err = -EFAULT;
933 goto error;
934 }
935
936 count -= n;
937 data += n;
938 offset += n;
939 total += n;
940 kfree(tc);
941 tc = NULL;
942 kfree(rc);
943 rc = NULL;
944 } while (count > 0 && n == rsize);
945
946 return total;
947
948error:
949 kfree(tc);
950 kfree(rc);
951 return err;
952}
953EXPORT_SYMBOL(p9_client_uread);
954
955int
956p9_client_uwrite(struct p9_fid *fid, const char __user *data, u64 offset,
957 u32 count)
958{
959 int err, n, rsize, total;
960 struct p9_fcall *tc, *rc;
961 struct p9_client *clnt;
962
963 P9_DPRINTK(P9_DEBUG_9P, "fid %d offset %llu count %d\n", fid->fid,
964 (long long unsigned) offset, count);
965 err = 0;
966 tc = NULL;
967 rc = NULL;
968 clnt = fid->clnt;
969 total = 0;
970
971 rsize = fid->iounit;
972 if (!rsize || rsize > clnt->msize-P9_IOHDRSZ)
973 rsize = clnt->msize - P9_IOHDRSZ;
974
975 do {
976 if (count < rsize)
977 rsize = count;
978
979 tc = p9_create_twrite_u(fid->fid, offset, rsize, data);
980 if (IS_ERR(tc)) {
981 err = PTR_ERR(tc);
982 tc = NULL;
983 goto error;
984 }
985
Eric Van Hensbergene2735b72008-02-06 19:25:58 -0600986 err = p9_client_rpc(clnt, tc, &rc);
Latchesar Ionkovbd238fb2007-07-10 17:57:28 -0500987 if (err)
988 goto error;
989
990 n = rc->params.rread.count;
991 count -= n;
992 data += n;
993 offset += n;
994 total += n;
995 kfree(tc);
996 tc = NULL;
997 kfree(rc);
998 rc = NULL;
999 } while (count > 0);
1000
1001 return total;
1002
1003error:
1004 kfree(tc);
1005 kfree(rc);
1006 return err;
1007}
1008EXPORT_SYMBOL(p9_client_uwrite);
1009
1010int p9_client_readn(struct p9_fid *fid, char *data, u64 offset, u32 count)
1011{
1012 int n, total;
1013
1014 P9_DPRINTK(P9_DEBUG_9P, "fid %d offset %llu count %d\n", fid->fid,
1015 (long long unsigned) offset, count);
1016 n = 0;
1017 total = 0;
1018 while (count) {
1019 n = p9_client_read(fid, data, offset, count);
1020 if (n <= 0)
1021 break;
1022
1023 data += n;
1024 offset += n;
1025 count -= n;
1026 total += n;
1027 }
1028
1029 if (n < 0)
1030 total = n;
1031
1032 return total;
1033}
1034EXPORT_SYMBOL(p9_client_readn);
1035
Eric Van Hensbergen5503ac52008-10-13 18:45:24 -05001036static struct p9_stat *p9_clone_stat(struct p9_stat *st, int dotu)
1037{
1038 int n;
1039 char *p;
1040 struct p9_stat *ret;
1041
1042 n = sizeof(struct p9_stat) + st->name.len + st->uid.len + st->gid.len +
1043 st->muid.len;
1044
1045 if (dotu)
1046 n += st->extension.len;
1047
1048 ret = kmalloc(n, GFP_KERNEL);
1049 if (!ret)
1050 return ERR_PTR(-ENOMEM);
1051
1052 memmove(ret, st, sizeof(struct p9_stat));
1053 p = ((char *) ret) + sizeof(struct p9_stat);
1054 memmove(p, st->name.str, st->name.len);
1055 ret->name.str = p;
1056 p += st->name.len;
1057 memmove(p, st->uid.str, st->uid.len);
1058 ret->uid.str = p;
1059 p += st->uid.len;
1060 memmove(p, st->gid.str, st->gid.len);
1061 ret->gid.str = p;
1062 p += st->gid.len;
1063 memmove(p, st->muid.str, st->muid.len);
1064 ret->muid.str = p;
1065 p += st->muid.len;
1066
1067 if (dotu) {
1068 memmove(p, st->extension.str, st->extension.len);
1069 ret->extension.str = p;
1070 p += st->extension.len;
1071 }
1072
1073 return ret;
1074}
1075
Latchesar Ionkovbd238fb2007-07-10 17:57:28 -05001076struct p9_stat *p9_client_stat(struct p9_fid *fid)
1077{
1078 int err;
1079 struct p9_fcall *tc, *rc;
1080 struct p9_client *clnt;
1081 struct p9_stat *ret;
1082
1083 P9_DPRINTK(P9_DEBUG_9P, "fid %d\n", fid->fid);
1084 err = 0;
1085 tc = NULL;
1086 rc = NULL;
1087 ret = NULL;
1088 clnt = fid->clnt;
1089
1090 tc = p9_create_tstat(fid->fid);
1091 if (IS_ERR(tc)) {
1092 err = PTR_ERR(tc);
1093 tc = NULL;
1094 goto error;
1095 }
1096
Eric Van Hensbergene2735b72008-02-06 19:25:58 -06001097 err = p9_client_rpc(clnt, tc, &rc);
Latchesar Ionkovbd238fb2007-07-10 17:57:28 -05001098 if (err)
1099 goto error;
1100
1101 ret = p9_clone_stat(&rc->params.rstat.stat, clnt->dotu);
1102 if (IS_ERR(ret)) {
1103 err = PTR_ERR(ret);
1104 ret = NULL;
1105 goto error;
1106 }
1107
1108 kfree(tc);
1109 kfree(rc);
1110 return ret;
1111
1112error:
1113 kfree(tc);
1114 kfree(rc);
1115 kfree(ret);
1116 return ERR_PTR(err);
1117}
1118EXPORT_SYMBOL(p9_client_stat);
1119
1120int p9_client_wstat(struct p9_fid *fid, struct p9_wstat *wst)
1121{
1122 int err;
1123 struct p9_fcall *tc, *rc;
1124 struct p9_client *clnt;
1125
1126 P9_DPRINTK(P9_DEBUG_9P, "fid %d\n", fid->fid);
1127 err = 0;
1128 tc = NULL;
1129 rc = NULL;
1130 clnt = fid->clnt;
1131
1132 tc = p9_create_twstat(fid->fid, wst, clnt->dotu);
1133 if (IS_ERR(tc)) {
1134 err = PTR_ERR(tc);
1135 tc = NULL;
1136 goto done;
1137 }
1138
Eric Van Hensbergene2735b72008-02-06 19:25:58 -06001139 err = p9_client_rpc(clnt, tc, &rc);
Latchesar Ionkovbd238fb2007-07-10 17:57:28 -05001140
1141done:
1142 kfree(tc);
1143 kfree(rc);
1144 return err;
1145}
1146EXPORT_SYMBOL(p9_client_wstat);
1147
1148struct p9_stat *p9_client_dirread(struct p9_fid *fid, u64 offset)
1149{
1150 int err, n, m;
1151 struct p9_fcall *tc, *rc;
1152 struct p9_client *clnt;
1153 struct p9_stat st, *ret;
1154
1155 P9_DPRINTK(P9_DEBUG_9P, "fid %d offset %llu\n", fid->fid,
1156 (long long unsigned) offset);
1157 err = 0;
1158 tc = NULL;
1159 rc = NULL;
1160 ret = NULL;
1161 clnt = fid->clnt;
1162
1163 /* if the offset is below or above the current response, free it */
1164 if (offset < fid->rdir_fpos || (fid->rdir_fcall &&
1165 offset >= fid->rdir_fpos+fid->rdir_fcall->params.rread.count)) {
1166 fid->rdir_pos = 0;
1167 if (fid->rdir_fcall)
1168 fid->rdir_fpos += fid->rdir_fcall->params.rread.count;
1169
1170 kfree(fid->rdir_fcall);
1171 fid->rdir_fcall = NULL;
1172 if (offset < fid->rdir_fpos)
1173 fid->rdir_fpos = 0;
1174 }
1175
1176 if (!fid->rdir_fcall) {
1177 n = fid->iounit;
1178 if (!n || n > clnt->msize-P9_IOHDRSZ)
1179 n = clnt->msize - P9_IOHDRSZ;
1180
1181 while (1) {
1182 if (fid->rdir_fcall) {
1183 fid->rdir_fpos +=
1184 fid->rdir_fcall->params.rread.count;
1185 kfree(fid->rdir_fcall);
1186 fid->rdir_fcall = NULL;
1187 }
1188
1189 tc = p9_create_tread(fid->fid, fid->rdir_fpos, n);
1190 if (IS_ERR(tc)) {
1191 err = PTR_ERR(tc);
1192 tc = NULL;
1193 goto error;
1194 }
1195
Eric Van Hensbergene2735b72008-02-06 19:25:58 -06001196 err = p9_client_rpc(clnt, tc, &rc);
Latchesar Ionkovbd238fb2007-07-10 17:57:28 -05001197 if (err)
1198 goto error;
1199
1200 n = rc->params.rread.count;
1201 if (n == 0)
1202 goto done;
1203
1204 fid->rdir_fcall = rc;
1205 rc = NULL;
1206 if (offset >= fid->rdir_fpos &&
1207 offset < fid->rdir_fpos+n)
1208 break;
1209 }
1210
1211 fid->rdir_pos = 0;
1212 }
1213
1214 m = offset - fid->rdir_fpos;
1215 if (m < 0)
1216 goto done;
1217
1218 n = p9_deserialize_stat(fid->rdir_fcall->params.rread.data + m,
1219 fid->rdir_fcall->params.rread.count - m, &st, clnt->dotu);
1220
1221 if (!n) {
1222 err = -EIO;
1223 goto error;
1224 }
1225
1226 fid->rdir_pos += n;
1227 st.size = n;
1228 ret = p9_clone_stat(&st, clnt->dotu);
1229 if (IS_ERR(ret)) {
1230 err = PTR_ERR(ret);
1231 ret = NULL;
1232 goto error;
1233 }
1234
1235done:
1236 kfree(tc);
1237 kfree(rc);
1238 return ret;
1239
1240error:
1241 kfree(tc);
1242 kfree(rc);
1243 kfree(ret);
1244 return ERR_PTR(err);
1245}
1246EXPORT_SYMBOL(p9_client_dirread);