win,fs: fix mkdir returning EPERM on network drives

On Windows network drives (SSHFS/FTP), CreateDirectoryW returns
ERROR_ACCESS_DENIED instead of ERROR_ALREADY_EXISTS when a
directory already exists. This caused uv_fs_mkdir to return
UV_EPERM instead of the expected UV_EEXIST.

Fix by checking if ERROR_ACCESS_DENIED refers to an existing
directory and mapping it to UV_EEXIST.

Fixes: #4959
This commit is contained in:
Aizen200 2025-12-05 10:13:53 +05:30
parent 01250432eb
commit 7cda217af3
3 changed files with 40 additions and 0 deletions

View File

@ -1253,11 +1253,22 @@ static void fs__unlink(uv_fs_t* req) {
void fs__mkdir(uv_fs_t* req) {
DWORD attr;
/* TODO: use req->mode. */
if (CreateDirectoryW(req->file.pathw, NULL)) {
SET_REQ_RESULT(req, 0);
} else {
SET_REQ_WIN32_ERROR(req, GetLastError());
if (req->sys_errno_ == ERROR_ACCESS_DENIED) {
/* Check if the directory already exists. */
attr = GetFileAttributesW(req->file.pathw);
if (attr != INVALID_FILE_ATTRIBUTES &&
(attr & FILE_ATTRIBUTE_DIRECTORY)) {
req->sys_errno_ = ERROR_ALREADY_EXISTS;
req->result = UV_EEXIST;
}
}
if (req->sys_errno_ == ERROR_INVALID_NAME ||
req->sys_errno_ == ERROR_DIRECTORY)
req->result = UV_EINVAL;

View File

@ -4928,3 +4928,30 @@ TEST_IMPL(fs_wtf) {
return 0;
}
#endif
TEST_IMPL(fs_mkdir_existing) {
int r;
uv_fs_t req;
loop = uv_default_loop();
/* Setup */
unlink("test_dir/file");
rmdir("test_dir");
/* Create directory */
r = uv_fs_mkdir(NULL, &req, "test_dir", 0755, NULL);
ASSERT_OK(r);
uv_fs_req_cleanup(&req);
/* Create directory again - synchronous */
r = uv_fs_mkdir(NULL, &req, "test_dir", 0755, NULL);
ASSERT_EQ(r, UV_EEXIST);
uv_fs_req_cleanup(&req);
/* Cleanup */
rmdir("test_dir");
MAKE_VALGRIND_HAPPY(loop);
return 0;
}

View File

@ -365,6 +365,7 @@ TEST_DECLARE (fs_async_dir)
TEST_DECLARE (fs_async_sendfile)
TEST_DECLARE (fs_async_sendfile_nodata)
TEST_DECLARE (fs_mkdtemp)
TEST_DECLARE (fs_mkdir_existing)
TEST_DECLARE (fs_mkstemp)
TEST_DECLARE (fs_fstat)
TEST_DECLARE (fs_fstat_stdio)
@ -1089,6 +1090,7 @@ TASK_LIST_START
TEST_ENTRY (fs_async_sendfile)
TEST_ENTRY (fs_async_sendfile_nodata)
TEST_ENTRY (fs_mkdtemp)
TEST_ENTRY (fs_mkdir_existing)
TEST_ENTRY (fs_mkstemp)
TEST_ENTRY (fs_fstat)
TEST_ENTRY (fs_fstat_stdio)