# repo22 Cold Nested Namei Regression Report - Date: 2026-08-08 UTC - Baseline: `29a215dd4519579f6313b3df67d524a6b4bdf3ca` - Guest: FreeBSD 15.0-RELEASE-p8 amd64, QEMU TCG - Final module SHA-256: `50a19ea96c7414f73d92049671e66a9eacc9ff5abe27950a46d16c8e6c763baf` - Result: PASS ## Root Cause FreeBSD pathname lookup passes a component as `cn_nameptr` plus `cn_namelen`. An intermediate component is followed by `/` in the pathname buffer and is not NUL-terminated at `cn_namelen`. The imported Linux EROFS comparison assumed Linux dentry-name termination, ignored the supplied length, and tested `qn_name[i] == '\0'` after matching the on-disk name. Therefore a final component worked, while the same name used as an intermediate component compared greater than the on-disk entry and returned `ENOENT`. Looking up the parent as a final component first populated the FreeBSD namecache and hid the bug on the next nested lookup. The old error path also inserted a negative cache entry for every lookup error, including integrity and I/O errors, which could mask later corruption as `ENOENT`. ## Implementation - `src/namei.c` - Compares pathname components by explicit length without reading beyond `cn_namelen`. - Uses unsigned-byte ordering compatible with EROFS directory sorting. - Validates the minimum block size before reading the first dirent. - Validates the dirent-array boundary, strictly increasing name offsets, name-slot bounds, name length, and zero-only NUL padding. - Inserts negative namecache entries only for real `ENOENT` misses. - `src/dir.c` - Applies the same directory-block and name-padding validation to `readdir`. - Determines the actual last-name length before enforcing `EROFS_NAME_LEN`, so valid full-block zero padding is accepted. - Keeps on-disk offsets unchanged and appends a synthetic `.` at `i_size` for `dot_omitted`, matching Linux EROFS. - Aligns restart positions relative to each directory block and preserves the `i_size` cookie needed to resume the synthetic dot entry. - Initializes returned cookie-array outputs before allocation. ## Deterministic Fixture `prepare-fixtures.sh` creates the same image twice and requires `cmp` success. The base image contains a cold multi-level path and a 320-file, multi-block directory. Fixed timestamp, ownership, UUID, worker count, and uncompressed layout are used. Final image hashes: ```text 2e9fd75159011ced31646a38f942fa0822d5b3e8e19b7df55b844575fba991ea corrupt-nameoff.erofs 63d12232f9ea0dc7f7ff477d20b95a8412d191fc10bf4b67dacc47e050c379fb corrupt-padding.erofs 77f8a56f969e173d291ccbd957821f1ba284d3e54d875dbcdb9bc398024dfa5a corrupt-short-block.erofs 11064ffbb814ff41027aebc8f36e56d2f24affb7b50836948b3ffbf6d79dee0e dot-omitted.erofs 7a2c244ec10e9a43531b0e8b92e26b11f52d67bab00528b8ba2f584e20e57420 nested-repeat.erofs 7a2c244ec10e9a43531b0e8b92e26b11f52d67bab00528b8ba2f584e20e57420 nested.erofs ``` ## Build Results - `./build.sh`: PASS. - Final `build/erofs.ko` SHA-256 remained `50a19ea96c7414f73d92049671e66a9eacc9ff5abe27950a46d16c8e6c763baf`. - `nm -u build/erofs.ko | grep -w bcmp`: no match. - `git diff --check` for all scoped source and test files: PASS. The final integration rerun used the same combined module and these guest commands: ```sh cd /root/repo22-namei cc -Wall -Wextra -O2 -o readdir_probe readdir_probe.c ./vm-regression.sh ``` `vm-regression.sh` performs `kldload`, creates each vnode-backed md device, mounts it with `mount -t erofs`, executes the cold lookup and readdir probes, unmounts and detaches each image, and finishes with exact module unload. ## FreeBSD VM Results - `kldload`: PASS. - Cold direct read of `/alpha/bravo/charlie/payload.txt` without parent lookup or `readdir`: PASS. - Repeated lookup and sibling nested lookup: PASS. - Two negative lookups followed by an existing nested lookup: PASS. - Multi-block `wide` readdir: 322 dirents (`.`, `..`, 320 files), PASS. - Resume from every one of the 322 returned `d_off` cookies: PASS. - `dot_omitted` root cookies: `12`, `24`, `47`, `48`; resume at `47` returns only `.`, and resume at `48` returns EOF: PASS. - Short directory block: two lookups and direct `getdirentries` all return `EINTEGRITY`, PASS. - Non-monotonic `nameoff`: two lookups and direct `getdirentries` all return `EINTEGRITY`, PASS. - Nonzero data after NUL padding: two lookups and direct `getdirentries` all return `EINTEGRITY`, PASS. - `kldunload`: PASS. - Post-test EROFS module, mount, and md-device state: clean. - Final explicit state check: zero EROFS modules, zero EROFS mounts, zero md devices, and no recent panic or fatal trap in dmesg. ## Remaining Scope No unresolved issue remains for the requested cold lookup and directory regression. The NFS-specific `a_cookies` consumer path was not exercised by an NFS export; the tested `d_off` restart-cookie sequence uses the same generated cookie values.