Skip to content

Latest commit

Β 

History

History
993 lines (710 loc) Β· 29.2 KB

File metadata and controls

993 lines (710 loc) Β· 29.2 KB

Virtual File System

Stability: 1 - Experimental

The node:vfs module provides a virtual file system that can be mounted alongside the real file system. Virtual files can be read using standard node:fs operations and loaded as modules using require() or import.

To access it:

import vfs from 'node:vfs';
const vfs = require('node:vfs');

This module is only available under the node: scheme.

Overview

The Virtual File System (VFS) allows you to create in-memory file systems that integrate seamlessly with the Node.js node:fs module and module loading system. This is useful for:

  • Bundling assets in Single Executable Applications (SEA)
  • Testing file system operations without touching the disk
  • Creating virtual module systems
  • Embedding configuration or data files in applications

Operating modes

The VFS supports two operating modes:

Standard mode (default)

When mounted at a path prefix (e.g., /virtual), the VFS handles all operations for paths starting with that prefix. The VFS completely shadows any real file system paths under the mount point.

Overlay mode

When created with { overlay: true }, the VFS selectively intercepts only paths that exist within the VFS. Paths that don't exist in the VFS fall through to the real file system. This is useful for mocking specific files while leaving others unchanged.

const vfs = require('node:vfs');
const fs = require('node:fs');

// Overlay mode: only intercept files that exist in VFS
const myVfs = vfs.create({ overlay: true });
myVfs.writeFileSync('/etc/config.json', JSON.stringify({ mocked: true }));
myVfs.mount('/');

// This reads from VFS (file exists in VFS)
fs.readFileSync('/etc/config.json', 'utf8'); // '{"mocked": true}'

// This reads from real FS (file doesn't exist in VFS)
fs.readFileSync('/etc/hostname', 'utf8'); // Real file content

See Security considerations for important warnings about overlay mode.

Basic usage

The following example shows how to create a virtual file system, add files, and access them through the standard node:fs API:

import vfs from 'node:vfs';
import fs from 'node:fs';

// Create a new virtual file system
const myVfs = vfs.create();

// Create directories and files
myVfs.mkdirSync('/app');
myVfs.writeFileSync('/app/config.json', JSON.stringify({ port: 3000 }));
myVfs.writeFileSync('/app/greet.js', 'module.exports = (name) => "Hello, " + name + "!";');

// Mount the VFS at a path prefix
myVfs.mount('/virtual');

// Now standard fs operations work on the virtual files
const config = JSON.parse(fs.readFileSync('/virtual/app/config.json', 'utf8'));
console.log(config.port); // 3000

// Modules can be required from the VFS
const greet = await import('/virtual/app/greet.js');
console.log(greet.default('World')); // Hello, World!

// Clean up
myVfs.unmount();
const vfs = require('node:vfs');
const fs = require('node:fs');

// Create a new virtual file system
const myVfs = vfs.create();

// Create directories and files
myVfs.mkdirSync('/app');
myVfs.writeFileSync('/app/config.json', JSON.stringify({ port: 3000 }));
myVfs.writeFileSync('/app/greet.js', 'module.exports = (name) => "Hello, " + name + "!";');

// Mount the VFS at a path prefix
myVfs.mount('/virtual');

// Now standard fs operations work on the virtual files
const config = JSON.parse(fs.readFileSync('/virtual/app/config.json', 'utf8'));
console.log(config.port); // 3000

// Modules can be required from the VFS
const greet = require('/virtual/app/greet.js');
console.log(greet('World')); // Hello, World!

// Clean up
myVfs.unmount();

Limitations

The VFS has the following limitations:

Native addons

Native addons (.node files) cannot be loaded from the VFS. Native addons must exist on the real file system because they are loaded by the operating system's dynamic linker, which cannot access virtual files.

Child processes

Child processes spawned via child_process.spawn(), child_process.exec(), or similar methods cannot directly access VFS files. The child process runs in a separate address space and does not inherit the parent's VFS mounts. To share data with child processes, write files to the real file system or use inter-process communication.

Worker threads

Each worker thread has its own independent VFS state. A VFS mounted in the main thread is not automatically available in worker threads. To use VFS in workers, create and mount a new VFS instance within each worker.

fs.watch limitations

The fs.watch() and fs.watchFile() functions work with VFS files but use polling internally rather than native file system notifications, since VFS files exist only in memory.

Code caching in SEA

When using VFS with Single Executable Applications, the useCodeCache option in the SEA configuration does not currently apply to modules loaded from the VFS. This is a current limitation due to incomplete implementation, not a technical impossibility. Consider bundling the application to enable code caching and do not rely on module loading in VFS.

vfs.create([provider][, options])

  • provider {VirtualProvider} Optional provider instance. Defaults to a new MemoryProvider.
  • options {Object}
    • moduleHooks {boolean} Whether to enable require()/import hooks for loading modules from the VFS. Default: true.
    • virtualCwd {boolean} Whether to enable virtual working directory support. Default: false.
    • overlay {boolean} Whether to enable overlay mode. In overlay mode, the VFS only intercepts paths that exist in the VFS, allowing other paths to fall through to the real file system. Useful for mocking specific files while leaving others unchanged. See Security considerations for important warnings. Default: false.
  • Returns: {VirtualFileSystem}

Creates a new VirtualFileSystem instance. If no provider is specified, a MemoryProvider is used, which stores files in memory.

import vfs from 'node:vfs';

// Create with default MemoryProvider
const memoryVfs = vfs.create();

// Create with explicit provider
const customVfs = vfs.create(new vfs.MemoryProvider());

// Create with options only
const vfsWithOptions = vfs.create({ moduleHooks: false });
const vfs = require('node:vfs');

// Create with default MemoryProvider
const memoryVfs = vfs.create();

// Create with explicit provider
const customVfs = vfs.create(new vfs.MemoryProvider());

// Create with options only
const vfsWithOptions = vfs.create({ moduleHooks: false });

Class: VirtualFileSystem

The VirtualFileSystem class provides a file system interface backed by a provider. It supports standard file system operations and can be mounted to make virtual files accessible through the node:fs module.

new VirtualFileSystem([provider][, options])

  • provider {VirtualProvider} The provider to use. Default: MemoryProvider.
  • options {Object}
    • moduleHooks {boolean} Enable module loading hooks. Default: true.
    • virtualCwd {boolean} Enable virtual working directory. Default: false.

Creates a new VirtualFileSystem instance.

Multiple VirtualFileSystem instances can be created and used independently. Each instance maintains its own file tree and can be mounted at different paths. However, only one VFS can be mounted at a given path prefix at a time. If two VFS instances are mounted at overlapping paths (e.g., /virtual and /virtual/sub), the more specific path takes precedence for matching paths.

vfs.chdir(path)

  • path {string} The new working directory path within the VFS.

Changes the virtual working directory. This only affects path resolution within the VFS when virtualCwd is enabled in the constructor options.

Throws ERR_INVALID_STATE if virtualCwd was not enabled during construction.

When mounted with virtualCwd enabled, the VFS also hooks process.chdir() and process.cwd() to support virtual paths transparently. In Worker threads, process.chdir() to virtual paths will work, but attempting to change to real file system paths will throw ERR_WORKER_UNSUPPORTED_OPERATION.

vfs.cwd()

  • Returns: {string|null}

Returns the current virtual working directory, or null if no virtual directory has been set yet.

Throws ERR_INVALID_STATE if virtualCwd was not enabled during construction.

vfs.mount(prefix)

  • prefix {string} The path prefix where the VFS will be mounted.
  • Returns: {VirtualFileSystem} The VFS instance (for chaining or using).

Mounts the virtual file system at the specified path prefix. After mounting, files in the VFS can be accessed via the node:fs module using paths that start with the prefix.

If a real file system path already exists at the mount prefix, the VFS shadows that path. All operations to paths under the mount prefix will be directed to the VFS, making the real files inaccessible until the VFS is unmounted. See Security considerations for important warnings about this behavior.

const vfs = require('node:vfs');

const myVfs = vfs.create();
myVfs.writeFileSync('/data.txt', 'Hello');
myVfs.mount('/virtual');

// Now accessible as /virtual/data.txt
require('node:fs').readFileSync('/virtual/data.txt', 'utf8'); // 'Hello'

On Windows, mount paths use drive letters:

const vfs = require('node:vfs');

const myVfs = vfs.create();
myVfs.writeFileSync('/data.txt', 'Hello');
myVfs.mount('C:\\virtual');

// Now accessible as C:\virtual\data.txt
require('node:fs').readFileSync('C:\\virtual\\data.txt', 'utf8'); // 'Hello'

The VFS supports the Explicit Resource Management proposal. Use the using declaration to automatically unmount when leaving scope:

const vfs = require('node:vfs');
const fs = require('node:fs');

{
  using myVfs = vfs.create();
  myVfs.writeFileSync('/data.txt', 'Hello');
  myVfs.mount('/virtual');

  fs.readFileSync('/virtual/data.txt', 'utf8'); // 'Hello'
} // VFS is automatically unmounted here

fs.existsSync('/virtual/data.txt'); // false - VFS is unmounted

vfs.mounted

  • {boolean}

Returns true if the VFS is currently mounted.

vfs.mountPoint

  • {string | null}

The current mount point as an absolute path, or null if not mounted.

vfs.overlay

  • {boolean}

Returns true if overlay mode is enabled. In overlay mode, the VFS only intercepts paths that exist in the VFS, allowing other paths to fall through to the real file system.

vfs.provider

  • {VirtualProvider}

The underlying provider for this VFS instance. Can be used to access provider-specific methods like setReadOnly() for MemoryProvider.

const vfs = require('node:vfs');

const myVfs = vfs.create();

// Access the provider
console.log(myVfs.provider.readonly); // false
myVfs.provider.setReadOnly();
console.log(myVfs.provider.readonly); // true

vfs.readonly

  • {boolean}

Returns true if the underlying provider is read-only.

vfs.unmount()

Unmounts the virtual file system. After unmounting, virtual files are no longer accessible through the node:fs module. The VFS can be remounted at the same or a different path by calling mount() again. Unmounting also resets the virtual working directory if one was set.

This method is idempotent: calling unmount() on an already unmounted VFS has no effect.

File System Methods

The VirtualFileSystem class provides methods that mirror the node:fs module API. All paths are relative to the VFS root (not the mount point).

These methods accept the same argument types as their node:fs counterparts, including string, Buffer, TypedArray, and DataView where applicable.

Overlay mode behavior

When overlay mode is enabled, the following behavior applies to node:fs operations on mounted paths.

Path encoding: The VFS uses UTF-8 encoding for file and directory names internally. In overlay mode, path matching is performed using the VFS's UTF-8 encoding. When falling through to the real file system, paths are passed to the native file system APIs which handle encoding according to platform conventions (UTF-8 on most Unix systems, UTF-16 on Windows). This means the VFS inherits the underlying file system's encoding behavior for paths that fall through, while VFS-internal paths always use UTF-8.

Case sensitivity: The VFS is always case-sensitive internally. In overlay mode, this can cause unexpected behavior when overlaying a case-insensitive file system (such as macOS HFS+ or Windows NTFS):

  • A VFS file at /Data.txt will not shadow a real file at /data.txt
  • Looking up /DATA.TXT will fall through to the real file system (not found in case-sensitive VFS), potentially finding a real file with different casing
  • This mismatch is intentional: the VFS maintains consistent cross-platform behavior rather than emulating the underlying file system's case handling

If case-insensitive matching is required, applications should normalize paths before VFS operations.

Operation routing:

  • Read operations (readFile, readdir, stat, lstat, access, exists, realpath, readlink): Check VFS first. If the path doesn't exist in VFS, fall through to the real file system.
  • Write operations (writeFile, appendFile, mkdir, rename, unlink, rmdir, symlink, copyFile): Always operate on VFS. New files are created in VFS, and attempting to modify a real file that doesn't exist in VFS will create a new VFS file instead.
  • File descriptors: Once a file is opened, all subsequent operations on that descriptor stay within the same layer (VFS or real FS) where it was opened.

Synchronous Methods

The VirtualFileSystem class supports all common synchronous node:fs methods for reading, writing, and managing files and directories. Methods mirror the node:fs module API.

The following node:fs sync methods have no VFS equivalent:

  • chmodSync() / fchmodSync() - VFS does not support permission changes
  • chownSync() / fchownSync() - VFS does not support ownership changes
  • truncateSync() / ftruncateSync() - Use writeFileSync() instead
  • utimesSync() / futimesSync() / lutimesSync() - VFS does not support changing timestamps
  • linkSync() - VFS does not support hard links (use symlinkSync())
  • fdatasyncSync() / fsyncSync() - Not applicable to in-memory storage

Promise Methods

All synchronous methods have promise-based equivalents available through vfs.promises:

import vfs from 'node:vfs';

const myVfs = vfs.create();

await myVfs.promises.writeFile('/data.txt', 'Hello');
const content = await myVfs.promises.readFile('/data.txt', 'utf8');
console.log(content); // 'Hello'
const vfs = require('node:vfs');

const myVfs = vfs.create();

async function example() {
  await myVfs.promises.writeFile('/data.txt', 'Hello');
  const content = await myVfs.promises.readFile('/data.txt', 'utf8');
  console.log(content); // 'Hello'
}

Class: VirtualProvider

The VirtualProvider class is an abstract base class for VFS providers. Providers implement the actual file system storage and operations.

provider.readonly

  • {boolean}

Returns true if the provider is read-only.

provider.supportsSymlinks

  • {boolean}

Returns true if the provider supports symbolic links.

Creating Custom Providers

To create a custom provider, extend VirtualProvider and implement the required methods:

const { VirtualProvider } = require('node:vfs');

class MyProvider extends VirtualProvider {
  get readonly() { return false; }
  get supportsSymlinks() { return true; }

  openSync(path, flags, mode) {
    // Implementation
  }

  statSync(path, options) {
    // Implementation
  }

  readdirSync(path, options) {
    // Implementation
  }

  // ... implement other required methods
}

Class: MemoryProvider

The MemoryProvider stores files in memory. It supports full read/write operations and symbolic links.

const { create, MemoryProvider } = require('node:vfs');

const myVfs = create(new MemoryProvider());

memoryProvider.setReadOnly()

Sets the provider to read-only mode. Once set to read-only, the provider cannot be changed back to writable. This is useful for finalizing a VFS after initial population.

const vfs = require('node:vfs');

const myVfs = vfs.create();

// Populate the VFS
myVfs.mkdirSync('/app');
myVfs.writeFileSync('/app/config.json', '{"readonly": true}');

// Make it read-only
myVfs.provider.setReadOnly();

// This would now throw an error
// myVfs.writeFileSync('/app/config.json', 'new content');

Class: RealFSProvider

The RealFSProvider wraps a real file system directory, allowing it to be mounted at a different VFS path. This is useful for:

  • Mounting a directory at a different path
  • Enabling virtualCwd support in Worker threads (by mounting the real file system through VFS)
  • Creating sandboxed views of real directories

new RealFSProvider(rootPath)

  • rootPath {string} The real file system path to use as the provider root.

Creates a new RealFSProvider that wraps the specified directory. All paths accessed through this provider are resolved relative to rootPath. Path traversal outside rootPath (via ..) is prevented for security.

import vfs from 'node:vfs';

// Mount /home/user/project at /project
const projectVfs = vfs.create(new vfs.RealFSProvider('/home/user/project'));
projectVfs.mount('/project');

// Now /project/src/index.js maps to /home/user/project/src/index.js
import fs from 'node:fs';
const content = fs.readFileSync('/project/src/index.js', 'utf8');
const vfs = require('node:vfs');

// Mount /home/user/project at /project
const projectVfs = vfs.create(new vfs.RealFSProvider('/home/user/project'));
projectVfs.mount('/project');

// Now /project/src/index.js maps to /home/user/project/src/index.js
const fs = require('node:fs');
const content = fs.readFileSync('/project/src/index.js', 'utf8');

realFSProvider.rootPath

  • {string}

The real file system path that this provider wraps.

Integration with node:fs module

When a VFS is mounted, the standard node:fs module automatically routes operations to the VFS for paths that match the mount prefix:

import vfs from 'node:vfs';
import fs from 'node:fs';

const myVfs = vfs.create();
myVfs.writeFileSync('/hello.txt', 'Hello from VFS!');
myVfs.mount('/virtual');

// These all work transparently
fs.readFileSync('/virtual/hello.txt', 'utf8');        // Sync
await fs.promises.readFile('/virtual/hello.txt', 'utf8');   // Promise
fs.createReadStream('/virtual/hello.txt');            // Stream

// Real file system is still accessible
fs.readFileSync('/etc/passwd');  // Real file
const vfs = require('node:vfs');
const fs = require('node:fs');

const myVfs = vfs.create();
myVfs.writeFileSync('/hello.txt', 'Hello from VFS!');
myVfs.mount('/virtual');

// These all work transparently
fs.readFileSync('/virtual/hello.txt', 'utf8');        // Sync
fs.promises.readFile('/virtual/hello.txt', 'utf8');   // Promise
fs.createReadStream('/virtual/hello.txt');            // Stream

// Real file system is still accessible
fs.readFileSync('/etc/passwd');  // Real file

Integration with module loading

Virtual files can be loaded as modules using require() or import:

const vfs = require('node:vfs');

const myVfs = vfs.create();
myVfs.writeFileSync('/math.js', `
  exports.add = (a, b) => a + b;
  exports.multiply = (a, b) => a * b;
`);
myVfs.mount('/modules');

const math = require('/modules/math.js');
console.log(math.add(2, 3)); // 5
import vfs from 'node:vfs';

const myVfs = vfs.create();
myVfs.writeFileSync('/greet.mjs', `
  export default function greet(name) {
    return \`Hello, \${name}!\`;
  }
`);
myVfs.mount('/modules');

const { default: greet } = await import('/modules/greet.mjs');
console.log(greet('World')); // Hello, World!

Implementation details

Stats objects

The VFS returns real {fs.Stats} objects from stat(), lstat(), and fstat() operations. These Stats objects behave identically to those returned by the real file system:

  • stats.isFile(), stats.isDirectory(), stats.isSymbolicLink() work correctly
  • stats.size reflects the actual content size
  • stats.mtime, stats.ctime, stats.birthtime are tracked per file
  • stats.mode includes the file type bits and permissions

Use with Single Executable Applications

When running as a Single Executable Application (SEA) with "useVfs": true in the SEA configuration, bundled assets are automatically mounted at /sea. No additional setup is required:

// In your SEA entry script
const fs = require('node:fs');

// Access bundled assets directly - they are automatically available at /sea
const config = JSON.parse(fs.readFileSync('/sea/config.json', 'utf8'));
const template = fs.readFileSync('/sea/templates/index.html', 'utf8');

See the Single Executable Applications documentation for more information on creating SEA builds with assets.

Symbolic links

The VFS supports symbolic links within the virtual file system. Symlinks are created using vfs.symlinkSync() or vfs.promises.symlink() and can point to files or directories within the same VFS.

Cross-boundary symlinks

Symbolic links in the VFS are VFS-internal only. They cannot:

  • Point from a VFS path to a real file system path
  • Point from a real file system path to a VFS path
  • Be followed across VFS mount boundaries

When resolving symlinks, the VFS only follows links that target paths within the same VFS instance. Attempts to create symlinks with absolute paths that would resolve outside the VFS are allowed but will result in dangling symlinks.

const vfs = require('node:vfs');

const myVfs = vfs.create();
myVfs.mkdirSync('/data');
myVfs.writeFileSync('/data/config.json', JSON.stringify({}));

// This works - symlink within VFS
myVfs.symlinkSync('/data/config.json', '/config');
myVfs.readFileSync('/config', 'utf8'); // '{}'

// This creates a dangling symlink - target doesn't exist in VFS
myVfs.symlinkSync('/etc/passwd', '/passwd-link');
// myVfs.readFileSync('/passwd-link'); // Throws ENOENT

Symlinks in overlay mode

In overlay mode ({ overlay: true }), VFS and real file system symlinks remain completely independent:

  • VFS symlinks can only target other VFS paths. A VFS symlink cannot point to a real file system file, even if that file exists at the same logical path.
  • Real file system symlinks can only target other real file system paths. A real symlink cannot point to a VFS file.
  • No cross-layer resolution occurs. When following a symlink, the resolution stays entirely within either the VFS layer or the real file system layer.
const vfs = require('node:vfs');
const fs = require('node:fs');

const myVfs = vfs.create({ overlay: true });
myVfs.mkdirSync('/data');
myVfs.writeFileSync('/data/config.json', JSON.stringify({ source: 'vfs' }));
myVfs.symlinkSync('/data/config.json', '/data/link');
myVfs.mount('/app');

// VFS symlink resolves within VFS
fs.readFileSync('/app/data/link', 'utf8'); // '{"source": "vfs"}'

// If /app/data/real-link is a real FS symlink pointing to /app/data/config.json,
// it will NOT resolve to the VFS file - it looks for a real file at that path

This design ensures predictable behavior: symlinks always resolve within their own layer, preventing unexpected interactions between virtual and real files.

Worker threads

VFS instances are not shared across worker threads. Each worker thread has its own V8 isolate and module cache, which means:

  • A VFS mounted in the main thread is not accessible from worker threads
  • Each worker thread must create and mount its own VFS instance
  • VFS data is not synchronized between threads - changes in one thread are not visible in another

If you need to share virtual file content with worker threads, you must either:

  1. Recreate the VFS in each worker - Pass the data to workers via workerData and have each worker create its own VFS:
const { Worker, isMainThread, workerData } = require('node:worker_threads');
const vfs = require('node:vfs');

if (isMainThread) {
  const fileData = { '/config.json': '{"key": "value"}' };
  new Worker(__filename, { workerData: fileData });
} else {
  // Worker: recreate VFS from passed data
  const myVfs = vfs.create();
  for (const [path, content] of Object.entries(workerData)) {
    myVfs.writeFileSync(path, content);
  }
  myVfs.mount('/virtual');
  // Now the worker has its own copy of the VFS
}
  1. Use RealFSProvider - If the data exists on the real file system, use RealFSProvider in each worker to mount the same directory.

Using virtualCwd in Worker threads

Since process.chdir() is not available in Worker threads, you can use RealFSProvider to enable virtual working directory support:

const { Worker, isMainThread, parentPort } = require('node:worker_threads');
const vfs = require('node:vfs');

if (isMainThread) {
  new Worker(__filename);
} else {
  // In worker: mount real file system with virtualCwd enabled
  const realVfs = vfs.create(
    new vfs.RealFSProvider('/home/user/project'),
    { virtualCwd: true },
  );
  realVfs.mount('/project');

  // Now we can use virtual chdir in the worker
  realVfs.chdir('/project/src');
  console.log(realVfs.cwd()); // '/project/src'
}

This limitation exists because implementing cross-thread VFS access would require moving the implementation to C++ with shared memory management, which significantly increases complexity. This may be addressed in future versions.

Security considerations

Path shadowing

When a VFS is mounted, it shadows any real file system paths under the mount prefix. This means:

  • Real files at the mount path become inaccessible
  • All operations are redirected to the VFS
  • Modules loaded from shadowed paths will use VFS content

This behavior can be exploited maliciously. A module could mount a VFS over critical system paths (like /etc on Unix or C:\Windows on Windows) and intercept sensitive operations:

// WARNING: Example of dangerous behavior - DO NOT DO THIS
const vfs = require('node:vfs');

const maliciousVfs = vfs.create();
maliciousVfs.writeFileSync('/passwd', 'malicious content');
maliciousVfs.mount('/etc');  // Shadows /etc/passwd!

// Now fs.readFileSync('/etc/passwd') returns 'malicious content'

Overlay mode risks

Overlay mode ({ overlay: true }) allows a VFS to selectively intercept file operations only for paths that exist in the VFS. While this is useful for mocking specific files in tests, it can also be exploited to covertly intercept access to specific files:

// WARNING: Example of dangerous behavior - DO NOT DO THIS
const vfs = require('node:vfs');

// Create an overlay VFS that intercepts a specific file
const spyVfs = vfs.create(new vfs.MemoryProvider(), { overlay: true });
spyVfs.writeFileSync('/etc/shadow', 'intercepted!');
spyVfs.mount('/');  // Mount at root with overlay mode

// Only /etc/shadow is intercepted, other files work normally
fs.readFileSync('/etc/passwd');  // Real file (works normally)
fs.readFileSync('/etc/shadow');  // Returns 'intercepted!' (mocked)

This is particularly dangerous because:

  • It is harder to detect than full path shadowing.
  • Only specific targeted files are affected.
  • Other operations appear to work normally.

Monitoring VFS mounts

To help detect unauthorized VFS usage, node:process emits events when a VFS is mounted or unmounted:

process.on('vfs-mount', (info) => {
  console.log(`VFS mounted at ${info.mountPoint}`);
  console.log(`  overlay: ${info.overlay}`);
  console.log(`  readonly: ${info.readonly}`);
});

process.on('vfs-unmount', (info) => {
  console.log(`VFS unmounted from ${info.mountPoint}`);
});

The event object contains:

  • mountPoint {string} The path where the VFS is mounted.
  • overlay {boolean} Whether overlay mode is enabled.
  • readonly {boolean} Whether the VFS is read-only.

Applications can use these events to log VFS activity, alert on suspicious mounts (such as mounting over system paths), or enforce security policies.

Recommendations

  • Audit dependencies: Be cautious of third-party modules that use VFS, as they could shadow important paths.
  • Use unique mount points: Mount VFS at paths that don't conflict with real file system paths, such as /@virtual or /vfs-{unique-id}.
  • Verify mount points: Before trusting file content from paths that could be shadowed, verify the mount state.
  • Limit VFS usage: Only use VFS in controlled environments where you trust all loaded modules.