Rob Shearman : kernel32: Implement ReplaceFileW.

Alexandre Julliard julliard at winehq.org
Tue Feb 12 16:46:16 CST 2008


Module: wine
Branch: master
Commit: c72de7bce2c4c5bda08caa61f140fb4a5358997f
URL:    http://source.winehq.org/git/wine.git/?a=commit;h=c72de7bce2c4c5bda08caa61f140fb4a5358997f

Author: Rob Shearman <rob at codeweavers.com>
Date:   Thu Feb  7 12:43:25 2008 +0000

kernel32: Implement ReplaceFileW.

Based on a patch by Erich Hoover.

---

 dlls/kernel32/file.c |  163 ++++++++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 158 insertions(+), 5 deletions(-)

diff --git a/dlls/kernel32/file.c b/dlls/kernel32/file.c
index acc762b..a5da6c6 100644
--- a/dlls/kernel32/file.c
+++ b/dlls/kernel32/file.c
@@ -1524,14 +1524,167 @@ BOOL WINAPI DeleteFileA( LPCSTR path )
  *           ReplaceFileW   (KERNEL32.@)
  *           ReplaceFile    (KERNEL32.@)
  */
-BOOL WINAPI ReplaceFileW(LPCWSTR lpReplacedFileName,LPCWSTR lpReplacementFileName,
+BOOL WINAPI ReplaceFileW(LPCWSTR lpReplacedFileName, LPCWSTR lpReplacementFileName,
                          LPCWSTR lpBackupFileName, DWORD dwReplaceFlags,
                          LPVOID lpExclude, LPVOID lpReserved)
 {
-    FIXME("(%s,%s,%s,%08x,%p,%p) stub\n",debugstr_w(lpReplacedFileName),debugstr_w(lpReplacementFileName),
-                                          debugstr_w(lpBackupFileName),dwReplaceFlags,lpExclude,lpReserved);
-    SetLastError(ERROR_UNABLE_TO_MOVE_REPLACEMENT);
-    return FALSE;
+    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;
+    NTSTATUS status;
+    IO_STATUS_BLOCK io;
+    OBJECT_ATTRIBUTES attr;
+
+    if (dwReplaceFlags)
+        FIXME("Ignoring flags %x\n", dwReplaceFlags);
+
+    /* First two arguments are mandatory */
+    if (!lpReplacedFileName || !lpReplacementFileName)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        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;
+    attr.ObjectName = NULL;
+    attr.SecurityDescriptor = NULL;
+    attr.SecurityQualityOfService = NULL;
+
+    /* Open the "replaced" file for reading and writing */
+    if (!(RtlDosPathNameToNtPathName_U(lpReplacedFileName, &nt_replaced_name, NULL, NULL)))
+    {
+        error = ERROR_PATH_NOT_FOUND;
+        goto fail;
+    }
+    replaced_flags = lpBackupFileName ? FILE_OPEN : FILE_OPEN_IF;
+    attr.ObjectName = &nt_replaced_name;
+    status = NtOpenFile(&hReplaced, GENERIC_READ|GENERIC_WRITE|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;
+    }
+
+    /*
+     * Open the replacement file for reading, writing, and deleting
+     * (writing and deleting are needed when finished)
+     */
+    if (!(RtlDosPathNameToNtPathName_U(lpReplacementFileName, &nt_replacement_name, NULL, NULL)))
+    {
+        error = ERROR_PATH_NOT_FOUND;
+        goto fail;
+    }
+    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;
+    }
+
+    /* 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,
+                              &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);
+        if (status != STATUS_SUCCESS)
+        {
+            error = RtlNtStatusToDosError(status);
+            goto fail;
+        }
+
+        /* If an existing backup exists then copy over it */
+        if (rename(unix_replaced_name.Buffer, unix_backup_name.Buffer) == -1)
+        {
+            error = ERROR_UNABLE_TO_REMOVE_REPLACED; /* is this correct? */
+            goto fail;
+        }
+    }
+
+    /*
+     * 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 (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;
+        else
+            error = ERROR_UNABLE_TO_MOVE_REPLACEMENT_2;
+        goto fail;
+    }
+    /* 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;
 }
 
 




More information about the wine-cvs mailing list