# repo22 Final Review WIP Manual Test Report

Started: 2026-08-09 01:24 UTC

Completed: 2026-08-09 03:33 UTC

Parent commit: `9a3604fba32cfe2ff263d954d80fe588f99db88f`

Guest: FreeBSD 15.0-RELEASE-p8 amd64, QEMU TCG

Linux reference: `/work/dev-src-linux/fs/erofs`

FreeBSD reference: `/work/dev-freebsd-releng`

## Scope

This run reviews and validates four final correctness findings:

- Linux-compatible 48-bit superblock union selection;
- rejection of extended inode sizes above FreeBSD `OFF_MAX`;
- bounded reads from multi-GiB explicit extent holes;
- 64-bit directory block-search indexes.

No CI work, binary fixture, guest overlay, build object, or raw VM artifact is
part of the intended commit.

## Result

| Test | Result | Evidence |
|---|---|---|
| TC150 48-bit fallback root | PASS | Real FreeBSD mount, root read, inode and `df` |
| TC151 size above `OFF_MAX` | PASS | Six direct failures, rc 1, syscall errno 97 |
| TC152 bounded extent hole | PASS | Two pread/mmap probes, stable active allocation |
| TC153 large directory index | SHELVED | Host fixture reproducible; kernel run incomplete |

TC153 is deliberately not marked PASS. See
`issues/TC153-large-directory-block-index-validation.md`.

## Source Review

Linux `super.c` initializes `blocks_lo` and uses `rb.blocks_hi` only inside
`48BIT && rootnid_8b`. repo22 now decodes blocks and root NID together under
that same selector.

FreeBSD exposes signed `off_t` pager and vnode interfaces bounded by
`OFF_MAX`; repo22 rejects a larger decoded inode before vnode/pager setup.

The extent-hole path now zeroes only the current requested span in
`z_erofs_do_read()` instead of allocating the complete logical extent.

The directory block search now uses 64-bit bounds and a checked block-offset
multiplication. The within-block search remains 32-bit, matching the validated
block-sized domain.

## Build Results

Commands:

```sh
EROFS_ZSTDIO=0 ./build.sh
EROFS_ZSTDIO=1 ./build.sh
nm -u build/erofs.ko
git diff --check -- repo-community/repo22
```

| Configuration | Module SHA256 | Result |
|---|---|---|
| `EROFS_ZSTDIO=0` | `65bc19d53a2a7f0525bfacadb441dab37ee318f5f5b4c62b5ecd1b5090fe46d2` | PASS |
| `EROFS_ZSTDIO=1` | `d46ca4dfc858deaf4840fad8b8589b16d4b71b34afa96dc7acf1255890c5cef7` | PASS |

Both freestanding builds completed. Neither module had an unresolved `bcmp`
reference. The ZSTDIO-enabled module was transferred to the guest, loaded as
KLD ID 7, and unloaded after testing.

## Fixture Evidence

| Fixture | SHA256 |
|---|---|
| `fallback-48bit-root2.erofs` | `bed3be4dfb8499d4e794b03eddd5b9cda95bc8e5571ae7d765c4852ece0d95a3` |
| `extended-size-bit63.erofs` | `e4a0f550168f1a2911603863d0074d474e61adc787c14c0278c83a060643ee38` |
| `extent-hole-5g.erofs` | `50014a24493918247e36511ad34a2fe8ab47ae09ea46d7fd62a1bed445a6c65f` |
| `large-dir-intmax.erofs` | `0f90d3d57adbbbd946e41b225c1f6c464915c6abb0b13478ec9b2a318def1f72` |

The TC153 hash is host generator evidence only.

## TC150

The fixture encoded `rootnid_2b=36`, `rootnid_8b=0`, `blocks_lo=1`, and the
48-bit incompat bit. The 4 KiB image mounted on `/dev/md0` and produced:

```text
content=48-bit fallback root
sha256=d361f537492113ca93cfbf91c06ebc06e2b8b695d8b6e63ad3666013eaa029f0
root inode=36
df total=4 one-KiB blocks
```

This proves the union was not shifted into a high block count. It does not
replace the large-provider TC010 test.

## TC151

The fixture used extended inode NID 40 at byte 1280 with
`i_size=0x8000000000000000`. The image mounted, but every access to `big.dat`
failed before open or pager setup:

| Access | Attempts | Direct rc | `truss` result |
|---|---:|---:|---|
| `stat` | 2 | 1, 1 | `fstatat ... ERR#97` |
| `cat` | 2 | 1, 1 | `openat ... ERR#97` |
| `mmap_fault` | 2 | 1, 1 | `openat ... ERR#97` |

All six errors were `Integrity check failed`. No file descriptor reached the
read or mmap phase.

## TC152

The mounted file size was `5368713216` bytes. The native helper probed offset
`3221225472` with a one-byte pread and one-page private mmap:

```text
attempt 1: PASS, real 0.03 s
attempt 2: PASS, real 0.02 s
```

The `erofs` malloc row was `3` active allocations and `768` active bytes both
before and after. The cumulative allocation counter moved from 89 to 95, as
expected for temporary request buffers; active memory did not scale with the
5 GiB hole.

## TC153

The first 4096-byte fixture was Layout 2 and failed before the target namei
path. A second 65536-byte base with 400 entries was still Layout 2. The tracked
generator now converts the directory to Layout 0 by moving its complete data
to appended blocks and produced a 90112-byte patched image with raw block 16,
22 image blocks, and the hash listed above.

The corrected fixture was not executed in the FreeBSD kernel during this run.
TC153 remains SHELVED, and the complete attempt history and acceptance criteria
are in its issue document.

## Cleanup

The qualified rerun used dmesg line count 123 before and after. It ended with:

```text
EROFS mounts: 0
md providers: none
EROFS modules: 0
```

An earlier unqualified probe encountered a preloaded differently named EROFS
KLD and a `truss` exit-status ambiguity. It was discarded. The qualified run
first unloaded that KLD, loaded the exact module hash above, captured direct
command exit codes separately from syscall traces, and then cleaned up.

## Deferred Issues

- `issues/TC153-large-directory-block-index-validation.md`
- `issues/TC010-48bit-statfs-large-provider.md`
- `issues/extent-metadata-fixture-unavailable.md`

The raw guest transcripts remain untracked under `/work/build`. This report,
the deterministic generator, and source/test documentation are the tracked
evidence.
