[KERNEL32]
[reactos.git] / reactos / dll / win32 / kernel32 / client / file / move.c
index 87fbc2f..e71405b 100644 (file)
@@ -1,5 +1,4 @@
-/* $Id: move.c 54326 2011-11-07 00:18:13Z ion $
- *
+/*
  * COPYRIGHT:       See COPYING in the top level directory
  * PROJECT:         ReactOS system libraries
  * FILE:            lib/kernel32/file/file.c
@@ -7,6 +6,7 @@
  * PROGRAMMER:      Ariadne ( ariadne@xs4all.nl)
  *                  Gerhard W. Gruber (sparhawk_at_gmx.at)
  *                  Dmitry Philippov (shedon@mail.ru)
+ *                  Pierre Schweitzer (pierre@reactos.org)
  * UPDATE HISTORY:
  *                  Created 01/11/98
  *                  DP (29/07/2006)
 
 #include <k32.h>
 #include <malloc.h>
+#include <strsafe.h>
 #define NDEBUG
 #include <debug.h>
 DEBUG_CHANNEL(kernel32file);
 
 /* GLOBALS *****************************************************************/
 
-/* FUNCTIONS ****************************************************************/
-static BOOL
-RemoveReadOnlyAttributeW(IN LPCWSTR lpFileName)
+/* DEFINES *****************************************************************/
+typedef struct _COPY_PROGRESS_CONTEXT
 {
-    DWORD Attributes;
-    Attributes = GetFileAttributesW(lpFileName);
-    if (Attributes != INVALID_FILE_ATTRIBUTES)
-    {  
-        return SetFileAttributesW(lpFileName,Attributes - 
-                                             (Attributes & ~FILE_ATTRIBUTE_READONLY));
-    }
-    return FALSE;
-}
-
+    ULONG Flags;
+    LPPROGRESS_ROUTINE UserRoutine;
+    LPVOID UserData;
+} COPY_PROGRESS_CONTEXT, *PCOPY_PROGRESS_CONTEXT;
 
-/***********************************************************************
- *           add_boot_rename_entry
- *
- * Adds an entry to the registry that is loaded when windows boots and
- * checks if there are some files to be removed or renamed/moved.
- * <fn1> has to be valid and <fn2> may be NULL. If both pointers are
- * non-NULL then the file is moved, otherwise it is deleted.  The
- * entry of the registrykey is always appended with two zero
- * terminated strings. If <fn2> is NULL then the second entry is
- * simply a single 0-byte. Otherwise the second filename goes
- * there. The entries are prepended with \??\ before the path and the
- * second filename gets also a '!' as the first character if
- * MOVEFILE_REPLACE_EXISTING is set. After the final string another
- * 0-byte follows to indicate the end of the strings.
- * i.e.:
- * \??\D:\test\file1[0]
- * !\??\D:\test\file1_renamed[0]
- * \??\D:\Test|delete[0]
- * [0]                        <- file is to be deleted, second string empty
- * \??\D:\test\file2[0]
- * !\??\D:\test\file2_renamed[0]
- * [0]                        <- indicates end of strings
- *
- * or:
- * \??\D:\test\file1[0]
- * !\??\D:\test\file1_renamed[0]
- * \??\D:\Test|delete[0]
- * [0]                        <- file is to be deleted, second string empty
- * [0]                        <- indicates end of strings
- *
+/* FUNCTIONS ****************************************************************/
+/*
+ * @implemented
  */
-static BOOL add_boot_rename_entry( LPCWSTR source, LPCWSTR dest, DWORD flags )
+NTSTATUS
+WINAPI
+BasepMoveFileDelayed(IN PUNICODE_STRING ExistingPath,
+                     IN PUNICODE_STRING NewPath,
+                     IN INT KeyId,
+                     IN BOOL CreateIfNotFound)
 {
-    static const WCHAR ValueName[] = {'P','e','n','d','i','n','g',
-                                      'F','i','l','e','R','e','n','a','m','e',
-                                      'O','p','e','r','a','t','i','o','n','s',0};
-
-    UNICODE_STRING KeyName = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control\\Session Manager");
-
-    static const int info_size = FIELD_OFFSET( KEY_VALUE_PARTIAL_INFORMATION, Data );
-
-    OBJECT_ATTRIBUTES ObjectAttributes;
-    UNICODE_STRING nameW, source_name, dest_name;
-    KEY_VALUE_PARTIAL_INFORMATION *info;
-    BOOL rc = FALSE;
-    HANDLE Reboot = NULL;
-    DWORD len1, len2;
-    DWORD DestLen = 0;
-    DWORD DataSize = 0;
-    BYTE *Buffer = NULL;
-    WCHAR *p;
+#define STRING_LENGTH 0x400
     NTSTATUS Status;
+    HANDLE KeyHandle;
+    PVOID Buffer, BufferBegin;
+    OBJECT_ATTRIBUTES ObjectAttributes;
+    PWSTR PendingOperations, BufferWrite;
+    ULONG DataSize, BufferLength, StringLength = STRING_LENGTH;
+    UNICODE_STRING SessionManagerString, PendingOperationsString;
+    /* +6 because a INT shouldn't take more than 6 chars. Especially given the call path */
+    WCHAR PendingOperationsBuffer[sizeof(L"PendingFileRenameOperations") / sizeof(WCHAR) + 6];
 
-    TRACE("add_boot_rename_entry( %S, %S, %d ) \n", source, dest, flags);
-
-    if(dest)
-        DestLen = wcslen(dest);
+    RtlInitUnicodeString(&SessionManagerString, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Session Manager");
 
-    if (!RtlDosPathNameToNtPathName_U( source, &source_name, NULL, NULL ))
+    /* Select appropriate key for adding our file */
+    if (KeyId == 1)
     {
-        SetLastError( ERROR_PATH_NOT_FOUND );
-        return FALSE;
+        PendingOperations = L"PendingFileRenameOperations";
     }
-    dest_name.Buffer = NULL;
-    if (DestLen && !RtlDosPathNameToNtPathName_U( dest, &dest_name, NULL, NULL ))
+    else
     {
-        RtlFreeHeap( RtlGetProcessHeap(), 0, source_name.Buffer );
-        SetLastError( ERROR_PATH_NOT_FOUND );
-        return FALSE;
+        StringCbPrintfW(PendingOperationsBuffer, sizeof(PendingOperationsBuffer), L"PendingFileRenameOperations%d", KeyId);
+        PendingOperations = PendingOperationsBuffer;
     }
+    RtlInitUnicodeString(&PendingOperationsString, PendingOperations);
 
     InitializeObjectAttributes(&ObjectAttributes,
-                               &KeyName,
+                               &SessionManagerString,
                                OBJ_OPENIF | OBJ_CASE_INSENSITIVE,
-                               NULL,
-                               NULL);
-
-     Status = NtCreateKey(&Reboot, 
-                          KEY_QUERY_VALUE | KEY_SET_VALUE,
-                          &ObjectAttributes,
-                          0,
-                          NULL,
-                          REG_OPTION_NON_VOLATILE,
-                          NULL);
-
-     if (Status == STATUS_ACCESS_DENIED)
-     {
-         Status = NtCreateKey(
-             &Reboot, 
-             KEY_QUERY_VALUE | KEY_SET_VALUE,
-             &ObjectAttributes,
-             0,
-             NULL,
-             REG_OPTION_BACKUP_RESTORE,
-             NULL);
-     }
+                               NULL, NULL);
+
+    /* Open parent key */
+    Status = NtCreateKey(&KeyHandle,
+                         GENERIC_READ | GENERIC_WRITE,
+                         &ObjectAttributes, 0, NULL,
+                         REG_OPTION_NON_VOLATILE, NULL);
+    if (Status == STATUS_ACCESS_DENIED)
+    {
+        Status = NtCreateKey(&KeyHandle,
+                             GENERIC_READ | GENERIC_WRITE,
+                             &ObjectAttributes, 0, NULL,
+                             REG_OPTION_BACKUP_RESTORE, NULL);
+    }
 
     if (!NT_SUCCESS(Status))
     {
-        WARN("NtCreateKey() failed (Status 0x%lx)\n", Status);
-        if (source_name.Buffer)
-            RtlFreeHeap(RtlGetProcessHeap(), 0, source_name.Buffer);
-        if (dest_name.Buffer)
-            RtlFreeHeap(RtlGetProcessHeap(), 0, dest_name.Buffer);
-        return FALSE;
+        return Status;
     }
 
-    len1 = source_name.Length + sizeof(WCHAR);
-    if (DestLen)
+    /* Reserve enough to read previous string + to append our with required null chars */
+    BufferLength = NewPath->Length + ExistingPath->Length + STRING_LENGTH + 3 * sizeof(UNICODE_NULL);
+    /* Check we didn't overflow */
+    if (BufferLength < STRING_LENGTH)
     {
-        len2 = dest_name.Length + sizeof(WCHAR);
-        if (flags & MOVEFILE_REPLACE_EXISTING)
-            len2 += sizeof(WCHAR); /* Plus 1 because of the leading '!' */
+        NtClose(KeyHandle);
+        return STATUS_BUFFER_TOO_SMALL;
     }
-    else
+
+    while (TRUE)
     {
-        len2 = sizeof(WCHAR); /* minimum is the 0 characters for the empty second string */
+        /* Allocate output buffer */
+        Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferLength);
+        if (Buffer == NULL)
+        {
+            NtClose(KeyHandle);
+            return STATUS_NO_MEMORY;
+        }
+
+        Status = NtQueryValueKey(KeyHandle,
+                                 &PendingOperationsString,
+                                 KeyValuePartialInformation,
+                                 Buffer, StringLength, &DataSize);
+        if (Status != STATUS_BUFFER_OVERFLOW)
+        {
+            break;
+        }
+
+        /* If buffer was too small, then, reallocate one which is big enough */
+        StringLength = DataSize;
+        RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
+        BufferLength = ExistingPath->Length + StringLength + NewPath->Length + 3 * sizeof(UNICODE_NULL);
+        if (BufferLength < StringLength)
+        {
+            NtClose(KeyHandle);
+            return STATUS_BUFFER_TOO_SMALL;
+        }
     }
 
-    RtlInitUnicodeString( &nameW, ValueName );
-
-    /* First we check if the key exists and if so how many bytes it already contains. */
-    Status = NtQueryValueKey(
-        Reboot,
-        &nameW,
-        KeyValuePartialInformation,
-        NULL,
-        0, 
-        &DataSize );
-    if ((Status == STATUS_BUFFER_OVERFLOW) ||
-        (Status == STATUS_BUFFER_TOO_SMALL))
+    /* Check if it existed - if not, create only IF asked to */
+    if (!NT_SUCCESS(Status) && (Status != STATUS_OBJECT_NAME_NOT_FOUND || !CreateIfNotFound))
     {
-        if (!(Buffer = HeapAlloc(GetProcessHeap(), 0, DataSize + len1 + len2 + sizeof(WCHAR))))
-            goto Quit;
-        Status = NtQueryValueKey(Reboot, &nameW, KeyValuePartialInformation,
-            Buffer, DataSize, &DataSize);
-        if(!NT_SUCCESS(Status))
-            goto Quit;
-        info = (KEY_VALUE_PARTIAL_INFORMATION *)Buffer;
-        if (info->Type != REG_MULTI_SZ) goto Quit;
-        if (DataSize > sizeof(info)) DataSize -= sizeof(WCHAR);  /* remove terminating null (will be added back later) */
+        NtClose(KeyHandle);
+        RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
+        return Status;
+    }
+
+    if (!NT_SUCCESS(Status))
+    {
+        /* We didn't find any - ie, we create, so use complete buffer */
+        BufferBegin = Buffer;
+        BufferWrite = Buffer;
     }
     else
     {
-        DataSize = info_size;
-        if (!(Buffer = HeapAlloc( GetProcessHeap(), 0, DataSize + len1 + len2 + sizeof(WCHAR) )))
-            goto Quit;
+        PKEY_VALUE_PARTIAL_INFORMATION PartialInfo = (PKEY_VALUE_PARTIAL_INFORMATION)Buffer;
+
+        /* Get data, our buffer begin and then where we should append data
+         * (- null char, this is REG_MULTI_SZ, it already includes double termination, we keep only one)
+         */
+        BufferBegin = PartialInfo->Data;
+        BufferWrite = (PWSTR)((ULONG_PTR)PartialInfo->Data + PartialInfo->DataLength - sizeof(UNICODE_NULL));
     }
 
-    memcpy( Buffer + DataSize, source_name.Buffer, len1 );
-    DataSize += len1;
-    p = (WCHAR *)(Buffer + DataSize);
-    if (DestLen)
+    /* First copy existing */
+    RtlCopyMemory(BufferWrite, ExistingPath->Buffer, ExistingPath->Length);
+    BufferWrite += ExistingPath->Length / sizeof(WCHAR);
+    /* And append null char */
+    *BufferWrite = UNICODE_NULL;
+    ++BufferWrite;
+    /* Append destination */
+    RtlCopyMemory(BufferWrite, NewPath->Buffer, NewPath->Length);
+    BufferWrite += NewPath->Length / sizeof(WCHAR);
+    /* And append two null char (end of string) */
+    *BufferWrite = UNICODE_NULL;
+    ++BufferWrite;
+    *BufferWrite = UNICODE_NULL;
+
+    /* Set new value */
+    Status = NtSetValueKey(KeyHandle,
+                           &PendingOperationsString,
+                           0, REG_MULTI_SZ, BufferBegin,
+                           (ULONG_PTR)BufferWrite - (ULONG_PTR)BufferBegin + sizeof(WCHAR));
+
+    NtClose(KeyHandle);
+    RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
+
+    return Status;
+}
+
+
+/*
+ * @unimplemented
+ */
+NTSTATUS
+WINAPI
+BasepNotifyTrackingService(IN PHANDLE ExistingHandle,
+                           IN POBJECT_ATTRIBUTES ObjectAttributes,
+                           IN HANDLE NewHandle,
+                           IN PUNICODE_STRING NewPath)
+{
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+
+/*
+ * @implemented
+ */
+DWORD
+WINAPI
+BasepMoveFileCopyProgress(IN LARGE_INTEGER TotalFileSize,
+                          IN LARGE_INTEGER TotalBytesTransferred,
+                          IN LARGE_INTEGER StreamSize,
+                          IN LARGE_INTEGER StreamBytesTransferred,
+                          IN DWORD dwStreamNumber,
+                          IN DWORD dwCallbackReason,
+                          IN HANDLE hSourceFile,
+                          IN HANDLE hDestinationFile,
+                          IN LPVOID lpData OPTIONAL)
+{
+    DWORD Ret = 0;
+    PCOPY_PROGRESS_CONTEXT Context = (PCOPY_PROGRESS_CONTEXT)lpData;
+
+    if (Context->Flags & MOVEFILE_WRITE_THROUGH)
     {
-        if (flags & MOVEFILE_REPLACE_EXISTING)
-            *p++ = '!';
-        memcpy( p, dest_name.Buffer, len2 );
-        DataSize += len2;
+        if (!dwCallbackReason)
+        {
+            if (StreamBytesTransferred.QuadPart == StreamSize.QuadPart)
+            {
+                FlushFileBuffers(hDestinationFile);
+            }
+        }
     }
-    else
+
+    if (Context->UserRoutine)
     {
-        *p = 0;
-        DataSize += sizeof(WCHAR);
+        Ret = Context->UserRoutine(TotalFileSize,
+                                   TotalBytesTransferred,
+                                   StreamSize,
+                                   StreamBytesTransferred,
+                                   dwStreamNumber,
+                                   dwCallbackReason,
+                                   hSourceFile,
+                                   hDestinationFile,
+                                   Context->UserData);
     }
 
-    /* add final null */
-    p = (WCHAR *)(Buffer + DataSize);
-    *p = 0;
-    DataSize += sizeof(WCHAR);
-
-    rc = NT_SUCCESS(NtSetValueKey(Reboot, &nameW, 0, REG_MULTI_SZ, Buffer + info_size, DataSize - info_size));
-
- Quit:
-    RtlFreeHeap(RtlGetProcessHeap(), 0, source_name.Buffer);
-    if (dest_name.Buffer)
-        RtlFreeHeap(RtlGetProcessHeap(), 0, dest_name.Buffer);
-    NtClose(Reboot);
-    if(Buffer)
-        HeapFree(GetProcessHeap(), 0, Buffer);
-    return(rc);
+    return Ret;
 }
 
 
@@ -227,465 +247,329 @@ static BOOL add_boot_rename_entry( LPCWSTR source, LPCWSTR dest, DWORD flags )
  */
 BOOL
 WINAPI
-MoveFileWithProgressW (
-       LPCWSTR                 lpExistingFileName,
-       LPCWSTR                 lpNewFileName,
-       LPPROGRESS_ROUTINE      lpProgressRoutine,
-       LPVOID                  lpData,
-       DWORD                   dwFlags
-       )
+MoveFileWithProgressW(IN LPCWSTR lpExistingFileName,
+                      IN LPCWSTR lpNewFileName,
+                      IN LPPROGRESS_ROUTINE lpProgressRoutine,
+                      IN LPVOID lpData,
+                      IN DWORD dwFlags)
 {
-       HANDLE hFile = NULL, hNewFile = NULL;
-       IO_STATUS_BLOCK IoStatusBlock;
+    NTSTATUS Status;
+    PWSTR NewBuffer;
+    IO_STATUS_BLOCK IoStatusBlock;
+    COPY_PROGRESS_CONTEXT CopyContext;
     OBJECT_ATTRIBUTES ObjectAttributes;
-       PFILE_RENAME_INFORMATION FileRename;
-       NTSTATUS errCode;
-       BOOL Result;
-       UNICODE_STRING DstPathU;
-       BOOL folder = FALSE;
+    PFILE_RENAME_INFORMATION RenameInfo;
+    UNICODE_STRING NewPathU, ExistingPathU;
+    FILE_ATTRIBUTE_TAG_INFORMATION FileAttrTagInfo;
+    HANDLE SourceHandle = INVALID_HANDLE_VALUE, NewHandle, ExistingHandle;
+    BOOL Ret = FALSE, ReplaceIfExists, DelayUntilReboot, AttemptReopenWithoutReparse;
 
-       TRACE("MoveFileWithProgressW()\n");
+    DPRINT("MoveFileWithProgressW(%S, %S, %p, %p, %x)\n", lpExistingFileName, lpNewFileName, lpProgressRoutine, lpData, dwFlags);
 
-       if (dwFlags & MOVEFILE_DELAY_UNTIL_REBOOT)
-               return add_boot_rename_entry( lpExistingFileName, lpNewFileName, dwFlags );
+    NewPathU.Buffer = NULL;
+    ExistingPathU.Buffer = NULL;
 
-//    if (dwFlags & MOVEFILE_WRITE_THROUGH)
-//        FIXME("MOVEFILE_WRITE_THROUGH unimplemented\n");
+    _SEH2_TRY
+    {
+        /* Don't allow renaming to a disk */
+        if (lpNewFileName && RtlIsDosDeviceName_U(lpNewFileName))
+        {
+            BaseSetLastNTError(STATUS_OBJECT_NAME_COLLISION);
+            _SEH2_LEAVE;
+        }
 
-    if (!lpNewFileName)
-        return DeleteFileW(lpExistingFileName);
+        ReplaceIfExists = !!(dwFlags & MOVEFILE_REPLACE_EXISTING);
 
-    /* validate & translate the filename */
-    if (!RtlDosPathNameToNtPathName_U (lpNewFileName,
-                                          &DstPathU,
-                                          NULL,
-                                          NULL))
-    {
-        WARN("Invalid destination path\n");
-        SetLastError(ERROR_PATH_NOT_FOUND);
-        return FALSE;
-    }
+        /* Get file path */
+        if (!RtlDosPathNameToNtPathName_U(lpExistingFileName, &ExistingPathU, NULL, NULL))
+        {
+            BaseSetLastNTError(STATUS_OBJECT_PATH_NOT_FOUND);
+            _SEH2_LEAVE;
+        }
 
-    InitializeObjectAttributes(&ObjectAttributes,
-                               &DstPathU,
-                               OBJ_CASE_INSENSITIVE,
+        /* Sanitize input */
+        DelayUntilReboot = !!(dwFlags & MOVEFILE_DELAY_UNTIL_REBOOT);
+        if (DelayUntilReboot && (dwFlags & MOVEFILE_CREATE_HARDLINK))
+        {
+            BaseSetLastNTError(STATUS_INVALID_PARAMETER);
+            _SEH2_LEAVE;
+        }
+
+        /* Unless we manage a proper opening, we'll attempt to reopen without reparse support */
+        AttemptReopenWithoutReparse = TRUE;
+        InitializeObjectAttributes(&ObjectAttributes,
+                                   &ExistingPathU,
+                                   OBJ_CASE_INSENSITIVE,
+                                   NULL,
+                                   NULL);
+        /* Attempt to open source file */
+        Status = NtOpenFile(&SourceHandle,
+                            FILE_READ_ATTRIBUTES | DELETE | SYNCHRONIZE,
+                            &ObjectAttributes,
+                            &IoStatusBlock,
+                            FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                            FILE_OPEN_FOR_BACKUP_INTENT | ((dwFlags & MOVEFILE_WRITE_THROUGH) ? FILE_WRITE_THROUGH : 0));
+        if (!NT_SUCCESS(Status))
+        {
+            /* If we failed and the file doesn't exist, don't attempt to reopen without reparse */
+            if (DelayUntilReboot &&
+                (Status == STATUS_SHARING_VIOLATION || Status == STATUS_OBJECT_NAME_NOT_FOUND || Status == STATUS_OBJECT_PATH_NOT_FOUND))
+            {
+                /* Here we don't fail completely, as we postpone the operation to reboot
+                 * File might exist afterwards, and we don't need a handle here
+                 */
+                SourceHandle = INVALID_HANDLE_VALUE;
+                AttemptReopenWithoutReparse = FALSE;
+            }
+            /* If we failed for any reason than unsupported reparse, fail completely */
+            else if (Status != STATUS_INVALID_PARAMETER)
+            {
+                BaseSetLastNTError(Status);
+                _SEH2_LEAVE;
+            }
+        }
+        else
+        {
+            /* We managed to open, so query information */
+            Status = NtQueryInformationFile(SourceHandle,
+                                            &IoStatusBlock,
+                                            &FileAttrTagInfo,
+                                            sizeof(FILE_ATTRIBUTE_TAG_INFORMATION),
+                                            FileAttributeTagInformation);
+            if (!NT_SUCCESS(Status))
+            {
+                /* Do not tolerate any other error than something related to not supported operation */
+                if (Status != STATUS_NOT_IMPLEMENTED && Status != STATUS_INVALID_PARAMETER)
+                {
+                    BaseSetLastNTError(Status);
+                    _SEH2_LEAVE;
+                }
+
+                /* Not a reparse point, no need to reopen, it's fine */
+                AttemptReopenWithoutReparse = FALSE;
+            }
+            /* Validate the reparse point (do we support it?) */
+            else if (FileAttrTagInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT &&
+                     FileAttrTagInfo.ReparseTag != IO_REPARSE_TAG_MOUNT_POINT)
+            {
+                NtClose(SourceHandle);
+                SourceHandle = INVALID_HANDLE_VALUE;
+            }
+        }
+
+        /* Simply reopen if required */
+        if (AttemptReopenWithoutReparse)
+        {
+            Status = NtOpenFile(&SourceHandle,
+                                DELETE | SYNCHRONIZE,
+                                &ObjectAttributes,
+                                &IoStatusBlock,
+                                FILE_SHARE_READ | FILE_SHARE_WRITE,
+                                ((dwFlags & MOVEFILE_WRITE_THROUGH) ? FILE_WRITE_THROUGH : 0));
+            if (!NT_SUCCESS(Status))
+            {
+                BaseSetLastNTError(Status);
+                _SEH2_LEAVE;
+            }
+        }
+
+        /* Nullify string if we're to use it */
+        if (DelayUntilReboot && !lpNewFileName)
+        {
+            RtlInitUnicodeString(&NewPathU, 0);
+        }
+        /* Check whether path exists */
+        else if (!RtlDosPathNameToNtPathName_U(lpNewFileName, &NewPathU, 0, 0))
+        {
+            BaseSetLastNTError(STATUS_OBJECT_PATH_NOT_FOUND);
+            _SEH2_LEAVE;
+        }
+
+        /* Handle postponed renaming */
+        if (DelayUntilReboot)
+        {
+            /* If new file exists and we're allowed to replace, then mark the path with ! */
+            if (ReplaceIfExists && NewPathU.Length)
+            {
+                NewBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, NewPathU.Length + sizeof(WCHAR));
+                if (NewBuffer == NULL)
+                {
+                    BaseSetLastNTError(STATUS_NO_MEMORY);
+                    _SEH2_LEAVE;
+                }
+
+                NewBuffer[0] = L'!';
+                RtlCopyMemory(&NewBuffer[1], NewPathU.Buffer, NewPathU.Length);
+                NewPathU.Length += sizeof(WCHAR);
+                NewPathU.MaximumLength += sizeof(WCHAR);
+                RtlFreeHeap(RtlGetProcessHeap(), 0, NewPathU.Buffer);
+                NewPathU.Buffer = NewBuffer;
+            }
+
+            /* Check whether 'copy' renaming is allowed if required */
+            if (RtlDetermineDosPathNameType_U(lpExistingFileName) == RtlPathTypeUncAbsolute || dwFlags & MOVEFILE_COPY_ALLOWED)
+            {
+                Status = STATUS_INVALID_PARAMETER;
+            }
+            else
+            {
+                /* First, probe 2nd key to see whether it exists - if so, it will be appended there */
+                Status = BasepMoveFileDelayed(&ExistingPathU, &NewPathU, 2, FALSE);
+                if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
+                {
+                    /* If doesn't exist, append to first key first, creating it if it doesn't exist */
+                    Status = BasepMoveFileDelayed(&ExistingPathU, &NewPathU, 1, TRUE);
+
+                    if (Status == STATUS_INSUFFICIENT_RESOURCES)
+                    {
+                        /* If it failed because it's too big, then create 2nd key and put it there */
+                        Status = BasepMoveFileDelayed(&ExistingPathU, &NewPathU, 2, TRUE);
+                    }
+                }
+            }
+
+            /* If we failed at some point, return the error */
+            if (!NT_SUCCESS(Status))
+            {
+                BaseSetLastNTError(Status);
+                _SEH2_LEAVE;
+            }
+
+            Ret = TRUE;
+            _SEH2_LEAVE;
+        }
+
+        /* At that point, we MUST have a source handle */
+        ASSERT(SourceHandle != INVALID_HANDLE_VALUE);
+
+        /* Allocate renaming buffer and fill it */
+        RenameInfo = RtlAllocateHeap(RtlGetProcessHeap(), 0, NewPathU.Length + sizeof(FILE_RENAME_INFORMATION));
+        if (RenameInfo == NULL)
+        {
+            BaseSetLastNTError(STATUS_NO_MEMORY);
+            _SEH2_LEAVE;
+        }
+
+        RtlCopyMemory(&RenameInfo->FileName, NewPathU.Buffer, NewPathU.Length);
+        RenameInfo->ReplaceIfExists = ReplaceIfExists;
+        RenameInfo->RootDirectory = 0;
+        RenameInfo->FileNameLength = NewPathU.Length;
+
+        /* Attempt to rename the file */
+        Status = NtSetInformationFile(SourceHandle,
+                                      &IoStatusBlock,
+                                      RenameInfo,
+                                      NewPathU.Length + sizeof(FILE_RENAME_INFORMATION),
+                                      ((dwFlags & MOVEFILE_CREATE_HARDLINK) ? FileLinkInformation : FileRenameInformation));
+        RtlFreeHeap(RtlGetProcessHeap(), 0, RenameInfo);
+        if (NT_SUCCESS(Status))
+        {
+            /* If it succeed, all fine, quit */
+            Ret = TRUE;
+            _SEH2_LEAVE;
+        }
+        /* If we failed for any other reason than not the same device, fail
+         * If we failed because of different devices, only allow renaming if user allowed copy
+         */
+        if (Status != STATUS_NOT_SAME_DEVICE || !(dwFlags & MOVEFILE_COPY_ALLOWED))
+        {
+            /* ReactOS hack! To be removed once all FSD have proper renaming support
+             * Just leave status to error and leave
+             */
+            if (Status == STATUS_NOT_IMPLEMENTED)
+            {
+                DPRINT1("Forcing copy, renaming not supported by FSD\n");
+            }
+            else
+            {
+                BaseSetLastNTError(Status);
+                _SEH2_LEAVE;
+            }
+        }
+
+        /* Close source file */
+        NtClose(SourceHandle);
+        SourceHandle = INVALID_HANDLE_VALUE;
+
+        /* Issue the copy of the file */
+        CopyContext.Flags = dwFlags;
+        CopyContext.UserRoutine = lpProgressRoutine;
+        CopyContext.UserData = lpData;
+        NewHandle = INVALID_HANDLE_VALUE;
+        ExistingHandle = INVALID_HANDLE_VALUE;
+
+        Ret = BasepCopyFileExW(lpExistingFileName,
+                               lpNewFileName,
+                               BasepMoveFileCopyProgress,
+                               &CopyContext,
                                NULL,
-                               NULL);
+                               (ReplaceIfExists == 0) | COPY_FILE_OPEN_SOURCE_FOR_WRITE,
+                               0,
+                               &ExistingHandle,
+                               &NewHandle);
+        if (!Ret)
+        {
+            /* If it failed, don't leak any handle */
+            if (ExistingHandle != INVALID_HANDLE_VALUE)
+            {
+                CloseHandle(ExistingHandle);
+                ExistingHandle = INVALID_HANDLE_VALUE;
+            }
+        }
+        else if (ExistingHandle != INVALID_HANDLE_VALUE)
+        {
+            if (NewHandle != INVALID_HANDLE_VALUE)
+            {
+                /* If copying succeed, notify */
+                Status = BasepNotifyTrackingService(&ExistingHandle, &ObjectAttributes, NewHandle, &NewPathU);
+                if (!NT_SUCCESS(Status))
+                {
+                    /* Fail in case it had to succeed */
+                    if (dwFlags & MOVEFILE_FAIL_IF_NOT_TRACKABLE)
+                    {
+                        if (NewHandle != INVALID_HANDLE_VALUE)
+                            CloseHandle(NewHandle);
+                        NewHandle = INVALID_HANDLE_VALUE;
+                        DeleteFileW(lpNewFileName);
+                        Ret = FALSE;
+                        BaseSetLastNTError(Status);
+                    }
+                }
+            }
 
-    errCode = NtOpenFile( &hNewFile,
-                          GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
-                          &ObjectAttributes,
-                          &IoStatusBlock,
-                          0,
-                          FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT |
-                          ((dwFlags & MOVEFILE_WRITE_THROUGH) ? FILE_WRITE_THROUGH : 0) );
+            CloseHandle(ExistingHandle);
+            ExistingHandle = INVALID_HANDLE_VALUE;
+        }
 
-    if (NT_SUCCESS(errCode)) /* Destination exists */
-    {
-        NtClose(hNewFile);
+        /* In case copy worked, close file */
+        if (NewHandle != INVALID_HANDLE_VALUE)
+        {
+            CloseHandle(NewHandle);
+            NewHandle = INVALID_HANDLE_VALUE;
+        }
 
-        if (!(dwFlags & MOVEFILE_REPLACE_EXISTING))
+        /* If it succeed, delete source file */
+        if (Ret)
         {
-                       SetLastError(ERROR_ALREADY_EXISTS);
-                       return FALSE;
-       }
-       else if (GetFileAttributesW(lpNewFileName) & FILE_ATTRIBUTE_DIRECTORY)
-       {
-               SetLastError(ERROR_ACCESS_DENIED);
-               return FALSE;
-       }
+            if (!DeleteFileW(lpExistingFileName))
+            {
+                /* Reset file attributes if required */
+                SetFileAttributesW(lpExistingFileName, FILE_ATTRIBUTE_NORMAL);
+                DeleteFileW(lpExistingFileName);
+            }
+        }
     }
+    _SEH2_FINALLY
+    {
+        if (SourceHandle != INVALID_HANDLE_VALUE)
+            NtClose(SourceHandle);
+
+        RtlFreeHeap(RtlGetProcessHeap(), 0, ExistingPathU.Buffer);
+        RtlFreeHeap(RtlGetProcessHeap(), 0, NewPathU.Buffer);
+    }
+    _SEH2_END;
 
-       hFile = CreateFileW (lpExistingFileName,
-                            GENERIC_ALL,
-                            FILE_SHARE_WRITE|FILE_SHARE_READ,
-                            NULL,
-                            OPEN_EXISTING,
-                            FILE_FLAG_BACKUP_SEMANTICS |
-                            ((dwFlags & MOVEFILE_WRITE_THROUGH) ? FILE_FLAG_WRITE_THROUGH : 0),
-                            NULL);
-
-       if (hFile == INVALID_HANDLE_VALUE)
-       {
-           return FALSE;
-       }
-
-       FileRename = RtlAllocateHeap(
-               RtlGetProcessHeap(),
-               HEAP_ZERO_MEMORY,
-               sizeof(FILE_RENAME_INFORMATION) + DstPathU.Length);
-       if( !FileRename ) {
-               CloseHandle(hFile);
-               SetLastError(ERROR_NOT_ENOUGH_MEMORY);
-               return FALSE;
-       }
-       if( dwFlags & MOVEFILE_REPLACE_EXISTING ) {
-               FileRename->ReplaceIfExists = TRUE;
-       }
-       else {
-               FileRename->ReplaceIfExists = FALSE;
-       }
-
-
-       memcpy(FileRename->FileName, DstPathU.Buffer, DstPathU.Length);
-        RtlFreeHeap (RtlGetProcessHeap (),
-                    0,
-                    DstPathU.Buffer);
-
-       FileRename->FileNameLength = DstPathU.Length;
-       errCode = NtSetInformationFile (hFile,
-                                       &IoStatusBlock,
-                                       FileRename,
-                                       sizeof(FILE_RENAME_INFORMATION) + DstPathU.Length,
-                                       FileRenameInformation);
-       CloseHandle(hFile);
-       RtlFreeHeap(RtlGetProcessHeap(), 0, FileRename);
-
-       if (GetFileAttributesW(lpExistingFileName) & FILE_ATTRIBUTE_DIRECTORY)
-       {
-           folder = TRUE;
-       }
-
-       
-       /*
-        *  FIXME:
-        *  Fail now move the folder 
-        *  Before we fail at CreateFileW 
-        */
-        
-        
-       if (NT_SUCCESS(errCode))
-       {
-               Result = TRUE;
-       }
-       else 
-       {
-               if (folder==FALSE)
-               {
-                   Result = CopyFileExW (lpExistingFileName,
-                                     lpNewFileName,
-                                     lpProgressRoutine,
-                                     lpData,
-                                     NULL,
-                                     (dwFlags & MOVEFILE_REPLACE_EXISTING) ? 0 : COPY_FILE_FAIL_IF_EXISTS);
-                   if (Result)
-                   {
-                       /* Cleanup the source file */                   
-                       Result = DeleteFileW (lpExistingFileName);
-                   }
-                  }
-                else
-                {
-                  /* move folder code start */
-                  WIN32_FIND_DATAW findBuffer;
-                  LPWSTR lpExistingFileName2 = NULL;
-                  LPWSTR lpNewFileName2 = NULL; 
-                  LPWSTR lpDeleteFile = NULL;
-                  INT size;
-                  INT size2;
-                  BOOL loop = TRUE;
-                  BOOL Result = FALSE;
-                  INT max_size = MAX_PATH;
-
-
-                                  /* Build the string */
-                  size = wcslen(lpExistingFileName); 
-                  if (size+6> max_size)
-                          max_size = size + 6;
-
-                  lpDeleteFile = (LPWSTR) HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,max_size * sizeof(WCHAR));
-                  if (lpDeleteFile == NULL)               
-                      return FALSE;                              
-
-                  lpNewFileName2 = (LPWSTR) HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,max_size * sizeof(WCHAR));
-                  if (lpNewFileName2 == NULL)
-                  {            
-                    HeapFree(GetProcessHeap(),0,(VOID *)  lpDeleteFile);
-                        return FALSE;
-                  }
-
-                  lpExistingFileName2 = (LPWSTR) HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,max_size * sizeof(WCHAR));
-                  if (lpExistingFileName2 == NULL)
-                  {            
-                    HeapFree(GetProcessHeap(),0,(VOID *)  lpNewFileName2);                                                     
-                    HeapFree(GetProcessHeap(),0,(VOID *) lpDeleteFile);                
-                    return FALSE;
-                  }                               
-               
-                  wcscpy( (WCHAR *)lpExistingFileName2,lpExistingFileName);
-                  wcscpy( (WCHAR *)&lpExistingFileName2[size],L"\\*.*\0");
-         
-                  /* Get the file name */
-                  memset(&findBuffer,0,sizeof(WIN32_FIND_DATAW));
-                  hFile = FindFirstFileW(lpExistingFileName2,  &findBuffer);
-                  if (hFile == INVALID_HANDLE_VALUE) 
-                      loop=FALSE;
-
-                  if (findBuffer.cFileName[0] == L'\0')
-                      loop=FALSE;
-               
-
-                   /* FIXME 
-                        * remove readonly flag from source folder and do not set the readonly flag to dest folder 
-                        */
-                  RemoveReadOnlyAttributeW(lpExistingFileName);
-           RemoveReadOnlyAttributeW(lpNewFileName);
-                  //CreateDirectoryExW(lpExistingFileName,lpNewFileName,NULL);
-                  CreateDirectoryW(lpNewFileName, NULL);
-                                  
-                  /* search the files/folders and move them */
-                  while (loop==TRUE)
-                  {    
-                    Result = TRUE;
-
-                    if ((!wcscmp(findBuffer.cFileName,L"..")) || (!wcscmp(findBuffer.cFileName,L".")))
-                    {            
-                      loop = FindNextFileW(hFile, &findBuffer);                  
-                                 
-                      if (!loop)
-                      {                                         
-                        size = wcslen(lpExistingFileName2)-4;
-                        FindClose(hFile);
-                        hFile = INVALID_HANDLE_VALUE;
-
-                        wcscpy( &lpExistingFileName2[size],L"\0");
-
-                        if (wcsncmp(lpExistingFileName,lpExistingFileName2,size))
-                        {  
-                                  DWORD Attributes;
-
-                                  /* delete folder */                                                                   
-                                  TRACE("MoveFileWithProgressW : Delete folder : %S\n",lpDeleteFile);
-
-                                  /* remove system folder flag other wise we can not delete the folder */
-                                  Attributes = GetFileAttributesW(lpExistingFileName2);
-                   if (Attributes != INVALID_FILE_ATTRIBUTES)
-                   {   
-                     SetFileAttributesW(lpExistingFileName2,(Attributes & ~FILE_ATTRIBUTE_SYSTEM));
-                                  }
-                                  
-                                  RemoveReadOnlyAttributeW(lpExistingFileName2);
-                                                                          
-                                  Result = RemoveDirectoryW(lpExistingFileName2);
-                                  if (Result == FALSE)
-                                      break;
-                                                                
-                                  loop=TRUE;                                                            
-                                  size = wcslen(lpExistingFileName); 
-                               
-                                  if (size+6>max_size)
-                                  {                                   
-                             if (lpNewFileName2 != NULL)                                       
-                                 HeapFree(GetProcessHeap(),0,(VOID *)  lpNewFileName2);
-                          
-                             if (lpExistingFileName2 != NULL)                            
-                                 HeapFree(GetProcessHeap(),0,(VOID *) lpExistingFileName2);            
-                                          
-                             if (lpDeleteFile != NULL)                         
-                                 HeapFree(GetProcessHeap(),0,(VOID *) lpDeleteFile);           
-                         
-                                     return FALSE;
-                                  }
-
-                                  wcscpy( lpExistingFileName2,lpExistingFileName);
-                                  wcscpy( &lpExistingFileName2[size],L"\\*.*\0");
-
-                                  /* Get the file name */
-                                  memset(&findBuffer,0,sizeof(WIN32_FIND_DATAW));
-                                  hFile = FindFirstFileW(lpExistingFileName2, &findBuffer);                     
-                        }
-               }                 
-               continue;                                 
-             }
-                         
-                    if (findBuffer.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
-                    {                 
-
-               /* Build the new src string */                                            
-               size = wcslen(findBuffer.cFileName); 
-               size2= wcslen(lpExistingFileName2);
-
-               if (size2+size+6>max_size)
-               {
-                     FindClose(hFile);                  
-
-                        if (lpNewFileName2 != NULL)                                    
-                             HeapFree(GetProcessHeap(),0,(VOID *)  lpNewFileName2);
-                          
-                        if (lpExistingFileName2 != NULL)                                 
-                             HeapFree(GetProcessHeap(),0,(VOID *) lpExistingFileName2);                
-                                          
-                        if (lpDeleteFile != NULL)                              
-                             HeapFree(GetProcessHeap(),0,(VOID *) lpDeleteFile);               
-                         
-                                return FALSE;                                                   
-                  }
-
-                   wcscpy( &lpExistingFileName2[size2-3],findBuffer.cFileName);                                                  
-                wcscpy( &lpExistingFileName2[size2+size-3],L"\0");
-                          
-
-                          /* Continue */
-                          wcscpy( lpDeleteFile,lpExistingFileName2);
-                  wcscpy( &lpExistingFileName2[size2+size-3],L"\\*.*\0");
-          
-                 
-                  /* Build the new dst string */
-                  size = wcslen(lpExistingFileName2) + wcslen(lpNewFileName);
-                  size2 = wcslen(lpExistingFileName);
-                 
-                  if (size>max_size)
-                  {
-                    FindClose(hFile);                   
-
-                        if (lpNewFileName2 != NULL)                                    
-                             HeapFree(GetProcessHeap(),0,(VOID *)  lpNewFileName2);
-                          
-                        if (lpExistingFileName2 != NULL)                                 
-                             HeapFree(GetProcessHeap(),0,(VOID *) lpExistingFileName2);                
-                                          
-                        if (lpDeleteFile != NULL)                              
-                             HeapFree(GetProcessHeap(),0,(VOID *) lpDeleteFile);               
-                         
-                                return FALSE;                       
-                  }
-
-                  wcscpy( lpNewFileName2,lpNewFileName);                                 
-                  size = wcslen(lpNewFileName);                 
-                  wcscpy( &lpNewFileName2[size], &lpExistingFileName2[size2]);
-                  size = wcslen(lpNewFileName2);
-                  wcscpy( &lpNewFileName2[size-4],L"\0");
-                  
-                  /* Create Folder */            
-
-                          /* FIXME 
-                           * remove readonly flag from source folder and do not set the readonly flag to dest folder 
-                               */
-                          RemoveReadOnlyAttributeW(lpDeleteFile);
-                          RemoveReadOnlyAttributeW(lpNewFileName2);
-
-                          CreateDirectoryW(lpNewFileName2,NULL);
-                  //CreateDirectoryExW(lpDeleteFile, lpNewFileName2,NULL);
-                          
-
-                          /* set new search path  from src string */
-                  FindClose(hFile);
-                  memset(&findBuffer,0,sizeof(WIN32_FIND_DATAW));
-                  hFile = FindFirstFileW(lpExistingFileName2, &findBuffer);
-                }
-                    else
-                    {
-                 
-                  /* Build the new string */                                                             
-                  size = wcslen(findBuffer.cFileName); 
-                  size2= wcslen(lpExistingFileName2);
-                  wcscpy( lpDeleteFile,lpExistingFileName2);      
-                  wcscpy( &lpDeleteFile[size2-3],findBuffer.cFileName);           
-                 
-                  /* Build dest string */
-                  size = wcslen(lpDeleteFile) + wcslen(lpNewFileName);
-                  size2 = wcslen(lpExistingFileName);
-
-                  if (size>max_size)
-                  {                                              
-                             FindClose(hFile);                  
-
-                         if (lpNewFileName2 != NULL)                                   
-                             HeapFree(GetProcessHeap(),0,(VOID *)  lpNewFileName2);
-                          
-                         if (lpExistingFileName2 != NULL)                                
-                             HeapFree(GetProcessHeap(),0,(VOID *) lpExistingFileName2);                
-                                          
-                         if (lpDeleteFile != NULL)                             
-                             HeapFree(GetProcessHeap(),0,(VOID *) lpDeleteFile);               
-                         
-                                 return FALSE;
-                          }
-                 
-               wcscpy( lpNewFileName2,lpNewFileName);                            
-               size = wcslen(lpNewFileName);            
-               wcscpy(&lpNewFileName2[size],&lpDeleteFile[size2]);
-                
-              
-                          /* overrite existsen file, if the file got the flag have readonly 
-                           * we need reomve that flag 
-                               */
-                          
-                           /* copy file */
-                          
-                          TRACE("MoveFileWithProgressW : Copy file : %S to %S\n",lpDeleteFile, lpNewFileName2);
-                          RemoveReadOnlyAttributeW(lpDeleteFile);
-                          RemoveReadOnlyAttributeW(lpNewFileName2);
-                         
-                          Result = CopyFileExW (lpDeleteFile,
-                                     lpNewFileName2,
-                                     lpProgressRoutine,
-                                     lpData,
-                                     NULL,
-                                     0);
-
-                          if (Result == FALSE)                                
-                   break;
-              
-               /* delete file */                                           
-                          TRACE("MoveFileWithProgressW : remove readonly flag from file : %S\n",lpNewFileName2);
-                          Result = RemoveReadOnlyAttributeW(lpDeleteFile);
-                          if (Result == FALSE)
-                              break;
-
-               TRACE("MoveFileWithProgressW : Delete file : %S\n",lpDeleteFile);
-                          Result = DeleteFileW(lpDeleteFile);
-               if (Result == FALSE)                               
-                   break;
-              
-             }                       
-             loop = FindNextFileW(hFile, &findBuffer);           
-           }
-
-           
-                  /* Remove last folder */
-           if ((loop == FALSE) && (Result != FALSE))
-                  {
-                    DWORD Attributes;
-
-                        Attributes = GetFileAttributesW(lpDeleteFile);
-             if (Attributes != INVALID_FILE_ATTRIBUTES)
-             { 
-                SetFileAttributesW(lpDeleteFile,(Attributes & ~FILE_ATTRIBUTE_SYSTEM));
-                        }
-                                                                                                        
-                    Result = RemoveDirectoryW(lpExistingFileName);                  
-                  }
-           
-                  /* Cleanup */
-                  FindClose(hFile);    
-                  
-                  if (lpNewFileName2 != NULL)
-                  {            
-                    HeapFree(GetProcessHeap(),0,(VOID *)  lpNewFileName2);
-                    lpNewFileName2 = NULL;
-                  }
-
-                  if (lpExistingFileName2 != NULL)
-                  {      
-                    HeapFree(GetProcessHeap(),0,(VOID *) lpExistingFileName2);         
-                    lpExistingFileName2 = NULL;
-                  }
-
-                  if (lpDeleteFile != NULL)
-                  {            
-                    HeapFree(GetProcessHeap(),0,(VOID *) lpDeleteFile);                
-                    lpDeleteFile = NULL;
-                  }
-
-           return Result;
-
-                  // end move folder code               
-                 }
-       }
-       
-       
-       return Result;
+    return Ret;
 }
 
 
@@ -694,33 +578,39 @@ MoveFileWithProgressW (
  */
 BOOL
 WINAPI
-MoveFileWithProgressA (
-       LPCSTR                  lpExistingFileName,
-       LPCSTR                  lpNewFileName,
-       LPPROGRESS_ROUTINE      lpProgressRoutine,
-       LPVOID                  lpData,
-       DWORD                   dwFlags
-       )
+MoveFileWithProgressA(IN LPCSTR lpExistingFileName,
+                      IN LPCSTR lpNewFileName OPTIONAL,
+                      IN LPPROGRESS_ROUTINE lpProgressRoutine OPTIONAL,
+                      IN LPVOID lpData OPTIONAL,
+                      IN DWORD dwFlags)
 {
-       PWCHAR ExistingFileNameW;
-   PWCHAR NewFileNameW;
-       BOOL ret;
+    BOOL Ret;
+    UNICODE_STRING ExistingFileNameW, NewFileNameW;
 
-   if (!(ExistingFileNameW = FilenameA2W(lpExistingFileName, FALSE)))
-      return FALSE;
+    if (!Basep8BitStringToDynamicUnicodeString(&ExistingFileNameW, lpExistingFileName))
+    {
+        return FALSE;
+    }
 
-   if (!(NewFileNameW= FilenameA2W(lpNewFileName, TRUE)))
-      return FALSE;
+    if (lpNewFileName)
+    {
+        if (!Basep8BitStringToDynamicUnicodeString(&NewFileNameW, lpNewFileName))
+        {
+            RtlFreeUnicodeString(&ExistingFileNameW);
+            return FALSE;
+        }
+    }
+    else
+    {
+        NewFileNameW.Buffer = NULL;
+    }
 
-   ret = MoveFileWithProgressW (ExistingFileNameW ,
-                                   NewFileNameW,
-                                       lpProgressRoutine,
-                                       lpData,
-                                       dwFlags);
+    Ret = MoveFileWithProgressW(ExistingFileNameW.Buffer, NewFileNameW.Buffer, lpProgressRoutine, lpData, dwFlags);
 
-   RtlFreeHeap (RtlGetProcessHeap (), 0, NewFileNameW);
+    RtlFreeUnicodeString(&ExistingFileNameW);
+    RtlFreeUnicodeString(&NewFileNameW);
 
-       return ret;
+    return Ret;
 }
 
 
@@ -729,14 +619,14 @@ MoveFileWithProgressA (
  */
 BOOL
 WINAPI
-MoveFileW (
-       LPCWSTR lpExistingFileName,
-       LPCWSTR lpNewFileName
-       )
+MoveFileW(IN LPCWSTR lpExistingFileName,
+          IN LPCWSTR lpNewFileName)
 {
-       return MoveFileExW (lpExistingFileName,
-                           lpNewFileName,
-                           MOVEFILE_COPY_ALLOWED);
+    return MoveFileWithProgressW(lpExistingFileName,
+                                 lpNewFileName,
+                                 NULL,
+                                 NULL,
+                                 MOVEFILE_COPY_ALLOWED);
 }
 
 
@@ -745,17 +635,15 @@ MoveFileW (
  */
 BOOL
 WINAPI
-MoveFileExW (
-       LPCWSTR lpExistingFileName,
-       LPCWSTR lpNewFileName,
-       DWORD   dwFlags
-       )
+MoveFileExW(IN LPCWSTR lpExistingFileName,
+            IN LPCWSTR lpNewFileName OPTIONAL,
+            IN DWORD dwFlags)
 {
-       return MoveFileWithProgressW (lpExistingFileName,
-                                     lpNewFileName,
-                                     NULL,
-                                     NULL,
-                                     dwFlags);
+    return MoveFileWithProgressW(lpExistingFileName,
+                                lpNewFileName,
+                                NULL,
+                                NULL,
+                                dwFlags);
 }
 
 
@@ -764,14 +652,14 @@ MoveFileExW (
  */
 BOOL
 WINAPI
-MoveFileA (
-       LPCSTR  lpExistingFileName,
-       LPCSTR  lpNewFileName
-       )
+MoveFileA(IN LPCSTR lpExistingFileName,
+          IN LPCSTR lpNewFileName)
 {
-       return MoveFileExA (lpExistingFileName,
-                           lpNewFileName,
-                           MOVEFILE_COPY_ALLOWED);
+    return MoveFileWithProgressA(lpExistingFileName,
+                                 lpNewFileName,
+                                NULL,
+                                NULL,
+                                 MOVEFILE_COPY_ALLOWED);
 }
 
 
@@ -780,17 +668,15 @@ MoveFileA (
  */
 BOOL
 WINAPI
-MoveFileExA (
-       LPCSTR  lpExistingFileName,
-       LPCSTR  lpNewFileName,
-       DWORD   dwFlags
-       )
+MoveFileExA(IN LPCSTR lpExistingFileName,
+            IN LPCSTR lpNewFileName OPTIONAL,
+            IN DWORD dwFlags)
 {
-       return MoveFileWithProgressA (lpExistingFileName,
-                                     lpNewFileName,
-                                     NULL,
-                                     NULL,
-                                     dwFlags);
+    return MoveFileWithProgressA(lpExistingFileName,
+                                 lpNewFileName,
+                                 NULL,
+                                 NULL,
+                                 dwFlags);
 }
 
 /*
@@ -798,50 +684,57 @@ MoveFileExA (
  */
 BOOL
 WINAPI
-ReplaceFileA(
-    LPCSTR  lpReplacedFileName,
-    LPCSTR  lpReplacementFileName,
-    LPCSTR  lpBackupFileName,
-    DWORD   dwReplaceFlags,
-    LPVOID  lpExclude,
-    LPVOID  lpReserved
-    )
+ReplaceFileA(IN LPCSTR lpReplacedFileName,
+             IN LPCSTR lpReplacementFileName,
+             IN LPCSTR lpBackupFileName OPTIONAL,
+             IN DWORD dwReplaceFlags,
+             IN LPVOID lpExclude,
+             IN LPVOID lpReserved)
 {
-    WCHAR *replacedW, *replacementW, *backupW = NULL;
-    BOOL ret;
+    BOOL Ret;
+    UNICODE_STRING ReplacedFileNameW, ReplacementFileNameW, BackupFileNameW;
 
-    /* This function only makes sense when the first two parameters are defined */
-    if (!lpReplacedFileName || !(replacedW = FilenameA2W(lpReplacedFileName, TRUE)))
+    if (!lpReplacedFileName || !lpReplacementFileName || lpExclude || lpReserved || dwReplaceFlags & ~(REPLACEFILE_WRITE_THROUGH | REPLACEFILE_IGNORE_MERGE_ERRORS))
     {
         SetLastError(ERROR_INVALID_PARAMETER);
         return FALSE;
     }
 
-    if (!lpReplacementFileName || !(replacementW = FilenameA2W(lpReplacementFileName, TRUE)))
+    if (!Basep8BitStringToDynamicUnicodeString(&ReplacedFileNameW, lpReplacedFileName))
     {
-        HeapFree(GetProcessHeap(), 0, replacedW);
-        SetLastError(ERROR_INVALID_PARAMETER);
         return FALSE;
     }
 
-    /* The backup parameter, however, is optional */
+    if (!Basep8BitStringToDynamicUnicodeString(&ReplacementFileNameW, lpReplacementFileName))
+    {
+        RtlFreeUnicodeString(&ReplacedFileNameW);
+        return FALSE;
+    }
+
     if (lpBackupFileName)
     {
-        if (!(backupW = FilenameA2W(lpBackupFileName, TRUE)))
+        if (!Basep8BitStringToDynamicUnicodeString(&BackupFileNameW, lpBackupFileName))
         {
-            HeapFree(GetProcessHeap(), 0, replacedW);
-            HeapFree(GetProcessHeap(), 0, replacementW);
-            SetLastError(ERROR_INVALID_PARAMETER);
+            RtlFreeUnicodeString(&ReplacementFileNameW);
+            RtlFreeUnicodeString(&ReplacedFileNameW);
             return FALSE;
         }
     }
+    else
+    {
+        BackupFileNameW.Buffer = NULL;
+    }
 
-    ret = ReplaceFileW(replacedW, replacementW, backupW, dwReplaceFlags, lpExclude, lpReserved);
-    HeapFree(GetProcessHeap(), 0, replacedW);
-    HeapFree(GetProcessHeap(), 0, replacementW);
-    HeapFree(GetProcessHeap(), 0, backupW);
+    Ret = ReplaceFileW(ReplacedFileNameW.Buffer, ReplacementFileNameW.Buffer, BackupFileNameW.Buffer, dwReplaceFlags, 0, 0);
 
-    return ret;
+    if (lpBackupFileName)
+    {
+        RtlFreeUnicodeString(&BackupFileNameW);
+    }
+    RtlFreeUnicodeString(&ReplacementFileNameW);
+    RtlFreeUnicodeString(&ReplacedFileNameW);
+
+    return Ret;
 }
 
 /*
@@ -991,7 +884,7 @@ Cleanup:
     /* If there was an error, set the error code */
     if(!Ret)
     {
-        TRACE("ReplaceFileW failed (error=%d)\n", Error);
+        TRACE("ReplaceFileW failed (error=%lu)\n", Error);
         SetLastError(Error);
     }
     return Ret;