xref: /reactos/dll/win32/kernel32/client/file/move.c (revision cdf90707)
1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS system libraries
4  * FILE:            dll/win32/kernel32/client/file/move.c
5  * PURPOSE:         Directory functions
6  * PROGRAMMER:      Ariadne ( ariadne@xs4all.nl)
7  *                  Gerhard W. Gruber (sparhawk_at_gmx.at)
8  *                  Dmitry Philippov (shedon@mail.ru)
9  *                  Pierre Schweitzer (pierre@reactos.org)
10  * UPDATE HISTORY:
11  *                  Created 01/11/98
12  *                  DP (29/07/2006)
13  *                      Fix some bugs in the add_boot_rename_entry function
14  */
15 
16 /* INCLUDES *****************************************************************/
17 
18 #include <k32.h>
19 #include <malloc.h>
20 #include <strsafe.h>
21 #define NDEBUG
22 #include <debug.h>
23 DEBUG_CHANNEL(kernel32file);
24 
25 /* GLOBALS *****************************************************************/
26 
27 /* DEFINES *****************************************************************/
28 typedef struct _COPY_PROGRESS_CONTEXT
29 {
30     ULONG Flags;
31     LPPROGRESS_ROUTINE UserRoutine;
32     LPVOID UserData;
33 } COPY_PROGRESS_CONTEXT, *PCOPY_PROGRESS_CONTEXT;
34 
35 /* FUNCTIONS ****************************************************************/
36 /*
37  * @implemented
38  */
39 NTSTATUS
40 WINAPI
41 BasepMoveFileDelayed(IN PUNICODE_STRING ExistingPath,
42                      IN PUNICODE_STRING NewPath,
43                      IN INT KeyId,
44                      IN BOOL CreateIfNotFound)
45 {
46 #define STRING_LENGTH 0x400
47     NTSTATUS Status;
48     HANDLE KeyHandle;
49     PVOID Buffer, BufferBegin;
50     OBJECT_ATTRIBUTES ObjectAttributes;
51     PWSTR PendingOperations, BufferWrite;
52     ULONG DataSize, BufferLength, StringLength = STRING_LENGTH;
53     UNICODE_STRING SessionManagerString, PendingOperationsString;
54     /* +6 because a INT shouldn't take more than 6 chars. Especially given the call path */
55     WCHAR PendingOperationsBuffer[sizeof(L"PendingFileRenameOperations") / sizeof(WCHAR) + 6];
56 
57     RtlInitUnicodeString(&SessionManagerString, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Session Manager");
58 
59     /* Select appropriate key for adding our file */
60     if (KeyId == 1)
61     {
62         PendingOperations = L"PendingFileRenameOperations";
63     }
64     else
65     {
66         StringCbPrintfW(PendingOperationsBuffer, sizeof(PendingOperationsBuffer), L"PendingFileRenameOperations%d", KeyId);
67         PendingOperations = PendingOperationsBuffer;
68     }
69     RtlInitUnicodeString(&PendingOperationsString, PendingOperations);
70 
71     InitializeObjectAttributes(&ObjectAttributes,
72                                &SessionManagerString,
73                                OBJ_OPENIF | OBJ_CASE_INSENSITIVE,
74                                NULL, NULL);
75 
76     /* Open parent key */
77     Status = NtCreateKey(&KeyHandle,
78                          GENERIC_READ | GENERIC_WRITE,
79                          &ObjectAttributes, 0, NULL,
80                          REG_OPTION_NON_VOLATILE, NULL);
81     if (Status == STATUS_ACCESS_DENIED)
82     {
83         Status = NtCreateKey(&KeyHandle,
84                              GENERIC_READ | GENERIC_WRITE,
85                              &ObjectAttributes, 0, NULL,
86                              REG_OPTION_BACKUP_RESTORE, NULL);
87     }
88 
89     if (!NT_SUCCESS(Status))
90     {
91         return Status;
92     }
93 
94     /* Reserve enough to read previous string + to append our with required null chars */
95     BufferLength = NewPath->Length + ExistingPath->Length + STRING_LENGTH + 3 * sizeof(UNICODE_NULL);
96 
97     while (TRUE)
98     {
99         /* Allocate output buffer */
100         Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferLength);
101         if (Buffer == NULL)
102         {
103             NtClose(KeyHandle);
104             return STATUS_NO_MEMORY;
105         }
106 
107         Status = NtQueryValueKey(KeyHandle,
108                                  &PendingOperationsString,
109                                  KeyValuePartialInformation,
110                                  Buffer, StringLength, &DataSize);
111         if (Status != STATUS_BUFFER_OVERFLOW)
112         {
113             break;
114         }
115 
116         /* If buffer was too small, then, reallocate one which is big enough */
117         StringLength = DataSize;
118         RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
119         BufferLength = ExistingPath->Length + StringLength + NewPath->Length + 3 * sizeof(UNICODE_NULL);
120         /* Check we didn't overflow */
121         if (BufferLength < StringLength)
122         {
123             NtClose(KeyHandle);
124             return STATUS_BUFFER_TOO_SMALL;
125         }
126     }
127 
128     /* Check if it existed - if not, create only IF asked to */
129     if (!NT_SUCCESS(Status) && (Status != STATUS_OBJECT_NAME_NOT_FOUND || !CreateIfNotFound))
130     {
131         NtClose(KeyHandle);
132         RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
133         return Status;
134     }
135 
136     if (!NT_SUCCESS(Status))
137     {
138         /* We didn't find any - ie, we create, so use complete buffer */
139         BufferBegin = Buffer;
140         BufferWrite = Buffer;
141     }
142     else
143     {
144         PKEY_VALUE_PARTIAL_INFORMATION PartialInfo = (PKEY_VALUE_PARTIAL_INFORMATION)Buffer;
145 
146         /* Get data, our buffer begin and then where we should append data
147          * (- null char, this is REG_MULTI_SZ, it already includes double termination, we keep only one)
148          */
149         BufferBegin = PartialInfo->Data;
150         BufferWrite = (PWSTR)((ULONG_PTR)PartialInfo->Data + PartialInfo->DataLength - sizeof(UNICODE_NULL));
151     }
152 
153     /* First copy existing */
154     RtlCopyMemory(BufferWrite, ExistingPath->Buffer, ExistingPath->Length);
155     BufferWrite += ExistingPath->Length / sizeof(WCHAR);
156     /* And append null char */
157     *BufferWrite = UNICODE_NULL;
158     ++BufferWrite;
159     /* Append destination */
160     RtlCopyMemory(BufferWrite, NewPath->Buffer, NewPath->Length);
161     BufferWrite += NewPath->Length / sizeof(WCHAR);
162     /* And append two null char (end of string) */
163     *BufferWrite = UNICODE_NULL;
164     ++BufferWrite;
165     *BufferWrite = UNICODE_NULL;
166 
167     /* Set new value */
168     Status = NtSetValueKey(KeyHandle,
169                            &PendingOperationsString,
170                            0, REG_MULTI_SZ, BufferBegin,
171                            (ULONG_PTR)BufferWrite - (ULONG_PTR)BufferBegin + sizeof(WCHAR));
172 
173     NtClose(KeyHandle);
174     RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
175 
176     return Status;
177 }
178 
179 
180 /*
181  * @implemented
182  */
183 DWORD
184 WINAPI
185 BasepGetComputerNameFromNtPath(IN PUNICODE_STRING NewPath,
186                                IN HANDLE NewHandle,
187                                OUT PWSTR ComputerName,
188                                IN OUT PULONG ComputerNameLength)
189 {
190     BOOL Query = FALSE;
191     WCHAR Letter;
192     PWSTR AbsolutePath, EndOfName;
193     USHORT AbsolutePathLength, NameLength;
194     WCHAR TargetDevice[0x105];
195     WCHAR DeviceName[] = {'A', ':', '\0'}; /* Init to something, will be set later */
196     UNICODE_STRING UncString = RTL_CONSTANT_STRING(L"\\??\\UNC\\");
197     UNICODE_STRING GlobalString = RTL_CONSTANT_STRING(L"\\??\\");
198 
199     DPRINT("BasepGetComputerNameFromNtPath(%wZ, %p, %p, %lu)\n", NewPath, NewHandle, ComputerName, ComputerNameLength);
200 
201     /* If it's an UNC path */
202     if (RtlPrefixUnicodeString(&UncString, NewPath, TRUE))
203     {
204         /* Check for broken caller */
205         if (NewPath->Length <= UncString.Length)
206         {
207             return ERROR_BAD_PATHNAME;
208         }
209 
210         /* Skip UNC prefix */
211         AbsolutePath = &NewPath->Buffer[UncString.Length / sizeof(WCHAR)];
212         AbsolutePathLength = NewPath->Length - UncString.Length;
213 
214         /* And query DFS */
215         Query = TRUE;
216     }
217     /* Otherwise, we have to be in global (NT path!), with drive letter */
218     else if (RtlPrefixUnicodeString(&GlobalString, NewPath, TRUE) && NewPath->Buffer[5] == ':')
219     {
220         /* Path is like that: \??\C:\Complete Path\To File.ext */
221         /* Get the letter and upcase it if required */
222         Letter = NewPath->Buffer[4];
223         if (Letter >= 'a' && Letter <= 'z')
224         {
225             Letter -= ('a' - 'A');
226         }
227         DeviceName[0] = Letter;
228 
229         /* Query the associated DOS device */
230         if (!QueryDosDeviceW(DeviceName, TargetDevice, ARRAYSIZE(TargetDevice)))
231         {
232             return GetLastError();
233         }
234 
235         /* If that's a network share */
236         if (TargetDevice == wcsstr(TargetDevice, L"\\Device\\LanmanRedirector\\;"))
237         {
238             /* Path is like that: \Device\LanmanRedirector\;C:0000000000000000\Complete Path\To File.ext */
239             /* Check we have the correct drive letter */
240             if (TargetDevice[26] == DeviceName[0] &&
241                 TargetDevice[27] == ':')
242             {
243                 /* Check for the path begin, computer name is before */
244                 PWSTR Path = wcschr(&TargetDevice[28], '\\');
245                 if (Path == NULL)
246                 {
247                     return ERROR_BAD_PATHNAME;
248                 }
249 
250                 AbsolutePath = Path + 1;
251                 AbsolutePathLength = sizeof(WCHAR) * (ARRAYSIZE(TargetDevice) - (AbsolutePath - TargetDevice));
252             }
253             else
254             {
255                 return ERROR_BAD_PATHNAME;
256             }
257         }
258         /* If it's a local device */
259         else if (TargetDevice == wcsstr(TargetDevice, L"\\Device\\Harddisk")
260                  || TargetDevice == wcsstr(TargetDevice, L"\\Device\\CdRom")
261                  || TargetDevice == wcsstr(TargetDevice, L"\\Device\\Floppy"))
262         {
263             /* Just query the computer name */
264             if (!GetComputerNameW(ComputerName, ComputerNameLength))
265             {
266                 return GetLastError();
267             }
268 
269             return ERROR_SUCCESS;
270         }
271         /* If it's a DFS share */
272         else if (TargetDevice == wcsstr(TargetDevice, L"\\Device\\WinDfs\\"))
273         {
274             /* Obviously, query DFS */
275             Query = TRUE;
276         }
277         else
278         {
279             return ERROR_BAD_PATHNAME;
280         }
281     }
282     else
283     {
284         return ERROR_BAD_PATHNAME;
285     }
286 
287     /* Query DFS, currently not implemented - shouldn't be missing in ReactOS yet ;-) */
288     if (Query)
289     {
290         UNIMPLEMENTED_DBGBREAK("Querying DFS not implemented!\n");
291         AbsolutePath = NULL;
292         AbsolutePathLength = 0;
293     }
294 
295     /* Now, properly extract the computer name from the full path */
296     EndOfName = AbsolutePath;
297     if (AbsolutePathLength)
298     {
299         for (NameLength = 0; NameLength < AbsolutePathLength; NameLength += sizeof(WCHAR))
300         {
301             /* Look for the next \, it will be the end of computer name */
302             if (EndOfName[0] == '\\')
303             {
304                 break;
305             }
306             /* Computer name cannot contain ., if we get to that point, something went wrong... */
307             else if (EndOfName[0] == '.')
308             {
309                 return ERROR_BAD_PATHNAME;
310             }
311 
312             ++EndOfName;
313         }
314     }
315 
316     NameLength = EndOfName - AbsolutePath;
317     /* Check we didn't overflow and that our computer name isn't ill-formed */
318     if (NameLength >= AbsolutePathLength || NameLength >= MAX_COMPUTERNAME_LENGTH * sizeof(WCHAR))
319     {
320         return ERROR_BAD_PATHNAME;
321     }
322 
323     /* Check we can fit */
324     if (NameLength + sizeof(UNICODE_NULL) > *ComputerNameLength * sizeof(WCHAR))
325     {
326         return ERROR_BUFFER_OVERFLOW;
327     }
328 
329     /* Write, zero and done! */
330     RtlCopyMemory(ComputerName, AbsolutePath, NameLength);
331     *ComputerNameLength = NameLength / sizeof(WCHAR);
332     ComputerName[NameLength / sizeof(WCHAR)] = UNICODE_NULL;
333 
334     return ERROR_SUCCESS;
335 }
336 
337 
338 /*
339  * @implemented
340  */
341 NTSTATUS
342 WINAPI
343 BasepNotifyTrackingService(IN OUT PHANDLE ExistingHandle,
344                            IN POBJECT_ATTRIBUTES ObjectAttributes,
345                            IN HANDLE NewHandle,
346                            IN PUNICODE_STRING NewPath)
347 {
348     NTSTATUS Status;
349     ULONG ComputerNameLength, FileAttributes;
350     WCHAR ComputerName[MAX_COMPUTERNAME_LENGTH + 1];
351     OEM_STRING ComputerNameStringA;
352     CHAR ComputerNameStringBuffer[0x105];
353     UNICODE_STRING ComputerNameStringW;
354     IO_STATUS_BLOCK IoStatusBlock;
355     FILE_BASIC_INFORMATION FileBasicInfo;
356     HANDLE hFullWrite;
357     struct
358     {
359         FILE_TRACKING_INFORMATION;
360         CHAR Buffer[(MAX_COMPUTERNAME_LENGTH + 1) * sizeof(WCHAR)];
361     } FileTrackingInfo;
362 
363     DPRINT("BasepNotifyTrackingService(%p, %p, %p, %wZ)\n", *ExistingHandle, ObjectAttributes, NewHandle, NewPath);
364 
365     Status = STATUS_SUCCESS;
366     ComputerNameLength = ARRAYSIZE(ComputerName);
367 
368     /* Attempt to get computer name of target handle */
369     if (BasepGetComputerNameFromNtPath(NewPath, NewHandle, ComputerName, &ComputerNameLength))
370     {
371         /* If we failed to get it, we will just notify with the handle */
372         FileTrackingInfo.ObjectInformationLength = 0;
373     }
374     else
375     {
376         /* Convert the retrieved computer name to ANSI and attach it to the notification */
377         ComputerNameStringA.Length = 0;
378         ComputerNameStringA.MaximumLength = ARRAYSIZE(ComputerNameStringBuffer);
379         ComputerNameStringA.Buffer = ComputerNameStringBuffer;
380 
381         RtlInitUnicodeString(&ComputerNameStringW, ComputerName);
382         Status = RtlUnicodeStringToOemString(&ComputerNameStringA, &ComputerNameStringW, 0);
383         if (!NT_SUCCESS(Status))
384         {
385             return Status;
386         }
387 
388         RtlCopyMemory(FileTrackingInfo.ObjectInformation, ComputerNameStringA.Buffer, ComputerNameStringA.Length);
389         FileTrackingInfo.ObjectInformation[ComputerNameStringA.Length] = 0;
390         FileTrackingInfo.ObjectInformationLength = ComputerNameStringA.Length + 1;
391     }
392 
393     /* Attach the handle we moved */
394     FileTrackingInfo.DestinationFile = NewHandle;
395 
396     /* Final, notify */
397     Status = NtSetInformationFile(*ExistingHandle,
398                                   &IoStatusBlock,
399                                   &FileTrackingInfo,
400                                   sizeof(FileTrackingInfo),
401                                   FileTrackingInformation);
402     if (Status != STATUS_ACCESS_DENIED)
403     {
404         return Status;
405     }
406 
407     /* If we get here, we got access denied error, this comes from a
408      * read-only flag. So, close the file, in order to reopen it with enough
409      * rights to remove said flag and reattempt notification
410      */
411     CloseHandle(*ExistingHandle);
412 
413     /* Reopen it, to be able to change the destination file attributes */
414     Status = NtOpenFile(ExistingHandle,
415                         SYNCHRONIZE | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES,
416                         ObjectAttributes,
417                         &IoStatusBlock,
418                         FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
419                         FILE_SYNCHRONOUS_IO_NONALERT);
420     if (!NT_SUCCESS(Status))
421     {
422         *ExistingHandle = INVALID_HANDLE_VALUE;
423         return Status;
424     }
425 
426     /* Get the file attributes */
427     Status = NtQueryInformationFile(*ExistingHandle,
428                                     &IoStatusBlock,
429                                     &FileBasicInfo,
430                                     sizeof(FileBasicInfo),
431                                     FileBasicInformation);
432     if (!NT_SUCCESS(Status))
433     {
434         return Status;
435     }
436 
437     /* Get rid of the read only flag */
438     FileAttributes = FileBasicInfo.FileAttributes & ~FILE_ATTRIBUTE_READONLY;
439     RtlZeroMemory(&FileBasicInfo, sizeof(FileBasicInfo));
440     FileBasicInfo.FileAttributes = FileAttributes;
441 
442     /* Attempt... */
443     Status = NtSetInformationFile(*ExistingHandle,
444                                   &IoStatusBlock,
445                                   &FileBasicInfo,
446                                   sizeof(FileBasicInfo),
447                                   FileBasicInformation);
448     if (!NT_SUCCESS(Status))
449     {
450         return Status;
451     }
452 
453     /* Now, reopen with maximum accesses to notify */
454     Status = NtOpenFile(&hFullWrite,
455                         GENERIC_WRITE | SYNCHRONIZE,
456                         ObjectAttributes,
457                         &IoStatusBlock,
458                         FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
459                         FILE_SYNCHRONOUS_IO_NONALERT);
460     if (NT_SUCCESS(Status))
461     {
462         NtClose(*ExistingHandle);
463         *ExistingHandle = hFullWrite;
464 
465         /* Full success, notify! */
466         Status = NtSetInformationFile(*ExistingHandle,
467                                       &IoStatusBlock,
468                                       &FileTrackingInfo,
469                                       sizeof(FileTrackingInfo),
470                                       FileTrackingInformation);
471     }
472 
473     /* If opening with full access failed or if notify failed, restore read-only */
474     if (!NT_SUCCESS(Status))
475     {
476         FileBasicInfo.FileAttributes |= FILE_ATTRIBUTE_READONLY;
477 
478         Status = NtSetInformationFile(*ExistingHandle,
479                                       &IoStatusBlock,
480                                       &FileBasicInfo,
481                                       sizeof(FileBasicInfo),
482                                       FileBasicInformation);
483     }
484 
485     /* We're done */
486     return Status;
487 }
488 
489 
490 /*
491  * @implemented
492  */
493 NTSTATUS
494 WINAPI
495 BasepOpenFileForMove(IN LPCWSTR File,
496                      OUT PUNICODE_STRING RelativeNtName,
497                      OUT LPWSTR * NtName,
498                      OUT PHANDLE FileHandle,
499                      OUT POBJECT_ATTRIBUTES ObjectAttributes,
500                      IN ACCESS_MASK DesiredAccess,
501                      IN ULONG ShareAccess,
502                      IN ULONG OpenOptions)
503 {
504     RTL_RELATIVE_NAME_U RelativeName;
505     NTSTATUS Status;
506     IO_STATUS_BLOCK IoStatusBlock;
507     FILE_ATTRIBUTE_TAG_INFORMATION TagInfo;
508     ULONG IntShareAccess;
509     BOOLEAN HasRelative = FALSE;
510 
511     _SEH2_TRY
512     {
513         /* Zero output */
514         RelativeNtName->Length =
515         RelativeNtName->MaximumLength = 0;
516         RelativeNtName->Buffer = NULL;
517         *NtName = NULL;
518 
519         if (!RtlDosPathNameToRelativeNtPathName_U(File, RelativeNtName, NULL, &RelativeName))
520         {
521             Status = STATUS_OBJECT_PATH_NOT_FOUND;
522             _SEH2_LEAVE;
523         }
524 
525         HasRelative = TRUE;
526         *NtName = RelativeNtName->Buffer;
527 
528         if (RelativeName.RelativeName.Length)
529         {
530             RelativeNtName->Length = RelativeName.RelativeName.Length;
531             RelativeNtName->MaximumLength = RelativeName.RelativeName.MaximumLength;
532             RelativeNtName->Buffer = RelativeName.RelativeName.Buffer;
533         }
534         else
535         {
536             RelativeName.ContainingDirectory = 0;
537         }
538 
539         InitializeObjectAttributes(ObjectAttributes,
540                                    RelativeNtName,
541                                    OBJ_CASE_INSENSITIVE,
542                                    RelativeName.ContainingDirectory,
543                                    NULL);
544         /* Force certain flags here, given ops we'll do */
545         IntShareAccess = ShareAccess | FILE_SHARE_READ | FILE_SHARE_WRITE;
546         OpenOptions |= FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT;
547 
548         /* We'll try to read reparse tag */
549         Status = NtOpenFile(FileHandle,
550                             DesiredAccess | FILE_READ_ATTRIBUTES | SYNCHRONIZE,
551                             ObjectAttributes,
552                             &IoStatusBlock,
553                             IntShareAccess,
554                             OpenOptions | FILE_OPEN_REPARSE_POINT);
555         if (NT_SUCCESS(Status))
556         {
557             /* Attempt the read */
558             Status = NtQueryInformationFile(*FileHandle,
559                                             &IoStatusBlock,
560                                             &TagInfo,
561                                             sizeof(FILE_ATTRIBUTE_TAG_INFORMATION),
562                                             FileAttributeTagInformation);
563 
564             /* Return if failure with a status that wouldn't mean the FSD cannot support reparse points */
565             if (!NT_SUCCESS(Status) &&
566                 (Status != STATUS_NOT_IMPLEMENTED && Status != STATUS_INVALID_PARAMETER))
567             {
568                 _SEH2_LEAVE;
569             }
570 
571             if (NT_SUCCESS(Status))
572             {
573                 /* This cannot happen on mount points */
574                 if (TagInfo.FileAttributes & FILE_ATTRIBUTE_DEVICE ||
575                     TagInfo.ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
576                 {
577                     _SEH2_LEAVE;
578                 }
579             }
580 
581             NtClose(*FileHandle);
582             *FileHandle = INVALID_HANDLE_VALUE;
583 
584             IntShareAccess = ShareAccess | FILE_SHARE_READ | FILE_SHARE_DELETE;
585         }
586         else if (Status == STATUS_INVALID_PARAMETER)
587         {
588             IntShareAccess = ShareAccess | FILE_SHARE_READ | FILE_SHARE_WRITE;
589         }
590         else
591         {
592             _SEH2_LEAVE;
593         }
594 
595         /* Reattempt to open normally, following reparse point if needed */
596         Status = NtOpenFile(FileHandle,
597                             DesiredAccess | SYNCHRONIZE,
598                             ObjectAttributes,
599                             &IoStatusBlock,
600                             IntShareAccess,
601                             OpenOptions);
602     }
603     _SEH2_FINALLY
604     {
605         if (HasRelative)
606         {
607             RtlReleaseRelativeName(&RelativeName);
608         }
609     }
610     _SEH2_END;
611 
612     return Status;
613 }
614 
615 
616 /*
617  * @implemented
618  */
619 DWORD
620 WINAPI
621 BasepMoveFileCopyProgress(IN LARGE_INTEGER TotalFileSize,
622                           IN LARGE_INTEGER TotalBytesTransferred,
623                           IN LARGE_INTEGER StreamSize,
624                           IN LARGE_INTEGER StreamBytesTransferred,
625                           IN DWORD dwStreamNumber,
626                           IN DWORD dwCallbackReason,
627                           IN HANDLE hSourceFile,
628                           IN HANDLE hDestinationFile,
629                           IN LPVOID lpData OPTIONAL)
630 {
631     DWORD Ret = 0;
632     PCOPY_PROGRESS_CONTEXT Context = (PCOPY_PROGRESS_CONTEXT)lpData;
633 
634     if (Context->Flags & MOVEFILE_WRITE_THROUGH)
635     {
636         if (!dwCallbackReason)
637         {
638             if (StreamBytesTransferred.QuadPart == StreamSize.QuadPart)
639             {
640                 FlushFileBuffers(hDestinationFile);
641             }
642         }
643     }
644 
645     if (Context->UserRoutine)
646     {
647         Ret = Context->UserRoutine(TotalFileSize,
648                                    TotalBytesTransferred,
649                                    StreamSize,
650                                    StreamBytesTransferred,
651                                    dwStreamNumber,
652                                    dwCallbackReason,
653                                    hSourceFile,
654                                    hDestinationFile,
655                                    Context->UserData);
656     }
657 
658     return Ret;
659 }
660 
661 
662 /*
663  * @implemented
664  */
665 BOOL
666 WINAPI
667 MoveFileWithProgressW(IN LPCWSTR lpExistingFileName,
668                       IN LPCWSTR lpNewFileName,
669                       IN LPPROGRESS_ROUTINE lpProgressRoutine,
670                       IN LPVOID lpData,
671                       IN DWORD dwFlags)
672 {
673     NTSTATUS Status;
674     PWSTR NewBuffer;
675     IO_STATUS_BLOCK IoStatusBlock;
676     COPY_PROGRESS_CONTEXT CopyContext;
677     OBJECT_ATTRIBUTES ObjectAttributes;
678     PFILE_RENAME_INFORMATION RenameInfo;
679     UNICODE_STRING NewPathU, ExistingPathU;
680     FILE_ATTRIBUTE_TAG_INFORMATION FileAttrTagInfo;
681     HANDLE SourceHandle = INVALID_HANDLE_VALUE, NewHandle, ExistingHandle;
682     BOOL Ret = FALSE, ReplaceIfExists, DelayUntilReboot, AttemptReopenWithoutReparse;
683 
684     DPRINT("MoveFileWithProgressW(%S, %S, %p, %p, %x)\n", lpExistingFileName, lpNewFileName, lpProgressRoutine, lpData, dwFlags);
685 
686     NewPathU.Buffer = NULL;
687     ExistingPathU.Buffer = NULL;
688 
689     _SEH2_TRY
690     {
691         /* Don't allow renaming to a disk */
692         if (lpNewFileName && RtlIsDosDeviceName_U(lpNewFileName))
693         {
694             BaseSetLastNTError(STATUS_OBJECT_NAME_COLLISION);
695             _SEH2_LEAVE;
696         }
697 
698         ReplaceIfExists = !!(dwFlags & MOVEFILE_REPLACE_EXISTING);
699 
700         /* Get file path */
701         if (!RtlDosPathNameToNtPathName_U(lpExistingFileName, &ExistingPathU, NULL, NULL))
702         {
703             BaseSetLastNTError(STATUS_OBJECT_PATH_NOT_FOUND);
704             _SEH2_LEAVE;
705         }
706 
707         /* Sanitize input */
708         DelayUntilReboot = !!(dwFlags & MOVEFILE_DELAY_UNTIL_REBOOT);
709         if (DelayUntilReboot && (dwFlags & MOVEFILE_CREATE_HARDLINK))
710         {
711             BaseSetLastNTError(STATUS_INVALID_PARAMETER);
712             _SEH2_LEAVE;
713         }
714 
715         /* Unless we manage a proper opening, we'll attempt to reopen without reparse support */
716         AttemptReopenWithoutReparse = TRUE;
717         InitializeObjectAttributes(&ObjectAttributes,
718                                    &ExistingPathU,
719                                    OBJ_CASE_INSENSITIVE,
720                                    NULL,
721                                    NULL);
722         /* Attempt to open source file */
723         Status = NtOpenFile(&SourceHandle,
724                             FILE_READ_ATTRIBUTES | DELETE | SYNCHRONIZE,
725                             &ObjectAttributes,
726                             &IoStatusBlock,
727                             FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
728                             FILE_OPEN_FOR_BACKUP_INTENT | ((dwFlags & MOVEFILE_WRITE_THROUGH) ? FILE_WRITE_THROUGH : 0));
729         if (!NT_SUCCESS(Status))
730         {
731             /* If we failed and the file doesn't exist, don't attempt to reopen without reparse */
732             if (DelayUntilReboot &&
733                 (Status == STATUS_SHARING_VIOLATION || Status == STATUS_OBJECT_NAME_NOT_FOUND || Status == STATUS_OBJECT_PATH_NOT_FOUND))
734             {
735                 /* Here we don't fail completely, as we postpone the operation to reboot
736                  * File might exist afterwards, and we don't need a handle here
737                  */
738                 SourceHandle = INVALID_HANDLE_VALUE;
739                 AttemptReopenWithoutReparse = FALSE;
740             }
741             /* If we failed for any reason than unsupported reparse, fail completely */
742             else if (Status != STATUS_INVALID_PARAMETER)
743             {
744                 BaseSetLastNTError(Status);
745                 _SEH2_LEAVE;
746             }
747         }
748         else
749         {
750             /* We managed to open, so query information */
751             Status = NtQueryInformationFile(SourceHandle,
752                                             &IoStatusBlock,
753                                             &FileAttrTagInfo,
754                                             sizeof(FILE_ATTRIBUTE_TAG_INFORMATION),
755                                             FileAttributeTagInformation);
756             if (!NT_SUCCESS(Status))
757             {
758                 /* Do not tolerate any other error than something related to not supported operation */
759                 if (Status != STATUS_NOT_IMPLEMENTED && Status != STATUS_INVALID_PARAMETER)
760                 {
761                     BaseSetLastNTError(Status);
762                     _SEH2_LEAVE;
763                 }
764 
765                 /* Not a reparse point, no need to reopen, it's fine */
766                 AttemptReopenWithoutReparse = FALSE;
767             }
768             /* Validate the reparse point (do we support it?) */
769             else if (FileAttrTagInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT &&
770                      FileAttrTagInfo.ReparseTag != IO_REPARSE_TAG_MOUNT_POINT)
771             {
772                 NtClose(SourceHandle);
773                 SourceHandle = INVALID_HANDLE_VALUE;
774             }
775             else
776             {
777                 /* Mount point, let's rename it */
778                 AttemptReopenWithoutReparse = FALSE;
779             }
780         }
781 
782         /* Simply reopen if required */
783         if (AttemptReopenWithoutReparse)
784         {
785             Status = NtOpenFile(&SourceHandle,
786                                 DELETE | SYNCHRONIZE,
787                                 &ObjectAttributes,
788                                 &IoStatusBlock,
789                                 FILE_SHARE_READ | FILE_SHARE_WRITE,
790                                 ((dwFlags & MOVEFILE_WRITE_THROUGH) ? FILE_WRITE_THROUGH : 0));
791             if (!NT_SUCCESS(Status))
792             {
793                 BaseSetLastNTError(Status);
794                 _SEH2_LEAVE;
795             }
796         }
797 
798         /* Nullify string if we're to use it */
799         if (DelayUntilReboot && !lpNewFileName)
800         {
801             RtlInitUnicodeString(&NewPathU, 0);
802         }
803         /* Check whether path exists */
804         else if (!RtlDosPathNameToNtPathName_U(lpNewFileName, &NewPathU, 0, 0))
805         {
806             BaseSetLastNTError(STATUS_OBJECT_PATH_NOT_FOUND);
807             _SEH2_LEAVE;
808         }
809 
810         /* Handle postponed renaming */
811         if (DelayUntilReboot)
812         {
813             /* If new file exists and we're allowed to replace, then mark the path with ! */
814             if (ReplaceIfExists && NewPathU.Length)
815             {
816                 NewBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, NewPathU.Length + sizeof(WCHAR));
817                 if (NewBuffer == NULL)
818                 {
819                     BaseSetLastNTError(STATUS_NO_MEMORY);
820                     _SEH2_LEAVE;
821                 }
822 
823                 NewBuffer[0] = L'!';
824                 RtlCopyMemory(&NewBuffer[1], NewPathU.Buffer, NewPathU.Length);
825                 NewPathU.Length += sizeof(WCHAR);
826                 NewPathU.MaximumLength += sizeof(WCHAR);
827                 RtlFreeHeap(RtlGetProcessHeap(), 0, NewPathU.Buffer);
828                 NewPathU.Buffer = NewBuffer;
829             }
830 
831             /* Check whether 'copy' renaming is allowed if required */
832             if (RtlDetermineDosPathNameType_U(lpExistingFileName) == RtlPathTypeUncAbsolute || dwFlags & MOVEFILE_COPY_ALLOWED)
833             {
834                 Status = STATUS_INVALID_PARAMETER;
835             }
836             else
837             {
838                 /* First, probe 2nd key to see whether it exists - if so, it will be appended there */
839                 Status = BasepMoveFileDelayed(&ExistingPathU, &NewPathU, 2, FALSE);
840                 if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
841                 {
842                     /* If doesn't exist, append to first key first, creating it if it doesn't exist */
843                     Status = BasepMoveFileDelayed(&ExistingPathU, &NewPathU, 1, TRUE);
844 
845                     if (Status == STATUS_INSUFFICIENT_RESOURCES)
846                     {
847                         /* If it failed because it's too big, then create 2nd key and put it there */
848                         Status = BasepMoveFileDelayed(&ExistingPathU, &NewPathU, 2, TRUE);
849                     }
850                 }
851             }
852 
853             /* If we failed at some point, return the error */
854             if (!NT_SUCCESS(Status))
855             {
856                 BaseSetLastNTError(Status);
857                 _SEH2_LEAVE;
858             }
859 
860             Ret = TRUE;
861             _SEH2_LEAVE;
862         }
863 
864         /* At that point, we MUST have a source handle */
865         ASSERT(SourceHandle != INVALID_HANDLE_VALUE);
866 
867         /* Allocate renaming buffer and fill it */
868         RenameInfo = RtlAllocateHeap(RtlGetProcessHeap(), 0, NewPathU.Length + sizeof(FILE_RENAME_INFORMATION));
869         if (RenameInfo == NULL)
870         {
871             BaseSetLastNTError(STATUS_NO_MEMORY);
872             _SEH2_LEAVE;
873         }
874 
875         RtlCopyMemory(&RenameInfo->FileName, NewPathU.Buffer, NewPathU.Length);
876         RenameInfo->ReplaceIfExists = ReplaceIfExists;
877         RenameInfo->RootDirectory = 0;
878         RenameInfo->FileNameLength = NewPathU.Length;
879 
880         /* Attempt to rename the file */
881         Status = NtSetInformationFile(SourceHandle,
882                                       &IoStatusBlock,
883                                       RenameInfo,
884                                       NewPathU.Length + sizeof(FILE_RENAME_INFORMATION),
885                                       ((dwFlags & MOVEFILE_CREATE_HARDLINK) ? FileLinkInformation : FileRenameInformation));
886         RtlFreeHeap(RtlGetProcessHeap(), 0, RenameInfo);
887         if (NT_SUCCESS(Status))
888         {
889             /* If it succeed, all fine, quit */
890             Ret = TRUE;
891             _SEH2_LEAVE;
892         }
893         /* If we failed for any other reason than not the same device, fail
894          * If we failed because of different devices, only allow renaming if user allowed copy
895          */
896         if (Status != STATUS_NOT_SAME_DEVICE || !(dwFlags & MOVEFILE_COPY_ALLOWED))
897         {
898             /* ReactOS hack! To be removed once all FSD have proper renaming support
899              * Just leave status to error and leave
900              */
901             if (Status == STATUS_NOT_IMPLEMENTED)
902             {
903                 DPRINT1("Forcing copy, renaming not supported by FSD\n");
904             }
905             else
906             {
907                 BaseSetLastNTError(Status);
908                 _SEH2_LEAVE;
909             }
910         }
911 
912         /* Close source file */
913         NtClose(SourceHandle);
914         SourceHandle = INVALID_HANDLE_VALUE;
915 
916         /* Issue the copy of the file */
917         CopyContext.Flags = dwFlags;
918         CopyContext.UserRoutine = lpProgressRoutine;
919         CopyContext.UserData = lpData;
920         NewHandle = INVALID_HANDLE_VALUE;
921         ExistingHandle = INVALID_HANDLE_VALUE;
922 
923         Ret = BasepCopyFileExW(lpExistingFileName,
924                                lpNewFileName,
925                                BasepMoveFileCopyProgress,
926                                &CopyContext,
927                                NULL,
928                                (ReplaceIfExists == 0) | COPY_FILE_OPEN_SOURCE_FOR_WRITE,
929                                0,
930                                &ExistingHandle,
931                                &NewHandle);
932         if (!Ret)
933         {
934             /* If it failed, don't leak any handle */
935             if (ExistingHandle != INVALID_HANDLE_VALUE)
936             {
937                 CloseHandle(ExistingHandle);
938                 ExistingHandle = INVALID_HANDLE_VALUE;
939             }
940         }
941         else if (ExistingHandle != INVALID_HANDLE_VALUE)
942         {
943             if (NewHandle != INVALID_HANDLE_VALUE)
944             {
945                 /* If copying succeed, notify */
946                 Status = BasepNotifyTrackingService(&ExistingHandle, &ObjectAttributes, NewHandle, &NewPathU);
947                 if (!NT_SUCCESS(Status))
948                 {
949                     /* Fail in case it had to succeed */
950                     if (dwFlags & MOVEFILE_FAIL_IF_NOT_TRACKABLE)
951                     {
952                         if (NewHandle != INVALID_HANDLE_VALUE)
953                             CloseHandle(NewHandle);
954                         NewHandle = INVALID_HANDLE_VALUE;
955                         DeleteFileW(lpNewFileName);
956                         Ret = FALSE;
957                         BaseSetLastNTError(Status);
958                     }
959                 }
960             }
961 
962             CloseHandle(ExistingHandle);
963             ExistingHandle = INVALID_HANDLE_VALUE;
964         }
965 
966         /* In case copy worked, close file */
967         if (NewHandle != INVALID_HANDLE_VALUE)
968         {
969             CloseHandle(NewHandle);
970             NewHandle = INVALID_HANDLE_VALUE;
971         }
972 
973         /* If it succeed, delete source file */
974         if (Ret)
975         {
976             if (!DeleteFileW(lpExistingFileName))
977             {
978                 /* Reset file attributes if required */
979                 SetFileAttributesW(lpExistingFileName, FILE_ATTRIBUTE_NORMAL);
980                 DeleteFileW(lpExistingFileName);
981             }
982         }
983     }
984     _SEH2_FINALLY
985     {
986         if (SourceHandle != INVALID_HANDLE_VALUE)
987             NtClose(SourceHandle);
988 
989         RtlFreeHeap(RtlGetProcessHeap(), 0, ExistingPathU.Buffer);
990         RtlFreeHeap(RtlGetProcessHeap(), 0, NewPathU.Buffer);
991     }
992     _SEH2_END;
993 
994     return Ret;
995 }
996 
997 
998 /*
999  * @implemented
1000  */
1001 BOOL
1002 WINAPI
1003 MoveFileWithProgressA(IN LPCSTR lpExistingFileName,
1004                       IN LPCSTR lpNewFileName OPTIONAL,
1005                       IN LPPROGRESS_ROUTINE lpProgressRoutine OPTIONAL,
1006                       IN LPVOID lpData OPTIONAL,
1007                       IN DWORD dwFlags)
1008 {
1009     BOOL Ret;
1010     UNICODE_STRING ExistingFileNameW, NewFileNameW;
1011 
1012     if (!Basep8BitStringToDynamicUnicodeString(&ExistingFileNameW, lpExistingFileName))
1013     {
1014         return FALSE;
1015     }
1016 
1017     if (lpNewFileName)
1018     {
1019         if (!Basep8BitStringToDynamicUnicodeString(&NewFileNameW, lpNewFileName))
1020         {
1021             RtlFreeUnicodeString(&ExistingFileNameW);
1022             return FALSE;
1023         }
1024     }
1025     else
1026     {
1027         NewFileNameW.Buffer = NULL;
1028     }
1029 
1030     Ret = MoveFileWithProgressW(ExistingFileNameW.Buffer, NewFileNameW.Buffer, lpProgressRoutine, lpData, dwFlags);
1031 
1032     RtlFreeUnicodeString(&ExistingFileNameW);
1033     RtlFreeUnicodeString(&NewFileNameW);
1034 
1035     return Ret;
1036 }
1037 
1038 
1039 /*
1040  * @implemented
1041  */
1042 BOOL
1043 WINAPI
1044 MoveFileW(IN LPCWSTR lpExistingFileName,
1045           IN LPCWSTR lpNewFileName)
1046 {
1047     return MoveFileWithProgressW(lpExistingFileName,
1048                                  lpNewFileName,
1049                                  NULL,
1050                                  NULL,
1051                                  MOVEFILE_COPY_ALLOWED);
1052 }
1053 
1054 
1055 /*
1056  * @implemented
1057  */
1058 BOOL
1059 WINAPI
1060 MoveFileExW(IN LPCWSTR lpExistingFileName,
1061             IN LPCWSTR lpNewFileName OPTIONAL,
1062             IN DWORD dwFlags)
1063 {
1064     return MoveFileWithProgressW(lpExistingFileName,
1065 	                         lpNewFileName,
1066 	                         NULL,
1067 	                         NULL,
1068 	                         dwFlags);
1069 }
1070 
1071 
1072 /*
1073  * @implemented
1074  */
1075 BOOL
1076 WINAPI
1077 MoveFileA(IN LPCSTR lpExistingFileName,
1078           IN LPCSTR lpNewFileName)
1079 {
1080     return MoveFileWithProgressA(lpExistingFileName,
1081                                  lpNewFileName,
1082 	                         NULL,
1083 	                         NULL,
1084                                  MOVEFILE_COPY_ALLOWED);
1085 }
1086 
1087 
1088 /*
1089  * @implemented
1090  */
1091 BOOL
1092 WINAPI
1093 MoveFileExA(IN LPCSTR lpExistingFileName,
1094             IN LPCSTR lpNewFileName OPTIONAL,
1095             IN DWORD dwFlags)
1096 {
1097     return MoveFileWithProgressA(lpExistingFileName,
1098                                  lpNewFileName,
1099                                  NULL,
1100                                  NULL,
1101                                  dwFlags);
1102 }
1103 
1104 /*
1105  * @implemented
1106  */
1107 BOOL
1108 WINAPI
1109 ReplaceFileA(IN LPCSTR lpReplacedFileName,
1110              IN LPCSTR lpReplacementFileName,
1111              IN LPCSTR lpBackupFileName OPTIONAL,
1112              IN DWORD dwReplaceFlags,
1113              IN LPVOID lpExclude,
1114              IN LPVOID lpReserved)
1115 {
1116     BOOL Ret;
1117     UNICODE_STRING ReplacedFileNameW, ReplacementFileNameW, BackupFileNameW;
1118 
1119     if (!lpReplacedFileName || !lpReplacementFileName || lpExclude || lpReserved || dwReplaceFlags & ~(REPLACEFILE_WRITE_THROUGH | REPLACEFILE_IGNORE_MERGE_ERRORS))
1120     {
1121         SetLastError(ERROR_INVALID_PARAMETER);
1122         return FALSE;
1123     }
1124 
1125     if (!Basep8BitStringToDynamicUnicodeString(&ReplacedFileNameW, lpReplacedFileName))
1126     {
1127         return FALSE;
1128     }
1129 
1130     if (!Basep8BitStringToDynamicUnicodeString(&ReplacementFileNameW, lpReplacementFileName))
1131     {
1132         RtlFreeUnicodeString(&ReplacedFileNameW);
1133         return FALSE;
1134     }
1135 
1136     if (lpBackupFileName)
1137     {
1138         if (!Basep8BitStringToDynamicUnicodeString(&BackupFileNameW, lpBackupFileName))
1139         {
1140             RtlFreeUnicodeString(&ReplacementFileNameW);
1141             RtlFreeUnicodeString(&ReplacedFileNameW);
1142             return FALSE;
1143         }
1144     }
1145     else
1146     {
1147         BackupFileNameW.Buffer = NULL;
1148     }
1149 
1150     Ret = ReplaceFileW(ReplacedFileNameW.Buffer, ReplacementFileNameW.Buffer, BackupFileNameW.Buffer, dwReplaceFlags, 0, 0);
1151 
1152     if (lpBackupFileName)
1153     {
1154         RtlFreeUnicodeString(&BackupFileNameW);
1155     }
1156     RtlFreeUnicodeString(&ReplacementFileNameW);
1157     RtlFreeUnicodeString(&ReplacedFileNameW);
1158 
1159     return Ret;
1160 }
1161 
1162 /*
1163  * @unimplemented
1164  */
1165 BOOL
1166 WINAPI
1167 ReplaceFileW(
1168     LPCWSTR lpReplacedFileName,
1169     LPCWSTR lpReplacementFileName,
1170     LPCWSTR lpBackupFileName,
1171     DWORD   dwReplaceFlags,
1172     LPVOID  lpExclude,
1173     LPVOID  lpReserved
1174     )
1175 {
1176     HANDLE hReplaced = NULL, hReplacement = NULL;
1177     UNICODE_STRING NtReplacedName = { 0, 0, NULL };
1178     UNICODE_STRING NtReplacementName = { 0, 0, NULL };
1179     DWORD Error = ERROR_SUCCESS;
1180     NTSTATUS Status;
1181     BOOL Ret = FALSE;
1182     IO_STATUS_BLOCK IoStatusBlock;
1183     OBJECT_ATTRIBUTES ObjectAttributes;
1184     PVOID Buffer = NULL ;
1185 
1186     if (dwReplaceFlags)
1187         FIXME("Ignoring flags %x\n", dwReplaceFlags);
1188 
1189     /* First two arguments are mandatory */
1190     if (!lpReplacedFileName || !lpReplacementFileName)
1191     {
1192         SetLastError(ERROR_INVALID_PARAMETER);
1193         return FALSE;
1194     }
1195 
1196     /* Back it up */
1197     if(lpBackupFileName)
1198     {
1199         if(!CopyFileW(lpReplacedFileName, lpBackupFileName, FALSE))
1200         {
1201             Error = GetLastError();
1202             goto Cleanup ;
1203         }
1204     }
1205 
1206     /* Open the "replaced" file for reading and writing */
1207     if (!(RtlDosPathNameToNtPathName_U(lpReplacedFileName, &NtReplacedName, NULL, NULL)))
1208     {
1209         Error = ERROR_PATH_NOT_FOUND;
1210         goto Cleanup;
1211     }
1212 
1213     InitializeObjectAttributes(&ObjectAttributes,
1214                                &NtReplacedName,
1215                                OBJ_CASE_INSENSITIVE,
1216                                NULL,
1217                                NULL);
1218 
1219     Status = NtOpenFile(&hReplaced,
1220                         GENERIC_READ | GENERIC_WRITE | DELETE | SYNCHRONIZE | WRITE_DAC,
1221                         &ObjectAttributes,
1222                         &IoStatusBlock,
1223                         FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1224                         FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE);
1225 
1226     if (!NT_SUCCESS(Status))
1227     {
1228         if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
1229             Error = ERROR_FILE_NOT_FOUND;
1230         else
1231             Error = ERROR_UNABLE_TO_REMOVE_REPLACED;
1232         goto Cleanup;
1233     }
1234 
1235     /* Blank it */
1236     SetEndOfFile(hReplaced) ;
1237 
1238     /*
1239      * Open the replacement file for reading, writing, and deleting
1240      * (deleting is needed when finished)
1241      */
1242     if (!(RtlDosPathNameToNtPathName_U(lpReplacementFileName, &NtReplacementName, NULL, NULL)))
1243     {
1244         Error = ERROR_PATH_NOT_FOUND;
1245         goto Cleanup;
1246     }
1247 
1248     InitializeObjectAttributes(&ObjectAttributes,
1249                                &NtReplacementName,
1250                                OBJ_CASE_INSENSITIVE,
1251                                NULL,
1252                                NULL);
1253 
1254     Status = NtOpenFile(&hReplacement,
1255                         GENERIC_READ | DELETE | SYNCHRONIZE,
1256                         &ObjectAttributes,
1257                         &IoStatusBlock,
1258                         0,
1259                         FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE | FILE_DELETE_ON_CLOSE);
1260 
1261     if (!NT_SUCCESS(Status))
1262     {
1263         Error = RtlNtStatusToDosError(Status);
1264         goto Cleanup;
1265     }
1266 
1267     Buffer = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, 0x10000) ;
1268     if (!Buffer)
1269     {
1270         Error = ERROR_NOT_ENOUGH_MEMORY;
1271         goto Cleanup ;
1272     }
1273     while (Status != STATUS_END_OF_FILE)
1274     {
1275         Status = NtReadFile(hReplacement, NULL, NULL, NULL, &IoStatusBlock, Buffer, 0x10000, NULL, NULL) ;
1276         if (NT_SUCCESS(Status))
1277         {
1278             Status = NtWriteFile(hReplaced, NULL, NULL, NULL, &IoStatusBlock, Buffer,
1279                     IoStatusBlock.Information, NULL, NULL) ;
1280             if (!NT_SUCCESS(Status))
1281             {
1282                 Error = RtlNtStatusToDosError(Status);
1283                 goto Cleanup;
1284             }
1285         }
1286         else if (Status != STATUS_END_OF_FILE)
1287         {
1288             Error = RtlNtStatusToDosError(Status);
1289             goto Cleanup;
1290         }
1291     }
1292 
1293     Ret = TRUE;
1294 
1295     /* Perform resource cleanup */
1296 Cleanup:
1297     if (hReplaced) NtClose(hReplaced);
1298     if (hReplacement) NtClose(hReplacement);
1299     if (Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
1300 
1301     if (NtReplacementName.Buffer)
1302         RtlFreeHeap(GetProcessHeap(), 0, NtReplacementName.Buffer);
1303     if (NtReplacedName.Buffer)
1304         RtlFreeHeap(GetProcessHeap(), 0, NtReplacedName.Buffer);
1305 
1306     /* If there was an error, set the error code */
1307     if(!Ret)
1308     {
1309         TRACE("ReplaceFileW failed (error=%lu)\n", Error);
1310         SetLastError(Error);
1311     }
1312     return Ret;
1313 }
1314 
1315 
1316 /*
1317  * @implemented
1318  */
1319 BOOL
1320 WINAPI
1321 PrivMoveFileIdentityW(IN LPCWSTR lpSource, IN LPCWSTR lpDestination, IN DWORD dwFlags)
1322 {
1323     ACCESS_MASK SourceAccess;
1324     UNICODE_STRING NtSource, NtDestination;
1325     LPWSTR RelativeSource, RelativeDestination;
1326     HANDLE SourceHandle, DestinationHandle;
1327     OBJECT_ATTRIBUTES ObjectAttributesSource, ObjectAttributesDestination;
1328     NTSTATUS Status, OldStatus = STATUS_SUCCESS;
1329     ACCESS_MASK DestAccess;
1330     IO_STATUS_BLOCK IoStatusBlock;
1331     FILE_BASIC_INFORMATION SourceInformation, DestinationInformation;
1332     FILE_DISPOSITION_INFORMATION FileDispositionInfo;
1333 
1334     DPRINT("PrivMoveFileIdentityW(%S, %S, %x)\n", lpSource, lpDestination, dwFlags);
1335 
1336     SourceHandle = INVALID_HANDLE_VALUE;
1337     NtSource.Length =
1338     NtSource.MaximumLength = 0;
1339     NtSource.Buffer = NULL;
1340     RelativeSource = NULL;
1341     DestinationHandle = INVALID_HANDLE_VALUE;
1342     NtDestination.Length =
1343     NtDestination.MaximumLength = 0;
1344     NtDestination.Buffer = NULL;
1345     RelativeDestination = NULL;
1346 
1347     /* FILE_WRITE_DATA is required for later on notification */
1348     SourceAccess = FILE_READ_ATTRIBUTES | FILE_WRITE_DATA;
1349     if (dwFlags & PRIV_DELETE_ON_SUCCESS)
1350     {
1351         SourceAccess |= DELETE;
1352     }
1353 
1354     _SEH2_TRY
1355     {
1356         /* We will loop twice:
1357          * First we attempt to open with FILE_WRITE_DATA for notification
1358          * If it fails and we have flag for non-trackable files, we retry
1359          * without FILE_WRITE_DATA.
1360          * If that one fails, then, we quit for real
1361          */
1362         while (TRUE)
1363         {
1364             Status = BasepOpenFileForMove(lpSource,
1365                                           &NtSource,
1366                                           &RelativeSource,
1367                                           &SourceHandle,
1368                                           &ObjectAttributesSource,
1369                                           SourceAccess,
1370                                           FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1371                                           FILE_OPEN_NO_RECALL);
1372             if (NT_SUCCESS(Status))
1373             {
1374                 break;
1375             }
1376 
1377             /* If we already attempted the opening without FILE_WRITE_DATA
1378              * or if we cannot move on non-trackable files, fail.
1379              */
1380             if (!(SourceAccess & FILE_WRITE_DATA) || !(dwFlags & PRIV_ALLOW_NON_TRACKABLE))
1381             {
1382                 _SEH2_LEAVE;
1383             }
1384 
1385             if (RelativeSource)
1386             {
1387                 RtlFreeHeap(RtlGetProcessHeap(), 0, RelativeSource);
1388                 RelativeSource = NULL;
1389             }
1390 
1391             if (SourceHandle != INVALID_HANDLE_VALUE)
1392             {
1393                 NtClose(SourceHandle);
1394                 SourceHandle = INVALID_HANDLE_VALUE;
1395             }
1396 
1397             SourceAccess &= ~FILE_WRITE_DATA;
1398 
1399             /* Remember fist failure in the path */
1400             if (NT_SUCCESS(OldStatus))
1401             {
1402                 OldStatus = Status;
1403             }
1404         }
1405 
1406         DestAccess = FILE_WRITE_ATTRIBUTES;
1407         /* If we could preserve FILE_WRITE_DATA for source, attempt to get it for destination
1408          * Still for notification purposes
1409          */
1410         if (SourceAccess & FILE_WRITE_DATA)
1411         {
1412             DestAccess |= FILE_WRITE_DATA;
1413         }
1414 
1415         /* cf comment for first loop */
1416         while (TRUE)
1417         {
1418             Status = BasepOpenFileForMove(lpDestination,
1419                                           &NtDestination,
1420                                           &RelativeDestination,
1421                                           &DestinationHandle,
1422                                           &ObjectAttributesDestination,
1423                                           DestAccess,
1424                                           FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1425                                           FILE_OPEN_NO_RECALL);
1426             if (NT_SUCCESS(Status))
1427             {
1428                 break;
1429             }
1430 
1431             /* If we already attempted the opening without FILE_WRITE_DATA
1432              * or if we cannot move on non-trackable files, fail.
1433              */
1434             if (!(DestAccess & FILE_WRITE_DATA) || !(dwFlags & PRIV_ALLOW_NON_TRACKABLE))
1435             {
1436                 _SEH2_LEAVE;
1437             }
1438 
1439             if (RelativeDestination)
1440             {
1441                 RtlFreeHeap(RtlGetProcessHeap(), 0, RelativeDestination);
1442                 RelativeDestination = NULL;
1443             }
1444 
1445             if (DestinationHandle != INVALID_HANDLE_VALUE)
1446             {
1447                 NtClose(DestinationHandle);
1448                 DestinationHandle = INVALID_HANDLE_VALUE;
1449             }
1450 
1451             DestAccess &= ~FILE_WRITE_DATA;
1452 
1453             /* Remember fist failure in the path */
1454             if (NT_SUCCESS(OldStatus))
1455             {
1456                 OldStatus = Status;
1457             }
1458         }
1459 
1460         /* Get the creation time from source */
1461         Status = NtQueryInformationFile(SourceHandle,
1462                                         &IoStatusBlock,
1463                                         &SourceInformation,
1464                                         sizeof(SourceInformation),
1465                                         FileBasicInformation);
1466         if (NT_SUCCESS(Status))
1467         {
1468             /* Then, prepare to set it for destination */
1469             RtlZeroMemory(&DestinationInformation, sizeof(DestinationInformation));
1470             DestinationInformation.CreationTime.QuadPart = SourceInformation.CreationTime.QuadPart;
1471 
1472             /* And set it, that's all folks! */
1473             Status = NtSetInformationFile(DestinationHandle,
1474                                           &IoStatusBlock,
1475                                           &DestinationInformation,
1476                                           sizeof(DestinationInformation),
1477                                           FileBasicInformation);
1478         }
1479 
1480         if (!NT_SUCCESS(Status))
1481         {
1482             if (!(dwFlags & PRIV_ALLOW_NON_TRACKABLE))
1483             {
1484                 _SEH2_LEAVE;
1485             }
1486 
1487             /* Remember the failure for later notification */
1488             if (NT_SUCCESS(OldStatus))
1489             {
1490                 OldStatus = Status;
1491             }
1492         }
1493 
1494         /* If we could open with FILE_WRITE_DATA both source and destination,
1495          * then, notify
1496          */
1497         if (DestAccess & FILE_WRITE_DATA && SourceAccess & FILE_WRITE_DATA)
1498         {
1499             Status = BasepNotifyTrackingService(&SourceHandle,
1500                                                 &ObjectAttributesSource,
1501                                                 DestinationHandle,
1502                                                 &NtDestination);
1503             if (!NT_SUCCESS(Status))
1504             {
1505                 if (dwFlags & PRIV_ALLOW_NON_TRACKABLE)
1506                 {
1507                     if (NT_SUCCESS(OldStatus))
1508                         OldStatus = Status;
1509 
1510                     /* Reset status, we allow non trackable files */
1511                     Status = STATUS_SUCCESS;
1512                 }
1513             }
1514         }
1515     }
1516     _SEH2_FINALLY
1517     {
1518         if (RelativeSource)
1519             RtlFreeHeap(RtlGetProcessHeap(), 0, RelativeSource);
1520 
1521         if (RelativeDestination)
1522             RtlFreeHeap(RtlGetProcessHeap(), 0, RelativeDestination);
1523     }
1524     _SEH2_END;
1525 
1526     /* If caller asked for source deletion, if everything succeed, proceed */
1527     if (NT_SUCCESS(Status) && dwFlags & PRIV_DELETE_ON_SUCCESS)
1528     {
1529         FileDispositionInfo.DeleteFile = TRUE;
1530 
1531         Status = NtSetInformationFile(SourceHandle,
1532                                       &IoStatusBlock,
1533                                       &FileDispositionInfo,
1534                                       sizeof(FileDispositionInfo),
1535                                       FileDispositionInformation);
1536     }
1537 
1538     /* Cleanup/close portion */
1539     if (DestinationHandle != INVALID_HANDLE_VALUE)
1540     {
1541         NtClose(DestinationHandle);
1542     }
1543 
1544     if (SourceHandle != INVALID_HANDLE_VALUE)
1545     {
1546         NtClose(SourceHandle);
1547     }
1548 
1549     /* Set last error if any, and quit */
1550     if (NT_SUCCESS(Status))
1551     {
1552         if (!NT_SUCCESS(OldStatus))
1553         {
1554             BaseSetLastNTError(OldStatus);
1555         }
1556     }
1557     else
1558     {
1559         BaseSetLastNTError(Status);
1560     }
1561 
1562     return NT_SUCCESS(Status);
1563 }
1564 
1565 /* EOF */
1566