Skip to content

Fix NT path handling and stat in eio_windows - #932

Merged
talex5 merged 14 commits into
ocaml-multicore:mainfrom
avsm:issue-931-nt-paths
Sep 8, 2026
Merged

Fix NT path handling and stat in eio_windows#932
talex5 merged 14 commits into
ocaml-multicore:mainfrom
avsm:issue-931-nt-paths

Conversation

@avsm

@avsm avsm commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

While fixing #931 and adding tests, I went into a rabbithole of fixes involving Windows symlinks. For anyone who want to follow (pun intended) in my footsteps, beware of Windows Redirect Guard which is now active on OpenSSH by default and completely destroys the symlink-using Flexlink OCaml toolchain.

This fixes the issue reported in #931 and uses Nt_path consistently (exposing some new functions there too). I found some other bugs in symlink handling too:

  • The openat/unlinkat stubs checked an uninitialized handle before theNtCreateFile status, so failures were misreported.
  • nofollow replaced the create options with a Win32 flag, dropping the directory/non-directory constraints so stat kinds didnt' work
  • open_out ELOOP recovery ran readlink relative to the process directory rather than the sandbox root.
  • openat was only partially applied inside Err.run, so Unix errors from open_in escaped unmapped.
  • Opening for reading or stat could create a missing file.
  • stat always passed FILE_NON_DIRECTORY_FILE, so statting a directory failed.
  • stat with ~follow:false followed the link anyway; there is now a third open mode that opens the symlink, and fstat reports the target length as the size like the Posix backend does.

Best reviewed commit by commit and the testcases are separate so I could verify they failed before and worked after.

The stubs checked an uninitialized handle before the NtCreateFile status
@avsm avsm added the windows label Sep 7, 2026
Comment thread lib_eio_windows/test/test_fs.ml Outdated

@talex5 talex5 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks - this looks like a huge improvement!

Comment thread lib_eio_windows/test/test_fs.ml Outdated
Comment thread lib_eio/utils/nt_path.ml Outdated
Comment thread lib_eio_windows/eio_windows_stubs.c Outdated
Comment thread lib_eio_windows/low_level.mli Outdated
avsm added 13 commits September 7, 2026 18:44
Absolute and relative paths given to fs are passed to NtCreateFile unqualified
and fail with ENOENT, unless already in NT form (ocaml-multicore#931).
Unqualified paths (like `C:\foo`) raised ENOENT. We now convert such paths
before passing them through to Win32.

Fixes ocaml-multicore#931.
Filename doesn know about volume prefixes, so it splits a UNC root or a bare
drive as if it were an ordinary path.
nofollow formerly replaced the NT create options with a Win32 flag,
dropping the directory/non-directory constraints.
The ELOOP handler ran readlink relative to the process directory instead of the
sandbox root.
The openat call was only partially applied inside Err.run,
so the syscall ran outside it and Unix errors escaped.
stat always passed FILE_NON_DIRECTORY_FILE, so statting a directory failed.
stat needs a third mode that opens the link rather than its target, to match
the posix backend. fstat now reports the length of the target as the size, as
lstat does (the odd looking division by 2 in the stub is because Windows
comes back with a 2-byte WCHAR array).
@avsm
avsm force-pushed the issue-931-nt-paths branch from e85667a to eea7d54 Compare September 7, 2026 20:59
@avsm

avsm commented Sep 7, 2026

Copy link
Copy Markdown
Contributor Author

Weird unrelated (?) error on fedora:

+++ b/_build/default/tests/.mdx/process.md.corrected
@@ -190,7 +190,7 @@ A script whose interpreter is missing reports the executable as not found:
let f = Eio.Stdenv.cwd env / "badinterp" in
Eio.Path.save f "#!/nonexistent/interpreter\n" ~create:(`Exclusive 0o700);
Fun.protect ~finally:(fun () -> Eio.Path.unlink f) (fun () -> Process.run mgr ["./badinterp"]);;
-Exception: Eio.Io Process Executable_not_found "./badinterp"
+Exception: Unix.Unix_error(Unix.EUNKNOWNERR 26, "execve", "")

@avsm

avsm commented Sep 8, 2026

Copy link
Copy Markdown
Contributor Author

That was a transient error, gone on re-run. This is good to go I think.

@talex5
talex5 merged commit c06e504 into ocaml-multicore:main Sep 8, 2026
5 checks passed
@avsm
avsm deleted the issue-931-nt-paths branch September 8, 2026 11:08
@talex5

talex5 commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

Unix.EUNKNOWNERR 26 is ETXTBSY. I'll investigate...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants