diff mbox series

btrfs: fix off-by-one in delalloc search during lseek

Message ID da314182610b43632ca81ba78ff73fac5ae1bf06.1671820088.git.fdmanana@suse.com (mailing list archive)
State New, archived
Headers show
Series btrfs: fix off-by-one in delalloc search during lseek | expand

Commit Message

Filipe Manana Dec. 23, 2022, 6:28 p.m. UTC
From: Filipe Manana <fdmanana@suse.com>

During lseek, when searching for delalloc in a range that represents a
hole and that range has a length of 1 byte, we end up not doing the actual
delalloc search in the inode's io tree, resulting in not correctly
reporting the offset with data or a hole. This actually only happens when
the start offset is 0 because with any other start offset we round it down
by sector size.

Reproducer:

  $ mkfs.btrfs -f /dev/sdc
  $ mount /dev/sdc /mnt/sdc

  $ xfs_io -f -c "pwrite -q 0 1" /mnt/sdc/foo

  $ xfs_io -c "seek -d 0" /mnt/sdc/foo
  Whence   Result
  DATA	   EOF

It should have reported an offset of 0 instead of EOF.

Fix this by updating btrfs_find_delalloc_in_range() and count_range_bits()
to deal with inclusive ranges properly. These functions are already
suppossed to work with inclusive end offsets, they just got it wrong in a
couple places due to off-by-one mistakes.

A test case for fstests will be added later.

Reported-by: Joan Bruguera Micó <joanbrugueram@gmail.com>
Link: https://lore.kernel.org/linux-btrfs/20221223020509.457113-1-joanbrugueram@gmail.com/
Fixes: b6e833567ea1 ("btrfs: make hole and data seeking a lot more efficient")
Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
 fs/btrfs/extent-io-tree.c | 2 +-
 fs/btrfs/file.c           | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

Comments

Joan Bruguera Micó Dec. 24, 2022, 4:18 p.m. UTC | #1
Tested-by: Joan Bruguera Micó <joanbrugueram@gmail.com>
David Sterba Jan. 2, 2023, 3:29 p.m. UTC | #2
On Fri, Dec 23, 2022 at 06:28:53PM +0000, fdmanana@kernel.org wrote:
> From: Filipe Manana <fdmanana@suse.com>
> 
> During lseek, when searching for delalloc in a range that represents a
> hole and that range has a length of 1 byte, we end up not doing the actual
> delalloc search in the inode's io tree, resulting in not correctly
> reporting the offset with data or a hole. This actually only happens when
> the start offset is 0 because with any other start offset we round it down
> by sector size.
> 
> Reproducer:
> 
>   $ mkfs.btrfs -f /dev/sdc
>   $ mount /dev/sdc /mnt/sdc
> 
>   $ xfs_io -f -c "pwrite -q 0 1" /mnt/sdc/foo
> 
>   $ xfs_io -c "seek -d 0" /mnt/sdc/foo
>   Whence   Result
>   DATA	   EOF
> 
> It should have reported an offset of 0 instead of EOF.
> 
> Fix this by updating btrfs_find_delalloc_in_range() and count_range_bits()
> to deal with inclusive ranges properly. These functions are already
> suppossed to work with inclusive end offsets, they just got it wrong in a
> couple places due to off-by-one mistakes.
> 
> A test case for fstests will be added later.
> 
> Reported-by: Joan Bruguera Micó <joanbrugueram@gmail.com>
> Link: https://lore.kernel.org/linux-btrfs/20221223020509.457113-1-joanbrugueram@gmail.com/
> Fixes: b6e833567ea1 ("btrfs: make hole and data seeking a lot more efficient")
> Signed-off-by: Filipe Manana <fdmanana@suse.com>

Added to misc-next, thanks.
diff mbox series

Patch

diff --git a/fs/btrfs/extent-io-tree.c b/fs/btrfs/extent-io-tree.c
index 9ae9cd1e7035..3c7766dfaa69 100644
--- a/fs/btrfs/extent-io-tree.c
+++ b/fs/btrfs/extent-io-tree.c
@@ -1551,7 +1551,7 @@  u64 count_range_bits(struct extent_io_tree *tree,
 	u64 last = 0;
 	int found = 0;
 
-	if (WARN_ON(search_end <= cur_start))
+	if (WARN_ON(search_end < cur_start))
 		return 0;
 
 	spin_lock(&tree->lock);
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index 91b00eb2440e..834bbcb91102 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -3354,7 +3354,7 @@  bool btrfs_find_delalloc_in_range(struct btrfs_inode *inode, u64 start, u64 end,
 	bool search_io_tree = true;
 	bool ret = false;
 
-	while (cur_offset < end) {
+	while (cur_offset <= end) {
 		u64 delalloc_start;
 		u64 delalloc_end;
 		bool delalloc;