diff options
author | Josef Bacik <josef@redhat.com> | 2011-07-25 15:55:42 -0400 |
---|---|---|
committer | Josef Bacik <josef@redhat.com> | 2011-10-19 15:12:29 -0400 |
commit | 830c4adbd04a79f806d4fa579546f36a71b727c1 (patch) | |
tree | 3701532975661460217d8e25e6f67e846a83b62c /fs/btrfs | |
parent | ba5b8958dabbd7890a6929af1ffc0d87187765dc (diff) |
Btrfs: fix how we mount subvol=<whatever>
We've only been able to mount with subvol=<whatever> where whatever was a subvol
within whatever root we had as the default. This allows us to mount -o
subvol=path/to/subvol/you/want relative from the normal fs_tree root. Thanks,
Signed-off-by: Josef Bacik <josef@redhat.com>
Diffstat (limited to 'fs/btrfs')
-rw-r--r-- | fs/btrfs/super.c | 199 |
1 files changed, 135 insertions, 64 deletions
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index 244fa46c50b8..934789f7fd33 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c | |||
@@ -40,6 +40,7 @@ | |||
40 | #include <linux/magic.h> | 40 | #include <linux/magic.h> |
41 | #include <linux/slab.h> | 41 | #include <linux/slab.h> |
42 | #include <linux/cleancache.h> | 42 | #include <linux/cleancache.h> |
43 | #include <linux/mnt_namespace.h> | ||
43 | #include "compat.h" | 44 | #include "compat.h" |
44 | #include "delayed-inode.h" | 45 | #include "delayed-inode.h" |
45 | #include "ctree.h" | 46 | #include "ctree.h" |
@@ -58,6 +59,7 @@ | |||
58 | #include <trace/events/btrfs.h> | 59 | #include <trace/events/btrfs.h> |
59 | 60 | ||
60 | static const struct super_operations btrfs_super_ops; | 61 | static const struct super_operations btrfs_super_ops; |
62 | static struct file_system_type btrfs_fs_type; | ||
61 | 63 | ||
62 | static const char *btrfs_decode_error(struct btrfs_fs_info *fs_info, int errno, | 64 | static const char *btrfs_decode_error(struct btrfs_fs_info *fs_info, int errno, |
63 | char nbuf[16]) | 65 | char nbuf[16]) |
@@ -411,7 +413,7 @@ static int btrfs_parse_early_options(const char *options, fmode_t flags, | |||
411 | int intarg; | 413 | int intarg; |
412 | 414 | ||
413 | if (!options) | 415 | if (!options) |
414 | goto out; | 416 | return 0; |
415 | 417 | ||
416 | /* | 418 | /* |
417 | * strsep changes the string, duplicate it because parse_options | 419 | * strsep changes the string, duplicate it because parse_options |
@@ -460,26 +462,15 @@ static int btrfs_parse_early_options(const char *options, fmode_t flags, | |||
460 | error = btrfs_scan_one_device(match_strdup(&args[0]), | 462 | error = btrfs_scan_one_device(match_strdup(&args[0]), |
461 | flags, holder, fs_devices); | 463 | flags, holder, fs_devices); |
462 | if (error) | 464 | if (error) |
463 | goto out_free_opts; | 465 | goto out; |
464 | break; | 466 | break; |
465 | default: | 467 | default: |
466 | break; | 468 | break; |
467 | } | 469 | } |
468 | } | 470 | } |
469 | 471 | ||
470 | out_free_opts: | 472 | out: |
471 | kfree(orig); | 473 | kfree(orig); |
472 | out: | ||
473 | /* | ||
474 | * If no subvolume name is specified we use the default one. Allocate | ||
475 | * a copy of the string "." here so that code later in the | ||
476 | * mount path doesn't care if it's the default volume or another one. | ||
477 | */ | ||
478 | if (!*subvol_name) { | ||
479 | *subvol_name = kstrdup(".", GFP_KERNEL); | ||
480 | if (!*subvol_name) | ||
481 | return -ENOMEM; | ||
482 | } | ||
483 | return error; | 474 | return error; |
484 | } | 475 | } |
485 | 476 | ||
@@ -730,6 +721,118 @@ static int btrfs_set_super(struct super_block *s, void *data) | |||
730 | return set_anon_super(s, data); | 721 | return set_anon_super(s, data); |
731 | } | 722 | } |
732 | 723 | ||
724 | /* | ||
725 | * This will strip out the subvol=%s argument for an argument string and add | ||
726 | * subvolid=0 to make sure we get the actual tree root for path walking to the | ||
727 | * subvol we want. | ||
728 | */ | ||
729 | static char *setup_root_args(char *args) | ||
730 | { | ||
731 | unsigned copied = 0; | ||
732 | unsigned len = strlen(args) + 2; | ||
733 | char *pos; | ||
734 | char *ret; | ||
735 | |||
736 | /* | ||
737 | * We need the same args as before, but minus | ||
738 | * | ||
739 | * subvol=a | ||
740 | * | ||
741 | * and add | ||
742 | * | ||
743 | * subvolid=0 | ||
744 | * | ||
745 | * which is a difference of 2 characters, so we allocate strlen(args) + | ||
746 | * 2 characters. | ||
747 | */ | ||
748 | ret = kzalloc(len * sizeof(char), GFP_NOFS); | ||
749 | if (!ret) | ||
750 | return NULL; | ||
751 | pos = strstr(args, "subvol="); | ||
752 | |||
753 | /* This shouldn't happen, but just in case.. */ | ||
754 | if (!pos) { | ||
755 | kfree(ret); | ||
756 | return NULL; | ||
757 | } | ||
758 | |||
759 | /* | ||
760 | * The subvol=<> arg is not at the front of the string, copy everybody | ||
761 | * up to that into ret. | ||
762 | */ | ||
763 | if (pos != args) { | ||
764 | *pos = '\0'; | ||
765 | strcpy(ret, args); | ||
766 | copied += strlen(args); | ||
767 | pos++; | ||
768 | } | ||
769 | |||
770 | strncpy(ret + copied, "subvolid=0", len - copied); | ||
771 | |||
772 | /* Length of subvolid=0 */ | ||
773 | copied += 10; | ||
774 | |||
775 | /* | ||
776 | * If there is no , after the subvol= option then we know there's no | ||
777 | * other options and we can just return. | ||
778 | */ | ||
779 | pos = strchr(pos, ','); | ||
780 | if (!pos) | ||
781 | return ret; | ||
782 | |||
783 | /* Copy the rest of the arguments into our buffer */ | ||
784 | strncpy(ret + copied, pos, len - copied); | ||
785 | copied += strlen(pos); | ||
786 | |||
787 | return ret; | ||
788 | } | ||
789 | |||
790 | static struct dentry *mount_subvol(const char *subvol_name, int flags, | ||
791 | const char *device_name, char *data) | ||
792 | { | ||
793 | struct super_block *s; | ||
794 | struct dentry *root; | ||
795 | struct vfsmount *mnt; | ||
796 | struct mnt_namespace *ns_private; | ||
797 | char *newargs; | ||
798 | struct path path; | ||
799 | int error; | ||
800 | |||
801 | newargs = setup_root_args(data); | ||
802 | if (!newargs) | ||
803 | return ERR_PTR(-ENOMEM); | ||
804 | mnt = vfs_kern_mount(&btrfs_fs_type, flags, device_name, | ||
805 | newargs); | ||
806 | kfree(newargs); | ||
807 | if (IS_ERR(mnt)) | ||
808 | return ERR_CAST(mnt); | ||
809 | |||
810 | ns_private = create_mnt_ns(mnt); | ||
811 | if (IS_ERR(ns_private)) { | ||
812 | mntput(mnt); | ||
813 | return ERR_CAST(ns_private); | ||
814 | } | ||
815 | |||
816 | /* | ||
817 | * This will trigger the automount of the subvol so we can just | ||
818 | * drop the mnt we have here and return the dentry that we | ||
819 | * found. | ||
820 | */ | ||
821 | error = vfs_path_lookup(mnt->mnt_root, mnt, subvol_name, | ||
822 | LOOKUP_FOLLOW, &path); | ||
823 | put_mnt_ns(ns_private); | ||
824 | if (error) | ||
825 | return ERR_PTR(error); | ||
826 | |||
827 | /* Get a ref to the sb and the dentry we found and return it */ | ||
828 | s = path.mnt->mnt_sb; | ||
829 | atomic_inc(&s->s_active); | ||
830 | root = dget(path.dentry); | ||
831 | path_put(&path); | ||
832 | down_write(&s->s_umount); | ||
833 | |||
834 | return root; | ||
835 | } | ||
733 | 836 | ||
734 | /* | 837 | /* |
735 | * Find a superblock for the given device / mount point. | 838 | * Find a superblock for the given device / mount point. |
@@ -761,13 +864,19 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags, | |||
761 | if (error) | 864 | if (error) |
762 | return ERR_PTR(error); | 865 | return ERR_PTR(error); |
763 | 866 | ||
867 | if (subvol_name) { | ||
868 | root = mount_subvol(subvol_name, flags, device_name, data); | ||
869 | kfree(subvol_name); | ||
870 | return root; | ||
871 | } | ||
872 | |||
764 | error = btrfs_scan_one_device(device_name, mode, fs_type, &fs_devices); | 873 | error = btrfs_scan_one_device(device_name, mode, fs_type, &fs_devices); |
765 | if (error) | 874 | if (error) |
766 | goto error_free_subvol_name; | 875 | return ERR_PTR(error); |
767 | 876 | ||
768 | error = btrfs_open_devices(fs_devices, mode, fs_type); | 877 | error = btrfs_open_devices(fs_devices, mode, fs_type); |
769 | if (error) | 878 | if (error) |
770 | goto error_free_subvol_name; | 879 | return ERR_PTR(error); |
771 | 880 | ||
772 | if (!(flags & MS_RDONLY) && fs_devices->rw_devices == 0) { | 881 | if (!(flags & MS_RDONLY) && fs_devices->rw_devices == 0) { |
773 | error = -EACCES; | 882 | error = -EACCES; |
@@ -792,14 +901,15 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags, | |||
792 | 901 | ||
793 | bdev = fs_devices->latest_bdev; | 902 | bdev = fs_devices->latest_bdev; |
794 | s = sget(fs_type, btrfs_test_super, btrfs_set_super, tree_root); | 903 | s = sget(fs_type, btrfs_test_super, btrfs_set_super, tree_root); |
795 | if (IS_ERR(s)) | 904 | if (IS_ERR(s)) { |
796 | goto error_s; | 905 | error = PTR_ERR(s); |
906 | goto error_close_devices; | ||
907 | } | ||
797 | 908 | ||
798 | if (s->s_root) { | 909 | if (s->s_root) { |
799 | if ((flags ^ s->s_flags) & MS_RDONLY) { | 910 | if ((flags ^ s->s_flags) & MS_RDONLY) { |
800 | deactivate_locked_super(s); | 911 | deactivate_locked_super(s); |
801 | error = -EBUSY; | 912 | return ERR_PTR(-EBUSY); |
802 | goto error_close_devices; | ||
803 | } | 913 | } |
804 | 914 | ||
805 | btrfs_close_devices(fs_devices); | 915 | btrfs_close_devices(fs_devices); |
@@ -814,64 +924,25 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags, | |||
814 | flags & MS_SILENT ? 1 : 0); | 924 | flags & MS_SILENT ? 1 : 0); |
815 | if (error) { | 925 | if (error) { |
816 | deactivate_locked_super(s); | 926 | deactivate_locked_super(s); |
817 | goto error_free_subvol_name; | 927 | return ERR_PTR(error); |
818 | } | 928 | } |
819 | 929 | ||
820 | btrfs_sb(s)->fs_info->bdev_holder = fs_type; | 930 | btrfs_sb(s)->fs_info->bdev_holder = fs_type; |
821 | s->s_flags |= MS_ACTIVE; | 931 | s->s_flags |= MS_ACTIVE; |
822 | } | 932 | } |
823 | 933 | ||
824 | /* if they gave us a subvolume name bind mount into that */ | 934 | root = get_default_root(s, subvol_objectid); |
825 | if (strcmp(subvol_name, ".")) { | 935 | if (IS_ERR(root)) { |
826 | struct dentry *new_root; | 936 | deactivate_locked_super(s); |
827 | 937 | return root; | |
828 | root = get_default_root(s, subvol_rootid); | ||
829 | if (IS_ERR(root)) { | ||
830 | error = PTR_ERR(root); | ||
831 | deactivate_locked_super(s); | ||
832 | goto error_free_subvol_name; | ||
833 | } | ||
834 | |||
835 | mutex_lock(&root->d_inode->i_mutex); | ||
836 | new_root = lookup_one_len(subvol_name, root, | ||
837 | strlen(subvol_name)); | ||
838 | mutex_unlock(&root->d_inode->i_mutex); | ||
839 | |||
840 | if (IS_ERR(new_root)) { | ||
841 | dput(root); | ||
842 | deactivate_locked_super(s); | ||
843 | error = PTR_ERR(new_root); | ||
844 | goto error_free_subvol_name; | ||
845 | } | ||
846 | if (!new_root->d_inode) { | ||
847 | dput(root); | ||
848 | dput(new_root); | ||
849 | deactivate_locked_super(s); | ||
850 | error = -ENXIO; | ||
851 | goto error_free_subvol_name; | ||
852 | } | ||
853 | dput(root); | ||
854 | root = new_root; | ||
855 | } else { | ||
856 | root = get_default_root(s, subvol_objectid); | ||
857 | if (IS_ERR(root)) { | ||
858 | error = PTR_ERR(root); | ||
859 | deactivate_locked_super(s); | ||
860 | goto error_free_subvol_name; | ||
861 | } | ||
862 | } | 938 | } |
863 | 939 | ||
864 | kfree(subvol_name); | ||
865 | return root; | 940 | return root; |
866 | 941 | ||
867 | error_s: | ||
868 | error = PTR_ERR(s); | ||
869 | error_close_devices: | 942 | error_close_devices: |
870 | btrfs_close_devices(fs_devices); | 943 | btrfs_close_devices(fs_devices); |
871 | kfree(fs_info); | 944 | kfree(fs_info); |
872 | kfree(tree_root); | 945 | kfree(tree_root); |
873 | error_free_subvol_name: | ||
874 | kfree(subvol_name); | ||
875 | return ERR_PTR(error); | 946 | return ERR_PTR(error); |
876 | } | 947 | } |
877 | 948 | ||