Skip to content

Commit 521e054

Browse files
kdavemasoncl
authored andcommitted
btrfs: protect snapshots from deleting during send
The patch "Btrfs: fix protection between send and root deletion" (18f687d) does not actually prevent to delete the snapshot and just takes care during background cleaning, but this seems rather user unfriendly, this patch implements the idea presented in http://www.spinics.net/lists/linux-btrfs/msg30813.html - add an internal root_item flag to denote a dead root - check if the send_in_progress is set and refuse to delete, otherwise set the flag and proceed - check the flag in send similar to the btrfs_root_readonly checks, for all involved roots The root lookup in send via btrfs_read_fs_root_no_name will check if the root is really dead or not. If it is, ENOENT, aborted send. If it's alive, it's protected by send_in_progress, send can continue. CC: Miao Xie <miaox@cn.fujitsu.com> CC: Wang Shilong <wangsl.fnst@cn.fujitsu.com> Signed-off-by: David Sterba <dsterba@suse.cz> Signed-off-by: Chris Mason <clm@fb.com>
1 parent 944a451 commit 521e054

File tree

3 files changed

+53
-2
lines changed

3 files changed

+53
-2
lines changed

fs/btrfs/ctree.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -756,6 +756,12 @@ struct btrfs_dir_item {
756756

757757
#define BTRFS_ROOT_SUBVOL_RDONLY (1ULL << 0)
758758

759+
/*
760+
* Internal in-memory flag that a subvolume has been marked for deletion but
761+
* still visible as a directory
762+
*/
763+
#define BTRFS_ROOT_SUBVOL_DEAD (1ULL << 48)
764+
759765
struct btrfs_root_item {
760766
struct btrfs_inode_item inode;
761767
__le64 generation;
@@ -2791,6 +2797,11 @@ static inline bool btrfs_root_readonly(struct btrfs_root *root)
27912797
return (root->root_item.flags & cpu_to_le64(BTRFS_ROOT_SUBVOL_RDONLY)) != 0;
27922798
}
27932799

2800+
static inline bool btrfs_root_dead(struct btrfs_root *root)
2801+
{
2802+
return (root->root_item.flags & cpu_to_le64(BTRFS_ROOT_SUBVOL_DEAD)) != 0;
2803+
}
2804+
27942805
/* struct btrfs_root_backup */
27952806
BTRFS_SETGET_STACK_FUNCS(backup_tree_root, struct btrfs_root_backup,
27962807
tree_root, 64);

fs/btrfs/ioctl.c

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2219,6 +2219,7 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,
22192219
struct btrfs_ioctl_vol_args *vol_args;
22202220
struct btrfs_trans_handle *trans;
22212221
struct btrfs_block_rsv block_rsv;
2222+
u64 root_flags;
22222223
u64 qgroup_reserved;
22232224
int namelen;
22242225
int ret;
@@ -2240,6 +2241,7 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,
22402241
if (err)
22412242
goto out;
22422243

2244+
22432245
err = mutex_lock_killable_nested(&dir->i_mutex, I_MUTEX_PARENT);
22442246
if (err == -EINTR)
22452247
goto out_drop_write;
@@ -2301,6 +2303,27 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,
23012303
}
23022304

23032305
mutex_lock(&inode->i_mutex);
2306+
2307+
/*
2308+
* Don't allow to delete a subvolume with send in progress. This is
2309+
* inside the i_mutex so the error handling that has to drop the bit
2310+
* again is not run concurrently.
2311+
*/
2312+
spin_lock(&dest->root_item_lock);
2313+
root_flags = btrfs_root_flags(&root->root_item);
2314+
if (root->send_in_progress == 0) {
2315+
btrfs_set_root_flags(&root->root_item,
2316+
root_flags | BTRFS_ROOT_SUBVOL_DEAD);
2317+
spin_unlock(&dest->root_item_lock);
2318+
} else {
2319+
spin_unlock(&dest->root_item_lock);
2320+
btrfs_warn(root->fs_info,
2321+
"Attempt to delete subvolume %llu during send",
2322+
root->root_key.objectid);
2323+
err = -EPERM;
2324+
goto out_dput;
2325+
}
2326+
23042327
err = d_invalidate(dentry);
23052328
if (err)
23062329
goto out_unlock;
@@ -2389,6 +2412,13 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,
23892412
out_up_write:
23902413
up_write(&root->fs_info->subvol_sem);
23912414
out_unlock:
2415+
if (err) {
2416+
spin_lock(&dest->root_item_lock);
2417+
root_flags = btrfs_root_flags(&root->root_item);
2418+
btrfs_set_root_flags(&root->root_item,
2419+
root_flags & ~BTRFS_ROOT_SUBVOL_DEAD);
2420+
spin_unlock(&dest->root_item_lock);
2421+
}
23922422
mutex_unlock(&inode->i_mutex);
23932423
if (!err) {
23942424
shrink_dcache_sb(root->fs_info->sb);

fs/btrfs/send.c

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5518,7 +5518,7 @@ long btrfs_ioctl_send(struct file *mnt_file, void __user *arg_)
55185518

55195519
/*
55205520
* The subvolume must remain read-only during send, protect against
5521-
* making it RW.
5521+
* making it RW. This also protects against deletion.
55225522
*/
55235523
spin_lock(&send_root->root_item_lock);
55245524
send_root->send_in_progress++;
@@ -5578,6 +5578,15 @@ long btrfs_ioctl_send(struct file *mnt_file, void __user *arg_)
55785578
}
55795579

55805580
sctx->send_root = send_root;
5581+
/*
5582+
* Unlikely but possible, if the subvolume is marked for deletion but
5583+
* is slow to remove the directory entry, send can still be started
5584+
*/
5585+
if (btrfs_root_dead(sctx->send_root)) {
5586+
ret = -EPERM;
5587+
goto out;
5588+
}
5589+
55815590
sctx->clone_roots_cnt = arg->clone_sources_count;
55825591

55835592
sctx->send_max_size = BTRFS_SEND_BUF_SIZE;
@@ -5667,7 +5676,8 @@ long btrfs_ioctl_send(struct file *mnt_file, void __user *arg_)
56675676

56685677
spin_lock(&sctx->parent_root->root_item_lock);
56695678
sctx->parent_root->send_in_progress++;
5670-
if (!btrfs_root_readonly(sctx->parent_root)) {
5679+
if (!btrfs_root_readonly(sctx->parent_root) ||
5680+
btrfs_root_dead(sctx->parent_root)) {
56715681
spin_unlock(&sctx->parent_root->root_item_lock);
56725682
srcu_read_unlock(&fs_info->subvol_srcu, index);
56735683
ret = -EPERM;

0 commit comments

Comments
 (0)