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 *
 159 * Return 0 upon success, -ERRNO upon failure.
 160 */
 161
 162static int v9fs_parse_options(struct v9fs_session_info *v9ses, char *opts)
 163{
 164        char *options, *tmp_options;
 165        substring_t args[MAX_OPT_ARGS];
 166        char *p;
 167        int option = 0;
 168        char *s, *e;
 169        int ret = 0;
 170
 171        /* setup defaults */
 172        v9ses->afid = ~0;
 173        v9ses->debug = 0;
 174        v9ses->cache = CACHE_NONE;
 175#ifdef CONFIG_9P_FSCACHE
 176        v9ses->cachetag = NULL;
 177#endif
 178        v9ses->session_lock_timeout = P9_LOCK_TIMEOUT;
 179
 180        if (!opts)
 181                return 0;
 182
 183        tmp_options = kstrdup(opts, GFP_KERNEL);
 184        if (!tmp_options) {
 185                ret = -ENOMEM;
 186                goto fail_option_alloc;
 187        }
 188        options = tmp_options;
 189
 190        while ((p = strsep(&options, ",")) != NULL) {
 191                int token, r;
 192                if (!*p)
 193                        continue;
 194                token = match_token(p, tokens, args);
 195                switch (token) {
 196                case Opt_debug:
 197                        r = match_int(&args[0], &option);
 198                        if (r < 0) {
 199                                p9_debug(P9_DEBUG_ERROR,
 200                                         "integer field, but no integer?\n");
 201                                ret = r;
 202                        } else {
 203                                v9ses->debug = option;
 204#ifdef CONFIG_NET_9P_DEBUG
 205                                p9_debug_level = option;
 206#endif
 207                        }
 208                        break;
 209
 210                case Opt_dfltuid:
 211                        r = match_int(&args[0], &option);
 212                        if (r < 0) {
 213                                p9_debug(P9_DEBUG_ERROR,
 214                                         "integer field, but no integer?\n");
 215                                ret = r;
 216                                continue;
 217                        }
 218                        v9ses->dfltuid = make_kuid(current_user_ns(), option);
 219                        if (!uid_valid(v9ses->dfltuid)) {
 220                                p9_debug(P9_DEBUG_ERROR,
 221                                         "uid field, but not a uid?\n");
 222                                ret = -EINVAL;
 223                        }
 224                        break;
 225                case Opt_dfltgid:
 226                        r = match_int(&args[0], &option);
 227                        if (r < 0) {
 228                                p9_debug(P9_DEBUG_ERROR,
 229                                         "integer field, but no integer?\n");
 230                                ret = r;
 231                                continue;
 232                        }
 233                        v9ses->dfltgid = make_kgid(current_user_ns(), option);
 234                        if (!gid_valid(v9ses->dfltgid)) {
 235                                p9_debug(P9_DEBUG_ERROR,
 236                                         "gid field, but not a gid?\n");
 237                                ret = -EINVAL;
 238                        }
 239                        break;
 240                case Opt_afid:
 241                        r = match_int(&args[0], &option);
 242                        if (r < 0) {
 243                                p9_debug(P9_DEBUG_ERROR,
 244                                         "integer field, but no integer?\n");
 245                                ret = r;
 246                        } else {
 247                                v9ses->afid = option;
 248                        }
 249                        break;
 250                case Opt_uname:
 251                        kfree(v9ses->uname);
 252                        v9ses->uname = match_strdup(&args[0]);
 253                        if (!v9ses->uname) {
 254                                ret = -ENOMEM;
 255                                goto free_and_return;
 256                        }
 257                        break;
 258                case Opt_remotename:
 259                        kfree(v9ses->aname);
 260                        v9ses->aname = match_strdup(&args[0]);
 261                        if (!v9ses->aname) {
 262                                ret = -ENOMEM;
 263                                goto free_and_return;
 264                        }
 265                        break;
 266                case Opt_nodevmap:
 267                        v9ses->nodev = 1;
 268                        break;
 269                case Opt_cache_loose:
 270                        v9ses->cache = CACHE_LOOSE;
 271                        break;
 272                case Opt_fscache:
 273                        v9ses->cache = CACHE_FSCACHE;
 274                        break;
 275                case Opt_mmap:
 276                        v9ses->cache = CACHE_MMAP;
 277                        break;
 278                case Opt_cachetag:
 279#ifdef CONFIG_9P_FSCACHE
 280                        kfree(v9ses->cachetag);
 281                        v9ses->cachetag = match_strdup(&args[0]);
 282                        if (!v9ses->cachetag) {
 283                                ret = -ENOMEM;
 284                                goto free_and_return;
 285                        }
 286#endif
 287                        break;
 288                case Opt_cache:
 289                        s = match_strdup(&args[0]);
 290                        if (!s) {
 291                                ret = -ENOMEM;
 292                                p9_debug(P9_DEBUG_ERROR,
 293                                         "problem allocating copy of cache arg\n");
 294                                goto free_and_return;
 295                        }
 296                        r = get_cache_mode(s);
 297                        if (r < 0)
 298                                ret = r;
 299                        else
 300                                v9ses->cache = r;
 301
 302                        kfree(s);
 303                        break;
 304
 305                case Opt_access:
 306                        s = match_strdup(&args[0]);
 307                        if (!s) {
 308                                ret = -ENOMEM;
 309                                p9_debug(P9_DEBUG_ERROR,
 310                                         "problem allocating copy of access arg\n");
 311                                goto free_and_return;
 312                        }
 313
 314                        v9ses->flags &= ~V9FS_ACCESS_MASK;
 315                        if (strcmp(s, "user") == 0)
 316                                v9ses->flags |= V9FS_ACCESS_USER;
 317                        else if (strcmp(s, "any") == 0)
 318                                v9ses->flags |= V9FS_ACCESS_ANY;
 319                        else if (strcmp(s, "client") == 0) {
 320                                v9ses->flags |= V9FS_ACCESS_CLIENT;
 321                        } else {
 322                                uid_t uid;
 323                                v9ses->flags |= V9FS_ACCESS_SINGLE;
 324                                uid = simple_strtoul(s, &e, 10);
 325                                if (*e != '\0') {
 326                                        ret = -EINVAL;
 327                                        pr_info("Unknown access argument %s\n",
 328                                                s);
 329                                        kfree(s);
 330                                        continue;
 331                                }
 332                                v9ses->uid = make_kuid(current_user_ns(), uid);
 333                                if (!uid_valid(v9ses->uid)) {
 334                                        ret = -EINVAL;
 335                                        pr_info("Unknown uid %s\n", s);
 336                                }
 337                        }
 338
 339                        kfree(s);
 340                        break;
 341
 342                case Opt_posixacl:
 343#ifdef CONFIG_9P_FS_POSIX_ACL
 344                        v9ses->flags |= V9FS_POSIX_ACL;
 345#else
 346                        p9_debug(P9_DEBUG_ERROR,
 347                                 "Not defined CONFIG_9P_FS_POSIX_ACL. Ignoring posixacl option\n");
 348#endif
 349                        break;
 350
 351                case Opt_locktimeout:
 352                        r = match_int(&args[0], &option);
 353                        if (r < 0) {
 354                                p9_debug(P9_DEBUG_ERROR,
 355                                         "integer field, but no integer?\n");
 356                                ret = r;
 357                                continue;
 358                        }
 359                        if (option < 1) {
 360                                p9_debug(P9_DEBUG_ERROR,
 361                                         "locktimeout must be a greater than zero integer.\n");
 362                                ret = -EINVAL;
 363                                continue;
 364                        }
 365                        v9ses->session_lock_timeout = (long)option * HZ;
 366                        break;
 367
 368                default:
 369                        continue;
 370                }
 371        }
 372
 373free_and_return:
 374        kfree(tmp_options);
 375fail_option_alloc:
 376        return ret;
 377}
 378
 379/**
 380 * v9fs_session_init - initialize session
 381 * @v9ses: session information structure
 382 * @dev_name: device being mounted
 383 * @data: options
 384 *
 385 */
 386
 387struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses,
 388                  const char *dev_name, char *data)
 389{
 390        struct p9_fid *fid;
 391        int rc = -ENOMEM;
 392
 393        v9ses->uname = kstrdup(V9FS_DEFUSER, GFP_KERNEL);
 394        if (!v9ses->uname)
 395                goto err_names;
 396
 397        v9ses->aname = kstrdup(V9FS_DEFANAME, GFP_KERNEL);
 398        if (!v9ses->aname)
 399                goto err_names;
 400        init_rwsem(&v9ses->rename_sem);
 401
 402        v9ses->uid = INVALID_UID;
 403        v9ses->dfltuid = V9FS_DEFUID;
 404        v9ses->dfltgid = V9FS_DEFGID;
 405
 406        v9ses->clnt = p9_client_create(dev_name, data);
 407        if (IS_ERR(v9ses->clnt)) {
 408                rc = PTR_ERR(v9ses->clnt);
 409                p9_debug(P9_DEBUG_ERROR, "problem initializing 9p client\n");
 410                goto err_names;
 411        }
 412
 413        v9ses->flags = V9FS_ACCESS_USER;
 414
 415        if (p9_is_proto_dotl(v9ses->clnt)) {
 416                v9ses->flags = V9FS_ACCESS_CLIENT;
 417                v9ses->flags |= V9FS_PROTO_2000L;
 418        } else if (p9_is_proto_dotu(v9ses->clnt)) {
 419                v9ses->flags |= V9FS_PROTO_2000U;
 420        }
 421
 422        rc = v9fs_parse_options(v9ses, data);
 423        if (rc < 0)
 424                goto err_clnt;
 425
 426        v9ses->maxdata = v9ses->clnt->msize - P9_IOHDRSZ;
 427
 428        if (!v9fs_proto_dotl(v9ses) &&
 429            ((v9ses->flags & V9FS_ACCESS_MASK) == V9FS_ACCESS_CLIENT)) {
 430                /*
 431                 * We support ACCESS_CLIENT only for dotl.
 432                 * Fall back to ACCESS_USER
 433                 */
 434                v9ses->flags &= ~V9FS_ACCESS_MASK;
 435                v9ses->flags |= V9FS_ACCESS_USER;
 436        }
 437        /*FIXME !! */
 438        /* for legacy mode, fall back to V9FS_ACCESS_ANY */
 439        if (!(v9fs_proto_dotu(v9ses) || v9fs_proto_dotl(v9ses)) &&
 440                ((v9ses->flags&V9FS_ACCESS_MASK) == V9FS_ACCESS_USER)) {
 441
 442                v9ses->flags &= ~V9FS_ACCESS_MASK;
 443                v9ses->flags |= V9FS_ACCESS_ANY;
 444                v9ses->uid = INVALID_UID;
 445        }
 446        if (!v9fs_proto_dotl(v9ses) ||
 447                !((v9ses->flags & V9FS_ACCESS_MASK) == V9FS_ACCESS_CLIENT)) {
 448                /*
 449                 * We support ACL checks on clinet only if the protocol is
 450                 * 9P2000.L and access is V9FS_ACCESS_CLIENT.
 451                 */
 452                v9ses->flags &= ~V9FS_ACL_MASK;
 453        }
 454
 455        fid = p9_client_attach(v9ses->clnt, NULL, v9ses->uname, INVALID_UID,
 456                                                        v9ses->aname);
 457        if (IS_ERR(fid)) {
 458                rc = PTR_ERR(fid);
 459                p9_debug(P9_DEBUG_ERROR, "cannot attach\n");
 460                goto err_clnt;
 461        }
 462
 463        if ((v9ses->flags & V9FS_ACCESS_MASK) == V9FS_ACCESS_SINGLE)
 464                fid->uid = v9ses->uid;
 465        else
 466                fid->uid = INVALID_UID;
 467
 468#ifdef CONFIG_9P_FSCACHE
 469        /* register the session for caching */
 470        v9fs_cache_session_get_cookie(v9ses);
 471#endif
 472        spin_lock(&v9fs_sessionlist_lock);
 473        list_add(&v9ses->slist, &v9fs_sessionlist);
 474        spin_unlock(&v9fs_sessionlist_lock);
 475
 476        return fid;
 477
 478err_clnt:
 479#ifdef CONFIG_9P_FSCACHE
 480        kfree(v9ses->cachetag);
 481#endif
 482        p9_client_destroy(v9ses->clnt);
 483err_names:
 484        kfree(v9ses->uname);
 485        kfree(v9ses->aname);
 486        return ERR_PTR(rc);
 487}
 488
 489/**
 490 * v9fs_session_close - shutdown a session
 491 * @v9ses: session information structure
 492 *
 493 */
 494
 495void v9fs_session_close(struct v9fs_session_info *v9ses)
 496{
 497        if (v9ses->clnt) {
 498                p9_client_destroy(v9ses->clnt);
 499                v9ses->clnt = NULL;
 500        }
 501
 502#ifdef CONFIG_9P_FSCACHE
 503        if (v9ses->fscache)
 504                v9fs_cache_session_put_cookie(v9ses);
 505        kfree(v9ses->cachetag);
 506#endif
 507        kfree(v9ses->uname);
 508        kfree(v9ses->aname);
 509
 510        spin_lock(&v9fs_sessionlist_lock);
 511        list_del(&v9ses->slist);
 512        spin_unlock(&v9fs_sessionlist_lock);
 513}
 514
 515/**
 516 * v9fs_session_cancel - terminate a session
 517 * @v9ses: session to terminate
 518 *
 519 * mark transport as disconnected and cancel all pending requests.
 520 */
 521
 522void v9fs_session_cancel(struct v9fs_session_info *v9ses) {
 523        p9_debug(P9_DEBUG_ERROR, "cancel session %p\n", v9ses);
 524        p9_client_disconnect(v9ses->clnt);
 525}
 526
 527/**
 528 * v9fs_session_begin_cancel - Begin terminate of a session
 529 * @v9ses: session to terminate
 530 *
 531 * After this call we don't allow any request other than clunk.
 532 */
 533
 534void v9fs_session_begin_cancel(struct v9fs_session_info *v9ses)
 535{
 536        p9_debug(P9_DEBUG_ERROR, "begin cancel session %p\n", v9ses);
 537        p9_client_begin_disconnect(v9ses->clnt);
 538}
 539
 540extern int v9fs_error_init(void);
 541
 542static struct kobject *v9fs_kobj;
 543
 544#ifdef CONFIG_9P_FSCACHE
 545/**
 546 * caches_show - list caches associated with a session
 547 *
 548 * Returns the size of buffer written.
 549 */
 550
 551static ssize_t caches_show(struct kobject *kobj,
 552                           struct kobj_attribute *attr,
 553                           char *buf)
 554{
 555        ssize_t n = 0, count = 0, limit = PAGE_SIZE;
 556        struct v9fs_session_info *v9ses;
 557
 558        spin_lock(&v9fs_sessionlist_lock);
 559        list_for_each_entry(v9ses, &v9fs_sessionlist, slist) {
 560                if (v9ses->cachetag) {
 561                        n = snprintf(buf, limit, "%s\n", v9ses->cachetag);
 562                        if (n < 0) {
 563                                count = n;
 564                                break;
 565                        }
 566
 567                        count += n;
 568                        limit -= n;
 569                }
 570        }
 571
 572        spin_unlock(&v9fs_sessionlist_lock);
 573        return count;
 574}
 575
 576static struct kobj_attribute v9fs_attr_cache = __ATTR_RO(caches);
 577#endif /* CONFIG_9P_FSCACHE */
 578
 579static struct attribute *v9fs_attrs[] = {
 580#ifdef CONFIG_9P_FSCACHE
 581        &v9fs_attr_cache.attr,
 582#endif
 583        NULL,
 584};
 585
 586static const struct attribute_group v9fs_attr_group = {
 587        .attrs = v9fs_attrs,
 588};
 589
 590/**
 591 * v9fs_sysfs_init - Initialize the v9fs sysfs interface
 592 *
 593 */
 594
 595static int __init v9fs_sysfs_init(void)
 596{
 597        v9fs_kobj = kobject_create_and_add("9p", fs_kobj);
 598        if (!v9fs_kobj)
 599                return -ENOMEM;
 600
 601        if (sysfs_create_group(v9fs_kobj, &v9fs_attr_group)) {
 602                kobject_put(v9fs_kobj);
 603                return -ENOMEM;
 604        }
 605
 606        return 0;
 607}
 608
 609/**
 610 * v9fs_sysfs_cleanup - Unregister the v9fs sysfs interface
 611 *
 612 */
 613
 614static void v9fs_sysfs_cleanup(void)
 615{
 616        sysfs_remove_group(v9fs_kobj, &v9fs_attr_group);
 617        kobject_put(v9fs_kobj);
 618}
 619
 620static void v9fs_inode_init_once(void *foo)
 621{
 622        struct v9fs_inode *v9inode = (struct v9fs_inode *)foo;
 623#ifdef CONFIG_9P_FSCACHE
 624        v9inode->fscache = NULL;
 625#endif
 626        memset(&v9inode->qid, 0, sizeof(v9inode->qid));
 627        inode_init_once(&v9inode->vfs_inode);
 628}
 629
 630/**
 631 * v9fs_init_inode_cache - initialize a cache for 9P
 632 * Returns 0 on success.
 633 */
 634static int v9fs_init_inode_cache(void)
 635{
 636        v9fs_inode_cache = kmem_cache_create("v9fs_inode_cache",
 637                                          sizeof(struct v9fs_inode),
 638                                          0, (SLAB_RECLAIM_ACCOUNT|
 639                                              SLAB_MEM_SPREAD|SLAB_ACCOUNT),
 640                                          v9fs_inode_init_once);
 641        if (!v9fs_inode_cache)
 642                return -ENOMEM;
 643
 644        return 0;
 645}
 646
 647/**
 648 * v9fs_destroy_inode_cache - destroy the cache of 9P inode
 649 *
 650 */
 651static void v9fs_destroy_inode_cache(void)
 652{
 653        /*
 654         * Make sure all delayed rcu free inodes are flushed before we
 655         * destroy cache.
 656         */
 657        rcu_barrier();
 658        kmem_cache_destroy(v9fs_inode_cache);
 659}
 660
 661static int v9fs_cache_register(void)
 662{
 663        int ret;
 664        ret = v9fs_init_inode_cache();
 665        if (ret < 0)
 666                return ret;
 667#ifdef CONFIG_9P_FSCACHE
 668        ret = fscache_register_netfs(&v9fs_cache_netfs);
 669        if (ret < 0)
 670                v9fs_destroy_inode_cache();
 671#endif
 672        return ret;
 673}
 674
 675static void v9fs_cache_unregister(void)
 676{
 677        v9fs_destroy_inode_cache();
 678#ifdef CONFIG_9P_FSCACHE
 679        fscache_unregister_netfs(&v9fs_cache_netfs);
 680#endif
 681}
 682
 683/**
 684 * init_v9fs - Initialize module
 685 *
 686 */
 687
 688static int __init init_v9fs(void)
 689{
 690        int err;
 691        pr_info("Installing v9fs 9p2000 file system support\n");
 692        /* TODO: Setup list of registered trasnport modules */
 693
 694        err = v9fs_cache_register();
 695        if (err < 0) {
 696                pr_err("Failed to register v9fs for caching\n");
 697                return err;
 698        }
 699
 700        err = v9fs_sysfs_init();
 701        if (err < 0) {
 702                pr_err("Failed to register with sysfs\n");
 703                goto out_cache;
 704        }
 705        err = register_filesystem(&v9fs_fs_type);
 706        if (err < 0) {
 707                pr_err("Failed to register filesystem\n");
 708                goto out_sysfs_cleanup;
 709        }
 710
 711        return 0;
 712
 713out_sysfs_cleanup:
 714        v9fs_sysfs_cleanup();
 715
 716out_cache:
 717        v9fs_cache_unregister();
 718
 719        return err;
 720}
 721
 722/**
 723 * exit_v9fs - shutdown module
 724 *
 725 */
 726
 727static void __exit exit_v9fs(void)
 728{
 729        v9fs_sysfs_cleanup();
 730        v9fs_cache_unregister();
 731        unregister_filesystem(&v9fs_fs_type);
 732}
 733
 734module_init(init_v9fs)
 735module_exit(exit_v9fs)
 736
 737MODULE_AUTHOR("Latchesar Ionkov <lucho@ionkov.net>");
 738MODULE_AUTHOR("Eric Van Hensbergen <ericvh@gmail.com>");
 739MODULE_AUTHOR("Ron Minnich <rminnich@lanl.gov>");
 740MODULE_LICENSE("GPL");
 741