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