Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions src/node_file.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
30 changes: 30 additions & 0 deletions test/parallel/test-fs-cp-sync-file-over-dangling-symlink.mjs
Original file line number Diff line number Diff line change
@@ -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')));
Loading