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