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