Btrfs: rework error handling in btrfs_mount()
authorIlya Dryomov <idryomov@gmail.com>
Wed, 9 Nov 2011 12:41:22 +0000 (14:41 +0200)
committerIlya Dryomov <idryomov@gmail.com>
Wed, 9 Nov 2011 20:53:39 +0000 (22:53 +0200)
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>
fs/btrfs/super.c

index 58e9492230ce82ec560ae243293bb008fe8a6e2b..629281c65ff5c058689e0471373c7fc240219835 100644 (file)
@@ -891,7 +891,6 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags,
        struct super_block *s;
        struct dentry *root;
        struct btrfs_fs_devices *fs_devices = NULL;
-       struct btrfs_root *tree_root = NULL;
        struct btrfs_fs_info *fs_info = NULL;
        fmode_t mode = FMODE_READ;
        char *subvol_name = NULL;
@@ -920,15 +919,6 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags,
        if (error)
                return ERR_PTR(error);
 
-       error = btrfs_open_devices(fs_devices, mode, fs_type);
-       if (error)
-               return ERR_PTR(error);
-
-       if (!(flags & MS_RDONLY) && fs_devices->rw_devices == 0) {
-               error = -EACCES;
-               goto error_close_devices;
-       }
-
        /*
         * Setup a dummy root and fs_info for test/set super.  This is because
         * 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,
         * then open_ctree will properly initialize everything later.
         */
        fs_info = kzalloc(sizeof(struct btrfs_fs_info), GFP_NOFS);
-       if (!fs_info) {
-               error = -ENOMEM;
-               goto error_close_devices;
-       }
-       tree_root = kzalloc(sizeof(struct btrfs_root), GFP_NOFS);
-       if (!tree_root) {
+       if (!fs_info)
+               return ERR_PTR(-ENOMEM);
+
+       fs_info->tree_root = kzalloc(sizeof(struct btrfs_root), GFP_NOFS);
+       if (!fs_info->tree_root) {
                error = -ENOMEM;
-               goto error_close_devices;
+               goto error_fs_info;
        }
-       fs_info->tree_root = tree_root;
+       fs_info->tree_root->fs_info = fs_info;
        fs_info->fs_devices = fs_devices;
-       tree_root->fs_info = fs_info;
 
        fs_info->super_copy = kzalloc(BTRFS_SUPER_INFO_SIZE, GFP_NOFS);
        fs_info->super_for_commit = kzalloc(BTRFS_SUPER_INFO_SIZE, GFP_NOFS);
        if (!fs_info->super_copy || !fs_info->super_for_commit) {
                error = -ENOMEM;
+               goto error_fs_info;
+       }
+
+       error = btrfs_open_devices(fs_devices, mode, fs_type);
+       if (error)
+               goto error_fs_info;
+
+       if (!(flags & MS_RDONLY) && fs_devices->rw_devices == 0) {
+               error = -EACCES;
                goto error_close_devices;
        }
 
        bdev = fs_devices->latest_bdev;
-       s = sget(fs_type, btrfs_test_super, btrfs_set_super, tree_root);
+       s = sget(fs_type, btrfs_test_super, btrfs_set_super,
+                fs_info->tree_root);
        if (IS_ERR(s)) {
                error = PTR_ERR(s);
                goto error_close_devices;
@@ -966,7 +964,8 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags,
        if (s->s_root) {
                if ((flags ^ s->s_flags) & MS_RDONLY) {
                        deactivate_locked_super(s);
-                       return ERR_PTR(-EBUSY);
+                       error = -EBUSY;
+                       goto error_close_devices;
                }
 
                btrfs_close_devices(fs_devices);
@@ -997,6 +996,7 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags,
 
 error_close_devices:
        btrfs_close_devices(fs_devices);
+error_fs_info:
        free_fs_info(fs_info);
        return ERR_PTR(error);
 }