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..a1761aade426 --- /dev/null +++ b/test/parallel/test-fs-cp-async-dangling-symlink-over-symlink.mjs @@ -0,0 +1,28 @@ +// This tests that cp() replaces an existing symlink with a dangling one. +// Refs: https://github.com/nodejs/node/issues/66946 +import { isWindows, skip } from '../common/index.mjs'; +import { nextdir } from '../common/fs.js'; +import assert from 'node:assert'; +import { mkdirSync, readlinkSync, symlinkSync, writeFileSync, promises } from 'node:fs'; +import { join, resolve } from 'node:path'; +import tmpdir from '../common/tmpdir.js'; + +if (isWindows) + skip('symbolic links need elevated privileges on Windows'); + +tmpdir.refresh(); + +for (const verbatimSymlinks of [false, true]) { + const dir = nextdir(); + mkdirSync(dir); + const target = join(dir, 'target'); + writeFileSync(target, 'data'); + const src = join(dir, 'src'); + const dest = join(dir, 'dest'); + symlinkSync('missing', src); + symlinkSync(target, dest); + + await promises.cp(src, dest, { force: true, verbatimSymlinks }); + assert.strictEqual(readlinkSync(dest), + verbatimSymlinks ? 'missing' : resolve(dir, 'missing')); +}