vfs: fs hook gaps - #65852
Open
pipobscure wants to merge 2 commits into
Open
Conversation
jasnell
approved these changes
Sep 6, 2026
pipobscure
marked this pull request as ready for review
September 6, 2026 14:23
This comment has been minimized.
This comment has been minimized.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #65852 +/- ##
==========================================
- Coverage 90.17% 90.16% -0.02%
==========================================
Files 771 771
Lines 265470 265548 +78
Branches 50463 50499 +36
==========================================
+ Hits 239383 239426 +43
- Misses 17055 17074 +19
- Partials 9032 9048 +16
🚀 New features to boost your workflow:
|
mcollina
reviewed
Sep 6, 2026
This comment was marked as outdated.
This comment was marked as outdated.
pipobscure
force-pushed
the
vfs-fs-hook-gaps
branch
from
September 7, 2026 15:41
fc111c7 to
e04bc8b
Compare
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
trivikr
approved these changes
Sep 8, 2026
This comment has been minimized.
This comment has been minimized.
Several `node:fs` entry points behave differently for a mounted path than for a real one, because of how the call reaches the VFS hooks. Make them behave as they do for a real path: * Add the `watchFile`, `unwatchFile` and `promisesWatch` handlers, backed by the provider's stat watcher and async watcher; those calls threw a TypeError before. Have `watch` refuse a path that does not exist with ENOENT instead of handing back a watcher that polls forever and keeps the process alive. * Convert timestamps and validate arguments before the hook runs in `utimes`, `lutimes` and `readdir` (sync, callback and promise forms), so a mounted path gets the same ERR_INVALID_ARG_* errors and the same seconds-since-epoch numbers as a real one. * Pass the mode and times through to the `fchmod` and `futimes` hooks and route them to the handle's entry, so descriptor operations take effect like their path forms instead of being no-ops; the memory handle validates the way a FileHandle would since one calls it directly. * Treat a `mkdtemp` prefix as text rather than a path when it ends in a separator, so the directory is created inside the intended parent. * Map the first directory a recursive `mkdir` created back under the mount point instead of returning the provider-relative path. * Make disposing an already closed virtual `Dir` a no-op, as on the native `Dir`, instead of rejecting with ERR_DIR_CLOSED. test-vfs-fs-hook-gaps adds a test per gap, stating the real-fs outcome as the expectation. The existing file handle test asserted that `chmod()` and `utimes()` without arguments were no-ops; they now validate and apply, so it exercises that instead. Signed-off-by: Philipp Dunkel <pip@pipobscure.com>
pipobscure
force-pushed
the
vfs-fs-hook-gaps
branch
from
September 11, 2026 07:54
e04bc8b to
2ceca4a
Compare
Contributor
Author
|
Rebased on latest main since that moved significantly |
mcollina
reviewed
Sep 11, 2026
| @@ -0,0 +1,111 @@ | |||
| // Flags: --experimental-vfs | |||
Member
There was a problem hiding this comment.
Can you name this file differently? "hook gaps" has no context
Contributor
Author
There was a problem hiding this comment.
Renamed. Do you want me to split it into topics? watchers, argument validation and descriptor calls? Or leave it as a single file for now?
The test checks that `node:fs` calls on a mounted path give the same result as on a real filesystem. With the gaps closed, "hook gaps" no longer describes it, so name it for what every case compares against. Signed-off-by: Philipp Dunkel <pip@pipobscure.com>
pipobscure
force-pushed
the
vfs-fs-hook-gaps
branch
from
September 11, 2026 09:16
f3a66e0 to
c4a7af8
Compare
panva
approved these changes
Sep 12, 2026
Contributor
Failed to start CI- Validating Jenkins credentials ✔ Jenkins credentials valid - Querying data for job/node-test-pull-request/77227/ SyntaxError: Unexpected token '<', ..." https://github.com/nodejs/node/actions/runs/34683155451 |
This comment has been minimized.
This comment has been minimized.
Collaborator
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.
vfs: close gaps in the fs hooks for mounted paths
Several
node:fsentry points behave differently for a mounted paththan for a real one, because of how the call reaches the VFS hooks:
fs.watchFileandfs.promises.watchcall handler methods that donot exist, so they throw a TypeError instead of watching.
fs.watchon a path that does not exist returns a polling watcherinstead of throwing ENOENT, and that watcher keeps the process alive.
fs.utimesSyncandfs.readdirSyncconsult the hook beforevalidating
their arguments: numeric-string timestamps are ignored, an object
timestamp becomes NaN, and an invalid encoding is accepted.
fs.futimesSyncandfs.fchmodSyncon a virtual descriptor areno-ops while the path forms of the same operations work.
fs.mkdtempSyncwith a prefix ending in a separator creates thedirectory next to the intended parent, because the prefix is resolved
as a path before the suffix is appended.
fs.mkdirSync({ recursive: true })returns the provider-relative pathof the first directory created instead of the mounted path.
Dirasynchronously rejects withERR_DIR_CLOSED; the real
Dirtreats disposal as idempotent.Note: Since these are gaps/defects in existing functionality, I decided to create the failing tests first (first commit) and then add the fix/solution as a second commit. That way whoever wants to review this can first prove out the issue, before applying the solution.
This goes with the VFS work by @mcollina and the bug-fix PRs by @trivikr.