Skip to content

fs.copyFile on Mac OS does not respect permissions #26936

Description

@akhoury
  • Version: v11.2.0
  • Platform: Darwin iMac.local 18.2.0 Darwin Kernel Version 18.2.0: Thu Dec 20 20:46:53 PST 2018; root:xnu-4903.241.1~1/RELEASE_X86_64 x86_64
  • Subsystem:

When setting a file mode to 444 using chmodSync, then trying to copy another file over it, on Windows and on Linux, copyFile will throw an error, but on a Mac it does not, and it will override the file

fs.chmodSync(dest, '444');
// then try to 
fs.copyFile(source, dest, function (err) {
    // 	i do expect an err here, 
   // but there is no error on Mac
});

i even check the stat.mode using parseInt(fs.statSync(dest).mode.toString(8), 10) and it does in fact say 100444

This is a sample when everything is working well
https://repl.it/repls/CylindricalGlisteningCondition

However, when i run it on my mac, I get this

This platform is darwin
fs.stat.mode ./dest: 100444
/Users/akhoury/rcloned/code/NodeBB/rrr.js:26
                throw new Error(`This should've thrown an error!`);
                ^

Error: This should've thrown an error!

Thanks

Activity

  1. Trott commented on Mar 27, 2019

    @Trott
    Member

    I'm able to replicate this bug on Mojave. Seems like libuv is somehow running copyfile() with COPYFILE_UNLINK set or else there's a bug in macOS? @nodejs/fs @nodejs/libuv @nodejs/platform-macos

  2. added
    confirmed-bugIssues and PRs for confirmed bugs.
    fsIssues and PRs related to file-system APIs and the fs module.
    libuvIssues and PRs related to the libuv dependency or the uv binding.
    macosIssues and PRs related to the macOS platform.
    on Mar 27, 2019
  3. Trott commented on Mar 27, 2019

    @Trott
    Member

    Confirmed with Node.js 8.x, 10.x, 11.x, and current master branch. (fs.copyFile() does not exist in Node.js 6.x.)

  4. Trott commented on Mar 27, 2019

    @Trott
    Member

    I tried adding this to libuv's fs.c file and recompiling but it didn't change anything...

      // Turn off unlinking of the destination file.
      // Refs: https://github.com/nodejs/node/issues/26936
      flags &= ~(1 << 21); /* COPYFILE_UNLINK */

    (Of course, it's entirely likely I'm doing the bit math wrong or something simple like that.....)

  5. richardlau commented on Mar 27, 2019

    @richardlau
    Member

    I'm able to replicate this bug on Mojave. Seems like libuv is somehow running copyfile() with COPYFILE_UNLINK set or else there's a bug in macOS? @nodejs/fs @nodejs/libuv @nodejs/platform-macos

    I'm not a macOS person, but being able to bypass file permissions sounds like an OS bug.

  6. cjihrig commented on Mar 27, 2019

    @cjihrig
    Contributor

    This seems to be related to permissions and probably not a bug we can control. I'm also seeing no error by default, but if I run with sudo -u nobody then I get an EPERM error.

  7. Trott commented on Mar 27, 2019

    @Trott
    Member

    I'm not a macOS person, but being able to bypass file permissions sounds like an OS bug.

    Yeah, definitely agree after playing around with different copyfile() flags inside libuv. Astonishingly, it seems that copyfile(3) on macOS does not respect file permissions. We can't be the first people to ever experience this, though... 🤔

  8. Trott commented on Mar 27, 2019

    @Trott
    Member

    libuv's fs.c has Apple-specific code for uv__fs_copyfile() added by @cjihrig in libuv/libuv#1465. Do you know if there was a reason other than "use analogous native functionality when you can" for using different code for macOS? Removing it all and using the code used by other UNIX-like operating systems fixes this problem. node continues to pass all tests too. So maybe that's the way to go?

  9. added a commit that references this issue on Mar 27, 2019
  10. Trott commented on Mar 27, 2019

    @Trott
    Member
  11. Trott commented on Mar 27, 2019

    @Trott
    Member
  12. vtjnash commented on Mar 29, 2019

    @vtjnash
    Contributor

    I had recently noticed this behavior divergence also, as it can occur in the context of symlinks (hard or soft): libuv/libuv#2199 (comment). Turning that into an issue has been on my TODO list (and providing analysis on what it seems like it should do), but I think this should already address it.

    I was actually a bit surprised by that the proposed behavior here is the desired choice, since it means that a copy may end up with different attributes from the original file, and it uses either the permissions of the hardlink or of the containing directory, if the destination didn't exist at the time. The result is not necessarily more nor less restrictive, just different.

    But it seems that what libuv does on unix is the explicit choice for the C++17 standard for std::filesystem::copy_file and is also what unix cp appears to do. So that seems like a good track record to follow. By contrast, cp -f behaves either like "unix" (if the dest file could be opened for write) or like "macos" (if the dest file could not).

    Shell test script:

    echo 1 > file1
    echo 2 > file2
    ln file1 file_link
    # optional: chmod a-w file1
    cp file2 file_link # or cp -f
    cat file1 # prints 2
    
  13. added a commit that references this issue on Apr 11, 2019
  14. cjihrig commented on May 2, 2019

    @cjihrig
    Contributor

    This was fixed by #27241.

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

    confirmed-bugIssues and PRs for confirmed bugs.fsIssues and PRs related to file-system APIs and the fs module.libuvIssues and PRs related to the libuv dependency or the uv binding.macosIssues and PRs related to the macOS platform.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions