# 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: ```text 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: ```c 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: ```c 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: ```text 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: ```text 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: ```sh 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.