Partial merge of condrv_restructure branch r65657.
[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(WCHAR);
96 /* Check we didn't overflow */
97 if (BufferLength < STRING_LENGTH)
98 {
99 NtClose(KeyHandle);
100 return STATUS_BUFFER_TOO_SMALL;
101 }
102
103 while (TRUE)
104 {
105 /* Allocate output buffer */
106 Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferLength);
107 if (Buffer == NULL)
108 {
109 NtClose(KeyHandle);
110 return STATUS_NO_MEMORY;
111 }
112
113 Status = NtQueryValueKey(KeyHandle,
114 &PendingOperationsString,
115 KeyValuePartialInformation,
116 Buffer, StringLength, &DataSize);
117 if (Status != STATUS_BUFFER_OVERFLOW)
118 {
119 break;
120 }
121
122 /* If buffer was too small, then, reallocate one which is big enough */
123 StringLength = DataSize;
124 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
125 BufferLength = ExistingPath->Length + StringLength + NewPath->Length + 3 * sizeof(WCHAR);
126 if (BufferLength < StringLength)
127 {
128 NtClose(KeyHandle);
129 return STATUS_BUFFER_TOO_SMALL;
130 }
131 }
132
133 /* Check if it existed - if not, create only IF asked to */
134 if (!NT_SUCCESS(Status) && (Status != STATUS_OBJECT_NAME_NOT_FOUND || !CreateIfNotFound))
135 {
136 NtClose(KeyHandle);
137 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
138 return Status;
139 }
140
141 if (!NT_SUCCESS(Status))
142 {
143 /* We didn't find any - ie, we create, so use complete buffer */
144 BufferBegin = Buffer;
145 BufferWrite = Buffer;
146 }
147 else
148 {
149 PKEY_VALUE_PARTIAL_INFORMATION PartialInfo = (PKEY_VALUE_PARTIAL_INFORMATION)Buffer;
150
151 /* Get data, our buffer begin and then where we should append data (+ null char) */
152 BufferBegin = PartialInfo->Data;
153 BufferWrite = (PWSTR)((ULONG_PTR)PartialInfo->Data + PartialInfo->DataLength + sizeof(WCHAR));
154 }
155
156 /* First copy existing */
157 RtlCopyMemory(BufferWrite, ExistingPath->Buffer, ExistingPath->Length);
158 BufferWrite += ExistingPath->Length / sizeof(WCHAR);
159 /* And append null char */
160 *BufferWrite = UNICODE_NULL;
161 ++BufferWrite;
162 /* Append destination */
163 RtlCopyMemory(BufferWrite, NewPath->Buffer, NewPath->Length);
164 BufferWrite += NewPath->Length / sizeof(WCHAR);
165 /* And append two null char (end of string) */
166 *BufferWrite = UNICODE_NULL;
167 ++BufferWrite;
168 *BufferWrite = UNICODE_NULL;
169
170 /* Set new value */
171 Status = NtSetValueKey(KeyHandle,
172 &PendingOperationsString,
173 0, REG_MULTI_SZ, BufferBegin,
174 (ULONG_PTR)BufferWrite - (ULONG_PTR)BufferBegin + sizeof(WCHAR));
175
176 NtClose(KeyHandle);
177 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
178
179 return Status;
180 }
181
182
183 /*
184 * @unimplemented
185 */
186 NTSTATUS
187 WINAPI
188 BasepNotifyTrackingService(IN PHANDLE ExistingHandle,
189 IN POBJECT_ATTRIBUTES ObjectAttributes,
190 IN HANDLE NewHandle,
191 IN PUNICODE_STRING NewPath)
192 {
193 return STATUS_NOT_IMPLEMENTED;
194 }
195
196
197 /*
198 * @implemented
199 */
200 DWORD
201 WINAPI
202 BasepMoveFileCopyProgress(IN LARGE_INTEGER TotalFileSize,
203 IN LARGE_INTEGER TotalBytesTransferred,
204 IN LARGE_INTEGER StreamSize,
205 IN LARGE_INTEGER StreamBytesTransferred,
206 IN DWORD dwStreamNumber,
207 IN DWORD dwCallbackReason,
208 IN HANDLE hSourceFile,
209 IN HANDLE hDestinationFile,
210 IN LPVOID lpData OPTIONAL)
211 {
212 DWORD Ret = 0;
213 PCOPY_PROGRESS_CONTEXT Context = (PCOPY_PROGRESS_CONTEXT)lpData;
214
215 if (Context->Flags & MOVEFILE_WRITE_THROUGH)
216 {
217 if (!dwCallbackReason)
218 {
219 if (StreamBytesTransferred.QuadPart == StreamSize.QuadPart)
220 {
221 FlushFileBuffers(hDestinationFile);
222 }
223 }
224 }
225
226 if (Context->UserRoutine)
227 {
228 Ret = Context->UserRoutine(TotalFileSize,
229 TotalBytesTransferred,
230 StreamSize,
231 StreamBytesTransferred,
232 dwStreamNumber,
233 dwCallbackReason,
234 hSourceFile,
235 hDestinationFile,
236 Context->UserData);
237 }
238
239 return Ret;
240 }
241
242
243 /*
244 * @implemented
245 */
246 BOOL
247 WINAPI
248 MoveFileWithProgressW(IN LPCWSTR lpExistingFileName,
249 IN LPCWSTR lpNewFileName,
250 IN LPPROGRESS_ROUTINE lpProgressRoutine,
251 IN LPVOID lpData,
252 IN DWORD dwFlags)
253 {
254 NTSTATUS Status;
255 PWSTR NewBuffer;
256 IO_STATUS_BLOCK IoStatusBlock;
257 COPY_PROGRESS_CONTEXT CopyContext;
258 OBJECT_ATTRIBUTES ObjectAttributes;
259 PFILE_RENAME_INFORMATION RenameInfo;
260 UNICODE_STRING NewPathU, ExistingPathU;
261 FILE_ATTRIBUTE_TAG_INFORMATION FileAttrTagInfo;
262 HANDLE SourceHandle = INVALID_HANDLE_VALUE, NewHandle, ExistingHandle;
263 BOOL Ret = FALSE, ReplaceIfExists, DelayUntilReboot, AttemptReopenWithoutReparse;
264
265 DPRINT("MoveFileWithProgressW(%S, %S, %p, %p, %x)\n", lpExistingFileName, lpNewFileName, lpProgressRoutine, lpData, dwFlags);
266
267 NewPathU.Buffer = NULL;
268 ExistingPathU.Buffer = NULL;
269
270 _SEH2_TRY
271 {
272 /* Don't allow renaming to a disk */
273 if (lpNewFileName && RtlIsDosDeviceName_U(lpNewFileName))
274 {
275 BaseSetLastNTError(STATUS_OBJECT_NAME_COLLISION);
276 _SEH2_LEAVE;
277 }
278
279 ReplaceIfExists = !!(dwFlags & MOVEFILE_REPLACE_EXISTING);
280
281 /* Get file path */
282 if (!RtlDosPathNameToNtPathName_U(lpExistingFileName, &ExistingPathU, NULL, NULL))
283 {
284 BaseSetLastNTError(STATUS_OBJECT_PATH_NOT_FOUND);
285 _SEH2_LEAVE;
286 }
287
288 /* Sanitize input */
289 DelayUntilReboot = !!(dwFlags & MOVEFILE_DELAY_UNTIL_REBOOT);
290 if (DelayUntilReboot && (dwFlags & MOVEFILE_CREATE_HARDLINK))
291 {
292 BaseSetLastNTError(STATUS_INVALID_PARAMETER);
293 _SEH2_LEAVE;
294 }
295
296 /* Unless we manage a proper opening, we'll attempt to reopen without reparse support */
297 AttemptReopenWithoutReparse = TRUE;
298 InitializeObjectAttributes(&ObjectAttributes,
299 &ExistingPathU,
300 OBJ_CASE_INSENSITIVE,
301 NULL,
302 NULL);
303 /* Attempt to open source file */
304 Status = NtOpenFile(&SourceHandle,
305 FILE_READ_ATTRIBUTES | DELETE | SYNCHRONIZE,
306 &ObjectAttributes,
307 &IoStatusBlock,
308 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
309 FILE_OPEN_FOR_BACKUP_INTENT | ((dwFlags & MOVEFILE_WRITE_THROUGH) ? FILE_WRITE_THROUGH : 0));
310 if (!NT_SUCCESS(Status))
311 {
312 /* If we failed and the file doesn't exist, don't attempt to reopen without reparse */
313 if (DelayUntilReboot &&
314 (Status == STATUS_SHARING_VIOLATION || Status == STATUS_OBJECT_NAME_NOT_FOUND || Status == STATUS_OBJECT_PATH_NOT_FOUND))
315 {
316 /* Here we don't fail completely, as we postpone the operation to reboot
317 * File might exist afterwards, and we don't need a handle here
318 */
319 SourceHandle = INVALID_HANDLE_VALUE;
320 AttemptReopenWithoutReparse = FALSE;
321 }
322 /* If we failed for any reason than unsupported reparse, fail completely */
323 else if (Status != STATUS_INVALID_PARAMETER)
324 {
325 BaseSetLastNTError(Status);
326 _SEH2_LEAVE;
327 }
328 }
329 else
330 {
331 /* We managed to open, so query information */
332 Status = NtQueryInformationFile(SourceHandle,
333 &IoStatusBlock,
334 &FileAttrTagInfo,
335 sizeof(FILE_ATTRIBUTE_TAG_INFORMATION),
336 FileAttributeTagInformation);
337 if (!NT_SUCCESS(Status))
338 {
339 /* Do not tolerate any other error than something related to not supported operation */
340 if (Status != STATUS_NOT_IMPLEMENTED && Status != STATUS_INVALID_PARAMETER)
341 {
342 BaseSetLastNTError(Status);
343 _SEH2_LEAVE;
344 }
345
346 /* Not a reparse point, no need to reopen, it's fine */
347 AttemptReopenWithoutReparse = FALSE;
348 }
349 /* Validate the reparse point (do we support it?) */
350 else if (FileAttrTagInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT &&
351 FileAttrTagInfo.ReparseTag != IO_REPARSE_TAG_MOUNT_POINT)
352 {
353 NtClose(SourceHandle);
354 SourceHandle = INVALID_HANDLE_VALUE;
355 }
356 }
357
358 /* Simply reopen if required */
359 if (AttemptReopenWithoutReparse)
360 {
361 Status = NtOpenFile(&SourceHandle,
362 DELETE | SYNCHRONIZE,
363 &ObjectAttributes,
364 &IoStatusBlock,
365 FILE_SHARE_READ | FILE_SHARE_WRITE,
366 ((dwFlags & MOVEFILE_WRITE_THROUGH) ? FILE_WRITE_THROUGH : 0));
367 if (!NT_SUCCESS(Status))
368 {
369 BaseSetLastNTError(Status);
370 _SEH2_LEAVE;
371 }
372 }
373
374 /* Nullify string if we're to use it */
375 if (DelayUntilReboot && !lpNewFileName)
376 {
377 RtlInitUnicodeString(&NewPathU, 0);
378 }
379 /* Check whether path exists */
380 else if (!RtlDosPathNameToNtPathName_U(lpNewFileName, &NewPathU, 0, 0))
381 {
382 BaseSetLastNTError(STATUS_OBJECT_PATH_NOT_FOUND);
383 _SEH2_LEAVE;
384 }
385
386 /* Handle postponed renaming */
387 if (DelayUntilReboot)
388 {
389 /* If new file exists and we're allowed to replace, then mark the path with ! */
390 if (ReplaceIfExists && NewPathU.Length)
391 {
392 NewBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, NewPathU.Length + sizeof(WCHAR));
393 if (NewBuffer == NULL)
394 {
395 BaseSetLastNTError(STATUS_NO_MEMORY);
396 _SEH2_LEAVE;
397 }
398
399 NewBuffer[0] = L'!';
400 RtlCopyMemory(&NewBuffer[1], NewPathU.Buffer, NewPathU.Length);
401 NewPathU.Length += sizeof(WCHAR);
402 NewPathU.MaximumLength += sizeof(WCHAR);
403 RtlFreeHeap(RtlGetProcessHeap(), 0, NewPathU.Buffer);
404 NewPathU.Buffer = NewBuffer;
405 }
406
407 /* Check whether 'copy' renaming is allowed if required */
408 if (RtlDetermineDosPathNameType_U(lpExistingFileName) == RtlPathTypeUncAbsolute || dwFlags & MOVEFILE_COPY_ALLOWED)
409 {
410 Status = STATUS_INVALID_PARAMETER;
411 }
412 else
413 {
414 /* First, probe 2nd key to see whether it exists - if so, it will be appended there */
415 Status = BasepMoveFileDelayed(&ExistingPathU, &NewPathU, 2, FALSE);
416 if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
417 {
418 /* If doesn't exist, append to first key first, creating it if it doesn't exist */
419 Status = BasepMoveFileDelayed(&ExistingPathU, &NewPathU, 1, TRUE);
420
421 if (Status == STATUS_INSUFFICIENT_RESOURCES)
422 {
423 /* If it failed because it's too big, then create 2nd key and put it there */
424 Status = BasepMoveFileDelayed(&ExistingPathU, &NewPathU, 2, TRUE);
425 }
426 }
427 }
428
429 /* If we failed at some point, return the error */
430 if (!NT_SUCCESS(Status))
431 {
432 BaseSetLastNTError(Status);
433 _SEH2_LEAVE;
434 }
435
436 Ret = TRUE;
437 _SEH2_LEAVE;
438 }
439
440 /* At that point, we MUST have a source handle */
441 ASSERT(SourceHandle != INVALID_HANDLE_VALUE);
442
443 /* Allocate renaming buffer and fill it */
444 RenameInfo = RtlAllocateHeap(RtlGetProcessHeap(), 0, NewPathU.Length + sizeof(FILE_RENAME_INFORMATION));
445 if (RenameInfo == NULL)
446 {
447 BaseSetLastNTError(STATUS_NO_MEMORY);
448 _SEH2_LEAVE;
449 }
450
451 RtlCopyMemory(&RenameInfo->FileName, NewPathU.Buffer, NewPathU.Length);
452 RenameInfo->ReplaceIfExists = ReplaceIfExists;
453 RenameInfo->RootDirectory = 0;
454 RenameInfo->FileNameLength = NewPathU.Length;
455
456 /* Attempt to rename the file */
457 Status = NtSetInformationFile(SourceHandle,
458 &IoStatusBlock,
459 RenameInfo,
460 NewPathU.Length + sizeof(FILE_RENAME_INFORMATION),
461 ((dwFlags & MOVEFILE_CREATE_HARDLINK) ? FileLinkInformation : FileRenameInformation));
462 RtlFreeHeap(RtlGetProcessHeap(), 0, RenameInfo);
463 if (NT_SUCCESS(Status))
464 {
465 /* If it succeed, all fine, quit */
466 Ret = TRUE;
467 _SEH2_LEAVE;
468 }
469 /* If we failed for any other reason than not the same device, fail
470 * If we failed because of different devices, only allow renaming if user allowed copy
471 */
472 if (Status != STATUS_NOT_SAME_DEVICE || !(dwFlags & MOVEFILE_COPY_ALLOWED))
473 {
474 /* ReactOS hack! To be removed once all FSD have proper renaming support
475 * Just leave status to error and leave
476 */
477 if (Status == STATUS_NOT_IMPLEMENTED)
478 {
479 DPRINT1("Forcing copy, renaming not supported by FSD\n");
480 }
481 else
482 {
483 BaseSetLastNTError(Status);
484 _SEH2_LEAVE;
485 }
486 }
487
488 /* Close source file */
489 NtClose(SourceHandle);
490 SourceHandle = INVALID_HANDLE_VALUE;
491
492 /* Issue the copy of the file */
493 CopyContext.Flags = dwFlags;
494 CopyContext.UserRoutine = lpProgressRoutine;
495 CopyContext.UserData = lpData;
496 NewHandle = INVALID_HANDLE_VALUE;
497 ExistingHandle = INVALID_HANDLE_VALUE;
498
499 Ret = BasepCopyFileExW(lpExistingFileName,
500 lpNewFileName,
501 BasepMoveFileCopyProgress,
502 &CopyContext,
503 NULL,
504 (ReplaceIfExists == 0) | COPY_FILE_OPEN_SOURCE_FOR_WRITE,
505 0,
506 &ExistingHandle,
507 &NewHandle);
508 if (!Ret)
509 {
510 /* If it failed, don't leak any handle */
511 if (ExistingHandle != INVALID_HANDLE_VALUE)
512 {
513 CloseHandle(ExistingHandle);
514 ExistingHandle = INVALID_HANDLE_VALUE;
515 }
516 }
517 else if (ExistingHandle != INVALID_HANDLE_VALUE)
518 {
519 if (NewHandle != INVALID_HANDLE_VALUE)
520 {
521 /* If copying succeed, notify */
522 Status = BasepNotifyTrackingService(&ExistingHandle, &ObjectAttributes, NewHandle, &NewPathU);
523 if (!NT_SUCCESS(Status))
524 {
525 /* Fail in case it had to succeed */
526 if (dwFlags & MOVEFILE_FAIL_IF_NOT_TRACKABLE)
527 {
528 if (NewHandle != INVALID_HANDLE_VALUE)
529 CloseHandle(NewHandle);
530 NewHandle = INVALID_HANDLE_VALUE;
531 DeleteFileW(lpNewFileName);
532 Ret = FALSE;
533 BaseSetLastNTError(Status);
534 }
535 }
536 }
537
538 CloseHandle(ExistingHandle);
539 ExistingHandle = INVALID_HANDLE_VALUE;
540 }
541
542 /* In case copy worked, close file */
543 if (NewHandle != INVALID_HANDLE_VALUE)
544 {
545 CloseHandle(NewHandle);
546 NewHandle = INVALID_HANDLE_VALUE;
547 }
548
549 /* If it succeed, delete source file */
550 if (Ret)
551 {
552 if (!DeleteFileW(lpExistingFileName))
553 {
554 /* Reset file attributes if required */
555 SetFileAttributesW(lpExistingFileName, FILE_ATTRIBUTE_NORMAL);
556 DeleteFileW(lpExistingFileName);
557 }
558 }
559 }
560 _SEH2_FINALLY
561 {
562 if (SourceHandle != INVALID_HANDLE_VALUE)
563 NtClose(SourceHandle);
564
565 RtlFreeHeap(RtlGetProcessHeap(), 0, ExistingPathU.Buffer);
566 RtlFreeHeap(RtlGetProcessHeap(), 0, NewPathU.Buffer);
567 }
568 _SEH2_END;
569
570 return Ret;
571 }
572
573
574 /*
575 * @implemented
576 */
577 BOOL
578 WINAPI
579 MoveFileWithProgressA(IN LPCSTR lpExistingFileName,
580 IN LPCSTR lpNewFileName OPTIONAL,
581 IN LPPROGRESS_ROUTINE lpProgressRoutine OPTIONAL,
582 IN LPVOID lpData OPTIONAL,
583 IN DWORD dwFlags)
584 {
585 BOOL Ret;
586 UNICODE_STRING ExistingFileNameW, NewFileNameW;
587
588 if (!Basep8BitStringToDynamicUnicodeString(&ExistingFileNameW, lpExistingFileName))
589 {
590 return FALSE;
591 }
592
593 if (lpNewFileName)
594 {
595 if (!Basep8BitStringToDynamicUnicodeString(&NewFileNameW, lpNewFileName))
596 {
597 RtlFreeUnicodeString(&ExistingFileNameW);
598 return FALSE;
599 }
600 }
601 else
602 {
603 NewFileNameW.Buffer = NULL;
604 }
605
606 Ret = MoveFileWithProgressW(ExistingFileNameW.Buffer, NewFileNameW.Buffer, lpProgressRoutine, lpData, dwFlags);
607
608 RtlFreeUnicodeString(&ExistingFileNameW);
609 RtlFreeUnicodeString(&NewFileNameW);
610
611 return Ret;
612 }
613
614
615 /*
616 * @implemented
617 */
618 BOOL
619 WINAPI
620 MoveFileW(IN LPCWSTR lpExistingFileName,
621 IN LPCWSTR lpNewFileName)
622 {
623 return MoveFileWithProgressW(lpExistingFileName,
624 lpNewFileName,
625 NULL,
626 NULL,
627 MOVEFILE_COPY_ALLOWED);
628 }
629
630
631 /*
632 * @implemented
633 */
634 BOOL
635 WINAPI
636 MoveFileExW(IN LPCWSTR lpExistingFileName,
637 IN LPCWSTR lpNewFileName OPTIONAL,
638 IN DWORD dwFlags)
639 {
640 return MoveFileWithProgressW(lpExistingFileName,
641 lpNewFileName,
642 NULL,
643 NULL,
644 dwFlags);
645 }
646
647
648 /*
649 * @implemented
650 */
651 BOOL
652 WINAPI
653 MoveFileA(IN LPCSTR lpExistingFileName,
654 IN LPCSTR lpNewFileName)
655 {
656 return MoveFileWithProgressA(lpExistingFileName,
657 lpNewFileName,
658 NULL,
659 NULL,
660 MOVEFILE_COPY_ALLOWED);
661 }
662
663
664 /*
665 * @implemented
666 */
667 BOOL
668 WINAPI
669 MoveFileExA(IN LPCSTR lpExistingFileName,
670 IN LPCSTR lpNewFileName OPTIONAL,
671 IN DWORD dwFlags)
672 {
673 return MoveFileWithProgressA(lpExistingFileName,
674 lpNewFileName,
675 NULL,
676 NULL,
677 dwFlags);
678 }
679
680 /*
681 * @implemented
682 */
683 BOOL
684 WINAPI
685 ReplaceFileA(IN LPCSTR lpReplacedFileName,
686 IN LPCSTR lpReplacementFileName,
687 IN LPCSTR lpBackupFileName OPTIONAL,
688 IN DWORD dwReplaceFlags,
689 IN LPVOID lpExclude,
690 IN LPVOID lpReserved)
691 {
692 BOOL Ret;
693 UNICODE_STRING ReplacedFileNameW, ReplacementFileNameW, BackupFileNameW;
694
695 if (!lpReplacedFileName || !lpReplacementFileName || lpExclude || lpReserved || dwReplaceFlags & ~(REPLACEFILE_WRITE_THROUGH | REPLACEFILE_IGNORE_MERGE_ERRORS))
696 {
697 SetLastError(ERROR_INVALID_PARAMETER);
698 return FALSE;
699 }
700
701 if (!Basep8BitStringToDynamicUnicodeString(&ReplacedFileNameW, lpReplacedFileName))
702 {
703 return FALSE;
704 }
705
706 if (!Basep8BitStringToDynamicUnicodeString(&ReplacementFileNameW, lpReplacementFileName))
707 {
708 RtlFreeUnicodeString(&ReplacedFileNameW);
709 return FALSE;
710 }
711
712 if (lpBackupFileName)
713 {
714 if (!Basep8BitStringToDynamicUnicodeString(&BackupFileNameW, lpBackupFileName))
715 {
716 RtlFreeUnicodeString(&ReplacementFileNameW);
717 RtlFreeUnicodeString(&ReplacedFileNameW);
718 return FALSE;
719 }
720 }
721 else
722 {
723 BackupFileNameW.Buffer = NULL;
724 }
725
726 Ret = ReplaceFileW(ReplacedFileNameW.Buffer, ReplacementFileNameW.Buffer, BackupFileNameW.Buffer, dwReplaceFlags, 0, 0);
727
728 if (lpBackupFileName)
729 {
730 RtlFreeUnicodeString(&BackupFileNameW);
731 }
732 RtlFreeUnicodeString(&ReplacementFileNameW);
733 RtlFreeUnicodeString(&ReplacedFileNameW);
734
735 return Ret;
736 }
737
738 /*
739 * @unimplemented
740 */
741 BOOL
742 WINAPI
743 ReplaceFileW(
744 LPCWSTR lpReplacedFileName,
745 LPCWSTR lpReplacementFileName,
746 LPCWSTR lpBackupFileName,
747 DWORD dwReplaceFlags,
748 LPVOID lpExclude,
749 LPVOID lpReserved
750 )
751 {
752 HANDLE hReplaced = NULL, hReplacement = NULL;
753 UNICODE_STRING NtReplacedName = { 0, 0, NULL };
754 UNICODE_STRING NtReplacementName = { 0, 0, NULL };
755 DWORD Error = ERROR_SUCCESS;
756 NTSTATUS Status;
757 BOOL Ret = FALSE;
758 IO_STATUS_BLOCK IoStatusBlock;
759 OBJECT_ATTRIBUTES ObjectAttributes;
760 PVOID Buffer = NULL ;
761
762 if (dwReplaceFlags)
763 FIXME("Ignoring flags %x\n", dwReplaceFlags);
764
765 /* First two arguments are mandatory */
766 if (!lpReplacedFileName || !lpReplacementFileName)
767 {
768 SetLastError(ERROR_INVALID_PARAMETER);
769 return FALSE;
770 }
771
772 /* Back it up */
773 if(lpBackupFileName)
774 {
775 if(!CopyFileW(lpReplacedFileName, lpBackupFileName, FALSE))
776 {
777 Error = GetLastError();
778 goto Cleanup ;
779 }
780 }
781
782 /* Open the "replaced" file for reading and writing */
783 if (!(RtlDosPathNameToNtPathName_U(lpReplacedFileName, &NtReplacedName, NULL, NULL)))
784 {
785 Error = ERROR_PATH_NOT_FOUND;
786 goto Cleanup;
787 }
788
789 InitializeObjectAttributes(&ObjectAttributes,
790 &NtReplacedName,
791 OBJ_CASE_INSENSITIVE,
792 NULL,
793 NULL);
794
795 Status = NtOpenFile(&hReplaced,
796 GENERIC_READ | GENERIC_WRITE | DELETE | SYNCHRONIZE | WRITE_DAC,
797 &ObjectAttributes,
798 &IoStatusBlock,
799 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
800 FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE);
801
802 if (!NT_SUCCESS(Status))
803 {
804 if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
805 Error = ERROR_FILE_NOT_FOUND;
806 else
807 Error = ERROR_UNABLE_TO_REMOVE_REPLACED;
808 goto Cleanup;
809 }
810
811 /* Blank it */
812 SetEndOfFile(hReplaced) ;
813
814 /*
815 * Open the replacement file for reading, writing, and deleting
816 * (deleting is needed when finished)
817 */
818 if (!(RtlDosPathNameToNtPathName_U(lpReplacementFileName, &NtReplacementName, NULL, NULL)))
819 {
820 Error = ERROR_PATH_NOT_FOUND;
821 goto Cleanup;
822 }
823
824 InitializeObjectAttributes(&ObjectAttributes,
825 &NtReplacementName,
826 OBJ_CASE_INSENSITIVE,
827 NULL,
828 NULL);
829
830 Status = NtOpenFile(&hReplacement,
831 GENERIC_READ | DELETE | SYNCHRONIZE,
832 &ObjectAttributes,
833 &IoStatusBlock,
834 0,
835 FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE | FILE_DELETE_ON_CLOSE);
836
837 if (!NT_SUCCESS(Status))
838 {
839 Error = RtlNtStatusToDosError(Status);
840 goto Cleanup;
841 }
842
843 Buffer = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, 0x10000) ;
844 if (!Buffer)
845 {
846 Error = ERROR_NOT_ENOUGH_MEMORY;
847 goto Cleanup ;
848 }
849 while (Status != STATUS_END_OF_FILE)
850 {
851 Status = NtReadFile(hReplacement, NULL, NULL, NULL, &IoStatusBlock, Buffer, 0x10000, NULL, NULL) ;
852 if (NT_SUCCESS(Status))
853 {
854 Status = NtWriteFile(hReplaced, NULL, NULL, NULL, &IoStatusBlock, Buffer,
855 IoStatusBlock.Information, NULL, NULL) ;
856 if (!NT_SUCCESS(Status))
857 {
858 Error = RtlNtStatusToDosError(Status);
859 goto Cleanup;
860 }
861 }
862 else if (Status != STATUS_END_OF_FILE)
863 {
864 Error = RtlNtStatusToDosError(Status);
865 goto Cleanup;
866 }
867 }
868
869 Ret = TRUE;
870
871 /* Perform resource cleanup */
872 Cleanup:
873 if (hReplaced) NtClose(hReplaced);
874 if (hReplacement) NtClose(hReplacement);
875 if (Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
876
877 if (NtReplacementName.Buffer)
878 RtlFreeHeap(GetProcessHeap(), 0, NtReplacementName.Buffer);
879 if (NtReplacedName.Buffer)
880 RtlFreeHeap(GetProcessHeap(), 0, NtReplacedName.Buffer);
881
882 /* If there was an error, set the error code */
883 if(!Ret)
884 {
885 TRACE("ReplaceFileW failed (error=%lu)\n", Error);
886 SetLastError(Error);
887 }
888 return Ret;
889 }
890
891 /*
892 * @unimplemented
893 */
894 BOOL
895 WINAPI
896 PrivMoveFileIdentityW(DWORD Unknown1, DWORD Unknown2, DWORD Unknown3)
897 {
898 STUB;
899 return FALSE;
900 }
901
902 /* EOF */