diff options
| -rw-r--r-- | fs/nfs/super.c | 178 |
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 |
| 273 | static int nfs4_get_sb(struct file_system_type *fs_type, | 275 | static 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); |
| 277 | static int nfs4_remote_get_sb(struct file_system_type *fs_type, | ||
| 278 | int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt); | ||
| 275 | static int nfs4_xdev_get_sb(struct file_system_type *fs_type, | 279 | static 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); |
| 277 | static int nfs4_referral_get_sb(struct file_system_type *fs_type, | 281 | static 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); |
| 283 | static 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); | ||
| 279 | static void nfs4_kill_super(struct super_block *sb); | 285 | static void nfs4_kill_super(struct super_block *sb); |
| 280 | 286 | ||
| 281 | static struct file_system_type nfs4_fs_type = { | 287 | static 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 | ||
| 295 | static 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 | |||
| 289 | struct file_system_type nfs4_xdev_fs_type = { | 303 | struct 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 | ||
| 311 | static 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 | |||
| 297 | struct file_system_type nfs4_referral_fs_type = { | 319 | struct 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 | */ |
| 2438 | static int nfs4_get_sb(struct file_system_type *fs_type, | 2460 | static 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 | ||
| 2512 | out: | 2528 | out: |
| 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); |
| 2518 | out_free_fh: | 2530 | out_free_fh: |
| 2519 | kfree(mntfh); | 2531 | kfree(mntfh); |
| 2520 | kfree(data); | ||
| 2521 | return error; | 2532 | return error; |
| 2522 | 2533 | ||
| 2523 | out_free: | 2534 | out_free: |
| @@ -2531,6 +2542,102 @@ error_splat_super: | |||
| 2531 | goto out; | 2542 | goto out; |
| 2532 | } | 2543 | } |
| 2533 | 2544 | ||
| 2545 | static 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 | |||
| 2562 | static 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; | ||
| 2591 | out_mntput: | ||
| 2592 | mntput(root_mnt); | ||
| 2593 | out_err: | ||
| 2594 | return ret; | ||
| 2595 | } | ||
| 2596 | |||
| 2597 | /* | ||
| 2598 | * Get the superblock for an NFS4 mountpoint | ||
| 2599 | */ | ||
| 2600 | static 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 | |||
| 2629 | out: | ||
| 2630 | kfree(data->client_address); | ||
| 2631 | kfree(data->nfs_server.export_path); | ||
| 2632 | kfree(data->nfs_server.hostname); | ||
| 2633 | kfree(data->fscache_uniq); | ||
| 2634 | out_free_data: | ||
| 2635 | kfree(data); | ||
| 2636 | dprintk("<-- nfs4_get_sb() = %d%s\n", error, | ||
| 2637 | error != 0 ? " [error]" : ""); | ||
| 2638 | return error; | ||
| 2639 | } | ||
| 2640 | |||
| 2534 | static void nfs4_kill_super(struct super_block *sb) | 2641 | static 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 | /* | 2737 | static 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) |
| 2633 | static 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 | */ | ||
| 2821 | static 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); | ||
| 2844 | out: | ||
| 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 */ |
