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