Files
erofs-freebsd-out-tree/docs/pre10-batch-c.md
T
2026-08-18 09:20:44 +02:00

8.4 KiB

Pre10 Batch C: BSD Decompressor Request Dispatch

Status: STATIC PASS; RUNTIME NOT RUN

This batch aligns the BSD decompressor boundary with the Linux source shape where the responsibility is equivalent. It keeps the FreeBSD implementation synchronous and contiguous-buffer based. No page, folio, bio, workqueue, XArray, shrinker, or asynchronous decompression abstraction was added.

Baseline and Scope

The implementation starts from Pre10 commit d3c1cb90 and preserves Batch A, Batch B, and the documented insmntque() lifecycle issue. The source changes are limited to:

repo-pre-10/src/internal.h
repo-pre-10/src/decompressor.c
repo-pre-10/src/lz4.c
repo-pre-10/src/decompressor_lzma.c
repo-pre-10/src/decompressor_deflate.c
repo-pre-10/src/decompressor_zstd.c

zdata.c was inspected but did not need a mechanical edit: its existing call to z_erofs_decompress() remains the sole dispatcher call, and its buffer release and decoded LZMA extent-cache publication remain unchanged.

Request Contract

The new request is stack-owned by z_erofs_decompress() and is valid only for the synchronous callback invocation:

struct z_erofs_decompress_req {
        struct erofs_mount *em;
        const struct erofs_map_blocks *map;
        const void *in;
        size_t inputsize;
        void *out;
        size_t outputsize;
        bool partial_decoding;
};

Field mapping is direct:

Request field Previous source Ownership
em em / codec configuration arguments Borrowed mount, never retained
map map and its algorithm/offset fields Borrowed mapping, never retained
in src after padding removal Borrowed compressed buffer
inputsize srclen after padding removal Value copy
out dst Borrowed decoded buffer
outputsize dstlen Value copy
partial_decoding partial Value copy

The request does not own either data buffer. zdata.c continues to release the compressed buffer with erofs_brelse(), and continues to free or publish the decoded allocation after the callback returns.

The descriptor callback is intentionally synchronous:

struct z_erofs_decompressor {
        const char *name;
        int (*config)(struct erofs_mount *,
            const struct erofs_super_block *, const void *, size_t);
        int (*decompress)(const struct z_erofs_decompress_req *);
};

The configuration callback receives the superblock argument because the LZ4 legacy configuration path uses it. The other codec configuration callbacks retain their previous inputs and explicitly ignore that additional argument.

Descriptor Coverage

decompressor.c contains one static array indexed by the on-disk algorithm number. The entries are:

0  LZ4         config + z_erofs_lz4_decompress
1  LZMA        config + z_erofs_lzma_decompress
2  DEFLATE     config + z_erofs_deflate_decompress
3  ZSTD        config + z_erofs_zstd_decompress
4  SHIFTED     plain transform callback
5  INTERLACED  plain transform callback

The first four entries cover every real EROFS compression algorithm currently defined by erofs_fs.h. The two runtime-only entries cover the existing plain mapping forms. No duplicate backend entry point or compatibility wrapper is present.

Configuration parsing preserves the old ordering:

  1. Read the configuration record and its payload.
  2. Return the read error immediately if either read fails.
  3. Select the descriptor and invoke its configuration callback.
  4. Release the payload with erofs_brelse().
  5. Return the callback's original error unchanged.

This retains the existing I/O-error priority. Unknown on-disk algorithm bits are rejected before configuration reads by the existing Z_EROFS_ALL_COMPR_ALGS mask check. A supported algorithm whose configuration is unavailable returns EOPNOTSUPP from its existing callback. In particular, the no-ZSTDIO build still emits the existing mount error and returns EOPNOTSUPP.

Decode and Error Semantics

The old and new paths have the following equivalent behavior:

Condition Result before Batch C Result after Batch C
Shifted/interlaced output larger than input EINTEGRITY EINTEGRITY
Shifted/interlaced transform success 0 0
Invalid algorithm format EOPNOTSUPP EOPNOTSUPP
Required zero-padding absent EINTEGRITY EINTEGRITY
LZMA dictionary not configured EINTEGRITY EINTEGRITY
Backend success 0 0
Any backend failure EIO EIO

Padding removal remains in the dispatcher and is performed before the backend request is updated. The LZ4 zero-padding exception remains unchanged. The LZMA dictionary check remains before callback dispatch. Backend-specific checks are unchanged apart from reading their previous parameters from the request or mount configuration:

  • LZ4 preserves literal/match bounds, overlap copying, partial completion, and trailing-zero validation.
  • MicroLZMA preserves input/output size limits, dictionary selection, decoder shutdown, full-stream consumption, and partial output acceptance.
  • DEFLATE preserves window validation, inflateEnd(), no-progress detection, output completion, and full-stream input consumption.
  • ZSTD preserves window selection, decoder destruction on every initialized path, no-progress detection, output completion, and full-stream consumption.

Consumer and Symbol Proof

Before the change, repository searches found exactly one in-tree consumer of each old backend symbol: the switch in decompressor.c. The only consumer of z_erofs_decompress() is zdata.c. After the change:

old lz4_decompress       0 definitions/references
old lzma_decompress      0 definitions/references
old deflate_decompress   0 definitions/references
old zstd_decompress      0 definitions/references

new LZ4 backend           one definition, one descriptor reference, one prototype
new LZMA backend          one definition, one descriptor reference, one prototype
new DEFLATE backend       one definition, one descriptor reference, one prototype
new ZSTD backend          two definitions for #ifdef/#else, one descriptor reference,
                          one prototype

The two ZSTD definitions are mutually exclusive build branches, not duplicate runtime implementations. No old-name wrapper was retained because no real consumer remains.

Cache and Ownership Review

The Pre9 decoded LZMA cache policy is untouched. The request callback returns before the cache code runs, so the following remain owned by zdata.c:

  • compressed-buffer release;
  • decoded allocation cleanup on failure;
  • decoded extent publication;
  • duplicate-cache replacement and old-entry freeing;
  • one-entry-per-mount bound and cache lock lifecycle.

No callback stores the request pointer or either borrowed buffer after return.

Static Checks

The following checks were run before this report was written:

git diff --check
git diff --name-only | sort
grep -RInE '(^|[^_])(lz4_decompress|lzma_decompress|deflate_decompress|zstd_decompress)\\(' repo-pre-10/src
grep -RInE 'z_erofs_(lz4|lzma|deflate|zstd)_decompress' repo-pre-10/src
grep -RInE '\\b(page|folio|bio|workqueue|xarray|shrinker)\\b' \\
  repo-pre-10/src/internal.h repo-pre-10/src/decompressor.c \\
  repo-pre-10/src/lz4.c repo-pre-10/src/decompressor_lzma.c \\
  repo-pre-10/src/decompressor_deflate.c repo-pre-10/src/decompressor_zstd.c \\
  repo-pre-10/src/zdata.c

Results:

  • git diff --check: PASS.
  • Changed paths: only the six allowed source files: PASS.
  • Old backend symbol search: no matches: PASS.
  • New backend definitions and descriptor references: PASS.
  • Forbidden Linux memory-model concepts in the changed codec boundary: no matches: PASS.
  • Descriptor entries: six unique designated entries covering algorithms 0-5: PASS.
  • zdata.c cache and ownership diff: unchanged: PASS.

The host is not a FreeBSD build environment, so this batch does not claim a KLD build. Per the Pre10 instruction, QEMU smoke testing and the full feature matrix were not run in this batch.

Concerns and Deferred Validation

The primary remaining validation is a FreeBSD guest KLD build followed by the planned Pre10 smoke run. That runtime work is intentionally separate from this static implementation batch. Full feature tests remain outside Pre10.

The existing erofs_vget() insmntque() failure-path P1 remains documented in repo-pre-10/issues/erofs-vget-insmntque-failure-use-after-release.md and was not changed here.