aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorTrond Myklebust <Trond.Myklebust@netapp.com>2009-06-22 15:09:14 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2009-06-23 00:28:25 -0400
commitc02d7adf8c5429727a98bad1d039bccad4c61c50 (patch)
tree10f9a95817f6491426b80f0353eed54964dc1b90 /fs
parentcf8d2c11cb77f129675478792122f50827e5b0ae (diff)
NFSv4: Replace nfs4_path_walk() with VFS path lookup in a private namespace
As noted in the previous patch, the NFSv4 client mount code currently has several limitations. If the mount path contains symlinks, or referrals, or even if it just contains a '..', then the client code in nfs4_path_walk() will fail with an error. This patch replaces the nfs4_path_walk()-based lookup with a helper function that sets up a private namespace to represent the namespace on the server, then uses the ordinary VFS and NFS path lookup code to walk down the mount path in that namespace. Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs')
-rw-r--r--fs/nfs/super.c178
1 files changed, 157 insertions, 21 deletions
diff --git a/fs/nfs/super.c b/fs/nfs/super.c
index 3d460527daab..8e0673a0d6aa 100644
--- a/fs/nfs/super.c
+++ b/fs/nfs/super.c
@@ -42,6 +42,8 @@
42#include <linux/smp_lock.h> 42#include <linux/smp_lock.h>
43#include <linux/seq_file.h> 43#include <linux/seq_file.h>
44#include <linux/mount.h> 44#include <linux/mount.h>
45#include <linux/mnt_namespace.h>
46#include <linux/namei.h>
45#include <linux/nfs_idmap.h> 47#include <linux/nfs_idmap.h>
46#include <linux/vfs.h> 48#include <linux/vfs.h>
47#include <linux/inet.h> 49#include <linux/inet.h>
@@ -272,10 +274,14 @@ static const struct super_operations nfs_sops = {
272#ifdef CONFIG_NFS_V4 274#ifdef CONFIG_NFS_V4
273static int nfs4_get_sb(struct file_system_type *fs_type, 275static int nfs4_get_sb(struct file_system_type *fs_type,
274 int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt); 276 int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt);
277static int nfs4_remote_get_sb(struct file_system_type *fs_type,
278 int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt);
275static int nfs4_xdev_get_sb(struct file_system_type *fs_type, 279static int nfs4_xdev_get_sb(struct file_system_type *fs_type,
276 int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt); 280 int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt);
277static int nfs4_referral_get_sb(struct file_system_type *fs_type, 281static int nfs4_referral_get_sb(struct file_system_type *fs_type,
278 int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt); 282 int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt);
283static int nfs4_remote_referral_get_sb(struct file_system_type *fs_type,
284 int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt);
279static void nfs4_kill_super(struct super_block *sb); 285static void nfs4_kill_super(struct super_block *sb);
280 286
281static struct file_system_type nfs4_fs_type = { 287static struct file_system_type nfs4_fs_type = {
@@ -286,6 +292,14 @@ static struct file_system_type nfs4_fs_type = {
286 .fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA, 292 .fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
287}; 293};
288 294
295static struct file_system_type nfs4_remote_fs_type = {
296 .owner = THIS_MODULE,
297 .name = "nfs4",
298 .get_sb = nfs4_remote_get_sb,
299 .kill_sb = nfs4_kill_super,
300 .fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
301};
302
289struct file_system_type nfs4_xdev_fs_type = { 303struct file_system_type nfs4_xdev_fs_type = {
290 .owner = THIS_MODULE, 304 .owner = THIS_MODULE,
291 .name = "nfs4", 305 .name = "nfs4",
@@ -294,6 +308,14 @@ struct file_system_type nfs4_xdev_fs_type = {
294 .fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA, 308 .fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
295}; 309};
296 310
311static struct file_system_type nfs4_remote_referral_fs_type = {
312 .owner = THIS_MODULE,
313 .name = "nfs4",
314 .get_sb = nfs4_remote_referral_get_sb,
315 .kill_sb = nfs4_kill_super,
316 .fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
317};
318
297struct file_system_type nfs4_referral_fs_type = { 319struct file_system_type nfs4_referral_fs_type = {
298 .owner = THIS_MODULE, 320 .owner = THIS_MODULE,
299 .name = "nfs4", 321 .name = "nfs4",
@@ -2433,12 +2455,12 @@ out_no_client_address:
2433} 2455}
2434 2456
2435/* 2457/*
2436 * Get the superblock for an NFS4 mountpoint 2458 * Get the superblock for the NFS4 root partition
2437 */ 2459 */
2438static int nfs4_get_sb(struct file_system_type *fs_type, 2460static int nfs4_remote_get_sb(struct file_system_type *fs_type,
2439 int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt) 2461 int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt)
2440{ 2462{
2441 struct nfs_parsed_mount_data *data; 2463 struct nfs_parsed_mount_data *data = raw_data;
2442 struct super_block *s; 2464 struct super_block *s;
2443 struct nfs_server *server; 2465 struct nfs_server *server;
2444 struct nfs_fh *mntfh; 2466 struct nfs_fh *mntfh;
@@ -2449,18 +2471,12 @@ static int nfs4_get_sb(struct file_system_type *fs_type,
2449 }; 2471 };
2450 int error = -ENOMEM; 2472 int error = -ENOMEM;
2451 2473
2452 data = kzalloc(sizeof(*data), GFP_KERNEL);
2453 mntfh = kzalloc(sizeof(*mntfh), GFP_KERNEL); 2474 mntfh = kzalloc(sizeof(*mntfh), GFP_KERNEL);
2454 if (data == NULL || mntfh == NULL) 2475 if (data == NULL || mntfh == NULL)
2455 goto out_free_fh; 2476 goto out_free_fh;
2456 2477
2457 security_init_mnt_opts(&data->lsm_opts); 2478 security_init_mnt_opts(&data->lsm_opts);
2458 2479
2459 /* Validate the mount data */
2460 error = nfs4_validate_mount_data(raw_data, data, dev_name);
2461 if (error < 0)
2462 goto out;
2463
2464 /* Get a volume representation */ 2480 /* Get a volume representation */
2465 server = nfs4_create_server(data, mntfh); 2481 server = nfs4_create_server(data, mntfh);
2466 if (IS_ERR(server)) { 2482 if (IS_ERR(server)) {
@@ -2473,7 +2489,7 @@ static int nfs4_get_sb(struct file_system_type *fs_type,
2473 compare_super = NULL; 2489 compare_super = NULL;
2474 2490
2475 /* Get a superblock - note that we may end up sharing one that already exists */ 2491 /* Get a superblock - note that we may end up sharing one that already exists */
2476 s = sget(fs_type, compare_super, nfs_set_super, &sb_mntdata); 2492 s = sget(&nfs4_fs_type, compare_super, nfs_set_super, &sb_mntdata);
2477 if (IS_ERR(s)) { 2493 if (IS_ERR(s)) {
2478 error = PTR_ERR(s); 2494 error = PTR_ERR(s);
2479 goto out_free; 2495 goto out_free;
@@ -2510,14 +2526,9 @@ static int nfs4_get_sb(struct file_system_type *fs_type,
2510 error = 0; 2526 error = 0;
2511 2527
2512out: 2528out:
2513 kfree(data->client_address);
2514 kfree(data->nfs_server.export_path);
2515 kfree(data->nfs_server.hostname);
2516 kfree(data->fscache_uniq);
2517 security_free_mnt_opts(&data->lsm_opts); 2529 security_free_mnt_opts(&data->lsm_opts);
2518out_free_fh: 2530out_free_fh:
2519 kfree(mntfh); 2531 kfree(mntfh);
2520 kfree(data);
2521 return error; 2532 return error;
2522 2533
2523out_free: 2534out_free:
@@ -2531,6 +2542,102 @@ error_splat_super:
2531 goto out; 2542 goto out;
2532} 2543}
2533 2544
2545static struct vfsmount *nfs_do_root_mount(struct file_system_type *fs_type,
2546 int flags, void *data, const char *hostname)
2547{
2548 struct vfsmount *root_mnt;
2549 char *root_devname;
2550 size_t len;
2551
2552 len = strlen(hostname) + 3;
2553 root_devname = kmalloc(len, GFP_KERNEL);
2554 if (root_devname == NULL)
2555 return ERR_PTR(-ENOMEM);
2556 snprintf(root_devname, len, "%s:/", hostname);
2557 root_mnt = vfs_kern_mount(fs_type, flags, root_devname, data);
2558 kfree(root_devname);
2559 return root_mnt;
2560}
2561
2562static int nfs_follow_remote_path(struct vfsmount *root_mnt,
2563 const char *export_path, struct vfsmount *mnt_target)
2564{
2565 struct mnt_namespace *ns_private;
2566 struct nameidata nd;
2567 struct super_block *s;
2568 int ret;
2569
2570 ns_private = create_mnt_ns(root_mnt);
2571 ret = PTR_ERR(ns_private);
2572 if (IS_ERR(ns_private))
2573 goto out_mntput;
2574
2575 ret = vfs_path_lookup(root_mnt->mnt_root, root_mnt,
2576 export_path, LOOKUP_FOLLOW, &nd);
2577
2578 put_mnt_ns(ns_private);
2579
2580 if (ret != 0)
2581 goto out_err;
2582
2583 s = nd.path.mnt->mnt_sb;
2584 atomic_inc(&s->s_active);
2585 mnt_target->mnt_sb = s;
2586 mnt_target->mnt_root = dget(nd.path.dentry);
2587
2588 path_put(&nd.path);
2589 down_write(&s->s_umount);
2590 return 0;
2591out_mntput:
2592 mntput(root_mnt);
2593out_err:
2594 return ret;
2595}
2596
2597/*
2598 * Get the superblock for an NFS4 mountpoint
2599 */
2600static int nfs4_get_sb(struct file_system_type *fs_type,
2601 int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt)
2602{
2603 struct nfs_parsed_mount_data *data;
2604 char *export_path;
2605 struct vfsmount *root_mnt;
2606 int error = -ENOMEM;
2607
2608 data = kzalloc(sizeof(*data), GFP_KERNEL);
2609 if (data == NULL)
2610 goto out_free_data;
2611
2612 /* Validate the mount data */
2613 error = nfs4_validate_mount_data(raw_data, data, dev_name);
2614 if (error < 0)
2615 goto out;
2616
2617 export_path = data->nfs_server.export_path;
2618 data->nfs_server.export_path = "/";
2619 root_mnt = nfs_do_root_mount(&nfs4_remote_fs_type, flags, data,
2620 data->nfs_server.hostname);
2621 data->nfs_server.export_path = export_path;
2622
2623 error = PTR_ERR(root_mnt);
2624 if (IS_ERR(root_mnt))
2625 goto out;
2626
2627 error = nfs_follow_remote_path(root_mnt, export_path, mnt);
2628
2629out:
2630 kfree(data->client_address);
2631 kfree(data->nfs_server.export_path);
2632 kfree(data->nfs_server.hostname);
2633 kfree(data->fscache_uniq);
2634out_free_data:
2635 kfree(data);
2636 dprintk("<-- nfs4_get_sb() = %d%s\n", error,
2637 error != 0 ? " [error]" : "");
2638 return error;
2639}
2640
2534static void nfs4_kill_super(struct super_block *sb) 2641static void nfs4_kill_super(struct super_block *sb)
2535{ 2642{
2536 struct nfs_server *server = NFS_SB(sb); 2643 struct nfs_server *server = NFS_SB(sb);
@@ -2627,12 +2734,9 @@ error_splat_super:
2627 return error; 2734 return error;
2628} 2735}
2629 2736
2630/* 2737static int nfs4_remote_referral_get_sb(struct file_system_type *fs_type,
2631 * Create an NFS4 server record on referral traversal 2738 int flags, const char *dev_name, void *raw_data,
2632 */ 2739 struct vfsmount *mnt)
2633static int nfs4_referral_get_sb(struct file_system_type *fs_type, int flags,
2634 const char *dev_name, void *raw_data,
2635 struct vfsmount *mnt)
2636{ 2740{
2637 struct nfs_clone_mount *data = raw_data; 2741 struct nfs_clone_mount *data = raw_data;
2638 struct super_block *s; 2742 struct super_block *s;
@@ -2711,4 +2815,36 @@ error_splat_super:
2711 return error; 2815 return error;
2712} 2816}
2713 2817
2818/*
2819 * Create an NFS4 server record on referral traversal
2820 */
2821static int nfs4_referral_get_sb(struct file_system_type *fs_type,
2822 int flags, const char *dev_name, void *raw_data,
2823 struct vfsmount *mnt)
2824{
2825 struct nfs_clone_mount *data = raw_data;
2826 char *export_path;
2827 struct vfsmount *root_mnt;
2828 int error;
2829
2830 dprintk("--> nfs4_referral_get_sb()\n");
2831
2832 export_path = data->mnt_path;
2833 data->mnt_path = "/";
2834
2835 root_mnt = nfs_do_root_mount(&nfs4_remote_referral_fs_type,
2836 flags, data, data->hostname);
2837 data->mnt_path = export_path;
2838
2839 error = PTR_ERR(root_mnt);
2840 if (IS_ERR(root_mnt))
2841 goto out;
2842
2843 error = nfs_follow_remote_path(root_mnt, export_path, mnt);
2844out:
2845 dprintk("<-- nfs4_referral_get_sb() = %d%s\n", error,
2846 error != 0 ? " [error]" : "");
2847 return error;
2848}
2849
2714#endif /* CONFIG_NFS_V4 */ 2850#endif /* CONFIG_NFS_V4 */