From: Dmitry Timoshkov Subject: [PATCH] kernel32: CreateDirectory shouldn't return ERROR_ACCESS_DENIED for the root of the drive. Message-Id: <20181211130332.df4244c0451dcacab0fe4803@baikal.ru> Date: Tue, 11 Dec 2018 13:03:32 +0300 According to the testbot results CreateDirectory("C:\\", NULL) fails with ERROR_ACCESS_DENIED for not administrators. However with UAC enabled and not and administrator account I get ERROR_ALREADY_EXISTS in that case with Windows 7 64-bit running on real hardware. Moreover, Wine doesn't really perform any access checks in that case and blindly assumes that returning STATUS_ACCESS_DENIED is correct behaviour for the drive's root: dlls/ntdll/directory.c,lookup_unix_name(). This patch fixes an application that can't find its data files because after it receives ERROR_ACCESS_DENIED it stops further directory traversing. Signed-off-by: Dmitry Timoshkov --- dlls/kernel32/tests/directory.c | 43 +++++++++++++++++++++++++++++++++ dlls/ntdll/directory.c | 3 +-- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/dlls/kernel32/tests/directory.c b/dlls/kernel32/tests/directory.c index 512dc6d22a..de78c61681 100644 --- a/dlls/kernel32/tests/directory.c +++ b/dlls/kernel32/tests/directory.c @@ -541,10 +541,53 @@ static void test_SetCurrentDirectoryA(void) ok( GetLastError() == ERROR_PATH_NOT_FOUND, "wrong error %d\n", GetLastError() ); } +static void test_CreateDirectory_root(void) +{ + static const WCHAR drive_c_root[] = { 'C',':','\\',0 }; + static const WCHAR drive_c[] = { 'C',':',0 }; + char curdir[MAX_PATH]; + BOOL ret; + + GetCurrentDirectoryA(sizeof(curdir), curdir); + + ret = SetCurrentDirectoryA("C:\\"); + ok(ret, "SetCurrentDirectory error %u\n", GetLastError()); + + SetLastError(0xdeadbeef); + ret = CreateDirectoryA("C:\\", NULL); + ok(!ret, "CreateDirectory should fail\n"); + if (GetLastError() == ERROR_ACCESS_DENIED) + { + win_skip("not an administrator\n"); + SetCurrentDirectoryA(curdir); + return; + } + ok(GetLastError() == ERROR_ALREADY_EXISTS, "got %u\n", GetLastError()); + + SetLastError(0xdeadbeef); + ret = CreateDirectoryA("C:", NULL); + ok(!ret, "CreateDirectory should fail\n"); + ok(GetLastError() == ERROR_ALREADY_EXISTS, "got %u\n", GetLastError()); + + SetLastError(0xdeadbeef); + ret = CreateDirectoryW(drive_c_root, NULL); + ok(!ret, "CreateDirectory should fail\n"); + ok(GetLastError() == ERROR_ALREADY_EXISTS, "got %u\n", GetLastError()); + + SetLastError(0xdeadbeef); + ret = CreateDirectoryW(drive_c, NULL); + ok(!ret, "CreateDirectory should fail\n"); + ok(GetLastError() == ERROR_ALREADY_EXISTS, "got %u\n", GetLastError()); + + SetCurrentDirectoryA(curdir); +} + START_TEST(directory) { init(); + test_CreateDirectory_root(); + test_GetWindowsDirectoryA(); test_GetWindowsDirectoryW(); diff --git a/dlls/ntdll/directory.c b/dlls/ntdll/directory.c index c48b9e97ea..2719958f67 100644 --- a/dlls/ntdll/directory.c +++ b/dlls/ntdll/directory.c @@ -2650,9 +2650,8 @@ static NTSTATUS lookup_unix_name( const WCHAR *name, int name_len, char **buffer { if (!stat( unix_name, &st )) { - /* creation fails with STATUS_ACCESS_DENIED for the root of the drive */ if (disposition == FILE_CREATE) - return name_len ? STATUS_OBJECT_NAME_COLLISION : STATUS_ACCESS_DENIED; + return STATUS_OBJECT_NAME_COLLISION; return STATUS_SUCCESS; } } -- 2.19.2