Skip to content

fs.copyFile fails if source is readonly and target is a WSL volume #44261

Closed as not planned

Description

@juanrgm

Version

v18.7.0

Platform

Linux 774484ed2c79 5.10.102.1-microsoft-standard-WSL2 #1 SMP Wed Mar 2 00:30:59 UTC 2022 x86_64 Linux

Subsystem

fs

What steps will reproduce the bug?

  • Command Prompt
docker run --rm -it ^
-e "CODE=require('fs').copyFileSync('/tmp/file', '/target/file')" ^
-v "%TEMP%:/target" ^
node:alpine ^
sh -c "touch /tmp/file && chmod -w /tmp/file && node -e 'eval(process.env.CODE)'"
  • Git Bash
docker run --rm -it \
-e "CODE=require('fs').copyFileSync('/tmp/file', '/target/file')" \
-v "$TEMP:/target" \
node:alpine \
sh -c "touch /tmp/file && chmod -w /tmp/file && node -e 'eval(process.env.CODE)'"

How often does it reproduce? Is there a required condition?

Always.

What is the expected behavior?

No error.

What do you see instead?

node:internal/fs/utils:348
    throw err;
    ^

Error: EACCES: permission denied, copyfile '/tmp/file' -> '/target/file'
    at Object.copyFileSync (node:fs:2866:3)
    at eval (eval at <anonymous> ([eval]:1:1), <anonymous>:1:15)
    at [eval]:1:1
    at Script.runInThisContext (node:vm:129:12)
    at Object.runInThisContext (node:vm:313:38)
    at node:internal/process/execution:76:19
    at [eval]-wrapper:6:22
    at evalScript (node:internal/process/execution:75:60)
    at node:internal/main/eval_string:27:3 {
  errno: -13,
  syscall: 'copyfile',
  code: 'EACCES',
  path: '/tmp/file',
  dest: '/target/file'
}

Additional information

Activity

  1. added
    fsIssues and PRs related to file-system APIs and the fs module.
    on Aug 17, 2022
  2. prettydiff commented on Aug 19, 2022

    @prettydiff
    Contributor

    Is the file in question read protected by the operating system? If the operating system has marked a file as read protected, such that it is locked by an underlying operating system process Node will not be able to perform the necessary fs.open method on the file to access the file contents. You will not see this very often on files you, as a user or administrator, own. However, it will happen on some files at the file system root, in system32, file associated with regedit, and so forth. You should still be able to see these files with fs.readdir and fs.stat though.

    See also:

  3. juanrgm commented on Aug 19, 2022

    @juanrgm
    Author

    But this it works:

    • Command Prompt
    docker run --rm -it ^
    -v "%TEMP%:/target" ^
    node:alpine ^
    sh -c "touch /tmp/file && chmod -w /tmp/file && cp /tmp/file /target/file"
  4. github-actions commented on Jun 25, 2026

    @github-actions
    Contributor

    This issue has been marked as stale due to 210 days of inactivity.
    It will be automatically closed in 30 days if no further activity occurs. If this is still relevant, please leave a comment or update it to keep it open.

  5. added
    staleIssues and PRs marked stale due to inactivity and scheduled for automatic closure.
    on Jun 25, 2026
  6. github-actions commented on Jul 26, 2026

    @github-actions
    Contributor

    This issue has been automatically closed after 30 days of inactivity following its stale status (no activity for a total of 120 days).
    If this is still relevant, feel free to reopen it or leave a comment with additional details so we can continue the discussion.

  7. Zamiell commented on Sep 3, 2026

    @Zamiell

    This is still reproducible on Node.js v24.19.0 with current WSL2. Unlike the original report, the source file is not read-only and Docker is not involved.

    Environment

    Node.js: v24.19.0
    libuv: 1.52.1
    WSL: 2.7.11.0
    Kernel: 6.18.33.2-microsoft-standard-WSL2
    Windows: 10.0.26100.9168
    Architecture: x86_64
    

    The Windows drive is mounted as 9p:

    C:\ on /mnt/c type 9p (rw,noatime,aname=drvfs;path=C:\;uid=0;gid=0;symlinkroot=/mnt/,cache=0x5,access=client,msize=65536,trans=fd,rfd=6,wfd=6)
    

    Reproduction

    The source is on the native WSL filesystem and has mode 0644. The destination is a writable directory under /mnt/c.

    const fs = require("node:fs");
    
    fs.copyFileSync(
      "/home/james/repositories/isaac-run-timer/node_modules/isaacscript/isaacscript-watcher/resources/gfx/logo.png",
      "/mnt/c/Program Files (x86)/Steam/steamapps/common/The Binding of Isaac Rebirth/mods/copyfile-repro.png",
    );

    Result:

    Error: EPERM: operation not permitted, copyfile
    

    The destination directory is writable and its Windows ACL grants my user full control. These operations succeed:

    • fs.writeFile() to the same destination
    • GNU cp between the same source and destination
    • Bun's fs.copyFile() implementation

    A direct permission change on a newly created destination file fails:

    $ stat -c '%a' /mnt/c/.../probe
    777
    
    $ chmod 0644 /mnt/c/.../probe
    chmod: Operation not permitted
    
    $ echo $?
    1

    This appears to be a different remaining case from #56248. That issue fixed futimens() failures on CIFS/SMB. Node v24.19.0 contains that fix.

    In the current libuv uv__fs_copyfile() implementation, an EPERM from fchmod() is ignored only when uv__is_cifs_or_smb(dstfd) succeeds. WSL's /mnt/c is reported as filesystem type 9p, so the error remains fatal even though the file data can be copied successfully and Unix mode preservation is not supported by this mount.

    Could this issue be reopened, or should the fchmod() handling be moved to libuv with 9p/WSL included among the filesystems where permission preservation is best-effort?

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    fsIssues and PRs related to file-system APIs and the fs module.staleIssues and PRs marked stale due to inactivity and scheduled for automatic closure.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions