mirror of
https://github.com/Fishwaldo/Star64_linux.git
synced 2025-07-07 15:01:44 +00:00
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
3 changed files with 144 additions and 136 deletions
|
@ -3193,11 +3193,7 @@ int btrfs_unlink_inode(struct btrfs_trans_handle *trans,
|
||||||
int btrfs_add_link(struct btrfs_trans_handle *trans,
|
int btrfs_add_link(struct btrfs_trans_handle *trans,
|
||||||
struct btrfs_inode *parent_inode, struct btrfs_inode *inode,
|
struct btrfs_inode *parent_inode, struct btrfs_inode *inode,
|
||||||
const char *name, int name_len, int add_backref, u64 index);
|
const char *name, int name_len, int add_backref, u64 index);
|
||||||
int btrfs_unlink_subvol(struct btrfs_trans_handle *trans,
|
int btrfs_delete_subvolume(struct inode *dir, struct dentry *dentry);
|
||||||
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_truncate_block(struct inode *inode, loff_t from, loff_t len,
|
int btrfs_truncate_block(struct inode *inode, loff_t from, loff_t len,
|
||||||
int front);
|
int front);
|
||||||
int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans,
|
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;
|
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 btrfs_root *root,
|
||||||
struct inode *dir, u64 objectid,
|
struct inode *dir, u64 objectid,
|
||||||
const char *name, int name_len)
|
const char *name, int name_len)
|
||||||
|
@ -4330,7 +4330,7 @@ out:
|
||||||
* Helper to check if the subvolume references other subvolumes or if it's
|
* Helper to check if the subvolume references other subvolumes or if it's
|
||||||
* default.
|
* 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_fs_info *fs_info = root->fs_info;
|
||||||
struct btrfs_path *path;
|
struct btrfs_path *path;
|
||||||
|
@ -4381,6 +4381,145 @@ out:
|
||||||
return ret;
|
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)
|
static int btrfs_rmdir(struct inode *dir, struct dentry *dentry)
|
||||||
{
|
{
|
||||||
struct inode *inode = d_inode(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 *root = BTRFS_I(dir)->root;
|
||||||
struct btrfs_root *dest = NULL;
|
struct btrfs_root *dest = NULL;
|
||||||
struct btrfs_ioctl_vol_args *vol_args;
|
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 namelen;
|
||||||
int ret;
|
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
if (!S_ISDIR(dir->i_mode))
|
if (!S_ISDIR(dir->i_mode))
|
||||||
|
@ -2344,133 +2339,11 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,
|
||||||
}
|
}
|
||||||
|
|
||||||
inode_lock(inode);
|
inode_lock(inode);
|
||||||
|
err = btrfs_delete_subvolume(dir, dentry);
|
||||||
/*
|
|
||||||
* 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:
|
|
||||||
inode_unlock(inode);
|
inode_unlock(inode);
|
||||||
if (!err) {
|
if (!err)
|
||||||
d_invalidate(dentry);
|
|
||||||
btrfs_invalidate_inodes(dest);
|
|
||||||
d_delete(dentry);
|
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:
|
out_dput:
|
||||||
dput(dentry);
|
dput(dentry);
|
||||||
out_unlock_dir:
|
out_unlock_dir:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue