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