# Pre11 Completion and Smoke Report

## Status

Pre11 is complete for its approved mechanical maintenance scope.

- Final implementation commit tested: `040fc69025c5031436205c3acd421be1d95fe86b`
- Final `repo-pre-11` tree: `f0cca92670c76ab13cc5b9353d5aeb993effa6e2`
- Final `repo-pre-11/src` tree: `c4aac4d69cb25a4b049239d0c1f422dcb89465d6`
- Static review: `ACCEPT`, with no P0-P3 findings in the included changes
- FreeBSD KLD build: `PASS`, using `WITH_ZSTDIO=0`
- Targeted plain/LZ4/LZMA QEMU smoke: `PASS`
- Full feature test and `test_all.py`: `NOT RUN`

This report does not claim full feature validation. Pre11 deliberately contains
mechanical naming, responsibility, dead-scaffolding, file-layout, and cleanup
changes that could be reviewed for behavioral equivalence and then covered by a
focused build and smoke run. Behavior changes requiring purpose-built fixtures
remain deferred.

## Snapshot and Planning

Pre11 was created from the completed Pre10 tree in:

```text
0483dcb41c2a980badb829ff4d17361bd1fec930
snapshot: create repo-pre-11 from repo-pre-10
```

At the snapshot boundary, `repo-pre-10/src` and `repo-pre-11/src` were both:

```text
ec684d8dbd7662070234da1fe947aba48a293db8
```

The executable Pre11 plan was added in:

```text
9ad1fa0cf8ba251b574da033bf0c9fad83acaa38
docs: add pre11 execution plan
```

The original plan included several candidates that would have changed visible
filesystem behavior. An independent boundary review narrowed the release in:

```text
c89c228ac1817cfd0337905b39247245eaa67f3b
docs: narrow pre11 execution boundaries
```

The narrowed scope retained only work that could preserve control flow, errno,
on-disk interpretation, vnode and GEOM ownership, iterator order, decompressor
semantics, and cache lifetime. The following candidates were removed from
implementation because honest validation would require targeted feature tests:

- root vnode type rejection;
- zero-length mapping behavior in the UIO reader;
- header-only xattr semantics;
- duplicate or missing `device.N` behavior;
- direct propagation of `vnode_create_vobject()` errors;
- broad map, inode, directory, and name-lookup interface reshaping.

This narrowing allowed Pre11 to remain a comparatively large maintenance
release without mixing low-risk structural alignment with unvalidated semantic
changes.

## Batch A: Type and Vocabulary Alignment

Commit:

```text
b4e38c9fb15df127470d70bc144404f1f3afa2af
pre11: align mount and device helper vocabulary
```

Exact changes:

- renamed `struct erofs_mount.block_bits` to `blkszbits` across its consumers;
- renamed the private `erofs_fill_map_dev()` helper to
  `erofs_fill_from_devinfo()`;
- added the Batch A implementation report.

Linux-maintenance benefit:

- `blkszbits` matches established EROFS vocabulary and makes comparisons with
  Linux mount geometry code more direct;
- `erofs_fill_from_devinfo()` describes the same responsibility as the Linux
  helper without importing Linux block-device objects into FreeBSD.

BSD invariants retained:

- field type, width, units, expressions, overflow handling, and structure
  layout were unchanged;
- the helper remained a private FreeBSD device-map field filler;
- GEOM provider selection, range checks, flat-device behavior, errno, and
  physical I/O remained unchanged;
- no broad or search-driven integer type replacement was performed.

Static checks confirmed 48 intended `block_bits` replacements, zero remaining
old identifiers, four helper-name replacements, zero stale helper references,
and a source diff containing only the approved identifier changes.

## Batch B: Vnode Operation Scaffolding

Commit:

```text
67625d3afebb9142e69b19afd08cdfc1d2adaac5
refactor: clean up vnode operation scaffolding
```

Exact changes:

- removed the unused `EROFS_MOUNT_XATTR_USER` option bit;
- removed the zero-consumer `erofs_node.vnode` member and its only assignment;
- grouped VOP declarations and `erofs_vnodeops` initializers by responsibility;
- added the Batch B implementation report.

Linux-maintenance benefit:

- removes misleading compatibility-shaped state that had no consumer;
- presents vnode operations in a responsibility-oriented order that is easier
  to compare with upstream filesystem responsibilities while retaining the
  native FreeBSD operation table.

BSD invariants retained:

- no VOP function definition or registration changed;
- `erofs_vnodeops` retained 24 unique `field=value` entries;
- `erofs_fifoops` retained 14 unique entries and its original sequence;
- `fifo_specops`, vnode locking, hash insertion, reclaim, pager behavior, and
  `vnode_create_vobject()` errno translation were unchanged.

The removed symbols had no reads, address consumers, initializer dependencies,
offset consumers, or external ABI use.

## Batch C: ACL Parsing Responsibilities

Commit:

```text
a86b5cac4d193d131de8870bca4132fdc881f992
erofs: split ACL parsing responsibilities
```

Exact changes:

- extracted private `erofs_posix_acl_from_xattr()` from `erofs_get_acl()`;
- changed the private mode fallback helper to receive the inode mode directly;
- left xattr acquisition and all FreeBSD VOP/UIO responsibilities in the
  caller;
- added the Batch C implementation report.

Linux-maintenance benefit:

- separates wire-format ACL parsing from filesystem acquisition and VOP glue,
  matching the upstream responsibility split more closely;
- gives maintainers a bounded parser to compare without introducing Linux ACL
  objects, RCU, xattr handlers, or inode lifecycle assumptions.

BSD invariants retained:

- mount-option checks, ACL type validation, default-ACL directory checks,
  namespace selection, both `erofs_getxattr()` stages, UIO setup, and `ENOATTR`
  fallback remain in `erofs_get_acl()`;
- parser validation order, positive errno, tag phases, duplicate ID checks,
  `UINT32_MAX` handling, partial ACL writes, and final
  `acl_posix1e_check()` ordering remain unchanged;
- input storage remains caller-owned stack memory, with no new allocation or
  cleanup path;
- `erofs_xattr_load_body()` and the known header-only xattr behavior were not
  changed.

Static comparison confirmed equivalent branch, error, phase-transition, and
ACL-population behavior.

## Batch D: LZ4 Backend File Responsibility

Commit:

```text
e631e33a1b8c053ebcb2d737af33e363cba48b34
erofs: align LZ4 backend file responsibility
```

Exact changes:

- renamed `src/lz4.c` to `src/decompressor_lz4.c`;
- updated the Makefile source entry;
- updated current architecture documentation references;
- added the Batch D implementation report.

Linux-maintenance benefit:

- makes the filename communicate that the translation unit is the LZ4 codec
  backend beside `decompressor_lzma.c`, `decompressor_deflate.c`, and
  `decompressor_zstd.c`;
- reduces unnecessary file-layout differences when maintainers inspect codec
  implementations across the independent Linux and FreeBSD repositories.

BSD invariants retained:

- Git identified a 100% rename;
- the old and new source blobs are both
  `2cc19f5eb0c283e12ba0a3a4334b8817a1686888`;
- function bodies, request ABI, descriptor registration, input/output bounds,
  partial decode behavior, and error normalization were unchanged;
- historical reports and historical test evidence were not rewritten.

## Batch E: Mount Device Argument Cleanup

Commit:

```text
040fc69025c5031436205c3acd421be1d95fe86b
erofs: consolidate mount device argument cleanup
```

Exact changes:

- consolidated caller-owned copied `device.N` argument release into one
  `out_args` exit in `erofs_mount()`;
- removed two impossible NULL checks immediately following
  `M_WAITOK | M_ZERO` allocations;
- added the Batch E implementation report.

Linux-maintenance benefit:

- makes mount setup ownership and cleanup easier to audit by reducing repeated
  release sites;
- removes defensive branches that contradict the FreeBSD `M_WAITOK` contract,
  keeping BSD-specific allocation behavior explicit instead of emulating a
  nullable Linux allocation path.

BSD invariants retained:

- operation order remains parse, filter, get option, open primary, update I/O
  size, mount setup, free copied arguments, publish mounted-from, and statfs;
- parse failure still returns before caller ownership exists;
- `out_args` releases only copied arguments and never releases `primary`;
- `erofs_mountfs()` still takes ownership of `primary` at entry;
- `erofs_mountfs()`, `erofs_sb_free()`, reverse external-device cleanup, errno,
  and mount error text are unchanged;
- duplicate and missing external-device slot behavior remains deferred.

## Static Review

Independent reviews were performed after the individual batches and again over
the complete source range from `0483dcb` through `040fc690`.

The final review found no P0-P3 issue in the included Pre11 source changes and
accepted all five batches. It confirmed:

- the source changes were confined to the approved Batch A-E paths;
- no deferred behavior fix entered the source;
- old identifiers had no stale consumers and new private helpers had unique
  definitions and expected call counts;
- both VOP vectors retained the same registration mappings;
- ACL acquisition, parsing, fallback, errno, and partial-write behavior were
  preserved;
- the LZ4 backend was a byte-identical rename and the Makefile contained the
  new filename once;
- mount cleanup retained error order and GEOM ownership;
- uncompressed mapping interfaces, directory/name lookup, decompressor request
  ABI, LZMA cache policy, disk structures, and Linux reference sources were not
  changed;
- `git diff --check` passed and the implementation worktree was clean before
  runtime validation.

The final source delta from the snapshot contains 202 insertions and 188
deletions across ten source paths, including the 100% LZ4 file rename. Much of
that textual delta is identifier replacement, declaration/vector reordering,
or moved ACL parser code rather than new behavior.

## First Smoke Attempt: Infrastructure Failure

Evidence directory:

```text
/work/tests-dev/temp/pre11-smoke-final-20260813T122923Z
```

Result: `FAIL`, specifically an infrastructure failure before DUT transfer or
execution.

The guest reached FreeBSD and two SSH identity probes succeeded, but the first
runner opened an SSH ControlMaster using a detached invocation. The open command
returned success and created the control socket, then the master terminated or
lost its connection before the immediate health check. The check failed with:

```text
Control socket connect(.../control.sock): Connection refused
```

The test stopped before source transfer, KLD build, module load, or any EROFS
mount. Therefore this attempt is not a PASS, but it is also not evidence of a
Pre11 build or driver failure. Its source archive SHA was:

```text
5bbffb101364f350fe1a3771b6f018bc10a68785d8c55cc2a186273a074e9182
```

The owned QEMU stopped, port `10043` was released, its overlay was removed, the
base image remained unchanged, and the retained guard VM remained reachable.
The run's cleanup status was nonzero only because closing the already-dead
ControlMaster returned an error.

## Corrected Smoke Retry

Evidence directory:

```text
/work/tests-dev/temp/pre11-smoke-retry-final-20260813T124214Z
```

Result: `PASS`.

The retry used an SSH master process in normal foreground mode but launched it
in the runner shell background, without `ssh -f`. The runner captured the real
master PID, repeatedly required both `kill -0` and `ssh -O check` to succeed,
and refused to transfer sources unless the master remained alive. Long build
work ran inside the guest with a completion sentinel, so one long SSH command
was not treated as the build lifetime.

Tested identities:

```text
commit:            040fc69025c5031436205c3acd421be1d95fe86b
repo-pre-11 tree:  f0cca92670c76ab13cc5b9353d5aeb993effa6e2
src tree:          c4aac4d69cb25a4b049239d0c1f422dcb89465d6
source archive:    179b80365c26295770afab58f1209ae3fffd73182ada0bcf97ea027904f781e9
guest:             FreeBSD 15.0-RELEASE-p8
```

The archive hashes differ between attempts because separately generated gzip
archives are not required to be byte-reproducible. Both identity files name the
same committed repository and source trees; only the passing retry is used as
runtime evidence.

## Build and Module Lifecycle

The committed `repo-pre-11` archive was transferred to the disposable guest,
validated there, extracted, and built with:

```sh
make WITH_ZSTDIO=0
```

Results:

- guest build return code: `0`;
- `erofs.ko` SHA256:
  `77ce41bff5cb7d7c934dd9dba20ba06c20b660ad15ebfc4210f86f6473dffbf7`;
- `kldload /root/pre11-smoke/erofs.ko`: success;
- `kldstat -n erofs`: success, reporting `erofs.ko` loaded;
- all smoke mounts and md providers were released;
- `kldunload erofs`: success;
- the post-unload `kldstat -n erofs` check confirmed the module was absent.

`WITH_ZSTDIO=1` was not built and is not claimed as tested.

## Smoke Cases

Each case used an independently attached vnode-backed md provider, required a
successful EROFS mount to be visible in `mount -p`, computed the complete target
file hash, then unmounted and detached the provider before the next case.

| Case | Target SHA256 | Elapsed | Provider | Result |
| --- | --- | ---: | --- | --- |
| Plain | `056f8f7585667dc695e2edf936deaf84cfee0f88671ba6cd5be22ce890763433` | 0 s | `md70` | PASS |
| LZ4 | `3ff012b76087c4da65ce0b69813a76f47ea367e95782edf8b8cd6cd3ec4d1880` | 5 s | `md71` | PASS |
| LZMA | `ddda39737f0f6093e828a032ec161511fefbb1fa361bc6cbffdbc91e48e4c461` | 2 s | `md72` | PASS |

The LZMA case hashed the complete file. No timeout, mount failure, hash mismatch,
unmount failure, or md detach failure occurred.

## Kernel Messages and Cleanup

The runner captured `dmesg` before loading the module and after all smoke cases.
The delta contained no EROFS error, decompression failure, panic, or assertion;
the suspect-output file is empty.

Cleanup evidence records:

```text
qemu_stopped=1
port_released=1
overlay_removed=1
base_mode_after=444
base_sha_after=67f359621f23a1d745f0889370cbb99a096cee3e99a0b2f3bb18fc7a91bf6fef
guard_unchanged_and_reachable=1
body_rc=0
cleanup_rc=0
```

The test owned only port `10044`, its QEMU PID, its ControlMaster PID, and its
overlay. All were closed or removed. The permanent base image was never booted
directly and retained mode `0444` and its original SHA256. The retained guard VM
PID `26318` kept start time `1102996924`, an identical command line, and a
reachable forwarded SSH port at `9222`.

## Feature-Test Decision

No feature test and no `test_all.py` run was performed.

That decision is appropriate for the included Pre11 scope because:

- Batch A changed only private identifiers;
- Batch B removed proven dead state and reordered designated VOP initializers
  without changing their mappings;
- Batch C mechanically moved parser statements while retaining inputs, outputs,
  writes, error order, and ownership;
- Batch D was a byte-identical file rename;
- Batch E consolidated one caller-owned cleanup action and removed checks that
  cannot be reached after `M_WAITOK` allocation.

Static equivalence review plus the FreeBSD build and plain/LZ4/LZMA smoke is
sufficient evidence for these changes. It is not sufficient for the deferred
behavior changes below, and this report does not claim otherwise.

## Deferred Behavior and Required Targeted Tests

### Non-directory root

The current root path does not add a new rejection for a root NID that resolves
to a non-directory vnode. A future fix requires a purpose-built image and checks
for mount errno, vnode release, mount error text, and complete cleanup.

### Zero-length internal run

The uncompressed UIO reader's existing `run_len == 0` behavior was not changed.
A future fix requires a corrupt mapping fixture that reaches a zero-length run
while the request remains inside the file, with exact comparison to the
contiguous-buffer path and expected `EINTEGRITY`.

### Header-only xattr

The existing behavior for an ibody containing only the xattr header was not
changed. Targeted coverage must distinguish zero and nonzero shared counts,
name filters, get/list results, ACL mode fallback, and corruption results.

### Duplicate or missing device slots

`device.N` slot parsing behavior and diagnostics were not changed. Future tests
must cover duplicate, out-of-order, missing, excessive, and malformed slots,
flat and non-flat device tables, exact errno and mount error text, and GEOM
cleanup.

### `insmntque()` race closure

The earlier ownership fix remains accepted by static lifecycle review but is
not runtime-closed by ordinary smoke. A dedicated mount/unmount or forced
unmount race must stress vnode creation and inspect panic, use-after-free, lock,
reference, and cleanup behavior.

### Explicit mapped extent fixtures

Positive payload fixtures are still unavailable for explicit mapped extent
records of 4, 8, 16, and 32 bytes. No restructuring or coverage claim should be
made until validated mappings and file hashes exist.

### `vnode_create_vobject()` errno

Pre11 intentionally retains the current conversion of a nonzero
`vnode_create_vobject()` result to `ENOMEM`. Direct propagation is a visible
behavior change and requires controlled pager failure or fault injection that
verifies the caller-visible errno and vnode state.

## Final Conclusion

Pre11 completed all five narrowed maintenance batches, passed independent
static review, built successfully as a FreeBSD KLD, and passed the required
plain, LZ4, and full-file LZMA smoke. The first smoke attempt was correctly
classified as an infrastructure failure before DUT transfer or build; the
corrected retry provides the runtime evidence.

Pre11 improves cross-repository maintainability without replacing native
FreeBSD vnode, GEOM, UIO, pager, ACL, or synchronous decompression contracts.
Full feature validation remains outside this release and must not be inferred
from the targeted smoke result.
