]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
btrfs: send: check for dead send root under critical section
authorFilipe Manana <fdmanana@suse.com>
Wed, 6 Nov 2024 10:32:09 +0000 (10:32 +0000)
committerDavid Sterba <dsterba@suse.com>
Mon, 11 Nov 2024 13:34:23 +0000 (14:34 +0100)
We're checking if the send root is dead without the protection of the
root's root_item_lock spinlock, which is what protects the root's flags.
The inverse, setting the dead flag on a root, is done under the protection
of that lock, at btrfs_delete_subvolume(). Also checking and updating the
root's send_in_progress counter is supposed to be done in the same
critical section as checking for or setting the root dead flag, so that
these operations are done atomically as a single step (which is correctly
done by btrfs_delete_subvolume()).

So fix this by checking if the send root is dead in the same critical
section that updates the send_in_progress counter, which is protected by
the root's root_item_lock spinlock.

Signed-off-by: Filipe Manana <fdmanana@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/send.c

index cadb945bb345a71125b3e554f64f61ab4a4202c6..3fcc8113641dd4309a0aa20353ea1071ccf007eb 100644 (file)
@@ -8125,6 +8125,14 @@ long btrfs_ioctl_send(struct btrfs_inode *inode, const struct btrfs_ioctl_send_a
         * making it RW. This also protects against deletion.
         */
        spin_lock(&send_root->root_item_lock);
+       /*
+        * Unlikely but possible, if the subvolume is marked for deletion but
+        * is slow to remove the directory entry, send can still be started.
+        */
+       if (btrfs_root_dead(send_root)) {
+               spin_unlock(&send_root->root_item_lock);
+               return -EPERM;
+       }
        if (btrfs_root_readonly(send_root) && send_root->dedupe_in_progress) {
                dedupe_in_progress_warn(send_root);
                spin_unlock(&send_root->root_item_lock);
@@ -8207,15 +8215,6 @@ long btrfs_ioctl_send(struct btrfs_inode *inode, const struct btrfs_ioctl_send_a
        }
 
        sctx->send_root = send_root;
-       /*
-        * Unlikely but possible, if the subvolume is marked for deletion but
-        * is slow to remove the directory entry, send can still be started
-        */
-       if (btrfs_root_dead(sctx->send_root)) {
-               ret = -EPERM;
-               goto out;
-       }
-
        sctx->clone_roots_cnt = arg->clone_sources_count;
 
        if (sctx->proto >= 2) {