[NODEFS] hardlinks, utimensat nofollow, and fadvise/fallocate seek errors#27305
Merged
Conversation
634e042 to
2703eea
Compare
sbc100
approved these changes
Jul 10, 2026
8a9e5da to
f8a0538
Compare
c9be8ca to
39d23ee
Compare
Collaborator
Author
|
I added an addition here to properly support I'm open to removing / reconsidering any items here if we want to gate these as unsupported instead, but they are as far as I can tell some of the last parity gaps. |
sbc100
reviewed
Jul 10, 2026
39d23ee to
aefb630
Compare
guybedford
added a commit
to guybedford/nix
that referenced
this pull request
Jul 11, 2026
Adds wasm32-unknown-emscripten as a target for nix, plus a CI job that runs the full test suite under Node. Source changes to compile on emscripten (which behaves like a Linux/musl libc): * ioctl: use the Linux ioctl module and a c_int ioctl_num_type on emscripten (src/sys/ioctl, src/sys/mod.rs). * resource: enable getrlimit/setrlimit and the Resource enum via libc::rlimit. * socket: include emscripten in the SockaddrStorage AF_UNIX (pathname/unnamed) decode arms. * unistd: sethostname is unavailable on emscripten, so gate it out. The suite spawns OS threads (signal and socket-peer tests), so std is rebuilt with atomics via -Zbuild-std and linked -pthread with -sPROXY_TO_PTHREAD so the main thread can block; JSPI (-sJSPI) provides the return-to-host suspension for blocking reads/writes, and NODERAWFS/NODERAWSOCKETS back the filesystem and sockets with node's. This needs nightly + rust-src and a JSPI-capable Node (26+, or 22 with --experimental-wasm-jspi). No custom target spec is required: nightly now emits the __main_argc_argv entry point (rust-lang/rust#158937). Genuinely-unsupported operations are ignored with #[cfg_attr(target_os = "emscripten", ignore)] rather than cfg'd out, covering fork/wait, pty/termios, mkfifo, getpwnam/setgroups/acct, flock, if_nametoindex, pipe2 FD_CLOEXEC, AF_UNIX SCM_RIGHTS/datagram, thread signal masking, and setrlimit; a few tests are cfg'd out where the feature is entirely absent (raw signal actions, socketpair) or the host differs (linkat symlink follow). Temporary, until the dependencies land upstream: * Cargo.toml patches libc to guybedford/libc#libc-0.2-emscripten for the emscripten externs (rust-lang/libc#5270). * the CI job builds against the guybedford/emscripten `cf` fork, which carries the NODEFS/socket/blocking patches this target depends on (emscripten-core/emscripten#27305, #27306). Suite result: 148 passed, 0 failed, 35 ignored under Node - green on Linux CI.
sbc100
reviewed
Jul 11, 2026
sbc100
left a comment
Collaborator
There was a problem hiding this comment.
still lgtm, but I don't see the getuid stuff
guybedford
added a commit
to guybedford/nix
that referenced
this pull request
Jul 11, 2026
Adds wasm32-unknown-emscripten as a target for nix, plus a CI job that runs the full test suite under Node. Source changes to compile on emscripten (which behaves like a Linux/musl libc): * ioctl: use the Linux ioctl module and a c_int ioctl_num_type on emscripten (src/sys/ioctl, src/sys/mod.rs). * resource: enable getrlimit/setrlimit and the Resource enum via libc::rlimit. * socket: include emscripten in the SockaddrStorage AF_UNIX (pathname/unnamed) decode arms. * unistd: sethostname is unavailable on emscripten, so gate it out. The suite spawns OS threads (signal and socket-peer tests), so std is rebuilt with atomics via -Zbuild-std and linked -pthread with -sPROXY_TO_PTHREAD so the main thread can block; JSPI (-sJSPI) provides the return-to-host suspension for blocking reads/writes, and NODERAWFS/NODERAWSOCKETS back the filesystem and sockets with node's. This needs nightly + rust-src and a JSPI-capable Node (26+, or 22 with --experimental-wasm-jspi). No custom target spec is required: nightly now emits the __main_argc_argv entry point (rust-lang/rust#158937). Genuinely-unsupported operations are ignored with #[cfg_attr(target_os = "emscripten", ignore)] rather than cfg'd out, covering fork/wait, pty/termios, mkfifo, getpwnam/setgroups/acct, flock, if_nametoindex, pipe2 FD_CLOEXEC, AF_UNIX SCM_RIGHTS/datagram, thread signal masking, and setrlimit; a few tests are cfg'd out where the feature is entirely absent (raw signal actions, socketpair) or the host differs (linkat symlink follow). Temporary, until the dependencies land upstream: * Cargo.toml patches libc to guybedford/libc#libc-0.2-emscripten for the emscripten externs (rust-lang/libc#5270). * the CI job builds against the guybedford/emscripten `cf` fork, which carries the NODEFS/socket/blocking patches this target depends on (emscripten-core/emscripten#27305, #27306). Suite result: 148 passed, 0 failed, 35 ignored under Node - green on Linux CI.
aefb630 to
ea0e6a9
Compare
Collaborator
Author
Reposted this in #27319. |
guybedford
added a commit
to guybedford/nix
that referenced
this pull request
Jul 11, 2026
Adds wasm32-unknown-emscripten as a target for nix, plus a CI job that runs the full test suite under Node. Source changes to compile on emscripten (which behaves like a Linux/musl libc): * ioctl: use the Linux ioctl module and a c_int ioctl_num_type on emscripten (src/sys/ioctl, src/sys/mod.rs). * resource: enable getrlimit/setrlimit and the Resource enum via libc::rlimit. * socket: include emscripten in the SockaddrStorage AF_UNIX (pathname/unnamed) decode arms. * unistd: sethostname is unavailable on emscripten, so gate it out. The suite spawns OS threads (signal and socket-peer tests), so std is rebuilt with atomics via -Zbuild-std and linked -pthread with -sPROXY_TO_PTHREAD so the main thread can block; JSPI (-sJSPI) provides the return-to-host suspension for blocking reads/writes, and NODERAWFS/NODERAWSOCKETS back the filesystem and sockets with node's. This needs nightly + rust-src and a JSPI-capable Node (26+, or 22 with --experimental-wasm-jspi). No custom target spec is required: nightly now emits the __main_argc_argv entry point (rust-lang/rust#158937). Genuinely-unsupported operations are ignored with #[cfg_attr(target_os = "emscripten", ignore)] rather than cfg'd out, covering fork/wait, pty/termios, mkfifo, getpwnam/setgroups/acct, flock, if_nametoindex, pipe2 FD_CLOEXEC, AF_UNIX SCM_RIGHTS/datagram, thread signal masking, and setrlimit; a few tests are cfg'd out where the feature is entirely absent (raw signal actions, socketpair) or the host differs (linkat symlink follow). Temporary, until the dependencies land upstream: * Cargo.toml patches libc to guybedford/libc#libc-0.2-emscripten for the emscripten externs (rust-lang/libc#5270). * the CI job builds against the guybedford/emscripten `cf` fork, which carries the NODEFS/socket/blocking patches this target depends on (emscripten-core/emscripten#27305, #27306). Suite result: 148 passed, 0 failed, 35 ignored under Node - green on Linux CI.
guybedford
added a commit
to guybedford/nix
that referenced
this pull request
Jul 11, 2026
Adds wasm32-unknown-emscripten as a target for nix, plus a CI job that runs the full test suite under Node. Source changes to compile on emscripten (which behaves like a Linux/musl libc): * ioctl: use the Linux ioctl module and a c_int ioctl_num_type on emscripten (src/sys/ioctl, src/sys/mod.rs). * resource: enable getrlimit/setrlimit and the Resource enum via libc::rlimit. * socket: include emscripten in the SockaddrStorage AF_UNIX (pathname/unnamed) decode arms. * unistd: sethostname is unavailable on emscripten, so gate it out. The suite spawns OS threads (signal and socket-peer tests), so std is rebuilt with atomics via -Zbuild-std and linked -pthread with -sPROXY_TO_PTHREAD so the main thread can block; JSPI (-sJSPI) provides the return-to-host suspension for blocking reads/writes, and NODERAWFS/NODERAWSOCKETS back the filesystem and sockets with node's. This needs nightly + rust-src and a JSPI-capable Node (26+, or 22 with --experimental-wasm-jspi). No custom target spec is required: nightly now emits the __main_argc_argv entry point (rust-lang/rust#158937). Genuinely-unsupported operations are ignored with fork/wait, pty/termios, mkfifo, getpwnam/setgroups/acct, flock, if_nametoindex, pipe2 FD_CLOEXEC, AF_UNIX SCM_RIGHTS/datagram, thread signal masking, and setrlimit; a few tests are cfg'd out where the feature is entirely absent (raw signal actions, socketpair) or the host differs (linkat symlink follow). Temporary, until the dependencies land upstream: * Cargo.toml patches libc to guybedford/libc#libc-0.2-emscripten for the emscripten externs (rust-lang/libc#5270). * the CI job builds against the guybedford/emscripten `cf` fork, which carries the NODEFS/socket/blocking patches this target depends on (emscripten-core/emscripten#27305, #27306). Suite result: 148 passed, 0 failed, 35 ignored under Node - green on Linux CI.
guybedford
added a commit
to guybedford/nix
that referenced
this pull request
Jul 11, 2026
Adds wasm32-unknown-emscripten as a target for nix, plus a CI job that runs the full test suite under Node. Source changes to compile on emscripten (which behaves like a Linux/musl libc): * ioctl: use the Linux ioctl module and a c_int ioctl_num_type on emscripten (src/sys/ioctl, src/sys/mod.rs). * resource: enable getrlimit/setrlimit and the Resource enum via libc::rlimit. * socket: include emscripten in the SockaddrStorage AF_UNIX (pathname/unnamed) decode arms. * unistd: sethostname is unavailable on emscripten, so gate it out. The suite spawns OS threads (signal and socket-peer tests), so std is rebuilt with atomics via -Zbuild-std and linked -pthread with -sPROXY_TO_PTHREAD so the main thread can block; JSPI (-sJSPI) provides the return-to-host suspension for blocking reads/writes, and NODERAWFS/NODERAWSOCKETS back the filesystem and sockets with node's. This needs nightly + rust-src and a JSPI-capable Node (26+, or 22 with --experimental-wasm-jspi). No custom target spec is required: nightly now emits the __main_argc_argv entry point (rust-lang/rust#158937). Unsupported operations are ignored with #[cfg_attr(target_os = "emscripten", ignore)] rather than cfg'd out wherever they still compile, covering fork/wait, pty/termios, mkfifo, getpwnam/setgroups/acct, flock, if_nametoindex, pipe2 FD_CLOEXEC, AF_UNIX SCM_RIGHTS/datagram, getsockname, socketpair, setrlimit, and the linkat symlink-follow host check. The signal-delivery tests (signal, killpg, thread mask) and the AF_ROUTE test are cfg'd out instead: compiling their extern "C" handlers in perturbs the wasm indirect function table and aborts the runtime. Temporary, until the dependencies land upstream: * the emscripten CI job injects a libc patch (guybedford/libc#libc-0.2-emscripten, rust-lang/libc#5270) via `cargo --config`, so other targets and CI jobs are unaffected. * it also builds against the guybedford/emscripten `cf` fork, which carries the NODEFS/socket/blocking patches this target depends on (emscripten-core/emscripten#27305, #27306). Suite result: 148 passed, 0 failed, 38 ignored under Node - green on Linux CI.
…allocate seek errors Adds a set of filesystem fixes to the legacy (non-WASMFS) JS filesystem, principally for the NODEFS/NODERAWFS host backends: * linkat(2): replace the weak `-EMLINK` stub with a real implementation. A new `FS.link` core op looks up the target and delegates to a backend `link` node op when present, returning EMLINK for backends without one. Only NODERAWFS provides it (via `fs.linkSync`); NODEFS deliberately does not, since a real host hardlink cannot be confined to the mount root (host symlinks in intermediate path components would escape it), so it reports EMLINK like MEMFS. AT_SYMLINK_FOLLOW is honored. * utimensat(2): support AT_SYMLINK_NOFOLLOW instead of asserting `!flags`. `FS.utime` gains a `dontFollow` argument threaded through `doSetAttr`. NODERAWFS and NODEFS use `fs.lutimesSync` so a symlink's own timestamps are set without the host resolving the link (which would otherwise escape the NODEFS mount root). * getuid/geteuid/getgid/getegid: report the real host process credentials under NODERAWFS (via node's `process.get*id()`), rather than always 0. These move from weak C stubs to the JS syscall layer, since a weak C definition would otherwise win over the JS override. Other backends (and Windows, which has no uid/gid concept) continue to report 0. The set* variants remain EPERM: musl routes them through `__setxid`, which emscripten stubs out because it relies on dynamic (numeric) syscall dispatch. * posix_fadvise/posix_fallocate: return ESPIPE on a non-seekable fd (pipe or socket), matching Linux. `__syscall_fadvise64` moves from a weak C stub to the JS syscall layer so it can inspect the stream. * NODERAWFS fstat: defer virtual streams (pipes, sockets) that have no backing host fd to their own `getattr` rather than calling `fs.fstatSync` on an undefined nfd. Adds test/fs coverage across MEMFS/NODEFS/NODERAWFS: test_fs_link, test_fs_utimensat_nofollow, test_fs_getuid, and test_fs_fadvise_fallocate.
774a16a to
c9c21ba
Compare
Collaborator
Author
|
@sbc100 ah it was still including the |
sbc100
approved these changes
Jul 11, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds a handful of filesystem fixes to the legacy (non-WASMFS) JS filesystem, mostly around the NODEFS/NODERAWFS host backends where these operations previously either errored unconditionally or asserted.
linkat(2): the weak stub always returnedEMLINK, so hardlinks were impossible even on host-backed filesystems. A newFS.linkcore op looks up the target and delegates to a backendlinknode op when one exists, still returningEMLINKfor backends without one. Only NODERAWFS provides it (viafs.linkSync). NODEFS deliberately does not: unlike NODERAWFS it confines the guest to a mount root, and a real host hardlink can't be guaranteed to stay within that root (host symlinks in intermediate path components are resolved by the OS and would escape it, with no portable atomic way to check), so NODEFS reportsEMLINKlike MEMFS.AT_SYMLINK_FOLLOWis honored.utimensat(2): previously asserted!flags, soAT_SYMLINK_NOFOLLOWaborted under assertions.FS.utimegains adontFollowargument threaded throughdoSetAttr. Both NODERAWFS and NODEFS usefs.lutimesSyncfor the nofollow case so a symlink's own timestamps are set without the host resolving the link — which also avoids escaping the NODEFS mount root thatfs.utimesSync(follows symlinks) would allow.posix_fadvise/posix_fallocate: returnESPIPEon a non-seekable fd (pipe/socket) to match Linux, rather than silently succeeding or truncating.__syscall_fadvise64moves from a weak C stub into the JS syscall layer so it can inspect the stream.fstat: virtual streams (pipes, sockets) have no backing host fd, so defer to their owngetattrinstead of callingfs.fstatSyncon an undefined nfd.Tests in
test/fscover these across MEMFS/NODEFS/NODERAWFS (test_fs_link,test_fs_utimensat_nofollow,test_fs_fadvise_fallocate). Codesize expectations are updated for the newFS.link/FS_linksymbol.Made with AI assistance under my review