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

Qu Wenruo <[email protected]> Sat, 1 Aug 2026 08:07:42 +0930
Newsgroups gmane.comp.boot-loaders.u-boot,gmane.comp.file-systems.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
>