Skip to content

fix(fs): use libuv for recursive cpSync to avoid VirtioFS EACCES - #65547

Open
shoemoney wants to merge 1 commit into
nodejs:mainfrom
shoemoney:fix/cpsync-virtiofs-permission
Open

fix(fs): use libuv for recursive cpSync to avoid VirtioFS EACCES#65547
shoemoney wants to merge 1 commit into
nodejs:mainfrom
shoemoney:fix/cpsync-virtiofs-permission

Conversation

@shoemoney

@shoemoney shoemoney commented Aug 25, 2026

Copy link
Copy Markdown

Fixes recursive cpSync EACCES on Docker VirtioFS bind mounts.

Bug: CpSyncCopyDir at src/node_file.cc:4205 uses std::filesystem::copy_file which libstdc++ implements by creating dest with 0200 then fchmod. VirtioFS blocks the 0200 creation with EACCES. Single-file path at 4015 already avoids this by using uv_fs_copyfile when mode != 0.

Fix: Use uv_fs_copyfile with mode 0 for recursive regular-file copies, preserving force, errorOnExist and skipExisting semantics via UV_FS_COPYFILE_EXCL and an exists check. Mirrors the single-file path fix.

Evidence: Verified RED before and GREEN after. All 42 test/parallel/test-fs-cp-sync* tests pass sequentially.

Fixes: #65497
Signed-off-by: Jeremy Schoemaker (jeremy@shoemoney.com)

@nodejs-github-bot nodejs-github-bot added c++ Issues and PRs that require attention from people who are familiar with C++. fs Issues and PRs related to file-system APIs and the fs module. needs-ci PRs that need a full CI run. labels Aug 25, 2026
@shoemoney
shoemoney force-pushed the fix/cpsync-virtiofs-permission branch from 0f9ffa7 to 221cfd9 Compare August 25, 2026 22:13
@shoemoney
shoemoney force-pushed the fix/cpsync-virtiofs-permission branch from 7f3d0f5 to be00f60 Compare September 8, 2026 20:32
@shoemoney

Copy link
Copy Markdown
Author

Rebased onto main. CopyDirRecursive() was refactored upstream since this PR was opened: it is now the single implementation shared by fs.cpSync() and the thread-pool fs.cp() job, with a new fresh_destination path used only by fs.cp(). The EACCES bug is still present on the merge-into-existing-directory path, which is the one fs.cpSync() takes, so the fix now lives in CopyDirRecursive()'s regular-file branch instead of directly in CpSyncCopyDir(). Same approach as before: uv_fs_copyfile() instead of std::filesystem::copy_file(), preserving force, errorOnExist and skip-existing semantics via UV_FS_COPYFILE_EXCL and an existence check.

@shoemoney
shoemoney force-pushed the fix/cpsync-virtiofs-permission branch from be00f60 to e2396c6 Compare September 8, 2026 23:01
@codecov

codecov Bot commented Sep 9, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 84.21053% with 3 lines in your changes missing coverage. Please review.
βœ… Project coverage is 90.17%. Comparing base (fd6682c) to head (69470d1).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
src/node_file.cc 84.21% 1 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #65547      +/-   ##
==========================================
+ Coverage   90.16%   90.17%   +0.01%     
==========================================
  Files         771      771              
  Lines      265445   265451       +6     
  Branches    50455    50458       +3     
==========================================
+ Hits       239329   239373      +44     
+ Misses      17056    17009      -47     
- Partials     9060     9069       +9     
Files with missing lines Coverage Ξ”
src/node_file.cc 75.34% <84.21%> (-0.11%) ⬇️

... and 31 files with indirect coverage changes

πŸš€ New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • πŸ“¦ JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@MikeMcC399

Copy link
Copy Markdown
Contributor

All tests are failing in CI.

They will continue to fail without the commit from PR #65921.

Please rebase the branch in this PR according to the Pull requests documentation so that your PR branch includes the fix.

It is also advisable before pushing any commit to test it locally, see Pull requests > Step 6: Test

Before submitting your changes in a pull request, always run the full Node.js test suite.

with further details under BUILDING > Running tests.

including:

make lint / vcbuild lint for linting only.

fs.cpSync() recursive copy fails EACCES on Docker VirtioFS bind
mounts. CopyDirRecursive()'s regular-file branch calls
std::filesystem::copy_file(), which libstdc++ implements by creating
dest with mode 0200 and then fchmod(). VirtioFS rejects that initial
0200 open with EACCES. The single-file cpSync path and the
fresh-destination branch of CopyDirRecursive() already avoid this by
using uv_fs_copyfile(), which opens dest with its final mode
directly.

Use uv_fs_copyfile() for the merge-into-existing-directory case too,
preserving force, errorOnExist and skip-existing semantics via
UV_FS_COPYFILE_EXCL and an existence check. Also drops the
file_copy_opts capture, now unused once the regular-file branch no
longer calls std::filesystem::copy_file().

Fixes: nodejs#65497
Signed-off-by: Jeremy Schoemaker <jeremy@shoemoney.com>
@shoemoney
shoemoney force-pushed the fix/cpsync-virtiofs-permission branch from e2396c6 to 69470d1 Compare September 9, 2026 18:04
@shoemoney

Copy link
Copy Markdown
Author

Rebased onto main β€” the branch head is now 69470d1, on top of 7ac314f (v26.8.2), which includes the fix from #65921 (dab3a73). No changes to the patch itself; the diff is still the same 33/21 lines in src/node_file.cc.

Thanks for the pointer to #65921 β€” that explains the full red board, since every job was failing in the workflow YAML before it ever reached the test suite.

@shoemoney

Copy link
Copy Markdown
Author

CI on the rebased head (69470d1677) is green apart from one unrelated failure, so the "all tests are failing" symptom from the pre-rebase run is resolved.

Test macOS (attempt #2) fails only at the "Re-run test in a folder whose name contains unusual chars" step β€” the main Test step passes. The single failing test there is:

=== release test-common-wpt-inspect ===
Path: parallel/test-common-wpt-inspect
--- stderr ---
Timed out waiting for matching notification (Debugger.paused)

That is an inspector/debugger timeout, and this patch only touches src/node_file.cc (fs.cpSync recursion). Test macOS is green on main for every completed run today, including two that started after this one, so it looks like a flake on the loaded macOS runner rather than something this change introduced.

Everything else is passing: test-linux (both x64 and arm), test-tarball-linux, coverage-linux, coverage-windows, all lint-*, and every shared-library build.

I do not have permission to re-run jobs on this repo β€” happy to push an empty commit to retrigger if that is preferred.

@MikeMcC399

Copy link
Copy Markdown
Contributor

happy to push an empty commit to retrigger if that is preferred.

An empty commit would not trigger a re-run, so long as you are classed as a "first-time contributor", in which case every GitHub Actions CI run has to be manually triggered by a Node.js team member.

I have re-run the failing test, and now the GitHub Actions CI is green.

The next step depends on a technical review of your PR. It's not possible to predict how long that will take.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

c++ Issues and PRs that require attention from people who are familiar with C++. fs Issues and PRs related to file-system APIs and the fs module. needs-ci PRs that need a full CI run.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fs.cpSync fails with EACCES when the destination is on a Docker bind mount (regression in 22.17)

3 participants