Repository: nodejs/node
Pull request: fs: fix dereference: false on cpSync

Fixes #58939.

<!--
Before submitting a pull request, please read:

- the CONTRIBUTING guide at https://github.com/nodejs/node/blob/HEAD/CONTRIBUTING.md
- the commit message formatting guidelines at
  https://github.com/nodejs/node/blob/HEAD/doc/contributing/pull-requests.md#commit-message-guidelines

For code changes:
1. Include tests for any bug fixes or new features.
2. Update documentation if relevant.
3. Ensure that `make -j4 test` (UNIX), or `vcbuild test` (Windows) passes.

If you believe this PR should be highlighted in the Node.js CHANGELOG
please add the `notable-change` label.

Developer's Certificate of Origin 1.1

By making a contribution to this project, I certify that:

(a) The contribution was created in whole or in part by me and I
    have the right to submit it under the open source license
    indicated in the file; or

(b) The contribution is based upon previous work that, to the best
    of my knowledge, is covered under an appropriate open source
    license and I have the right under that license to submit that
    work with modifications, whether created in whole or in part
    by me, under the same open source license (unless I am
    permitted to submit under a different license), as indicated
    in the file; or

(c) The contribution was provided directly to me by some other
    person who certified (a), (b) or (c) and I have not modified
    it.

(d) I understand and agree that this project and the contribution
    are public and that a record of the contribution (including all
    personal information I submit with it, including my sign-off) is
    maintained indefinitely and may be redistributed consistent with
    this project or the open source license(s) involved.
-->


Changed files:
filename: src/node_file.cc
patch: @@ -3245,8 +3245,8 @@ static void CpSyncCheckPaths(const FunctionCallbackInfo<Value>& args) {
         errorno, dereference ? "stat" : "lstat", nullptr, src.out());
   }
   auto dest_status =
-      dereference ? std::filesystem::symlink_status(dest_path, error_code)
-                  : std::filesystem::status(dest_path, error_code);
+      dereference ? std::filesystem::status(dest_path, error_code)
+                  : std::filesystem::symlink_status(dest_path, error_code);
 
   bool dest_exists = !error_code && dest_status.type() !=
                                         std::filesystem::file_type::not_found;

filename: test/known_issues/test-fs-cp-sync-dereference.js
patch: @@ -1,39 +0,0 @@
-'use strict';
-
-// Refs: https://github.com/nodejs/node/issues/58939
-//
-// The cpSync function is not correctly handling the `dereference` option.
-// In this test, both the cp and cpSync functions are attempting to copy
-// a file over a symlinked directory. In the cp case it works fine. In the
-// cpSync case it fails with an error.
-
-const common = require('../common');
-
-const {
-  cp,
-  cpSync,
-  mkdirSync,
-  symlinkSync,
-  writeFileSync,
-} = require('fs');
-
-const {
-  join,
-} = require('path');
-
-const tmpdir = require('../common/tmpdir');
-tmpdir.refresh();
-
-const pathA = join(tmpdir.path, 'a');
-const pathB = join(tmpdir.path, 'b');
-const pathC = join(tmpdir.path, 'c');
-const pathD = join(tmpdir.path, 'd');
-
-writeFileSync(pathA, 'file a');
-mkdirSync(pathB);
-symlinkSync(pathB, pathC, 'dir');
-symlinkSync(pathB, pathD, 'dir');
-
-cp(pathA, pathD, { dereference: false }, common.mustSucceed());
-
-cpSync(pathA, pathC, { dereference: false });

filename: test/parallel/test-fs-cp-sync-dereference.js
patch: @@ -0,0 +1,50 @@
+'use strict';
+
+// Refs: https://github.com/nodejs/node/issues/58939
+//
+// In this test, both the cp and cpSync functions are attempting to copy
+// a file over a symlinked directory.
+
+const common = require('../common');
+
+const {
+  cp,
+  cpSync,
+  mkdirSync,
+  symlinkSync,
+  writeFileSync,
+  readFileSync,
+  statSync
+} = require('fs');
+
+const {
+  join,
+} = require('path');
+
+const assert = require('assert');
+
+const tmpdir = require('../common/tmpdir');
+tmpdir.refresh();
+
+const pathA = join(tmpdir.path, 'a'); // file
+const pathB = join(tmpdir.path, 'b'); // directory
+const pathC = join(tmpdir.path, 'c'); // c -> b
+const pathD = join(tmpdir.path, 'd'); // d -> b
+
+writeFileSync(pathA, 'file a');
+mkdirSync(pathB);
+symlinkSync(pathB, pathC, 'dir');
+symlinkSync(pathB, pathD, 'dir');
+
+cp(pathA, pathD, { dereference: false }, common.mustSucceed(() => {
+  // The path d is now a file, not a symlink
+  assert.strictEqual(readFileSync(pathA, 'utf-8'), readFileSync(pathD, 'utf-8'));
+  assert.ok(statSync(pathA).isFile());
+  assert.ok(statSync(pathD).isFile());
+}));
+
+cpSync(pathA, pathC, { dereference: false });
+
+assert.strictEqual(readFileSync(pathA, 'utf-8'), readFileSync(pathC, 'utf-8'));
+assert.ok(statSync(pathA).isFile());
+assert.ok(statSync(pathC).isFile());