Commit 19f2d8e735c for nodejs
commit 19f2d8e735c8c064ad70e0fbe395d7e25a37213e
Author: Yohannes Desta <108905193+yohannesjoya@users.noreply.github.com>
Date: Tue Nov 10 15:23:21 2026 +0300
fs: fix cpSync() abort when dest is a dangling symlink
CpSyncCheckPaths() used the throwing overload of
std::filesystem::equivalent(), which fails when dest is a dangling
symlink. The uncaught C++ exception aborted the process. Use the
std::error_code overload instead: a dangling dest is not the same file
as src, so the copy goes ahead and replaces the link.
Fixes: https://github.com/nodejs/node/issues/67141
Signed-off-by: yohannesjoya <yohannesdestagebru10@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/67142
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
Reviewed-By: Jason Zhang <xzha4350@gmail.com>
diff --git a/src/node_file.cc b/src/node_file.cc
index c2cd5998e6f..e9f44171556 100644
--- a/src/node_file.cc
+++ b/src/node_file.cc
@@ -4643,8 +4643,10 @@ static void CpSyncCheckPaths(const FunctionCallbackInfo<Value>& args) {
auto dest_path_str = ConvertPathToUTF8(dest_path);
if (!error_code) {
- // Check if src and dest are identical.
- if (std::filesystem::equivalent(src_path, dest_path)) {
+ // Check if src and dest are identical. A dangling dest symlink makes
+ // equivalent() fail, which means they are not the same file.
+ std::error_code equivalent_error;
+ if (std::filesystem::equivalent(src_path, dest_path, equivalent_error)) {
static constexpr const char* message =
"src and dest cannot be the same %s";
return THROW_ERR_FS_CP_EINVAL(env, message, dest_path_str);
diff --git a/test/parallel/test-fs-cp-sync-file-over-dangling-symlink.mjs b/test/parallel/test-fs-cp-sync-file-over-dangling-symlink.mjs
new file mode 100644
index 00000000000..51ab2d6def4
--- /dev/null
+++ b/test/parallel/test-fs-cp-sync-file-over-dangling-symlink.mjs
@@ -0,0 +1,30 @@
+// This tests that cpSync() replaces a dangling symlink in dest with a file
+// instead of aborting the process.
+import { mustNotMutateObjectDeep } from '../common/index.mjs';
+import { nextdir } from '../common/fs.js';
+import assert from 'node:assert';
+import {
+ cpSync,
+ existsSync,
+ lstatSync,
+ mkdirSync,
+ readFileSync,
+ symlinkSync,
+ writeFileSync,
+} from 'node:fs';
+import { join } from 'node:path';
+import tmpdir from '../common/tmpdir.js';
+
+tmpdir.refresh();
+
+const dir = nextdir();
+mkdirSync(dir);
+const src = join(dir, 'src');
+const dest = join(dir, 'dest');
+writeFileSync(src, 'data');
+symlinkSync('missing', dest);
+
+cpSync(src, dest, mustNotMutateObjectDeep({ force: true }));
+assert(lstatSync(dest).isFile());
+assert.strictEqual(readFileSync(dest, 'utf8'), 'data');
+assert(!existsSync(join(dir, 'missing')));