[KERNEL32]
[reactos.git] / reactos / dll / win32 / kernel32 / client / file / move.c
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 */