Commit 3f0c2e4c8ba for nodejs
commit 3f0c2e4c8ba02464b3ecb5cde371471714ac378e
Author: Antoine du Hamel <duhamelantoine1995@gmail.com>
Date: Tue Oct 13 15:48:49 2026 +0200
Revert "sea: mount vfsArchive at layer 0 and in workers"
This reverts commit ac9d85c4188369a866a02fbe896b56323510d657.
PR-URL: https://github.com/nodejs/node/pull/66706
Fixes: https://github.com/nodejs/node/issues/66703
Reviewed-By: Richard Lau <richard.lau@ibm.com>
Reviewed-By: Stewart X Addison <sxa@redhat.com>
Reviewed-By: Filip Skokan <panva.ip@gmail.com>
Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
diff --git a/doc/api/cli.md b/doc/api/cli.md
index 4e0dc709d10..fcca81b1598 100644
--- a/doc/api/cli.md
+++ b/doc/api/cli.md
@@ -3946,10 +3946,6 @@ from anywhere else, such as the real file system, needs nothing added.
the command line's decision, and the environment must not be able to redirect
it.
-A [single executable application][] built with `"vfsArchive"` mounts that
-archive at the mount point reserved for `--vfs-load`, so `--vfs-load` cannot be
-used in it.
-
```console
$ node --experimental-vfs --vfs-load=app.zip
```
diff --git a/doc/api/single-executable-applications.md b/doc/api/single-executable-applications.md
index 6f35f28d4b9..e5fa839a17f 100644
--- a/doc/api/single-executable-applications.md
+++ b/doc/api/single-executable-applications.md
@@ -296,27 +296,6 @@ executable only embeds the archive; read the files through the file system
APIs instead. `"vfsArchive"` requires `"useVfs": true` and cannot be
combined with `"assets"`.
-The archive is mounted at the mount point reserved for [`--vfs-load`][], which
-is the same in every thread, and worker threads mount it as well. A path into
-the archive, such as the main script's `__filename`, can therefore be loaded
-in a worker:
-
-```cjs
-const { Worker, isMainThread } = require('node:worker_threads');
-
-if (isMainThread) {
- new Worker(__filename);
-} else {
- // The worker runs the same script, from the same archive.
- console.log(require('./lib/math.js').add(2, 3));
-}
-```
-
-Since that mount point is taken by the archive, `--vfs-load` cannot be used
-with `"vfsArchive"`: `--build-sea` rejects it in `"execArgv"`, and the
-executable rejects it at startup when it comes from `--node-options` or from
-the `execArgv` of a worker.
-
#### Snapshot and code caching limitations
`"useVfs": true` cannot be used together with `"useSnapshot": true` or
@@ -807,7 +786,6 @@ to help us document them.
[Using native addons in the injected main script]: #using-native-addons-in-the-injected-main-script
[VFS documentation]: vfs.md
[Windows SDK]: https://developer.microsoft.com/en-us/windows/downloads/windows-sdk/
-[`--vfs-load`]: cli.md#--vfs-loadsource
[`node:zlib`]: zlib.md
[`process.execPath`]: process.md#processexecpath
[`require()`]: modules.md#requireid
diff --git a/doc/api/vfs.md b/doc/api/vfs.md
index ecadd05d75b..3da056de5c0 100644
--- a/doc/api/vfs.md
+++ b/doc/api/vfs.md
@@ -223,7 +223,7 @@ const fs = require('node:fs');
const myVfs = vfs.create();
myVfs.writeFileSync('/data.txt', 'Hello');
const mountPoint = myVfs.mount();
-// e.g. '/dev/null/vfs/1'
+// e.g. '/dev/null/vfs/0'
fs.readFileSync(`${mountPoint}/data.txt`, 'utf8'); // 'Hello'
```
diff --git a/doc/node.1 b/doc/node.1
index e7b84c16149..d7da9e120a9 100644
--- a/doc/node.1
+++ b/doc/node.1
@@ -2012,9 +2012,6 @@ from anywhere else, such as the real file system, needs nothing added.
\fB--vfs-load\fR is not permitted in \fBNODE_OPTIONS\fR: which entry point runs is
the command line's decision, and the environment must not be able to redirect
it.
-A single executable application built with \fB"vfsArchive"\fR mounts that
-archive at the mount point reserved for \fB--vfs-load\fR, so \fB--vfs-load\fR cannot be
-used in it.
.Bd -literal
$ node --experimental-vfs --vfs-load=app.zip
.Ed
diff --git a/lib/internal/main/embedding.js b/lib/internal/main/embedding.js
index ff5aad4c17f..cb88a443c40 100644
--- a/lib/internal/main/embedding.js
+++ b/lib/internal/main/embedding.js
@@ -16,6 +16,7 @@ const {
isExperimentalSeaWarningNeeded,
isSea,
isVfsEnabled,
+ mainCodePath: seaMainCodePath,
} = internalBinding('sea');
const { emitExperimentalWarning } = require('internal/util');
const { emitWarningSync } = require('internal/process/warning');
@@ -133,12 +134,13 @@ function embedderRunESM(content, filename) {
* @returns {string|null} The VFS path of the main script
*/
function setUpSeaVfs(content) {
- const { initSeaVfs, getSeaMainName } = require('internal/vfs/sea');
- const seaVfs = initSeaVfs(content);
+ const mainName = path.basename(seaMainCodePath || process.execPath);
+ const { initSeaVfs } = require('internal/vfs/sea');
+ const seaVfs = initSeaVfs({ extraFiles: { [mainName]: content } });
if (seaVfs === null) {
return null;
}
- return path.join(seaVfs.mountPoint, getSeaMainName());
+ return path.join(seaVfs.mountPoint, mainName);
}
/* c8 ignore stop */
diff --git a/lib/internal/main/worker_thread.js b/lib/internal/main/worker_thread.js
index 95904bb4ae6..e2b30de754a 100644
--- a/lib/internal/main/worker_thread.js
+++ b/lib/internal/main/worker_thread.js
@@ -152,9 +152,6 @@ port.on('message', (message) => {
if (getOptionValue('--import').length === 0) {
finishVfsMounts();
}
- // Unlike the --vfs-load source, a SEA archive needs no provider that a
- // preload could register, so its mount is never deferred.
- require('internal/vfs/sea').initSeaVfsInWorker();
}
if (!hasStdin)
diff --git a/lib/internal/vfs/sea.js b/lib/internal/vfs/sea.js
index 4bf2273c8bb..0aafa7705f7 100644
--- a/lib/internal/vfs/sea.js
+++ b/lib/internal/vfs/sea.js
@@ -9,9 +9,8 @@ const {
isVfsEnabled,
isVfsArchiveEnabled,
getAsset,
- getMainCode,
- mainCodePath,
} = internalBinding('sea');
+const { kEmptyObject } = require('internal/util');
const {
codes: {
ERR_INVALID_STATE,
@@ -30,12 +29,14 @@ let initialized = false;
* SEA main script is placed at the mount point root so bundled code can
* reach the assets through `__dirname`-relative paths and relative
* `require()` calls.
- * @param {string|Buffer} mainCode The source of the SEA main script
+ * @param {object} [options] Configuration options
+ * @param {Record<string, string|Buffer>} [options.extraFiles] Additional
+ * files to serve alongside the assets (used for the SEA main script)
* @returns {VirtualFileSystem|null} The mounted VFS, or null if not running
* as a SEA or VFS is not enabled in the SEA configuration
* @throws {ERR_INVALID_STATE} If already initialized
*/
-function initSeaVfs(mainCode) {
+function initSeaVfs(options = kEmptyObject) {
if (initialized) {
throw new ERR_INVALID_STATE('SEA VFS is already initialized');
}
@@ -45,51 +46,25 @@ function initSeaVfs(mainCode) {
return null;
}
- const { VirtualFileSystem, kLoadLayer } = require('internal/vfs/file_system');
+ const { VirtualFileSystem } = require('internal/vfs/file_system');
- const extraFiles = { __proto__: null, [getSeaMainName()]: mainCode };
- const isArchive = isVfsArchiveEnabled();
let provider;
- if (isArchive) {
- provider = createZipProvider(extraFiles);
+ if (isVfsArchiveEnabled()) {
+ provider = createZipProvider(options.extraFiles);
} else {
const { SEAProvider } = require('internal/vfs/providers/sea');
- provider = new SEAProvider({ extraFiles });
+ provider = new SEAProvider({ extraFiles: options.extraFiles });
}
// The SEA warning already covers the feature; don't emit the
// VirtualFileSystem experimental warning for the implicit SEA mount.
- // An archive is mounted where --vfs-load mounts its source, which is the
- // same mount point in every thread, so a worker can reach the code in it.
const vfs = new VirtualFileSystem(provider, {
- __proto__: null,
emitExperimentalWarning: false,
- [kLoadLayer]: isArchive,
});
vfs.mount();
return vfs;
}
-/**
- * Mounts the SEA virtual file system in a worker, when it is backed by a
- * `"vfsArchive"`, so that a path into the mount in the main thread, such as
- * the main script's `__filename`, is also valid in the worker.
- */
-function initSeaVfsInWorker() {
- if (isVfsArchiveEnabled()) {
- initSeaVfs(getMainCode());
- }
-}
-
-/**
- * The name under which the SEA main script is placed at the mount point root.
- * @returns {string}
- */
-function getSeaMainName() {
- const { basename } = require('path');
- return basename(mainCodePath || process.execPath);
-}
-
// The reserved asset key under which --build-sea stores the ZIP archive
// named by "vfsArchive". Must match kVfsArchiveAssetName in src/node_sea.cc.
const kVfsArchiveAssetName = 'node:sea:vfs.zip';
@@ -130,7 +105,5 @@ function createZipProvider(extraFiles) {
/* c8 ignore stop */
module.exports = {
- getSeaMainName,
initSeaVfs,
- initSeaVfsInWorker,
};
diff --git a/src/node_options.cc b/src/node_options.cc
index d7373bf0618..acd98e6b927 100644
--- a/src/node_options.cc
+++ b/src/node_options.cc
@@ -316,16 +316,6 @@ void EnvironmentOptions::CheckOptions(std::vector<std::string>* errors,
errors->push_back("either --check or --eval can be used, not both");
}
-#ifndef DISABLE_SINGLE_EXECUTABLE_APPLICATION
- // The archive takes the mount point reserved for the --vfs-load source.
- if (!vfs_load_source.empty() && sea::IsSingleExecutable() &&
- sea::FindSingleExecutableResource().use_vfs_archive()) {
- errors->push_back(
- "--vfs-load cannot be used in a single executable application "
- "built with \"vfsArchive\"");
- }
-#endif // DISABLE_SINGLE_EXECUTABLE_APPLICATION
-
for (const std::string& pattern : permission::ParseEnvAllowList(allow_env)) {
if (!permission::IsValidEnvAllowPattern(pattern)) {
errors->push_back("--allow-env must be '*', a variable name, or a "
diff --git a/src/node_sea.cc b/src/node_sea.cc
index 975df52d6ef..065ec8285d9 100644
--- a/src/node_sea.cc
+++ b/src/node_sea.cc
@@ -250,10 +250,6 @@ bool SeaResource::use_code_cache() const {
return static_cast<bool>(flags & SeaFlags::kUseCodeCache);
}
-bool SeaResource::use_vfs_archive() const {
- return static_cast<bool>(flags & SeaFlags::kVfsArchive);
-}
-
const SeaResource& FindSingleExecutableResource() {
static const SeaResource sea_resource = []() -> SeaResource {
std::string_view blob = FindSingleExecutableBlob();
@@ -281,8 +277,12 @@ void IsVfsEnabled(const FunctionCallbackInfo<Value>& args) {
}
void IsVfsArchiveEnabled(const FunctionCallbackInfo<Value>& args) {
- args.GetReturnValue().Set(IsSingleExecutable() &&
- FindSingleExecutableResource().use_vfs_archive());
+ bool enabled = false;
+ if (IsSingleExecutable()) {
+ const SeaResource& sea_resource = FindSingleExecutableResource();
+ enabled = static_cast<bool>(sea_resource.flags & SeaFlags::kVfsArchive);
+ }
+ args.GetReturnValue().Set(enabled);
}
void IsExperimentalSeaWarningNeeded(const FunctionCallbackInfo<Value>& args) {
@@ -636,15 +636,6 @@ std::optional<SeaConfig> ParseSingleExecutableConfig(
config_path);
return std::nullopt;
}
- // The archive takes the mount point reserved for the --vfs-load source.
- for (const std::string& arg : result.exec_argv) {
- if (arg == "--vfs-load" || arg.starts_with("--vfs-load=")) {
- FPrintF(stderr,
- "\"vfsArchive\" cannot be used together with --vfs-load in "
- "\"execArgv\"\n");
- return std::nullopt;
- }
- }
// The archive is embedded as a single reserved asset.
result.flags |= SeaFlags::kIncludeAssets;
}
@@ -975,20 +966,6 @@ void GetAssetKeys(const FunctionCallbackInfo<Value>& args) {
args.GetReturnValue().Set(result);
}
-// A worker has no main script handed to it, but places the SEA main script in
-// its virtual file system just as the main thread does.
-void GetMainCode(const FunctionCallbackInfo<Value>& args) {
- CHECK_EQ(args.Length(), 0);
- const SeaResource& sea_resource = FindSingleExecutableResource();
- CHECK(!sea_resource.use_snapshot());
- Local<Value> code;
- if (ToV8Value(args.GetIsolate()->GetCurrentContext(),
- sea_resource.main_code_or_snapshot)
- .ToLocal(&code)) {
- args.GetReturnValue().Set(code);
- }
-}
-
MaybeLocal<Value> LoadSingleExecutableApplication(
const StartExecutionCallbackInfoWithModule& info) {
// Here we are currently relying on the fact that in NodeMainInstance::Run(),
@@ -1071,7 +1048,6 @@ void Initialize(Local<Object> target,
IsExperimentalSeaWarningNeeded);
SetMethod(context, target, "getAsset", GetAsset);
SetMethod(context, target, "getAssetKeys", GetAssetKeys);
- SetMethod(context, target, "getMainCode", GetMainCode);
}
void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
@@ -1081,7 +1057,6 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
registry->Register(IsExperimentalSeaWarningNeeded);
registry->Register(GetAsset);
registry->Register(GetAssetKeys);
- registry->Register(GetMainCode);
}
} // namespace sea
diff --git a/src/node_sea.h b/src/node_sea.h
index b4c852eff4e..32879e90227 100644
--- a/src/node_sea.h
+++ b/src/node_sea.h
@@ -64,7 +64,6 @@ struct SeaResource {
bool use_snapshot() const;
bool use_code_cache() const;
- bool use_vfs_archive() const;
static constexpr size_t kHeaderSize = sizeof(kMagic) + sizeof(SeaFlags) +
sizeof(SeaExecArgvExtension) +
diff --git a/test/fixtures/sea/vfs-zip/sea-config-vfs-load-rejected.json b/test/fixtures/sea/vfs-zip/sea-config-vfs-load-rejected.json
deleted file mode 100644
index 6b42595460b..00000000000
--- a/test/fixtures/sea/vfs-zip/sea-config-vfs-load-rejected.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "main": "sea.js",
- "output": "sea-vfs-load-rejected",
- "useVfs": true,
- "vfsArchive": "assets.zip",
- "execArgvExtension": "cli"
-}
diff --git a/test/fixtures/sea/vfs-zip/sea.js b/test/fixtures/sea/vfs-zip/sea.js
index 439728c78e1..eb26e0f5965 100644
--- a/test/fixtures/sea/vfs-zip/sea.js
+++ b/test/fixtures/sea/vfs-zip/sea.js
@@ -2,19 +2,6 @@
const fs = require('fs');
const path = require('path');
const assert = require('assert');
-const os = require('os');
-const { Worker, isMainThread, parentPort } = require('worker_threads');
-
-if (!isMainThread) {
- // A worker mounts the archive too, at the same mount point, so the main
- // script and the code next to it can be loaded from there.
- parentPort.postMessage({
- dirname: __dirname,
- greeting: fs.readFileSync(path.join(__dirname, 'data', 'greeting.txt'), 'utf8'),
- sum: require('./modules/math.js').add(2, 3),
- });
- return;
-}
// With "useVfsZip", the bundled assets are stored as a single ZIP archive
// and mounted through the ZipProvider. Everything is reachable through
@@ -26,9 +13,6 @@ assert.strictEqual(path.basename(__filename), 'sea.js');
assert.strictEqual(require.main, module);
console.log('main script runs from', __filename);
-// The archive is mounted at the mount point reserved for --vfs-load.
-assert.strictEqual(__dirname, path.join(os.devNull, 'vfs', '0'));
-
// The main script itself is readable through fs.
assert.strictEqual(fs.existsSync(__filename), true);
@@ -74,17 +58,4 @@ assert.notStrictEqual(a, b);
a[0] = 0;
assert.strictEqual(b.toString('utf8'), 'Hello from SEA VFS!');
-// The mount point is taken by the archive, so a worker cannot --vfs-load.
-assert.throws(() => new Worker(__filename, {
- execArgv: ['--experimental-vfs', `--vfs-load=${__dirname}`],
-}), { code: 'ERR_WORKER_INVALID_EXEC_ARGV' });
-
-const worker = new Worker(__filename);
-worker.once('message', (message) => {
- assert.deepStrictEqual(message, {
- dirname: __dirname,
- greeting: 'Hello from SEA VFS!',
- sum: 5,
- });
- console.log('All SEA VFS zip tests passed!');
-});
+console.log('All SEA VFS zip tests passed!');
diff --git a/test/sea/test-build-sea-vfs-incompatible-options.js b/test/sea/test-build-sea-vfs-incompatible-options.js
index bd51ee12c16..68bd520cb50 100644
--- a/test/sea/test-build-sea-vfs-incompatible-options.js
+++ b/test/sea/test-build-sea-vfs-incompatible-options.js
@@ -141,24 +141,3 @@ skipIfBuildSEAIsNotSupported();
stderr: /"vfsArchive" field of .*vfs-archive-not-string\.json is not a string/,
});
}
-
-// Test: "vfsArchive" combined with --vfs-load in "execArgv"
-for (const arg of ['--vfs-load=assets.zip', '--vfs-load']) {
- tmpdir.refresh();
- const config = tmpdir.resolve('vfs-archive-vfs-load.json');
- writeFileSync(config, JSON.stringify({
- main: 'bundle.js',
- output: 'sea',
- useVfs: true,
- vfsArchive: 'assets.zip',
- execArgv: ['--experimental-vfs', arg],
- }), 'utf8');
- spawnSyncAndAssert(
- process.execPath,
- ['--build-sea', config], {
- cwd: tmpdir.path,
- }, {
- status: 1,
- stderr: /"vfsArchive" cannot be used together with --vfs-load in "execArgv"/,
- });
-}
diff --git a/test/sea/test-single-executable-application-vfs-zip.js b/test/sea/test-single-executable-application-vfs-zip.js
index 6165d567de5..57282f9022b 100644
--- a/test/sea/test-single-executable-application-vfs-zip.js
+++ b/test/sea/test-single-executable-application-vfs-zip.js
@@ -57,25 +57,4 @@ main().then(common.mustCall(() => {
},
},
);
-
- // The archive is mounted where --vfs-load would mount its source, so the
- // executable rejects --vfs-load at startup. --build-sea already refuses it
- // in "execArgv", so pass it through --node-options instead.
- const rejectedFile = buildSEA(fixtures.path('sea', 'vfs-zip'), {
- configPath: 'sea-config-vfs-load-rejected.json',
- });
- spawnSyncAndAssert(
- rejectedFile,
- ['--node-options=--experimental-vfs --vfs-load=assets.zip'],
- {
- env: {
- ...process.env,
- NODE_DEBUG_NATIVE: undefined,
- },
- },
- {
- status: 9,
- stderr: /--vfs-load cannot be used in a single executable application built with "vfsArchive"/,
- },
- );
}));