From 7cda217af3e0343b22d044a2bf34f50f5b676f4d Mon Sep 17 00:00:00 2001 From: Aizen200 Date: Fri, 5 Dec 2025 10:13:53 +0530 Subject: [PATCH] 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 --- src/win/fs.c | 11 +++++++++++ test/test-fs.c | 27 +++++++++++++++++++++++++++ test/test-list.h | 2 ++ 3 files changed, 40 insertions(+) diff --git a/src/win/fs.c b/src/win/fs.c index cc2e8db94..d7ee9ce76 100644 --- a/src/win/fs.c +++ b/src/win/fs.c @@ -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; diff --git a/test/test-fs.c b/test/test-fs.c index 4ce059c39..0e7abc0d2 100644 --- a/test/test-fs.c +++ b/test/test-fs.c @@ -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; +} diff --git a/test/test-list.h b/test/test-list.h index 954eff330..467130c32 100644 --- a/test/test-list.h +++ b/test/test-list.h @@ -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)