Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fs.writeFile / fs.writeFileSync fails to write to hidden file #41093

Open
sryze opened this issue Dec 5, 2021 · 3 comments · May be fixed by libuv/libuv#3380
Open

fs.writeFile / fs.writeFileSync fails to write to hidden file #41093

sryze opened this issue Dec 5, 2021 · 3 comments · May be fixed by libuv/libuv#3380
Labels
fs Issues and PRs related to the fs subsystem / file system.

Comments

@sryze
Copy link

sryze commented Dec 5, 2021

Version

18-pre (from master branch)

Platform

Windows

Subsystem

fs

What steps will reproduce the bug?

  1. Create a hidden file: touch file.txt && attrib +h file.txt
  2. Run this script:
const fs = require('fs');

fs.writeFileSync('file.txt', 'test', 'utf-8');

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

Always

What is the expected behavior?

Expected the write call to succeed

What do you see instead?

$ node writeHiddenFile.js
node:internal/fs/utils:344
    throw err;
    ^

Error: EPERM: operation not permitted, open 'file.txt'
    at Object.openSync (node:fs:585:3)
    at Object.writeFileSync (node:fs:2153:35)
    at Object.<anonymous> (E:\Projects\Issues\nodejs-hidden-file-bug\writeHiddenFile.js:3:4)
    at Module._compile (node:internal/modules/cjs/loader:1101:14)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1153:10)
    at Module.load (node:internal/modules/cjs/loader:981:32)
    at Function.Module._load (node:internal/modules/cjs/loader:822:12)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)
    at node:internal/main/run_main_module:17:47 {
  errno: -4048,
  syscall: 'open',
  code: 'EPERM',
  path: 'file.txt'
}

Additional information

When the file is opened via fs.open, this call to CreateFileW fails:

file = CreateFileW(req->file.pathw,

I did some debugging and noticed that the issue occurs because the dwCreationDisposition argument passed to CreateFileW() is set to CREATE_ALWAYS. If the file mode flag is changed to something that causes writeFileSync to use OPEN_EXISTING or OPEN_ALWAYS instead, it works, e.g.:

// Writes to an existing file or fails if it doesn't exist; same as OPEN_EXISTING
fs.writeFileSync('file.txt', 'test', {
    encoding: 'utf-8',
    flag: fs.constants.O_EXCL | fs.constants.O_WRONLY
});
// Writes to an existing file and creates it if it doesn't exist, same as OPEN_ALWAYS
fs.writeFileSync('file.txt', 'test', {
    encoding: 'utf-8',
    flag: fs.constants.O_CREAT | fs.constants.O_WRONLY
});

So basically one can use this ^^^ workaround to support writing to hidden files on Windows systems.

Documentation page for CreateFile mentions the following:

If CREATE_ALWAYS and FILE_ATTRIBUTE_NORMAL are specified, CreateFile fails and sets the last error to ERROR_ACCESS_DENIED if the file exists and has the FILE_ATTRIBUTE_HIDDEN or FILE_ATTRIBUTE_SYSTEM attribute. To avoid the error, specify the same attributes as the existing file.

I think to fix this inside Node.js, one needs to query the file attributes first before opening a file, and then apply the hidden attribute accordingly when opening it. I'm not sure if this should be handled inside Node or UV. I can try to create a pull request if this sounds like a good solution.

@sryze
Copy link
Author

sryze commented Dec 5, 2021

Here is a patch that fixed it for me:

diff --git a/deps/uv/src/win/fs.c b/deps/uv/src/win/fs.c
index 674070400b..9afc8bba1d 100644
--- a/deps/uv/src/win/fs.c
+++ b/deps/uv/src/win/fs.c
@@ -606,6 +606,15 @@ void fs__open(uv_fs_t* req) {
     goto einval;
   }

+
+  /* Copy existing attributes to prevent an "Access is denied" error for hidden
+   * and system files. */
+  DWORD existing_attributes = GetFileAttributesW(req->file.pathw);
+  if (existing_attributes != INVALID_FILE_ATTRIBUTES) {
+    attributes |= (existing_attributes
+      & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM));
+  }
+
   /* Setting this flag makes it possible to open a directory. */
   attributes |= FILE_FLAG_BACKUP_SEMANTICS;

@lpinca
Copy link
Member

lpinca commented Dec 6, 2021

Feel free to open a PR in the libuv repository.

@lpinca lpinca added the fs Issues and PRs related to the fs subsystem / file system. label Dec 6, 2021
@sryze
Copy link
Author

sryze commented Dec 9, 2021

libuv/libuv#3380

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
fs Issues and PRs related to the fs subsystem / file system.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants