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:
- Read the configuration record and its payload.
- Return the read error immediately if either read fails.
- Select the descriptor and invoke its configuration callback.
- Release the payload with
erofs_brelse(). - 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.ccache 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.