Skip to content

test(nfs): run the conformance special-files cases over both NFS wires - #18

Merged
pi0 merged 1 commit into
mainfrom
feat/nfs-mknod
Aug 1, 2026
Merged

test(nfs): run the conformance special-files cases over both NFS wires#18
pi0 merged 1 commit into
mainfrom
feat/nfs-mknod

Conversation

@pi0x

@pi0x pi0x commented Aug 1, 2026

Copy link
Copy Markdown
Collaborator

Builds on #17 (feat/driver-mknode, which carries ConformanceTarget.carries and the mknod.anyType gate) — its commits are in this branch's history, so review only the last commit. Together the two close #14.

What

nfsDriver and nfs4Driver now offer mountx.mknod, routed to the MKNOD (RFC 1813 §3.3.11) and CREATE (RFC 8881 §18.4) methods the clients already had, and THROUGH_NFS/THROUGH_NFS4 declare extensions: ["mknod"]. Four of the six special-files cases now run over the wire against the memory targets in both columns:

  • the FIFO and socket type predicates, decided off ftype3/nfs_ftype4 in the wire's own attributes
  • rdev surviving the 8-bit split into specdata3/specdata4 and rejoining out of fattr3.rdev/rawdev
  • a special file behaving as an ordinary name under rename and unlink
  • the EEXIST and ENOENT refusals, each the far side's answer arriving as an nfsstat3/nfsstat4

What stays skipped, and why that is the point

Neither wire carries the file type in the mode: sattr3.mode is masked to 0o7777 at both ends, and fattr4's mode is permission bits. So mknod(path, S_IFREG | …), mknod(path, 0) and mknod(path, S_IFDIR | …) are questions these wires cannot ask — over v4.1 NF4REG is NFS4ERR_BADTYPE by §15.1.4.1 and NF4DIR is mkdir. Both memory targets declare carries: [], and the two cases gated on mknod.anyType skip.

The adapters refuse those types with a plain Error naming the wire's limit, deliberately not errno-shaped — no code, no errno, nothing rejects() could match. Deciding EPERM in the client would be inventing the refusal the driver on the far side owes, which is the fake invariant 5 names. One case per column pins that property, so un-gating a case fails loudly instead of passing for the wrong reason.

The node-fs targets keep extensions: []: that driver implements no mountx.mknod, so the sessions correctly answer NFS3ERR_NOTSUPP / NFS4ERR_BADTYPE. Same wording as the 9P column's oracle override.

Also

test/matrix.ts's "Capability loss" preamble and .agents/testing.md said the loopback and 9P columns were the only ones offering the extension by name and that NFS could not offer the whole thing; both now say what the run shows, and .agents/testing.md gains a paragraph on carries/mknod.anyType. .agents/conformance-matrix.md is regenerated: NFSv3 and NFSv4.1 go from 124 passed / 18 skipped to 128 / 14.

pnpm test is green (2998 passed, 315 skipped).

🤖 Generated with Claude Code

@vercel

vercel Bot commented Aug 1, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
mountx Ready Ready Preview Aug 1, 2026 9:08am

Request Review

@pi0
pi0 marked this pull request as ready for review August 1, 2026 09:07
MKNOD (§3.3.11) and CREATE (§18.4) are operations an NFS client can call
by name, so `nfsDriver` and `nfs4Driver` now offer `mountx.mknod` and the
two columns declare the extension. Four of the six special-files cases
run over the wire against the memory targets: the FIFO and socket type
predicates off `ftype3`/`nfs_ftype4`, `rdev` surviving the 8-bit split
through `specdata3`/`specdata4` and back out of `fattr3.rdev`/`rawdev`, a
special file behaving as an ordinary name under rename and unlink, and
the `EEXIST`/`ENOENT` refusals — every one of them the far side's answer
arriving as an `nfsstat3`/`nfsstat4`.

The other two stay skipped, because neither wire carries the file type in
the *mode*: `sattr3.mode` is masked to `0o7777` at both ends and `fattr4`'s
`mode` is permission bits, so a mode naming a regular file, a directory or
no type at all is a question that cannot be asked. Both targets declare
`carries: []` for it. The adapters refuse those types with a plain,
deliberately non-errno-shaped `Error` naming the wire's limit rather than
deciding `EPERM` locally — that refusal belongs to the driver on the far
side, and inventing it here is exactly what invariant 5 forbids. A case
per column pins that the refusal has no `code` and no `errno`, so
un-gating one fails loudly instead of passing for the wrong reason.

The node-fs targets keep `extensions: []`: that driver implements no
`mountx.mknod`, so the sessions answer `NFS3ERR_NOTSUPP`/`NFS4ERR_BADTYPE`
as they should, and the column carrying an extension is not the same claim
as a target having one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@pi0
pi0 merged commit 4162213 into main Aug 1, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

test: carry the conformance special-files cases over NFSv3 and NFSv4.1

2 participants