From: Dominique Martinet <dominique.marti...@atmark-techno.com> btrfs_read_extent_reg correctly computed the extent offset in the BTRFS_COMPRESS_NONE case, but did not account for the 'offset - key.offset' part correctly in the compressed case, making the function read incorrect data.
In the case I examined, the last 4k of a file was corrupted and contained data from a few blocks prior: btrfs_file_read() -> btrfs_read_extent_reg (aligned part loop from 0x40480000 to 0x40ba0000, 128KB at a time) last read had 0x4000 bytes in extent, but aligned_end - cur limited the read to 0x3000 (offset 0x720000) -> read_and_truncate_page -> btrfs_read_extent_reg reading the last 0x1000 bytes from offset 0x73000 (end of the previous 0x4000) into 0x40ba3000 here key.offset = 0x70000 so we need to use it to recover the 0x3000 offset. Confirmed by checking data, before patch: u-boot=> mmc load 1:1 $loadaddr boot/uImage u-boot=> md 0x40ba0000 40ba0000: c0232ff8 c0c722cb 030e94be bf10000c ./#..".......... u-boot=> md 0x40ba3000 40ba3000: c0232ff8 c0c722cb 030e94be bf10000c ./#..".......... After patch (also confirmed with data read from linux): u-boot=> md 0x40ba3000 40ba3000: 64cc9f03 81142256 6910000c 0c03483c ...dV".....i<H.. Note that the code previously (before commit e3427184f38a ("fs: btrfs: Implement btrfs_file_read()")) did not split that read in two, so this is a regression. Fixes: a26a6bedafcf ("fs: btrfs: Introduce btrfs_read_extent_inline() and btrfs_read_extent_reg()") Signed-off-by: Dominique Martinet <dominique.marti...@atmark-techno.com> --- fs/btrfs/inode.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 40025662f250..3d6e39e6544d 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -443,6 +443,8 @@ int btrfs_read_extent_reg(struct btrfs_path *path, IS_ALIGNED(len, fs_info->sectorsize)); ASSERT(offset >= key.offset && offset + len <= key.offset + extent_num_bytes); + /* offset is now offset within extent */ + offset = btrfs_file_extent_offset(leaf, fi) + offset - key.offset; /* Preallocated or hole , fill @dest with zero */ if (btrfs_file_extent_type(leaf, fi) == BTRFS_FILE_EXTENT_PREALLOC || @@ -454,9 +456,7 @@ int btrfs_read_extent_reg(struct btrfs_path *path, if (btrfs_file_extent_compression(leaf, fi) == BTRFS_COMPRESS_NONE) { u64 logical; - logical = btrfs_file_extent_disk_bytenr(leaf, fi) + - btrfs_file_extent_offset(leaf, fi) + - offset - key.offset; + logical = btrfs_file_extent_disk_bytenr(leaf, fi) + offset; read = len; num_copies = btrfs_num_copies(fs_info, logical, len); @@ -511,7 +511,7 @@ int btrfs_read_extent_reg(struct btrfs_path *path, if (ret < dsize) memset(dbuf + ret, 0, dsize - ret); /* Then copy the needed part */ - memcpy(dest, dbuf + btrfs_file_extent_offset(leaf, fi), len); + memcpy(dest, dbuf + offset, len); ret = len; out: free(cbuf); -- 2.39.2