feat(personalities): real NTSTATUS/LastError + Darwin errno maps - #90
Conversation
Windows failures previously flattened every VFS return code to ERROR_FILE_NOT_FOUND or ERROR_INVALID_HANDLE, and NTSTATUS existed only as an unused helper. Darwin had an errno table with no caller and no errno slot. - windows.in: paired win-last-error/win-last-status slots, winerror.h + NTSTATUS tables, RtlNtStatusToDosError subset and its inverse, single-writer setters used by every failure exit, existence probe to refine 0/-1 fs failures into ALREADY_EXISTS / DIR_NOT_EMPTY / PATH_NOT_FOUND, and calls 34-36 (RtlGetLastNtStatus, RtlNtStatusToDosError, RtlGetLastWin32Error). - darwin.in: darwin-dispatch wraps darwin-dispatch-call and records negative results in darwin-errno-last (success leaves it alone, per BSD); __error() shims 0x2000/0x2001; darwin-errno covers ESRCH/ENOMEM/ENOSYS. - Both demos assert the mapping; check scripts grep the exact codes and fail on the FAILED marker. Verified: check-personalities.sh green (SCI contract + both QEMU demos). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_45913720-e1dd-452e-a130-8e85309f3a99) |
|
Tick the box to add this pull request to the merge queue (same as
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3e6a0bef15
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if err == WIN-ERROR-FILE-EXISTS { | ||
| return WIN-STATUS-OBJECT-NAME-COLLISION |
There was a problem hiding this comment.
Preserve ERROR_FILE_EXISTS through the paired mapping
When a caller invokes SetLastError(ERROR_FILE_EXISTS), this branch stores ERROR_FILE_EXISTS (80) but pairs it with STATUS_OBJECT_NAME_COLLISION; RtlNtStatusToDosError maps that status to ERROR_ALREADY_EXISTS (183). The advertised invariant therefore fails for a supported constant, even though the golden test covers only ERROR_DISK_FULL; either the mapping must round-trip 80 or SetLastError must not synthesize a supposedly equivalent NTSTATUS.
Useful? React with 👍 / 👎.
| win-clear-error() | ||
| return vfs-lseek(vfd, offset, whence) |
There was a problem hiding this comment.
Record lseek failures before returning
With a valid file handle but an invalid whence or negative resulting offset, vfs-lseek returns -22; this path clears both error slots before returning that failure, so GetLastError() and RtlGetLastNtStatus() incorrectly report success. Capture the result first and route negative values through win-set-error-rc.
Useful? React with 👍 / 👎.
| if vfs-rc == -38 { | ||
| return DARWIN-ENOSYS | ||
| } | ||
| return DARWIN-EINVAL |
There was a problem hiding this comment.
Convert VFS errors to Darwin errno numbers
The new errno slot still exposes Linux/VFS-shaped numbers rather than Darwin values: an unknown syscall maps -38 to errno 38 even though Darwin's ENOSYS is 78, while exhausting the VFS descriptor table makes vfs-open return -16, which falls through here to EINVAL (22) instead of Darwin EMFILE (24). Darwin callers inspecting the newly added errno slot therefore receive incorrect compatibility results for these failures.
Useful? React with 👍 / 👎.
| return 0 | ||
| } | ||
| if n == 0 && len > 0 { | ||
| win-set-status(WIN-STATUS-END-OF-FILE) |
There was a problem hiding this comment.
Use the real STATUS_END_OF_FILE value
When a read reaches EOF, this new path records WIN-STATUS-END-OF-FILE, but that constant is -1073741805 (0xC0000013), which is STATUS_NO_MEDIA_IN_DEVICE; the standard STATUS_END_OF_FILE value is 0xC0000011 (-1073741807). Consequently RtlGetLastNtStatus() returns the wrong standard status after EOF, and RtlNtStatusToDosError also treats the no-media value as ERROR_HANDLE_EOF.
Useful? React with 👍 / 👎.
| serial-nl(port) | ||
|
|
||
| // Every VFS/errno-shaped code must round-trip status → dos error. | ||
| let rcs = alloc(8 * 10) |
There was a problem hiding this comment.
Include every advertised return code in the golden check
The mapping table handles 13 documented VFS codes, but this supposedly exhaustive golden check allocates and tests only ten entries, omitting -6, -21, and -38. The script can therefore print errno/NTSTATUS pairs consistent 10/10 and mark the feature complete even if the no-media, directory, or not-implemented mappings are broken; build the array from all 13 advertised cases and assert the corresponding total.
Useful? React with 👍 / 👎.
| let rc = darwin-dispatch-call(port, num, a0, a1, a2, a3) | ||
| if rc < 0 { | ||
| darwin-errno-last = 0 - darwin-errno(rc) | ||
| } |
There was a problem hiding this comment.
Record zero-valued getcwd failures in errno
When getcwd receives a buffer too small for the current path, posix-sys-getcwd returns 0, but this wrapper updates errno only for negative results. The call therefore fails while leaving darwin-errno-last stale instead of reporting ERANGE, so the new errno contract is not end-to-end for this existing Darwin syscall; the dispatch layer needs either syscall-specific failure detection or normalized negative error returns.
Useful? React with 👍 / 👎.
Closes the M4 gap-matrix row "NTSTATUS / errno maps" and the M4 checkbox "LastError / errno consistency end-to-end" in
docs/personalities-roadmap.md.Problem
ERROR_FILE_NOT_FOUNDorERROR_INVALID_HANDLE.win-ntstatusexisted but was never called on a real code — only fed the literal-2in the demo. So a disk-full or too-many-handles failure was indistinguishable from a missing file.darwin-errnohad a table and zero callers, and there was no errno slot at all, so a failing call left nothing behind to inspect.Windows (
components/windows.in)win-last-error/win-last-statusslots with a single writer (win-set-error,win-set-status,win-set-error-rc), so the two can never disagree.winerror.h+ NTSTATUS constant tables;win-ntstatus(rc)maps the VFS/errno-shaped codes the tree actually returns (-2 -5 -6 -9 -12 -13 -16 -17 -21 -22 -28 -38 -39);win-dos-erroris anRtlNtStatusToDosErrorsubset andwin-status-from-errorits inverse for theSetLastErrorpath.ERROR_ALREADY_EXISTSvsERROR_PATH_NOT_FOUND; unlink/rmdir →ERROR_FILE_NOT_FOUNDvsERROR_DIR_NOT_EMPTY; rename → missing source vs occupied destination.RtlGetLastNtStatus, 35RtlNtStatusToDosError, 36RtlGetLastWin32Error.Darwin (
components/darwin.in)darwin-dispatchwraps the old body (darwin-dispatch-call) and records every negative result indarwin-errno-last. Successful calls leave errno untouched, per BSD.__error()shims:0x2000get,0x2001set.darwin-errno-namerenders the number in serial logs.darwin-errnonow covers everyDARWIN-E*the surface returns (ESRCH, ENOMEM, ENOSYS); unknown codes fall back toEINVAL.Verification
Both check scripts now grep the exact codes and fail on an explicit
FAILEDmarker, so this is a golden-log gate rather than eyeballing:windows: LastError/NTSTATUS consistency OK— 5/5 scenarios plus 10/10 rc pairs satisfyingGetLastError() == RtlNtStatusToDosError(RtlGetLastNtStatus()).darwin: errno consistency OK— 6/6 scenarios.Ran locally (macOS host, QEMU,
INAUGURATION_DIR=../inauguration):scripts/check-personalities.sh→ SCI contract + both QEMU demos PASSscripts/check-qemu-boot.sh→ PASSScope is deliberately tables + wiring only: no PE loader, no event/sync objects, no Mach IPC.
🤖 Generated with Claude Code
Note
Cursor Bugbot is generating a summary for commit 3e6a0be. Configure here.