@@ -1864,6 +1864,122 @@ static noinline int btrfs_ioctl_snap_create_v2(struct file *file,
return ret;
}
+static int btrfs_undelete_subvolume(const struct path *parent,
+ struct btrfs_root *root,
+ const char *name, int namelen)
+{
+ struct inode *dir = d_inode(parent->dentry);
+ struct btrfs_fs_info *fs_info = btrfs_sb(dir->i_sb);
+ struct btrfs_root_item *root_item = &root->root_item;
+ struct btrfs_trans_handle *trans;
+ struct btrfs_block_rsv block_rsv;
+ struct dentry *dentry;
+ struct inode *inode;
+ u64 root_flags;
+ int ret;
+
+ btrfs_debug(fs_info, "Undelete subvolume %llu",
+ root->root_key.objectid);
+
+ /* only care about the intact subvolume */
+ if (btrfs_disk_key_objectid(&root_item->drop_progress) != 0)
+ return 0;
+
+ /* root_refs of destination parent root must not be 0 */
+ if (btrfs_root_refs(&BTRFS_I(dir)->root->root_item) == 0)
+ return -ENOENT;
+
+ ret = down_write_killable_nested(&dir->i_rwsem, I_MUTEX_PARENT);
+ if (ret == -EINTR)
+ return ret;
+
+ dentry = lookup_one_len(name, parent->dentry, namelen);
+ if (IS_ERR(dentry)) {
+ ret = PTR_ERR(dentry);
+ goto out_unlock;
+ }
+
+ down_write(&fs_info->subvol_sem);
+
+ ret = btrfs_may_create(dir, dentry);
+ if (ret)
+ goto out_up_write;
+
+ ret = btrfs_check_dir_item_collision(root, dir->i_ino, name, namelen);
+ if (ret)
+ goto out_up_write;
+
+ btrfs_init_block_rsv(&block_rsv, BTRFS_BLOCK_RSV_TEMP);
+ /*
+ * 1 - parent dir inode
+ * 2 - dir entries
+ * 2 - root ref/backref
+ * 1 - UUID item
+ */
+ ret = btrfs_subvolume_reserve_metadata(root, &block_rsv, 6, false);
+ if (ret)
+ goto out_up_write;
+
+ trans = btrfs_start_transaction(BTRFS_I(dir)->root, 0);
+ if (IS_ERR(trans)) {
+ ret = PTR_ERR(trans);
+ btrfs_subvolume_release_metadata(fs_info, &block_rsv);
+ goto out_up_write;
+ }
+
+ trans->block_rsv = &block_rsv;
+ trans->bytes_reserved = block_rsv.size;
+
+ ret = btrfs_link_subvol(trans, dir, root->root_key.objectid, name,
+ namelen);
+ if (ret)
+ goto fail;
+
+ /* clear BTRFS_ROOT_SUBVOL_DEAD root flag and set root_refs to 1*/
+ root_flags = btrfs_root_flags(root_item);
+ btrfs_set_root_flags(root_item,
+ root_flags & ~BTRFS_ROOT_SUBVOL_DEAD);
+ btrfs_set_root_refs(root_item, 1);
+ ret = btrfs_update_root(trans, fs_info->tree_root,
+ &root->root_key, &root->root_item);
+ if (ret) {
+ btrfs_abort_transaction(trans, ret);
+ goto fail;
+ }
+
+ ret = btrfs_uuid_tree_add(trans, root_item->uuid, BTRFS_UUID_KEY_SUBVOL,
+ root->root_key.objectid);
+ if (ret) {
+ btrfs_abort_transaction(trans, ret);
+ goto fail;
+ }
+
+ ret = btrfs_del_orphan_item(trans, fs_info->tree_root,
+ root->root_key.objectid);
+ if (ret && ret != -ENOENT) {
+ btrfs_abort_transaction(trans, ret);
+ goto fail;
+ }
+fail:
+ trans->block_rsv = NULL;
+ trans->bytes_reserved = 0;
+ btrfs_subvolume_release_metadata(fs_info, &block_rsv);
+ ret = btrfs_commit_transaction(trans);
+ if (!ret) {
+ inode = btrfs_lookup_dentry(dir, dentry);
+ if (IS_ERR(inode))
+ return PTR_ERR(inode);
+ d_instantiate(dentry, inode);
+ fsnotify_mkdir(dir, dentry);
+ }
+out_up_write:
+ up_write(&fs_info->subvol_sem);
+ dput(dentry);
+out_unlock:
+ inode_unlock(dir);
+ return ret;
+}
+
static noinline int btrfs_ioctl_subvol_getflags(struct file *file,
void __user *arg)
{
The function will do the following things which are almost the opposite of what btrfs_delete_subvolume() does: 1. link the subvolume to the parent specified; 2. clear root flag and set root_refs to 1; 3. add the subvol to the uuid_tree; 4. delete the orphan_item. Signed-off-by: Lu Fengqi <lufq.fnst@cn.fujitsu.com> --- fs/btrfs/ioctl.c | 116 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+)