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 BasepGetComputerNameFromNtPath(IN PUNICODE_STRING NewPath
,
187 OUT PWSTR ComputerName
,
188 IN OUT PULONG ComputerNameLength
)
192 PWSTR AbsolutePath
, EndOfName
;
193 USHORT AbsolutePathLength
, NameLength
;
194 WCHAR TargetDevice
[0x105];
195 WCHAR DeviceName
[] = {'A', ':', '\0'}; /* Init to something, will be set later */
196 UNICODE_STRING UncString
= RTL_CONSTANT_STRING(L
"\\??\\UNC\\");
197 UNICODE_STRING GlobalString
= RTL_CONSTANT_STRING(L
"\\??\\");
199 DPRINT("BasepGetComputerNameFromNtPath(%wZ, %p, %p, %lu)\n", NewPath
, NewHandle
, ComputerName
, ComputerNameLength
);
201 /* If it's an UNC path */
202 if (RtlPrefixUnicodeString(&UncString
, NewPath
, TRUE
))
204 /* Check for broken caller */
205 if (NewPath
->Length
<= UncString
.Length
)
207 return ERROR_BAD_PATHNAME
;
210 /* Skip UNC prefix */
211 AbsolutePath
= &NewPath
->Buffer
[UncString
.Length
/ sizeof(WCHAR
)];
212 AbsolutePathLength
= NewPath
->Length
- UncString
.Length
;
217 /* Otherwise, we have to be in global (NT path!), with drive letter */
218 else if (RtlPrefixUnicodeString(&GlobalString
, NewPath
, TRUE
) && NewPath
->Buffer
[5] == ':')
220 /* Path is like that: \??\C:\Complete Path\To File.ext */
221 /* Get the letter and upcase it if required */
222 Letter
= NewPath
->Buffer
[4];
223 if (Letter
>= 'a' && Letter
<= 'z')
225 Letter
-= ('a' - 'A');
227 DeviceName
[0] = Letter
;
229 /* Query the associated DOS device */
230 if (!QueryDosDeviceW(DeviceName
, TargetDevice
, ARRAYSIZE(TargetDevice
)))
232 return GetLastError();
235 /* If that's a network share */
236 if (TargetDevice
== wcsstr(TargetDevice
, L
"\\Device\\LanmanRedirector\\;"))
238 /* Path is like that: \Device\LanmanRedirector\;C:0000000000000000\Complete Path\To File.ext */
239 /* Check we have the correct drive letter */
240 if (TargetDevice
[26] == DeviceName
[0] &&
241 TargetDevice
[27] == ':')
243 /* Check for the path begin, computer name is before */
244 PWSTR Path
= wcschr(&TargetDevice
[28], '\\');
247 return ERROR_BAD_PATHNAME
;
250 AbsolutePath
= Path
+ 1;
251 AbsolutePathLength
= sizeof(WCHAR
) * (ARRAYSIZE(TargetDevice
) - (AbsolutePath
- TargetDevice
));
255 return ERROR_BAD_PATHNAME
;
258 /* If it's a local device */
259 else if (TargetDevice
== wcsstr(TargetDevice
, L
"\\Device\\Harddisk")
260 || TargetDevice
== wcsstr(TargetDevice
, L
"\\Device\\CdRom")
261 || TargetDevice
== wcsstr(TargetDevice
, L
"\\Device\\Floppy"))
263 /* Just query the computer name */
264 if (!GetComputerNameW(ComputerName
, ComputerNameLength
))
266 return GetLastError();
269 return ERROR_SUCCESS
;
271 /* If it's a DFS share */
272 else if (TargetDevice
== wcsstr(TargetDevice
, L
"\\Device\\WinDfs\\"))
274 /* Obviously, query DFS */
279 return ERROR_BAD_PATHNAME
;
284 return ERROR_BAD_PATHNAME
;
287 /* Query DFS, currently not implemented - shouldn't be missing in ReactOS yet ;-) */
290 UNIMPLEMENTED_DBGBREAK("Querying DFS not implemented!\n");
292 AbsolutePathLength
= 0;
295 /* Now, properly extract the computer name from the full path */
296 EndOfName
= AbsolutePath
;
297 if (AbsolutePathLength
)
299 for (NameLength
= 0; NameLength
< AbsolutePathLength
; NameLength
+= sizeof(WCHAR
))
301 /* Look for the next \, it will be the end of computer name */
302 if (EndOfName
[0] == '\\')
306 /* Computer name cannot contain ., if we get to that point, something went wrong... */
307 else if (EndOfName
[0] == '.')
309 return ERROR_BAD_PATHNAME
;
316 NameLength
= EndOfName
- AbsolutePath
;
317 /* Check we didn't overflow and that our computer name isn't ill-formed */
318 if (NameLength
>= AbsolutePathLength
|| NameLength
>= MAX_COMPUTERNAME_LENGTH
* sizeof(WCHAR
))
320 return ERROR_BAD_PATHNAME
;
323 /* Check we can fit */
324 if (NameLength
+ sizeof(UNICODE_NULL
) > *ComputerNameLength
* sizeof(WCHAR
))
326 return ERROR_BUFFER_OVERFLOW
;
329 /* Write, zero and done! */
330 RtlCopyMemory(ComputerName
, AbsolutePath
, NameLength
);
331 *ComputerNameLength
= NameLength
/ sizeof(WCHAR
);
332 ComputerName
[NameLength
/ sizeof(WCHAR
)] = UNICODE_NULL
;
334 return ERROR_SUCCESS
;
343 BasepNotifyTrackingService(IN OUT PHANDLE ExistingHandle
,
344 IN POBJECT_ATTRIBUTES ObjectAttributes
,
346 IN PUNICODE_STRING NewPath
)
349 ULONG ComputerNameLength
, FileAttributes
;
350 WCHAR ComputerName
[MAX_COMPUTERNAME_LENGTH
+ 1];
351 OEM_STRING ComputerNameStringA
;
352 CHAR ComputerNameStringBuffer
[0x105];
353 UNICODE_STRING ComputerNameStringW
;
354 IO_STATUS_BLOCK IoStatusBlock
;
355 FILE_BASIC_INFORMATION FileBasicInfo
;
359 FILE_TRACKING_INFORMATION
;
360 CHAR Buffer
[(MAX_COMPUTERNAME_LENGTH
+ 1) * sizeof(WCHAR
)];
363 DPRINT("BasepNotifyTrackingService(%p, %p, %p, %wZ)\n", *ExistingHandle
, ObjectAttributes
, NewHandle
, NewPath
);
365 Status
= STATUS_SUCCESS
;
366 ComputerNameLength
= ARRAYSIZE(ComputerName
);
368 /* Attempt to get computer name of target handle */
369 if (BasepGetComputerNameFromNtPath(NewPath
, NewHandle
, ComputerName
, &ComputerNameLength
))
371 /* If we failed to get it, we will just notify with the handle */
372 FileTrackingInfo
.ObjectInformationLength
= 0;
376 /* Convert the retrieved computer name to ANSI and attach it to the notification */
377 ComputerNameStringA
.Length
= 0;
378 ComputerNameStringA
.MaximumLength
= ARRAYSIZE(ComputerNameStringBuffer
);
379 ComputerNameStringA
.Buffer
= ComputerNameStringBuffer
;
381 RtlInitUnicodeString(&ComputerNameStringW
, ComputerName
);
382 Status
= RtlUnicodeStringToOemString(&ComputerNameStringA
, &ComputerNameStringW
, 0);
383 if (!NT_SUCCESS(Status
))
388 RtlCopyMemory(FileTrackingInfo
.ObjectInformation
, ComputerNameStringA
.Buffer
, ComputerNameStringA
.Length
);
389 FileTrackingInfo
.ObjectInformation
[ComputerNameStringA
.Length
] = 0;
390 FileTrackingInfo
.ObjectInformationLength
= ComputerNameStringA
.Length
+ 1;
393 /* Attach the handle we moved */
394 FileTrackingInfo
.DestinationFile
= NewHandle
;
397 Status
= NtSetInformationFile(*ExistingHandle
,
400 sizeof(FileTrackingInfo
),
401 FileTrackingInformation
);
402 if (Status
!= STATUS_ACCESS_DENIED
)
407 /* If we get here, we got access denied error, this comes from a
408 * read-only flag. So, close the file, in order to reopen it with enough
409 * rights to remove said flag and reattempt notification
411 CloseHandle(*ExistingHandle
);
413 /* Reopen it, to be able to change the destination file attributes */
414 Status
= NtOpenFile(ExistingHandle
,
415 SYNCHRONIZE
| FILE_READ_ATTRIBUTES
| FILE_WRITE_ATTRIBUTES
,
418 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
,
419 FILE_SYNCHRONOUS_IO_NONALERT
);
420 if (!NT_SUCCESS(Status
))
422 *ExistingHandle
= INVALID_HANDLE_VALUE
;
426 /* Get the file attributes */
427 Status
= NtQueryInformationFile(*ExistingHandle
,
430 sizeof(FileBasicInfo
),
431 FileBasicInformation
);
432 if (!NT_SUCCESS(Status
))
437 /* Get rid of the read only flag */
438 FileAttributes
= FileBasicInfo
.FileAttributes
& ~FILE_ATTRIBUTE_READONLY
;
439 RtlZeroMemory(&FileBasicInfo
, sizeof(FileBasicInfo
));
440 FileBasicInfo
.FileAttributes
= FileAttributes
;
443 Status
= NtSetInformationFile(*ExistingHandle
,
446 sizeof(FileBasicInfo
),
447 FileBasicInformation
);
448 if (!NT_SUCCESS(Status
))
453 /* Now, reopen with maximum accesses to notify */
454 Status
= NtOpenFile(&hFullWrite
,
455 GENERIC_WRITE
| SYNCHRONIZE
,
458 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
,
459 FILE_SYNCHRONOUS_IO_NONALERT
);
460 if (NT_SUCCESS(Status
))
462 NtClose(*ExistingHandle
);
463 *ExistingHandle
= hFullWrite
;
465 /* Full success, notify! */
466 Status
= NtSetInformationFile(*ExistingHandle
,
469 sizeof(FileTrackingInfo
),
470 FileTrackingInformation
);
473 /* If opening with full access failed or if notify failed, restore read-only */
474 if (!NT_SUCCESS(Status
))
476 FileBasicInfo
.FileAttributes
|= FILE_ATTRIBUTE_READONLY
;
478 Status
= NtSetInformationFile(*ExistingHandle
,
481 sizeof(FileBasicInfo
),
482 FileBasicInformation
);
495 BasepOpenFileForMove(IN LPCWSTR File
,
496 OUT PUNICODE_STRING RelativeNtName
,
498 OUT PHANDLE FileHandle
,
499 OUT POBJECT_ATTRIBUTES ObjectAttributes
,
500 IN ACCESS_MASK DesiredAccess
,
501 IN ULONG ShareAccess
,
502 IN ULONG OpenOptions
)
504 RTL_RELATIVE_NAME_U RelativeName
;
506 IO_STATUS_BLOCK IoStatusBlock
;
507 FILE_ATTRIBUTE_TAG_INFORMATION TagInfo
;
508 ULONG IntShareAccess
;
509 BOOLEAN HasRelative
= FALSE
;
514 RelativeNtName
->Length
=
515 RelativeNtName
->MaximumLength
= 0;
516 RelativeNtName
->Buffer
= NULL
;
519 if (!RtlDosPathNameToRelativeNtPathName_U(File
, RelativeNtName
, NULL
, &RelativeName
))
521 Status
= STATUS_OBJECT_PATH_NOT_FOUND
;
526 *NtName
= RelativeNtName
->Buffer
;
528 if (RelativeName
.RelativeName
.Length
)
530 RelativeNtName
->Length
= RelativeName
.RelativeName
.Length
;
531 RelativeNtName
->MaximumLength
= RelativeName
.RelativeName
.MaximumLength
;
532 RelativeNtName
->Buffer
= RelativeName
.RelativeName
.Buffer
;
536 RelativeName
.ContainingDirectory
= 0;
539 InitializeObjectAttributes(ObjectAttributes
,
541 OBJ_CASE_INSENSITIVE
,
542 RelativeName
.ContainingDirectory
,
544 /* Force certain flags here, given ops we'll do */
545 IntShareAccess
= ShareAccess
| FILE_SHARE_READ
| FILE_SHARE_WRITE
;
546 OpenOptions
|= FILE_OPEN_FOR_BACKUP_INTENT
| FILE_SYNCHRONOUS_IO_NONALERT
;
548 /* We'll try to read reparse tag */
549 Status
= NtOpenFile(FileHandle
,
550 DesiredAccess
| FILE_READ_ATTRIBUTES
| SYNCHRONIZE
,
554 OpenOptions
| FILE_OPEN_REPARSE_POINT
);
555 if (NT_SUCCESS(Status
))
557 /* Attempt the read */
558 Status
= NtQueryInformationFile(*FileHandle
,
561 sizeof(FILE_ATTRIBUTE_TAG_INFORMATION
),
562 FileAttributeTagInformation
);
564 /* Return if failure with a status that wouldn't mean the FSD cannot support reparse points */
565 if (!NT_SUCCESS(Status
) &&
566 (Status
!= STATUS_NOT_IMPLEMENTED
&& Status
!= STATUS_INVALID_PARAMETER
))
571 if (NT_SUCCESS(Status
))
573 /* This cannot happen on mount points */
574 if (TagInfo
.FileAttributes
& FILE_ATTRIBUTE_DEVICE
||
575 TagInfo
.ReparseTag
== IO_REPARSE_TAG_MOUNT_POINT
)
581 NtClose(*FileHandle
);
582 *FileHandle
= INVALID_HANDLE_VALUE
;
584 IntShareAccess
= ShareAccess
| FILE_SHARE_READ
| FILE_SHARE_DELETE
;
586 else if (Status
== STATUS_INVALID_PARAMETER
)
588 IntShareAccess
= ShareAccess
| FILE_SHARE_READ
| FILE_SHARE_WRITE
;
595 /* Reattempt to open normally, following reparse point if needed */
596 Status
= NtOpenFile(FileHandle
,
597 DesiredAccess
| SYNCHRONIZE
,
607 RtlReleaseRelativeName(&RelativeName
);
621 BasepMoveFileCopyProgress(IN LARGE_INTEGER TotalFileSize
,
622 IN LARGE_INTEGER TotalBytesTransferred
,
623 IN LARGE_INTEGER StreamSize
,
624 IN LARGE_INTEGER StreamBytesTransferred
,
625 IN DWORD dwStreamNumber
,
626 IN DWORD dwCallbackReason
,
627 IN HANDLE hSourceFile
,
628 IN HANDLE hDestinationFile
,
629 IN LPVOID lpData OPTIONAL
)
632 PCOPY_PROGRESS_CONTEXT Context
= (PCOPY_PROGRESS_CONTEXT
)lpData
;
634 if (Context
->Flags
& MOVEFILE_WRITE_THROUGH
)
636 if (!dwCallbackReason
)
638 if (StreamBytesTransferred
.QuadPart
== StreamSize
.QuadPart
)
640 FlushFileBuffers(hDestinationFile
);
645 if (Context
->UserRoutine
)
647 Ret
= Context
->UserRoutine(TotalFileSize
,
648 TotalBytesTransferred
,
650 StreamBytesTransferred
,
667 MoveFileWithProgressW(IN LPCWSTR lpExistingFileName
,
668 IN LPCWSTR lpNewFileName
,
669 IN LPPROGRESS_ROUTINE lpProgressRoutine
,
675 IO_STATUS_BLOCK IoStatusBlock
;
676 COPY_PROGRESS_CONTEXT CopyContext
;
677 OBJECT_ATTRIBUTES ObjectAttributes
;
678 PFILE_RENAME_INFORMATION RenameInfo
;
679 UNICODE_STRING NewPathU
, ExistingPathU
;
680 FILE_ATTRIBUTE_TAG_INFORMATION FileAttrTagInfo
;
681 HANDLE SourceHandle
= INVALID_HANDLE_VALUE
, NewHandle
, ExistingHandle
;
682 BOOL Ret
= FALSE
, ReplaceIfExists
, DelayUntilReboot
, AttemptReopenWithoutReparse
;
684 DPRINT("MoveFileWithProgressW(%S, %S, %p, %p, %x)\n", lpExistingFileName
, lpNewFileName
, lpProgressRoutine
, lpData
, dwFlags
);
686 NewPathU
.Buffer
= NULL
;
687 ExistingPathU
.Buffer
= NULL
;
691 /* Don't allow renaming to a disk */
692 if (lpNewFileName
&& RtlIsDosDeviceName_U(lpNewFileName
))
694 BaseSetLastNTError(STATUS_OBJECT_NAME_COLLISION
);
698 ReplaceIfExists
= !!(dwFlags
& MOVEFILE_REPLACE_EXISTING
);
701 if (!RtlDosPathNameToNtPathName_U(lpExistingFileName
, &ExistingPathU
, NULL
, NULL
))
703 BaseSetLastNTError(STATUS_OBJECT_PATH_NOT_FOUND
);
708 DelayUntilReboot
= !!(dwFlags
& MOVEFILE_DELAY_UNTIL_REBOOT
);
709 if (DelayUntilReboot
&& (dwFlags
& MOVEFILE_CREATE_HARDLINK
))
711 BaseSetLastNTError(STATUS_INVALID_PARAMETER
);
715 /* Unless we manage a proper opening, we'll attempt to reopen without reparse support */
716 AttemptReopenWithoutReparse
= TRUE
;
717 InitializeObjectAttributes(&ObjectAttributes
,
719 OBJ_CASE_INSENSITIVE
,
722 /* Attempt to open source file */
723 Status
= NtOpenFile(&SourceHandle
,
724 FILE_READ_ATTRIBUTES
| DELETE
| SYNCHRONIZE
,
727 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
,
728 FILE_OPEN_FOR_BACKUP_INTENT
| ((dwFlags
& MOVEFILE_WRITE_THROUGH
) ? FILE_WRITE_THROUGH
: 0));
729 if (!NT_SUCCESS(Status
))
731 /* If we failed and the file doesn't exist, don't attempt to reopen without reparse */
732 if (DelayUntilReboot
&&
733 (Status
== STATUS_SHARING_VIOLATION
|| Status
== STATUS_OBJECT_NAME_NOT_FOUND
|| Status
== STATUS_OBJECT_PATH_NOT_FOUND
))
735 /* Here we don't fail completely, as we postpone the operation to reboot
736 * File might exist afterwards, and we don't need a handle here
738 SourceHandle
= INVALID_HANDLE_VALUE
;
739 AttemptReopenWithoutReparse
= FALSE
;
741 /* If we failed for any reason than unsupported reparse, fail completely */
742 else if (Status
!= STATUS_INVALID_PARAMETER
)
744 BaseSetLastNTError(Status
);
750 /* We managed to open, so query information */
751 Status
= NtQueryInformationFile(SourceHandle
,
754 sizeof(FILE_ATTRIBUTE_TAG_INFORMATION
),
755 FileAttributeTagInformation
);
756 if (!NT_SUCCESS(Status
))
758 /* Do not tolerate any other error than something related to not supported operation */
759 if (Status
!= STATUS_NOT_IMPLEMENTED
&& Status
!= STATUS_INVALID_PARAMETER
)
761 BaseSetLastNTError(Status
);
765 /* Not a reparse point, no need to reopen, it's fine */
766 AttemptReopenWithoutReparse
= FALSE
;
768 /* Validate the reparse point (do we support it?) */
769 else if (FileAttrTagInfo
.FileAttributes
& FILE_ATTRIBUTE_REPARSE_POINT
&&
770 FileAttrTagInfo
.ReparseTag
!= IO_REPARSE_TAG_MOUNT_POINT
)
772 NtClose(SourceHandle
);
773 SourceHandle
= INVALID_HANDLE_VALUE
;
777 /* Simply reopen if required */
778 if (AttemptReopenWithoutReparse
)
780 Status
= NtOpenFile(&SourceHandle
,
781 DELETE
| SYNCHRONIZE
,
784 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
785 ((dwFlags
& MOVEFILE_WRITE_THROUGH
) ? FILE_WRITE_THROUGH
: 0));
786 if (!NT_SUCCESS(Status
))
788 BaseSetLastNTError(Status
);
793 /* Nullify string if we're to use it */
794 if (DelayUntilReboot
&& !lpNewFileName
)
796 RtlInitUnicodeString(&NewPathU
, 0);
798 /* Check whether path exists */
799 else if (!RtlDosPathNameToNtPathName_U(lpNewFileName
, &NewPathU
, 0, 0))
801 BaseSetLastNTError(STATUS_OBJECT_PATH_NOT_FOUND
);
805 /* Handle postponed renaming */
806 if (DelayUntilReboot
)
808 /* If new file exists and we're allowed to replace, then mark the path with ! */
809 if (ReplaceIfExists
&& NewPathU
.Length
)
811 NewBuffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, NewPathU
.Length
+ sizeof(WCHAR
));
812 if (NewBuffer
== NULL
)
814 BaseSetLastNTError(STATUS_NO_MEMORY
);
819 RtlCopyMemory(&NewBuffer
[1], NewPathU
.Buffer
, NewPathU
.Length
);
820 NewPathU
.Length
+= sizeof(WCHAR
);
821 NewPathU
.MaximumLength
+= sizeof(WCHAR
);
822 RtlFreeHeap(RtlGetProcessHeap(), 0, NewPathU
.Buffer
);
823 NewPathU
.Buffer
= NewBuffer
;
826 /* Check whether 'copy' renaming is allowed if required */
827 if (RtlDetermineDosPathNameType_U(lpExistingFileName
) == RtlPathTypeUncAbsolute
|| dwFlags
& MOVEFILE_COPY_ALLOWED
)
829 Status
= STATUS_INVALID_PARAMETER
;
833 /* First, probe 2nd key to see whether it exists - if so, it will be appended there */
834 Status
= BasepMoveFileDelayed(&ExistingPathU
, &NewPathU
, 2, FALSE
);
835 if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
837 /* If doesn't exist, append to first key first, creating it if it doesn't exist */
838 Status
= BasepMoveFileDelayed(&ExistingPathU
, &NewPathU
, 1, TRUE
);
840 if (Status
== STATUS_INSUFFICIENT_RESOURCES
)
842 /* If it failed because it's too big, then create 2nd key and put it there */
843 Status
= BasepMoveFileDelayed(&ExistingPathU
, &NewPathU
, 2, TRUE
);
848 /* If we failed at some point, return the error */
849 if (!NT_SUCCESS(Status
))
851 BaseSetLastNTError(Status
);
859 /* At that point, we MUST have a source handle */
860 ASSERT(SourceHandle
!= INVALID_HANDLE_VALUE
);
862 /* Allocate renaming buffer and fill it */
863 RenameInfo
= RtlAllocateHeap(RtlGetProcessHeap(), 0, NewPathU
.Length
+ sizeof(FILE_RENAME_INFORMATION
));
864 if (RenameInfo
== NULL
)
866 BaseSetLastNTError(STATUS_NO_MEMORY
);
870 RtlCopyMemory(&RenameInfo
->FileName
, NewPathU
.Buffer
, NewPathU
.Length
);
871 RenameInfo
->ReplaceIfExists
= ReplaceIfExists
;
872 RenameInfo
->RootDirectory
= 0;
873 RenameInfo
->FileNameLength
= NewPathU
.Length
;
875 /* Attempt to rename the file */
876 Status
= NtSetInformationFile(SourceHandle
,
879 NewPathU
.Length
+ sizeof(FILE_RENAME_INFORMATION
),
880 ((dwFlags
& MOVEFILE_CREATE_HARDLINK
) ? FileLinkInformation
: FileRenameInformation
));
881 RtlFreeHeap(RtlGetProcessHeap(), 0, RenameInfo
);
882 if (NT_SUCCESS(Status
))
884 /* If it succeed, all fine, quit */
888 /* If we failed for any other reason than not the same device, fail
889 * If we failed because of different devices, only allow renaming if user allowed copy
891 if (Status
!= STATUS_NOT_SAME_DEVICE
|| !(dwFlags
& MOVEFILE_COPY_ALLOWED
))
893 /* ReactOS hack! To be removed once all FSD have proper renaming support
894 * Just leave status to error and leave
896 if (Status
== STATUS_NOT_IMPLEMENTED
)
898 DPRINT1("Forcing copy, renaming not supported by FSD\n");
902 BaseSetLastNTError(Status
);
907 /* Close source file */
908 NtClose(SourceHandle
);
909 SourceHandle
= INVALID_HANDLE_VALUE
;
911 /* Issue the copy of the file */
912 CopyContext
.Flags
= dwFlags
;
913 CopyContext
.UserRoutine
= lpProgressRoutine
;
914 CopyContext
.UserData
= lpData
;
915 NewHandle
= INVALID_HANDLE_VALUE
;
916 ExistingHandle
= INVALID_HANDLE_VALUE
;
918 Ret
= BasepCopyFileExW(lpExistingFileName
,
920 BasepMoveFileCopyProgress
,
923 (ReplaceIfExists
== 0) | COPY_FILE_OPEN_SOURCE_FOR_WRITE
,
929 /* If it failed, don't leak any handle */
930 if (ExistingHandle
!= INVALID_HANDLE_VALUE
)
932 CloseHandle(ExistingHandle
);
933 ExistingHandle
= INVALID_HANDLE_VALUE
;
936 else if (ExistingHandle
!= INVALID_HANDLE_VALUE
)
938 if (NewHandle
!= INVALID_HANDLE_VALUE
)
940 /* If copying succeed, notify */
941 Status
= BasepNotifyTrackingService(&ExistingHandle
, &ObjectAttributes
, NewHandle
, &NewPathU
);
942 if (!NT_SUCCESS(Status
))
944 /* Fail in case it had to succeed */
945 if (dwFlags
& MOVEFILE_FAIL_IF_NOT_TRACKABLE
)
947 if (NewHandle
!= INVALID_HANDLE_VALUE
)
948 CloseHandle(NewHandle
);
949 NewHandle
= INVALID_HANDLE_VALUE
;
950 DeleteFileW(lpNewFileName
);
952 BaseSetLastNTError(Status
);
957 CloseHandle(ExistingHandle
);
958 ExistingHandle
= INVALID_HANDLE_VALUE
;
961 /* In case copy worked, close file */
962 if (NewHandle
!= INVALID_HANDLE_VALUE
)
964 CloseHandle(NewHandle
);
965 NewHandle
= INVALID_HANDLE_VALUE
;
968 /* If it succeed, delete source file */
971 if (!DeleteFileW(lpExistingFileName
))
973 /* Reset file attributes if required */
974 SetFileAttributesW(lpExistingFileName
, FILE_ATTRIBUTE_NORMAL
);
975 DeleteFileW(lpExistingFileName
);
981 if (SourceHandle
!= INVALID_HANDLE_VALUE
)
982 NtClose(SourceHandle
);
984 RtlFreeHeap(RtlGetProcessHeap(), 0, ExistingPathU
.Buffer
);
985 RtlFreeHeap(RtlGetProcessHeap(), 0, NewPathU
.Buffer
);
998 MoveFileWithProgressA(IN LPCSTR lpExistingFileName
,
999 IN LPCSTR lpNewFileName OPTIONAL
,
1000 IN LPPROGRESS_ROUTINE lpProgressRoutine OPTIONAL
,
1001 IN LPVOID lpData OPTIONAL
,
1005 UNICODE_STRING ExistingFileNameW
, NewFileNameW
;
1007 if (!Basep8BitStringToDynamicUnicodeString(&ExistingFileNameW
, lpExistingFileName
))
1014 if (!Basep8BitStringToDynamicUnicodeString(&NewFileNameW
, lpNewFileName
))
1016 RtlFreeUnicodeString(&ExistingFileNameW
);
1022 NewFileNameW
.Buffer
= NULL
;
1025 Ret
= MoveFileWithProgressW(ExistingFileNameW
.Buffer
, NewFileNameW
.Buffer
, lpProgressRoutine
, lpData
, dwFlags
);
1027 RtlFreeUnicodeString(&ExistingFileNameW
);
1028 RtlFreeUnicodeString(&NewFileNameW
);
1039 MoveFileW(IN LPCWSTR lpExistingFileName
,
1040 IN LPCWSTR lpNewFileName
)
1042 return MoveFileWithProgressW(lpExistingFileName
,
1046 MOVEFILE_COPY_ALLOWED
);
1055 MoveFileExW(IN LPCWSTR lpExistingFileName
,
1056 IN LPCWSTR lpNewFileName OPTIONAL
,
1059 return MoveFileWithProgressW(lpExistingFileName
,
1072 MoveFileA(IN LPCSTR lpExistingFileName
,
1073 IN LPCSTR lpNewFileName
)
1075 return MoveFileWithProgressA(lpExistingFileName
,
1079 MOVEFILE_COPY_ALLOWED
);
1088 MoveFileExA(IN LPCSTR lpExistingFileName
,
1089 IN LPCSTR lpNewFileName OPTIONAL
,
1092 return MoveFileWithProgressA(lpExistingFileName
,
1104 ReplaceFileA(IN LPCSTR lpReplacedFileName
,
1105 IN LPCSTR lpReplacementFileName
,
1106 IN LPCSTR lpBackupFileName OPTIONAL
,
1107 IN DWORD dwReplaceFlags
,
1108 IN LPVOID lpExclude
,
1109 IN LPVOID lpReserved
)
1112 UNICODE_STRING ReplacedFileNameW
, ReplacementFileNameW
, BackupFileNameW
;
1114 if (!lpReplacedFileName
|| !lpReplacementFileName
|| lpExclude
|| lpReserved
|| dwReplaceFlags
& ~(REPLACEFILE_WRITE_THROUGH
| REPLACEFILE_IGNORE_MERGE_ERRORS
))
1116 SetLastError(ERROR_INVALID_PARAMETER
);
1120 if (!Basep8BitStringToDynamicUnicodeString(&ReplacedFileNameW
, lpReplacedFileName
))
1125 if (!Basep8BitStringToDynamicUnicodeString(&ReplacementFileNameW
, lpReplacementFileName
))
1127 RtlFreeUnicodeString(&ReplacedFileNameW
);
1131 if (lpBackupFileName
)
1133 if (!Basep8BitStringToDynamicUnicodeString(&BackupFileNameW
, lpBackupFileName
))
1135 RtlFreeUnicodeString(&ReplacementFileNameW
);
1136 RtlFreeUnicodeString(&ReplacedFileNameW
);
1142 BackupFileNameW
.Buffer
= NULL
;
1145 Ret
= ReplaceFileW(ReplacedFileNameW
.Buffer
, ReplacementFileNameW
.Buffer
, BackupFileNameW
.Buffer
, dwReplaceFlags
, 0, 0);
1147 if (lpBackupFileName
)
1149 RtlFreeUnicodeString(&BackupFileNameW
);
1151 RtlFreeUnicodeString(&ReplacementFileNameW
);
1152 RtlFreeUnicodeString(&ReplacedFileNameW
);
1163 LPCWSTR lpReplacedFileName
,
1164 LPCWSTR lpReplacementFileName
,
1165 LPCWSTR lpBackupFileName
,
1166 DWORD dwReplaceFlags
,
1171 HANDLE hReplaced
= NULL
, hReplacement
= NULL
;
1172 UNICODE_STRING NtReplacedName
= { 0, 0, NULL
};
1173 UNICODE_STRING NtReplacementName
= { 0, 0, NULL
};
1174 DWORD Error
= ERROR_SUCCESS
;
1177 IO_STATUS_BLOCK IoStatusBlock
;
1178 OBJECT_ATTRIBUTES ObjectAttributes
;
1179 PVOID Buffer
= NULL
;
1182 FIXME("Ignoring flags %x\n", dwReplaceFlags
);
1184 /* First two arguments are mandatory */
1185 if (!lpReplacedFileName
|| !lpReplacementFileName
)
1187 SetLastError(ERROR_INVALID_PARAMETER
);
1192 if(lpBackupFileName
)
1194 if(!CopyFileW(lpReplacedFileName
, lpBackupFileName
, FALSE
))
1196 Error
= GetLastError();
1201 /* Open the "replaced" file for reading and writing */
1202 if (!(RtlDosPathNameToNtPathName_U(lpReplacedFileName
, &NtReplacedName
, NULL
, NULL
)))
1204 Error
= ERROR_PATH_NOT_FOUND
;
1208 InitializeObjectAttributes(&ObjectAttributes
,
1210 OBJ_CASE_INSENSITIVE
,
1214 Status
= NtOpenFile(&hReplaced
,
1215 GENERIC_READ
| GENERIC_WRITE
| DELETE
| SYNCHRONIZE
| WRITE_DAC
,
1218 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
,
1219 FILE_SYNCHRONOUS_IO_NONALERT
| FILE_NON_DIRECTORY_FILE
);
1221 if (!NT_SUCCESS(Status
))
1223 if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
1224 Error
= ERROR_FILE_NOT_FOUND
;
1226 Error
= ERROR_UNABLE_TO_REMOVE_REPLACED
;
1231 SetEndOfFile(hReplaced
) ;
1234 * Open the replacement file for reading, writing, and deleting
1235 * (deleting is needed when finished)
1237 if (!(RtlDosPathNameToNtPathName_U(lpReplacementFileName
, &NtReplacementName
, NULL
, NULL
)))
1239 Error
= ERROR_PATH_NOT_FOUND
;
1243 InitializeObjectAttributes(&ObjectAttributes
,
1245 OBJ_CASE_INSENSITIVE
,
1249 Status
= NtOpenFile(&hReplacement
,
1250 GENERIC_READ
| DELETE
| SYNCHRONIZE
,
1254 FILE_SYNCHRONOUS_IO_NONALERT
| FILE_NON_DIRECTORY_FILE
| FILE_DELETE_ON_CLOSE
);
1256 if (!NT_SUCCESS(Status
))
1258 Error
= RtlNtStatusToDosError(Status
);
1262 Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY
, 0x10000) ;
1265 Error
= ERROR_NOT_ENOUGH_MEMORY
;
1268 while (Status
!= STATUS_END_OF_FILE
)
1270 Status
= NtReadFile(hReplacement
, NULL
, NULL
, NULL
, &IoStatusBlock
, Buffer
, 0x10000, NULL
, NULL
) ;
1271 if (NT_SUCCESS(Status
))
1273 Status
= NtWriteFile(hReplaced
, NULL
, NULL
, NULL
, &IoStatusBlock
, Buffer
,
1274 IoStatusBlock
.Information
, NULL
, NULL
) ;
1275 if (!NT_SUCCESS(Status
))
1277 Error
= RtlNtStatusToDosError(Status
);
1281 else if (Status
!= STATUS_END_OF_FILE
)
1283 Error
= RtlNtStatusToDosError(Status
);
1290 /* Perform resource cleanup */
1292 if (hReplaced
) NtClose(hReplaced
);
1293 if (hReplacement
) NtClose(hReplacement
);
1294 if (Buffer
) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
1296 if (NtReplacementName
.Buffer
)
1297 RtlFreeHeap(GetProcessHeap(), 0, NtReplacementName
.Buffer
);
1298 if (NtReplacedName
.Buffer
)
1299 RtlFreeHeap(GetProcessHeap(), 0, NtReplacedName
.Buffer
);
1301 /* If there was an error, set the error code */
1304 TRACE("ReplaceFileW failed (error=%lu)\n", Error
);
1305 SetLastError(Error
);
1316 PrivMoveFileIdentityW(IN LPCWSTR lpSource
, IN LPCWSTR lpDestination
, IN DWORD dwFlags
)
1318 ACCESS_MASK SourceAccess
;
1319 UNICODE_STRING NtSource
, NtDestination
;
1320 LPWSTR RelativeSource
, RelativeDestination
;
1321 HANDLE SourceHandle
, DestinationHandle
;
1322 OBJECT_ATTRIBUTES ObjectAttributesSource
, ObjectAttributesDestination
;
1323 NTSTATUS Status
, OldStatus
= STATUS_SUCCESS
;
1324 ACCESS_MASK DestAccess
;
1325 IO_STATUS_BLOCK IoStatusBlock
;
1326 FILE_BASIC_INFORMATION SourceInformation
, DestinationInformation
;
1327 FILE_DISPOSITION_INFORMATION FileDispositionInfo
;
1329 DPRINT("PrivMoveFileIdentityW(%S, %S, %x)\n", lpSource
, lpDestination
, dwFlags
);
1331 SourceHandle
= INVALID_HANDLE_VALUE
;
1333 NtSource
.MaximumLength
= 0;
1334 NtSource
.Buffer
= NULL
;
1335 RelativeSource
= NULL
;
1336 DestinationHandle
= INVALID_HANDLE_VALUE
;
1337 NtDestination
.Length
=
1338 NtDestination
.MaximumLength
= 0;
1339 NtDestination
.Buffer
= NULL
;
1340 RelativeDestination
= NULL
;
1342 /* FILE_WRITE_DATA is required for later on notification */
1343 SourceAccess
= FILE_READ_ATTRIBUTES
| FILE_WRITE_DATA
;
1344 if (dwFlags
& PRIV_DELETE_ON_SUCCESS
)
1346 SourceAccess
|= DELETE
;
1351 /* We will loop twice:
1352 * First we attempt to open with FILE_WRITE_DATA for notification
1353 * If it fails and we have flag for non-trackable files, we retry
1354 * without FILE_WRITE_DATA.
1355 * If that one fails, then, we quit for real
1359 Status
= BasepOpenFileForMove(lpSource
,
1363 &ObjectAttributesSource
,
1365 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
,
1366 FILE_OPEN_NO_RECALL
);
1367 if (NT_SUCCESS(Status
))
1372 /* If we already attempted the opening without FILE_WRITE_DATA
1373 * or if we cannot move on non-trackable files, fail.
1375 if (!(SourceAccess
& FILE_WRITE_DATA
) || !(dwFlags
& PRIV_ALLOW_NON_TRACKABLE
))
1382 RtlFreeHeap(RtlGetProcessHeap(), 0, RelativeSource
);
1383 RelativeSource
= NULL
;
1386 if (SourceHandle
!= INVALID_HANDLE_VALUE
)
1388 NtClose(SourceHandle
);
1389 SourceHandle
= INVALID_HANDLE_VALUE
;
1392 SourceAccess
&= ~FILE_WRITE_DATA
;
1394 /* Remember fist failure in the path */
1395 if (NT_SUCCESS(OldStatus
))
1401 DestAccess
= FILE_WRITE_ATTRIBUTES
;
1402 /* If we could preserve FILE_WRITE_DATA for source, attempt to get it for destination
1403 * Still for notification purposes
1405 if (SourceAccess
& FILE_WRITE_DATA
)
1407 DestAccess
|= FILE_WRITE_DATA
;
1410 /* cf comment for first loop */
1413 Status
= BasepOpenFileForMove(lpDestination
,
1415 &RelativeDestination
,
1417 &ObjectAttributesDestination
,
1419 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
,
1420 FILE_OPEN_NO_RECALL
);
1421 if (NT_SUCCESS(Status
))
1426 /* If we already attempted the opening without FILE_WRITE_DATA
1427 * or if we cannot move on non-trackable files, fail.
1429 if (!(DestAccess
& FILE_WRITE_DATA
) || !(dwFlags
& PRIV_ALLOW_NON_TRACKABLE
))
1434 if (RelativeDestination
)
1436 RtlFreeHeap(RtlGetProcessHeap(), 0, RelativeDestination
);
1437 RelativeDestination
= NULL
;
1440 if (DestinationHandle
!= INVALID_HANDLE_VALUE
)
1442 NtClose(DestinationHandle
);
1443 DestinationHandle
= INVALID_HANDLE_VALUE
;
1446 DestAccess
&= ~FILE_WRITE_DATA
;
1448 /* Remember fist failure in the path */
1449 if (NT_SUCCESS(OldStatus
))
1455 /* Get the creation time from source */
1456 Status
= NtQueryInformationFile(SourceHandle
,
1459 sizeof(SourceInformation
),
1460 FileBasicInformation
);
1461 if (NT_SUCCESS(Status
))
1463 /* Then, prepare to set it for destination */
1464 RtlZeroMemory(&DestinationInformation
, sizeof(DestinationInformation
));
1465 DestinationInformation
.CreationTime
.QuadPart
= SourceInformation
.CreationTime
.QuadPart
;
1467 /* And set it, that's all folks! */
1468 Status
= NtSetInformationFile(DestinationHandle
,
1470 &DestinationInformation
,
1471 sizeof(DestinationInformation
),
1472 FileBasicInformation
);
1475 if (!NT_SUCCESS(Status
))
1477 if (!(dwFlags
& PRIV_ALLOW_NON_TRACKABLE
))
1482 /* Remember the failure for later notification */
1483 if (NT_SUCCESS(OldStatus
))
1489 /* If we could open with FILE_WRITE_DATA both source and destination,
1492 if (DestAccess
& FILE_WRITE_DATA
&& SourceAccess
& FILE_WRITE_DATA
)
1494 Status
= BasepNotifyTrackingService(&SourceHandle
,
1495 &ObjectAttributesSource
,
1498 if (!NT_SUCCESS(Status
))
1500 if (dwFlags
& PRIV_ALLOW_NON_TRACKABLE
)
1502 if (NT_SUCCESS(OldStatus
))
1505 /* Reset status, we allow non trackable files */
1506 Status
= STATUS_SUCCESS
;
1514 RtlFreeHeap(RtlGetProcessHeap(), 0, RelativeSource
);
1516 if (RelativeDestination
)
1517 RtlFreeHeap(RtlGetProcessHeap(), 0, RelativeDestination
);
1521 /* If caller asked for source deletion, if everything succeed, proceed */
1522 if (NT_SUCCESS(Status
) && dwFlags
& PRIV_DELETE_ON_SUCCESS
)
1524 FileDispositionInfo
.DeleteFile
= TRUE
;
1526 Status
= NtSetInformationFile(SourceHandle
,
1528 &FileDispositionInfo
,
1529 sizeof(FileDispositionInfo
),
1530 FileDispositionInformation
);
1533 /* Cleanup/close portion */
1534 if (DestinationHandle
!= INVALID_HANDLE_VALUE
)
1536 NtClose(DestinationHandle
);
1539 if (SourceHandle
!= INVALID_HANDLE_VALUE
)
1541 NtClose(SourceHandle
);
1544 /* Set last error if any, and quit */
1545 if (NT_SUCCESS(Status
))
1547 if (!NT_SUCCESS(OldStatus
))
1549 BaseSetLastNTError(OldStatus
);
1554 BaseSetLastNTError(Status
);
1557 return NT_SUCCESS(Status
);