]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/blob
1fc46c8d5191e4530ed9e25ca049c6d426a2020f
[thirdparty/kernel/stable-queue.git] /
1 From 3324d0547861b16cf436d54abba7052e0c8aa9de Mon Sep 17 00:00:00 2001
2 From: Omar Sandoval <osandov@fb.com>
3 Date: Thu, 4 Jan 2024 11:48:47 -0800
4 Subject: btrfs: avoid copying BTRFS_ROOT_SUBVOL_DEAD flag to snapshot of subvolume being deleted
5
6 From: Omar Sandoval <osandov@fb.com>
7
8 commit 3324d0547861b16cf436d54abba7052e0c8aa9de upstream.
9
10 Sweet Tea spotted a race between subvolume deletion and snapshotting
11 that can result in the root item for the snapshot having the
12 BTRFS_ROOT_SUBVOL_DEAD flag set. The race is:
13
14 Thread 1 | Thread 2
15 ----------------------------------------------|----------
16 btrfs_delete_subvolume |
17 btrfs_set_root_flags(BTRFS_ROOT_SUBVOL_DEAD)|
18 |btrfs_mksubvol
19 | down_read(subvol_sem)
20 | create_snapshot
21 | ...
22 | create_pending_snapshot
23 | copy root item from source
24 down_write(subvol_sem) |
25
26 This flag is only checked in send and swap activate, which this would
27 cause to fail mysteriously.
28
29 create_snapshot() now checks the root refs to reject a deleted
30 subvolume, so we can fix this by locking subvol_sem earlier so that the
31 BTRFS_ROOT_SUBVOL_DEAD flag and the root refs are updated atomically.
32
33 CC: stable@vger.kernel.org # 4.14+
34 Reported-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
35 Reviewed-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
36 Reviewed-by: Anand Jain <anand.jain@oracle.com>
37 Signed-off-by: Omar Sandoval <osandov@fb.com>
38 Reviewed-by: David Sterba <dsterba@suse.com>
39 Signed-off-by: David Sterba <dsterba@suse.com>
40 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
41 ---
42 fs/btrfs/inode.c | 22 +++++++++++++---------
43 1 file changed, 13 insertions(+), 9 deletions(-)
44
45 --- a/fs/btrfs/inode.c
46 +++ b/fs/btrfs/inode.c
47 @@ -4501,6 +4501,8 @@ int btrfs_delete_subvolume(struct inode
48 u64 root_flags;
49 int ret;
50
51 + down_write(&fs_info->subvol_sem);
52 +
53 /*
54 * Don't allow to delete a subvolume with send in progress. This is
55 * inside the inode lock so the error handling that has to drop the bit
56 @@ -4512,25 +4514,25 @@ int btrfs_delete_subvolume(struct inode
57 btrfs_warn(fs_info,
58 "attempt to delete subvolume %llu during send",
59 dest->root_key.objectid);
60 - return -EPERM;
61 + ret = -EPERM;
62 + goto out_up_write;
63 }
64 if (atomic_read(&dest->nr_swapfiles)) {
65 spin_unlock(&dest->root_item_lock);
66 btrfs_warn(fs_info,
67 "attempt to delete subvolume %llu with active swapfile",
68 root->root_key.objectid);
69 - return -EPERM;
70 + ret = -EPERM;
71 + goto out_up_write;
72 }
73 root_flags = btrfs_root_flags(&dest->root_item);
74 btrfs_set_root_flags(&dest->root_item,
75 root_flags | BTRFS_ROOT_SUBVOL_DEAD);
76 spin_unlock(&dest->root_item_lock);
77
78 - down_write(&fs_info->subvol_sem);
79 -
80 ret = may_destroy_subvol(dest);
81 if (ret)
82 - goto out_up_write;
83 + goto out_undead;
84
85 btrfs_init_block_rsv(&block_rsv, BTRFS_BLOCK_RSV_TEMP);
86 /*
87 @@ -4540,7 +4542,7 @@ int btrfs_delete_subvolume(struct inode
88 */
89 ret = btrfs_subvolume_reserve_metadata(root, &block_rsv, 5, true);
90 if (ret)
91 - goto out_up_write;
92 + goto out_undead;
93
94 trans = btrfs_start_transaction(root, 0);
95 if (IS_ERR(trans)) {
96 @@ -4606,15 +4608,17 @@ out_end_trans:
97 inode->i_flags |= S_DEAD;
98 out_release:
99 btrfs_subvolume_release_metadata(root, &block_rsv);
100 -out_up_write:
101 - up_write(&fs_info->subvol_sem);
102 +out_undead:
103 if (ret) {
104 spin_lock(&dest->root_item_lock);
105 root_flags = btrfs_root_flags(&dest->root_item);
106 btrfs_set_root_flags(&dest->root_item,
107 root_flags & ~BTRFS_ROOT_SUBVOL_DEAD);
108 spin_unlock(&dest->root_item_lock);
109 - } else {
110 + }
111 +out_up_write:
112 + up_write(&fs_info->subvol_sem);
113 + if (!ret) {
114 d_invalidate(dentry);
115 btrfs_prune_dentries(dest);
116 ASSERT(dest->send_in_progress == 0);