Re: [PATCH] fs: btrfs: fix zstd decompression of sector-padded extents
Cole Munz <[email protected]> Fri, 31 Jul 2026 22:26:11 +0000
| Newsgroups | org.kernel.vger.linux-btrfs,org.u-boot-project.lists.u-boot |
|---|---|
| Message-ID | <-pmHXWS0jQjmnEICAo6l7i5Q5tFivBJ1Mm162qdIm_69a1gcfU0tBo4C-bfELAT1lCrGHRKlYr3_EcKwLFRX18SHErryHV_TCdz93esbQ30=@proton.me> |
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 =3D btrfs_compress_bio(inode, 0, blocksize, compress_type, ...);
so the page is zero-padded past EOF and the resulting zstd frame declares
frameContentSize =3D 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, siz=
e);
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 mo=
st
destlen. U-Boot's decompress_zstd() is the one-shot zstd_decompress_dctx(),
and btrfs_read_extent_inline() sizes the destination with
dsize =3D 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.
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 who=
se
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=3Dzstd, write an unaligned file, and include the dump-tree output =
plus
the frame header read from the leaf in the v2 cover.
The fix itself stays in btrfs's decompress_zstd() rather than lib/zstd
because for FIT/ximg/ubifs an undersized destination really does mean corru=
pt
input; only btrfs hands the decompressor a destination smaller than the fra=
me
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