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)
13 * Fix some bugs in the add_boot_rename_entry function
16 /* INCLUDES *****************************************************************/
23 DEBUG_CHANNEL(kernel32file
);
25 /* GLOBALS *****************************************************************/
27 /* DEFINES *****************************************************************/
28 typedef struct _COPY_PROGRESS_CONTEXT
31 LPPROGRESS_ROUTINE UserRoutine
;
33 } COPY_PROGRESS_CONTEXT
, *PCOPY_PROGRESS_CONTEXT
;
35 /* FUNCTIONS ****************************************************************/
41 BasepMoveFileDelayed(IN PUNICODE_STRING ExistingPath
,
42 IN PUNICODE_STRING NewPath
,
44 IN BOOL CreateIfNotFound
)
46 #define STRING_LENGTH 0x400
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];
57 RtlInitUnicodeString(&SessionManagerString
, L
"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Session Manager");
59 /* Select appropriate key for adding our file */
62 PendingOperations
= L
"PendingFileRenameOperations";
66 StringCbPrintfW(PendingOperationsBuffer
, sizeof(PendingOperationsBuffer
), L
"PendingFileRenameOperations%d", KeyId
);
67 PendingOperations
= PendingOperationsBuffer
;
69 RtlInitUnicodeString(&PendingOperationsString
, PendingOperations
);
71 InitializeObjectAttributes(&ObjectAttributes
,
72 &SessionManagerString
,
73 OBJ_OPENIF
| OBJ_CASE_INSENSITIVE
,
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
)
83 Status
= NtCreateKey(&KeyHandle
,
84 GENERIC_READ
| GENERIC_WRITE
,
85 &ObjectAttributes
, 0, NULL
,
86 REG_OPTION_BACKUP_RESTORE
, NULL
);
89 if (!NT_SUCCESS(Status
))
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
);
99 /* Allocate output buffer */
100 Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferLength
);
104 return STATUS_NO_MEMORY
;
107 Status
= NtQueryValueKey(KeyHandle
,
108 &PendingOperationsString
,
109 KeyValuePartialInformation
,
110 Buffer
, StringLength
, &DataSize
);
111 if (Status
!= STATUS_BUFFER_OVERFLOW
)
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
)
124 return STATUS_BUFFER_TOO_SMALL
;
128 /* Check if it existed - if not, create only IF asked to */
129 if (!NT_SUCCESS(Status
) && (Status
!= STATUS_OBJECT_NAME_NOT_FOUND
|| !CreateIfNotFound
))
132 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
136 if (!NT_SUCCESS(Status
))
138 /* We didn't find any - ie, we create, so use complete buffer */
139 BufferBegin
= Buffer
;
140 BufferWrite
= Buffer
;
144 PKEY_VALUE_PARTIAL_INFORMATION PartialInfo
= (PKEY_VALUE_PARTIAL_INFORMATION
)Buffer
;
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)
149 BufferBegin
= PartialInfo
->Data
;
150 BufferWrite
= (PWSTR
)((ULONG_PTR
)PartialInfo
->Data
+ PartialInfo
->DataLength
- sizeof(UNICODE_NULL
));
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
;
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
;
165 *BufferWrite
= UNICODE_NULL
;
168 Status
= NtSetValueKey(KeyHandle
,
169 &PendingOperationsString
,
170 0, REG_MULTI_SZ
, BufferBegin
,
171 (ULONG_PTR
)BufferWrite
- (ULONG_PTR
)BufferBegin
+ sizeof(WCHAR
));
174 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
185 BasepNotifyTrackingService(IN PHANDLE ExistingHandle
,
186 IN POBJECT_ATTRIBUTES ObjectAttributes
,
188 IN PUNICODE_STRING NewPath
)
190 return STATUS_NOT_IMPLEMENTED
;
199 BasepOpenFileForMove(IN LPCWSTR File
,
200 OUT PUNICODE_STRING RelativeNtName
,
202 OUT PHANDLE FileHandle
,
203 OUT POBJECT_ATTRIBUTES ObjectAttributes
,
204 IN ACCESS_MASK DesiredAccess
,
205 IN ULONG ShareAccess
,
206 IN ULONG OpenOptions
)
208 RTL_RELATIVE_NAME_U RelativeName
;
210 IO_STATUS_BLOCK IoStatusBlock
;
211 FILE_ATTRIBUTE_TAG_INFORMATION TagInfo
;
212 ULONG IntShareAccess
;
213 BOOLEAN HasRelative
= FALSE
;
218 RelativeNtName
->Length
=
219 RelativeNtName
->MaximumLength
= 0;
220 RelativeNtName
->Buffer
= NULL
;
223 if (!RtlDosPathNameToRelativeNtPathName_U(File
, RelativeNtName
, NULL
, &RelativeName
))
225 Status
= STATUS_OBJECT_PATH_NOT_FOUND
;
230 *NtName
= RelativeNtName
->Buffer
;
232 if (RelativeName
.RelativeName
.Length
)
234 RelativeNtName
->Length
= RelativeName
.RelativeName
.Length
;
235 RelativeNtName
->MaximumLength
= RelativeName
.RelativeName
.MaximumLength
;
236 RelativeNtName
->Buffer
= RelativeName
.RelativeName
.Buffer
;
240 RelativeName
.ContainingDirectory
= 0;
243 InitializeObjectAttributes(ObjectAttributes
,
245 OBJ_CASE_INSENSITIVE
,
246 RelativeName
.ContainingDirectory
,
248 /* Force certain flags here, given ops we'll do */
249 IntShareAccess
= ShareAccess
| FILE_SHARE_READ
| FILE_SHARE_WRITE
;
250 OpenOptions
|= FILE_OPEN_FOR_BACKUP_INTENT
| FILE_SYNCHRONOUS_IO_NONALERT
;
252 /* We'll try to read reparse tag */
253 Status
= NtOpenFile(FileHandle
,
254 DesiredAccess
| FILE_READ_ATTRIBUTES
| SYNCHRONIZE
,
258 OpenOptions
| FILE_OPEN_REPARSE_POINT
);
259 if (NT_SUCCESS(Status
))
261 /* Attempt the read */
262 Status
= NtQueryInformationFile(*FileHandle
,
265 sizeof(FILE_ATTRIBUTE_TAG_INFORMATION
),
266 FileAttributeTagInformation
);
268 /* Return if failure with a status that wouldn't mean the FSD cannot support reparse points */
269 if (!NT_SUCCESS(Status
) &&
270 (Status
!= STATUS_NOT_IMPLEMENTED
&& Status
!= STATUS_INVALID_PARAMETER
))
275 if (NT_SUCCESS(Status
))
277 /* This cannot happen on mount points */
278 if (TagInfo
.FileAttributes
& FILE_ATTRIBUTE_DEVICE
||
279 TagInfo
.ReparseTag
== IO_REPARSE_TAG_MOUNT_POINT
)
285 NtClose(*FileHandle
);
286 *FileHandle
= INVALID_HANDLE_VALUE
;
288 IntShareAccess
= ShareAccess
| FILE_SHARE_READ
| FILE_SHARE_DELETE
;
290 else if (Status
== STATUS_INVALID_PARAMETER
)
292 IntShareAccess
= ShareAccess
| FILE_SHARE_READ
| FILE_SHARE_WRITE
;
299 /* Reattempt to open normally, following reparse point if needed */
300 Status
= NtOpenFile(FileHandle
,
301 DesiredAccess
| SYNCHRONIZE
,
311 RtlReleaseRelativeName(&RelativeName
);
325 BasepMoveFileCopyProgress(IN LARGE_INTEGER TotalFileSize
,
326 IN LARGE_INTEGER TotalBytesTransferred
,
327 IN LARGE_INTEGER StreamSize
,
328 IN LARGE_INTEGER StreamBytesTransferred
,
329 IN DWORD dwStreamNumber
,
330 IN DWORD dwCallbackReason
,
331 IN HANDLE hSourceFile
,
332 IN HANDLE hDestinationFile
,
333 IN LPVOID lpData OPTIONAL
)
336 PCOPY_PROGRESS_CONTEXT Context
= (PCOPY_PROGRESS_CONTEXT
)lpData
;
338 if (Context
->Flags
& MOVEFILE_WRITE_THROUGH
)
340 if (!dwCallbackReason
)
342 if (StreamBytesTransferred
.QuadPart
== StreamSize
.QuadPart
)
344 FlushFileBuffers(hDestinationFile
);
349 if (Context
->UserRoutine
)
351 Ret
= Context
->UserRoutine(TotalFileSize
,
352 TotalBytesTransferred
,
354 StreamBytesTransferred
,
371 MoveFileWithProgressW(IN LPCWSTR lpExistingFileName
,
372 IN LPCWSTR lpNewFileName
,
373 IN LPPROGRESS_ROUTINE lpProgressRoutine
,
379 IO_STATUS_BLOCK IoStatusBlock
;
380 COPY_PROGRESS_CONTEXT CopyContext
;
381 OBJECT_ATTRIBUTES ObjectAttributes
;
382 PFILE_RENAME_INFORMATION RenameInfo
;
383 UNICODE_STRING NewPathU
, ExistingPathU
;
384 FILE_ATTRIBUTE_TAG_INFORMATION FileAttrTagInfo
;
385 HANDLE SourceHandle
= INVALID_HANDLE_VALUE
, NewHandle
, ExistingHandle
;
386 BOOL Ret
= FALSE
, ReplaceIfExists
, DelayUntilReboot
, AttemptReopenWithoutReparse
;
388 DPRINT("MoveFileWithProgressW(%S, %S, %p, %p, %x)\n", lpExistingFileName
, lpNewFileName
, lpProgressRoutine
, lpData
, dwFlags
);
390 NewPathU
.Buffer
= NULL
;
391 ExistingPathU
.Buffer
= NULL
;
395 /* Don't allow renaming to a disk */
396 if (lpNewFileName
&& RtlIsDosDeviceName_U(lpNewFileName
))
398 BaseSetLastNTError(STATUS_OBJECT_NAME_COLLISION
);
402 ReplaceIfExists
= !!(dwFlags
& MOVEFILE_REPLACE_EXISTING
);
405 if (!RtlDosPathNameToNtPathName_U(lpExistingFileName
, &ExistingPathU
, NULL
, NULL
))
407 BaseSetLastNTError(STATUS_OBJECT_PATH_NOT_FOUND
);
412 DelayUntilReboot
= !!(dwFlags
& MOVEFILE_DELAY_UNTIL_REBOOT
);
413 if (DelayUntilReboot
&& (dwFlags
& MOVEFILE_CREATE_HARDLINK
))
415 BaseSetLastNTError(STATUS_INVALID_PARAMETER
);
419 /* Unless we manage a proper opening, we'll attempt to reopen without reparse support */
420 AttemptReopenWithoutReparse
= TRUE
;
421 InitializeObjectAttributes(&ObjectAttributes
,
423 OBJ_CASE_INSENSITIVE
,
426 /* Attempt to open source file */
427 Status
= NtOpenFile(&SourceHandle
,
428 FILE_READ_ATTRIBUTES
| DELETE
| SYNCHRONIZE
,
431 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
,
432 FILE_OPEN_FOR_BACKUP_INTENT
| ((dwFlags
& MOVEFILE_WRITE_THROUGH
) ? FILE_WRITE_THROUGH
: 0));
433 if (!NT_SUCCESS(Status
))
435 /* If we failed and the file doesn't exist, don't attempt to reopen without reparse */
436 if (DelayUntilReboot
&&
437 (Status
== STATUS_SHARING_VIOLATION
|| Status
== STATUS_OBJECT_NAME_NOT_FOUND
|| Status
== STATUS_OBJECT_PATH_NOT_FOUND
))
439 /* Here we don't fail completely, as we postpone the operation to reboot
440 * File might exist afterwards, and we don't need a handle here
442 SourceHandle
= INVALID_HANDLE_VALUE
;
443 AttemptReopenWithoutReparse
= FALSE
;
445 /* If we failed for any reason than unsupported reparse, fail completely */
446 else if (Status
!= STATUS_INVALID_PARAMETER
)
448 BaseSetLastNTError(Status
);
454 /* We managed to open, so query information */
455 Status
= NtQueryInformationFile(SourceHandle
,
458 sizeof(FILE_ATTRIBUTE_TAG_INFORMATION
),
459 FileAttributeTagInformation
);
460 if (!NT_SUCCESS(Status
))
462 /* Do not tolerate any other error than something related to not supported operation */
463 if (Status
!= STATUS_NOT_IMPLEMENTED
&& Status
!= STATUS_INVALID_PARAMETER
)
465 BaseSetLastNTError(Status
);
469 /* Not a reparse point, no need to reopen, it's fine */
470 AttemptReopenWithoutReparse
= FALSE
;
472 /* Validate the reparse point (do we support it?) */
473 else if (FileAttrTagInfo
.FileAttributes
& FILE_ATTRIBUTE_REPARSE_POINT
&&
474 FileAttrTagInfo
.ReparseTag
!= IO_REPARSE_TAG_MOUNT_POINT
)
476 NtClose(SourceHandle
);
477 SourceHandle
= INVALID_HANDLE_VALUE
;
481 /* Simply reopen if required */
482 if (AttemptReopenWithoutReparse
)
484 Status
= NtOpenFile(&SourceHandle
,
485 DELETE
| SYNCHRONIZE
,
488 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
489 ((dwFlags
& MOVEFILE_WRITE_THROUGH
) ? FILE_WRITE_THROUGH
: 0));
490 if (!NT_SUCCESS(Status
))
492 BaseSetLastNTError(Status
);
497 /* Nullify string if we're to use it */
498 if (DelayUntilReboot
&& !lpNewFileName
)
500 RtlInitUnicodeString(&NewPathU
, 0);
502 /* Check whether path exists */
503 else if (!RtlDosPathNameToNtPathName_U(lpNewFileName
, &NewPathU
, 0, 0))
505 BaseSetLastNTError(STATUS_OBJECT_PATH_NOT_FOUND
);
509 /* Handle postponed renaming */
510 if (DelayUntilReboot
)
512 /* If new file exists and we're allowed to replace, then mark the path with ! */
513 if (ReplaceIfExists
&& NewPathU
.Length
)
515 NewBuffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, NewPathU
.Length
+ sizeof(WCHAR
));
516 if (NewBuffer
== NULL
)
518 BaseSetLastNTError(STATUS_NO_MEMORY
);
523 RtlCopyMemory(&NewBuffer
[1], NewPathU
.Buffer
, NewPathU
.Length
);
524 NewPathU
.Length
+= sizeof(WCHAR
);
525 NewPathU
.MaximumLength
+= sizeof(WCHAR
);
526 RtlFreeHeap(RtlGetProcessHeap(), 0, NewPathU
.Buffer
);
527 NewPathU
.Buffer
= NewBuffer
;
530 /* Check whether 'copy' renaming is allowed if required */
531 if (RtlDetermineDosPathNameType_U(lpExistingFileName
) == RtlPathTypeUncAbsolute
|| dwFlags
& MOVEFILE_COPY_ALLOWED
)
533 Status
= STATUS_INVALID_PARAMETER
;
537 /* First, probe 2nd key to see whether it exists - if so, it will be appended there */
538 Status
= BasepMoveFileDelayed(&ExistingPathU
, &NewPathU
, 2, FALSE
);
539 if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
541 /* If doesn't exist, append to first key first, creating it if it doesn't exist */
542 Status
= BasepMoveFileDelayed(&ExistingPathU
, &NewPathU
, 1, TRUE
);
544 if (Status
== STATUS_INSUFFICIENT_RESOURCES
)
546 /* If it failed because it's too big, then create 2nd key and put it there */
547 Status
= BasepMoveFileDelayed(&ExistingPathU
, &NewPathU
, 2, TRUE
);
552 /* If we failed at some point, return the error */
553 if (!NT_SUCCESS(Status
))
555 BaseSetLastNTError(Status
);
563 /* At that point, we MUST have a source handle */
564 ASSERT(SourceHandle
!= INVALID_HANDLE_VALUE
);
566 /* Allocate renaming buffer and fill it */
567 RenameInfo
= RtlAllocateHeap(RtlGetProcessHeap(), 0, NewPathU
.Length
+ sizeof(FILE_RENAME_INFORMATION
));
568 if (RenameInfo
== NULL
)
570 BaseSetLastNTError(STATUS_NO_MEMORY
);
574 RtlCopyMemory(&RenameInfo
->FileName
, NewPathU
.Buffer
, NewPathU
.Length
);
575 RenameInfo
->ReplaceIfExists
= ReplaceIfExists
;
576 RenameInfo
->RootDirectory
= 0;
577 RenameInfo
->FileNameLength
= NewPathU
.Length
;
579 /* Attempt to rename the file */
580 Status
= NtSetInformationFile(SourceHandle
,
583 NewPathU
.Length
+ sizeof(FILE_RENAME_INFORMATION
),
584 ((dwFlags
& MOVEFILE_CREATE_HARDLINK
) ? FileLinkInformation
: FileRenameInformation
));
585 RtlFreeHeap(RtlGetProcessHeap(), 0, RenameInfo
);
586 if (NT_SUCCESS(Status
))
588 /* If it succeed, all fine, quit */
592 /* If we failed for any other reason than not the same device, fail
593 * If we failed because of different devices, only allow renaming if user allowed copy
595 if (Status
!= STATUS_NOT_SAME_DEVICE
|| !(dwFlags
& MOVEFILE_COPY_ALLOWED
))
597 /* ReactOS hack! To be removed once all FSD have proper renaming support
598 * Just leave status to error and leave
600 if (Status
== STATUS_NOT_IMPLEMENTED
)
602 DPRINT1("Forcing copy, renaming not supported by FSD\n");
606 BaseSetLastNTError(Status
);
611 /* Close source file */
612 NtClose(SourceHandle
);
613 SourceHandle
= INVALID_HANDLE_VALUE
;
615 /* Issue the copy of the file */
616 CopyContext
.Flags
= dwFlags
;
617 CopyContext
.UserRoutine
= lpProgressRoutine
;
618 CopyContext
.UserData
= lpData
;
619 NewHandle
= INVALID_HANDLE_VALUE
;
620 ExistingHandle
= INVALID_HANDLE_VALUE
;
622 Ret
= BasepCopyFileExW(lpExistingFileName
,
624 BasepMoveFileCopyProgress
,
627 (ReplaceIfExists
== 0) | COPY_FILE_OPEN_SOURCE_FOR_WRITE
,
633 /* If it failed, don't leak any handle */
634 if (ExistingHandle
!= INVALID_HANDLE_VALUE
)
636 CloseHandle(ExistingHandle
);
637 ExistingHandle
= INVALID_HANDLE_VALUE
;
640 else if (ExistingHandle
!= INVALID_HANDLE_VALUE
)
642 if (NewHandle
!= INVALID_HANDLE_VALUE
)
644 /* If copying succeed, notify */
645 Status
= BasepNotifyTrackingService(&ExistingHandle
, &ObjectAttributes
, NewHandle
, &NewPathU
);
646 if (!NT_SUCCESS(Status
))
648 /* Fail in case it had to succeed */
649 if (dwFlags
& MOVEFILE_FAIL_IF_NOT_TRACKABLE
)
651 if (NewHandle
!= INVALID_HANDLE_VALUE
)
652 CloseHandle(NewHandle
);
653 NewHandle
= INVALID_HANDLE_VALUE
;
654 DeleteFileW(lpNewFileName
);
656 BaseSetLastNTError(Status
);
661 CloseHandle(ExistingHandle
);
662 ExistingHandle
= INVALID_HANDLE_VALUE
;
665 /* In case copy worked, close file */
666 if (NewHandle
!= INVALID_HANDLE_VALUE
)
668 CloseHandle(NewHandle
);
669 NewHandle
= INVALID_HANDLE_VALUE
;
672 /* If it succeed, delete source file */
675 if (!DeleteFileW(lpExistingFileName
))
677 /* Reset file attributes if required */
678 SetFileAttributesW(lpExistingFileName
, FILE_ATTRIBUTE_NORMAL
);
679 DeleteFileW(lpExistingFileName
);
685 if (SourceHandle
!= INVALID_HANDLE_VALUE
)
686 NtClose(SourceHandle
);
688 RtlFreeHeap(RtlGetProcessHeap(), 0, ExistingPathU
.Buffer
);
689 RtlFreeHeap(RtlGetProcessHeap(), 0, NewPathU
.Buffer
);
702 MoveFileWithProgressA(IN LPCSTR lpExistingFileName
,
703 IN LPCSTR lpNewFileName OPTIONAL
,
704 IN LPPROGRESS_ROUTINE lpProgressRoutine OPTIONAL
,
705 IN LPVOID lpData OPTIONAL
,
709 UNICODE_STRING ExistingFileNameW
, NewFileNameW
;
711 if (!Basep8BitStringToDynamicUnicodeString(&ExistingFileNameW
, lpExistingFileName
))
718 if (!Basep8BitStringToDynamicUnicodeString(&NewFileNameW
, lpNewFileName
))
720 RtlFreeUnicodeString(&ExistingFileNameW
);
726 NewFileNameW
.Buffer
= NULL
;
729 Ret
= MoveFileWithProgressW(ExistingFileNameW
.Buffer
, NewFileNameW
.Buffer
, lpProgressRoutine
, lpData
, dwFlags
);
731 RtlFreeUnicodeString(&ExistingFileNameW
);
732 RtlFreeUnicodeString(&NewFileNameW
);
743 MoveFileW(IN LPCWSTR lpExistingFileName
,
744 IN LPCWSTR lpNewFileName
)
746 return MoveFileWithProgressW(lpExistingFileName
,
750 MOVEFILE_COPY_ALLOWED
);
759 MoveFileExW(IN LPCWSTR lpExistingFileName
,
760 IN LPCWSTR lpNewFileName OPTIONAL
,
763 return MoveFileWithProgressW(lpExistingFileName
,
776 MoveFileA(IN LPCSTR lpExistingFileName
,
777 IN LPCSTR lpNewFileName
)
779 return MoveFileWithProgressA(lpExistingFileName
,
783 MOVEFILE_COPY_ALLOWED
);
792 MoveFileExA(IN LPCSTR lpExistingFileName
,
793 IN LPCSTR lpNewFileName OPTIONAL
,
796 return MoveFileWithProgressA(lpExistingFileName
,
808 ReplaceFileA(IN LPCSTR lpReplacedFileName
,
809 IN LPCSTR lpReplacementFileName
,
810 IN LPCSTR lpBackupFileName OPTIONAL
,
811 IN DWORD dwReplaceFlags
,
813 IN LPVOID lpReserved
)
816 UNICODE_STRING ReplacedFileNameW
, ReplacementFileNameW
, BackupFileNameW
;
818 if (!lpReplacedFileName
|| !lpReplacementFileName
|| lpExclude
|| lpReserved
|| dwReplaceFlags
& ~(REPLACEFILE_WRITE_THROUGH
| REPLACEFILE_IGNORE_MERGE_ERRORS
))
820 SetLastError(ERROR_INVALID_PARAMETER
);
824 if (!Basep8BitStringToDynamicUnicodeString(&ReplacedFileNameW
, lpReplacedFileName
))
829 if (!Basep8BitStringToDynamicUnicodeString(&ReplacementFileNameW
, lpReplacementFileName
))
831 RtlFreeUnicodeString(&ReplacedFileNameW
);
835 if (lpBackupFileName
)
837 if (!Basep8BitStringToDynamicUnicodeString(&BackupFileNameW
, lpBackupFileName
))
839 RtlFreeUnicodeString(&ReplacementFileNameW
);
840 RtlFreeUnicodeString(&ReplacedFileNameW
);
846 BackupFileNameW
.Buffer
= NULL
;
849 Ret
= ReplaceFileW(ReplacedFileNameW
.Buffer
, ReplacementFileNameW
.Buffer
, BackupFileNameW
.Buffer
, dwReplaceFlags
, 0, 0);
851 if (lpBackupFileName
)
853 RtlFreeUnicodeString(&BackupFileNameW
);
855 RtlFreeUnicodeString(&ReplacementFileNameW
);
856 RtlFreeUnicodeString(&ReplacedFileNameW
);
867 LPCWSTR lpReplacedFileName
,
868 LPCWSTR lpReplacementFileName
,
869 LPCWSTR lpBackupFileName
,
870 DWORD dwReplaceFlags
,
875 HANDLE hReplaced
= NULL
, hReplacement
= NULL
;
876 UNICODE_STRING NtReplacedName
= { 0, 0, NULL
};
877 UNICODE_STRING NtReplacementName
= { 0, 0, NULL
};
878 DWORD Error
= ERROR_SUCCESS
;
881 IO_STATUS_BLOCK IoStatusBlock
;
882 OBJECT_ATTRIBUTES ObjectAttributes
;
883 PVOID Buffer
= NULL
;
886 FIXME("Ignoring flags %x\n", dwReplaceFlags
);
888 /* First two arguments are mandatory */
889 if (!lpReplacedFileName
|| !lpReplacementFileName
)
891 SetLastError(ERROR_INVALID_PARAMETER
);
898 if(!CopyFileW(lpReplacedFileName
, lpBackupFileName
, FALSE
))
900 Error
= GetLastError();
905 /* Open the "replaced" file for reading and writing */
906 if (!(RtlDosPathNameToNtPathName_U(lpReplacedFileName
, &NtReplacedName
, NULL
, NULL
)))
908 Error
= ERROR_PATH_NOT_FOUND
;
912 InitializeObjectAttributes(&ObjectAttributes
,
914 OBJ_CASE_INSENSITIVE
,
918 Status
= NtOpenFile(&hReplaced
,
919 GENERIC_READ
| GENERIC_WRITE
| DELETE
| SYNCHRONIZE
| WRITE_DAC
,
922 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
,
923 FILE_SYNCHRONOUS_IO_NONALERT
| FILE_NON_DIRECTORY_FILE
);
925 if (!NT_SUCCESS(Status
))
927 if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
928 Error
= ERROR_FILE_NOT_FOUND
;
930 Error
= ERROR_UNABLE_TO_REMOVE_REPLACED
;
935 SetEndOfFile(hReplaced
) ;
938 * Open the replacement file for reading, writing, and deleting
939 * (deleting is needed when finished)
941 if (!(RtlDosPathNameToNtPathName_U(lpReplacementFileName
, &NtReplacementName
, NULL
, NULL
)))
943 Error
= ERROR_PATH_NOT_FOUND
;
947 InitializeObjectAttributes(&ObjectAttributes
,
949 OBJ_CASE_INSENSITIVE
,
953 Status
= NtOpenFile(&hReplacement
,
954 GENERIC_READ
| DELETE
| SYNCHRONIZE
,
958 FILE_SYNCHRONOUS_IO_NONALERT
| FILE_NON_DIRECTORY_FILE
| FILE_DELETE_ON_CLOSE
);
960 if (!NT_SUCCESS(Status
))
962 Error
= RtlNtStatusToDosError(Status
);
966 Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY
, 0x10000) ;
969 Error
= ERROR_NOT_ENOUGH_MEMORY
;
972 while (Status
!= STATUS_END_OF_FILE
)
974 Status
= NtReadFile(hReplacement
, NULL
, NULL
, NULL
, &IoStatusBlock
, Buffer
, 0x10000, NULL
, NULL
) ;
975 if (NT_SUCCESS(Status
))
977 Status
= NtWriteFile(hReplaced
, NULL
, NULL
, NULL
, &IoStatusBlock
, Buffer
,
978 IoStatusBlock
.Information
, NULL
, NULL
) ;
979 if (!NT_SUCCESS(Status
))
981 Error
= RtlNtStatusToDosError(Status
);
985 else if (Status
!= STATUS_END_OF_FILE
)
987 Error
= RtlNtStatusToDosError(Status
);
994 /* Perform resource cleanup */
996 if (hReplaced
) NtClose(hReplaced
);
997 if (hReplacement
) NtClose(hReplacement
);
998 if (Buffer
) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
1000 if (NtReplacementName
.Buffer
)
1001 RtlFreeHeap(GetProcessHeap(), 0, NtReplacementName
.Buffer
);
1002 if (NtReplacedName
.Buffer
)
1003 RtlFreeHeap(GetProcessHeap(), 0, NtReplacedName
.Buffer
);
1005 /* If there was an error, set the error code */
1008 TRACE("ReplaceFileW failed (error=%lu)\n", Error
);
1009 SetLastError(Error
);
1020 PrivMoveFileIdentityW(IN LPCWSTR lpSource
, IN LPCWSTR lpDestination
, IN DWORD dwFlags
)
1022 ACCESS_MASK SourceAccess
;
1023 UNICODE_STRING NtSource
, NtDestination
;
1024 LPWSTR RelativeSource
, RelativeDestination
;
1025 HANDLE SourceHandle
, DestinationHandle
;
1026 OBJECT_ATTRIBUTES ObjectAttributesSource
, ObjectAttributesDestination
;
1027 NTSTATUS Status
, OldStatus
= STATUS_SUCCESS
;
1028 ACCESS_MASK DestAccess
;
1029 IO_STATUS_BLOCK IoStatusBlock
;
1030 FILE_BASIC_INFORMATION SourceInformation
, DestinationInformation
;
1031 FILE_DISPOSITION_INFORMATION FileDispositionInfo
;
1033 DPRINT("PrivMoveFileIdentityW(%S, %S, %x)\n", lpSource
, lpDestination
, dwFlags
);
1035 SourceHandle
= INVALID_HANDLE_VALUE
;
1037 NtSource
.MaximumLength
= 0;
1038 NtSource
.Buffer
= NULL
;
1039 RelativeSource
= NULL
;
1040 DestinationHandle
= INVALID_HANDLE_VALUE
;
1041 NtDestination
.Length
=
1042 NtDestination
.MaximumLength
= 0;
1043 NtDestination
.Buffer
= NULL
;
1044 RelativeDestination
= NULL
;
1046 /* FILE_WRITE_DATA is required for later on notification */
1047 SourceAccess
= FILE_READ_ATTRIBUTES
| FILE_WRITE_DATA
;
1048 if (dwFlags
& PRIV_DELETE_ON_SUCCESS
)
1050 SourceAccess
|= DELETE
;
1055 /* We will loop twice:
1056 * First we attempt to open with FILE_WRITE_DATA for notification
1057 * If it fails and we have flag for non-trackable files, we retry
1058 * without FILE_WRITE_DATA.
1059 * If that one fails, then, we quit for real
1063 Status
= BasepOpenFileForMove(lpSource
,
1067 &ObjectAttributesSource
,
1069 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
,
1070 FILE_OPEN_NO_RECALL
);
1071 if (NT_SUCCESS(Status
))
1076 /* If we already attempted the opening without FILE_WRITE_DATA
1077 * or if we cannot move on non-trackable files, fail.
1079 if (!(SourceAccess
& FILE_WRITE_DATA
) || !(dwFlags
& PRIV_ALLOW_NON_TRACKABLE
))
1086 RtlFreeHeap(RtlGetProcessHeap(), 0, RelativeSource
);
1087 RelativeSource
= NULL
;
1090 if (SourceHandle
!= INVALID_HANDLE_VALUE
)
1092 NtClose(SourceHandle
);
1093 SourceHandle
= INVALID_HANDLE_VALUE
;
1096 SourceAccess
&= ~FILE_WRITE_DATA
;
1098 /* Remember fist failure in the path */
1099 if (NT_SUCCESS(OldStatus
))
1105 DestAccess
= FILE_WRITE_ATTRIBUTES
;
1106 /* If we could preserve FILE_WRITE_DATA for source, attempt to get it for destination
1107 * Still for notification purposes
1109 if (SourceAccess
& FILE_WRITE_DATA
)
1111 DestAccess
|= FILE_WRITE_DATA
;
1114 /* cf comment for first loop */
1117 Status
= BasepOpenFileForMove(lpDestination
,
1119 &RelativeDestination
,
1121 &ObjectAttributesDestination
,
1123 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
,
1124 FILE_OPEN_NO_RECALL
);
1125 if (NT_SUCCESS(Status
))
1130 /* If we already attempted the opening without FILE_WRITE_DATA
1131 * or if we cannot move on non-trackable files, fail.
1133 if (!(DestAccess
& FILE_WRITE_DATA
) || !(dwFlags
& PRIV_ALLOW_NON_TRACKABLE
))
1138 if (RelativeDestination
)
1140 RtlFreeHeap(RtlGetProcessHeap(), 0, RelativeDestination
);
1141 RelativeDestination
= NULL
;
1144 if (DestinationHandle
!= INVALID_HANDLE_VALUE
)
1146 NtClose(DestinationHandle
);
1147 DestinationHandle
= INVALID_HANDLE_VALUE
;
1150 DestAccess
&= ~FILE_WRITE_DATA
;
1152 /* Remember fist failure in the path */
1153 if (NT_SUCCESS(OldStatus
))
1159 /* Get the creation time from source */
1160 Status
= NtQueryInformationFile(SourceHandle
,
1163 sizeof(SourceInformation
),
1164 FileBasicInformation
);
1165 if (NT_SUCCESS(Status
))
1167 /* Then, prepare to set it for destination */
1168 RtlZeroMemory(&DestinationInformation
, sizeof(DestinationInformation
));
1169 DestinationInformation
.CreationTime
.QuadPart
= SourceInformation
.CreationTime
.QuadPart
;
1171 /* And set it, that's all folks! */
1172 Status
= NtSetInformationFile(DestinationHandle
,
1174 &DestinationInformation
,
1175 sizeof(DestinationInformation
),
1176 FileBasicInformation
);
1179 if (!NT_SUCCESS(Status
))
1181 if (!(dwFlags
& PRIV_ALLOW_NON_TRACKABLE
))
1186 /* Remember the failure for later notification */
1187 if (NT_SUCCESS(OldStatus
))
1193 /* If we could open with FILE_WRITE_DATA both source and destination,
1196 if (DestAccess
& FILE_WRITE_DATA
&& SourceAccess
& FILE_WRITE_DATA
)
1198 Status
= BasepNotifyTrackingService(&SourceHandle
,
1199 &ObjectAttributesSource
,
1202 #if 1 // ReactOS HACK
1203 /* FIXME: To be removed once BasepNotifyTrackingService is implemented */
1204 if (Status
== STATUS_NOT_IMPLEMENTED
)
1205 Status
= STATUS_SUCCESS
;
1207 if (!NT_SUCCESS(Status
))
1209 if (dwFlags
& PRIV_ALLOW_NON_TRACKABLE
)
1211 if (NT_SUCCESS(OldStatus
))
1220 RtlFreeHeap(RtlGetProcessHeap(), 0, RelativeSource
);
1222 if (RelativeDestination
)
1223 RtlFreeHeap(RtlGetProcessHeap(), 0, RelativeDestination
);
1227 /* If caller asked for source deletion, if everything succeed, proceed */
1228 if (NT_SUCCESS(Status
) && dwFlags
& PRIV_DELETE_ON_SUCCESS
)
1230 FileDispositionInfo
.DeleteFile
= TRUE
;
1232 Status
= NtSetInformationFile(SourceHandle
,
1234 &FileDispositionInfo
,
1235 sizeof(FileDispositionInfo
),
1236 FileDispositionInformation
);
1239 /* Cleanup/close portion */
1240 if (DestinationHandle
!= INVALID_HANDLE_VALUE
)
1242 NtClose(DestinationHandle
);
1245 if (SourceHandle
!= INVALID_HANDLE_VALUE
)
1247 NtClose(SourceHandle
);
1250 /* Set last error if any, and quit */
1251 if (NT_SUCCESS(Status
))
1253 if (!NT_SUCCESS(OldStatus
))
1255 BaseSetLastNTError(OldStatus
);
1260 BaseSetLastNTError(Status
);
1263 return NT_SUCCESS(Status
);