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