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