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 */ |