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