Commit 2f2e84ca authored by Filipe Manana's avatar Filipe Manana Committed by David Sterba

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

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
supposed 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: default avatarJoan Bruguera Micó <joanbrugueram@gmail.com>
Link: https://lore.kernel.org/linux-btrfs/20221223020509.457113-1-joanbrugueram@gmail.com/
Fixes: b6e83356 ("btrfs: make hole and data seeking a lot more efficient")
CC: stable@vger.kernel.org # 6.1
Tested-by: default avatarJoan Bruguera Micó <joanbrugueram@gmail.com>
Signed-off-by: default avatarFilipe Manana <fdmanana@suse.com>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
parent 1d854e4f
...@@ -1551,7 +1551,7 @@ u64 count_range_bits(struct extent_io_tree *tree, ...@@ -1551,7 +1551,7 @@ u64 count_range_bits(struct extent_io_tree *tree,
u64 last = 0; u64 last = 0;
int found = 0; int found = 0;
if (WARN_ON(search_end <= cur_start)) if (WARN_ON(search_end < cur_start))
return 0; return 0;
spin_lock(&tree->lock); spin_lock(&tree->lock);
......
...@@ -3354,7 +3354,7 @@ bool btrfs_find_delalloc_in_range(struct btrfs_inode *inode, u64 start, u64 end, ...@@ -3354,7 +3354,7 @@ bool btrfs_find_delalloc_in_range(struct btrfs_inode *inode, u64 start, u64 end,
bool search_io_tree = true; bool search_io_tree = true;
bool ret = false; bool ret = false;
while (cur_offset < end) { while (cur_offset <= end) {
u64 delalloc_start; u64 delalloc_start;
u64 delalloc_end; u64 delalloc_end;
bool delalloc; bool delalloc;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment