[KERNEL32]
[reactos.git] / reactos / dll / win32 / kernel32 / client / file / move.c
index e3c87c1..0f476ed 100644 (file)
@@ -6,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 *****************************************************************/
 
+/* DEFINES *****************************************************************/
+typedef struct _COPY_PROGRESS_CONTEXT
+{
+    ULONG Flags;
+    LPPROGRESS_ROUTINE UserRoutine;
+    LPVOID UserData;
+} COPY_PROGRESS_CONTEXT, *PCOPY_PROGRESS_CONTEXT;
+
 /* FUNCTIONS ****************************************************************/
-static BOOL
-RemoveReadOnlyAttributeW(IN LPCWSTR lpFileName)
+/*
+ * @implemented
+ */
+NTSTATUS
+WINAPI
+BasepMoveFileDelayed(IN PUNICODE_STRING ExistingPath,
+                     IN PUNICODE_STRING NewPath,
+                     IN INT KeyId,
+                     IN BOOL CreateIfNotFound)
 {
-    DWORD Attributes;
-    Attributes = GetFileAttributesW(lpFileName);
-    if (Attributes != INVALID_FILE_ATTRIBUTES)
-    {  
-        return SetFileAttributesW(lpFileName,Attributes - 
-                                             (Attributes & ~FILE_ATTRIBUTE_READONLY));
-    }
-    return FALSE;
+#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];
+
+    RtlInitUnicodeString(&SessionManagerString, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Session Manager");
+
+    /* Select appropriate key for adding our file */
+    if (KeyId == 1)
+    {
+        PendingOperations = L"PendingFileRenameOperations";
+    }
+    else
+    {
+        StringCbPrintfW(PendingOperationsBuffer, sizeof(PendingOperationsBuffer), L"PendingFileRenameOperations%d", KeyId);
+        PendingOperations = PendingOperationsBuffer;
+    }
+    RtlInitUnicodeString(&PendingOperationsString, PendingOperations);
+
+    InitializeObjectAttributes(&ObjectAttributes,
+                               &SessionManagerString,
+                               OBJ_OPENIF | OBJ_CASE_INSENSITIVE,
+                               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))
+    {
+        return Status;
+    }
+
+    /* Reserve enough to read previous string + to append our with required null chars */
+    BufferLength = NewPath->Length + ExistingPath->Length + STRING_LENGTH + 3 * sizeof(UNICODE_NULL);
+
+    while (TRUE)
+    {
+        /* 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);
+        /* Check we didn't overflow */
+        if (BufferLength < StringLength)
+        {
+            NtClose(KeyHandle);
+            return 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))
+    {
+        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
+    {
+        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));
+    }
+
+    /* 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;
 }
 
 
-/***********************************************************************
- *           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
- *
+/*
+ * @implemented
  */
-static BOOL add_boot_rename_entry( LPCWSTR source, LPCWSTR dest, DWORD flags )
+DWORD
+WINAPI
+BasepGetComputerNameFromNtPath(IN PUNICODE_STRING NewPath,
+                               IN HANDLE NewHandle,
+                               OUT PWSTR ComputerName,
+                               IN OUT PULONG ComputerNameLength)
 {
-    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};
+    BOOL Query = FALSE;
+    WCHAR Letter;
+    PWSTR AbsolutePath, EndOfName;
+    USHORT AbsolutePathLength, NameLength;
+    WCHAR TargetDevice[0x105];
+    WCHAR DeviceName[] = {'A', ':', '\0'}; /* Init to something, will be set later */
+    UNICODE_STRING UncString = RTL_CONSTANT_STRING(L"\\??\\UNC\\");
+    UNICODE_STRING GlobalString = RTL_CONSTANT_STRING(L"\\??\\");
+
+    DPRINT("BasepGetComputerNameFromNtPath(%wZ, %p, %p, %lu)\n", NewPath, NewHandle, ComputerName, ComputerNameLength);
+
+    /* If it's an UNC path */
+    if (RtlPrefixUnicodeString(&UncString, NewPath, TRUE))
+    {
+        /* Check for broken caller */
+        if (NewPath->Length <= UncString.Length)
+        {
+            return ERROR_BAD_PATHNAME;
+        }
 
-    UNICODE_STRING KeyName = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control\\Session Manager");
+        /* Skip UNC prefix */
+        AbsolutePath = &NewPath->Buffer[UncString.Length / sizeof(WCHAR)];
+        AbsolutePathLength = NewPath->Length - UncString.Length;
 
-    static const int info_size = FIELD_OFFSET( KEY_VALUE_PARTIAL_INFORMATION, Data );
+        /* And query DFS */
+        Query = TRUE;
+    }
+    /* Otherwise, we have to be in global (NT path!), with drive letter */
+    else if (RtlPrefixUnicodeString(&GlobalString, NewPath, TRUE) && NewPath->Buffer[5] == ':')
+    {
+        /* Path is like that: \??\C:\Complete Path\To File.ext */
+        /* Get the letter and upcase it if required */
+        Letter = NewPath->Buffer[4];
+        if (Letter >= 'a' && Letter <= 'z')
+        {
+            Letter -= ('a' - 'A');
+        }
+        DeviceName[0] = Letter;
 
-    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;
+        /* Query the associated DOS device */
+        if (!QueryDosDeviceW(DeviceName, TargetDevice, ARRAYSIZE(TargetDevice)))
+        {
+            return GetLastError();
+        }
+
+        /* If that's a network share */
+        if (TargetDevice == wcsstr(TargetDevice, L"\\Device\\LanmanRedirector\\;"))
+        {
+            /* Path is like that: \Device\LanmanRedirector\;C:0000000000000000\Complete Path\To File.ext */
+            /* Check we have the correct drive letter */
+            if (TargetDevice[26] == DeviceName[0] &&
+                TargetDevice[27] == ':')
+            {
+                /* Check for the path begin, computer name is before */
+                PWSTR Path = wcschr(&TargetDevice[28], '\\');
+                if (Path == NULL)
+                {
+                    return ERROR_BAD_PATHNAME;
+                }
+
+                AbsolutePath = Path + 1;
+                AbsolutePathLength = sizeof(WCHAR) * (ARRAYSIZE(TargetDevice) - (AbsolutePath - TargetDevice));
+            }
+            else
+            {
+                return ERROR_BAD_PATHNAME;
+            }
+        }
+        /* If it's a local device */
+        else if (TargetDevice == wcsstr(TargetDevice, L"\\Device\\Harddisk")
+                 || TargetDevice == wcsstr(TargetDevice, L"\\Device\\CdRom")
+                 || TargetDevice == wcsstr(TargetDevice, L"\\Device\\Floppy"))
+        {
+            /* Just query the computer name */
+            if (!GetComputerNameW(ComputerName, ComputerNameLength))
+            {
+                return GetLastError();
+            }
+
+            return ERROR_SUCCESS;
+        }
+        /* If it's a DFS share */
+        else if (TargetDevice == wcsstr(TargetDevice, L"\\Device\\WinDfs\\"))
+        {
+            /* Obviously, query DFS */
+            Query = TRUE;
+        }
+        else
+        {
+            return ERROR_BAD_PATHNAME;
+        }
+    }
+    else
+    {
+        return ERROR_BAD_PATHNAME;
+    }
+
+    /* Query DFS, currently not implemented - shouldn't be missing in ReactOS yet ;-) */
+    if (Query)
+    {
+        UNIMPLEMENTED_DBGBREAK("Querying DFS not implemented!\n");
+        AbsolutePath = NULL;
+        AbsolutePathLength = 0;
+    }
+
+    /* Now, properly extract the computer name from the full path */
+    EndOfName = AbsolutePath;
+    if (AbsolutePathLength)
+    {
+        for (NameLength = 0; NameLength < AbsolutePathLength; NameLength += sizeof(WCHAR))
+        {
+            /* Look for the next \, it will be the end of computer name */
+            if (EndOfName[0] == '\\')
+            {
+                break;
+            }
+            /* Computer name cannot contain ., if we get to that point, something went wrong... */
+            else if (EndOfName[0] == '.')
+            {
+                return ERROR_BAD_PATHNAME;
+            }
+
+            ++EndOfName;
+        }
+    }
+
+    NameLength = EndOfName - AbsolutePath;
+    /* Check we didn't overflow and that our computer name isn't ill-formed */
+    if (NameLength >= AbsolutePathLength || NameLength >= MAX_COMPUTERNAME_LENGTH * sizeof(WCHAR))
+    {
+        return ERROR_BAD_PATHNAME;
+    }
+
+    /* Check we can fit */
+    if (NameLength + sizeof(UNICODE_NULL) > *ComputerNameLength * sizeof(WCHAR))
+    {
+        return ERROR_BUFFER_OVERFLOW;
+    }
+
+    /* Write, zero and done! */
+    RtlCopyMemory(ComputerName, AbsolutePath, NameLength);
+    *ComputerNameLength = NameLength / sizeof(WCHAR);
+    ComputerName[NameLength / sizeof(WCHAR)] = UNICODE_NULL;
+
+    return ERROR_SUCCESS;
+}
+
+
+/*
+ * @implemented
+ */
+NTSTATUS
+WINAPI
+BasepNotifyTrackingService(IN OUT PHANDLE ExistingHandle,
+                           IN POBJECT_ATTRIBUTES ObjectAttributes,
+                           IN HANDLE NewHandle,
+                           IN PUNICODE_STRING NewPath)
+{
     NTSTATUS Status;
+    ULONG ComputerNameLength, FileAttributes;
+    WCHAR ComputerName[MAX_COMPUTERNAME_LENGTH + 1];
+    OEM_STRING ComputerNameStringA;
+    CHAR ComputerNameStringBuffer[0x105];
+    UNICODE_STRING ComputerNameStringW;
+    IO_STATUS_BLOCK IoStatusBlock;
+    FILE_BASIC_INFORMATION FileBasicInfo;
+    HANDLE hFullWrite;
+    struct
+    {
+        FILE_TRACKING_INFORMATION;
+        CHAR Buffer[(MAX_COMPUTERNAME_LENGTH + 1) * sizeof(WCHAR)];
+    } FileTrackingInfo;
 
-    TRACE("add_boot_rename_entry( %S, %S, %lu ) \n", source, dest, flags);
+    DPRINT("BasepNotifyTrackingService(%p, %p, %p, %wZ)\n", *ExistingHandle, ObjectAttributes, NewHandle, NewPath);
 
-    if(dest)
-        DestLen = wcslen(dest);
+    Status = STATUS_SUCCESS;
+    ComputerNameLength = ARRAYSIZE(ComputerName);
 
-    if (!RtlDosPathNameToNtPathName_U( source, &source_name, NULL, NULL ))
+    /* Attempt to get computer name of target handle */
+    if (BasepGetComputerNameFromNtPath(NewPath, NewHandle, ComputerName, &ComputerNameLength))
     {
-        SetLastError( ERROR_PATH_NOT_FOUND );
-        return FALSE;
+        /* If we failed to get it, we will just notify with the handle */
+        FileTrackingInfo.ObjectInformationLength = 0;
     }
-    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;
+        /* Convert the retrieved computer name to ANSI and attach it to the notification */
+        ComputerNameStringA.Length = 0;
+        ComputerNameStringA.MaximumLength = ARRAYSIZE(ComputerNameStringBuffer);
+        ComputerNameStringA.Buffer = ComputerNameStringBuffer;
+
+        RtlInitUnicodeString(&ComputerNameStringW, ComputerName);
+        Status = RtlUnicodeStringToOemString(&ComputerNameStringA, &ComputerNameStringW, 0);
+        if (!NT_SUCCESS(Status))
+        {
+            return Status;
+        }
+
+        RtlCopyMemory(FileTrackingInfo.ObjectInformation, ComputerNameStringA.Buffer, ComputerNameStringA.Length);
+        FileTrackingInfo.ObjectInformation[ComputerNameStringA.Length] = 0;
+        FileTrackingInfo.ObjectInformationLength = ComputerNameStringA.Length + 1;
     }
 
-    InitializeObjectAttributes(&ObjectAttributes,
-                               &KeyName,
-                               OBJ_OPENIF | OBJ_CASE_INSENSITIVE,
-                               NULL,
-                               NULL);
+    /* Attach the handle we moved */
+    FileTrackingInfo.DestinationFile = NewHandle;
 
-     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);
-     }
+    /* Final, notify */
+    Status = NtSetInformationFile(*ExistingHandle,
+                                  &IoStatusBlock,
+                                  &FileTrackingInfo,
+                                  sizeof(FileTrackingInfo),
+                                  FileTrackingInformation);
+    if (Status != STATUS_ACCESS_DENIED)
+    {
+        return Status;
+    }
+
+    /* If we get here, we got access denied error, this comes from a
+     * read-only flag. So, close the file, in order to reopen it with enough
+     * rights to remove said flag and reattempt notification
+     */
+    CloseHandle(*ExistingHandle);
 
+    /* Reopen it, to be able to change the destination file attributes */
+    Status = NtOpenFile(ExistingHandle,
+                        SYNCHRONIZE | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES,
+                        ObjectAttributes,
+                        &IoStatusBlock,
+                        FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                        FILE_SYNCHRONOUS_IO_NONALERT);
     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;
+        *ExistingHandle = INVALID_HANDLE_VALUE;
+        return Status;
     }
 
-    len1 = source_name.Length + sizeof(WCHAR);
-    if (DestLen)
+    /* Get the file attributes */
+    Status = NtQueryInformationFile(*ExistingHandle,
+                                    &IoStatusBlock,
+                                    &FileBasicInfo,
+                                    sizeof(FileBasicInfo),
+                                    FileBasicInformation);
+    if (!NT_SUCCESS(Status))
     {
-        len2 = dest_name.Length + sizeof(WCHAR);
-        if (flags & MOVEFILE_REPLACE_EXISTING)
-            len2 += sizeof(WCHAR); /* Plus 1 because of the leading '!' */
+        return Status;
     }
-    else
+
+    /* Get rid of the read only flag */
+    FileAttributes = FileBasicInfo.FileAttributes & ~FILE_ATTRIBUTE_READONLY;
+    RtlZeroMemory(&FileBasicInfo, sizeof(FileBasicInfo));
+    FileBasicInfo.FileAttributes = FileAttributes;
+
+    /* Attempt... */
+    Status = NtSetInformationFile(*ExistingHandle,
+                                  &IoStatusBlock,
+                                  &FileBasicInfo,
+                                  sizeof(FileBasicInfo),
+                                  FileBasicInformation);
+    if (!NT_SUCCESS(Status))
     {
-        len2 = sizeof(WCHAR); /* minimum is the 0 characters for the empty second string */
+        return Status;
     }
 
-    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))
+    /* Now, reopen with maximum accesses to notify */
+    Status = NtOpenFile(&hFullWrite,
+                        GENERIC_WRITE | SYNCHRONIZE,
+                        ObjectAttributes,
+                        &IoStatusBlock,
+                        FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                        FILE_SYNCHRONOUS_IO_NONALERT);
+    if (NT_SUCCESS(Status))
     {
-        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(*ExistingHandle);
+        *ExistingHandle = hFullWrite;
+
+        /* Full success, notify! */
+        Status = NtSetInformationFile(*ExistingHandle,
+                                      &IoStatusBlock,
+                                      &FileTrackingInfo,
+                                      sizeof(FileTrackingInfo),
+                                      FileTrackingInformation);
     }
-    else
+
+    /* If opening with full access failed or if notify failed, restore read-only */
+    if (!NT_SUCCESS(Status))
     {
-        DataSize = info_size;
-        if (!(Buffer = HeapAlloc( GetProcessHeap(), 0, DataSize + len1 + len2 + sizeof(WCHAR) )))
-            goto Quit;
+        FileBasicInfo.FileAttributes |= FILE_ATTRIBUTE_READONLY;
+
+        Status = NtSetInformationFile(*ExistingHandle,
+                                      &IoStatusBlock,
+                                      &FileBasicInfo,
+                                      sizeof(FileBasicInfo),
+                                      FileBasicInformation);
     }
 
-    memcpy( Buffer + DataSize, source_name.Buffer, len1 );
-    DataSize += len1;
-    p = (WCHAR *)(Buffer + DataSize);
-    if (DestLen)
+    /* We're done */
+    return Status;
+}
+
+
+/*
+ * @implemented
+ */
+NTSTATUS
+WINAPI
+BasepOpenFileForMove(IN LPCWSTR File,
+                     OUT PUNICODE_STRING RelativeNtName,
+                     OUT LPWSTR * NtName,
+                     OUT PHANDLE FileHandle,
+                     OUT POBJECT_ATTRIBUTES ObjectAttributes,
+                     IN ACCESS_MASK DesiredAccess,
+                     IN ULONG ShareAccess,
+                     IN ULONG OpenOptions)
+{
+    RTL_RELATIVE_NAME_U RelativeName;
+    NTSTATUS Status;
+    IO_STATUS_BLOCK IoStatusBlock;
+    FILE_ATTRIBUTE_TAG_INFORMATION TagInfo;
+    ULONG IntShareAccess;
+    BOOLEAN HasRelative = FALSE;
+
+    _SEH2_TRY
     {
-        if (flags & MOVEFILE_REPLACE_EXISTING)
-            *p++ = '!';
-        memcpy( p, dest_name.Buffer, len2 );
-        DataSize += len2;
+        /* Zero output */
+        RelativeNtName->Length = 
+        RelativeNtName->MaximumLength = 0;
+        RelativeNtName->Buffer = NULL;
+        *NtName = NULL;
+
+        if (!RtlDosPathNameToRelativeNtPathName_U(File, RelativeNtName, NULL, &RelativeName))
+        {
+            Status = STATUS_OBJECT_PATH_NOT_FOUND;
+            _SEH2_LEAVE;
+        }
+
+        HasRelative = TRUE;
+        *NtName = RelativeNtName->Buffer;
+
+        if (RelativeName.RelativeName.Length)
+        {
+            RelativeNtName->Length = RelativeName.RelativeName.Length;
+            RelativeNtName->MaximumLength = RelativeName.RelativeName.MaximumLength;
+            RelativeNtName->Buffer = RelativeName.RelativeName.Buffer;
+        }
+        else
+        {
+            RelativeName.ContainingDirectory = 0;
+        }
+
+        InitializeObjectAttributes(ObjectAttributes,
+                                   RelativeNtName,
+                                   OBJ_CASE_INSENSITIVE,
+                                   RelativeName.ContainingDirectory,
+                                   NULL);
+        /* Force certain flags here, given ops we'll do */
+        IntShareAccess = ShareAccess | FILE_SHARE_READ | FILE_SHARE_WRITE;
+        OpenOptions |= FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT;
+
+        /* We'll try to read reparse tag */
+        Status = NtOpenFile(FileHandle,
+                            DesiredAccess | FILE_READ_ATTRIBUTES | SYNCHRONIZE,
+                            ObjectAttributes,
+                            &IoStatusBlock,
+                            IntShareAccess,
+                            OpenOptions | FILE_OPEN_REPARSE_POINT);
+        if (NT_SUCCESS(Status))
+        {
+            /* Attempt the read */
+            Status = NtQueryInformationFile(*FileHandle,
+                                            &IoStatusBlock,
+                                            &TagInfo,
+                                            sizeof(FILE_ATTRIBUTE_TAG_INFORMATION),
+                                            FileAttributeTagInformation);
+
+            /* Return if failure with a status that wouldn't mean the FSD cannot support reparse points */
+            if (!NT_SUCCESS(Status) &&
+                (Status != STATUS_NOT_IMPLEMENTED && Status != STATUS_INVALID_PARAMETER))
+            {
+                _SEH2_LEAVE;
+            }
+
+            if (NT_SUCCESS(Status))
+            {
+                /* This cannot happen on mount points */
+                if (TagInfo.FileAttributes & FILE_ATTRIBUTE_DEVICE ||
+                    TagInfo.ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
+                {
+                    _SEH2_LEAVE;
+                }
+            }
+
+            NtClose(*FileHandle);
+            *FileHandle = INVALID_HANDLE_VALUE;
+
+            IntShareAccess = ShareAccess | FILE_SHARE_READ | FILE_SHARE_DELETE;
+        }
+        else if (Status == STATUS_INVALID_PARAMETER)
+        {
+            IntShareAccess = ShareAccess | FILE_SHARE_READ | FILE_SHARE_WRITE;
+        }
+        else
+        {
+            _SEH2_LEAVE;
+        }
+
+        /* Reattempt to open normally, following reparse point if needed */
+        Status = NtOpenFile(FileHandle,
+                            DesiredAccess | SYNCHRONIZE,
+                            ObjectAttributes,
+                            &IoStatusBlock,
+                            IntShareAccess,
+                            OpenOptions);
     }
-    else
+    _SEH2_FINALLY
     {
-        *p = 0;
-        DataSize += sizeof(WCHAR);
+        if (HasRelative)
+        {
+            RtlReleaseRelativeName(&RelativeName);
+        }
     }
+    _SEH2_END;
 
-    /* add final null */
-    p = (WCHAR *)(Buffer + DataSize);
-    *p = 0;
-    DataSize += sizeof(WCHAR);
+    return Status;
+}
 
-    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);
+/*
+ * @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 (!dwCallbackReason)
+        {
+            if (StreamBytesTransferred.QuadPart == StreamSize.QuadPart)
+            {
+                FlushFileBuffers(hDestinationFile);
+            }
+        }
+    }
+
+    if (Context->UserRoutine)
+    {
+        Ret = Context->UserRoutine(TotalFileSize,
+                                   TotalBytesTransferred,
+                                   StreamSize,
+                                   StreamBytesTransferred,
+                                   dwStreamNumber,
+                                   dwCallbackReason,
+                                   hSourceFile,
+                                   hDestinationFile,
+                                   Context->UserData);
+    }
+
+    return Ret;
 }
 
 
@@ -226,465 +664,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);
+                    }
+                }
+            }
+
+            CloseHandle(ExistingHandle);
+            ExistingHandle = INVALID_HANDLE_VALUE;
+        }
 
-    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) );
-
-    if (NT_SUCCESS(errCode)) /* Destination exists */
-    {
-        NtClose(hNewFile);
-
-        if (!(dwFlags & MOVEFILE_REPLACE_EXISTING))
-        {
-                       SetLastError(ERROR_ALREADY_EXISTS);
-                       return FALSE;
-       }
-       else if (GetFileAttributesW(lpNewFileName) & FILE_ATTRIBUTE_DIRECTORY)
-       {
-               SetLastError(ERROR_ACCESS_DENIED);
-               return FALSE;
-       }
-    }
-
-       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;
+        /* In case copy worked, close file */
+        if (NewHandle != INVALID_HANDLE_VALUE)
+        {
+            CloseHandle(NewHandle);
+            NewHandle = INVALID_HANDLE_VALUE;
+        }
+
+        /* If it succeed, delete source file */
+        if (Ret)
+        {
+            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;
+
+    return Ret;
 }
 
 
@@ -693,33 +995,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;
 }
 
 
@@ -728,14 +1036,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);
 }
 
 
@@ -744,17 +1052,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);
 }
 
 
@@ -763,14 +1069,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);
 }
 
 
@@ -779,17 +1085,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);
 }
 
 /*
@@ -797,50 +1101,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);
+
+    if (lpBackupFileName)
+    {
+        RtlFreeUnicodeString(&BackupFileNameW);
+    }
+    RtlFreeUnicodeString(&ReplacementFileNameW);
+    RtlFreeUnicodeString(&ReplacedFileNameW);
 
-    return ret;
+    return Ret;
 }
 
 /*
@@ -996,15 +1307,256 @@ Cleanup:
     return Ret;
 }
 
+
 /*
- * @unimplemented
+ * @implemented
  */
 BOOL
 WINAPI
-PrivMoveFileIdentityW(DWORD Unknown1, DWORD Unknown2, DWORD Unknown3)
+PrivMoveFileIdentityW(IN LPCWSTR lpSource, IN LPCWSTR lpDestination, IN DWORD dwFlags)
 {
-    STUB;
-    return FALSE;
+    ACCESS_MASK SourceAccess;
+    UNICODE_STRING NtSource, NtDestination;
+    LPWSTR RelativeSource, RelativeDestination;
+    HANDLE SourceHandle, DestinationHandle;
+    OBJECT_ATTRIBUTES ObjectAttributesSource, ObjectAttributesDestination;
+    NTSTATUS Status, OldStatus = STATUS_SUCCESS;
+    ACCESS_MASK DestAccess;
+    IO_STATUS_BLOCK IoStatusBlock;
+    FILE_BASIC_INFORMATION SourceInformation, DestinationInformation;
+    FILE_DISPOSITION_INFORMATION FileDispositionInfo;
+
+    DPRINT("PrivMoveFileIdentityW(%S, %S, %x)\n", lpSource, lpDestination, dwFlags);
+
+    SourceHandle = INVALID_HANDLE_VALUE;
+    NtSource.Length =
+    NtSource.MaximumLength = 0;
+    NtSource.Buffer = NULL;
+    RelativeSource = NULL;
+    DestinationHandle = INVALID_HANDLE_VALUE;
+    NtDestination.Length =
+    NtDestination.MaximumLength = 0;
+    NtDestination.Buffer = NULL;
+    RelativeDestination = NULL;
+
+    /* FILE_WRITE_DATA is required for later on notification */
+    SourceAccess = FILE_READ_ATTRIBUTES | FILE_WRITE_DATA;
+    if (dwFlags & PRIV_DELETE_ON_SUCCESS)
+    {
+        SourceAccess |= DELETE;
+    }
+
+    _SEH2_TRY
+    {
+        /* We will loop twice:
+         * First we attempt to open with FILE_WRITE_DATA for notification
+         * If it fails and we have flag for non-trackable files, we retry
+         * without FILE_WRITE_DATA.
+         * If that one fails, then, we quit for real
+         */
+        while (TRUE)
+        {
+            Status = BasepOpenFileForMove(lpSource,
+                                          &NtSource,
+                                          &RelativeSource,
+                                          &SourceHandle,
+                                          &ObjectAttributesSource,
+                                          SourceAccess,
+                                          FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                                          FILE_OPEN_NO_RECALL);
+            if (NT_SUCCESS(Status))
+            {
+                break;
+            }
+
+            /* If we already attempted the opening without FILE_WRITE_DATA
+             * or if we cannot move on non-trackable files, fail.
+             */
+            if (!(SourceAccess & FILE_WRITE_DATA) || !(dwFlags & PRIV_ALLOW_NON_TRACKABLE))
+            {
+                _SEH2_LEAVE;
+            }
+
+            if (RelativeSource)
+            {
+                RtlFreeHeap(RtlGetProcessHeap(), 0, RelativeSource);
+                RelativeSource = NULL;
+            }
+
+            if (SourceHandle != INVALID_HANDLE_VALUE)
+            {
+                NtClose(SourceHandle);
+                SourceHandle = INVALID_HANDLE_VALUE;
+            }
+
+            SourceAccess &= ~FILE_WRITE_DATA;
+
+            /* Remember fist failure in the path */
+            if (NT_SUCCESS(OldStatus))
+            {
+                OldStatus = Status;
+            }
+        }
+
+        DestAccess = FILE_WRITE_ATTRIBUTES;
+        /* If we could preserve FILE_WRITE_DATA for source, attempt to get it for destination
+         * Still for notification purposes
+         */
+        if (SourceAccess & FILE_WRITE_DATA)
+        {
+            DestAccess |= FILE_WRITE_DATA;
+        }
+
+        /* cf comment for first loop */
+        while (TRUE)
+        {
+            Status = BasepOpenFileForMove(lpDestination,
+                                          &NtDestination,
+                                          &RelativeDestination,
+                                          &DestinationHandle,
+                                          &ObjectAttributesDestination,
+                                          DestAccess,
+                                          FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                                          FILE_OPEN_NO_RECALL);
+            if (NT_SUCCESS(Status))
+            {
+                break;
+            }
+
+            /* If we already attempted the opening without FILE_WRITE_DATA
+             * or if we cannot move on non-trackable files, fail.
+             */
+            if (!(DestAccess & FILE_WRITE_DATA) || !(dwFlags & PRIV_ALLOW_NON_TRACKABLE))
+            {
+                _SEH2_LEAVE;
+            }
+
+            if (RelativeDestination)
+            {
+                RtlFreeHeap(RtlGetProcessHeap(), 0, RelativeDestination);
+                RelativeDestination = NULL;
+            }
+
+            if (DestinationHandle != INVALID_HANDLE_VALUE)
+            {
+                NtClose(DestinationHandle);
+                DestinationHandle = INVALID_HANDLE_VALUE;
+            }
+
+            DestAccess &= ~FILE_WRITE_DATA;
+
+            /* Remember fist failure in the path */
+            if (NT_SUCCESS(OldStatus))
+            {
+                OldStatus = Status;
+            }
+        }
+
+        /* Get the creation time from source */
+        Status = NtQueryInformationFile(SourceHandle,
+                                        &IoStatusBlock,
+                                        &SourceInformation,
+                                        sizeof(SourceInformation),
+                                        FileBasicInformation);
+        if (NT_SUCCESS(Status))
+        {
+            /* Then, prepare to set it for destination */
+            RtlZeroMemory(&DestinationInformation, sizeof(DestinationInformation));
+            DestinationInformation.CreationTime.QuadPart = SourceInformation.CreationTime.QuadPart;
+
+            /* And set it, that's all folks! */
+            Status = NtSetInformationFile(DestinationHandle,
+                                          &IoStatusBlock,
+                                          &DestinationInformation,
+                                          sizeof(DestinationInformation),
+                                          FileBasicInformation);
+        }
+
+        if (!NT_SUCCESS(Status))
+        {
+            if (!(dwFlags & PRIV_ALLOW_NON_TRACKABLE))
+            {
+                _SEH2_LEAVE;
+            }
+
+            /* Remember the failure for later notification */
+            if (NT_SUCCESS(OldStatus))
+            {
+                OldStatus = Status;
+            }
+        }
+
+        /* If we could open with FILE_WRITE_DATA both source and destination,
+         * then, notify
+         */
+        if (DestAccess & FILE_WRITE_DATA && SourceAccess & FILE_WRITE_DATA)
+        {
+            Status = BasepNotifyTrackingService(&SourceHandle,
+                                                &ObjectAttributesSource,
+                                                DestinationHandle,
+                                                &NtDestination);
+#if 1 // ReactOS HACK
+            /* FIXME: To be removed once BasepNotifyTrackingService is implemented */
+            if (Status == STATUS_NOT_IMPLEMENTED)
+                Status = STATUS_SUCCESS;
+#endif
+            if (!NT_SUCCESS(Status))
+            {
+                if (dwFlags & PRIV_ALLOW_NON_TRACKABLE)
+                {
+                    if (NT_SUCCESS(OldStatus))
+                        OldStatus = Status;
+                }
+            }
+        }
+    }
+    _SEH2_FINALLY
+    {
+        if (RelativeSource)
+            RtlFreeHeap(RtlGetProcessHeap(), 0, RelativeSource);
+
+        if (RelativeDestination)
+            RtlFreeHeap(RtlGetProcessHeap(), 0, RelativeDestination);
+    }
+    _SEH2_END;
+
+    /* If caller asked for source deletion, if everything succeed, proceed */
+    if (NT_SUCCESS(Status) && dwFlags & PRIV_DELETE_ON_SUCCESS)
+    {
+        FileDispositionInfo.DeleteFile = TRUE;
+
+        Status = NtSetInformationFile(SourceHandle,
+                                      &IoStatusBlock,
+                                      &FileDispositionInfo,
+                                      sizeof(FileDispositionInfo),
+                                      FileDispositionInformation);
+    }
+
+    /* Cleanup/close portion */
+    if (DestinationHandle != INVALID_HANDLE_VALUE)
+    {
+        NtClose(DestinationHandle);
+    }
+
+    if (SourceHandle != INVALID_HANDLE_VALUE)
+    {
+        NtClose(SourceHandle);
+    }
+
+    /* Set last error if any, and quit */
+    if (NT_SUCCESS(Status))
+    {
+        if (!NT_SUCCESS(OldStatus))
+        {
+            BaseSetLastNTError(OldStatus);
+        }
+    }
+    else
+    {
+        BaseSetLastNTError(Status);
+    }
+
+    return NT_SUCCESS(Status);
 }
 
 /* EOF */