Linux Btrfs filesystem development
 help / color / mirror / Atom feed
From: Qu Wenruo <quwenruo.btrfs@gmx.com>
To: "Cole Munz" <Munzzyy1@proton.me>,
	"Marek Behún" <kabel@kernel.org>, "Tom Rini" <trini@konsulko.com>
Cc: Qu Wenruo <wqu@suse.com>, Simon Glass <sjg@chromium.org>,
	u-boot@lists.denx.de, linux-btrfs@vger.kernel.org
Subject: Re: [PATCH] fs: btrfs: fix zstd decompression of sector-padded extents
Date: Sat, 1 Aug 2026 07:08:29 +0930	[thread overview]
Message-ID: <3659c07e-7f73-460c-8391-e2b8508e2bd4@gmx.com> (raw)
In-Reply-To: <9acf406b5e73fa83001e1951e883143e2afb8b2d.1785528314.git.Munzzyy1@proton.me>



在 2026/8/1 05:37, Cole Munz 写道:
> Btrfs pads compressed extents up to a sector boundary, so a file whose
> size isn't sector-aligned still gets compressed as a frame whose
> declared content size is the sector-rounded length, larger than the
> extent's ram_bytes.

The padding at plain text level is completely common, since btrfs like 
all other major fses are block device based, all IO including 
compression is done at fs block level, so is the compressed data, which 
still needs to be block aligned.

But your "larger than the extent's ram_bytes" is where I do not get.

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".

Can you provide more info about this, or better, provide the dump-tree 
output for the involved files?

Thanks,
Qu

> zstd_decompress_dctx()'s one-shot API requires the
> destination buffer to cover the whole frame and fails with
> ZSTD_error_dstSize_tooSmall otherwise, even though
> btrfs_read_extent_reg() and btrfs_read_extent_inline() already
> zero-fill any short tail. The file then reads back truncated or the
> read fails outright, which is what breaks fdt apply on zstd-compressed
> overlays.
> 
> Decompress into a bounce buffer sized to the frame when the declared
> content size exceeds ram_bytes, and copy out only what the caller
> asked for. Also trim the input with zstd_find_frame_compressed_size()
> first, since the on-disk extent may carry trailing sector padding
> after the frame.
> 
> The fix lives here rather than in the shared lib/zstd/zstd.c wrapper
> on purpose: for FIT images, ximg and ubifs an undersized destination
> really does mean a corrupt input and should keep failing hard. Armbian
> carries the same caller-side fix for this exact failure on RK3399 and
> ODROID-N2 boards (armbian/build#9651, #10208), where it showed up as
> "zstd_decompress: failed to decompress: 70".
> 
> Fixes: 918adf8e0733 ("btrfs: Use U-Boot API for decompression")
> Signed-off-by: Cole Munz <Munzzyy1@proton.me>
> ---
>   fs/btrfs/compression.c | 76 +++++++++++++++++++++++++++++++++++++++---
>   1 file changed, 71 insertions(+), 5 deletions(-)
> 
> diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c
> index c69524d38ecc..017e61242a68 100644
> --- a/fs/btrfs/compression.c
> +++ b/fs/btrfs/compression.c
> @@ -6,7 +6,7 @@
>    */
>   
>   #include "btrfs.h"
> -#include <abuf.h>
> +#include <limits.h>
>   #include <log.h>
>   #include <malloc.h>
>   #include <linux/lzo.h>
> @@ -137,12 +137,78 @@ static u32 decompress_zlib(const u8 *_cbuf, u32 clen, u8 *dbuf, u32 dlen)
>   
>   static u32 decompress_zstd(const u8 *cbuf, u32 clen, u8 *dbuf, u32 dlen)
>   {
> -	struct abuf in, out;
> +	zstd_dctx *ctx;
> +	zstd_frame_header fh;
> +	size_t wsize, len, dst_capacity = dlen;
> +	void *workspace;
> +	u8 *dst = dbuf, *bounce = NULL;
> +	u32 ret;
> +
> +	wsize = zstd_dctx_workspace_bound();
> +	workspace = malloc(wsize);
> +	if (!workspace)
> +		return -1;
> +
> +	ctx = zstd_init_dctx(workspace, wsize);
> +	if (!ctx) {
> +		ret = -1;
> +		goto out;
> +	}
>   
> -	abuf_init_set(&in, (u8 *)cbuf, clen);
> -	abuf_init_set(&out, dbuf, dlen);
> +	/*
> +	 * Compressed extents are padded up to a sector boundary, so clen may
> +	 * include trailing junk after the actual zstd frame.
> +	 */
> +	len = zstd_find_frame_compressed_size(cbuf, clen);
> +	if (zstd_is_error(len)) {
> +		ret = -1;
> +		goto out;
> +	}
>   
> -	return zstd_decompress(&in, &out);
> +	/*
> +	 * Btrfs compresses whole sectors, so a file whose size is not a
> +	 * multiple of the sector size still yields a frame whose content
> +	 * size is the sector-rounded length, which can be larger than dlen
> +	 * (ram_bytes from the extent item). zstd_decompress_dctx() requires
> +	 * its output buffer to cover the whole frame and would otherwise
> +	 * fail with ZSTD_error_dstSize_tooSmall, even though the extra bytes
> +	 * are just padding the caller is going to discard: both
> +	 * btrfs_read_extent_reg() and btrfs_read_extent_inline() already
> +	 * zero-fill any tail beyond what we return here. Decompress into a
> +	 * bounce buffer sized to the frame when that happens.
> +	 */
> +	if (!zstd_get_frame_header(&fh, cbuf, len) &&
> +	    fh.frameContentSize != ZSTD_CONTENTSIZE_UNKNOWN &&
> +	    fh.frameContentSize > dlen) {
> +		if (fh.frameContentSize > SIZE_MAX) {
> +			ret = -1;
> +			goto out;
> +		}
> +		bounce = malloc(fh.frameContentSize);
> +		if (!bounce) {
> +			ret = -1;
> +			goto out;
> +		}
> +		dst = bounce;
> +		dst_capacity = fh.frameContentSize;
> +	}
> +
> +	len = zstd_decompress_dctx(ctx, dst, dst_capacity, cbuf, len);
> +	if (zstd_is_error(len)) {
> +		ret = -1;
> +		goto out;
> +	}
> +
> +	if (bounce) {
> +		memcpy(dbuf, bounce, dlen);
> +		ret = dlen;
> +	} else {
> +		ret = len;
> +	}
> +out:
> +	free(bounce);
> +	free(workspace);
> +	return ret;
>   }
>   
>   u32 btrfs_decompress(u8 type, const char *c, u32 clen, char *d, u32 dlen)


  reply	other threads:[~2026-07-31 21:38 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-31 20:07 [PATCH] fs: btrfs: fix zstd decompression of sector-padded extents Cole Munz
2026-07-31 21:38 ` Qu Wenruo [this message]
2026-07-31 22:26   ` Cole Munz
2026-07-31 22:37     ` Qu Wenruo
2026-08-01  0:55 ` [PATCH v2] fs: btrfs: fix zstd decompression of compressed inline extents Cole Munz
2026-08-01  1:13   ` Qu Wenruo

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=3659c07e-7f73-460c-8391-e2b8508e2bd4@gmx.com \
    --to=quwenruo.btrfs@gmx.com \
    --cc=Munzzyy1@proton.me \
    --cc=kabel@kernel.org \
    --cc=linux-btrfs@vger.kernel.org \
    --cc=sjg@chromium.org \
    --cc=trini@konsulko.com \
    --cc=u-boot@lists.denx.de \
    --cc=wqu@suse.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox