From: Filipe Manana fdmanana@suse.com
commit 2f2e84ca60660402bd81d0859703567c59556e6a upstream.
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: Joan Bruguera Micó joanbrugueram@gmail.com Link: https://lore.kernel.org/linux-btrfs/20221223020509.457113-1-joanbrugueram@gm... Fixes: b6e833567ea1 ("btrfs: make hole and data seeking a lot more efficient") CC: stable@vger.kernel.org # 6.1 Tested-by: Joan Bruguera Micó joanbrugueram@gmail.com Signed-off-by: Filipe Manana fdmanana@suse.com Signed-off-by: David Sterba dsterba@suse.com Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org --- fs/btrfs/extent-io-tree.c | 2 +- fs/btrfs/file.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)
--- a/fs/btrfs/extent-io-tree.c +++ b/fs/btrfs/extent-io-tree.c @@ -1507,7 +1507,7 @@ u64 count_range_bits(struct extent_io_tr 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); --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -3671,7 +3671,7 @@ bool btrfs_find_delalloc_in_range(struct u64 prev_delalloc_end = 0; bool ret = false;
- while (cur_offset < end) { + while (cur_offset <= end) { u64 delalloc_start; u64 delalloc_end; bool delalloc;