diff --git a/patches/kernel32-ReplaceFileW/0001-kernel32-Correct-ReplaceFileW-behaviour.patch b/patches/kernel32-ReplaceFileW/0001-kernel32-Correct-ReplaceFileW-behaviour.patch new file mode 100644 index 00000000..2ac5375c --- /dev/null +++ b/patches/kernel32-ReplaceFileW/0001-kernel32-Correct-ReplaceFileW-behaviour.patch @@ -0,0 +1,100 @@ +From 679a02527fb9051d20940072ab49cc1fa3fc7319 Mon Sep 17 00:00:00 2001 +From: Brock York +Date: Wed, 3 Apr 2019 17:00:22 +1100 +Subject: [PATCH] kernel32: Correct ReplaceFileW behaviour + +ReplaceFileW should fail with ERROR_ACCESS_DENIED when either the replaced +or replacement file is set to read only using SetFileAttributes. +Testing for the return values of ReplaceFileW was performed on a +Windows XP SP3 and Windows 10 vm. + +Open replaced file without GENERIC_WRITE flag if a sharing violation is +returned on the first call checking for the READ_ONLY attribute. +ReplaceFileW should not fail when called to replace the current executable which +is the case as tested on Windows 7, 10 and XP this is because the +"replaced" file is opened with the GENERIC_WRITE flag. +The MSDN also mentions that the replaced file is only opened with +GENERIC_READ, DELETE and SYNCHRONIZE. + +This patch will fix the following bug once it is +merged into Wine-Staging as the WarFrame launcher +requires patches from Wine-Staging to work. + +Wine-Bug:https://bugs.winehq.org/show_bug.cgi?id=33845 +--- + dlls/kernel32/file.c | 15 +++++++++++++-- + dlls/kernel32/tests/file.c | 4 ++-- + 2 files changed, 15 insertions(+), 4 deletions(-) + +diff --git a/dlls/kernel32/file.c b/dlls/kernel32/file.c +index a5c34fae12a..cc22a175fb5 100644 +--- a/dlls/kernel32/file.c ++++ b/dlls/kernel32/file.c +@@ -1772,7 +1772,7 @@ BOOL WINAPI ReplaceFileW(LPCWSTR lpReplacedFileName, LPCWSTR lpReplacementFileNa + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + +- /* Open the "replaced" file for reading and writing */ ++ /* Open the "replaced" file for reading and writing to check for READ_ONLY attribute */ + if (!(RtlDosPathNameToNtPathName_U(lpReplacedFileName, &nt_replaced_name, NULL, NULL))) + { + error = ERROR_PATH_NOT_FOUND; +@@ -1784,6 +1784,12 @@ BOOL WINAPI ReplaceFileW(LPCWSTR lpReplacedFileName, LPCWSTR lpReplacementFileNa + &attr, &io, + FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, + FILE_SYNCHRONOUS_IO_NONALERT|FILE_NON_DIRECTORY_FILE); ++ /*If we didn't get ACCESS_DENIED, then open the file for reading and delete ready for the replacement*/ ++ if (status == STATUS_SHARING_VIOLATION) ++ status = NtOpenFile(&hReplaced, GENERIC_READ|DELETE|SYNCHRONIZE, ++ &attr, &io, ++ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, ++ FILE_SYNCHRONOUS_IO_NONALERT|FILE_NON_DIRECTORY_FILE); + if (status == STATUS_SUCCESS) + status = wine_nt_to_unix_file_name(&nt_replaced_name, &unix_replaced_name, replaced_flags, FALSE); + RtlFreeUnicodeString(&nt_replaced_name); +@@ -1791,6 +1797,8 @@ BOOL WINAPI ReplaceFileW(LPCWSTR lpReplacedFileName, LPCWSTR lpReplacementFileNa + { + if (status == STATUS_OBJECT_NAME_NOT_FOUND) + error = ERROR_FILE_NOT_FOUND; ++ else if (status == STATUS_ACCESS_DENIED) ++ error = ERROR_ACCESS_DENIED; + else + error = ERROR_UNABLE_TO_REMOVE_REPLACED; + goto fail; +@@ -1815,7 +1823,10 @@ BOOL WINAPI ReplaceFileW(LPCWSTR lpReplacedFileName, LPCWSTR lpReplacementFileNa + RtlFreeUnicodeString(&nt_replacement_name); + if (status != STATUS_SUCCESS) + { +- error = RtlNtStatusToDosError(status); ++ if (status == STATUS_ACCESS_DENIED) ++ error = ERROR_ACCESS_DENIED; ++ else ++ error = RtlNtStatusToDosError(status); + goto fail; + } + +diff --git a/dlls/kernel32/tests/file.c b/dlls/kernel32/tests/file.c +index 3354bacf967..fb2440e0379 100644 +--- a/dlls/kernel32/tests/file.c ++++ b/dlls/kernel32/tests/file.c +@@ -3781,7 +3781,7 @@ static void test_ReplaceFileA(void) + */ + SetLastError(0xdeadbeef); + ret = pReplaceFileA(replaced, replacement, backup, 0, 0, 0); +- todo_wine ok(ret == 0 && GetLastError() == ERROR_ACCESS_DENIED, "ReplaceFileA: unexpected error %d\n", GetLastError()); ++ ok(ret == 0 && GetLastError() == ERROR_ACCESS_DENIED, "ReplaceFileA: unexpected error %d\n", GetLastError()); + /* make sure that the replacement file still exists */ + hReplacementFile = CreateFileA(replacement, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0); + ok(hReplacementFile != INVALID_HANDLE_VALUE || +@@ -3813,7 +3813,7 @@ static void test_ReplaceFileA(void) + "unexpected error, replaced file should be able to be opened %d\n", GetLastError()); + /*Calling ReplaceFileA on an exe should succeed*/ + ret = pReplaceFileA(replaced, replacement, NULL, 0, 0, 0); +- todo_wine ok(ret, "ReplaceFileA: unexpected error %d\n", GetLastError()); ++ ok(ret, "ReplaceFileA: unexpected error %d\n", GetLastError()); + CloseHandle(hReplacedFile); + + /* replacement file still exists, make pass w/o "replaced" */ +-- +2.20.1 + diff --git a/patches/kernel32-ReplaceFileW/definition b/patches/kernel32-ReplaceFileW/definition new file mode 100644 index 00000000..f3042419 --- /dev/null +++ b/patches/kernel32-ReplaceFileW/definition @@ -0,0 +1,3 @@ +# Reference +# https://www.winehq.org/pipermail/wine-devel/2018-October/133068.html +Fixes: [33845] kernel32: Correct ReplaceFileW behaviour for warframe diff --git a/patches/patchinstall.sh b/patches/patchinstall.sh index 2739ba2d..10763fab 100755 --- a/patches/patchinstall.sh +++ b/patches/patchinstall.sh @@ -168,6 +168,7 @@ patch_enable_all () enable_kernel32_NeedCurrentDirectoryForExePath="$1" enable_kernel32_PE_Loader_Fixes="$1" enable_kernel32_Processor_Group="$1" + enable_kernel32_ReplaceFileW="$1" enable_kernel32_SCSI_Sysfs="$1" enable_krnl386_exe16_GDT_LDT_Emulation="$1" enable_krnl386_exe16_Invalid_Console_Handles="$1" @@ -645,6 +646,9 @@ patch_enable () kernel32-Processor_Group) enable_kernel32_Processor_Group="$2" ;; + kernel32-ReplaceFileW) + enable_kernel32_ReplaceFileW="$2" + ;; kernel32-SCSI_Sysfs) enable_kernel32_SCSI_Sysfs="$2" ;; @@ -3873,6 +3877,21 @@ if test "$enable_kernel32_Processor_Group" -eq 1; then ) >> "$patchlist" fi +# Patchset kernel32-ReplaceFileW +# | +# | This patchset fixes the following Wine bugs: +# | * [#33845] kernel32: Correct ReplaceFileW behaviour for warframe +# | +# | Modified files: +# | * dlls/kernel32/file.c, dlls/kernel32/tests/file.c +# | +if test "$enable_kernel32_ReplaceFileW" -eq 1; then + patch_apply kernel32-ReplaceFileW/0001-kernel32-Correct-ReplaceFileW-behaviour.patch + ( + printf '%s\n' '+ { "Brock York", "kernel32: Correct ReplaceFileW behaviour.", 1 },'; + ) >> "$patchlist" +fi + # Patchset kernel32-SCSI_Sysfs # | # | This patchset fixes the following Wine bugs: