* [PATCH 1/2] btrfs: send: check for dead send root under critical section
2024-11-06 11:50 [PATCH 0/2] btrfs: some fixes around check send root flags fdmanana
@ 2024-11-06 11:50 ` fdmanana
2024-11-06 11:50 ` [PATCH 2/2] btrfs: send: check for read-only " fdmanana
2024-11-06 18:31 ` [PATCH 0/2] btrfs: some fixes around check send root flags David Sterba
2 siblings, 0 replies; 4+ messages in thread
From: fdmanana @ 2024-11-06 11:50 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
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>
---
fs/btrfs/send.c | 17 ++++++++---------
1 file changed, 8 insertions(+), 9 deletions(-)
diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c
index cadb945bb345..3fcc8113641d 100644
--- a/fs/btrfs/send.c
+++ b/fs/btrfs/send.c
@@ -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) {
--
2.45.2
^ permalink raw reply related [flat|nested] 4+ messages in thread* [PATCH 2/2] btrfs: send: check for read-only send root under critical section
2024-11-06 11:50 [PATCH 0/2] btrfs: some fixes around check send root flags fdmanana
2024-11-06 11:50 ` [PATCH 1/2] btrfs: send: check for dead send root under critical section fdmanana
@ 2024-11-06 11:50 ` fdmanana
2024-11-06 18:31 ` [PATCH 0/2] btrfs: some fixes around check send root flags David Sterba
2 siblings, 0 replies; 4+ messages in thread
From: fdmanana @ 2024-11-06 11:50 UTC (permalink / raw)
To: linux-btrfs
From: Filipe Manana <fdmanana@suse.com>
We're checking if the send root is read-only without being under the
protection of the root's root_item_lock spinlock, which is what protects
the root's flags when clearing the read-only flag, done at
btrfs_ioctl_subvol_setflags(). Furthermore, it should be done in the
same critical section that increments the root's send_in_progress counter,
as btrfs_ioctl_subvol_setflags() clears the read-only flag in the same
critical section that checks the counter's value.
So fix this by moving the read-only check under the critical section
delimited by the root's root_item_lock which also increments the root's
send_in_progress counter.
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
fs/btrfs/send.c | 16 ++++++----------
1 file changed, 6 insertions(+), 10 deletions(-)
diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c
index 3fcc8113641d..7254279c3cc9 100644
--- a/fs/btrfs/send.c
+++ b/fs/btrfs/send.c
@@ -8133,7 +8133,12 @@ long btrfs_ioctl_send(struct btrfs_inode *inode, const struct btrfs_ioctl_send_a
spin_unlock(&send_root->root_item_lock);
return -EPERM;
}
- if (btrfs_root_readonly(send_root) && send_root->dedupe_in_progress) {
+ /* Userspace tools do the checks and warn the user if it's not RO. */
+ if (!btrfs_root_readonly(send_root)) {
+ spin_unlock(&send_root->root_item_lock);
+ return -EPERM;
+ }
+ if (send_root->dedupe_in_progress) {
dedupe_in_progress_warn(send_root);
spin_unlock(&send_root->root_item_lock);
return -EAGAIN;
@@ -8141,15 +8146,6 @@ long btrfs_ioctl_send(struct btrfs_inode *inode, const struct btrfs_ioctl_send_a
send_root->send_in_progress++;
spin_unlock(&send_root->root_item_lock);
- /*
- * Userspace tools do the checks and warn the user if it's
- * not RO.
- */
- if (!btrfs_root_readonly(send_root)) {
- ret = -EPERM;
- goto out;
- }
-
/*
* Check that we don't overflow at later allocations, we request
* clone_sources_count + 1 items, and compare to unsigned long inside
--
2.45.2
^ permalink raw reply related [flat|nested] 4+ messages in thread* Re: [PATCH 0/2] btrfs: some fixes around check send root flags
2024-11-06 11:50 [PATCH 0/2] btrfs: some fixes around check send root flags fdmanana
2024-11-06 11:50 ` [PATCH 1/2] btrfs: send: check for dead send root under critical section fdmanana
2024-11-06 11:50 ` [PATCH 2/2] btrfs: send: check for read-only " fdmanana
@ 2024-11-06 18:31 ` David Sterba
2 siblings, 0 replies; 4+ messages in thread
From: David Sterba @ 2024-11-06 18:31 UTC (permalink / raw)
To: fdmanana; +Cc: linux-btrfs
On Wed, Nov 06, 2024 at 11:50:44AM +0000, fdmanana@kernel.org wrote:
> From: Filipe Manana <fdmanana@suse.com>
>
> Do proper checks for dead and read-only send roots while under the
> protection of the necessary lock. More details in the change logs.
>
> Filipe Manana (2):
> btrfs: send: check for dead send root under critical section
> btrfs: send: check for read-only send root under critical section
Reviewed-by: David Sterba <dsterba@suse.com>
^ permalink raw reply [flat|nested] 4+ messages in thread