From e5b7ba7ad4bfe1fce18e388985a9f84632808924 Mon Sep 17 00:00:00 2001 From: semimikoh Date: Fri, 23 Oct 2026 17:46:54 +0900 Subject: [PATCH] fs: copy dangling symlinks over existing symlinks Reuse srcIsDir instead of stat() for the subdirectory check. Signed-off-by: semimikoh --- lib/internal/fs/cp/cp.js | 3 +-- ...fs-cp-async-dangling-symlink-over-symlink.mjs | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 test/parallel/test-fs-cp-async-dangling-symlink-over-symlink.mjs diff --git a/lib/internal/fs/cp/cp.js b/lib/internal/fs/cp/cp.js index 1ec5a2d89cac..0c4338711690 100644 --- a/lib/internal/fs/cp/cp.js +++ b/lib/internal/fs/cp/cp.js @@ -398,8 +398,7 @@ async function onLink(destStat, src, dest, opts) { // Do not copy if src is a subdir of dest since unlinking // dest in this case would result in removing src contents // and therefore a broken symlink would be created. - const srcStat = await stat(src); - if (srcStat.isDirectory() && isSrcSubdir(resolvedDest, resolvedSrc)) { + if (srcIsDir && isSrcSubdir(resolvedDest, resolvedSrc)) { throw new ERR_FS_CP_SYMLINK_TO_SUBDIRECTORY({ message: `cannot overwrite ${resolvedDest} with ${resolvedSrc}`, path: dest, diff --git a/test/parallel/test-fs-cp-async-dangling-symlink-over-symlink.mjs b/test/parallel/test-fs-cp-async-dangling-symlink-over-symlink.mjs new file mode 100644 index 000000000000..e69686bdd502 --- /dev/null +++ b/test/parallel/test-fs-cp-async-dangling-symlink-over-symlink.mjs @@ -0,0 +1,16 @@ +import { mustNotMutateObjectDeep } from '../common/index.mjs'; +import { nextdir } from '../common/fs.js'; +import assert from 'node:assert'; +import { promises, readlinkSync, symlinkSync } from 'node:fs'; +import { dirname, resolve } from 'node:path'; +import tmpdir from '../common/tmpdir.js'; + +tmpdir.refresh(); + +const src = nextdir(); +const dest = nextdir(); +symlinkSync('missing', src); +symlinkSync(process.execPath, dest); + +await promises.cp(src, dest, mustNotMutateObjectDeep({ force: true })); +assert.strictEqual(readlinkSync(dest), resolve(dirname(src), 'missing'));