Skip to content

u-boot: v2026.01: fix BTRFS zstd decompression failure (error 70)#9651

Open
iav wants to merge 1 commit intoarmbian:mainfrom
iav:fix/uboot-btrfs-zstd-decompression
Open

u-boot: v2026.01: fix BTRFS zstd decompression failure (error 70)#9651
iav wants to merge 1 commit intoarmbian:mainfrom
iav:fix/uboot-btrfs-zstd-decompression

Conversation

@iav
Copy link
Copy Markdown
Contributor

@iav iav commented Apr 10, 2026

Summary

  • Fix zstd decompression in U-Boot when booting from a BTRFS partition with zstd compression
  • The generic zstd_decompress() wrapper in lib/zstd/zstd.c fails for BTRFS extents due to two sector-alignment mismatches
  • Call zstd_decompress_dctx() directly with zstd_find_frame_compressed_size() to strip input padding and ZSTD_getFrameContentSize() to handle output buffer size mismatch

Root cause

  1. Sector-aligned compressed size: BTRFS stores compressed extents padded to sector boundaries (4096 bytes). zstd_decompress_dctx() rejects trailing data after the zstd frame, breaking regular (non-inline) extents.

  2. Sector-aligned decompressed size: BTRFS compresses in sector-sized blocks, so the zstd frame content size may exceed ram_bytes from extent metadata (e.g. a 3906-byte file is compressed as a 4096-byte block). zstd_decompress_dctx() returns ZSTD_error_dstSize_tooSmall (error 70) for inline extents.

Symptoms:

zstd_decompress: failed to decompress: 70
BTRFS: An error occurred while reading file /boot/boot.scr

Testing done

  • Helios64 (RK3399), U-Boot v2026.01, SD card with BTRFS + zstd compression
  • boot.scr, armbianEnv.txt (inline extents) read successfully
  • Image (37MB), DTB (90KB), uInitrd (30MB) (regular compressed extents) read successfully
  • Kernel starts, system boots to login prompt

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Improved Zstandard decompression for Btrfs in U-Boot on Helios64: accurately detect and trim padded trailing bytes, handle full-frame content sizes (including large frames), allocate appropriate temporary buffers and workspaces, and avoid oversized-buffer or allocation-related failures to reduce decompression errors and improve compatibility with ZSTD-compressed filesystem data.

@iav iav requested a review from prahal as a code owner April 10, 2026 21:32
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 10, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1f19a68b-283d-4461-b250-5facf9d1fa20

📥 Commits

Reviewing files that changed from the base of the PR and between 2e91b75 and c644580.

📒 Files selected for processing (1)
  • patch/u-boot/v2026.01/board_helios64/general-fix-btrfs-zstd-decompression.patch
🚧 Files skipped from review as they are similar to previous changes (1)
  • patch/u-boot/v2026.01/board_helios64/general-fix-btrfs-zstd-decompression.patch

📝 Walkthrough

Walkthrough

Replaces BTRFS zstd decompression to use libzstd decompression-context APIs, detect exact zstd frame compressed/content sizes, strip BTRFS sector padding, optionally allocate temporary output/workspace buffers, perform decompression via a dctx, and return -1 on errors.

Changes

Cohort / File(s) Summary
BTRFS Zstd Decompression
fs/btrfs/compression.c, patch/u-boot/v2026.01/board_helios64/general-fix-btrfs-zstd-decompression.patch
Reworks internal decompress_zstd() to call zstd_find_frame_compressed_size() to trim sector padding, use ZSTD_getFrameContentSize() to determine output sizing, allocate temporary output if frame > requested dlen, allocate zstd workspace via zstd_dctx_workspace_bound(), init dctx (zstd_init_dctx()), call zstd_decompress_dctx(), copy first dlen bytes back when using temp buffer, and handle allocation/decompression errors by returning -1.

Sequence Diagram(s)

sequenceDiagram
    participant Caller
    participant decompress_zstd
    participant Allocator
    participant libzstd
    Caller->>decompress_zstd: call(ibuf, clen, dbuf, dlen)
    decompress_zstd->>libzstd: zstd_find_frame_compressed_size(ibuf, clen)
    libzstd-->>decompress_zstd: frame_compressed_size / error
    decompress_zstd->>decompress_zstd: adjust clen (strip padding)
    decompress_zstd->>libzstd: ZSTD_getFrameContentSize(ibuf)
    libzstd-->>decompress_zstd: frame_decompressed_size
    alt frame_decompressed_size known and > dlen
        decompress_zstd->>Allocator: malloc(temp_out, frame_decompressed_size)
        Allocator-->>decompress_zstd: temp_out / NULL
    end
    decompress_zstd->>Allocator: malloc(workspace, zstd_dctx_workspace_bound())
    Allocator-->>decompress_zstd: workspace / NULL
    decompress_zstd->>libzstd: zstd_init_dctx(workspace)
    libzstd-->>decompress_zstd: dctx / error
    decompress_zstd->>libzstd: zstd_decompress_dctx(dctx, ibuf, clen, outbuf, outlen)
    libzstd-->>decompress_zstd: success / error
    alt success and temp_out used
        decompress_zstd->>Allocator: memcpy(dbuf, temp_out, dlen)
        decompress_zstd->>Allocator: free(temp_out)
    end
    decompress_zstd->>Allocator: free(workspace)
    decompress_zstd-->>Caller: return dlen or -1
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰
I sniffed the frame beneath the padded bed,
Unwound its bytes, a ribbon blue and red,
A tiny workspace, dctx spun with care,
I hopped home clutching decompressed air —
Crunchy bytes for supper, crunchy bytes to share!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main fix: addressing BTRFS zstd decompression failure (error 70) in U-Boot v2026.01, which is precisely what the patch accomplishes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions bot added size/medium PR with more then 50 and less then 250 lines Needs review Seeking for review Hardware Hardware related like kernel, U-Boot, ... Patches Patches related to kernel, U-Boot, ... 05 Milestone: Second quarter release labels Apr 10, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
patch/u-boot/v2026.01/board_helios64/general-fix-btrfs-zstd-decompression.patch (1)

79-81: Consider size_t truncation on 32-bit platforms.

fcs is unsigned long long but malloc() takes size_t. On 32-bit systems where size_t is 32 bits, values > 4GB would truncate. While unlikely for BTRFS extents in practice (typical max extent size is 128MB), a defensive check could prevent unexpected behavior:

🛡️ Optional: Add size overflow check
 	if (fcs != ZSTD_CONTENTSIZE_ERROR &&
 	    fcs != ZSTD_CONTENTSIZE_UNKNOWN && fcs > dlen) {
+		if (fcs > SIZE_MAX)
+			return -1;
 		tmp = malloc(fcs);
 		if (!tmp)
 			return -1;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@patch/u-boot/v2026.01/board_helios64/general-fix-btrfs-zstd-decompression.patch`
around lines 79 - 81, The allocation uses malloc(tmp = malloc(fcs)) where fcs is
an unsigned long long and malloc takes size_t, so on 32-bit platforms fcs may
truncate; add a defensive size overflow check before calling malloc: verify fcs
<= (size_t)-1 (or SIZE_MAX) and fail fast (return -1) if it exceeds, then cast
fcs to size_t when calling malloc for tmp; reference symbols: fcs, tmp,
malloc().
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@patch/u-boot/v2026.01/board_helios64/general-fix-btrfs-zstd-decompression.patch`:
- Around line 79-81: The allocation uses malloc(tmp = malloc(fcs)) where fcs is
an unsigned long long and malloc takes size_t, so on 32-bit platforms fcs may
truncate; add a defensive size overflow check before calling malloc: verify fcs
<= (size_t)-1 (or SIZE_MAX) and fail fast (return -1) if it exceeds, then cast
fcs to size_t when calling malloc for tmp; reference symbols: fcs, tmp,
malloc().

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 011b3518-0333-4433-a861-0fed3835b12a

📥 Commits

Reviewing files that changed from the base of the PR and between 4cd04e6 and 23601bd.

📒 Files selected for processing (1)
  • patch/u-boot/v2026.01/board_helios64/general-fix-btrfs-zstd-decompression.patch

@iav iav force-pushed the fix/uboot-btrfs-zstd-decompression branch from b4c330a to 2e91b75 Compare April 10, 2026 21:56
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@patch/u-boot/v2026.01/board_helios64/general-fix-btrfs-zstd-decompression.patch`:
- Around line 53-56: The out_len variable is declared as u32 which can truncate
64-bit frame sizes (fcs); change its type to size_t (e.g., replace "u32 out_len
= dlen;" with "size_t out_len = dlen;") and update any subsequent assignments
from fcs (e.g., when doing "out_len = fcs;") to use size_t (cast if necessary:
"out_len = (size_t)fcs;") so the decompression call receives the full 64-bit
capacity; ensure any other uses of out_len match size_t semantics.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 222d1f07-33d1-4207-b8e2-441e709bf5c9

📥 Commits

Reviewing files that changed from the base of the PR and between 23601bd and b4c330a.

📒 Files selected for processing (1)
  • patch/u-boot/v2026.01/board_helios64/general-fix-btrfs-zstd-decompression.patch

U-Boot's generic zstd_decompress() wrapper fails when used by BTRFS
due to two sector-alignment mismatches:

1. Compressed extents are stored padded to sector boundaries (4096),
   but zstd_decompress_dctx() rejects trailing data after the frame.

2. BTRFS compresses in sector-sized blocks, so the zstd frame content
   size may exceed ram_bytes. When the output buffer is sized to
   ram_bytes, zstd_decompress_dctx() returns dstSize_tooSmall (error 70).

Symptoms on zstd-compressed BTRFS partition:

  zstd_decompress: failed to decompress: 70
  BTRFS: An error occurred while reading file /boot/boot.scr

Fix by calling zstd_decompress_dctx() directly with:
- zstd_find_frame_compressed_size() to strip sector padding from input
- ZSTD_getFrameContentSize() to allocate a larger output buffer when
  the frame decompresses beyond the caller's buffer size

Tested on Helios64 (RK3399) booting from BTRFS+zstd SD card.
@iav iav force-pushed the fix/uboot-btrfs-zstd-decompression branch from 2e91b75 to c644580 Compare April 10, 2026 22:06
@iav iav added the Bugfix Pull request is fixing a bug label Apr 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

05 Milestone: Second quarter release Bugfix Pull request is fixing a bug Hardware Hardware related like kernel, U-Boot, ... Needs review Seeking for review Patches Patches related to kernel, U-Boot, ... size/medium PR with more then 50 and less then 250 lines

Development

Successfully merging this pull request may close these issues.

1 participant