[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 * @unimplemented
182 */
183 NTSTATUS
184 WINAPI
185 BasepNotifyTrackingService(IN PHANDLE ExistingHandle,
186 IN POBJECT_ATTRIBUTES ObjectAttributes,
187 IN HANDLE NewHandle,
188 IN PUNICODE_STRING NewPath)
189 {
190 return STATUS_NOT_IMPLEMENTED;
191 }
192
193
194 /*
195 * @implemented
196 */
197 NTSTATUS
198 WINAPI
199 BasepOpenFileForMove(IN LPCWSTR File,
200 OUT PUNICODE_STRING RelativeNtName,
201 OUT LPWSTR * NtName,
202 OUT PHANDLE FileHandle,
203 OUT POBJECT_ATTRIBUTES ObjectAttributes,
204 IN ACCESS_MASK DesiredAccess,
205 IN ULONG ShareAccess,
206 IN ULONG OpenOptions)
207 {
208 RTL_RELATIVE_NAME_U RelativeName;
209 NTSTATUS Status;
210 IO_STATUS_BLOCK IoStatusBlock;
211 FILE_ATTRIBUTE_TAG_INFORMATION TagInfo;
212 ULONG IntShareAccess;
213 BOOLEAN HasRelative = FALSE;
214
215 _SEH2_TRY
216 {
217 /* Zero output */
218 RelativeNtName->Length =
219 RelativeNtName->MaximumLength = 0;
220 RelativeNtName->Buffer = NULL;
221 *NtName = NULL;
222
223 if (!RtlDosPathNameToRelativeNtPathName_U(File, RelativeNtName, NULL, &RelativeName))
224 {
225 Status = STATUS_OBJECT_PATH_NOT_FOUND;
226 _SEH2_LEAVE;
227 }
228
229 HasRelative = TRUE;
230 *NtName = RelativeNtName->Buffer;
231
232 if (RelativeName.RelativeName.Length)
233 {
234 RelativeNtName->Length = RelativeName.RelativeName.Length;
235 RelativeNtName->MaximumLength = RelativeName.RelativeName.MaximumLength;
236 RelativeNtName->Buffer = RelativeName.RelativeName.Buffer;
237 }
238 else
239 {
240 RelativeName.ContainingDirectory = 0;
241 }
242
243 InitializeObjectAttributes(ObjectAttributes,
244 RelativeNtName,
245 OBJ_CASE_INSENSITIVE,
246 RelativeName.ContainingDirectory,
247 NULL);
248 /* Force certain flags here, given ops we'll do */
249 IntShareAccess = ShareAccess | FILE_SHARE_READ | FILE_SHARE_WRITE;
250 OpenOptions |= FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT;
251
252 /* We'll try to read reparse tag */
253 Status = NtOpenFile(FileHandle,
254 DesiredAccess | FILE_READ_ATTRIBUTES | SYNCHRONIZE,
255 ObjectAttributes,
256 &IoStatusBlock,
257 IntShareAccess,
258 OpenOptions | FILE_OPEN_REPARSE_POINT);
259 if (NT_SUCCESS(Status))
260 {
261 /* Attempt the read */
262 Status = NtQueryInformationFile(*FileHandle,
263 &IoStatusBlock,
264 &TagInfo,
265 sizeof(FILE_ATTRIBUTE_TAG_INFORMATION),
266 FileAttributeTagInformation);
267
268 /* Return if failure with a status that wouldn't mean the FSD cannot support reparse points */
269 if (!NT_SUCCESS(Status) &&
270 (Status != STATUS_NOT_IMPLEMENTED && Status != STATUS_INVALID_PARAMETER))
271 {
272 _SEH2_LEAVE;
273 }
274
275 if (NT_SUCCESS(Status))
276 {
277 /* This cannot happen on mount points */
278 if (TagInfo.FileAttributes & FILE_ATTRIBUTE_DEVICE ||
279 TagInfo.ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
280 {
281 _SEH2_LEAVE;
282 }
283 }
284
285 NtClose(*FileHandle);
286 *FileHandle = INVALID_HANDLE_VALUE;
287
288 IntShareAccess = ShareAccess | FILE_SHARE_READ | FILE_SHARE_DELETE;
289 }
290 else if (Status == STATUS_INVALID_PARAMETER)
291 {
292 IntShareAccess = ShareAccess | FILE_SHARE_READ | FILE_SHARE_WRITE;
293 }
294 else
295 {
296 _SEH2_LEAVE;
297 }
298
299 /* Reattempt to open normally, following reparse point if needed */
300 Status = NtOpenFile(FileHandle,
301 DesiredAccess | SYNCHRONIZE,
302 ObjectAttributes,
303 &IoStatusBlock,
304 IntShareAccess,
305 OpenOptions);
306 }
307 _SEH2_FINALLY
308 {
309 if (HasRelative)
310 {
311 RtlReleaseRelativeName(&RelativeName);
312 }
313 }
314 _SEH2_END;
315
316 return Status;
317 }
318
319
320 /*
321 * @implemented
322 */
323 DWORD
324 WINAPI
325 BasepMoveFileCopyProgress(IN LARGE_INTEGER TotalFileSize,
326 IN LARGE_INTEGER TotalBytesTransferred,
327 IN LARGE_INTEGER StreamSize,
328 IN LARGE_INTEGER StreamBytesTransferred,
329 IN DWORD dwStreamNumber,
330 IN DWORD dwCallbackReason,
331 IN HANDLE hSourceFile,
332 IN HANDLE hDestinationFile,
333 IN LPVOID lpData OPTIONAL)
334 {
335 DWORD Ret = 0;
336 PCOPY_PROGRESS_CONTEXT Context = (PCOPY_PROGRESS_CONTEXT)lpData;
337
338 if (Context->Flags & MOVEFILE_WRITE_THROUGH)
339 {
340 if (!dwCallbackReason)
341 {
342 if (StreamBytesTransferred.QuadPart == StreamSize.QuadPart)
343 {
344 FlushFileBuffers(hDestinationFile);
345 }
346 }
347 }
348
349 if (Context->UserRoutine)
350 {
351 Ret = Context->UserRoutine(TotalFileSize,
352 TotalBytesTransferred,
353 StreamSize,
354 StreamBytesTransferred,
355 dwStreamNumber,
356 dwCallbackReason,
357 hSourceFile,
358 hDestinationFile,
359 Context->UserData);
360 }
361
362 return Ret;
363 }
364
365
366 /*
367 * @implemented
368 */
369 BOOL
370 WINAPI
371 MoveFileWithProgressW(IN LPCWSTR lpExistingFileName,
372 IN LPCWSTR lpNewFileName,
373 IN LPPROGRESS_ROUTINE lpProgressRoutine,
374 IN LPVOID lpData,
375 IN DWORD dwFlags)
376 {
377 NTSTATUS Status;
378 PWSTR NewBuffer;
379 IO_STATUS_BLOCK IoStatusBlock;
380 COPY_PROGRESS_CONTEXT CopyContext;
381 OBJECT_ATTRIBUTES ObjectAttributes;
382 PFILE_RENAME_INFORMATION RenameInfo;
383 UNICODE_STRING NewPathU, ExistingPathU;
384 FILE_ATTRIBUTE_TAG_INFORMATION FileAttrTagInfo;
385 HANDLE SourceHandle = INVALID_HANDLE_VALUE, NewHandle, ExistingHandle;
386 BOOL Ret = FALSE, ReplaceIfExists, DelayUntilReboot, AttemptReopenWithoutReparse;
387
388 DPRINT("MoveFileWithProgressW(%S, %S, %p, %p, %x)\n", lpExistingFileName, lpNewFileName, lpProgressRoutine, lpData, dwFlags);
389
390 NewPathU.Buffer = NULL;
391 ExistingPathU.Buffer = NULL;
392
393 _SEH2_TRY
394 {
395 /* Don't allow renaming to a disk */
396 if (lpNewFileName && RtlIsDosDeviceName_U(lpNewFileName))
397 {
398 BaseSetLastNTError(STATUS_OBJECT_NAME_COLLISION);
399 _SEH2_LEAVE;
400 }
401
402 ReplaceIfExists = !!(dwFlags & MOVEFILE_REPLACE_EXISTING);
403
404 /* Get file path */
405 if (!RtlDosPathNameToNtPathName_U(lpExistingFileName, &ExistingPathU, NULL, NULL))
406 {
407 BaseSetLastNTError(STATUS_OBJECT_PATH_NOT_FOUND);
408 _SEH2_LEAVE;
409 }
410
411 /* Sanitize input */
412 DelayUntilReboot = !!(dwFlags & MOVEFILE_DELAY_UNTIL_REBOOT);
413 if (DelayUntilReboot && (dwFlags & MOVEFILE_CREATE_HARDLINK))
414 {
415 BaseSetLastNTError(STATUS_INVALID_PARAMETER);
416 _SEH2_LEAVE;
417 }
418
419 /* Unless we manage a proper opening, we'll attempt to reopen without reparse support */
420 AttemptReopenWithoutReparse = TRUE;
421 InitializeObjectAttributes(&ObjectAttributes,
422 &ExistingPathU,
423 OBJ_CASE_INSENSITIVE,
424 NULL,
425 NULL);
426 /* Attempt to open source file */
427 Status = NtOpenFile(&SourceHandle,
428 FILE_READ_ATTRIBUTES | DELETE | SYNCHRONIZE,
429 &ObjectAttributes,
430 &IoStatusBlock,
431 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
432 FILE_OPEN_FOR_BACKUP_INTENT | ((dwFlags & MOVEFILE_WRITE_THROUGH) ? FILE_WRITE_THROUGH : 0));
433 if (!NT_SUCCESS(Status))
434 {
435 /* If we failed and the file doesn't exist, don't attempt to reopen without reparse */
436 if (DelayUntilReboot &&
437 (Status == STATUS_SHARING_VIOLATION || Status == STATUS_OBJECT_NAME_NOT_FOUND || Status == STATUS_OBJECT_PATH_NOT_FOUND))
438 {
439 /* Here we don't fail completely, as we postpone the operation to reboot
440 * File might exist afterwards, and we don't need a handle here
441 */
442 SourceHandle = INVALID_HANDLE_VALUE;
443 AttemptReopenWithoutReparse = FALSE;
444 }
445 /* If we failed for any reason than unsupported reparse, fail completely */
446 else if (Status != STATUS_INVALID_PARAMETER)
447 {
448 BaseSetLastNTError(Status);
449 _SEH2_LEAVE;
450 }
451 }
452 else
453 {
454 /* We managed to open, so query information */
455 Status = NtQueryInformationFile(SourceHandle,
456 &IoStatusBlock,
457 &FileAttrTagInfo,
458 sizeof(FILE_ATTRIBUTE_TAG_INFORMATION),
459 FileAttributeTagInformation);
460 if (!NT_SUCCESS(Status))
461 {
462 /* Do not tolerate any other error than something related to not supported operation */
463 if (Status != STATUS_NOT_IMPLEMENTED && Status != STATUS_INVALID_PARAMETER)
464 {
465 BaseSetLastNTError(Status);
466 _SEH2_LEAVE;
467 }
468
469 /* Not a reparse point, no need to reopen, it's fine */
470 AttemptReopenWithoutReparse = FALSE;
471 }
472 /* Validate the reparse point (do we support it?) */
473 else if (FileAttrTagInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT &&
474 FileAttrTagInfo.ReparseTag != IO_REPARSE_TAG_MOUNT_POINT)
475 {
476 NtClose(SourceHandle);
477 SourceHandle = INVALID_HANDLE_VALUE;
478 }
479 }
480
481 /* Simply reopen if required */
482 if (AttemptReopenWithoutReparse)
483 {
484 Status = NtOpenFile(&SourceHandle,
485 DELETE | SYNCHRONIZE,
486 &ObjectAttributes,
487 &IoStatusBlock,
488 FILE_SHARE_READ | FILE_SHARE_WRITE,
489 ((dwFlags & MOVEFILE_WRITE_THROUGH) ? FILE_WRITE_THROUGH : 0));
490 if (!NT_SUCCESS(Status))
491 {
492 BaseSetLastNTError(Status);
493 _SEH2_LEAVE;
494 }
495 }
496
497 /* Nullify string if we're to use it */
498 if (DelayUntilReboot && !lpNewFileName)
499 {
500 RtlInitUnicodeString(&NewPathU, 0);
501 }
502 /* Check whether path exists */
503 else if (!RtlDosPathNameToNtPathName_U(lpNewFileName, &NewPathU, 0, 0))
504 {
505 BaseSetLastNTError(STATUS_OBJECT_PATH_NOT_FOUND);
506 _SEH2_LEAVE;
507 }
508
509 /* Handle postponed renaming */
510 if (DelayUntilReboot)
511 {
512 /* If new file exists and we're allowed to replace, then mark the path with ! */
513 if (ReplaceIfExists && NewPathU.Length)
514 {
515 NewBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, NewPathU.Length + sizeof(WCHAR));
516 if (NewBuffer == NULL)
517 {
518 BaseSetLastNTError(STATUS_NO_MEMORY);
519 _SEH2_LEAVE;
520 }
521
522 NewBuffer[0] = L'!';
523 RtlCopyMemory(&NewBuffer[1], NewPathU.Buffer, NewPathU.Length);
524 NewPathU.Length += sizeof(WCHAR);
525 NewPathU.MaximumLength += sizeof(WCHAR);
526 RtlFreeHeap(RtlGetProcessHeap(), 0, NewPathU.Buffer);
527 NewPathU.Buffer = NewBuffer;
528 }
529
530 /* Check whether 'copy' renaming is allowed if required */
531 if (RtlDetermineDosPathNameType_U(lpExistingFileName) == RtlPathTypeUncAbsolute || dwFlags & MOVEFILE_COPY_ALLOWED)
532 {
533 Status = STATUS_INVALID_PARAMETER;
534 }
535 else
536 {
537 /* First, probe 2nd key to see whether it exists - if so, it will be appended there */
538 Status = BasepMoveFileDelayed(&ExistingPathU, &NewPathU, 2, FALSE);
539 if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
540 {
541 /* If doesn't exist, append to first key first, creating it if it doesn't exist */
542 Status = BasepMoveFileDelayed(&ExistingPathU, &NewPathU, 1, TRUE);
543
544 if (Status == STATUS_INSUFFICIENT_RESOURCES)
545 {
546 /* If it failed because it's too big, then create 2nd key and put it there */
547 Status = BasepMoveFileDelayed(&ExistingPathU, &NewPathU, 2, TRUE);
548 }
549 }
550 }
551
552 /* If we failed at some point, return the error */
553 if (!NT_SUCCESS(Status))
554 {
555 BaseSetLastNTError(Status);
556 _SEH2_LEAVE;
557 }
558
559 Ret = TRUE;
560 _SEH2_LEAVE;
561 }
562
563 /* At that point, we MUST have a source handle */
564 ASSERT(SourceHandle != INVALID_HANDLE_VALUE);
565
566 /* Allocate renaming buffer and fill it */
567 RenameInfo = RtlAllocateHeap(RtlGetProcessHeap(), 0, NewPathU.Length + sizeof(FILE_RENAME_INFORMATION));
568 if (RenameInfo == NULL)
569 {
570 BaseSetLastNTError(STATUS_NO_MEMORY);
571 _SEH2_LEAVE;
572 }
573
574 RtlCopyMemory(&RenameInfo->FileName, NewPathU.Buffer, NewPathU.Length);
575 RenameInfo->ReplaceIfExists = ReplaceIfExists;
576 RenameInfo->RootDirectory = 0;
577 RenameInfo->FileNameLength = NewPathU.Length;
578
579 /* Attempt to rename the file */
580 Status = NtSetInformationFile(SourceHandle,
581 &IoStatusBlock,
582 RenameInfo,
583 NewPathU.Length + sizeof(FILE_RENAME_INFORMATION),
584 ((dwFlags & MOVEFILE_CREATE_HARDLINK) ? FileLinkInformation : FileRenameInformation));
585 RtlFreeHeap(RtlGetProcessHeap(), 0, RenameInfo);
586 if (NT_SUCCESS(Status))
587 {
588 /* If it succeed, all fine, quit */
589 Ret = TRUE;
590 _SEH2_LEAVE;
591 }
592 /* If we failed for any other reason than not the same device, fail
593 * If we failed because of different devices, only allow renaming if user allowed copy
594 */
595 if (Status != STATUS_NOT_SAME_DEVICE || !(dwFlags & MOVEFILE_COPY_ALLOWED))
596 {
597 /* ReactOS hack! To be removed once all FSD have proper renaming support
598 * Just leave status to error and leave
599 */
600 if (Status == STATUS_NOT_IMPLEMENTED)
601 {
602 DPRINT1("Forcing copy, renaming not supported by FSD\n");
603 }
604 else
605 {
606 BaseSetLastNTError(Status);
607 _SEH2_LEAVE;
608 }
609 }
610
611 /* Close source file */
612 NtClose(SourceHandle);
613 SourceHandle = INVALID_HANDLE_VALUE;
614
615 /* Issue the copy of the file */
616 CopyContext.Flags = dwFlags;
617 CopyContext.UserRoutine = lpProgressRoutine;
618 CopyContext.UserData = lpData;
619 NewHandle = INVALID_HANDLE_VALUE;
620 ExistingHandle = INVALID_HANDLE_VALUE;
621
622 Ret = BasepCopyFileExW(lpExistingFileName,
623 lpNewFileName,
624 BasepMoveFileCopyProgress,
625 &CopyContext,
626 NULL,
627 (ReplaceIfExists == 0) | COPY_FILE_OPEN_SOURCE_FOR_WRITE,
628 0,
629 &ExistingHandle,
630 &NewHandle);
631 if (!Ret)
632 {
633 /* If it failed, don't leak any handle */
634 if (ExistingHandle != INVALID_HANDLE_VALUE)
635 {
636 CloseHandle(ExistingHandle);
637 ExistingHandle = INVALID_HANDLE_VALUE;
638 }
639 }
640 else if (ExistingHandle != INVALID_HANDLE_VALUE)
641 {
642 if (NewHandle != INVALID_HANDLE_VALUE)
643 {
644 /* If copying succeed, notify */
645 Status = BasepNotifyTrackingService(&ExistingHandle, &ObjectAttributes, NewHandle, &NewPathU);
646 if (!NT_SUCCESS(Status))
647 {
648 /* Fail in case it had to succeed */
649 if (dwFlags & MOVEFILE_FAIL_IF_NOT_TRACKABLE)
650 {
651 if (NewHandle != INVALID_HANDLE_VALUE)
652 CloseHandle(NewHandle);
653 NewHandle = INVALID_HANDLE_VALUE;
654 DeleteFileW(lpNewFileName);
655 Ret = FALSE;
656 BaseSetLastNTError(Status);
657 }
658 }
659 }
660
661 CloseHandle(ExistingHandle);
662 ExistingHandle = INVALID_HANDLE_VALUE;
663 }
664
665 /* In case copy worked, close file */
666 if (NewHandle != INVALID_HANDLE_VALUE)
667 {
668 CloseHandle(NewHandle);
669 NewHandle = INVALID_HANDLE_VALUE;
670 }
671
672 /* If it succeed, delete source file */
673 if (Ret)
674 {
675 if (!DeleteFileW(lpExistingFileName))
676 {
677 /* Reset file attributes if required */
678 SetFileAttributesW(lpExistingFileName, FILE_ATTRIBUTE_NORMAL);
679 DeleteFileW(lpExistingFileName);
680 }
681 }
682 }
683 _SEH2_FINALLY
684 {
685 if (SourceHandle != INVALID_HANDLE_VALUE)
686 NtClose(SourceHandle);
687
688 RtlFreeHeap(RtlGetProcessHeap(), 0, ExistingPathU.Buffer);
689 RtlFreeHeap(RtlGetProcessHeap(), 0, NewPathU.Buffer);
690 }
691 _SEH2_END;
692
693 return Ret;
694 }
695
696
697 /*
698 * @implemented
699 */
700 BOOL
701 WINAPI
702 MoveFileWithProgressA(IN LPCSTR lpExistingFileName,
703 IN LPCSTR lpNewFileName OPTIONAL,
704 IN LPPROGRESS_ROUTINE lpProgressRoutine OPTIONAL,
705 IN LPVOID lpData OPTIONAL,
706 IN DWORD dwFlags)
707 {
708 BOOL Ret;
709 UNICODE_STRING ExistingFileNameW, NewFileNameW;
710
711 if (!Basep8BitStringToDynamicUnicodeString(&ExistingFileNameW, lpExistingFileName))
712 {
713 return FALSE;
714 }
715
716 if (lpNewFileName)
717 {
718 if (!Basep8BitStringToDynamicUnicodeString(&NewFileNameW, lpNewFileName))
719 {
720 RtlFreeUnicodeString(&ExistingFileNameW);
721 return FALSE;
722 }
723 }
724 else
725 {
726 NewFileNameW.Buffer = NULL;
727 }
728
729 Ret = MoveFileWithProgressW(ExistingFileNameW.Buffer, NewFileNameW.Buffer, lpProgressRoutine, lpData, dwFlags);
730
731 RtlFreeUnicodeString(&ExistingFileNameW);
732 RtlFreeUnicodeString(&NewFileNameW);
733
734 return Ret;
735 }
736
737
738 /*
739 * @implemented
740 */
741 BOOL
742 WINAPI
743 MoveFileW(IN LPCWSTR lpExistingFileName,
744 IN LPCWSTR lpNewFileName)
745 {
746 return MoveFileWithProgressW(lpExistingFileName,
747 lpNewFileName,
748 NULL,
749 NULL,
750 MOVEFILE_COPY_ALLOWED);
751 }
752
753
754 /*
755 * @implemented
756 */
757 BOOL
758 WINAPI
759 MoveFileExW(IN LPCWSTR lpExistingFileName,
760 IN LPCWSTR lpNewFileName OPTIONAL,
761 IN DWORD dwFlags)
762 {
763 return MoveFileWithProgressW(lpExistingFileName,
764 lpNewFileName,
765 NULL,
766 NULL,
767 dwFlags);
768 }
769
770
771 /*
772 * @implemented
773 */
774 BOOL
775 WINAPI
776 MoveFileA(IN LPCSTR lpExistingFileName,
777 IN LPCSTR lpNewFileName)
778 {
779 return MoveFileWithProgressA(lpExistingFileName,
780 lpNewFileName,
781 NULL,
782 NULL,
783 MOVEFILE_COPY_ALLOWED);
784 }
785
786
787 /*
788 * @implemented
789 */
790 BOOL
791 WINAPI
792 MoveFileExA(IN LPCSTR lpExistingFileName,
793 IN LPCSTR lpNewFileName OPTIONAL,
794 IN DWORD dwFlags)
795 {
796 return MoveFileWithProgressA(lpExistingFileName,
797 lpNewFileName,
798 NULL,
799 NULL,
800 dwFlags);
801 }
802
803 /*
804 * @implemented
805 */
806 BOOL
807 WINAPI
808 ReplaceFileA(IN LPCSTR lpReplacedFileName,
809 IN LPCSTR lpReplacementFileName,
810 IN LPCSTR lpBackupFileName OPTIONAL,
811 IN DWORD dwReplaceFlags,
812 IN LPVOID lpExclude,
813 IN LPVOID lpReserved)
814 {
815 BOOL Ret;
816 UNICODE_STRING ReplacedFileNameW, ReplacementFileNameW, BackupFileNameW;
817
818 if (!lpReplacedFileName || !lpReplacementFileName || lpExclude || lpReserved || dwReplaceFlags & ~(REPLACEFILE_WRITE_THROUGH | REPLACEFILE_IGNORE_MERGE_ERRORS))
819 {
820 SetLastError(ERROR_INVALID_PARAMETER);
821 return FALSE;
822 }
823
824 if (!Basep8BitStringToDynamicUnicodeString(&ReplacedFileNameW, lpReplacedFileName))
825 {
826 return FALSE;
827 }
828
829 if (!Basep8BitStringToDynamicUnicodeString(&ReplacementFileNameW, lpReplacementFileName))
830 {
831 RtlFreeUnicodeString(&ReplacedFileNameW);
832 return FALSE;
833 }
834
835 if (lpBackupFileName)
836 {
837 if (!Basep8BitStringToDynamicUnicodeString(&BackupFileNameW, lpBackupFileName))
838 {
839 RtlFreeUnicodeString(&ReplacementFileNameW);
840 RtlFreeUnicodeString(&ReplacedFileNameW);
841 return FALSE;
842 }
843 }
844 else
845 {
846 BackupFileNameW.Buffer = NULL;
847 }
848
849 Ret = ReplaceFileW(ReplacedFileNameW.Buffer, ReplacementFileNameW.Buffer, BackupFileNameW.Buffer, dwReplaceFlags, 0, 0);
850
851 if (lpBackupFileName)
852 {
853 RtlFreeUnicodeString(&BackupFileNameW);
854 }
855 RtlFreeUnicodeString(&ReplacementFileNameW);
856 RtlFreeUnicodeString(&ReplacedFileNameW);
857
858 return Ret;
859 }
860
861 /*
862 * @unimplemented
863 */
864 BOOL
865 WINAPI
866 ReplaceFileW(
867 LPCWSTR lpReplacedFileName,
868 LPCWSTR lpReplacementFileName,
869 LPCWSTR lpBackupFileName,
870 DWORD dwReplaceFlags,
871 LPVOID lpExclude,
872 LPVOID lpReserved
873 )
874 {
875 HANDLE hReplaced = NULL, hReplacement = NULL;
876 UNICODE_STRING NtReplacedName = { 0, 0, NULL };
877 UNICODE_STRING NtReplacementName = { 0, 0, NULL };
878 DWORD Error = ERROR_SUCCESS;
879 NTSTATUS Status;
880 BOOL Ret = FALSE;
881 IO_STATUS_BLOCK IoStatusBlock;
882 OBJECT_ATTRIBUTES ObjectAttributes;
883 PVOID Buffer = NULL ;
884
885 if (dwReplaceFlags)
886 FIXME("Ignoring flags %x\n", dwReplaceFlags);
887
888 /* First two arguments are mandatory */
889 if (!lpReplacedFileName || !lpReplacementFileName)
890 {
891 SetLastError(ERROR_INVALID_PARAMETER);
892 return FALSE;
893 }
894
895 /* Back it up */
896 if(lpBackupFileName)
897 {
898 if(!CopyFileW(lpReplacedFileName, lpBackupFileName, FALSE))
899 {
900 Error = GetLastError();
901 goto Cleanup ;
902 }
903 }
904
905 /* Open the "replaced" file for reading and writing */
906 if (!(RtlDosPathNameToNtPathName_U(lpReplacedFileName, &NtReplacedName, NULL, NULL)))
907 {
908 Error = ERROR_PATH_NOT_FOUND;
909 goto Cleanup;
910 }
911
912 InitializeObjectAttributes(&ObjectAttributes,
913 &NtReplacedName,
914 OBJ_CASE_INSENSITIVE,
915 NULL,
916 NULL);
917
918 Status = NtOpenFile(&hReplaced,
919 GENERIC_READ | GENERIC_WRITE | DELETE | SYNCHRONIZE | WRITE_DAC,
920 &ObjectAttributes,
921 &IoStatusBlock,
922 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
923 FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE);
924
925 if (!NT_SUCCESS(Status))
926 {
927 if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
928 Error = ERROR_FILE_NOT_FOUND;
929 else
930 Error = ERROR_UNABLE_TO_REMOVE_REPLACED;
931 goto Cleanup;
932 }
933
934 /* Blank it */
935 SetEndOfFile(hReplaced) ;
936
937 /*
938 * Open the replacement file for reading, writing, and deleting
939 * (deleting is needed when finished)
940 */
941 if (!(RtlDosPathNameToNtPathName_U(lpReplacementFileName, &NtReplacementName, NULL, NULL)))
942 {
943 Error = ERROR_PATH_NOT_FOUND;
944 goto Cleanup;
945 }
946
947 InitializeObjectAttributes(&ObjectAttributes,
948 &NtReplacementName,
949 OBJ_CASE_INSENSITIVE,
950 NULL,
951 NULL);
952
953 Status = NtOpenFile(&hReplacement,
954 GENERIC_READ | DELETE | SYNCHRONIZE,
955 &ObjectAttributes,
956 &IoStatusBlock,
957 0,
958 FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE | FILE_DELETE_ON_CLOSE);
959
960 if (!NT_SUCCESS(Status))
961 {
962 Error = RtlNtStatusToDosError(Status);
963 goto Cleanup;
964 }
965
966 Buffer = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, 0x10000) ;
967 if (!Buffer)
968 {
969 Error = ERROR_NOT_ENOUGH_MEMORY;
970 goto Cleanup ;
971 }
972 while (Status != STATUS_END_OF_FILE)
973 {
974 Status = NtReadFile(hReplacement, NULL, NULL, NULL, &IoStatusBlock, Buffer, 0x10000, NULL, NULL) ;
975 if (NT_SUCCESS(Status))
976 {
977 Status = NtWriteFile(hReplaced, NULL, NULL, NULL, &IoStatusBlock, Buffer,
978 IoStatusBlock.Information, NULL, NULL) ;
979 if (!NT_SUCCESS(Status))
980 {
981 Error = RtlNtStatusToDosError(Status);
982 goto Cleanup;
983 }
984 }
985 else if (Status != STATUS_END_OF_FILE)
986 {
987 Error = RtlNtStatusToDosError(Status);
988 goto Cleanup;
989 }
990 }
991
992 Ret = TRUE;
993
994 /* Perform resource cleanup */
995 Cleanup:
996 if (hReplaced) NtClose(hReplaced);
997 if (hReplacement) NtClose(hReplacement);
998 if (Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
999
1000 if (NtReplacementName.Buffer)
1001 RtlFreeHeap(GetProcessHeap(), 0, NtReplacementName.Buffer);
1002 if (NtReplacedName.Buffer)
1003 RtlFreeHeap(GetProcessHeap(), 0, NtReplacedName.Buffer);
1004
1005 /* If there was an error, set the error code */
1006 if(!Ret)
1007 {
1008 TRACE("ReplaceFileW failed (error=%lu)\n", Error);
1009 SetLastError(Error);
1010 }
1011 return Ret;
1012 }
1013
1014
1015 /*
1016 * @implemented
1017 */
1018 BOOL
1019 WINAPI
1020 PrivMoveFileIdentityW(IN LPCWSTR lpSource, IN LPCWSTR lpDestination, IN DWORD dwFlags)
1021 {
1022 ACCESS_MASK SourceAccess;
1023 UNICODE_STRING NtSource, NtDestination;
1024 LPWSTR RelativeSource, RelativeDestination;
1025 HANDLE SourceHandle, DestinationHandle;
1026 OBJECT_ATTRIBUTES ObjectAttributesSource, ObjectAttributesDestination;
1027 NTSTATUS Status, OldStatus = STATUS_SUCCESS;
1028 ACCESS_MASK DestAccess;
1029 IO_STATUS_BLOCK IoStatusBlock;
1030 FILE_BASIC_INFORMATION SourceInformation, DestinationInformation;
1031 FILE_DISPOSITION_INFORMATION FileDispositionInfo;
1032
1033 DPRINT("PrivMoveFileIdentityW(%S, %S, %x)\n", lpSource, lpDestination, dwFlags);
1034
1035 SourceHandle = INVALID_HANDLE_VALUE;
1036 NtSource.Length =
1037 NtSource.MaximumLength = 0;
1038 NtSource.Buffer = NULL;
1039 RelativeSource = NULL;
1040 DestinationHandle = INVALID_HANDLE_VALUE;
1041 NtDestination.Length =
1042 NtDestination.MaximumLength = 0;
1043 NtDestination.Buffer = NULL;
1044 RelativeDestination = NULL;
1045
1046 /* FILE_WRITE_DATA is required for later on notification */
1047 SourceAccess = FILE_READ_ATTRIBUTES | FILE_WRITE_DATA;
1048 if (dwFlags & PRIV_DELETE_ON_SUCCESS)
1049 {
1050 SourceAccess |= DELETE;
1051 }
1052
1053 _SEH2_TRY
1054 {
1055 /* We will loop twice:
1056 * First we attempt to open with FILE_WRITE_DATA for notification
1057 * If it fails and we have flag for non-trackable files, we retry
1058 * without FILE_WRITE_DATA.
1059 * If that one fails, then, we quit for real
1060 */
1061 while (TRUE)
1062 {
1063 Status = BasepOpenFileForMove(lpSource,
1064 &NtSource,
1065 &RelativeSource,
1066 &SourceHandle,
1067 &ObjectAttributesSource,
1068 SourceAccess,
1069 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1070 FILE_OPEN_NO_RECALL);
1071 if (NT_SUCCESS(Status))
1072 {
1073 break;
1074 }
1075
1076 /* If we already attempted the opening without FILE_WRITE_DATA
1077 * or if we cannot move on non-trackable files, fail.
1078 */
1079 if (!(SourceAccess & FILE_WRITE_DATA) || !(dwFlags & PRIV_ALLOW_NON_TRACKABLE))
1080 {
1081 _SEH2_LEAVE;
1082 }
1083
1084 if (RelativeSource)
1085 {
1086 RtlFreeHeap(RtlGetProcessHeap(), 0, RelativeSource);
1087 RelativeSource = NULL;
1088 }
1089
1090 if (SourceHandle != INVALID_HANDLE_VALUE)
1091 {
1092 NtClose(SourceHandle);
1093 SourceHandle = INVALID_HANDLE_VALUE;
1094 }
1095
1096 SourceAccess &= ~FILE_WRITE_DATA;
1097
1098 /* Remember fist failure in the path */
1099 if (NT_SUCCESS(OldStatus))
1100 {
1101 OldStatus = Status;
1102 }
1103 }
1104
1105 DestAccess = FILE_WRITE_ATTRIBUTES;
1106 /* If we could preserve FILE_WRITE_DATA for source, attempt to get it for destination
1107 * Still for notification purposes
1108 */
1109 if (SourceAccess & FILE_WRITE_DATA)
1110 {
1111 DestAccess |= FILE_WRITE_DATA;
1112 }
1113
1114 /* cf comment for first loop */
1115 while (TRUE)
1116 {
1117 Status = BasepOpenFileForMove(lpDestination,
1118 &NtDestination,
1119 &RelativeDestination,
1120 &DestinationHandle,
1121 &ObjectAttributesDestination,
1122 DestAccess,
1123 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1124 FILE_OPEN_NO_RECALL);
1125 if (NT_SUCCESS(Status))
1126 {
1127 break;
1128 }
1129
1130 /* If we already attempted the opening without FILE_WRITE_DATA
1131 * or if we cannot move on non-trackable files, fail.
1132 */
1133 if (!(DestAccess & FILE_WRITE_DATA) || !(dwFlags & PRIV_ALLOW_NON_TRACKABLE))
1134 {
1135 _SEH2_LEAVE;
1136 }
1137
1138 if (RelativeDestination)
1139 {
1140 RtlFreeHeap(RtlGetProcessHeap(), 0, RelativeDestination);
1141 RelativeDestination = NULL;
1142 }
1143
1144 if (DestinationHandle != INVALID_HANDLE_VALUE)
1145 {
1146 NtClose(DestinationHandle);
1147 DestinationHandle = INVALID_HANDLE_VALUE;
1148 }
1149
1150 DestAccess &= ~FILE_WRITE_DATA;
1151
1152 /* Remember fist failure in the path */
1153 if (NT_SUCCESS(OldStatus))
1154 {
1155 OldStatus = Status;
1156 }
1157 }
1158
1159 /* Get the creation time from source */
1160 Status = NtQueryInformationFile(SourceHandle,
1161 &IoStatusBlock,
1162 &SourceInformation,
1163 sizeof(SourceInformation),
1164 FileBasicInformation);
1165 if (NT_SUCCESS(Status))
1166 {
1167 /* Then, prepare to set it for destination */
1168 RtlZeroMemory(&DestinationInformation, sizeof(DestinationInformation));
1169 DestinationInformation.CreationTime.QuadPart = SourceInformation.CreationTime.QuadPart;
1170
1171 /* And set it, that's all folks! */
1172 Status = NtSetInformationFile(DestinationHandle,
1173 &IoStatusBlock,
1174 &DestinationInformation,
1175 sizeof(DestinationInformation),
1176 FileBasicInformation);
1177 }
1178
1179 if (!NT_SUCCESS(Status))
1180 {
1181 if (!(dwFlags & PRIV_ALLOW_NON_TRACKABLE))
1182 {
1183 _SEH2_LEAVE;
1184 }
1185
1186 /* Remember the failure for later notification */
1187 if (NT_SUCCESS(OldStatus))
1188 {
1189 OldStatus = Status;
1190 }
1191 }
1192
1193 /* If we could open with FILE_WRITE_DATA both source and destination,
1194 * then, notify
1195 */
1196 if (DestAccess & FILE_WRITE_DATA && SourceAccess & FILE_WRITE_DATA)
1197 {
1198 Status = BasepNotifyTrackingService(&SourceHandle,
1199 &ObjectAttributesSource,
1200 DestinationHandle,
1201 &NtDestination);
1202 #if 1 // ReactOS HACK
1203 /* FIXME: To be removed once BasepNotifyTrackingService is implemented */
1204 if (Status == STATUS_NOT_IMPLEMENTED)
1205 Status = STATUS_SUCCESS;
1206 #endif
1207 if (!NT_SUCCESS(Status))
1208 {
1209 if (dwFlags & PRIV_ALLOW_NON_TRACKABLE)
1210 {
1211 if (NT_SUCCESS(OldStatus))
1212 OldStatus = Status;
1213 }
1214 }
1215 }
1216 }
1217 _SEH2_FINALLY
1218 {
1219 if (RelativeSource)
1220 RtlFreeHeap(RtlGetProcessHeap(), 0, RelativeSource);
1221
1222 if (RelativeDestination)
1223 RtlFreeHeap(RtlGetProcessHeap(), 0, RelativeDestination);
1224 }
1225 _SEH2_END;
1226
1227 /* If caller asked for source deletion, if everything succeed, proceed */
1228 if (NT_SUCCESS(Status) && dwFlags & PRIV_DELETE_ON_SUCCESS)
1229 {
1230 FileDispositionInfo.DeleteFile = TRUE;
1231
1232 Status = NtSetInformationFile(SourceHandle,
1233 &IoStatusBlock,
1234 &FileDispositionInfo,
1235 sizeof(FileDispositionInfo),
1236 FileDispositionInformation);
1237 }
1238
1239 /* Cleanup/close portion */
1240 if (DestinationHandle != INVALID_HANDLE_VALUE)
1241 {
1242 NtClose(DestinationHandle);
1243 }
1244
1245 if (SourceHandle != INVALID_HANDLE_VALUE)
1246 {
1247 NtClose(SourceHandle);
1248 }
1249
1250 /* Set last error if any, and quit */
1251 if (NT_SUCCESS(Status))
1252 {
1253 if (!NT_SUCCESS(OldStatus))
1254 {
1255 BaseSetLastNTError(OldStatus);
1256 }
1257 }
1258 else
1259 {
1260 BaseSetLastNTError(Status);
1261 }
1262
1263 return NT_SUCCESS(Status);
1264 }
1265
1266 /* EOF */