From 6eb09bf5cfea4a85a57e92281463d127510f9926 Mon Sep 17 00:00:00 2001 From: Philipp Dunkel Date: Mon, 21 Sep 2026 12:34:47 +0200 Subject: [PATCH] vfs: make the reserved root readable through fs The reserved root `${os.devNull}/vfs`, which holds the mount points of all virtual file systems, could not be read: fs calls on it fell through to the real file system, so nothing could list what was mounted. While any file system is mounted, serve the root as a read-only directory. It lists every mount point by the last segment of its path, a recursive listing descends into each mounted file system, and paths under it that no mount serves report ENOENT. Creating, removing or changing entries in it fails with EROFS. When nothing is mounted it does not exist, as before. Add vfs.vfsBase(), which returns the path of that directory, so that a program can read it without spelling the path out. A mount point cannot be removed or renamed, nor replaced by a rename: rmdir() and rename() fail with EBUSY, and a recursive rm() empties the file system and then fails the same way. Before, rmdir() of an empty mount point reported success without doing anything. The callback and promise forms of readdir() with `withFileTypes` now report each Dirent's parentPath as a host path, as readdirSync() did, instead of the provider-relative one, and split recursive names such as `dir/file.txt` into their directory and base name. A recursive listing joins subdirectories with the host separator rather than `/`, which mixed separators on Windows. realpath() of a mount point no longer returns it with a trailing separator. Signed-off-by: Philipp Dunkel --- doc/api/vfs.md | 67 +++++++- lib/internal/vfs/errors.js | 12 ++ lib/internal/vfs/file_system.js | 87 +++++++--- lib/internal/vfs/root.js | 213 ++++++++++++++++++++++++ lib/internal/vfs/router.js | 15 ++ lib/internal/vfs/setup.js | 72 ++++++-- lib/vfs.js | 11 ++ test/parallel/test-vfs-reserved-root.js | 208 +++++++++++++++++++++++ 8 files changed, 645 insertions(+), 40 deletions(-) create mode 100644 lib/internal/vfs/root.js create mode 100644 test/parallel/test-vfs-reserved-root.js diff --git a/doc/api/vfs.md b/doc/api/vfs.md index f12ca239e166..8d65d8f08c2f 100644 --- a/doc/api/vfs.md +++ b/doc/api/vfs.md @@ -152,6 +152,29 @@ $ node --experimental-vfs --require ./provider.js \ --vfs-load archive.customfmt ``` +## `vfs.vfsBase()` + + + +* Returns: {string} The absolute path of the [reserved root directory][]. + +Returns the directory that holds the mount points of every mounted virtual file +system, which is `path.join(os.devNull, 'vfs')`. Reading it lists what is +mounted; see [The reserved root directory][reserved root directory]. + +```cjs +const vfs = require('node:vfs'); +const fs = require('node:fs'); + +const myVfs = vfs.create(); +const mountPoint = myVfs.mount(); + +fs.readdirSync(vfs.vfsBase()); // The name of every mount point in it +mountPoint.startsWith(vfs.vfsBase()); // true +``` + ## Class: `VirtualFileSystem`