From 89df046a21a148808353c2b93bbe57d64ccff7e4 Mon Sep 17 00:00:00 2001 From: Tim Perry <1526883+pimterry@users.noreply.github.com> Date: Fri, 17 Jul 2026 18:39:25 +0200 Subject: [PATCH 1/2] test: keep finalization before-exit ref alive Signed-off-by: Tim Perry PR-URL: https://github.com/nodejs/node/pull/64521 Reviewed-By: Trivikram Kamat Reviewed-By: Luigi Pinca --- test/fixtures/process/before-exit.mjs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/fixtures/process/before-exit.mjs b/test/fixtures/process/before-exit.mjs index 99f147e0c3e485..b8c627b0545e17 100644 --- a/test/fixtures/process/before-exit.mjs +++ b/test/fixtures/process/before-exit.mjs @@ -1,7 +1,9 @@ import { strictEqual } from 'assert' +let obj + function setup() { - const obj = { foo: 'bar' } + obj = { foo: 'bar' } process.finalization.registerBeforeExit(obj, shutdown) } @@ -27,5 +29,6 @@ function shutdown(obj, event) { setup() process.on('exit', function () { + strictEqual(obj.foo, 'bar') strictEqual(shutdownCalled, true) }) From 1d4e86ec7f9cbd7b45a06e7f7997307fc89f909c Mon Sep 17 00:00:00 2001 From: Trivikram Kamat <16024985+trivikr@users.noreply.github.com> Date: Fri, 17 Jul 2026 13:47:53 -0700 Subject: [PATCH 2/2] vfs: make lchmod update symlink mode Use a non-following provider operation for lchmod so it updates the symbolic link metadata instead of changing the target file mode. Signed-off-by: Kamat, Trivikram <16024985+trivikr@users.noreply.github.com> Assisted-by: openai:gpt-5.5 PR-URL: https://github.com/nodejs/node/pull/64350 Fixes: https://github.com/nodejs/node/issues/64349 Reviewed-By: Matteo Collina --- lib/internal/vfs/file_system.js | 2 +- lib/internal/vfs/providers/memory.js | 7 +++++++ lib/internal/vfs/providers/real.js | 11 +++++++++++ test/parallel/test-vfs-fs-promises.js | 10 ++++++++++ 4 files changed, 29 insertions(+), 1 deletion(-) diff --git a/lib/internal/vfs/file_system.js b/lib/internal/vfs/file_system.js index ae38639582581b..542b63d5f079b0 100644 --- a/lib/internal/vfs/file_system.js +++ b/lib/internal/vfs/file_system.js @@ -1255,7 +1255,7 @@ class VirtualFileSystem { async lchmod(filePath, mode) { const providerPath = toProviderPath(filePath); - provider.chmodSync(providerPath, mode); + provider.lchmodSync(providerPath, mode); }, watch(filePath, options) { diff --git a/lib/internal/vfs/providers/memory.js b/lib/internal/vfs/providers/memory.js index a5681dfab1ca43..8490340101bc07 100644 --- a/lib/internal/vfs/providers/memory.js +++ b/lib/internal/vfs/providers/memory.js @@ -957,6 +957,13 @@ class MemoryProvider extends VirtualProvider { entry.ctime = DateNow(); } + lchmodSync(path, mode) { + const entry = this.#getEntry(path, 'chmod', false); + // Preserve file type bits, update permission bits + entry.mode = (entry.mode & ~0o7777) | (mode & 0o7777); + entry.ctime = DateNow(); + } + chownSync(path, uid, gid) { const entry = this.#getEntry(path, 'chown', true); if (uid >= 0) entry.uid = uid; diff --git a/lib/internal/vfs/providers/real.js b/lib/internal/vfs/providers/real.js index 085485ca8a1a97..df9bd00ac1adc4 100644 --- a/lib/internal/vfs/providers/real.js +++ b/lib/internal/vfs/providers/real.js @@ -13,6 +13,9 @@ const { VirtualProvider } = require('internal/vfs/provider'); const { VirtualFileHandle } = require('internal/vfs/file_handle'); const { getValidatedPath } = require('internal/fs/utils'); const { setOwnProperty } = require('internal/util'); +const { + ERR_METHOD_NOT_IMPLEMENTED, +} = require('internal/errors').codes; const { createEACCES, createEBADF, @@ -517,6 +520,14 @@ class RealFSProvider extends VirtualProvider { return fs.promises.access(realPath, mode); } + lchmodSync(vfsPath, mode) { + if (fs.lchmodSync === undefined) { + throw new ERR_METHOD_NOT_IMPLEMENTED('lchmodSync'); + } + const realPath = this.#resolvePath(vfsPath, false); + fs.lchmodSync(realPath, mode); + } + copyFileSync(srcVfsPath, destVfsPath, mode) { const srcRealPath = this.#resolvePath(srcVfsPath); const destRealPath = this.#resolvePath(destVfsPath); diff --git a/test/parallel/test-vfs-fs-promises.js b/test/parallel/test-vfs-fs-promises.js index d4b151162f2802..0f09c96f83aec5 100644 --- a/test/parallel/test-vfs-fs-promises.js +++ b/test/parallel/test-vfs-fs-promises.js @@ -75,6 +75,16 @@ const vfs = require('node:vfs'); await fsp.utimes(p('src/hello.txt'), now, now); await fsp.lutimes(p('src/hello.txt'), now, now); + await fsp.lchmod(p('src/plnk.txt'), 0o700); + assert.strictEqual( + (await fsp.lstat(p('src/hello.txt'))).mode & 0o777, + 0o644, + ); + assert.strictEqual( + (await fsp.lstat(p('src/plnk.txt'))).mode & 0o777, + 0o700, + ); + // FileHandle via fsp.open const handle = await fsp.open(p('src/hello.txt'), 'r'); assert.strictEqual(await handle.readFile('utf8'), 'hello');