aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs/ioctl.c
diff options
context:
space:
mode:
authorDavid Sterba <dsterba@suse.cz>2014-04-15 10:41:44 -0400
committerChris Mason <clm@fb.com>2014-06-09 20:20:31 -0400
commit521e0546c970c3d845076f243828fa7abd71edfc (patch)
treee203f19e76c97629173c097e051946e2aeb6ce4a /fs/btrfs/ioctl.c
parent944a4515b27302ac42d5964b7400c72f4d692782 (diff)
btrfs: protect snapshots from deleting during send
The patch "Btrfs: fix protection between send and root deletion" (18f687d538449373c37c) 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>
Diffstat (limited to 'fs/btrfs/ioctl.c')
-rw-r--r--fs/btrfs/ioctl.c30
1 files changed, 30 insertions, 0 deletions
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 2db12fc3f748..2db3fe12f50d 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -2219,6 +2219,7 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,
2219 struct btrfs_ioctl_vol_args *vol_args; 2219 struct btrfs_ioctl_vol_args *vol_args;
2220 struct btrfs_trans_handle *trans; 2220 struct btrfs_trans_handle *trans;
2221 struct btrfs_block_rsv block_rsv; 2221 struct btrfs_block_rsv block_rsv;
2222 u64 root_flags;
2222 u64 qgroup_reserved; 2223 u64 qgroup_reserved;
2223 int namelen; 2224 int namelen;
2224 int ret; 2225 int ret;
@@ -2240,6 +2241,7 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,
2240 if (err) 2241 if (err)
2241 goto out; 2242 goto out;
2242 2243
2244
2243 err = mutex_lock_killable_nested(&dir->i_mutex, I_MUTEX_PARENT); 2245 err = mutex_lock_killable_nested(&dir->i_mutex, I_MUTEX_PARENT);
2244 if (err == -EINTR) 2246 if (err == -EINTR)
2245 goto out_drop_write; 2247 goto out_drop_write;
@@ -2301,6 +2303,27 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,
2301 } 2303 }
2302 2304
2303 mutex_lock(&inode->i_mutex); 2305 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
2304 err = d_invalidate(dentry); 2327 err = d_invalidate(dentry);
2305 if (err) 2328 if (err)
2306 goto out_unlock; 2329 goto out_unlock;
@@ -2389,6 +2412,13 @@ out_release:
2389out_up_write: 2412out_up_write:
2390 up_write(&root->fs_info->subvol_sem); 2413 up_write(&root->fs_info->subvol_sem);
2391out_unlock: 2414out_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 }
2392 mutex_unlock(&inode->i_mutex); 2422 mutex_unlock(&inode->i_mutex);
2393 if (!err) { 2423 if (!err) {
2394 shrink_dcache_sb(root->fs_info->sb); 2424 shrink_dcache_sb(root->fs_info->sb);