diff options
author | Ilya Dryomov <idryomov@gmail.com> | 2011-11-09 07:41:22 -0500 |
---|---|---|
committer | Ilya Dryomov <idryomov@gmail.com> | 2011-11-09 15:53:39 -0500 |
commit | 04d21a244fdf79d0ac892eaaa9a46b682467277c (patch) | |
tree | da0cadb86766bbf34f30c4b3efddde01e3d6d677 /fs | |
parent | 586e46e2813c589d26258a599580421fb6fb576b (diff) |
Btrfs: rework error handling in btrfs_mount()
Commits 6c41761f and 45ea6095 introduced the possibility of NULL pointer
dereference on error paths, also we would leave all devices busy and
leak fs_info with all sub-structures on error when trying to mount an
already mounted fs to a different directory.
Fix this by doing all allocations before trying to open any of the
devices, adjust error path for mount-already-mounted-fs case.
Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/btrfs/super.c | 42 |
1 files changed, 21 insertions, 21 deletions
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index 58e9492230ce..629281c65ff5 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c | |||
@@ -891,7 +891,6 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags, | |||
891 | struct super_block *s; | 891 | struct super_block *s; |
892 | struct dentry *root; | 892 | struct dentry *root; |
893 | struct btrfs_fs_devices *fs_devices = NULL; | 893 | struct btrfs_fs_devices *fs_devices = NULL; |
894 | struct btrfs_root *tree_root = NULL; | ||
895 | struct btrfs_fs_info *fs_info = NULL; | 894 | struct btrfs_fs_info *fs_info = NULL; |
896 | fmode_t mode = FMODE_READ; | 895 | fmode_t mode = FMODE_READ; |
897 | char *subvol_name = NULL; | 896 | char *subvol_name = NULL; |
@@ -920,15 +919,6 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags, | |||
920 | if (error) | 919 | if (error) |
921 | return ERR_PTR(error); | 920 | return ERR_PTR(error); |
922 | 921 | ||
923 | error = btrfs_open_devices(fs_devices, mode, fs_type); | ||
924 | if (error) | ||
925 | return ERR_PTR(error); | ||
926 | |||
927 | if (!(flags & MS_RDONLY) && fs_devices->rw_devices == 0) { | ||
928 | error = -EACCES; | ||
929 | goto error_close_devices; | ||
930 | } | ||
931 | |||
932 | /* | 922 | /* |
933 | * Setup a dummy root and fs_info for test/set super. This is because | 923 | * Setup a dummy root and fs_info for test/set super. This is because |
934 | * we don't actually fill this stuff out until open_ctree, but we need | 924 | * we don't actually fill this stuff out until open_ctree, but we need |
@@ -936,28 +926,36 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags, | |||
936 | * then open_ctree will properly initialize everything later. | 926 | * then open_ctree will properly initialize everything later. |
937 | */ | 927 | */ |
938 | fs_info = kzalloc(sizeof(struct btrfs_fs_info), GFP_NOFS); | 928 | fs_info = kzalloc(sizeof(struct btrfs_fs_info), GFP_NOFS); |
939 | if (!fs_info) { | 929 | if (!fs_info) |
940 | error = -ENOMEM; | 930 | return ERR_PTR(-ENOMEM); |
941 | goto error_close_devices; | 931 | |
942 | } | 932 | fs_info->tree_root = kzalloc(sizeof(struct btrfs_root), GFP_NOFS); |
943 | tree_root = kzalloc(sizeof(struct btrfs_root), GFP_NOFS); | 933 | if (!fs_info->tree_root) { |
944 | if (!tree_root) { | ||
945 | error = -ENOMEM; | 934 | error = -ENOMEM; |
946 | goto error_close_devices; | 935 | goto error_fs_info; |
947 | } | 936 | } |
948 | fs_info->tree_root = tree_root; | 937 | fs_info->tree_root->fs_info = fs_info; |
949 | fs_info->fs_devices = fs_devices; | 938 | fs_info->fs_devices = fs_devices; |
950 | tree_root->fs_info = fs_info; | ||
951 | 939 | ||
952 | fs_info->super_copy = kzalloc(BTRFS_SUPER_INFO_SIZE, GFP_NOFS); | 940 | fs_info->super_copy = kzalloc(BTRFS_SUPER_INFO_SIZE, GFP_NOFS); |
953 | fs_info->super_for_commit = kzalloc(BTRFS_SUPER_INFO_SIZE, GFP_NOFS); | 941 | fs_info->super_for_commit = kzalloc(BTRFS_SUPER_INFO_SIZE, GFP_NOFS); |
954 | if (!fs_info->super_copy || !fs_info->super_for_commit) { | 942 | if (!fs_info->super_copy || !fs_info->super_for_commit) { |
955 | error = -ENOMEM; | 943 | error = -ENOMEM; |
944 | goto error_fs_info; | ||
945 | } | ||
946 | |||
947 | error = btrfs_open_devices(fs_devices, mode, fs_type); | ||
948 | if (error) | ||
949 | goto error_fs_info; | ||
950 | |||
951 | if (!(flags & MS_RDONLY) && fs_devices->rw_devices == 0) { | ||
952 | error = -EACCES; | ||
956 | goto error_close_devices; | 953 | goto error_close_devices; |
957 | } | 954 | } |
958 | 955 | ||
959 | bdev = fs_devices->latest_bdev; | 956 | bdev = fs_devices->latest_bdev; |
960 | s = sget(fs_type, btrfs_test_super, btrfs_set_super, tree_root); | 957 | s = sget(fs_type, btrfs_test_super, btrfs_set_super, |
958 | fs_info->tree_root); | ||
961 | if (IS_ERR(s)) { | 959 | if (IS_ERR(s)) { |
962 | error = PTR_ERR(s); | 960 | error = PTR_ERR(s); |
963 | goto error_close_devices; | 961 | goto error_close_devices; |
@@ -966,7 +964,8 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags, | |||
966 | if (s->s_root) { | 964 | if (s->s_root) { |
967 | if ((flags ^ s->s_flags) & MS_RDONLY) { | 965 | if ((flags ^ s->s_flags) & MS_RDONLY) { |
968 | deactivate_locked_super(s); | 966 | deactivate_locked_super(s); |
969 | return ERR_PTR(-EBUSY); | 967 | error = -EBUSY; |
968 | goto error_close_devices; | ||
970 | } | 969 | } |
971 | 970 | ||
972 | btrfs_close_devices(fs_devices); | 971 | btrfs_close_devices(fs_devices); |
@@ -997,6 +996,7 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags, | |||
997 | 996 | ||
998 | error_close_devices: | 997 | error_close_devices: |
999 | btrfs_close_devices(fs_devices); | 998 | btrfs_close_devices(fs_devices); |
999 | error_fs_info: | ||
1000 | free_fs_info(fs_info); | 1000 | free_fs_info(fs_info); |
1001 | return ERR_PTR(error); | 1001 | return ERR_PTR(error); |
1002 | } | 1002 | } |