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