forked from Minki/linux
btrfs: Factor out the main deletion process from btrfs_ioctl_snap_destroy()
Factor out the second half of btrfs_ioctl_snap_destroy() as btrfs_delete_subvolume(), which performs some subvolume specific checks before deletion: 1. send is not in progress 2. the subvolume is not the default subvolume 3. the subvolume does not contain other subvolumes and actual deletion process. btrfs_delete_subvolume() requires inode_lock for both @dir and inode of @dentry. The remaining part of btrfs_ioctl_snap_destroy() is mainly permission checks. Note that call of d_delete() is not included in btrfs_delete_subvolume() as this function will also be used by btrfs_rmdir() to delete an empty subvolume and in that case d_delete() is called in VFS layer. As a result, btrfs_unlink_subvol() and may_destroy_subvol() become static functions. No functional changes. Signed-off-by: Tomohiro Misono <misono.tomohiro@jp.fujitsu.com> Reviewed-by: David Sterba <dsterba@suse.com> [ minor comment updates ] Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
parent
ec42f16734
commit
f60a2364a4
@ -3193,11 +3193,7 @@ int btrfs_unlink_inode(struct btrfs_trans_handle *trans,
|
||||
int btrfs_add_link(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_inode *parent_inode, struct btrfs_inode *inode,
|
||||
const char *name, int name_len, int add_backref, u64 index);
|
||||
int btrfs_unlink_subvol(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root,
|
||||
struct inode *dir, u64 objectid,
|
||||
const char *name, int name_len);
|
||||
noinline int may_destroy_subvol(struct btrfs_root *root);
|
||||
int btrfs_delete_subvolume(struct inode *dir, struct dentry *dentry);
|
||||
int btrfs_truncate_block(struct inode *inode, loff_t from, loff_t len,
|
||||
int front);
|
||||
int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans,
|
||||
|
143
fs/btrfs/inode.c
143
fs/btrfs/inode.c
@ -4245,7 +4245,7 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int btrfs_unlink_subvol(struct btrfs_trans_handle *trans,
|
||||
static int btrfs_unlink_subvol(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root,
|
||||
struct inode *dir, u64 objectid,
|
||||
const char *name, int name_len)
|
||||
@ -4330,7 +4330,7 @@ out:
|
||||
* Helper to check if the subvolume references other subvolumes or if it's
|
||||
* default.
|
||||
*/
|
||||
noinline int may_destroy_subvol(struct btrfs_root *root)
|
||||
static noinline int may_destroy_subvol(struct btrfs_root *root)
|
||||
{
|
||||
struct btrfs_fs_info *fs_info = root->fs_info;
|
||||
struct btrfs_path *path;
|
||||
@ -4381,6 +4381,145 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int btrfs_delete_subvolume(struct inode *dir, struct dentry *dentry)
|
||||
{
|
||||
struct btrfs_fs_info *fs_info = btrfs_sb(dentry->d_sb);
|
||||
struct btrfs_root *root = BTRFS_I(dir)->root;
|
||||
struct inode *inode = d_inode(dentry);
|
||||
struct btrfs_root *dest = BTRFS_I(inode)->root;
|
||||
struct btrfs_trans_handle *trans;
|
||||
struct btrfs_block_rsv block_rsv;
|
||||
u64 root_flags;
|
||||
u64 qgroup_reserved;
|
||||
int ret;
|
||||
int err;
|
||||
|
||||
/*
|
||||
* Don't allow to delete a subvolume with send in progress. This is
|
||||
* inside the inode lock so the error handling that has to drop the bit
|
||||
* again is not run concurrently.
|
||||
*/
|
||||
spin_lock(&dest->root_item_lock);
|
||||
root_flags = btrfs_root_flags(&dest->root_item);
|
||||
if (dest->send_in_progress == 0) {
|
||||
btrfs_set_root_flags(&dest->root_item,
|
||||
root_flags | BTRFS_ROOT_SUBVOL_DEAD);
|
||||
spin_unlock(&dest->root_item_lock);
|
||||
} else {
|
||||
spin_unlock(&dest->root_item_lock);
|
||||
btrfs_warn(fs_info,
|
||||
"attempt to delete subvolume %llu during send",
|
||||
dest->root_key.objectid);
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
down_write(&fs_info->subvol_sem);
|
||||
|
||||
err = may_destroy_subvol(dest);
|
||||
if (err)
|
||||
goto out_up_write;
|
||||
|
||||
btrfs_init_block_rsv(&block_rsv, BTRFS_BLOCK_RSV_TEMP);
|
||||
/*
|
||||
* One for dir inode,
|
||||
* two for dir entries,
|
||||
* two for root ref/backref.
|
||||
*/
|
||||
err = btrfs_subvolume_reserve_metadata(root, &block_rsv,
|
||||
5, &qgroup_reserved, true);
|
||||
if (err)
|
||||
goto out_up_write;
|
||||
|
||||
trans = btrfs_start_transaction(root, 0);
|
||||
if (IS_ERR(trans)) {
|
||||
err = PTR_ERR(trans);
|
||||
goto out_release;
|
||||
}
|
||||
trans->block_rsv = &block_rsv;
|
||||
trans->bytes_reserved = block_rsv.size;
|
||||
|
||||
btrfs_record_snapshot_destroy(trans, BTRFS_I(dir));
|
||||
|
||||
ret = btrfs_unlink_subvol(trans, root, dir,
|
||||
dest->root_key.objectid,
|
||||
dentry->d_name.name,
|
||||
dentry->d_name.len);
|
||||
if (ret) {
|
||||
err = ret;
|
||||
btrfs_abort_transaction(trans, ret);
|
||||
goto out_end_trans;
|
||||
}
|
||||
|
||||
btrfs_record_root_in_trans(trans, dest);
|
||||
|
||||
memset(&dest->root_item.drop_progress, 0,
|
||||
sizeof(dest->root_item.drop_progress));
|
||||
dest->root_item.drop_level = 0;
|
||||
btrfs_set_root_refs(&dest->root_item, 0);
|
||||
|
||||
if (!test_and_set_bit(BTRFS_ROOT_ORPHAN_ITEM_INSERTED, &dest->state)) {
|
||||
ret = btrfs_insert_orphan_item(trans,
|
||||
fs_info->tree_root,
|
||||
dest->root_key.objectid);
|
||||
if (ret) {
|
||||
btrfs_abort_transaction(trans, ret);
|
||||
err = ret;
|
||||
goto out_end_trans;
|
||||
}
|
||||
}
|
||||
|
||||
ret = btrfs_uuid_tree_rem(trans, fs_info, dest->root_item.uuid,
|
||||
BTRFS_UUID_KEY_SUBVOL,
|
||||
dest->root_key.objectid);
|
||||
if (ret && ret != -ENOENT) {
|
||||
btrfs_abort_transaction(trans, ret);
|
||||
err = ret;
|
||||
goto out_end_trans;
|
||||
}
|
||||
if (!btrfs_is_empty_uuid(dest->root_item.received_uuid)) {
|
||||
ret = btrfs_uuid_tree_rem(trans, fs_info,
|
||||
dest->root_item.received_uuid,
|
||||
BTRFS_UUID_KEY_RECEIVED_SUBVOL,
|
||||
dest->root_key.objectid);
|
||||
if (ret && ret != -ENOENT) {
|
||||
btrfs_abort_transaction(trans, ret);
|
||||
err = ret;
|
||||
goto out_end_trans;
|
||||
}
|
||||
}
|
||||
|
||||
out_end_trans:
|
||||
trans->block_rsv = NULL;
|
||||
trans->bytes_reserved = 0;
|
||||
ret = btrfs_end_transaction(trans);
|
||||
if (ret && !err)
|
||||
err = ret;
|
||||
inode->i_flags |= S_DEAD;
|
||||
out_release:
|
||||
btrfs_subvolume_release_metadata(fs_info, &block_rsv);
|
||||
out_up_write:
|
||||
up_write(&fs_info->subvol_sem);
|
||||
if (err) {
|
||||
spin_lock(&dest->root_item_lock);
|
||||
root_flags = btrfs_root_flags(&dest->root_item);
|
||||
btrfs_set_root_flags(&dest->root_item,
|
||||
root_flags & ~BTRFS_ROOT_SUBVOL_DEAD);
|
||||
spin_unlock(&dest->root_item_lock);
|
||||
} else {
|
||||
d_invalidate(dentry);
|
||||
btrfs_invalidate_inodes(dest);
|
||||
ASSERT(dest->send_in_progress == 0);
|
||||
|
||||
/* the last ref */
|
||||
if (dest->ino_cache_inode) {
|
||||
iput(dest->ino_cache_inode);
|
||||
dest->ino_cache_inode = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int btrfs_rmdir(struct inode *dir, struct dentry *dentry)
|
||||
{
|
||||
struct inode *inode = d_inode(dentry);
|
||||
|
131
fs/btrfs/ioctl.c
131
fs/btrfs/ioctl.c
@ -2255,12 +2255,7 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,
|
||||
struct btrfs_root *root = BTRFS_I(dir)->root;
|
||||
struct btrfs_root *dest = NULL;
|
||||
struct btrfs_ioctl_vol_args *vol_args;
|
||||
struct btrfs_trans_handle *trans;
|
||||
struct btrfs_block_rsv block_rsv;
|
||||
u64 root_flags;
|
||||
u64 qgroup_reserved;
|
||||
int namelen;
|
||||
int ret;
|
||||
int err = 0;
|
||||
|
||||
if (!S_ISDIR(dir->i_mode))
|
||||
@ -2344,133 +2339,11 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,
|
||||
}
|
||||
|
||||
inode_lock(inode);
|
||||
|
||||
/*
|
||||
* Don't allow to delete a subvolume with send in progress. This is
|
||||
* inside the i_mutex so the error handling that has to drop the bit
|
||||
* again is not run concurrently.
|
||||
*/
|
||||
spin_lock(&dest->root_item_lock);
|
||||
root_flags = btrfs_root_flags(&dest->root_item);
|
||||
if (dest->send_in_progress == 0) {
|
||||
btrfs_set_root_flags(&dest->root_item,
|
||||
root_flags | BTRFS_ROOT_SUBVOL_DEAD);
|
||||
spin_unlock(&dest->root_item_lock);
|
||||
} else {
|
||||
spin_unlock(&dest->root_item_lock);
|
||||
btrfs_warn(fs_info,
|
||||
"Attempt to delete subvolume %llu during send",
|
||||
dest->root_key.objectid);
|
||||
err = -EPERM;
|
||||
goto out_unlock_inode;
|
||||
}
|
||||
|
||||
down_write(&fs_info->subvol_sem);
|
||||
|
||||
err = may_destroy_subvol(dest);
|
||||
if (err)
|
||||
goto out_up_write;
|
||||
|
||||
btrfs_init_block_rsv(&block_rsv, BTRFS_BLOCK_RSV_TEMP);
|
||||
/*
|
||||
* One for dir inode, two for dir entries, two for root
|
||||
* ref/backref.
|
||||
*/
|
||||
err = btrfs_subvolume_reserve_metadata(root, &block_rsv,
|
||||
5, &qgroup_reserved, true);
|
||||
if (err)
|
||||
goto out_up_write;
|
||||
|
||||
trans = btrfs_start_transaction(root, 0);
|
||||
if (IS_ERR(trans)) {
|
||||
err = PTR_ERR(trans);
|
||||
goto out_release;
|
||||
}
|
||||
trans->block_rsv = &block_rsv;
|
||||
trans->bytes_reserved = block_rsv.size;
|
||||
|
||||
btrfs_record_snapshot_destroy(trans, BTRFS_I(dir));
|
||||
|
||||
ret = btrfs_unlink_subvol(trans, root, dir,
|
||||
dest->root_key.objectid,
|
||||
dentry->d_name.name,
|
||||
dentry->d_name.len);
|
||||
if (ret) {
|
||||
err = ret;
|
||||
btrfs_abort_transaction(trans, ret);
|
||||
goto out_end_trans;
|
||||
}
|
||||
|
||||
btrfs_record_root_in_trans(trans, dest);
|
||||
|
||||
memset(&dest->root_item.drop_progress, 0,
|
||||
sizeof(dest->root_item.drop_progress));
|
||||
dest->root_item.drop_level = 0;
|
||||
btrfs_set_root_refs(&dest->root_item, 0);
|
||||
|
||||
if (!test_and_set_bit(BTRFS_ROOT_ORPHAN_ITEM_INSERTED, &dest->state)) {
|
||||
ret = btrfs_insert_orphan_item(trans,
|
||||
fs_info->tree_root,
|
||||
dest->root_key.objectid);
|
||||
if (ret) {
|
||||
btrfs_abort_transaction(trans, ret);
|
||||
err = ret;
|
||||
goto out_end_trans;
|
||||
}
|
||||
}
|
||||
|
||||
ret = btrfs_uuid_tree_rem(trans, fs_info, dest->root_item.uuid,
|
||||
BTRFS_UUID_KEY_SUBVOL,
|
||||
dest->root_key.objectid);
|
||||
if (ret && ret != -ENOENT) {
|
||||
btrfs_abort_transaction(trans, ret);
|
||||
err = ret;
|
||||
goto out_end_trans;
|
||||
}
|
||||
if (!btrfs_is_empty_uuid(dest->root_item.received_uuid)) {
|
||||
ret = btrfs_uuid_tree_rem(trans, fs_info,
|
||||
dest->root_item.received_uuid,
|
||||
BTRFS_UUID_KEY_RECEIVED_SUBVOL,
|
||||
dest->root_key.objectid);
|
||||
if (ret && ret != -ENOENT) {
|
||||
btrfs_abort_transaction(trans, ret);
|
||||
err = ret;
|
||||
goto out_end_trans;
|
||||
}
|
||||
}
|
||||
|
||||
out_end_trans:
|
||||
trans->block_rsv = NULL;
|
||||
trans->bytes_reserved = 0;
|
||||
ret = btrfs_end_transaction(trans);
|
||||
if (ret && !err)
|
||||
err = ret;
|
||||
inode->i_flags |= S_DEAD;
|
||||
out_release:
|
||||
btrfs_subvolume_release_metadata(fs_info, &block_rsv);
|
||||
out_up_write:
|
||||
up_write(&fs_info->subvol_sem);
|
||||
if (err) {
|
||||
spin_lock(&dest->root_item_lock);
|
||||
root_flags = btrfs_root_flags(&dest->root_item);
|
||||
btrfs_set_root_flags(&dest->root_item,
|
||||
root_flags & ~BTRFS_ROOT_SUBVOL_DEAD);
|
||||
spin_unlock(&dest->root_item_lock);
|
||||
}
|
||||
out_unlock_inode:
|
||||
err = btrfs_delete_subvolume(dir, dentry);
|
||||
inode_unlock(inode);
|
||||
if (!err) {
|
||||
d_invalidate(dentry);
|
||||
btrfs_invalidate_inodes(dest);
|
||||
if (!err)
|
||||
d_delete(dentry);
|
||||
ASSERT(dest->send_in_progress == 0);
|
||||
|
||||
/* the last ref */
|
||||
if (dest->ino_cache_inode) {
|
||||
iput(dest->ino_cache_inode);
|
||||
dest->ino_cache_inode = NULL;
|
||||
}
|
||||
}
|
||||
out_dput:
|
||||
dput(dentry);
|
||||
out_unlock_dir:
|
||||
|
Loading…
Reference in New Issue
Block a user