[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: lib/kernel32/file/file.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 }
776
777 /* Simply reopen if required */
778 if (AttemptReopenWithoutReparse)
779 {
780 Status = NtOpenFile(&SourceHandle,
781 DELETE | SYNCHRONIZE,
782 &ObjectAttributes,
783 &IoStatusBlock,
784 FILE_SHARE_READ | FILE_SHARE_WRITE,
785 ((dwFlags & MOVEFILE_WRITE_THROUGH) ? FILE_WRITE_THROUGH : 0));
786 if (!NT_SUCCESS(Status))
787 {
788 BaseSetLastNTError(Status);
789 _SEH2_LEAVE;
790 }
791 }
792
793 /* Nullify string if we're to use it */
794 if (DelayUntilReboot && !lpNewFileName)
795 {
796 RtlInitUnicodeString(&NewPathU, 0);
797 }
798 /* Check whether path exists */
799 else if (!RtlDosPathNameToNtPathName_U(lpNewFileName, &NewPathU, 0, 0))
800 {
801 BaseSetLastNTError(STATUS_OBJECT_PATH_NOT_FOUND);
802 _SEH2_LEAVE;
803 }
804
805 /* Handle postponed renaming */
806 if (DelayUntilReboot)
807 {
808 /* If new file exists and we're allowed to replace, then mark the path with ! */
809 if (ReplaceIfExists && NewPathU.Length)
810 {
811 NewBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, NewPathU.Length + sizeof(WCHAR));
812 if (NewBuffer == NULL)
813 {
814 BaseSetLastNTError(STATUS_NO_MEMORY);
815 _SEH2_LEAVE;
816 }
817
818 NewBuffer[0] = L'!';
819 RtlCopyMemory(&NewBuffer[1], NewPathU.Buffer, NewPathU.Length);
820 NewPathU.Length += sizeof(WCHAR);
821 NewPathU.MaximumLength += sizeof(WCHAR);
822 RtlFreeHeap(RtlGetProcessHeap(), 0, NewPathU.Buffer);
823 NewPathU.Buffer = NewBuffer;
824 }
825
826 /* Check whether 'copy' renaming is allowed if required */
827 if (RtlDetermineDosPathNameType_U(lpExistingFileName) == RtlPathTypeUncAbsolute || dwFlags & MOVEFILE_COPY_ALLOWED)
828 {
829 Status = STATUS_INVALID_PARAMETER;
830 }
831 else
832 {
833 /* First, probe 2nd key to see whether it exists - if so, it will be appended there */
834 Status = BasepMoveFileDelayed(&ExistingPathU, &NewPathU, 2, FALSE);
835 if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
836 {
837 /* If doesn't exist, append to first key first, creating it if it doesn't exist */
838 Status = BasepMoveFileDelayed(&ExistingPathU, &NewPathU, 1, TRUE);
839
840 if (Status == STATUS_INSUFFICIENT_RESOURCES)
841 {
842 /* If it failed because it's too big, then create 2nd key and put it there */
843 Status = BasepMoveFileDelayed(&ExistingPathU, &NewPathU, 2, TRUE);
844 }
845 }
846 }
847
848 /* If we failed at some point, return the error */
849 if (!NT_SUCCESS(Status))
850 {
851 BaseSetLastNTError(Status);
852 _SEH2_LEAVE;
853 }
854
855 Ret = TRUE;
856 _SEH2_LEAVE;
857 }
858
859 /* At that point, we MUST have a source handle */
860 ASSERT(SourceHandle != INVALID_HANDLE_VALUE);
861
862 /* Allocate renaming buffer and fill it */
863 RenameInfo = RtlAllocateHeap(RtlGetProcessHeap(), 0, NewPathU.Length + sizeof(FILE_RENAME_INFORMATION));
864 if (RenameInfo == NULL)
865 {
866 BaseSetLastNTError(STATUS_NO_MEMORY);
867 _SEH2_LEAVE;
868 }
869
870 RtlCopyMemory(&RenameInfo->FileName, NewPathU.Buffer, NewPathU.Length);
871 RenameInfo->ReplaceIfExists = ReplaceIfExists;
872 RenameInfo->RootDirectory = 0;
873 RenameInfo->FileNameLength = NewPathU.Length;
874
875 /* Attempt to rename the file */
876 Status = NtSetInformationFile(SourceHandle,
877 &IoStatusBlock,
878 RenameInfo,
879 NewPathU.Length + sizeof(FILE_RENAME_INFORMATION),
880 ((dwFlags & MOVEFILE_CREATE_HARDLINK) ? FileLinkInformation : FileRenameInformation));
881 RtlFreeHeap(RtlGetProcessHeap(), 0, RenameInfo);
882 if (NT_SUCCESS(Status))
883 {
884 /* If it succeed, all fine, quit */
885 Ret = TRUE;
886 _SEH2_LEAVE;
887 }
888 /* If we failed for any other reason than not the same device, fail
889 * If we failed because of different devices, only allow renaming if user allowed copy
890 */
891 if (Status != STATUS_NOT_SAME_DEVICE || !(dwFlags & MOVEFILE_COPY_ALLOWED))
892 {
893 /* ReactOS hack! To be removed once all FSD have proper renaming support
894 * Just leave status to error and leave
895 */
896 if (Status == STATUS_NOT_IMPLEMENTED)
897 {
898 DPRINT1("Forcing copy, renaming not supported by FSD\n");
899 }
900 else
901 {
902 BaseSetLastNTError(Status);
903 _SEH2_LEAVE;
904 }
905 }
906
907 /* Close source file */
908 NtClose(SourceHandle);
909 SourceHandle = INVALID_HANDLE_VALUE;
910
911 /* Issue the copy of the file */
912 CopyContext.Flags = dwFlags;
913 CopyContext.UserRoutine = lpProgressRoutine;
914 CopyContext.UserData = lpData;
915 NewHandle = INVALID_HANDLE_VALUE;
916 ExistingHandle = INVALID_HANDLE_VALUE;
917
918 Ret = BasepCopyFileExW(lpExistingFileName,
919 lpNewFileName,
920 BasepMoveFileCopyProgress,
921 &CopyContext,
922 NULL,
923 (ReplaceIfExists == 0) | COPY_FILE_OPEN_SOURCE_FOR_WRITE,
924 0,
925 &ExistingHandle,
926 &NewHandle);
927 if (!Ret)
928 {
929 /* If it failed, don't leak any handle */
930 if (ExistingHandle != INVALID_HANDLE_VALUE)
931 {
932 CloseHandle(ExistingHandle);
933 ExistingHandle = INVALID_HANDLE_VALUE;
934 }
935 }
936 else if (ExistingHandle != INVALID_HANDLE_VALUE)
937 {
938 if (NewHandle != INVALID_HANDLE_VALUE)
939 {
940 /* If copying succeed, notify */
941 Status = BasepNotifyTrackingService(&ExistingHandle, &ObjectAttributes, NewHandle, &NewPathU);
942 if (!NT_SUCCESS(Status))
943 {
944 /* Fail in case it had to succeed */
945 if (dwFlags & MOVEFILE_FAIL_IF_NOT_TRACKABLE)
946 {
947 if (NewHandle != INVALID_HANDLE_VALUE)
948 CloseHandle(NewHandle);
949 NewHandle = INVALID_HANDLE_VALUE;
950 DeleteFileW(lpNewFileName);
951 Ret = FALSE;
952 BaseSetLastNTError(Status);
953 }
954 }
955 }
956
957 CloseHandle(ExistingHandle);
958 ExistingHandle = INVALID_HANDLE_VALUE;
959 }
960
961 /* In case copy worked, close file */
962 if (NewHandle != INVALID_HANDLE_VALUE)
963 {
964 CloseHandle(NewHandle);
965 NewHandle = INVALID_HANDLE_VALUE;
966 }
967
968 /* If it succeed, delete source file */
969 if (Ret)
970 {
971 if (!DeleteFileW(lpExistingFileName))
972 {
973 /* Reset file attributes if required */
974 SetFileAttributesW(lpExistingFileName, FILE_ATTRIBUTE_NORMAL);
975 DeleteFileW(lpExistingFileName);
976 }
977 }
978 }
979 _SEH2_FINALLY
980 {
981 if (SourceHandle != INVALID_HANDLE_VALUE)
982 NtClose(SourceHandle);
983
984 RtlFreeHeap(RtlGetProcessHeap(), 0, ExistingPathU.Buffer);
985 RtlFreeHeap(RtlGetProcessHeap(), 0, NewPathU.Buffer);
986 }
987 _SEH2_END;
988
989 return Ret;
990 }
991
992
993 /*
994 * @implemented
995 */
996 BOOL
997 WINAPI
998 MoveFileWithProgressA(IN LPCSTR lpExistingFileName,
999 IN LPCSTR lpNewFileName OPTIONAL,
1000 IN LPPROGRESS_ROUTINE lpProgressRoutine OPTIONAL,
1001 IN LPVOID lpData OPTIONAL,
1002 IN DWORD dwFlags)
1003 {
1004 BOOL Ret;
1005 UNICODE_STRING ExistingFileNameW, NewFileNameW;
1006
1007 if (!Basep8BitStringToDynamicUnicodeString(&ExistingFileNameW, lpExistingFileName))
1008 {
1009 return FALSE;
1010 }
1011
1012 if (lpNewFileName)
1013 {
1014 if (!Basep8BitStringToDynamicUnicodeString(&NewFileNameW, lpNewFileName))
1015 {
1016 RtlFreeUnicodeString(&ExistingFileNameW);
1017 return FALSE;
1018 }
1019 }
1020 else
1021 {
1022 NewFileNameW.Buffer = NULL;
1023 }
1024
1025 Ret = MoveFileWithProgressW(ExistingFileNameW.Buffer, NewFileNameW.Buffer, lpProgressRoutine, lpData, dwFlags);
1026
1027 RtlFreeUnicodeString(&ExistingFileNameW);
1028 RtlFreeUnicodeString(&NewFileNameW);
1029
1030 return Ret;
1031 }
1032
1033
1034 /*
1035 * @implemented
1036 */
1037 BOOL
1038 WINAPI
1039 MoveFileW(IN LPCWSTR lpExistingFileName,
1040 IN LPCWSTR lpNewFileName)
1041 {
1042 return MoveFileWithProgressW(lpExistingFileName,
1043 lpNewFileName,
1044 NULL,
1045 NULL,
1046 MOVEFILE_COPY_ALLOWED);
1047 }
1048
1049
1050 /*
1051 * @implemented
1052 */
1053 BOOL
1054 WINAPI
1055 MoveFileExW(IN LPCWSTR lpExistingFileName,
1056 IN LPCWSTR lpNewFileName OPTIONAL,
1057 IN DWORD dwFlags)
1058 {
1059 return MoveFileWithProgressW(lpExistingFileName,
1060 lpNewFileName,
1061 NULL,
1062 NULL,
1063 dwFlags);
1064 }
1065
1066
1067 /*
1068 * @implemented
1069 */
1070 BOOL
1071 WINAPI
1072 MoveFileA(IN LPCSTR lpExistingFileName,
1073 IN LPCSTR lpNewFileName)
1074 {
1075 return MoveFileWithProgressA(lpExistingFileName,
1076 lpNewFileName,
1077 NULL,
1078 NULL,
1079 MOVEFILE_COPY_ALLOWED);
1080 }
1081
1082
1083 /*
1084 * @implemented
1085 */
1086 BOOL
1087 WINAPI
1088 MoveFileExA(IN LPCSTR lpExistingFileName,
1089 IN LPCSTR lpNewFileName OPTIONAL,
1090 IN DWORD dwFlags)
1091 {
1092 return MoveFileWithProgressA(lpExistingFileName,
1093 lpNewFileName,
1094 NULL,
1095 NULL,
1096 dwFlags);
1097 }
1098
1099 /*
1100 * @implemented
1101 */
1102 BOOL
1103 WINAPI
1104 ReplaceFileA(IN LPCSTR lpReplacedFileName,
1105 IN LPCSTR lpReplacementFileName,
1106 IN LPCSTR lpBackupFileName OPTIONAL,
1107 IN DWORD dwReplaceFlags,
1108 IN LPVOID lpExclude,
1109 IN LPVOID lpReserved)
1110 {
1111 BOOL Ret;
1112 UNICODE_STRING ReplacedFileNameW, ReplacementFileNameW, BackupFileNameW;
1113
1114 if (!lpReplacedFileName || !lpReplacementFileName || lpExclude || lpReserved || dwReplaceFlags & ~(REPLACEFILE_WRITE_THROUGH | REPLACEFILE_IGNORE_MERGE_ERRORS))
1115 {
1116 SetLastError(ERROR_INVALID_PARAMETER);
1117 return FALSE;
1118 }
1119
1120 if (!Basep8BitStringToDynamicUnicodeString(&ReplacedFileNameW, lpReplacedFileName))
1121 {
1122 return FALSE;
1123 }
1124
1125 if (!Basep8BitStringToDynamicUnicodeString(&ReplacementFileNameW, lpReplacementFileName))
1126 {
1127 RtlFreeUnicodeString(&ReplacedFileNameW);
1128 return FALSE;
1129 }
1130
1131 if (lpBackupFileName)
1132 {
1133 if (!Basep8BitStringToDynamicUnicodeString(&BackupFileNameW, lpBackupFileName))
1134 {
1135 RtlFreeUnicodeString(&ReplacementFileNameW);
1136 RtlFreeUnicodeString(&ReplacedFileNameW);
1137 return FALSE;
1138 }
1139 }
1140 else
1141 {
1142 BackupFileNameW.Buffer = NULL;
1143 }
1144
1145 Ret = ReplaceFileW(ReplacedFileNameW.Buffer, ReplacementFileNameW.Buffer, BackupFileNameW.Buffer, dwReplaceFlags, 0, 0);
1146
1147 if (lpBackupFileName)
1148 {
1149 RtlFreeUnicodeString(&BackupFileNameW);
1150 }
1151 RtlFreeUnicodeString(&ReplacementFileNameW);
1152 RtlFreeUnicodeString(&ReplacedFileNameW);
1153
1154 return Ret;
1155 }
1156
1157 /*
1158 * @unimplemented
1159 */
1160 BOOL
1161 WINAPI
1162 ReplaceFileW(
1163 LPCWSTR lpReplacedFileName,
1164 LPCWSTR lpReplacementFileName,
1165 LPCWSTR lpBackupFileName,
1166 DWORD dwReplaceFlags,
1167 LPVOID lpExclude,
1168 LPVOID lpReserved
1169 )
1170 {
1171 HANDLE hReplaced = NULL, hReplacement = NULL;
1172 UNICODE_STRING NtReplacedName = { 0, 0, NULL };
1173 UNICODE_STRING NtReplacementName = { 0, 0, NULL };
1174 DWORD Error = ERROR_SUCCESS;
1175 NTSTATUS Status;
1176 BOOL Ret = FALSE;
1177 IO_STATUS_BLOCK IoStatusBlock;
1178 OBJECT_ATTRIBUTES ObjectAttributes;
1179 PVOID Buffer = NULL ;
1180
1181 if (dwReplaceFlags)
1182 FIXME("Ignoring flags %x\n", dwReplaceFlags);
1183
1184 /* First two arguments are mandatory */
1185 if (!lpReplacedFileName || !lpReplacementFileName)
1186 {
1187 SetLastError(ERROR_INVALID_PARAMETER);
1188 return FALSE;
1189 }
1190
1191 /* Back it up */
1192 if(lpBackupFileName)
1193 {
1194 if(!CopyFileW(lpReplacedFileName, lpBackupFileName, FALSE))
1195 {
1196 Error = GetLastError();
1197 goto Cleanup ;
1198 }
1199 }
1200
1201 /* Open the "replaced" file for reading and writing */
1202 if (!(RtlDosPathNameToNtPathName_U(lpReplacedFileName, &NtReplacedName, NULL, NULL)))
1203 {
1204 Error = ERROR_PATH_NOT_FOUND;
1205 goto Cleanup;
1206 }
1207
1208 InitializeObjectAttributes(&ObjectAttributes,
1209 &NtReplacedName,
1210 OBJ_CASE_INSENSITIVE,
1211 NULL,
1212 NULL);
1213
1214 Status = NtOpenFile(&hReplaced,
1215 GENERIC_READ | GENERIC_WRITE | DELETE | SYNCHRONIZE | WRITE_DAC,
1216 &ObjectAttributes,
1217 &IoStatusBlock,
1218 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1219 FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE);
1220
1221 if (!NT_SUCCESS(Status))
1222 {
1223 if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
1224 Error = ERROR_FILE_NOT_FOUND;
1225 else
1226 Error = ERROR_UNABLE_TO_REMOVE_REPLACED;
1227 goto Cleanup;
1228 }
1229
1230 /* Blank it */
1231 SetEndOfFile(hReplaced) ;
1232
1233 /*
1234 * Open the replacement file for reading, writing, and deleting
1235 * (deleting is needed when finished)
1236 */
1237 if (!(RtlDosPathNameToNtPathName_U(lpReplacementFileName, &NtReplacementName, NULL, NULL)))
1238 {
1239 Error = ERROR_PATH_NOT_FOUND;
1240 goto Cleanup;
1241 }
1242
1243 InitializeObjectAttributes(&ObjectAttributes,
1244 &NtReplacementName,
1245 OBJ_CASE_INSENSITIVE,
1246 NULL,
1247 NULL);
1248
1249 Status = NtOpenFile(&hReplacement,
1250 GENERIC_READ | DELETE | SYNCHRONIZE,
1251 &ObjectAttributes,
1252 &IoStatusBlock,
1253 0,
1254 FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE | FILE_DELETE_ON_CLOSE);
1255
1256 if (!NT_SUCCESS(Status))
1257 {
1258 Error = RtlNtStatusToDosError(Status);
1259 goto Cleanup;
1260 }
1261
1262 Buffer = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, 0x10000) ;
1263 if (!Buffer)
1264 {
1265 Error = ERROR_NOT_ENOUGH_MEMORY;
1266 goto Cleanup ;
1267 }
1268 while (Status != STATUS_END_OF_FILE)
1269 {
1270 Status = NtReadFile(hReplacement, NULL, NULL, NULL, &IoStatusBlock, Buffer, 0x10000, NULL, NULL) ;
1271 if (NT_SUCCESS(Status))
1272 {
1273 Status = NtWriteFile(hReplaced, NULL, NULL, NULL, &IoStatusBlock, Buffer,
1274 IoStatusBlock.Information, NULL, NULL) ;
1275 if (!NT_SUCCESS(Status))
1276 {
1277 Error = RtlNtStatusToDosError(Status);
1278 goto Cleanup;
1279 }
1280 }
1281 else if (Status != STATUS_END_OF_FILE)
1282 {
1283 Error = RtlNtStatusToDosError(Status);
1284 goto Cleanup;
1285 }
1286 }
1287
1288 Ret = TRUE;
1289
1290 /* Perform resource cleanup */
1291 Cleanup:
1292 if (hReplaced) NtClose(hReplaced);
1293 if (hReplacement) NtClose(hReplacement);
1294 if (Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
1295
1296 if (NtReplacementName.Buffer)
1297 RtlFreeHeap(GetProcessHeap(), 0, NtReplacementName.Buffer);
1298 if (NtReplacedName.Buffer)
1299 RtlFreeHeap(GetProcessHeap(), 0, NtReplacedName.Buffer);
1300
1301 /* If there was an error, set the error code */
1302 if(!Ret)
1303 {
1304 TRACE("ReplaceFileW failed (error=%lu)\n", Error);
1305 SetLastError(Error);
1306 }
1307 return Ret;
1308 }
1309
1310
1311 /*
1312 * @implemented
1313 */
1314 BOOL
1315 WINAPI
1316 PrivMoveFileIdentityW(IN LPCWSTR lpSource, IN LPCWSTR lpDestination, IN DWORD dwFlags)
1317 {
1318 ACCESS_MASK SourceAccess;
1319 UNICODE_STRING NtSource, NtDestination;
1320 LPWSTR RelativeSource, RelativeDestination;
1321 HANDLE SourceHandle, DestinationHandle;
1322 OBJECT_ATTRIBUTES ObjectAttributesSource, ObjectAttributesDestination;
1323 NTSTATUS Status, OldStatus = STATUS_SUCCESS;
1324 ACCESS_MASK DestAccess;
1325 IO_STATUS_BLOCK IoStatusBlock;
1326 FILE_BASIC_INFORMATION SourceInformation, DestinationInformation;
1327 FILE_DISPOSITION_INFORMATION FileDispositionInfo;
1328
1329 DPRINT("PrivMoveFileIdentityW(%S, %S, %x)\n", lpSource, lpDestination, dwFlags);
1330
1331 SourceHandle = INVALID_HANDLE_VALUE;
1332 NtSource.Length =
1333 NtSource.MaximumLength = 0;
1334 NtSource.Buffer = NULL;
1335 RelativeSource = NULL;
1336 DestinationHandle = INVALID_HANDLE_VALUE;
1337 NtDestination.Length =
1338 NtDestination.MaximumLength = 0;
1339 NtDestination.Buffer = NULL;
1340 RelativeDestination = NULL;
1341
1342 /* FILE_WRITE_DATA is required for later on notification */
1343 SourceAccess = FILE_READ_ATTRIBUTES | FILE_WRITE_DATA;
1344 if (dwFlags & PRIV_DELETE_ON_SUCCESS)
1345 {
1346 SourceAccess |= DELETE;
1347 }
1348
1349 _SEH2_TRY
1350 {
1351 /* We will loop twice:
1352 * First we attempt to open with FILE_WRITE_DATA for notification
1353 * If it fails and we have flag for non-trackable files, we retry
1354 * without FILE_WRITE_DATA.
1355 * If that one fails, then, we quit for real
1356 */
1357 while (TRUE)
1358 {
1359 Status = BasepOpenFileForMove(lpSource,
1360 &NtSource,
1361 &RelativeSource,
1362 &SourceHandle,
1363 &ObjectAttributesSource,
1364 SourceAccess,
1365 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1366 FILE_OPEN_NO_RECALL);
1367 if (NT_SUCCESS(Status))
1368 {
1369 break;
1370 }
1371
1372 /* If we already attempted the opening without FILE_WRITE_DATA
1373 * or if we cannot move on non-trackable files, fail.
1374 */
1375 if (!(SourceAccess & FILE_WRITE_DATA) || !(dwFlags & PRIV_ALLOW_NON_TRACKABLE))
1376 {
1377 _SEH2_LEAVE;
1378 }
1379
1380 if (RelativeSource)
1381 {
1382 RtlFreeHeap(RtlGetProcessHeap(), 0, RelativeSource);
1383 RelativeSource = NULL;
1384 }
1385
1386 if (SourceHandle != INVALID_HANDLE_VALUE)
1387 {
1388 NtClose(SourceHandle);
1389 SourceHandle = INVALID_HANDLE_VALUE;
1390 }
1391
1392 SourceAccess &= ~FILE_WRITE_DATA;
1393
1394 /* Remember fist failure in the path */
1395 if (NT_SUCCESS(OldStatus))
1396 {
1397 OldStatus = Status;
1398 }
1399 }
1400
1401 DestAccess = FILE_WRITE_ATTRIBUTES;
1402 /* If we could preserve FILE_WRITE_DATA for source, attempt to get it for destination
1403 * Still for notification purposes
1404 */
1405 if (SourceAccess & FILE_WRITE_DATA)
1406 {
1407 DestAccess |= FILE_WRITE_DATA;
1408 }
1409
1410 /* cf comment for first loop */
1411 while (TRUE)
1412 {
1413 Status = BasepOpenFileForMove(lpDestination,
1414 &NtDestination,
1415 &RelativeDestination,
1416 &DestinationHandle,
1417 &ObjectAttributesDestination,
1418 DestAccess,
1419 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1420 FILE_OPEN_NO_RECALL);
1421 if (NT_SUCCESS(Status))
1422 {
1423 break;
1424 }
1425
1426 /* If we already attempted the opening without FILE_WRITE_DATA
1427 * or if we cannot move on non-trackable files, fail.
1428 */
1429 if (!(DestAccess & FILE_WRITE_DATA) || !(dwFlags & PRIV_ALLOW_NON_TRACKABLE))
1430 {
1431 _SEH2_LEAVE;
1432 }
1433
1434 if (RelativeDestination)
1435 {
1436 RtlFreeHeap(RtlGetProcessHeap(), 0, RelativeDestination);
1437 RelativeDestination = NULL;
1438 }
1439
1440 if (DestinationHandle != INVALID_HANDLE_VALUE)
1441 {
1442 NtClose(DestinationHandle);
1443 DestinationHandle = INVALID_HANDLE_VALUE;
1444 }
1445
1446 DestAccess &= ~FILE_WRITE_DATA;
1447
1448 /* Remember fist failure in the path */
1449 if (NT_SUCCESS(OldStatus))
1450 {
1451 OldStatus = Status;
1452 }
1453 }
1454
1455 /* Get the creation time from source */
1456 Status = NtQueryInformationFile(SourceHandle,
1457 &IoStatusBlock,
1458 &SourceInformation,
1459 sizeof(SourceInformation),
1460 FileBasicInformation);
1461 if (NT_SUCCESS(Status))
1462 {
1463 /* Then, prepare to set it for destination */
1464 RtlZeroMemory(&DestinationInformation, sizeof(DestinationInformation));
1465 DestinationInformation.CreationTime.QuadPart = SourceInformation.CreationTime.QuadPart;
1466
1467 /* And set it, that's all folks! */
1468 Status = NtSetInformationFile(DestinationHandle,
1469 &IoStatusBlock,
1470 &DestinationInformation,
1471 sizeof(DestinationInformation),
1472 FileBasicInformation);
1473 }
1474
1475 if (!NT_SUCCESS(Status))
1476 {
1477 if (!(dwFlags & PRIV_ALLOW_NON_TRACKABLE))
1478 {
1479 _SEH2_LEAVE;
1480 }
1481
1482 /* Remember the failure for later notification */
1483 if (NT_SUCCESS(OldStatus))
1484 {
1485 OldStatus = Status;
1486 }
1487 }
1488
1489 /* If we could open with FILE_WRITE_DATA both source and destination,
1490 * then, notify
1491 */
1492 if (DestAccess & FILE_WRITE_DATA && SourceAccess & FILE_WRITE_DATA)
1493 {
1494 Status = BasepNotifyTrackingService(&SourceHandle,
1495 &ObjectAttributesSource,
1496 DestinationHandle,
1497 &NtDestination);
1498 #if 1 // ReactOS HACK
1499 /* FIXME: To be removed once BasepNotifyTrackingService is implemented */
1500 if (Status == STATUS_NOT_IMPLEMENTED)
1501 Status = STATUS_SUCCESS;
1502 #endif
1503 if (!NT_SUCCESS(Status))
1504 {
1505 if (dwFlags & PRIV_ALLOW_NON_TRACKABLE)
1506 {
1507 if (NT_SUCCESS(OldStatus))
1508 OldStatus = Status;
1509 }
1510 }
1511 }
1512 }
1513 _SEH2_FINALLY
1514 {
1515 if (RelativeSource)
1516 RtlFreeHeap(RtlGetProcessHeap(), 0, RelativeSource);
1517
1518 if (RelativeDestination)
1519 RtlFreeHeap(RtlGetProcessHeap(), 0, RelativeDestination);
1520 }
1521 _SEH2_END;
1522
1523 /* If caller asked for source deletion, if everything succeed, proceed */
1524 if (NT_SUCCESS(Status) && dwFlags & PRIV_DELETE_ON_SUCCESS)
1525 {
1526 FileDispositionInfo.DeleteFile = TRUE;
1527
1528 Status = NtSetInformationFile(SourceHandle,
1529 &IoStatusBlock,
1530 &FileDispositionInfo,
1531 sizeof(FileDispositionInfo),
1532 FileDispositionInformation);
1533 }
1534
1535 /* Cleanup/close portion */
1536 if (DestinationHandle != INVALID_HANDLE_VALUE)
1537 {
1538 NtClose(DestinationHandle);
1539 }
1540
1541 if (SourceHandle != INVALID_HANDLE_VALUE)
1542 {
1543 NtClose(SourceHandle);
1544 }
1545
1546 /* Set last error if any, and quit */
1547 if (NT_SUCCESS(Status))
1548 {
1549 if (!NT_SUCCESS(OldStatus))
1550 {
1551 BaseSetLastNTError(OldStatus);
1552 }
1553 }
1554 else
1555 {
1556 BaseSetLastNTError(Status);
1557 }
1558
1559 return NT_SUCCESS(Status);
1560 }
1561
1562 /* EOF */