0001
0002
0003
0004
0005
0006
0007
0008
0009 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0010
0011 #include <linux/module.h>
0012 #include <linux/errno.h>
0013 #include <linux/fs.h>
0014 #include <linux/sched.h>
0015 #include <linux/cred.h>
0016 #include <linux/parser.h>
0017 #include <linux/idr.h>
0018 #include <linux/slab.h>
0019 #include <linux/seq_file.h>
0020 #include <net/9p/9p.h>
0021 #include <net/9p/client.h>
0022 #include <net/9p/transport.h>
0023 #include "v9fs.h"
0024 #include "v9fs_vfs.h"
0025 #include "cache.h"
0026
0027 static DEFINE_SPINLOCK(v9fs_sessionlist_lock);
0028 static LIST_HEAD(v9fs_sessionlist);
0029 struct kmem_cache *v9fs_inode_cache;
0030
0031
0032
0033
0034
0035
0036 enum {
0037
0038 Opt_debug, Opt_dfltuid, Opt_dfltgid, Opt_afid,
0039
0040 Opt_uname, Opt_remotename, Opt_cache, Opt_cachetag,
0041
0042 Opt_nodevmap,
0043
0044 Opt_cache_loose, Opt_fscache, Opt_mmap,
0045
0046 Opt_access, Opt_posixacl,
0047
0048 Opt_locktimeout,
0049
0050 Opt_err
0051 };
0052
0053 static const match_table_t tokens = {
0054 {Opt_debug, "debug=%x"},
0055 {Opt_dfltuid, "dfltuid=%u"},
0056 {Opt_dfltgid, "dfltgid=%u"},
0057 {Opt_afid, "afid=%u"},
0058 {Opt_uname, "uname=%s"},
0059 {Opt_remotename, "aname=%s"},
0060 {Opt_nodevmap, "nodevmap"},
0061 {Opt_cache, "cache=%s"},
0062 {Opt_cache_loose, "loose"},
0063 {Opt_fscache, "fscache"},
0064 {Opt_mmap, "mmap"},
0065 {Opt_cachetag, "cachetag=%s"},
0066 {Opt_access, "access=%s"},
0067 {Opt_posixacl, "posixacl"},
0068 {Opt_locktimeout, "locktimeout=%u"},
0069 {Opt_err, NULL}
0070 };
0071
0072 static const char *const v9fs_cache_modes[nr__p9_cache_modes] = {
0073 [CACHE_NONE] = "none",
0074 [CACHE_MMAP] = "mmap",
0075 [CACHE_LOOSE] = "loose",
0076 [CACHE_FSCACHE] = "fscache",
0077 };
0078
0079
0080 static int get_cache_mode(char *s)
0081 {
0082 int version = -EINVAL;
0083
0084 if (!strcmp(s, "loose")) {
0085 version = CACHE_LOOSE;
0086 p9_debug(P9_DEBUG_9P, "Cache mode: loose\n");
0087 } else if (!strcmp(s, "fscache")) {
0088 version = CACHE_FSCACHE;
0089 p9_debug(P9_DEBUG_9P, "Cache mode: fscache\n");
0090 } else if (!strcmp(s, "mmap")) {
0091 version = CACHE_MMAP;
0092 p9_debug(P9_DEBUG_9P, "Cache mode: mmap\n");
0093 } else if (!strcmp(s, "none")) {
0094 version = CACHE_NONE;
0095 p9_debug(P9_DEBUG_9P, "Cache mode: none\n");
0096 } else
0097 pr_info("Unknown Cache mode %s\n", s);
0098 return version;
0099 }
0100
0101
0102
0103
0104 int v9fs_show_options(struct seq_file *m, struct dentry *root)
0105 {
0106 struct v9fs_session_info *v9ses = root->d_sb->s_fs_info;
0107
0108 if (v9ses->debug)
0109 seq_printf(m, ",debug=%x", v9ses->debug);
0110 if (!uid_eq(v9ses->dfltuid, V9FS_DEFUID))
0111 seq_printf(m, ",dfltuid=%u",
0112 from_kuid_munged(&init_user_ns, v9ses->dfltuid));
0113 if (!gid_eq(v9ses->dfltgid, V9FS_DEFGID))
0114 seq_printf(m, ",dfltgid=%u",
0115 from_kgid_munged(&init_user_ns, v9ses->dfltgid));
0116 if (v9ses->afid != ~0)
0117 seq_printf(m, ",afid=%u", v9ses->afid);
0118 if (strcmp(v9ses->uname, V9FS_DEFUSER) != 0)
0119 seq_printf(m, ",uname=%s", v9ses->uname);
0120 if (strcmp(v9ses->aname, V9FS_DEFANAME) != 0)
0121 seq_printf(m, ",aname=%s", v9ses->aname);
0122 if (v9ses->nodev)
0123 seq_puts(m, ",nodevmap");
0124 if (v9ses->cache)
0125 seq_printf(m, ",%s", v9fs_cache_modes[v9ses->cache]);
0126 #ifdef CONFIG_9P_FSCACHE
0127 if (v9ses->cachetag && v9ses->cache == CACHE_FSCACHE)
0128 seq_printf(m, ",cachetag=%s", v9ses->cachetag);
0129 #endif
0130
0131 switch (v9ses->flags & V9FS_ACCESS_MASK) {
0132 case V9FS_ACCESS_USER:
0133 seq_puts(m, ",access=user");
0134 break;
0135 case V9FS_ACCESS_ANY:
0136 seq_puts(m, ",access=any");
0137 break;
0138 case V9FS_ACCESS_CLIENT:
0139 seq_puts(m, ",access=client");
0140 break;
0141 case V9FS_ACCESS_SINGLE:
0142 seq_printf(m, ",access=%u",
0143 from_kuid_munged(&init_user_ns, v9ses->uid));
0144 break;
0145 }
0146
0147 if (v9ses->flags & V9FS_POSIX_ACL)
0148 seq_puts(m, ",posixacl");
0149
0150 return p9_show_client_options(m, v9ses->clnt);
0151 }
0152
0153
0154
0155
0156
0157
0158
0159
0160
0161 static int v9fs_parse_options(struct v9fs_session_info *v9ses, char *opts)
0162 {
0163 char *options, *tmp_options;
0164 substring_t args[MAX_OPT_ARGS];
0165 char *p;
0166 int option = 0;
0167 char *s;
0168 int ret = 0;
0169
0170
0171 v9ses->afid = ~0;
0172 v9ses->debug = 0;
0173 v9ses->cache = CACHE_NONE;
0174 #ifdef CONFIG_9P_FSCACHE
0175 v9ses->cachetag = NULL;
0176 #endif
0177 v9ses->session_lock_timeout = P9_LOCK_TIMEOUT;
0178
0179 if (!opts)
0180 return 0;
0181
0182 tmp_options = kstrdup(opts, GFP_KERNEL);
0183 if (!tmp_options) {
0184 ret = -ENOMEM;
0185 goto fail_option_alloc;
0186 }
0187 options = tmp_options;
0188
0189 while ((p = strsep(&options, ",")) != NULL) {
0190 int token, r;
0191
0192 if (!*p)
0193 continue;
0194
0195 token = match_token(p, tokens, args);
0196 switch (token) {
0197 case Opt_debug:
0198 r = match_int(&args[0], &option);
0199 if (r < 0) {
0200 p9_debug(P9_DEBUG_ERROR,
0201 "integer field, but no integer?\n");
0202 ret = r;
0203 } else {
0204 v9ses->debug = option;
0205 #ifdef CONFIG_NET_9P_DEBUG
0206 p9_debug_level = option;
0207 #endif
0208 }
0209 break;
0210
0211 case Opt_dfltuid:
0212 r = match_int(&args[0], &option);
0213 if (r < 0) {
0214 p9_debug(P9_DEBUG_ERROR,
0215 "integer field, but no integer?\n");
0216 ret = r;
0217 continue;
0218 }
0219 v9ses->dfltuid = make_kuid(current_user_ns(), option);
0220 if (!uid_valid(v9ses->dfltuid)) {
0221 p9_debug(P9_DEBUG_ERROR,
0222 "uid field, but not a uid?\n");
0223 ret = -EINVAL;
0224 }
0225 break;
0226 case Opt_dfltgid:
0227 r = match_int(&args[0], &option);
0228 if (r < 0) {
0229 p9_debug(P9_DEBUG_ERROR,
0230 "integer field, but no integer?\n");
0231 ret = r;
0232 continue;
0233 }
0234 v9ses->dfltgid = make_kgid(current_user_ns(), option);
0235 if (!gid_valid(v9ses->dfltgid)) {
0236 p9_debug(P9_DEBUG_ERROR,
0237 "gid field, but not a gid?\n");
0238 ret = -EINVAL;
0239 }
0240 break;
0241 case Opt_afid:
0242 r = match_int(&args[0], &option);
0243 if (r < 0) {
0244 p9_debug(P9_DEBUG_ERROR,
0245 "integer field, but no integer?\n");
0246 ret = r;
0247 } else {
0248 v9ses->afid = option;
0249 }
0250 break;
0251 case Opt_uname:
0252 kfree(v9ses->uname);
0253 v9ses->uname = match_strdup(&args[0]);
0254 if (!v9ses->uname) {
0255 ret = -ENOMEM;
0256 goto free_and_return;
0257 }
0258 break;
0259 case Opt_remotename:
0260 kfree(v9ses->aname);
0261 v9ses->aname = match_strdup(&args[0]);
0262 if (!v9ses->aname) {
0263 ret = -ENOMEM;
0264 goto free_and_return;
0265 }
0266 break;
0267 case Opt_nodevmap:
0268 v9ses->nodev = 1;
0269 break;
0270 case Opt_cache_loose:
0271 v9ses->cache = CACHE_LOOSE;
0272 break;
0273 case Opt_fscache:
0274 v9ses->cache = CACHE_FSCACHE;
0275 break;
0276 case Opt_mmap:
0277 v9ses->cache = CACHE_MMAP;
0278 break;
0279 case Opt_cachetag:
0280 #ifdef CONFIG_9P_FSCACHE
0281 kfree(v9ses->cachetag);
0282 v9ses->cachetag = match_strdup(&args[0]);
0283 if (!v9ses->cachetag) {
0284 ret = -ENOMEM;
0285 goto free_and_return;
0286 }
0287 #endif
0288 break;
0289 case Opt_cache:
0290 s = match_strdup(&args[0]);
0291 if (!s) {
0292 ret = -ENOMEM;
0293 p9_debug(P9_DEBUG_ERROR,
0294 "problem allocating copy of cache arg\n");
0295 goto free_and_return;
0296 }
0297 r = get_cache_mode(s);
0298 if (r < 0)
0299 ret = r;
0300 else
0301 v9ses->cache = r;
0302
0303 kfree(s);
0304 break;
0305
0306 case Opt_access:
0307 s = match_strdup(&args[0]);
0308 if (!s) {
0309 ret = -ENOMEM;
0310 p9_debug(P9_DEBUG_ERROR,
0311 "problem allocating copy of access arg\n");
0312 goto free_and_return;
0313 }
0314
0315 v9ses->flags &= ~V9FS_ACCESS_MASK;
0316 if (strcmp(s, "user") == 0)
0317 v9ses->flags |= V9FS_ACCESS_USER;
0318 else if (strcmp(s, "any") == 0)
0319 v9ses->flags |= V9FS_ACCESS_ANY;
0320 else if (strcmp(s, "client") == 0) {
0321 v9ses->flags |= V9FS_ACCESS_CLIENT;
0322 } else {
0323 uid_t uid;
0324
0325 v9ses->flags |= V9FS_ACCESS_SINGLE;
0326 r = kstrtouint(s, 10, &uid);
0327 if (r) {
0328 ret = r;
0329 pr_info("Unknown access argument %s: %d\n",
0330 s, r);
0331 kfree(s);
0332 continue;
0333 }
0334 v9ses->uid = make_kuid(current_user_ns(), uid);
0335 if (!uid_valid(v9ses->uid)) {
0336 ret = -EINVAL;
0337 pr_info("Unknown uid %s\n", s);
0338 }
0339 }
0340
0341 kfree(s);
0342 break;
0343
0344 case Opt_posixacl:
0345 #ifdef CONFIG_9P_FS_POSIX_ACL
0346 v9ses->flags |= V9FS_POSIX_ACL;
0347 #else
0348 p9_debug(P9_DEBUG_ERROR,
0349 "Not defined CONFIG_9P_FS_POSIX_ACL. Ignoring posixacl option\n");
0350 #endif
0351 break;
0352
0353 case Opt_locktimeout:
0354 r = match_int(&args[0], &option);
0355 if (r < 0) {
0356 p9_debug(P9_DEBUG_ERROR,
0357 "integer field, but no integer?\n");
0358 ret = r;
0359 continue;
0360 }
0361 if (option < 1) {
0362 p9_debug(P9_DEBUG_ERROR,
0363 "locktimeout must be a greater than zero integer.\n");
0364 ret = -EINVAL;
0365 continue;
0366 }
0367 v9ses->session_lock_timeout = (long)option * HZ;
0368 break;
0369
0370 default:
0371 continue;
0372 }
0373 }
0374
0375 free_and_return:
0376 kfree(tmp_options);
0377 fail_option_alloc:
0378 return ret;
0379 }
0380
0381
0382
0383
0384
0385
0386
0387
0388
0389 struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses,
0390 const char *dev_name, char *data)
0391 {
0392 struct p9_fid *fid;
0393 int rc = -ENOMEM;
0394
0395 v9ses->uname = kstrdup(V9FS_DEFUSER, GFP_KERNEL);
0396 if (!v9ses->uname)
0397 goto err_names;
0398
0399 v9ses->aname = kstrdup(V9FS_DEFANAME, GFP_KERNEL);
0400 if (!v9ses->aname)
0401 goto err_names;
0402 init_rwsem(&v9ses->rename_sem);
0403
0404 v9ses->uid = INVALID_UID;
0405 v9ses->dfltuid = V9FS_DEFUID;
0406 v9ses->dfltgid = V9FS_DEFGID;
0407
0408 v9ses->clnt = p9_client_create(dev_name, data);
0409 if (IS_ERR(v9ses->clnt)) {
0410 rc = PTR_ERR(v9ses->clnt);
0411 p9_debug(P9_DEBUG_ERROR, "problem initializing 9p client\n");
0412 goto err_names;
0413 }
0414
0415 v9ses->flags = V9FS_ACCESS_USER;
0416
0417 if (p9_is_proto_dotl(v9ses->clnt)) {
0418 v9ses->flags = V9FS_ACCESS_CLIENT;
0419 v9ses->flags |= V9FS_PROTO_2000L;
0420 } else if (p9_is_proto_dotu(v9ses->clnt)) {
0421 v9ses->flags |= V9FS_PROTO_2000U;
0422 }
0423
0424 rc = v9fs_parse_options(v9ses, data);
0425 if (rc < 0)
0426 goto err_clnt;
0427
0428 v9ses->maxdata = v9ses->clnt->msize - P9_IOHDRSZ;
0429
0430 if (!v9fs_proto_dotl(v9ses) &&
0431 ((v9ses->flags & V9FS_ACCESS_MASK) == V9FS_ACCESS_CLIENT)) {
0432
0433
0434
0435
0436 v9ses->flags &= ~V9FS_ACCESS_MASK;
0437 v9ses->flags |= V9FS_ACCESS_USER;
0438 }
0439
0440
0441 if (!(v9fs_proto_dotu(v9ses) || v9fs_proto_dotl(v9ses)) &&
0442 ((v9ses->flags&V9FS_ACCESS_MASK) == V9FS_ACCESS_USER)) {
0443
0444 v9ses->flags &= ~V9FS_ACCESS_MASK;
0445 v9ses->flags |= V9FS_ACCESS_ANY;
0446 v9ses->uid = INVALID_UID;
0447 }
0448 if (!v9fs_proto_dotl(v9ses) ||
0449 !((v9ses->flags & V9FS_ACCESS_MASK) == V9FS_ACCESS_CLIENT)) {
0450
0451
0452
0453
0454 v9ses->flags &= ~V9FS_ACL_MASK;
0455 }
0456
0457 fid = p9_client_attach(v9ses->clnt, NULL, v9ses->uname, INVALID_UID,
0458 v9ses->aname);
0459 if (IS_ERR(fid)) {
0460 rc = PTR_ERR(fid);
0461 p9_debug(P9_DEBUG_ERROR, "cannot attach\n");
0462 goto err_clnt;
0463 }
0464
0465 if ((v9ses->flags & V9FS_ACCESS_MASK) == V9FS_ACCESS_SINGLE)
0466 fid->uid = v9ses->uid;
0467 else
0468 fid->uid = INVALID_UID;
0469
0470 #ifdef CONFIG_9P_FSCACHE
0471
0472 if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE) {
0473 rc = v9fs_cache_session_get_cookie(v9ses, dev_name);
0474 if (rc < 0)
0475 goto err_clnt;
0476 }
0477 #endif
0478 spin_lock(&v9fs_sessionlist_lock);
0479 list_add(&v9ses->slist, &v9fs_sessionlist);
0480 spin_unlock(&v9fs_sessionlist_lock);
0481
0482 return fid;
0483
0484 err_clnt:
0485 #ifdef CONFIG_9P_FSCACHE
0486 kfree(v9ses->cachetag);
0487 #endif
0488 p9_client_destroy(v9ses->clnt);
0489 err_names:
0490 kfree(v9ses->uname);
0491 kfree(v9ses->aname);
0492 return ERR_PTR(rc);
0493 }
0494
0495
0496
0497
0498
0499
0500
0501 void v9fs_session_close(struct v9fs_session_info *v9ses)
0502 {
0503 if (v9ses->clnt) {
0504 p9_client_destroy(v9ses->clnt);
0505 v9ses->clnt = NULL;
0506 }
0507
0508 #ifdef CONFIG_9P_FSCACHE
0509 fscache_relinquish_volume(v9fs_session_cache(v9ses), NULL, false);
0510 kfree(v9ses->cachetag);
0511 #endif
0512 kfree(v9ses->uname);
0513 kfree(v9ses->aname);
0514
0515 spin_lock(&v9fs_sessionlist_lock);
0516 list_del(&v9ses->slist);
0517 spin_unlock(&v9fs_sessionlist_lock);
0518 }
0519
0520
0521
0522
0523
0524
0525
0526
0527 void v9fs_session_cancel(struct v9fs_session_info *v9ses)
0528 {
0529 p9_debug(P9_DEBUG_ERROR, "cancel session %p\n", v9ses);
0530 p9_client_disconnect(v9ses->clnt);
0531 }
0532
0533
0534
0535
0536
0537
0538
0539
0540 void v9fs_session_begin_cancel(struct v9fs_session_info *v9ses)
0541 {
0542 p9_debug(P9_DEBUG_ERROR, "begin cancel session %p\n", v9ses);
0543 p9_client_begin_disconnect(v9ses->clnt);
0544 }
0545
0546 extern int v9fs_error_init(void);
0547
0548 static struct kobject *v9fs_kobj;
0549
0550 #ifdef CONFIG_9P_FSCACHE
0551
0552
0553
0554 static ssize_t caches_show(struct kobject *kobj,
0555 struct kobj_attribute *attr,
0556 char *buf)
0557 {
0558 ssize_t n = 0, count = 0, limit = PAGE_SIZE;
0559 struct v9fs_session_info *v9ses;
0560
0561 spin_lock(&v9fs_sessionlist_lock);
0562 list_for_each_entry(v9ses, &v9fs_sessionlist, slist) {
0563 if (v9ses->cachetag) {
0564 n = snprintf(buf, limit, "%s\n", v9ses->cachetag);
0565 if (n < 0) {
0566 count = n;
0567 break;
0568 }
0569
0570 count += n;
0571 limit -= n;
0572 }
0573 }
0574
0575 spin_unlock(&v9fs_sessionlist_lock);
0576 return count;
0577 }
0578
0579 static struct kobj_attribute v9fs_attr_cache = __ATTR_RO(caches);
0580 #endif
0581
0582 static struct attribute *v9fs_attrs[] = {
0583 #ifdef CONFIG_9P_FSCACHE
0584 &v9fs_attr_cache.attr,
0585 #endif
0586 NULL,
0587 };
0588
0589 static const struct attribute_group v9fs_attr_group = {
0590 .attrs = v9fs_attrs,
0591 };
0592
0593
0594
0595
0596
0597
0598 static int __init v9fs_sysfs_init(void)
0599 {
0600 v9fs_kobj = kobject_create_and_add("9p", fs_kobj);
0601 if (!v9fs_kobj)
0602 return -ENOMEM;
0603
0604 if (sysfs_create_group(v9fs_kobj, &v9fs_attr_group)) {
0605 kobject_put(v9fs_kobj);
0606 return -ENOMEM;
0607 }
0608
0609 return 0;
0610 }
0611
0612
0613
0614
0615
0616
0617 static void v9fs_sysfs_cleanup(void)
0618 {
0619 sysfs_remove_group(v9fs_kobj, &v9fs_attr_group);
0620 kobject_put(v9fs_kobj);
0621 }
0622
0623 static void v9fs_inode_init_once(void *foo)
0624 {
0625 struct v9fs_inode *v9inode = (struct v9fs_inode *)foo;
0626
0627 memset(&v9inode->qid, 0, sizeof(v9inode->qid));
0628 inode_init_once(&v9inode->netfs.inode);
0629 }
0630
0631
0632
0633
0634
0635 static int v9fs_init_inode_cache(void)
0636 {
0637 v9fs_inode_cache = kmem_cache_create("v9fs_inode_cache",
0638 sizeof(struct v9fs_inode),
0639 0, (SLAB_RECLAIM_ACCOUNT|
0640 SLAB_MEM_SPREAD|SLAB_ACCOUNT),
0641 v9fs_inode_init_once);
0642 if (!v9fs_inode_cache)
0643 return -ENOMEM;
0644
0645 return 0;
0646 }
0647
0648
0649
0650
0651
0652 static void v9fs_destroy_inode_cache(void)
0653 {
0654
0655
0656
0657
0658 rcu_barrier();
0659 kmem_cache_destroy(v9fs_inode_cache);
0660 }
0661
0662 static int v9fs_cache_register(void)
0663 {
0664 int ret;
0665
0666 ret = v9fs_init_inode_cache();
0667 if (ret < 0)
0668 return ret;
0669 return ret;
0670 }
0671
0672 static void v9fs_cache_unregister(void)
0673 {
0674 v9fs_destroy_inode_cache();
0675 }
0676
0677
0678
0679
0680
0681
0682 static int __init init_v9fs(void)
0683 {
0684 int err;
0685
0686 pr_info("Installing v9fs 9p2000 file system support\n");
0687
0688
0689 err = v9fs_cache_register();
0690 if (err < 0) {
0691 pr_err("Failed to register v9fs for caching\n");
0692 return err;
0693 }
0694
0695 err = v9fs_sysfs_init();
0696 if (err < 0) {
0697 pr_err("Failed to register with sysfs\n");
0698 goto out_cache;
0699 }
0700 err = register_filesystem(&v9fs_fs_type);
0701 if (err < 0) {
0702 pr_err("Failed to register filesystem\n");
0703 goto out_sysfs_cleanup;
0704 }
0705
0706 return 0;
0707
0708 out_sysfs_cleanup:
0709 v9fs_sysfs_cleanup();
0710
0711 out_cache:
0712 v9fs_cache_unregister();
0713
0714 return err;
0715 }
0716
0717
0718
0719
0720
0721
0722 static void __exit exit_v9fs(void)
0723 {
0724 v9fs_sysfs_cleanup();
0725 v9fs_cache_unregister();
0726 unregister_filesystem(&v9fs_fs_type);
0727 }
0728
0729 module_init(init_v9fs)
0730 module_exit(exit_v9fs)
0731
0732 MODULE_AUTHOR("Latchesar Ionkov <lucho@ionkov.net>");
0733 MODULE_AUTHOR("Eric Van Hensbergen <ericvh@gmail.com>");
0734 MODULE_AUTHOR("Ron Minnich <rminnich@lanl.gov>");
0735 MODULE_LICENSE("GPL");