# repo22 TC060 pathconf fix manual test report

Execution window: 2026-08-09 11:20-11:35 UTC

Exact source commit: `cdba7e54fb9e9d82980a06f178e68d0bbc1663ac`

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

## Scope and Result

TC060 is **PASS**. The original six-key helper returned the required values
with errno 0, compatibility queries retained their previous values, an unknown
name returned EINVAL, mount/read/read-only smoke passed, dmesg did not change,
and final mount/md/KLD/artifact counts were zero.

This was a direct manual run. No CI, result wrapper, or historical PASS supplied
the result. `WITH_ZSTDIO=1` was build-verified; runtime TC060 used
`WITH_ZSTDIO=0` because pathconf is compression-independent and this avoids an
unnecessary optional runtime dependency.

## Implementation Basis

FreeBSD 15 `sys/kern/vfs_default.c` shows that `vop_stdpathconf()` handles
generic `_PC_ASYNC_IO`, `_PC_PATH_MAX`, and several zero-valued optional
features, then returns EINVAL for other names. The same source tree's UFS,
tmpfs, and ext2fs vnode operations explicitly return 1 for
`_PC_CHOWN_RESTRICTED` and `_PC_NO_TRUNC` before delegating the remaining names
to `vop_stdpathconf()`.

The EROFS fix adds only those two switch labels and retains the existing
default delegation. `src/namei.c` rejects components longer than
`EROFS_NAME_LEN` with ENAMETOOLONG, while `src/erofs_vnops.c` rejects uid/gid
mutation with EROFS. Linux EROFS's 255-byte name limit and ENAMETOOLONG lookup
check were reviewed only for maintenance similarity; FreeBSD VOP behavior was
authoritative.

## Exact Build

The repo22 subtree was exported directly from the exact source commit. Later
`current/`-only reporting changes did not alter `src/`.

| Item | Value |
|---|---|
| Source archive SHA256 | `a4c871ca7cefc49470ef0003f876792380a8d8db6cbeaefb96456d06ad946d53` |
| FreeBSD source path | `/tmp/repo22-freebsd15-src` |
| Compiler | FreeBSD clang 19.1.7 |
| Build command | `FREEBSD_SRC=/tmp/repo22-freebsd15-src WITH_ZSTDIO=N ./build.sh` |

Both clean-object builds completed with kernel `-Werror`:

| Configuration | Module SHA256 | Size | Undefined-symbol audit | Result |
|---|---|---:|---|---|
| `WITH_ZSTDIO=0` | `68536e03ce93c6c04aab9cf29dab81ee5802d4b94401bd1a4bd752de40c0e504` | 73896 | no `bcmp`, no `ZSTD_*` | PASS |
| `WITH_ZSTDIO=1` | `598f171d355af78c64407a6d513d20f053530620da92df7f8ccfdf9a804e463d` | 78728 | no `bcmp`; only five expected FreeBSD `ZSTD_*` APIs | PASS |

The enabled module referenced `ZSTD_DCtx_setParameter`,
`ZSTD_createDCtx_advanced`, `ZSTD_decompressStream`, `ZSTD_freeDCtx`, and
`ZSTD_isError`.

## Fixture and Probes

| Artifact | SHA256 |
|---|---|
| `vfs-plain.erofs` | `79f0b5f4aa8e7ea532b711ee8fb466f14f35d0952446d41ba4bbc4d6e008b8ff` |
| tracked `g3_vfs_probe` binary | `08fe3999600dd1826805c00a17af3d893d247a593c0812f767d85a2633302068` |
| one-run `pathconf_audit` source | `c2cc69297848cad8c806d47585ef670ae5b378827450110e2cf47cf82ed527b3` |
| one-run `pathconf_audit` binary | `17aa9d738871ac424900ed7a928cb72cc2e5a288f2c2f9744906fa139a78835b` |

Both probes compiled natively with `-O2 -Wall -Wextra -Werror -std=c17`.
The one-run helper was kept outside the repository and only printed each
value/errno plus assertions for unknown-name and overlong-component behavior.

## TC060 Values

The exact original command was:

```text
./g3_vfs_probe pathconf /tmp/repo22-tc060-fix/mnt/testdir
```

It exited 0 and printed:

```text
name_max=255 path_max=1024 filesizebits=64 link_max=2147483647
no_trunc=1 chown_restricted=1
```

The expanded value/errno capture was:

| Query | Actual value | errno |
|---|---:|---:|
| `_PC_NAME_MAX` | 255 | 0 |
| `_PC_PATH_MAX` | 1024 | 0 |
| `_PC_FILESIZEBITS` | 64 | 0 |
| `_PC_LINK_MAX` | 2147483647 | 0 |
| `_PC_NO_TRUNC` | 1 | 0 |
| `_PC_CHOWN_RESTRICTED` | 1 | 0 |
| `_PC_ASYNC_IO` | 200112 | 0 |
| `_PC_ACL_EXTENDED` | 1 | 0 |
| `_PC_ACL_PATH_MAX` | 254 | 0 |
| `_PC_ACL_NFS4` | 0 | 0 |
| unknown name `INT_MAX` | -1 | 22 (`EINVAL`) |

A lookup using a 256-byte path component returned -1 with errno 63
(`ENAMETOOLONG`), confirming the reported no-truncation behavior.

## Mount and Read Smoke

The module loaded as KLD file ID 20, module `erofs`, and the fixture attached
as dynamic `md0`. The mount line was:

```text
/dev/md0 on /tmp/repo22-tc060-fix/mnt (erofs, local, read-only, acls)
```

`testdir` reported inode 44, mode `drwxr-xr-x`, and size 1039. Reading
`testdir/file.txt` produced `repo22 G3 file payload` and SHA256
`523af4c899ba3b8f4fa4d854fe609590be271905b63932cf067ca9630e612687`.
Opening the same file with `O_RDWR` returned errno 30 (`EROFS`) as expected.

## Dmesg and Cleanup

The qualified run's dmesg had 131 lines before and after, with identical
SHA256 `91bf74a3607f432b4802f143bcf9d050c2f9e741da0a54f3e6a1b47bcc996a06`.
There was no new EROFS diagnostic, assertion, trap, panic, or resource warning.

Qualified cleanup returned zero for umount, md detach, and KLD unload. After
removing the transferred archive and test directory, the final guest audit was:

```text
erofs_mounts=0
md_units=
erofs_klds=0
guest_artifacts=0
```

## Discarded Attempt

The first otherwise successful runtime pass loaded the KLD from the
non-canonical filename `erofs-zstdio0.ko`. Its explicit `kldunload erofs`
command could not resolve that filename, so the run stopped before final dmesg
and cleanup qualification. Its mount and md were already clean; KLD file ID 20
was then unloaded directly and the guest returned to zero resources. The full
test was repeated with canonical `erofs.ko`, and only that rerun is counted.

## Independent Review

The post-test review found no lock, resource, or error-path issue:

- `VOP_PATHCONF` is called with the vnode shared-locked; the function does not
  change lock state.
- The new constant-return branches allocate nothing and acquire no references.
- Both normal and FIFO vnode vectors use the same filesystem-wide result.
- Existing value branches are unchanged, and unsupported names still reach
  `vop_stdpathconf()` and return EINVAL.
- The syscall copies `retval` only on error 0, so the unknown-name error path
  cannot expose a stale value.
