GNU Linux-libre 6.7.9-gnu
[releases.git] / fs / 9p / v9fs.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  *  This file contains functions assisting in mapping VFS to 9P2000
4  *
5  *  Copyright (C) 2004-2008 by Eric Van Hensbergen <ericvh@gmail.com>
6  *  Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
7  */
8
9 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
10
11 #include <linux/module.h>
12 #include <linux/errno.h>
13 #include <linux/fs.h>
14 #include <linux/sched.h>
15 #include <linux/cred.h>
16 #include <linux/parser.h>
17 #include <linux/slab.h>
18 #include <linux/seq_file.h>
19 #include <net/9p/9p.h>
20 #include <net/9p/client.h>
21 #include <net/9p/transport.h>
22 #include "v9fs.h"
23 #include "v9fs_vfs.h"
24 #include "cache.h"
25
26 static DEFINE_SPINLOCK(v9fs_sessionlist_lock);
27 static LIST_HEAD(v9fs_sessionlist);
28 struct kmem_cache *v9fs_inode_cache;
29
30 /*
31  * Option Parsing (code inspired by NFS code)
32  *  NOTE: each transport will parse its own options
33  */
34
35 enum {
36         /* Options that take integer arguments */
37         Opt_debug, Opt_dfltuid, Opt_dfltgid, Opt_afid,
38         /* String options */
39         Opt_uname, Opt_remotename, Opt_cache, Opt_cachetag,
40         /* Options that take no arguments */
41         Opt_nodevmap, Opt_noxattr, Opt_directio, Opt_ignoreqv,
42         /* Access options */
43         Opt_access, Opt_posixacl,
44         /* Lock timeout option */
45         Opt_locktimeout,
46         /* Error token */
47         Opt_err
48 };
49
50 static const match_table_t tokens = {
51         {Opt_debug, "debug=%x"},
52         {Opt_dfltuid, "dfltuid=%u"},
53         {Opt_dfltgid, "dfltgid=%u"},
54         {Opt_afid, "afid=%u"},
55         {Opt_uname, "uname=%s"},
56         {Opt_remotename, "aname=%s"},
57         {Opt_nodevmap, "nodevmap"},
58         {Opt_noxattr, "noxattr"},
59         {Opt_directio, "directio"},
60         {Opt_ignoreqv, "ignoreqv"},
61         {Opt_cache, "cache=%s"},
62         {Opt_cachetag, "cachetag=%s"},
63         {Opt_access, "access=%s"},
64         {Opt_posixacl, "posixacl"},
65         {Opt_locktimeout, "locktimeout=%u"},
66         {Opt_err, NULL}
67 };
68
69 /* Interpret mount options for cache mode */
70 static int get_cache_mode(char *s)
71 {
72         int version = -EINVAL;
73
74         if (!strcmp(s, "loose")) {
75                 version = CACHE_SC_LOOSE;
76                 p9_debug(P9_DEBUG_9P, "Cache mode: loose\n");
77         } else if (!strcmp(s, "fscache")) {
78                 version = CACHE_SC_FSCACHE;
79                 p9_debug(P9_DEBUG_9P, "Cache mode: fscache\n");
80         } else if (!strcmp(s, "mmap")) {
81                 version = CACHE_SC_MMAP;
82                 p9_debug(P9_DEBUG_9P, "Cache mode: mmap\n");
83         } else if (!strcmp(s, "readahead")) {
84                 version = CACHE_SC_READAHEAD;
85                 p9_debug(P9_DEBUG_9P, "Cache mode: readahead\n");
86         } else if (!strcmp(s, "none")) {
87                 version = CACHE_SC_NONE;
88                 p9_debug(P9_DEBUG_9P, "Cache mode: none\n");
89         } else if (kstrtoint(s, 0, &version) != 0) {
90                 version = -EINVAL;
91                 pr_info("Unknown Cache mode or invalid value %s\n", s);
92         }
93         return version;
94 }
95
96 /*
97  * Display the mount options in /proc/mounts.
98  */
99 int v9fs_show_options(struct seq_file *m, struct dentry *root)
100 {
101         struct v9fs_session_info *v9ses = root->d_sb->s_fs_info;
102
103         if (v9ses->debug)
104                 seq_printf(m, ",debug=%x", v9ses->debug);
105         if (!uid_eq(v9ses->dfltuid, V9FS_DEFUID))
106                 seq_printf(m, ",dfltuid=%u",
107                            from_kuid_munged(&init_user_ns, v9ses->dfltuid));
108         if (!gid_eq(v9ses->dfltgid, V9FS_DEFGID))
109                 seq_printf(m, ",dfltgid=%u",
110                            from_kgid_munged(&init_user_ns, v9ses->dfltgid));
111         if (v9ses->afid != ~0)
112                 seq_printf(m, ",afid=%u", v9ses->afid);
113         if (strcmp(v9ses->uname, V9FS_DEFUSER) != 0)
114                 seq_printf(m, ",uname=%s", v9ses->uname);
115         if (strcmp(v9ses->aname, V9FS_DEFANAME) != 0)
116                 seq_printf(m, ",aname=%s", v9ses->aname);
117         if (v9ses->nodev)
118                 seq_puts(m, ",nodevmap");
119         if (v9ses->cache)
120                 seq_printf(m, ",cache=%x", v9ses->cache);
121 #ifdef CONFIG_9P_FSCACHE
122         if (v9ses->cachetag && (v9ses->cache & CACHE_FSCACHE))
123                 seq_printf(m, ",cachetag=%s", v9ses->cachetag);
124 #endif
125
126         switch (v9ses->flags & V9FS_ACCESS_MASK) {
127         case V9FS_ACCESS_USER:
128                 seq_puts(m, ",access=user");
129                 break;
130         case V9FS_ACCESS_ANY:
131                 seq_puts(m, ",access=any");
132                 break;
133         case V9FS_ACCESS_CLIENT:
134                 seq_puts(m, ",access=client");
135                 break;
136         case V9FS_ACCESS_SINGLE:
137                 seq_printf(m, ",access=%u",
138                            from_kuid_munged(&init_user_ns, v9ses->uid));
139                 break;
140         }
141
142         if (v9ses->flags & V9FS_IGNORE_QV)
143                 seq_puts(m, ",ignoreqv");
144         if (v9ses->flags & V9FS_DIRECT_IO)
145                 seq_puts(m, ",directio");
146         if (v9ses->flags & V9FS_POSIX_ACL)
147                 seq_puts(m, ",posixacl");
148
149         if (v9ses->flags & V9FS_NO_XATTR)
150                 seq_puts(m, ",noxattr");
151
152         return p9_show_client_options(m, v9ses->clnt);
153 }
154
155 /**
156  * v9fs_parse_options - parse mount options into session structure
157  * @v9ses: existing v9fs session information
158  * @opts: The mount option string
159  *
160  * Return 0 upon success, -ERRNO upon failure.
161  */
162
163 static int v9fs_parse_options(struct v9fs_session_info *v9ses, char *opts)
164 {
165         char *options, *tmp_options;
166         substring_t args[MAX_OPT_ARGS];
167         char *p;
168         int option = 0;
169         char *s;
170         int ret = 0;
171
172         /* setup defaults */
173         v9ses->afid = ~0;
174         v9ses->debug = 0;
175         v9ses->cache = CACHE_NONE;
176 #ifdef CONFIG_9P_FSCACHE
177         v9ses->cachetag = NULL;
178 #endif
179         v9ses->session_lock_timeout = P9_LOCK_TIMEOUT;
180
181         if (!opts)
182                 return 0;
183
184         tmp_options = kstrdup(opts, GFP_KERNEL);
185         if (!tmp_options) {
186                 ret = -ENOMEM;
187                 goto fail_option_alloc;
188         }
189         options = tmp_options;
190
191         while ((p = strsep(&options, ",")) != NULL) {
192                 int token, r;
193
194                 if (!*p)
195                         continue;
196
197                 token = match_token(p, tokens, args);
198                 switch (token) {
199                 case Opt_debug:
200                         r = match_int(&args[0], &option);
201                         if (r < 0) {
202                                 p9_debug(P9_DEBUG_ERROR,
203                                          "integer field, but no integer?\n");
204                                 ret = r;
205                         } else {
206                                 v9ses->debug = option;
207 #ifdef CONFIG_NET_9P_DEBUG
208                                 p9_debug_level = option;
209 #endif
210                         }
211                         break;
212
213                 case Opt_dfltuid:
214                         r = match_int(&args[0], &option);
215                         if (r < 0) {
216                                 p9_debug(P9_DEBUG_ERROR,
217                                          "integer field, but no integer?\n");
218                                 ret = r;
219                                 continue;
220                         }
221                         v9ses->dfltuid = make_kuid(current_user_ns(), option);
222                         if (!uid_valid(v9ses->dfltuid)) {
223                                 p9_debug(P9_DEBUG_ERROR,
224                                          "uid field, but not a uid?\n");
225                                 ret = -EINVAL;
226                         }
227                         break;
228                 case Opt_dfltgid:
229                         r = match_int(&args[0], &option);
230                         if (r < 0) {
231                                 p9_debug(P9_DEBUG_ERROR,
232                                          "integer field, but no integer?\n");
233                                 ret = r;
234                                 continue;
235                         }
236                         v9ses->dfltgid = make_kgid(current_user_ns(), option);
237                         if (!gid_valid(v9ses->dfltgid)) {
238                                 p9_debug(P9_DEBUG_ERROR,
239                                          "gid field, but not a gid?\n");
240                                 ret = -EINVAL;
241                         }
242                         break;
243                 case Opt_afid:
244                         r = match_int(&args[0], &option);
245                         if (r < 0) {
246                                 p9_debug(P9_DEBUG_ERROR,
247                                          "integer field, but no integer?\n");
248                                 ret = r;
249                         } else {
250                                 v9ses->afid = option;
251                         }
252                         break;
253                 case Opt_uname:
254                         kfree(v9ses->uname);
255                         v9ses->uname = match_strdup(&args[0]);
256                         if (!v9ses->uname) {
257                                 ret = -ENOMEM;
258                                 goto free_and_return;
259                         }
260                         break;
261                 case Opt_remotename:
262                         kfree(v9ses->aname);
263                         v9ses->aname = match_strdup(&args[0]);
264                         if (!v9ses->aname) {
265                                 ret = -ENOMEM;
266                                 goto free_and_return;
267                         }
268                         break;
269                 case Opt_nodevmap:
270                         v9ses->nodev = 1;
271                         break;
272                 case Opt_noxattr:
273                         v9ses->flags |= V9FS_NO_XATTR;
274                         break;
275                 case Opt_directio:
276                         v9ses->flags |= V9FS_DIRECT_IO;
277                         break;
278                 case Opt_ignoreqv:
279                         v9ses->flags |= V9FS_IGNORE_QV;
280                         break;
281                 case Opt_cachetag:
282 #ifdef CONFIG_9P_FSCACHE
283                         kfree(v9ses->cachetag);
284                         v9ses->cachetag = match_strdup(&args[0]);
285                         if (!v9ses->cachetag) {
286                                 ret = -ENOMEM;
287                                 goto free_and_return;
288                         }
289 #endif
290                         break;
291                 case Opt_cache:
292                         s = match_strdup(&args[0]);
293                         if (!s) {
294                                 ret = -ENOMEM;
295                                 p9_debug(P9_DEBUG_ERROR,
296                                          "problem allocating copy of cache arg\n");
297                                 goto free_and_return;
298                         }
299                         r = get_cache_mode(s);
300                         if (r < 0)
301                                 ret = r;
302                         else
303                                 v9ses->cache = r;
304
305                         kfree(s);
306                         break;
307
308                 case Opt_access:
309                         s = match_strdup(&args[0]);
310                         if (!s) {
311                                 ret = -ENOMEM;
312                                 p9_debug(P9_DEBUG_ERROR,
313                                          "problem allocating copy of access arg\n");
314                                 goto free_and_return;
315                         }
316
317                         v9ses->flags &= ~V9FS_ACCESS_MASK;
318                         if (strcmp(s, "user") == 0)
319                                 v9ses->flags |= V9FS_ACCESS_USER;
320                         else if (strcmp(s, "any") == 0)
321                                 v9ses->flags |= V9FS_ACCESS_ANY;
322                         else if (strcmp(s, "client") == 0) {
323                                 v9ses->flags |= V9FS_ACCESS_CLIENT;
324                         } else {
325                                 uid_t uid;
326
327                                 v9ses->flags |= V9FS_ACCESS_SINGLE;
328                                 r = kstrtouint(s, 10, &uid);
329                                 if (r) {
330                                         ret = r;
331                                         pr_info("Unknown access argument %s: %d\n",
332                                                 s, r);
333                                         kfree(s);
334                                         continue;
335                                 }
336                                 v9ses->uid = make_kuid(current_user_ns(), uid);
337                                 if (!uid_valid(v9ses->uid)) {
338                                         ret = -EINVAL;
339                                         pr_info("Unknown uid %s\n", s);
340                                 }
341                         }
342
343                         kfree(s);
344                         break;
345
346                 case Opt_posixacl:
347 #ifdef CONFIG_9P_FS_POSIX_ACL
348                         v9ses->flags |= V9FS_POSIX_ACL;
349 #else
350                         p9_debug(P9_DEBUG_ERROR,
351                                  "Not defined CONFIG_9P_FS_POSIX_ACL. Ignoring posixacl option\n");
352 #endif
353                         break;
354
355                 case Opt_locktimeout:
356                         r = match_int(&args[0], &option);
357                         if (r < 0) {
358                                 p9_debug(P9_DEBUG_ERROR,
359                                          "integer field, but no integer?\n");
360                                 ret = r;
361                                 continue;
362                         }
363                         if (option < 1) {
364                                 p9_debug(P9_DEBUG_ERROR,
365                                          "locktimeout must be a greater than zero integer.\n");
366                                 ret = -EINVAL;
367                                 continue;
368                         }
369                         v9ses->session_lock_timeout = (long)option * HZ;
370                         break;
371
372                 default:
373                         continue;
374                 }
375         }
376
377 free_and_return:
378         kfree(tmp_options);
379 fail_option_alloc:
380         return ret;
381 }
382
383 /**
384  * v9fs_session_init - initialize session
385  * @v9ses: session information structure
386  * @dev_name: device being mounted
387  * @data: options
388  *
389  */
390
391 struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses,
392                   const char *dev_name, char *data)
393 {
394         struct p9_fid *fid;
395         int rc = -ENOMEM;
396
397         v9ses->uname = kstrdup(V9FS_DEFUSER, GFP_KERNEL);
398         if (!v9ses->uname)
399                 goto err_names;
400
401         v9ses->aname = kstrdup(V9FS_DEFANAME, GFP_KERNEL);
402         if (!v9ses->aname)
403                 goto err_names;
404         init_rwsem(&v9ses->rename_sem);
405
406         v9ses->uid = INVALID_UID;
407         v9ses->dfltuid = V9FS_DEFUID;
408         v9ses->dfltgid = V9FS_DEFGID;
409
410         v9ses->clnt = p9_client_create(dev_name, data);
411         if (IS_ERR(v9ses->clnt)) {
412                 rc = PTR_ERR(v9ses->clnt);
413                 p9_debug(P9_DEBUG_ERROR, "problem initializing 9p client\n");
414                 goto err_names;
415         }
416
417         v9ses->flags = V9FS_ACCESS_USER;
418
419         if (p9_is_proto_dotl(v9ses->clnt)) {
420                 v9ses->flags = V9FS_ACCESS_CLIENT;
421                 v9ses->flags |= V9FS_PROTO_2000L;
422         } else if (p9_is_proto_dotu(v9ses->clnt)) {
423                 v9ses->flags |= V9FS_PROTO_2000U;
424         }
425
426         rc = v9fs_parse_options(v9ses, data);
427         if (rc < 0)
428                 goto err_clnt;
429
430         v9ses->maxdata = v9ses->clnt->msize - P9_IOHDRSZ;
431
432         if (!v9fs_proto_dotl(v9ses) &&
433             ((v9ses->flags & V9FS_ACCESS_MASK) == V9FS_ACCESS_CLIENT)) {
434                 /*
435                  * We support ACCESS_CLIENT only for dotl.
436                  * Fall back to ACCESS_USER
437                  */
438                 v9ses->flags &= ~V9FS_ACCESS_MASK;
439                 v9ses->flags |= V9FS_ACCESS_USER;
440         }
441         /*FIXME !! */
442         /* for legacy mode, fall back to V9FS_ACCESS_ANY */
443         if (!(v9fs_proto_dotu(v9ses) || v9fs_proto_dotl(v9ses)) &&
444                 ((v9ses->flags&V9FS_ACCESS_MASK) == V9FS_ACCESS_USER)) {
445
446                 v9ses->flags &= ~V9FS_ACCESS_MASK;
447                 v9ses->flags |= V9FS_ACCESS_ANY;
448                 v9ses->uid = INVALID_UID;
449         }
450         if (!v9fs_proto_dotl(v9ses) ||
451                 !((v9ses->flags & V9FS_ACCESS_MASK) == V9FS_ACCESS_CLIENT)) {
452                 /*
453                  * We support ACL checks on clinet only if the protocol is
454                  * 9P2000.L and access is V9FS_ACCESS_CLIENT.
455                  */
456                 v9ses->flags &= ~V9FS_ACL_MASK;
457         }
458
459         fid = p9_client_attach(v9ses->clnt, NULL, v9ses->uname, INVALID_UID,
460                                                         v9ses->aname);
461         if (IS_ERR(fid)) {
462                 rc = PTR_ERR(fid);
463                 p9_debug(P9_DEBUG_ERROR, "cannot attach\n");
464                 goto err_clnt;
465         }
466
467         if ((v9ses->flags & V9FS_ACCESS_MASK) == V9FS_ACCESS_SINGLE)
468                 fid->uid = v9ses->uid;
469         else
470                 fid->uid = INVALID_UID;
471
472 #ifdef CONFIG_9P_FSCACHE
473         /* register the session for caching */
474         if (v9ses->cache & CACHE_FSCACHE) {
475                 rc = v9fs_cache_session_get_cookie(v9ses, dev_name);
476                 if (rc < 0)
477                         goto err_clnt;
478         }
479 #endif
480         spin_lock(&v9fs_sessionlist_lock);
481         list_add(&v9ses->slist, &v9fs_sessionlist);
482         spin_unlock(&v9fs_sessionlist_lock);
483
484         return fid;
485
486 err_clnt:
487 #ifdef CONFIG_9P_FSCACHE
488         kfree(v9ses->cachetag);
489 #endif
490         p9_client_destroy(v9ses->clnt);
491 err_names:
492         kfree(v9ses->uname);
493         kfree(v9ses->aname);
494         return ERR_PTR(rc);
495 }
496
497 /**
498  * v9fs_session_close - shutdown a session
499  * @v9ses: session information structure
500  *
501  */
502
503 void v9fs_session_close(struct v9fs_session_info *v9ses)
504 {
505         if (v9ses->clnt) {
506                 p9_client_destroy(v9ses->clnt);
507                 v9ses->clnt = NULL;
508         }
509
510 #ifdef CONFIG_9P_FSCACHE
511         fscache_relinquish_volume(v9fs_session_cache(v9ses), NULL, false);
512         kfree(v9ses->cachetag);
513 #endif
514         kfree(v9ses->uname);
515         kfree(v9ses->aname);
516
517         spin_lock(&v9fs_sessionlist_lock);
518         list_del(&v9ses->slist);
519         spin_unlock(&v9fs_sessionlist_lock);
520 }
521
522 /**
523  * v9fs_session_cancel - terminate a session
524  * @v9ses: session to terminate
525  *
526  * mark transport as disconnected and cancel all pending requests.
527  */
528
529 void v9fs_session_cancel(struct v9fs_session_info *v9ses)
530 {
531         p9_debug(P9_DEBUG_ERROR, "cancel session %p\n", v9ses);
532         p9_client_disconnect(v9ses->clnt);
533 }
534
535 /**
536  * v9fs_session_begin_cancel - Begin terminate of a session
537  * @v9ses: session to terminate
538  *
539  * After this call we don't allow any request other than clunk.
540  */
541
542 void v9fs_session_begin_cancel(struct v9fs_session_info *v9ses)
543 {
544         p9_debug(P9_DEBUG_ERROR, "begin cancel session %p\n", v9ses);
545         p9_client_begin_disconnect(v9ses->clnt);
546 }
547
548 static struct kobject *v9fs_kobj;
549
550 #ifdef CONFIG_9P_FSCACHE
551 /*
552  * List caches associated with a session
553  */
554 static ssize_t caches_show(struct kobject *kobj,
555                            struct kobj_attribute *attr,
556                            char *buf)
557 {
558         ssize_t n = 0, count = 0, limit = PAGE_SIZE;
559         struct v9fs_session_info *v9ses;
560
561         spin_lock(&v9fs_sessionlist_lock);
562         list_for_each_entry(v9ses, &v9fs_sessionlist, slist) {
563                 if (v9ses->cachetag) {
564                         n = snprintf(buf, limit, "%s\n", v9ses->cachetag);
565                         if (n < 0) {
566                                 count = n;
567                                 break;
568                         }
569
570                         count += n;
571                         limit -= n;
572                 }
573         }
574
575         spin_unlock(&v9fs_sessionlist_lock);
576         return count;
577 }
578
579 static struct kobj_attribute v9fs_attr_cache = __ATTR_RO(caches);
580 #endif /* CONFIG_9P_FSCACHE */
581
582 static struct attribute *v9fs_attrs[] = {
583 #ifdef CONFIG_9P_FSCACHE
584         &v9fs_attr_cache.attr,
585 #endif
586         NULL,
587 };
588
589 static const struct attribute_group v9fs_attr_group = {
590         .attrs = v9fs_attrs,
591 };
592
593 /**
594  * v9fs_sysfs_init - Initialize the v9fs sysfs interface
595  *
596  */
597
598 static int __init v9fs_sysfs_init(void)
599 {
600         v9fs_kobj = kobject_create_and_add("9p", fs_kobj);
601         if (!v9fs_kobj)
602                 return -ENOMEM;
603
604         if (sysfs_create_group(v9fs_kobj, &v9fs_attr_group)) {
605                 kobject_put(v9fs_kobj);
606                 return -ENOMEM;
607         }
608
609         return 0;
610 }
611
612 /**
613  * v9fs_sysfs_cleanup - Unregister the v9fs sysfs interface
614  *
615  */
616
617 static void v9fs_sysfs_cleanup(void)
618 {
619         sysfs_remove_group(v9fs_kobj, &v9fs_attr_group);
620         kobject_put(v9fs_kobj);
621 }
622
623 static void v9fs_inode_init_once(void *foo)
624 {
625         struct v9fs_inode *v9inode = (struct v9fs_inode *)foo;
626
627         memset(&v9inode->qid, 0, sizeof(v9inode->qid));
628         inode_init_once(&v9inode->netfs.inode);
629 }
630
631 /**
632  * v9fs_init_inode_cache - initialize a cache for 9P
633  * Returns 0 on success.
634  */
635 static int v9fs_init_inode_cache(void)
636 {
637         v9fs_inode_cache = kmem_cache_create("v9fs_inode_cache",
638                                           sizeof(struct v9fs_inode),
639                                           0, (SLAB_RECLAIM_ACCOUNT|
640                                               SLAB_MEM_SPREAD|SLAB_ACCOUNT),
641                                           v9fs_inode_init_once);
642         if (!v9fs_inode_cache)
643                 return -ENOMEM;
644
645         return 0;
646 }
647
648 /**
649  * v9fs_destroy_inode_cache - destroy the cache of 9P inode
650  *
651  */
652 static void v9fs_destroy_inode_cache(void)
653 {
654         /*
655          * Make sure all delayed rcu free inodes are flushed before we
656          * destroy cache.
657          */
658         rcu_barrier();
659         kmem_cache_destroy(v9fs_inode_cache);
660 }
661
662 static int v9fs_cache_register(void)
663 {
664         int ret;
665
666         ret = v9fs_init_inode_cache();
667         if (ret < 0)
668                 return ret;
669         return ret;
670 }
671
672 static void v9fs_cache_unregister(void)
673 {
674         v9fs_destroy_inode_cache();
675 }
676
677 /**
678  * init_v9fs - Initialize module
679  *
680  */
681
682 static int __init init_v9fs(void)
683 {
684         int err;
685
686         pr_info("Installing v9fs 9p2000 file system support\n");
687         /* TODO: Setup list of registered trasnport modules */
688
689         err = v9fs_cache_register();
690         if (err < 0) {
691                 pr_err("Failed to register v9fs for caching\n");
692                 return err;
693         }
694
695         err = v9fs_sysfs_init();
696         if (err < 0) {
697                 pr_err("Failed to register with sysfs\n");
698                 goto out_cache;
699         }
700         err = register_filesystem(&v9fs_fs_type);
701         if (err < 0) {
702                 pr_err("Failed to register filesystem\n");
703                 goto out_sysfs_cleanup;
704         }
705
706         return 0;
707
708 out_sysfs_cleanup:
709         v9fs_sysfs_cleanup();
710
711 out_cache:
712         v9fs_cache_unregister();
713
714         return err;
715 }
716
717 /**
718  * exit_v9fs - shutdown module
719  *
720  */
721
722 static void __exit exit_v9fs(void)
723 {
724         v9fs_sysfs_cleanup();
725         v9fs_cache_unregister();
726         unregister_filesystem(&v9fs_fs_type);
727 }
728
729 module_init(init_v9fs)
730 module_exit(exit_v9fs)
731
732 MODULE_AUTHOR("Latchesar Ionkov <lucho@ionkov.net>");
733 MODULE_AUTHOR("Eric Van Hensbergen <ericvh@gmail.com>");
734 MODULE_AUTHOR("Ron Minnich <rminnich@lanl.gov>");
735 MODULE_DESCRIPTION("9P Client File System");
736 MODULE_LICENSE("GPL");