Repository: node
Issue: fs.writeFile / fs.writeFileSync fails to write to hidden file
Labels: fs

### 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:

```js
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:

https://github.com/nodejs/node/blob/b323cec78f713bc113be7f6030d787804a9af5a0/deps/uv/src/win/fs.c#L612

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.:

```js
// 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
});
```

```js
// 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](https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew) 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.