The NFS v3 client in sigilOS now has a complete directory mutation set. nfs3_rename_call/parse (proc 14) and nfs3_rmdir_call/parse (proc 12) land today alongside the removal of a stale placeholder. A sigilOS process with Cap<Network> can now create, remove, rename, and manage directories on any NFS v3 share — all five mutation operations, all through the VFS dispatch layer. 337/337 ALL PASS.
The complete mutation set
| Proc | Operation | Status |
|---|---|---|
| 8 | nfs3_create — create a file | ✓ PASS |
| 11 | nfs3_remove — remove a file | ✓ PASS |
| 9 | nfs3_mkdir — create a directory | ✓ PASS |
| 12 | nfs3_rmdir — remove an empty directory | ✓ PASS (new) |
| 14 | nfs3_rename — rename/move across directories | ✓ PASS (new) |
nfs3_rename: atomic move
nfs3_rename_call(buf, mid, from_dir_fh, from_name, to_dir_fh, to_name) builds an NFS3 RENAME request (proc 14). The wire layout encodes two directory file handles and two names — the source and the destination — in a single atomic operation:
RENAME request wire layout:
[from_dir_fh: fh3] -- directory containing the source entry
[from_name: str] -- source name
[to_dir_fh: fh3] -- directory to move into (may == from_dir_fh)
[to_name: str] -- destination name
RENAME response:
[status: u32] -- 0 = NFS3_OK
[from_wcc: wcc] -- weak cache consistency for from_dir (null)
[to_wcc: wcc] -- weak cache consistency for to_dir (null)
The two-fh wire format is what makes NFS3 RENAME atomic across directories: both the source and destination directory handles are in the same RPC call, so the server can move the entry in a single operation without a visible intermediate state. A rename within the same directory passes the same fh for both from_dir_fh and to_dir_fh.
nfs3_rename_parse checks the status word and returns 0 on success, -1 on any NFS error. The wcc fields are present in the response but null — sigilOS doesn't cache directory attributes, so there is nothing to invalidate.
nfs3_rmdir: remove an empty directory
nfs3_rmdir_call(buf, mid, dir_fh, name) is structurally identical to nfs3_remove (proc 11) — both take a directory file handle and a name and return a status and a wcc record. The only difference is the proc number (12 vs 11) and the server-side semantics: REMOVE works on files, RMDIR works on directories and fails if the directory is not empty.
RMDIR request wire layout:
[dir_fh: fh3] -- parent directory
[name: str] -- directory name to remove
RMDIR response:
[status: u32] -- 0 = NFS3_OK; NFS3ERR_NOTEMPTY if not empty
[wcc: wcc] -- weak cache consistency for dir_fh (null)
Cleanup: stale placeholder removed
nfs3_lookup.sg was a placeholder file from an earlier scaffolding pass. The actual LOOKUP implementation (proc 3, used for path resolution) lives in nfs3.sg and has been there since the initial NFS3 codec landed. The placeholder was never wired to anything and produced a duplicate-symbol warning when both files were in scope. It is now removed.
What the NFS3 client can do now
With all five mutation operations in place, the NFS3 client covers the full lifecycle of files and directories on a remote share:
- List a directory (
nfs3_readdir) - Look up a path component (
nfs3_lookup) - Read a file (
nfs3_read) - Write a file (
nfs3_write) - Create a file (
nfs3_create) - Remove a file (
nfs3_remove) - Create a directory (
nfs3_mkdir) - Remove a directory (
nfs3_rmdir) - Rename or move an entry (
nfs3_rename)
The remaining gap is TCP transport binding — the same as SMB2. Once tcp_connect/tcp_send/tcp_recv are wired, both NFS3 and SMB2 can talk to real servers. The codec layer for both is complete.