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.
-- v4: 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 | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+)
diff --git a/dlls/kernel32/tests/file.c b/dlls/kernel32/tests/file.c index 68b9f53700e..69c06c7b0c2 100644 --- a/dlls/kernel32/tests/file.c +++ b/dlls/kernel32/tests/file.c @@ -4123,6 +4123,7 @@ static void test_ReplaceFileW(void) { WCHAR replaced[MAX_PATH], replacement[MAX_PATH], backup[MAX_PATH]; static const WCHAR prefix[] = {'p','f','x',0}; + static const WCHAR *tmp_on_z = L"Z:/tmp"; WCHAR temp_path[MAX_PATH]; DWORD ret; BOOL removeBackup = FALSE; @@ -4202,6 +4203,27 @@ static void test_ReplaceFileW(void) broken(GetLastError() == ERROR_ACCESS_DENIED), /* win2k */ "DeleteFileW: error (backup) %ld\n", GetLastError()); } + + /* test with forward slashes in the destination and use + * Z:/tmp in Wine to ensure the root is not writable + */ + ret = GetFileAttributesW(tmp_on_z); + if (ret != INVALID_FILE_ATTRIBUTES && (ret & FILE_ATTRIBUTE_DIRECTORY)) + wcscpy(temp_path, tmp_on_z); + + ret = GetTempFileNameW(temp_path, prefix, 0, replaced); + ok(ret, "GetTempFileNameW error (replaced) %ld\n", GetLastError()); + for (int i = 0; i < wcslen(replaced); i++) + if (replaced[i] == L'\') replaced[i] = L'/'; + + ret = GetTempFileNameW(temp_path, prefix, 0, replacement); + ok(ret, "GetTempFileNameW error (replacement) %ld\n", GetLastError()); + ret = pReplaceFileW(replaced, replacement, NULL, 0, 0, 0); + todo_wine + ok(ret, "ReplaceFileW: error %ld\n", GetLastError()); + + DeleteFileW(replaced); + DeleteFileW(replacement); }
static void test_CreateFile(void)
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/kernel32/tests/file.c | 1 - dlls/kernelbase/file.c | 12 ++++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/dlls/kernel32/tests/file.c b/dlls/kernel32/tests/file.c index 69c06c7b0c2..e6787f9c323 100644 --- a/dlls/kernel32/tests/file.c +++ b/dlls/kernel32/tests/file.c @@ -4219,7 +4219,6 @@ static void test_ReplaceFileW(void) ret = GetTempFileNameW(temp_path, prefix, 0, replacement); ok(ret, "GetTempFileNameW error (replacement) %ld\n", GetLastError()); ret = pReplaceFileW(replaced, replacement, NULL, 0, 0, 0); - todo_wine ok(ret, "ReplaceFileW: error %ld\n", GetLastError());
DeleteFileW(replaced); 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;