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"/,
-    },
-  );
 }));