9p: Add fscache support to 9p
[linux-2.6.git] / fs / 9p / v9fs.c
1 /*
2  *  linux/fs/9p/v9fs.c
3  *
4  *  This file contains functions assisting in mapping VFS to 9P2000
5  *
6  *  Copyright (C) 2004-2008 by Eric Van Hensbergen <ericvh@gmail.com>
7  *  Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
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>
29 #include <linux/sched.h>
30 #include <linux/parser.h>
31 #include <linux/idr.h>
32 #include <net/9p/9p.h>
33 #include <net/9p/client.h>
34 #include <net/9p/transport.h>
35 #include "v9fs.h"
36 #include "v9fs_vfs.h"
37 #include "cache.h"
38
39 static DEFINE_SPINLOCK(v9fs_sessionlist_lock);
40 static LIST_HEAD(v9fs_sessionlist);
41
42 /*
43  * Option Parsing (code inspired by NFS code)
44  *  NOTE: each transport will parse its own options
45  */
46
47 enum {
48         /* Options that take integer arguments */
49         Opt_debug, Opt_dfltuid, Opt_dfltgid, Opt_afid,
50         /* String options */
51         Opt_uname, Opt_remotename, Opt_trans, Opt_cache, Opt_cachetag,
52         /* Options that take no arguments */
53         Opt_nodevmap,
54         /* Cache options */
55         Opt_cache_loose, Opt_fscache,
56         /* Access options */
57         Opt_access,
58         /* Error token */
59         Opt_err
60 };
61
62 static const match_table_t tokens = {
63         {Opt_debug, "debug=%x"},
64         {Opt_dfltuid, "dfltuid=%u"},
65         {Opt_dfltgid, "dfltgid=%u"},
66         {Opt_afid, "afid=%u"},
67         {Opt_uname, "uname=%s"},
68         {Opt_remotename, "aname=%s"},
69         {Opt_nodevmap, "nodevmap"},
70         {Opt_cache, "cache=%s"},
71         {Opt_cache_loose, "loose"},
72         {Opt_fscache, "fscache"},
73         {Opt_cachetag, "cachetag=%s"},
74         {Opt_access, "access=%s"},
75         {Opt_err, NULL}
76 };
77
78 /**
79  * v9fs_parse_options - parse mount options into session structure
80  * @v9ses: existing v9fs session information
81  *
82  * Return 0 upon success, -ERRNO upon failure.
83  */
84
85 static int v9fs_parse_options(struct v9fs_session_info *v9ses, char *opts)
86 {
87         char *options;
88         substring_t args[MAX_OPT_ARGS];
89         char *p;
90         int option = 0;
91         char *s, *e;
92         int ret = 0;
93
94         /* setup defaults */
95         v9ses->afid = ~0;
96         v9ses->debug = 0;
97         v9ses->cache = 0;
98 #ifdef CONFIG_9P_FSCACHE
99         v9ses->cachetag = NULL;
100 #endif
101
102         if (!opts)
103                 return 0;
104
105         options = kstrdup(opts, GFP_KERNEL);
106         if (!options)
107                 goto fail_option_alloc;
108
109         while ((p = strsep(&options, ",")) != NULL) {
110                 int token;
111                 if (!*p)
112                         continue;
113                 token = match_token(p, tokens, args);
114                 if (token < Opt_uname) {
115                         int r = match_int(&args[0], &option);
116                         if (r < 0) {
117                                 P9_DPRINTK(P9_DEBUG_ERROR,
118                                         "integer field, but no integer?\n");
119                                 ret = r;
120                                 continue;
121                         }
122                 }
123                 switch (token) {
124                 case Opt_debug:
125                         v9ses->debug = option;
126 #ifdef CONFIG_NET_9P_DEBUG
127                         p9_debug_level = option;
128 #endif
129                         break;
130
131                 case Opt_dfltuid:
132                         v9ses->dfltuid = option;
133                         break;
134                 case Opt_dfltgid:
135                         v9ses->dfltgid = option;
136                         break;
137                 case Opt_afid:
138                         v9ses->afid = option;
139                         break;
140                 case Opt_uname:
141                         match_strlcpy(v9ses->uname, &args[0], PATH_MAX);
142                         break;
143                 case Opt_remotename:
144                         match_strlcpy(v9ses->aname, &args[0], PATH_MAX);
145                         break;
146                 case Opt_nodevmap:
147                         v9ses->nodev = 1;
148                         break;
149                 case Opt_cache_loose:
150                         v9ses->cache = CACHE_LOOSE;
151                         break;
152                 case Opt_fscache:
153                         v9ses->cache = CACHE_FSCACHE;
154                         break;
155                 case Opt_cachetag:
156 #ifdef CONFIG_9P_FSCACHE
157                         v9ses->cachetag = match_strdup(&args[0]);
158 #endif
159                         break;
160                 case Opt_cache:
161                         s = match_strdup(&args[0]);
162                         if (!s)
163                                 goto fail_option_alloc;
164
165                         if (strcmp(s, "loose") == 0)
166                                 v9ses->cache = CACHE_LOOSE;
167                         else if (strcmp(s, "fscache") == 0)
168                                 v9ses->cache = CACHE_FSCACHE;
169                         else
170                                 v9ses->cache = CACHE_NONE;
171                         kfree(s);
172                         break;
173
174                 case Opt_access:
175                         s = match_strdup(&args[0]);
176                         if (!s)
177                                 goto fail_option_alloc;
178
179                         v9ses->flags &= ~V9FS_ACCESS_MASK;
180                         if (strcmp(s, "user") == 0)
181                                 v9ses->flags |= V9FS_ACCESS_USER;
182                         else if (strcmp(s, "any") == 0)
183                                 v9ses->flags |= V9FS_ACCESS_ANY;
184                         else {
185                                 v9ses->flags |= V9FS_ACCESS_SINGLE;
186                                 v9ses->uid = simple_strtoul(s, &e, 10);
187                                 if (*e != '\0')
188                                         v9ses->uid = ~0;
189                         }
190                         kfree(s);
191                         break;
192
193                 default:
194                         continue;
195                 }
196         }
197         kfree(options);
198         return ret;
199
200 fail_option_alloc:
201         P9_DPRINTK(P9_DEBUG_ERROR,
202                    "failed to allocate copy of option argument\n");
203         return -ENOMEM;
204 }
205
206 /**
207  * v9fs_session_init - initialize session
208  * @v9ses: session information structure
209  * @dev_name: device being mounted
210  * @data: options
211  *
212  */
213
214 struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses,
215                   const char *dev_name, char *data)
216 {
217         int retval = -EINVAL;
218         struct p9_fid *fid;
219         int rc;
220
221         v9ses->uname = __getname();
222         if (!v9ses->uname)
223                 return ERR_PTR(-ENOMEM);
224
225         v9ses->aname = __getname();
226         if (!v9ses->aname) {
227                 __putname(v9ses->uname);
228                 return ERR_PTR(-ENOMEM);
229         }
230
231         spin_lock(&v9fs_sessionlist_lock);
232         list_add(&v9ses->slist, &v9fs_sessionlist);
233         spin_unlock(&v9fs_sessionlist_lock);
234
235         v9ses->flags = V9FS_EXTENDED | V9FS_ACCESS_USER;
236         strcpy(v9ses->uname, V9FS_DEFUSER);
237         strcpy(v9ses->aname, V9FS_DEFANAME);
238         v9ses->uid = ~0;
239         v9ses->dfltuid = V9FS_DEFUID;
240         v9ses->dfltgid = V9FS_DEFGID;
241
242         rc = v9fs_parse_options(v9ses, data);
243         if (rc < 0) {
244                 retval = rc;
245                 goto error;
246         }
247
248         v9ses->clnt = p9_client_create(dev_name, data);
249         if (IS_ERR(v9ses->clnt)) {
250                 retval = PTR_ERR(v9ses->clnt);
251                 v9ses->clnt = NULL;
252                 P9_DPRINTK(P9_DEBUG_ERROR, "problem initializing 9p client\n");
253                 goto error;
254         }
255
256         if (!v9ses->clnt->dotu)
257                 v9ses->flags &= ~V9FS_EXTENDED;
258
259         v9ses->maxdata = v9ses->clnt->msize - P9_IOHDRSZ;
260
261         /* for legacy mode, fall back to V9FS_ACCESS_ANY */
262         if (!v9fs_extended(v9ses) &&
263                 ((v9ses->flags&V9FS_ACCESS_MASK) == V9FS_ACCESS_USER)) {
264
265                 v9ses->flags &= ~V9FS_ACCESS_MASK;
266                 v9ses->flags |= V9FS_ACCESS_ANY;
267                 v9ses->uid = ~0;
268         }
269
270         fid = p9_client_attach(v9ses->clnt, NULL, v9ses->uname, ~0,
271                                                         v9ses->aname);
272         if (IS_ERR(fid)) {
273                 retval = PTR_ERR(fid);
274                 fid = NULL;
275                 P9_DPRINTK(P9_DEBUG_ERROR, "cannot attach\n");
276                 goto error;
277         }
278
279         if ((v9ses->flags & V9FS_ACCESS_MASK) == V9FS_ACCESS_SINGLE)
280                 fid->uid = v9ses->uid;
281         else
282                 fid->uid = ~0;
283
284 #ifdef CONFIG_9P_FSCACHE
285         /* register the session for caching */
286         v9fs_cache_session_get_cookie(v9ses);
287 #endif
288
289         return fid;
290
291 error:
292         return ERR_PTR(retval);
293 }
294
295 /**
296  * v9fs_session_close - shutdown a session
297  * @v9ses: session information structure
298  *
299  */
300
301 void v9fs_session_close(struct v9fs_session_info *v9ses)
302 {
303         if (v9ses->clnt) {
304                 p9_client_destroy(v9ses->clnt);
305                 v9ses->clnt = NULL;
306         }
307
308 #ifdef CONFIG_9P_FSCACHE
309         if (v9ses->fscache) {
310                 v9fs_cache_session_put_cookie(v9ses);
311                 kfree(v9ses->cachetag);
312         }
313 #endif
314         __putname(v9ses->uname);
315         __putname(v9ses->aname);
316
317         spin_lock(&v9fs_sessionlist_lock);
318         list_del(&v9ses->slist);
319         spin_unlock(&v9fs_sessionlist_lock);
320 }
321
322 /**
323  * v9fs_session_cancel - terminate a session
324  * @v9ses: session to terminate
325  *
326  * mark transport as disconnected and cancel all pending requests.
327  */
328
329 void v9fs_session_cancel(struct v9fs_session_info *v9ses) {
330         P9_DPRINTK(P9_DEBUG_ERROR, "cancel session %p\n", v9ses);
331         p9_client_disconnect(v9ses->clnt);
332 }
333
334 extern int v9fs_error_init(void);
335
336 static struct kobject *v9fs_kobj;
337
338 #ifdef CONFIG_9P_FSCACHE
339 /**
340  * caches_show - list caches associated with a session
341  *
342  * Returns the size of buffer written.
343  */
344
345 static ssize_t caches_show(struct kobject *kobj,
346                            struct kobj_attribute *attr,
347                            char *buf)
348 {
349         ssize_t n = 0, count = 0, limit = PAGE_SIZE;
350         struct v9fs_session_info *v9ses;
351
352         spin_lock(&v9fs_sessionlist_lock);
353         list_for_each_entry(v9ses, &v9fs_sessionlist, slist) {
354                 if (v9ses->cachetag) {
355                         n = snprintf(buf, limit, "%s\n", v9ses->cachetag);
356                         if (n < 0) {
357                                 count = n;
358                                 break;
359                         }
360
361                         count += n;
362                         limit -= n;
363                 }
364         }
365
366         spin_unlock(&v9fs_sessionlist_lock);
367         return count;
368 }
369
370 static struct kobj_attribute v9fs_attr_cache = __ATTR_RO(caches);
371 #endif /* CONFIG_9P_FSCACHE */
372
373 static struct attribute *v9fs_attrs[] = {
374 #ifdef CONFIG_9P_FSCACHE
375         &v9fs_attr_cache.attr,
376 #endif
377         NULL,
378 };
379
380 static struct attribute_group v9fs_attr_group = {
381         .attrs = v9fs_attrs,
382 };
383
384 /**
385  * v9fs_sysfs_init - Initialize the v9fs sysfs interface
386  *
387  */
388
389 static int v9fs_sysfs_init(void)
390 {
391         v9fs_kobj = kobject_create_and_add("9p", fs_kobj);
392         if (!v9fs_kobj)
393                 return -ENOMEM;
394
395         if (sysfs_create_group(v9fs_kobj, &v9fs_attr_group)) {
396                 kobject_put(v9fs_kobj);
397                 return -ENOMEM;
398         }
399
400         return 0;
401 }
402
403 /**
404  * v9fs_sysfs_cleanup - Unregister the v9fs sysfs interface
405  *
406  */
407
408 static void v9fs_sysfs_cleanup(void)
409 {
410         sysfs_remove_group(v9fs_kobj, &v9fs_attr_group);
411         kobject_put(v9fs_kobj);
412 }
413
414 /**
415  * init_v9fs - Initialize module
416  *
417  */
418
419 static int __init init_v9fs(void)
420 {
421         int err;
422         printk(KERN_INFO "Installing v9fs 9p2000 file system support\n");
423         /* TODO: Setup list of registered trasnport modules */
424         err = register_filesystem(&v9fs_fs_type);
425         if (err < 0) {
426                 printk(KERN_ERR "Failed to register filesystem\n");
427                 return err;
428         }
429
430         err = v9fs_cache_register();
431         if (err < 0) {
432                 printk(KERN_ERR "Failed to register v9fs for caching\n");
433                 goto out_fs_unreg;
434         }
435
436         err = v9fs_sysfs_init();
437         if (err < 0) {
438                 printk(KERN_ERR "Failed to register with sysfs\n");
439                 goto out_sysfs_cleanup;
440         }
441
442         return 0;
443
444 out_sysfs_cleanup:
445         v9fs_sysfs_cleanup();
446
447 out_fs_unreg:
448         unregister_filesystem(&v9fs_fs_type);
449
450         return err;
451 }
452
453 /**
454  * exit_v9fs - shutdown module
455  *
456  */
457
458 static void __exit exit_v9fs(void)
459 {
460         v9fs_sysfs_cleanup();
461         v9fs_cache_unregister();
462         unregister_filesystem(&v9fs_fs_type);
463 }
464
465 module_init(init_v9fs)
466 module_exit(exit_v9fs)
467
468 MODULE_AUTHOR("Latchesar Ionkov <lucho@ionkov.net>");
469 MODULE_AUTHOR("Eric Van Hensbergen <ericvh@gmail.com>");
470 MODULE_AUTHOR("Ron Minnich <rminnich@lanl.gov>");
471 MODULE_LICENSE("GPL");