# repo22 Metadata and VFS Manual Test Report

Started: 2026-08-08 23:37 UTC
Completed: 2026-08-09 UTC
Baseline: `c208bf1f4b8d7a85777f7fe45e8c6e8d3a9f2d1a`
Guest: FreeBSD 15.0-RELEASE-p8 amd64, QEMU TCG
FreeBSD source reference: `/work/dev-freebsd-releng`, releng/15.0
Linux source reference: `/work/dev-src-linux/fs/erofs`
Host production tool: erofs-utils 1.8.6

## Result

**PASS** for the implemented metadata/VFS changes and every runnable required
FreeBSD 15 regression.

Two limitations are recorded rather than misreported as PASS:

1. The Linux host had no loop provider/EROFS kernel mount path, so the
   deterministic nonzero-padding fixture passed Linux erofs-utils 1.8.6
   `fsck.erofs` and `dump.erofs`, while the Linux kernel mount is
   **ENVIRONMENT-UNAVAILABLE**.
2. A real TC010 48-bit `statfs` mount needs a provider as large as the declared
   multi-terabyte image. The corrected test records this as a provider/tool
   requirement; no small-media mount is called a positive PASS.

An initial NFS stress run completed all data and metadata assertions but failed
cleanup because `service nfsd onerestart` inherited the deliberately open NFS
client descriptor. The procedure was corrected to run the service command with
`3<&-`; the complete stress/background/cleanup sequence then passed. The first
cleanup failure is retained here as evidence for the TC132 ordering fix.

## Baseline and Scope Audit

- `HEAD` and `FETCH_HEAD` both resolved to the required baseline.
- `find /work -name AGENTS.md -type f -print` returned no paths.
- Only `repo-community/repo22` was modified.
- Existing untracked `build/` objects and historical manual-test artifacts were
  preserved and excluded from staging.
- No CI implementation, binary fixture, overlay, image, or VM artifact is part
  of the intended commit.

## Build and ABI Validation

Commands:

```sh
EROFS_ZSTDIO=0 ./build.sh
EROFS_ZSTDIO=1 ./build.sh
nm -u module.ko | awk '$NF == "bcmp" { n++ } END { print n + 0 }'
git diff --check -- repo-community/repo22
```

Results:

| Build | SHA256 | `bcmp` count | Result |
|---|---|---:|---|
| ZSTDIO disabled | `031038ef195497dc6a1d840d55b293292e051fb888c679c89c8cfbd19b56d525` | 0 | PASS |
| ZSTDIO enabled | `16166b9ffc96a532bda9925513e10df11f4cf8a06b3c15afbc09b1a4e4b9d2df` | 0 | PASS |

Both modules loaded and unloaded on FreeBSD 15. The final unload used the
observed KLD ID so arbitrary copied filenames did not affect cleanup.

FreeBSD 15 source inspection confirmed the exact current ABI:

```text
vnode_pager_local_getpages(struct vop_getpages_args *)
vnode_pager_local_getpages_async(struct vop_getpages_async_args *)
```

The local ext2 vnode vector registers both functions directly. EROFS now does
the same.

## Deterministic Fixture Evidence

The checked-in `prepare-fixtures.sh` was syntax-checked and rerun into separate
untracked `repro3-*` directories. It reproduced the expected byte-identical
inline, special, pager, NFS, and nlink images and generated the current namei
variants.

Key structural evidence:

```text
inline_nid=39 inode_off=1248 inode_blockoff=1248 xattr_icount=0->695
inline_data_blockoff=4068 inline_size=31
wide_nid=42 block=6 dirents=80 last_nameoff=4043 padding_patch=4084:4092
nlink1_nid=43 i_format_bit4=1 i_nb=0x1234
special compact rdev raw_u=0x543abc21
special extended rdev raw_u=0x543abc21
```

Generated image hashes:

| Image | SHA256 |
|---|---|
| `inline.erofs` | `0435b3ea748a88cccbdc6390dec4285a3706bec3dc09de58aa1808544ebc63d0` |
| `inline-cross-block.erofs` | `63ebb7632687b564beb4c9dd8036eb4ac63c4495f63061ad7eec1c134c656932` |
| `special-compact.erofs` | `fd78256dd83d9d6d957e5f843c7a8e8a175a4b3243d528bebd299b0226853237` |
| `special-extended.erofs` | `e73e9b84d9ceb8c2b07e9c2732733b0fd607736c68c09522a2402fbeef6ba8d5` |
| `namei-base.erofs` | `d1730ff23836797c6c09e1b39b1cf23efc16e27f85ab07fdcab577bb82871659` |
| `namei-padding-nonzero.erofs` | `9a94e9af2cab264b9c11975a20d78e615d6fe1e6f86267173cb5ac86aecb2b17` |
| `namei-corrupt-short.erofs` | `fb89f74795a5569ed3a85d63836dd75a06e1f17823d0508048c37da710ea6e75` |
| `namei-corrupt-nameoff.erofs` | `b0b70ee615f163430f04edb91ab76c27ee8cc9a75b8ebb2008834f5b550e3933` |
| `namei-corrupt-name.erofs` | `6a980ad3e241603eda2ef71a470c82975291c614366a401e4e89c17c9adf9b91` |
| `pager-plain.erofs` | `36596edea5bfbaa1157f6c142095a7ee9949b5df1b1a35620c0d22ec853f3c09` |
| `pager-lz4.erofs` | `b06daee6b02a6ebe967655be760b496c8a9d922cf47acb5c840e82a48a34c51d` |
| `nfs-a.erofs` | `6d86dcf620b007d069895e3e94a21a74dadcf35e84c92dc4f7c21a2ec23bd901` |
| `nfs-b.erofs` | `18913fd319daca20b3e4d30a89c05c416b4d3ca396394509e5112d548674f4c2` |
| `nlink.erofs` | `798eb81b3ba7270ee653b00adeba47a6e03c982adef1982bf5b4e0935669ae83` |
| `nlink1-patched.erofs` | `19fd85f32ed89117d8e02bc19ca09655dd3bae9152cf693a399eda2135233042` |

The padding patch was followed by a rigorous CRC32C recomputation over the
superblock block. Linux erofs-utils produced the exact payload:

```text
wide entry 079
```

The Linux kernel comparison command failed before mount with “failed to setup
loop device”; it is therefore not labeled PASS.

## TC147: FLAT_INLINE Bounds

Commands included fresh md attach/mount for the base and cross-block images,
then cold `cat`/`stat` access.

Observed on the final module:

```text
positive hash=0347f272ba395aff6df5fd824a7c552fa26d7f017283f0544136385abef31b01 stat=31 8
corrupt stat: Integrity check failed
corrupt stat: Integrity check failed
corrupt cat:  Integrity check failed
dmesg_before=122 dmesg_after=122 mounts=0 mds= module_rc=1
```

The positive file read exactly. The checksum-valid corrupt inode failed with
`EINTEGRITY` before its 31-byte inline range could cross the metadata block.
The mapping path also uses checked additions, and primary/metabox declared
bounds are validated at inode decode.

Self-review first rejected the old `i_xattr_icount=1020` mutation because it
merely moved the inline data into the next block without crossing that block.
The corrected value `695` places the tail at block offset 4068. Its first
genuine rerun exposed a stale constructing vnode: the second `stat` returned
`EBADF`. The failure path now calls `vgone()` before `vput()`, and the complete
final rerun above returned `EINTEGRITY` for every repeated access.

## TC022/TC056: Special `st_rdev`

The source fixture used real Linux char/block nodes with major `2748`, minor
`344865`, plus a FIFO. `stat_special.c` checked `st_rdev` directly because
FreeBSD `stat -f %Lr` truncates before `minor()` for large values.

Compact and extended results were identical:

```text
PASS char  rdev=0xa430005bc21 major=2748 minor=344865
PASS block rdev=0xa430005bc21 major=2748 minor=344865
PASS fifo  rdev=0xffffffffffffffff
```

This proves Linux `new_decode_dev(0x543abc21)` followed by FreeBSD `makedev()`;
a little-endian integer cast would not produce this FreeBSD `dev_t`.

## TC055: Real Compressed Allocation

For every row, full FreeBSD kernel reads matched the expected SHA256 and
`st_blocks * 512` matched the inode's real on-disk compressed size:

| Shape | File | Size | Allocated bytes | SHA256/result |
|---|---|---:|---:|---|
| LZ4 full | `shape.dat` | 1048576 | 8192 | `370eb0a8...` PASS |
| LZ4 compact | `shape.dat` | 1048576 | 8192 | `370eb0a8...` PASS |
| LZ4 fragment | `fragment.dat` | 1048699 | 0 | `a3a83e5c...` PASS |
| LZ4 ztailpacking | `inline.dat` | 65536 | 0 | `e2aa4a0a...` PASS |
| MicroLZMA partial A | `a.dat` | 1048576 | 4096 | `370eb0a8...` PASS |
| MicroLZMA partial B | `b.dat` | 700000 | 4096 | `5a840803...` PASS |
| DEFLATE compact/partial | `a.dat` | 1048576 | 36864 | `370eb0a8...` PASS |
| DEFLATE full/partial | `b.dat` | 1050624 | 8192 | `61b17076...` PASS |
| ZSTD partial A | `a.dat` | 1048576 | 4096 | `370eb0a8...` PASS |
| ZSTD partial B | `b.dat` | 700000 | 4096 | `5a840803...` PASS |

Uncompressed regressions:

```text
inline size=31 st_blocks=8
plain size=21211 st_blocks=48
chunk size=90017 st_blocks=176
```

Representative compression image hashes were:

```text
LZ4 full d784f8dc...   LZ4 compact 37942ef1...
LZ4 fragment a4e5d40e...   LZ4 ztail 035069eb...
MicroLZMA 2f4bd89d...   DEFLATE 8bc720a1...   ZSTD 7f5ee4f8...
```

## TC141/TC148: Directory Compatibility and Strictness

FreeBSD results:

- Cold `/alpha/bravo/charlie/payload.txt`: PASS without parent warming.
- Repeated cold lookup and post-negative-cache existing lookup: PASS.
- Patched nonzero tail bytes: accepted.
- `wide` enumeration: 320 files plus `.` and `..`; 322 dirents through a
  128-byte buffer and restart cookies.
- Short block, non-monotonic `nameoff`, and `/` in an on-disk name: lookup and
  readdir both returned `EINTEGRITY`.
- Repeated corrupted lookup remained `EINTEGRITY`, not cached `ENOENT`.

The duplicate validators were removed; lookup and readdir use the shared
helper in `dir.c`.

## TC132/TC133: NFS Generation and Stress

Direct handle validation after the final superblock-hash implementation:

```text
nfs-a: fsid=00000034:000000e0 nid=0x2e gen=849213208
nfs-b: fsid=00000034:000000e0 nid=0x2e gen=4011239099
```

- `nfs-a` remount on the same md unit produced byte-identical complete handles.
- `va_gen` equaled handle generation.
- Replacing `nfs-a` with `nfs-b` on the same md unit made the old handle return
  `ESTALE` through both `fhstat` and `fhopen`.
- Bad length/pad returned `EINVAL`; bad generation/NID returned `ESTALE`.
- Handle generation mutation used `gen_xor 1`, not a hard-coded value.

NFSv3/TCP READDIRPLUS stress:

- Four clients mounted; requested 512/1024/4096 readdir sizes were clamped by
  FreeBSD to 8192, while default was 65536.
- All four 12,050-name sorted listings had SHA256
  `4fbf1b6103e8884223629c3fd03f4ec36061e65cd2a1becfc96670daa96f9d71`.
- Twelve traversal workers and eight cat/stat controller jobs were waited by
  PID; every exit status was zero.
- READDIRPLUS client/server count was 1304 in the recorded full run.
- RPC timeouts, invalid replies, retries, and server write RPCs were zero.
- The 21,757,952-byte throughput file hash was
  `0841effed82d1adf394b6834ce30d4d9eb5fc9427527e854ec1cb6bb4b86c119`.
- nfsd restart with an open descriptor passed after the service command closed
  inherited fd 3.
- Corrected full rerun ended `NFS-STRESS-CLEAN-PASS`.

## TC149: Real Pager Faults

`tests/mmap_fault.c` was compiled natively on FreeBSD 15 and run against both
plain and LZ4 images.

Both runs printed:

```text
PASS size=21211 pages=6 fnv1a64=a1890a1c216724be random-faults=6 \
eof-zero=PASS sigbus=PASS private-cow=PASS
```

The helper verified deterministic random faults after `MADV_DONTNEED`, full
mapping equality with `pread`, partial EOF-page zeroes, child `SIGBUS` on the
next full page, `MAP_SHARED` write denial, FreeBSD private COW semantics, and
`O_RDWR -> EROFS`.

dmesg added exactly the two expected child exits on signal 10 (`SIGBUS`). No
parent crash, VM assertion, trap, panic, or dirty writeback appeared.

## Additional Regressions

| Area | Evidence | Result |
|---|---|---|
| Inline/system xattr | trusted, security, long-prefix, user values exact | PASS |
| Shared xattr | shared and per-inode values exact | PASS |
| Metabox xattr | `dirA/nested.txt` and `hello.txt` enumerated/read | PASS |
| Fragment-backed metabox | positive file/xattrs exact; self-loop and range images rejected | PASS |
| Single-device chunk | `plain.bin`, `deep/payload.bin` hashes exact | PASS |
| Chunk multidevice | block-map and indexed files hashes exact | PASS |
| External compressed multidevice | complete 1 MiB LZ4 SHA256 `370eb0a8...` | PASS |
| Compact nlink rules | explicit nlink 1/2 plus flagged nlink-one image | PASS |
| ZSTD enabled final load/read | final module and `zstd-partial-ref` read | PASS |

Supporting fixture hashes:

```text
system-inline-four 6f521b62...
shared xattr       208b61ca...
metabox xattr      ef8d619c...
chunk single       7aa8db22...
chunk multidev     73343b70... + a36c2b9b...
external LZ4       56256124... + 0d71102c...
NFS stress image   6928f05b...
```

The final-module fragment-backed metabox rerun used the previously qualified,
checksum-valid image with SHA256
`8a9a62bd203994711b8272192915d811e6c3de23e07ad9607dd63e66cc109bcd`.
`/tree/d00/file000.txt` produced SHA256
`4536c1d7121f48829475f29179f54baa57154b4ef817cf0776f81782585d29ad`;
the shared and per-file xattrs were `shared-value` and `value-000`. The
self-loop and out-of-range images returned `Integrity check failed`. dmesg
remained at 122 lines, and the post-run audit showed zero mounts, md providers,
and loaded EROFS modules.

## Final-Binary Closure Rerun

After the `vgone()` error-path fix, the final ZSTDIO module SHA256
`16166b9ffc96a532bda9925513e10df11f4cf8a06b3c15afbc09b1a4e4b9d2df`
was used for one continuous closure matrix:

- LZ4 full/compact/fragment/ztailpacking, MicroLZMA partial, DEFLATE
  compact/full partial, and ZSTD partial hashes and `st_blocks` all matched.
- Inline trusted/security/long-prefix/user xattrs matched exact values.
- Metabox shared xattrs returned `answer=forty-two`; the fragment carrier file
  and `repo22.item-000=value-000` matched.
- Single-device chunk, external chunk provider, and external compressed LZ4
  hashes matched; compressed `st_blocks=16`.
- The final module preserved the same NFS handle across same-md remount, made
  the old handle `ESTALE` after image replacement, synchronized `va_gen`, and
  completed a real local NFSv3/TCP client read with matching SHA256.

The first closure script stopped after the system-xattr row because it queried
an obsolete metabox attribute name. Its trap left zero mounts, md providers,
modules, and services. The corrected `answer`/`metaboxshared` queries and all
remaining rows passed. The complete final audit was:

```text
dmesg_before=122 dmesg_after=122
erofs_mounts=0 nfs_mounts=0 mds= module_rc=1 services=0/0/0
FINAL-REGRESSION-REST-PASS
```

## Final Environment and Cleanup

Final guest audit:

```text
mounts=0
mds=
modules=0
services=0
final_dmesg_before=122 final_dmesg_after=122
```

The final dmesg tail contained only the historical pre-test duplicate-module
diagnostic and the two intentional pager-child SIGBUS exits. The final
compression, xattr, chunk, multidevice, generation, and NFS closure pass added
no dmesg lines.

Guest helper hashes:

| Helper | SHA256 |
|---|---|
| `mmap_fault` | `b19c9c28a7abdfebe4243e3f4876b711eebc1c02b9856c15cb8bf15ae2da3a65` |
| `nfs_fh_tool` | `0dcad233cc8768ca84569c8d030939b526d50c64190fc54d5f0de013191f11d6` |
| `stat_special` | `b5c0eabd06541268e799df4f72fa7f37a460617bd8960f918b27710483db3841` |
| `readdir_probe` | `44747ccd8d9a285a37d8049b7d61d00946db88482c08a3570e59b5a2fa1b4ea2` |

No password material was printed into this report or written into tracked
files.
