]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
btrfs: Factor out the main deletion process from btrfs_ioctl_snap_destroy()
authorMisono Tomohiro <misono.tomohiro@jp.fujitsu.com>
Wed, 18 Apr 2018 02:34:52 +0000 (11:34 +0900)
committerDavid Sterba <dsterba@suse.com>
Mon, 28 May 2018 16:07:18 +0000 (18:07 +0200)
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>
fs/btrfs/ctree.h
fs/btrfs/inode.c
fs/btrfs/ioctl.c

index fe7e5177119d8953f8cd821dac6302c7b7b55266..3a382ed940306d7afcaa2d09addc9ac41cb4d59c 100644 (file)
@@ -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,
index 1e51ca4489da6c38f1698831512eaff433ae0c15..6e03e7991a5dec33cfd86d3a2d4002c589cb5798 100644 (file)
@@ -4245,7 +4245,7 @@ static int btrfs_unlink(struct inode *dir, struct dentry *dentry)
        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 @@ int btrfs_unlink_subvol(struct btrfs_trans_handle *trans,
  * 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 @@ noinline int may_destroy_subvol(struct btrfs_root *root)
        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);
index 592ef10a66046d1151d67bfc2e96b64f56823afa..7beec1bf6d4bd1dd5ba6fb3ebb0ecdbc6197d866 100644 (file)
@@ -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: