Skip to content

Commit 8ab1fcc

Browse files
pipobscureaduh95
authored andcommitted
vfs: drop --vfs-mount, pin the load mount point
--vfs-mount mounted a source without running it, and shared one ordered list of sources with --vfs-load, so neither option could say which entry it had contributed: the entry point was recovered from the position of --vfs-load among the mounts, in a list NODE_OPTIONS could prepend to. Nothing needs more than one mount from the command line: a program that wants more can mount them itself through node:vfs, where it also gets the instance. Remove --vfs-mount, leaving --vfs-load with the single source it mounts and runs, and reserve layer 0 for that source, numbering the file systems a program mounts itself from 1. The source is then at the same mount point in every thread, whatever else that thread mounts - including a thread where a --require preload mounted a file system of its own first - so a path into it stays valid in a worker. A worker still does not run that entry point: it inherits the source but not the decision to load from it. A worker created with its own execArgv inherits neither, so the documentation now says that such a worker must be given --experimental-vfs and --vfs-load again to run a script from the mount, and that --experimental-vfs is also what makes node:vfs available to the worker's own code. ERR_VFS_INVALID_TARGET now names --vfs-load as the source's origin, and the startup test moves to test-vfs-load.js, with the cases that covered mounting without loading removed and cases for the reserved mount point added. Signed-off-by: Philipp Dunkel <pip@pipobscure.com> PR-URL: #66162 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
1 parent 9b2c5af commit 8ab1fcc

15 files changed

Lines changed: 250 additions & 383 deletions

‎doc/api/cli.md‎

Lines changed: 31 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -3783,56 +3783,18 @@ added: v26.10.0
37833783

37843784
Requires [`--experimental-vfs`][]. May be given at most once.
37853785

3786-
Mounts `source` exactly as [`--vfs-mount`][] does, and additionally runs the
3787-
entry point and all subsequent `require()`/`import` resolution against that
3788-
mount rather than the real file system. The entry point is taken from the mount
3789-
the same way `node <directory>` takes one: the mount's own `package.json`
3790-
`"main"`, or `index.js`. Any positional command-line argument is the program's
3791-
own (available from `process.argv[2]` onward), never an entry-point override.
3786+
Mounts `source` as a virtual file system ([`node:vfs`][]), and runs the entry
3787+
point and all subsequent `require()`/`import` resolution against that mount
3788+
rather than the real file system. The mount is placed at a reserved mount point
3789+
assigned by Node.js, so it never shadows real paths and no target can be
3790+
chosen. The entry point is taken from the mount the same way `node <directory>`
3791+
takes one: the mount's own `package.json` `"main"`, or `index.js`. Any
3792+
positional command-line argument is the program's own (available from
3793+
`process.argv[2]` onward), never an entry-point override.
37923794

37933795
`process.argv[1]` reports `source` rather than the reserved mount point, since
37943796
the mount point is an opaque implementation detail.
37953797

3796-
Mounting the same source twice mounts it twice, at two separate mount points.
3797-
The entry point then comes from the mount `--vfs-load` itself contributed, not
3798-
from an earlier `--vfs-mount` of the same source.
3799-
3800-
In worker threads `--vfs-load` mounts but does not load: a worker inherits the
3801-
same mounts, in the same order, and runs its own entry point.
3802-
3803-
`--vfs-load` is not permitted in [`NODE_OPTIONS`][]: which entry point runs is
3804-
the command line's decision, and the environment must not be able to redirect
3805-
it.
3806-
3807-
```console
3808-
$ node --experimental-vfs --vfs-load=app.zip
3809-
$ node --experimental-vfs --vfs-mount=lib.zip --vfs-load=app.zip
3810-
```
3811-
3812-
### `--vfs-mount=source`
3813-
3814-
<!-- YAML
3815-
added: v26.10.0
3816-
-->
3817-
3818-
* `source` {string} A directory or an archive file to mount.
3819-
3820-
Requires [`--experimental-vfs`][]. May be repeated to mount several sources.
3821-
3822-
Mounts `source` as a virtual file system ([`node:vfs`][]). Each mount is placed
3823-
at a reserved mount point assigned by Node.js, so mounts never shadow real
3824-
paths and no target can be chosen. Mounting alone does not change the entry
3825-
point; use [`--vfs-load`][] for the source to run from.
3826-
3827-
`--vfs-mount` and [`--vfs-load`][] mount in the order they are written, so
3828-
3829-
```console
3830-
$ node --experimental-vfs --vfs-mount=a --vfs-load=b --vfs-mount=c
3831-
```
3832-
3833-
mounts `a`, `b` and `c` in that order and runs `b`. Mounts contributed by
3834-
[`NODE_OPTIONS`][] are mounted before the command line's.
3835-
38363798
The provider backing a source is chosen from the source itself rather than from
38373799
its file name:
38383800

@@ -3845,6 +3807,29 @@ preloaded with [`--require`][] or [`--import`][]) are consulted first, in
38453807
reverse registration order, and may claim directories as well as files. If no
38463808
provider claims the source, Node.js exits with an error.
38473809

3810+
In worker threads `--vfs-load` mounts but does not load: a worker inherits the
3811+
mount and runs its own entry point, which may itself live in the mount.
3812+
3813+
The source is mounted at the same reserved mount point in every thread that
3814+
mounts it, whatever else that thread mounts, so a path into the mount means the
3815+
same thing in all of them.
3816+
3817+
A worker created with its own `execArgv` inherits none of the parent's options,
3818+
and so does not mount the source at all. To run a script from the mount, such a
3819+
worker must be given the same options again, `--experimental-vfs` and
3820+
`--vfs-load`; without them, that thread has no mount for the script to come
3821+
from, and the worker fails to load it. `--experimental-vfs` is also what makes
3822+
[`node:vfs`][] available to the worker's own code. A worker whose script comes
3823+
from anywhere else, such as the real file system, needs nothing added.
3824+
3825+
`--vfs-load` is not permitted in [`NODE_OPTIONS`][]: which entry point runs is
3826+
the command line's decision, and the environment must not be able to redirect
3827+
it.
3828+
3829+
```console
3830+
$ node --experimental-vfs --vfs-load=app.zip
3831+
```
3832+
38483833
### `--watch`
38493834

38503835
<!-- YAML
@@ -4285,7 +4270,6 @@ one is included in the list below.
42854270
* `--use-openssl-ca`
42864271
* `--use-system-ca`
42874272
* `--v8-pool-size`
4288-
* `--vfs-mount`
42894273
* `--watch-kill-signal`
42904274
* `--watch-path`
42914275
* `--watch-preserve-output`
@@ -4811,8 +4795,6 @@ node --stack-trace-limit=12 -p -e "Error.stackTraceLimit" # prints 12
48114795
[`--require`]: #-r---require-module
48124796
[`--use-env-proxy`]: #--use-env-proxy
48134797
[`--use-system-ca`]: #--use-system-ca
4814-
[`--vfs-load`]: #--vfs-loadsource
4815-
[`--vfs-mount`]: #--vfs-mountsource
48164798
[`AsyncLocalStorage`]: async_context.md#class-asynclocalstorage
48174799
[`Buffer`]: buffer.md#class-buffer
48184800
[`CRYPTO_secure_malloc_init`]: https://www.openssl.org/docs/man3.0/man3/CRYPTO_secure_malloc_init.html

‎doc/api/errors.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3548,7 +3548,7 @@ entry types are found.
35483548

35493549
### `ERR_VFS_INVALID_TARGET`
35503550

3551-
A `--vfs-mount` source does not exist, is neither a regular file nor a
3551+
A `--vfs-load` source does not exist, is neither a regular file nor a
35523552
directory, or is a source no provider claims.
35533553

35543554
<a id="ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING"></a>

‎doc/api/vfs.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ added: v26.10.0
106106
* `create` {Function} Called with the resolved path and its [`fs.Stats`][].
107107
Returns the {VirtualProvider} backing the source.
108108

109-
Registers a provider that [`--vfs-mount`][] can select for a source it
109+
Registers a provider that [`--vfs-load`][] can select for a source it
110110
recognizes, so a file format Node.js has no built-in provider for can still be
111111
mounted.
112112

@@ -118,7 +118,7 @@ source, the built-in providers handle it: a directory with
118118
[`RealFSProvider`][], and a file whose bytes are a ZIP archive with
119119
[`ZipProvider`][].
120120

121-
Providers must be registered before the mounts are created. Register from a
121+
Providers must be registered before the source is mounted. Register from a
122122
module preloaded with [`--require`][] or [`--import`][]:
123123

124124
```cjs
@@ -702,7 +702,7 @@ fields use synthetic but stable values:
702702
[Single Executable Application]: single-executable-applications.md
703703
[`--import`]: cli.md#--importmodule
704704
[`--require`]: cli.md#-r---require-module
705-
[`--vfs-mount`]: cli.md#--vfs-mountsource
705+
[`--vfs-load`]: cli.md#--vfs-loadsource
706706
[`MemoryProvider`]: #class-memoryprovider
707707
[`RealFSProvider`]: #class-realfsprovider
708708
[`VirtualFileSystem`]: #class-virtualfilesystem

‎doc/node.1‎

Lines changed: 28 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1201,7 +1201,7 @@ Legacy alias for \fB--no-require-module\fR.
12011201
Disable the experimental \fBnode:sqlite\fR module.
12021202
.
12031203
.It Fl -no-experimental-websocket
1204-
Disable exposition of \fB<WebSocket>\fR on the global scope.
1204+
Disable exposition of \fB{WebSocket}\fR on the global scope.
12051205
.
12061206
.It Fl -no-experimental-webstorage
12071207
Disable \fBWeb Storage\fR support.
@@ -1891,46 +1891,19 @@ Print node's version.
18911891
.It Fl -vfs-load Ns = Ns Ar source
18921892
.Bl -bullet
18931893
.It
1894-
\fBsource\fR \fB<string>\fR A directory or an archive file to mount and run.
1894+
\fBsource\fR \fB{string}\fR A directory or an archive file to mount and run.
18951895
.El
18961896
Requires \fB--experimental-vfs\fR. May be given at most once.
1897-
Mounts \fBsource\fR exactly as \fB--vfs-mount\fR does, and additionally runs the
1898-
entry point and all subsequent \fBrequire()\fR/\fBimport\fR resolution against that
1899-
mount rather than the real file system. The entry point is taken from the mount
1900-
the same way \fBnode <directory>\fR takes one: the mount's own \fBpackage.json\fR
1901-
\fB"main"\fR, or \fBindex.js\fR. Any positional command-line argument is the program's
1902-
own (available from \fBprocess.argv[2]\fR onward), never an entry-point override.
1897+
Mounts \fBsource\fR as a virtual file system (\fBnode:vfs\fR), and runs the entry
1898+
point and all subsequent \fBrequire()\fR/\fBimport\fR resolution against that mount
1899+
rather than the real file system. The mount is placed at a reserved mount point
1900+
assigned by Node.js, so it never shadows real paths and no target can be
1901+
chosen. The entry point is taken from the mount the same way \fBnode <directory>\fR
1902+
takes one: the mount's own \fBpackage.json\fR \fB"main"\fR, or \fBindex.js\fR. Any
1903+
positional command-line argument is the program's own (available from
1904+
\fBprocess.argv[2]\fR onward), never an entry-point override.
19031905
\fBprocess.argv[1]\fR reports \fBsource\fR rather than the reserved mount point, since
19041906
the mount point is an opaque implementation detail.
1905-
Mounting the same source twice mounts it twice, at two separate mount points.
1906-
The entry point then comes from the mount \fB--vfs-load\fR itself contributed, not
1907-
from an earlier \fB--vfs-mount\fR of the same source.
1908-
In worker threads \fB--vfs-load\fR mounts but does not load: a worker inherits the
1909-
same mounts, in the same order, and runs its own entry point.
1910-
\fB--vfs-load\fR is not permitted in \fBNODE_OPTIONS\fR: which entry point runs is
1911-
the command line's decision, and the environment must not be able to redirect
1912-
it.
1913-
.Bd -literal
1914-
$ node --experimental-vfs --vfs-load=app.zip
1915-
$ node --experimental-vfs --vfs-mount=lib.zip --vfs-load=app.zip
1916-
.Ed
1917-
.
1918-
.It Fl -vfs-mount Ns = Ns Ar source
1919-
.Bl -bullet
1920-
.It
1921-
\fBsource\fR \fB<string>\fR A directory or an archive file to mount.
1922-
.El
1923-
Requires \fB--experimental-vfs\fR. May be repeated to mount several sources.
1924-
Mounts \fBsource\fR as a virtual file system (\fBnode:vfs\fR). Each mount is placed
1925-
at a reserved mount point assigned by Node.js, so mounts never shadow real
1926-
paths and no target can be chosen. Mounting alone does not change the entry
1927-
point; use \fB--vfs-load\fR for the source to run from.
1928-
\fB--vfs-mount\fR and \fB--vfs-load\fR mount in the order they are written, so
1929-
.Bd -literal
1930-
$ node --experimental-vfs --vfs-mount=a --vfs-load=b --vfs-mount=c
1931-
.Ed
1932-
mounts \fBa\fR, \fBb\fR and \fBc\fR in that order and runs \fBb\fR. Mounts contributed by
1933-
\fBNODE_OPTIONS\fR are mounted before the command line's.
19341907
The provider backing a source is chosen from the source itself rather than from
19351908
its file name:
19361909
.Bl -bullet
@@ -1944,6 +1917,24 @@ Providers registered with \fBvfs.registerProvider()\fR (typically from a module
19441917
preloaded with \fB--require\fR or \fB--import\fR) are consulted first, in
19451918
reverse registration order, and may claim directories as well as files. If no
19461919
provider claims the source, Node.js exits with an error.
1920+
In worker threads \fB--vfs-load\fR mounts but does not load: a worker inherits the
1921+
mount and runs its own entry point, which may itself live in the mount.
1922+
The source is mounted at the same reserved mount point in every thread that
1923+
mounts it, whatever else that thread mounts, so a path into the mount means the
1924+
same thing in all of them.
1925+
A worker created with its own \fBexecArgv\fR inherits none of the parent's options,
1926+
and so does not mount the source at all. To run a script from the mount, such a
1927+
worker must be given the same options again, \fB--experimental-vfs\fR and
1928+
\fB--vfs-load\fR; without them, that thread has no mount for the script to come
1929+
from, and the worker fails to load it. \fB--experimental-vfs\fR is also what makes
1930+
\fBnode:vfs\fR available to the worker's own code. A worker whose script comes
1931+
from anywhere else, such as the real file system, needs nothing added.
1932+
\fB--vfs-load\fR is not permitted in \fBNODE_OPTIONS\fR: which entry point runs is
1933+
the command line's decision, and the environment must not be able to redirect
1934+
it.
1935+
.Bd -literal
1936+
$ node --experimental-vfs --vfs-load=app.zip
1937+
.Ed
19471938
.
19481939
.It Fl -watch
19491940
Starts Node.js in watch mode.
@@ -2436,8 +2427,6 @@ one is included in the list below.
24362427
.It
24372428
\fB--v8-pool-size\fR
24382429
.It
2439-
\fB--vfs-mount\fR
2440-
.It
24412430
\fB--watch-kill-signal\fR
24422431
.It
24432432
\fB--watch-path\fR

‎lib/internal/errors.js‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1959,7 +1959,7 @@ E('ERR_USE_AFTER_CLOSE', '%s was closed', Error);
19591959
E('ERR_VALID_PERFORMANCE_ENTRY_TYPE',
19601960
'At least one valid performance entry type is required', Error);
19611961
E('ERR_VFS_INVALID_TARGET',
1962-
'%s is not a valid --vfs-mount source: must be an existing file or directory', Error);
1962+
'%s is not a valid --vfs-load source: must be an existing file or directory', Error);
19631963
E('ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING',
19641964
'A dynamic import callback was not specified.', TypeError);
19651965
E('ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING_FLAG',

‎lib/internal/main/worker_thread.js‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,8 @@ port.on('message', (message) => {
145145
// initializeAsyncLoaderHooksOnLoaderHookWorker() which needs to run preloads
146146
// after the asynchronous loader hooks are registered.
147147
initializeModuleLoaders({ shouldSpawnLoaderHookWorker: true, shouldPreloadModules: true });
148-
// Re-mount inherited --vfs-mount sources so their reserved paths (which a
149-
// worker filename may point into) resolve in this thread too. With
148+
// Re-mount the inherited --vfs-load source so its reserved path (which a
149+
// worker filename may point into) resolves in this thread too. With
150150
// --import, mounting is deferred to after that loop in run_main, matching
151151
// the main thread; finishVfsMounts() is idempotent so it runs once.
152152
if (getOptionValue('--import').length === 0) {

0 commit comments

Comments
 (0)