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