PathRemoveFileSpecW keeps only the drive if the path contains all forward slashes as shown in tests.
But then the temporary file is created in the root folder which fails for drive Z:.
This fixes a corner case when using CMake in Wine. Another usage of PathRemoveFileSpecW is in PathRelativePathToW which should be also checked.
I'm leaving this as a draft until getting some feedback and adding tests.
-- v2: kernelbase: Handle correctly paths with forward slashes in ReplaceFileW. kernel32: Test ReplaceFileW with forward slashes.
From: Roman Pišl rpisl@seznam.cz
--- dlls/kernel32/tests/file.c | 66 ++++++++++++++++++++++++++++---------- 1 file changed, 49 insertions(+), 17 deletions(-)
diff --git a/dlls/kernel32/tests/file.c b/dlls/kernel32/tests/file.c index 68b9f53700e..eceb023ea46 100644 --- a/dlls/kernel32/tests/file.c +++ b/dlls/kernel32/tests/file.c @@ -4119,28 +4119,13 @@ static void test_ReplaceFileA(void) * ReplaceFileW is a simpler case of ReplaceFileA, there is no * need to be as thorough. */ -static void test_ReplaceFileW(void) +static void test_ReplaceFileW_(const WCHAR *temp_path) { WCHAR replaced[MAX_PATH], replacement[MAX_PATH], backup[MAX_PATH]; static const WCHAR prefix[] = {'p','f','x',0}; - WCHAR temp_path[MAX_PATH]; DWORD ret; BOOL removeBackup = FALSE; - - if (!pReplaceFileW) - { - win_skip("ReplaceFileW() is missing\n"); - return; - } - - ret = GetTempPathW(MAX_PATH, temp_path); - if (ret == 0 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) - { - win_skip("GetTempPathW is not available\n"); - return; - } - ok(ret != 0, "GetTempPathW error %ld\n", GetLastError()); - ok(ret < MAX_PATH, "temp path should fit into MAX_PATH\n"); + HANDLE hnd;
ret = GetTempFileNameW(temp_path, prefix, 0, replaced); ok(ret != 0, "GetTempFileNameW error (replaced) %ld\n", GetLastError()); @@ -4189,6 +4174,16 @@ static void test_ReplaceFileW(void) ret = pReplaceFileW(replaced, replacement, backup, 0, 0, 0); ok(!ret, "ReplaceFileW: error %ld\n", GetLastError());
+ /* open file in exclusive mode */ + hnd = CreateFileW(replaced, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_DELETE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + ok(hnd != INVALID_HANDLE_VALUE, "CreateFile unexpected error %ld\n", GetLastError()); + ret = GetTempFileNameW(temp_path, prefix, 0, replacement); + ok(ret != 0, "GetTempFileNameW error (replacement) %ld\n", GetLastError()); + ret = pReplaceFileW(replaced, replacement, NULL, 0, 0, 0); + ok(ret || GetLastError() == ERROR_ACCESS_DENIED, + "ReplaceFileW: error %ld\n", GetLastError()); + CloseHandle(hnd); + ret = pReplaceFileW(replaced, replacement, NULL, 0, 0, 0); ok(!ret && (GetLastError() == ERROR_FILE_NOT_FOUND || GetLastError() == ERROR_ACCESS_DENIED), @@ -4204,6 +4199,43 @@ static void test_ReplaceFileW(void) } }
+static void test_ReplaceFileW(void) +{ + static const WCHAR *tmp_on_z = L"Z:/tmp"; + WCHAR temp_path[MAX_PATH]; + const WCHAR *tmp_path_fwslash; + DWORD attrib, ret; + + if (!pReplaceFileW) + { + win_skip("ReplaceFileW() is missing\n"); + return; + } + + ret = GetTempPathW(MAX_PATH, temp_path); + if (ret == 0 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) + { + win_skip("GetTempPathW is not available\n"); + return; + } + ok(ret != 0, "GetTempPathW error %ld\n", GetLastError()); + ok(ret < MAX_PATH, "temp path should fit into MAX_PATH\n"); + + test_ReplaceFileW_(temp_path); + + attrib = GetFileAttributesW(tmp_on_z); + if (attrib != INVALID_FILE_ATTRIBUTES && (attrib & FILE_ATTRIBUTE_DIRECTORY)) + tmp_path_fwslash = tmp_on_z; + else + { + for (int i = 0; i < wcslen(temp_path); i++) + if (temp_path[i] == L'\') temp_path[i] = L'/'; + tmp_path_fwslash = temp_path; + } + + test_ReplaceFileW_(tmp_path_fwslash); +} + static void test_CreateFile(void) { static const struct test_data
From: Roman Pišl rpisl@seznam.cz
PathRemoveFileSpecW keeps only the drive if the path contains all forward slashes as shown in tests.
But then the temporary file is created in the root folder which fails for drive Z:. --- dlls/kernelbase/file.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c index f0dedfe3b14..53286f1331a 100644 --- a/dlls/kernelbase/file.c +++ b/dlls/kernelbase/file.c @@ -2717,8 +2717,16 @@ BOOL WINAPI DECLSPEC_HOTPATCH ReplaceFileW( const WCHAR *replaced, const WCHAR * * it out of the way first. */ WCHAR temp_path[MAX_PATH], temp_file[MAX_PATH];
- lstrcpynW( temp_path, replaced, ARRAY_SIZE( temp_path ) ); - PathRemoveFileSpecW( temp_path ); + WCHAR* filePart; + DWORD cnt = GetFullPathNameW(replaced, ARRAY_SIZE( temp_path ), temp_path, &filePart); + if (!cnt) return FALSE; + if (cnt >= ARRAY_SIZE( temp_path ) || !filePart) + { + SetLastError( ERROR_PATH_NOT_FOUND ); + return FALSE; + } + *filePart = 0; + if (!GetTempFileNameW( temp_path, L"rf", 0, temp_file ) || !MoveFileExW( replaced, temp_file, MOVEFILE_REPLACE_EXISTING )) return FALSE;