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.
-- v3: 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..eefff84a39d 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 destionation 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 eefff84a39d..5dbc36a239e 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;
Now I am ok with this and it's ready for review.