Commit 2256e901 authored by Omar Sandoval's avatar Omar Sandoval Committed by David Sterba

btrfs: fix anon_dev leak in create_subvol()

When btrfs_qgroup_inherit(), btrfs_alloc_tree_block, or
btrfs_insert_root() fail in create_subvol(), we return without freeing
anon_dev. Reorganize the error handling in create_subvol() to fix this.
Reviewed-by: default avatarSweet Tea Dorminy <sweettea-kernel@dorminy.me>
Signed-off-by: default avatarOmar Sandoval <osandov@fb.com>
Reviewed-by: default avatarDavid Sterba <dsterba@suse.com>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
parent c1621871
...@@ -561,7 +561,7 @@ static noinline int create_subvol(struct user_namespace *mnt_userns, ...@@ -561,7 +561,7 @@ static noinline int create_subvol(struct user_namespace *mnt_userns,
struct timespec64 cur_time = current_time(dir); struct timespec64 cur_time = current_time(dir);
struct inode *inode; struct inode *inode;
int ret; int ret;
dev_t anon_dev = 0; dev_t anon_dev;
u64 objectid; u64 objectid;
u64 index = 0; u64 index = 0;
...@@ -571,11 +571,7 @@ static noinline int create_subvol(struct user_namespace *mnt_userns, ...@@ -571,11 +571,7 @@ static noinline int create_subvol(struct user_namespace *mnt_userns,
ret = btrfs_get_free_objectid(fs_info->tree_root, &objectid); ret = btrfs_get_free_objectid(fs_info->tree_root, &objectid);
if (ret) if (ret)
goto fail_free; goto out_root_item;
ret = get_anon_bdev(&anon_dev);
if (ret < 0)
goto fail_free;
/* /*
* Don't create subvolume whose level is not zero. Or qgroup will be * Don't create subvolume whose level is not zero. Or qgroup will be
...@@ -583,9 +579,13 @@ static noinline int create_subvol(struct user_namespace *mnt_userns, ...@@ -583,9 +579,13 @@ static noinline int create_subvol(struct user_namespace *mnt_userns,
*/ */
if (btrfs_qgroup_level(objectid)) { if (btrfs_qgroup_level(objectid)) {
ret = -ENOSPC; ret = -ENOSPC;
goto fail_free; goto out_root_item;
} }
ret = get_anon_bdev(&anon_dev);
if (ret < 0)
goto out_root_item;
btrfs_init_block_rsv(&block_rsv, BTRFS_BLOCK_RSV_TEMP); btrfs_init_block_rsv(&block_rsv, BTRFS_BLOCK_RSV_TEMP);
/* /*
* The same as the snapshot creation, please see the comment * The same as the snapshot creation, please see the comment
...@@ -593,26 +593,26 @@ static noinline int create_subvol(struct user_namespace *mnt_userns, ...@@ -593,26 +593,26 @@ static noinline int create_subvol(struct user_namespace *mnt_userns,
*/ */
ret = btrfs_subvolume_reserve_metadata(root, &block_rsv, 8, false); ret = btrfs_subvolume_reserve_metadata(root, &block_rsv, 8, false);
if (ret) if (ret)
goto fail_free; goto out_anon_dev;
trans = btrfs_start_transaction(root, 0); trans = btrfs_start_transaction(root, 0);
if (IS_ERR(trans)) { if (IS_ERR(trans)) {
ret = PTR_ERR(trans); ret = PTR_ERR(trans);
btrfs_subvolume_release_metadata(root, &block_rsv); btrfs_subvolume_release_metadata(root, &block_rsv);
goto fail_free; goto out_anon_dev;
} }
trans->block_rsv = &block_rsv; trans->block_rsv = &block_rsv;
trans->bytes_reserved = block_rsv.size; trans->bytes_reserved = block_rsv.size;
ret = btrfs_qgroup_inherit(trans, 0, objectid, inherit); ret = btrfs_qgroup_inherit(trans, 0, objectid, inherit);
if (ret) if (ret)
goto fail; goto out;
leaf = btrfs_alloc_tree_block(trans, root, 0, objectid, NULL, 0, 0, 0, leaf = btrfs_alloc_tree_block(trans, root, 0, objectid, NULL, 0, 0, 0,
BTRFS_NESTING_NORMAL); BTRFS_NESTING_NORMAL);
if (IS_ERR(leaf)) { if (IS_ERR(leaf)) {
ret = PTR_ERR(leaf); ret = PTR_ERR(leaf);
goto fail; goto out;
} }
btrfs_mark_buffer_dirty(leaf); btrfs_mark_buffer_dirty(leaf);
...@@ -667,7 +667,7 @@ static noinline int create_subvol(struct user_namespace *mnt_userns, ...@@ -667,7 +667,7 @@ static noinline int create_subvol(struct user_namespace *mnt_userns,
btrfs_tree_unlock(leaf); btrfs_tree_unlock(leaf);
btrfs_free_tree_block(trans, objectid, leaf, 0, 1); btrfs_free_tree_block(trans, objectid, leaf, 0, 1);
free_extent_buffer(leaf); free_extent_buffer(leaf);
goto fail; goto out;
} }
free_extent_buffer(leaf); free_extent_buffer(leaf);
...@@ -676,19 +676,18 @@ static noinline int create_subvol(struct user_namespace *mnt_userns, ...@@ -676,19 +676,18 @@ static noinline int create_subvol(struct user_namespace *mnt_userns,
key.offset = (u64)-1; key.offset = (u64)-1;
new_root = btrfs_get_new_fs_root(fs_info, objectid, anon_dev); new_root = btrfs_get_new_fs_root(fs_info, objectid, anon_dev);
if (IS_ERR(new_root)) { if (IS_ERR(new_root)) {
free_anon_bdev(anon_dev);
ret = PTR_ERR(new_root); ret = PTR_ERR(new_root);
btrfs_abort_transaction(trans, ret); btrfs_abort_transaction(trans, ret);
goto fail; goto out;
} }
/* Freeing will be done in btrfs_put_root() of new_root */ /* anon_dev is owned by new_root now. */
anon_dev = 0; anon_dev = 0;
ret = btrfs_record_root_in_trans(trans, new_root); ret = btrfs_record_root_in_trans(trans, new_root);
if (ret) { if (ret) {
btrfs_put_root(new_root); btrfs_put_root(new_root);
btrfs_abort_transaction(trans, ret); btrfs_abort_transaction(trans, ret);
goto fail; goto out;
} }
ret = btrfs_create_subvol_root(trans, new_root, root, mnt_userns); ret = btrfs_create_subvol_root(trans, new_root, root, mnt_userns);
...@@ -696,7 +695,7 @@ static noinline int create_subvol(struct user_namespace *mnt_userns, ...@@ -696,7 +695,7 @@ static noinline int create_subvol(struct user_namespace *mnt_userns,
if (ret) { if (ret) {
/* We potentially lose an unused inode item here */ /* We potentially lose an unused inode item here */
btrfs_abort_transaction(trans, ret); btrfs_abort_transaction(trans, ret);
goto fail; goto out;
} }
/* /*
...@@ -705,28 +704,28 @@ static noinline int create_subvol(struct user_namespace *mnt_userns, ...@@ -705,28 +704,28 @@ static noinline int create_subvol(struct user_namespace *mnt_userns,
ret = btrfs_set_inode_index(BTRFS_I(dir), &index); ret = btrfs_set_inode_index(BTRFS_I(dir), &index);
if (ret) { if (ret) {
btrfs_abort_transaction(trans, ret); btrfs_abort_transaction(trans, ret);
goto fail; goto out;
} }
ret = btrfs_insert_dir_item(trans, name, namelen, BTRFS_I(dir), &key, ret = btrfs_insert_dir_item(trans, name, namelen, BTRFS_I(dir), &key,
BTRFS_FT_DIR, index); BTRFS_FT_DIR, index);
if (ret) { if (ret) {
btrfs_abort_transaction(trans, ret); btrfs_abort_transaction(trans, ret);
goto fail; goto out;
} }
btrfs_i_size_write(BTRFS_I(dir), dir->i_size + namelen * 2); btrfs_i_size_write(BTRFS_I(dir), dir->i_size + namelen * 2);
ret = btrfs_update_inode(trans, root, BTRFS_I(dir)); ret = btrfs_update_inode(trans, root, BTRFS_I(dir));
if (ret) { if (ret) {
btrfs_abort_transaction(trans, ret); btrfs_abort_transaction(trans, ret);
goto fail; goto out;
} }
ret = btrfs_add_root_ref(trans, objectid, root->root_key.objectid, ret = btrfs_add_root_ref(trans, objectid, root->root_key.objectid,
btrfs_ino(BTRFS_I(dir)), index, name, namelen); btrfs_ino(BTRFS_I(dir)), index, name, namelen);
if (ret) { if (ret) {
btrfs_abort_transaction(trans, ret); btrfs_abort_transaction(trans, ret);
goto fail; goto out;
} }
ret = btrfs_uuid_tree_add(trans, root_item->uuid, ret = btrfs_uuid_tree_add(trans, root_item->uuid,
...@@ -734,8 +733,7 @@ static noinline int create_subvol(struct user_namespace *mnt_userns, ...@@ -734,8 +733,7 @@ static noinline int create_subvol(struct user_namespace *mnt_userns,
if (ret) if (ret)
btrfs_abort_transaction(trans, ret); btrfs_abort_transaction(trans, ret);
fail: out:
kfree(root_item);
trans->block_rsv = NULL; trans->block_rsv = NULL;
trans->bytes_reserved = 0; trans->bytes_reserved = 0;
btrfs_subvolume_release_metadata(root, &block_rsv); btrfs_subvolume_release_metadata(root, &block_rsv);
...@@ -751,11 +749,10 @@ static noinline int create_subvol(struct user_namespace *mnt_userns, ...@@ -751,11 +749,10 @@ static noinline int create_subvol(struct user_namespace *mnt_userns,
return PTR_ERR(inode); return PTR_ERR(inode);
d_instantiate(dentry, inode); d_instantiate(dentry, inode);
} }
return ret; out_anon_dev:
fail_free:
if (anon_dev) if (anon_dev)
free_anon_bdev(anon_dev); free_anon_bdev(anon_dev);
out_root_item:
kfree(root_item); kfree(root_item);
return ret; return ret;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment