Zebediah Figura : kernel32: Reimplement ReplaceFile() on top of MoveFileEx().
Alexandre Julliard
julliard at winehq.org
Fri Mar 13 15:24:43 CDT 2020
Module: wine
Branch: master
Commit: 5e218fe758fe6beed5c7ad73405eccf33c307e6d
URL: https://source.winehq.org/git/wine.git/?a=commit;h=5e218fe758fe6beed5c7ad73405eccf33c307e6d
Author: Zebediah Figura <z.figura12 at gmail.com>
Date: Thu Mar 12 21:30:30 2020 -0500
kernel32: Reimplement ReplaceFile() on top of MoveFileEx().
Signed-off-by: Zebediah Figura <zfigura at codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard at winehq.org>
---
dlls/kernel32/file.c | 140 +++++++++++++--------------------------------------
1 file changed, 35 insertions(+), 105 deletions(-)
diff --git a/dlls/kernel32/file.c b/dlls/kernel32/file.c
index b2979f9622..2a2ace5dd8 100644
--- a/dlls/kernel32/file.c
+++ b/dlls/kernel32/file.c
@@ -423,11 +423,7 @@ BOOL WINAPI ReplaceFileW(LPCWSTR lpReplacedFileName, LPCWSTR lpReplacementFileNa
LPVOID lpExclude, LPVOID lpReserved)
{
UNICODE_STRING nt_replaced_name, nt_replacement_name;
- ANSI_STRING unix_replaced_name, unix_replacement_name, unix_backup_name;
- HANDLE hReplaced = NULL, hReplacement = NULL, hBackup = NULL;
- DWORD error = ERROR_SUCCESS;
- UINT replaced_flags;
- BOOL ret = FALSE;
+ HANDLE hReplacement = NULL;
NTSTATUS status;
IO_STATUS_BLOCK io;
OBJECT_ATTRIBUTES attr;
@@ -447,10 +443,6 @@ BOOL WINAPI ReplaceFileW(LPCWSTR lpReplacedFileName, LPCWSTR lpReplacementFileNa
return FALSE;
}
- unix_replaced_name.Buffer = NULL;
- unix_replacement_name.Buffer = NULL;
- unix_backup_name.Buffer = NULL;
-
attr.Length = sizeof(attr);
attr.RootDirectory = 0;
attr.Attributes = OBJ_CASE_INSENSITIVE;
@@ -461,39 +453,21 @@ BOOL WINAPI ReplaceFileW(LPCWSTR lpReplacedFileName, LPCWSTR lpReplacementFileNa
/* Open the "replaced" file for reading */
if (!(RtlDosPathNameToNtPathName_U(lpReplacedFileName, &nt_replaced_name, NULL, NULL)))
{
- error = ERROR_PATH_NOT_FOUND;
- goto fail;
+ SetLastError( ERROR_PATH_NOT_FOUND );
+ return FALSE;
}
- replaced_flags = lpBackupFileName ? FILE_OPEN : FILE_OPEN_IF;
attr.ObjectName = &nt_replaced_name;
- 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);
- if (status != STATUS_SUCCESS)
- {
- if (status == STATUS_OBJECT_NAME_NOT_FOUND)
- error = ERROR_FILE_NOT_FOUND;
- else
- error = ERROR_UNABLE_TO_REMOVE_REPLACED;
- goto fail;
- }
/* Replacement should fail if replaced is READ_ONLY */
- status = NtQueryInformationFile(hReplaced, &io, &info, sizeof(info), FileBasicInformation);
+ status = NtQueryAttributesFile(&attr, &info);
+ RtlFreeUnicodeString(&nt_replaced_name);
if (status != STATUS_SUCCESS)
- {
- error = RtlNtStatusToDosError(status);
- goto fail;
- }
+ return set_ntstatus( status );
if (info.FileAttributes & FILE_ATTRIBUTE_READONLY)
{
- error = ERROR_ACCESS_DENIED;
- goto fail;
+ SetLastError( ERROR_ACCESS_DENIED );
+ return FALSE;
}
/*
@@ -502,65 +476,41 @@ BOOL WINAPI ReplaceFileW(LPCWSTR lpReplacedFileName, LPCWSTR lpReplacementFileNa
*/
if (!(RtlDosPathNameToNtPathName_U(lpReplacementFileName, &nt_replacement_name, NULL, NULL)))
{
- error = ERROR_PATH_NOT_FOUND;
- goto fail;
+ SetLastError( ERROR_PATH_NOT_FOUND );
+ return FALSE;
}
attr.ObjectName = &nt_replacement_name;
status = NtOpenFile(&hReplacement,
GENERIC_READ|GENERIC_WRITE|DELETE|WRITE_DAC|SYNCHRONIZE,
&attr, &io, 0,
FILE_SYNCHRONOUS_IO_NONALERT|FILE_NON_DIRECTORY_FILE);
- if (status == STATUS_SUCCESS)
- status = wine_nt_to_unix_file_name(&nt_replacement_name, &unix_replacement_name, FILE_OPEN, FALSE);
RtlFreeUnicodeString(&nt_replacement_name);
if (status != STATUS_SUCCESS)
- {
- error = RtlNtStatusToDosError(status);
- goto fail;
- }
+ return set_ntstatus( status );
+ NtClose( hReplacement );
/* If the user wants a backup then that needs to be performed first */
if (lpBackupFileName)
{
- UNICODE_STRING nt_backup_name;
- FILE_BASIC_INFORMATION replaced_info;
-
- /* Obtain the file attributes from the "replaced" file */
- status = NtQueryInformationFile(hReplaced, &io, &replaced_info,
- sizeof(replaced_info),
- FileBasicInformation);
- if (status != STATUS_SUCCESS)
- {
- error = RtlNtStatusToDosError(status);
- goto fail;
- }
-
- if (!(RtlDosPathNameToNtPathName_U(lpBackupFileName, &nt_backup_name, NULL, NULL)))
- {
- error = ERROR_PATH_NOT_FOUND;
- goto fail;
- }
- attr.ObjectName = &nt_backup_name;
- /* Open the backup with permissions to write over it */
- status = NtCreateFile(&hBackup, GENERIC_WRITE | SYNCHRONIZE,
- &attr, &io, NULL, replaced_info.FileAttributes,
- FILE_SHARE_WRITE, FILE_OPEN_IF,
- FILE_SYNCHRONOUS_IO_NONALERT|FILE_NON_DIRECTORY_FILE,
- NULL, 0);
- if (status == STATUS_SUCCESS)
- status = wine_nt_to_unix_file_name(&nt_backup_name, &unix_backup_name, FILE_OPEN_IF, FALSE);
- RtlFreeUnicodeString(&nt_backup_name);
- if (status != STATUS_SUCCESS)
- {
- error = RtlNtStatusToDosError(status);
- goto fail;
- }
+ if (!MoveFileExW( lpReplacedFileName, lpBackupFileName, MOVEFILE_REPLACE_EXISTING ))
+ return FALSE;
+ }
+ else
+ {
+ /* ReplaceFile() can replace an open target. To do this, we need to move
+ * it out of the way first. */
+ static const WCHAR prefixW[] = {'r','f',0};
+ WCHAR temp_path[MAX_PATH], temp_file[MAX_PATH];
+
+ if (!GetTempPathW( ARRAY_SIZE(temp_path), temp_path )
+ || !GetTempFileNameW( temp_path, prefixW, 0, temp_file )
+ || !MoveFileExW( lpReplacedFileName, temp_file, MOVEFILE_REPLACE_EXISTING ))
+ return FALSE;
- /* If an existing backup exists then copy over it */
- if (rename(unix_replaced_name.Buffer, unix_backup_name.Buffer) == -1)
+ if (!DeleteFileW( temp_file ))
{
- error = ERROR_UNABLE_TO_REMOVE_REPLACED; /* is this correct? */
- goto fail;
+ SetLastError( ERROR_UNABLE_TO_REMOVE_REPLACED );
+ return FALSE;
}
}
@@ -568,37 +518,17 @@ BOOL WINAPI ReplaceFileW(LPCWSTR lpReplacedFileName, LPCWSTR lpReplacementFileNa
* Now that the backup has been performed (if requested), copy the replacement
* into place
*/
- if (rename(unix_replacement_name.Buffer, unix_replaced_name.Buffer) == -1)
+ if (!MoveFileExW( lpReplacementFileName, lpReplacedFileName, 0 ))
{
- if (errno == EACCES)
- {
- /* Inappropriate permissions on "replaced", rename will fail */
- error = ERROR_UNABLE_TO_REMOVE_REPLACED;
- goto fail;
- }
/* on failure we need to indicate whether a backup was made */
if (!lpBackupFileName)
- error = ERROR_UNABLE_TO_MOVE_REPLACEMENT;
+ SetLastError( ERROR_UNABLE_TO_MOVE_REPLACEMENT );
else
- error = ERROR_UNABLE_TO_MOVE_REPLACEMENT_2;
- goto fail;
+ SetLastError( ERROR_UNABLE_TO_MOVE_REPLACEMENT_2 );
+ return FALSE;
}
- /* Success! */
- ret = TRUE;
-
- /* Perform resource cleanup */
-fail:
- if (hBackup) CloseHandle(hBackup);
- if (hReplaced) CloseHandle(hReplaced);
- if (hReplacement) CloseHandle(hReplacement);
- RtlFreeAnsiString(&unix_backup_name);
- RtlFreeAnsiString(&unix_replacement_name);
- RtlFreeAnsiString(&unix_replaced_name);
-
- /* If there was an error, set the error code */
- if(!ret)
- SetLastError(error);
- return ret;
+
+ return TRUE;
}
More information about the wine-cvs
mailing list