Re: [PATCH] fs: btrfs: fix zstd decompression of sector-padded extents

Qu Wenruo <[email protected]>
Newsgroups org.u-boot-project.lists.u-boot,org.kernel.vger.linux-btrfs
Message-ID <[email protected]>

在 2026/8/1 07:56, Cole Munz 写道:
> Hi Qu,
> 
>> The ram_bytes described the decompressed size of a compressed extent,
>> except inlined extents, the ram_bytes should always be fs block aligned.
>>
>> I didn't see how things can go "larger than the extent's ram_bytes".
> 
> You're right, and the commit message overstates it. For regular extents
> ram_bytes is block aligned and matches the frame's content size, exactly as
> you describe. The case that actually fails is the one you carved out:
> compressed inline extents.
> 
> For the inline path, current mainline compresses the whole first block:
> 
>    run_delalloc_inline():
>      cb = btrfs_compress_bio(inode, 0, blocksize, compress_type, ...);
> 
> so the page is zero-padded past EOF and the resulting zstd frame declares
> frameContentSize = blocksize. But the extent item gets the unaligned size:
> 
>    __cow_file_range_inline(inode, i_size, compressed_size, ...) ->
>      insert_inline_extent() -> btrfs_set_file_extent_ram_bytes(leaf, ei, size);
> 
> So a 1900-byte file stored as a compressed inline extent has ram_bytes 1900
> while the frame decodes to 4096. The kernel side never notices because
> fs/btrfs/zstd.c zstd_decompress() streams into out_buf and copies out at most
> destlen. U-Boot's decompress_zstd() is the one-shot zstd_decompress_dctx(),
> and btrfs_read_extent_inline() sizes the destination with
> dsize = btrfs_file_extent_ram_bytes(), so the frame fails the whole-frame
> capacity check with dstSize_tooSmall - error code 70, which matches the
> "failed to decompress: 70" in the Armbian reports.

Thanks a lot! Now I see where the problem is.

> 
> One data point from testing while chasing this: an image built with
> mkfs.btrfs --rootdir --compress zstd (btrfs-progs 7.1) does NOT reproduce.
> For a 1900-byte file, progs writes an inline extent with ram_bytes 1900 whose
> frame also decodes to exactly 1900:
> 
>    item 8 key (257 EXTENT_DATA 0) itemoff 15316 itemsize 423
>        generation 6 type 0 (inline)
>        inline extent data size 402 ram_bytes 1900 compression 3 (zstd)
> 
> so mkfs-built images boot fine, and the failure only shows up on files
> (re)written at runtime through the kernel - which fits the Armbian pattern of
> /boot scripts and overlays breaking after a package update touched them.
> 
> I'll send a v2 with the commit message rewritten to name compressed inline
> extents as the failing case instead of the hand-wave about sector padding. If
> you'd like the on-disk evidence too, I can loop-mount a scratch fs with
> compress=zstd, write an unaligned file, and include the dump-tree output plus
> the frame header read from the leaf in the v2 cover.

Another thing is, since this bug only affects inlined extents, I'd 
prefer to have the fix located inside btrfs_read_extent_inline().

So that we allocate a full block for decompression, then only copy 
ram_bytes back to the destination.

With a mention about the limit during dbuf allocation inside 
btrfs_read_extent_inline().

Thanks a lot of the explanation and fix,
Qu

> 
> The fix itself stays in btrfs's decompress_zstd() rather than lib/zstd
> because for FIT/ximg/ubifs an undersized destination really does mean corrupt
> input; only btrfs hands the decompressor a destination smaller than the frame
> on purpose. If you'd rather see it shaped differently - say, only engaging
> the bounce path for inline extents - happy to do that in v2 as well.
> 
> Thanks,
> Cole
>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.