689 lines
23 KiB
C
689 lines
23 KiB
C
/*++
|
|
|
|
Copyright (c) 1990 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
fileopcr.c
|
|
|
|
Abstract:
|
|
|
|
This module implements File open and Create APIs for Win32
|
|
|
|
Author:
|
|
|
|
Mark Lucovsky (markl) 25-Sep-1990
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "basedll.h"
|
|
|
|
#define BASE_COPY_FILE_CHUNK (64*1024)
|
|
|
|
HANDLE
|
|
WINAPI
|
|
CreateFile(
|
|
PCOSTR lpFileName,
|
|
DWORD dwDesiredAccess,
|
|
DWORD dwShareMode,
|
|
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
|
|
DWORD dwCreationDisposition,
|
|
DWORD dwFlagsAndAttributes,
|
|
HANDLE hTemplateFile
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
A file can be created, opened, or truncated, and a handle opened to
|
|
access the new file using CreateFile.
|
|
|
|
This API is used to create or open a file and obtain a handle to it
|
|
that allows reading data, writing data, and moving the file pointer.
|
|
|
|
This API allows the caller to specify the following creation
|
|
dispositions:
|
|
|
|
- Create a new file and fail if the file exists ( CREATE_NEW )
|
|
|
|
- Create a new file and succeed if it exists ( CREATE_ALWAYS )
|
|
|
|
- Open an existing file ( OPEN_EXISTING )
|
|
|
|
- Open and existing file or create it if it does not exist (
|
|
OPEN_ALWAYS )
|
|
|
|
- Truncate and existing file ( TRUNCATE_EXISTING )
|
|
|
|
If this call is successful, a handle is returned that has
|
|
appropriate access to the specified file.
|
|
|
|
If as a result of this call, a file is created,
|
|
|
|
- The attributes of the file are determined by the value of the
|
|
FileAttributes parameter or'd with the FILE_ATTRIBUTE_ARCHIVE bit.
|
|
|
|
- The length of the file will be set to zero.
|
|
|
|
This call is logically equivalent to DOS (int 21h, function 5Bh), or
|
|
DOS (int 21h, function 3Ch) depending on the value of the
|
|
FailIfExists parameter.
|
|
|
|
Arguments:
|
|
|
|
lpFileName - Supplies the file name of the file to open. Depending on
|
|
the value of the FailIfExists parameter, this name may or may
|
|
not already exist.
|
|
|
|
dwDesiredAccess - Supplies the caller's desired access to the file.
|
|
|
|
DesiredAccess Flags:
|
|
|
|
GENERIC_READ - Read access to the file is requested. This
|
|
allows data to be read from the file and the file pointer to
|
|
be modified.
|
|
|
|
GENERIC_WRITE - Write access to the file is requested. This
|
|
allows data to be written to the file and the file pointer to
|
|
be modified.
|
|
|
|
dwShareMode - Supplies a set of flags that indicates how this file is
|
|
to be shared with other openers of the file. A value of zero
|
|
for this parameter indicates no sharing of the file, or
|
|
exclusive access to the file is to occur.
|
|
|
|
ShareMode Flags:
|
|
|
|
FILE_SHARE_READ - Other open operations may be performed on the
|
|
file for read access.
|
|
|
|
FILE_SHARE_WRITE - Other open operations may be performed on the
|
|
file for write access.
|
|
|
|
lpSecurityAttributes - Ignored on xbox
|
|
|
|
dwCreationDisposition - Supplies a creation disposition that
|
|
specifies how this call is to operate. This parameter must be
|
|
one of the following values.
|
|
|
|
dwCreationDisposition Value:
|
|
|
|
CREATE_NEW - Create a new file. If the specified file already
|
|
exists, then fail. The attributes for the new file are what
|
|
is specified in the dwFlagsAndAttributes parameter or'd with
|
|
FILE_ATTRIBUTE_ARCHIVE.
|
|
|
|
CREATE_ALWAYS - Always create the file. If the file already
|
|
exists, then it is overwritten. The attributes for the new
|
|
file are what is specified in the dwFlagsAndAttributes
|
|
parameter or'd with FILE_ATTRIBUTE_ARCHIVE.
|
|
|
|
OPEN_EXISTING - Open the file, but if it does not exist, then
|
|
fail the call.
|
|
|
|
OPEN_ALWAYS - Open the file if it exists. If it does not exist,
|
|
then create the file using the same rules as if the
|
|
disposition were CREATE_NEW.
|
|
|
|
TRUNCATE_EXISTING - Open the file, but if it does not exist,
|
|
then fail the call. Once opened, the file is truncated such
|
|
that its size is zero bytes. This disposition requires that
|
|
the caller open the file with at least GENERIC_WRITE access.
|
|
|
|
dwFlagsAndAttributes - Specifies flags and attributes for the file.
|
|
The attributes are only used when the file is created (as
|
|
opposed to opened or truncated). Any combination of attribute
|
|
flags is acceptable except that all other attribute flags
|
|
override the normal file attribute, FILE_ATTRIBUTE_NORMAL. The
|
|
FILE_ATTRIBUTE_ARCHIVE flag is always implied.
|
|
|
|
dwFlagsAndAttributes Flags:
|
|
|
|
FILE_ATTRIBUTE_NORMAL - A normal file should be created.
|
|
|
|
FILE_ATTRIBUTE_READONLY - A read-only file should be created.
|
|
|
|
FILE_ATTRIBUTE_HIDDEN - A hidden file should be created.
|
|
|
|
FILE_ATTRIBUTE_SYSTEM - A system file should be created.
|
|
|
|
FILE_FLAG_WRITE_THROUGH - Indicates that the system should
|
|
always write through any intermediate cache and go directly
|
|
to the file. The system may still cache writes, but may not
|
|
lazily flush the writes.
|
|
|
|
FILE_FLAG_OVERLAPPED - Indicates that the system should initialize
|
|
the file so that ReadFile and WriteFile operations that may
|
|
take a significant time to complete will return ERROR_IO_PENDING.
|
|
An event will be set to the signalled state when the operation
|
|
completes. When FILE_FLAG_OVERLAPPED is specified the system will
|
|
not maintain the file pointer. The position to read/write from
|
|
is passed to the system as part of the OVERLAPPED structure
|
|
which is an optional parameter to ReadFile and WriteFile.
|
|
|
|
FILE_FLAG_NO_BUFFERING - Indicates that the file is to be opened
|
|
with no intermediate buffering or caching done by the
|
|
system. Reads and writes to the file must be done on sector
|
|
boundries. Buffer addresses for reads and writes must be
|
|
aligned on at least disk sector boundries in memory.
|
|
|
|
FILE_FLAG_RANDOM_ACCESS - Indicates that access to the file may
|
|
be random. The system cache manager may use this to influence
|
|
its caching strategy for this file.
|
|
|
|
FILE_FLAG_SEQUENTIAL_SCAN - Indicates that access to the file
|
|
may be sequential. The system cache manager may use this to
|
|
influence its caching strategy for this file. The file may
|
|
in fact be accessed randomly, but the cache manager may
|
|
optimize its cacheing policy for sequential access.
|
|
|
|
FILE_FLAG_DELETE_ON_CLOSE - Indicates that the file is to be
|
|
automatically deleted when the last handle to it is closed.
|
|
|
|
FILE_FLAG_BACKUP_SEMANTICS - Indicates that the file is being opened
|
|
or created for the purposes of either a backup or a restore
|
|
operation. Thus, the system should make whatever checks are
|
|
appropriate to ensure that the caller is able to override
|
|
whatever security checks have been placed on the file to allow
|
|
this to happen.
|
|
|
|
hTemplateFile - Not supported on xbox (used for EA's)
|
|
|
|
Return Value:
|
|
|
|
Not -1 - Returns an open handle to the specified file. Subsequent
|
|
access to the file is controlled by the DesiredAccess parameter.
|
|
|
|
0xffffffff - The operation failed. Extended error status is available
|
|
using GetLastError.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
OBJECT_ATTRIBUTES Obja;
|
|
HANDLE Handle;
|
|
OBJECT_STRING FileName;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
ULONG CreateDisposition;
|
|
ULONG CreateFlags;
|
|
BOOL EndsInSlash;
|
|
|
|
#if DBG
|
|
if ( NULL != hTemplateFile )
|
|
{
|
|
// xbox doesn't support this parameter because we don't support EA's
|
|
RIP("CreateFile() - invalid parameter (hTemplateFile not supported)");
|
|
}
|
|
#endif // DBG
|
|
|
|
switch ( dwCreationDisposition ) {
|
|
case CREATE_NEW :
|
|
CreateDisposition = FILE_CREATE;
|
|
break;
|
|
case CREATE_ALWAYS :
|
|
CreateDisposition = FILE_OVERWRITE_IF;
|
|
break;
|
|
case OPEN_EXISTING :
|
|
CreateDisposition = FILE_OPEN;
|
|
break;
|
|
case OPEN_ALWAYS :
|
|
CreateDisposition = FILE_OPEN_IF;
|
|
break;
|
|
case TRUNCATE_EXISTING :
|
|
CreateDisposition = FILE_OVERWRITE;
|
|
if ( !(dwDesiredAccess & GENERIC_WRITE) ) {
|
|
XapiSetLastNTError(STATUS_INVALID_PARAMETER);
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
break;
|
|
default :
|
|
XapiSetLastNTError(STATUS_INVALID_PARAMETER);
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
RtlInitObjectString(&FileName,lpFileName);
|
|
|
|
if ( FileName.Length > 1 &&
|
|
lpFileName[(FileName.Length / sizeof(OCHAR))-1] == (OCHAR)'\\' ) {
|
|
EndsInSlash = TRUE;
|
|
}
|
|
else {
|
|
EndsInSlash = FALSE;
|
|
}
|
|
|
|
InitializeObjectAttributes(
|
|
&Obja,
|
|
&FileName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
ObDosDevicesDirectory(),
|
|
NULL
|
|
);
|
|
|
|
CreateFlags = 0;
|
|
CreateFlags |= (dwFlagsAndAttributes & FILE_FLAG_NO_BUFFERING ? FILE_NO_INTERMEDIATE_BUFFERING : 0 );
|
|
CreateFlags |= (dwFlagsAndAttributes & FILE_FLAG_WRITE_THROUGH ? FILE_WRITE_THROUGH : 0 );
|
|
CreateFlags |= (dwFlagsAndAttributes & FILE_FLAG_OVERLAPPED ? 0 : FILE_SYNCHRONOUS_IO_NONALERT );
|
|
CreateFlags |= (dwFlagsAndAttributes & FILE_FLAG_SEQUENTIAL_SCAN ? FILE_SEQUENTIAL_ONLY : 0 );
|
|
CreateFlags |= (dwFlagsAndAttributes & FILE_FLAG_RANDOM_ACCESS ? FILE_RANDOM_ACCESS : 0 );
|
|
CreateFlags |= (dwFlagsAndAttributes & FILE_FLAG_BACKUP_SEMANTICS ? FILE_OPEN_FOR_BACKUP_INTENT : 0 );
|
|
|
|
if ( dwFlagsAndAttributes & FILE_FLAG_DELETE_ON_CLOSE ) {
|
|
CreateFlags |= FILE_DELETE_ON_CLOSE;
|
|
dwDesiredAccess |= DELETE;
|
|
}
|
|
|
|
//
|
|
// Backup semantics allow directories to be opened
|
|
//
|
|
|
|
if ( !(dwFlagsAndAttributes & FILE_FLAG_BACKUP_SEMANTICS) ) {
|
|
CreateFlags |= FILE_NON_DIRECTORY_FILE;
|
|
}
|
|
|
|
Status = NtCreateFile(
|
|
&Handle,
|
|
(ACCESS_MASK)dwDesiredAccess | SYNCHRONIZE | FILE_READ_ATTRIBUTES,
|
|
&Obja,
|
|
&IoStatusBlock,
|
|
NULL,
|
|
dwFlagsAndAttributes & (FILE_ATTRIBUTE_VALID_FLAGS & ~FILE_ATTRIBUTE_DIRECTORY),
|
|
dwShareMode,
|
|
CreateDisposition,
|
|
CreateFlags
|
|
);
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
XapiSetLastNTError(Status);
|
|
if ( Status == STATUS_OBJECT_NAME_COLLISION ) {
|
|
SetLastError(ERROR_FILE_EXISTS);
|
|
}
|
|
else if ( Status == STATUS_FILE_IS_A_DIRECTORY ) {
|
|
if ( EndsInSlash ) {
|
|
SetLastError(ERROR_PATH_NOT_FOUND);
|
|
}
|
|
else {
|
|
SetLastError(ERROR_ACCESS_DENIED);
|
|
}
|
|
}
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
//
|
|
// if NT returns supersede/overwritten, it means that a create_always, openalways
|
|
// found an existing copy of the file. In this case ERROR_ALREADY_EXISTS is returned
|
|
//
|
|
|
|
if ( (dwCreationDisposition == CREATE_ALWAYS && IoStatusBlock.Information == FILE_OVERWRITTEN) ||
|
|
(dwCreationDisposition == OPEN_ALWAYS && IoStatusBlock.Information == FILE_OPENED) ){
|
|
SetLastError(ERROR_ALREADY_EXISTS);
|
|
}
|
|
else {
|
|
SetLastError(0);
|
|
}
|
|
|
|
return Handle;
|
|
}
|
|
|
|
BOOL
|
|
WINAPI
|
|
CopyFile(
|
|
PCOSTR lpExistingFileName,
|
|
PCOSTR lpNewFileName,
|
|
BOOL bFailIfExists
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
A file, its extended attributes, alternate data streams, and any other
|
|
attributes can be copied using CopyFile.
|
|
|
|
Arguments:
|
|
|
|
lpExistingFileName - Supplies the name of an existing file that is to be
|
|
copied.
|
|
|
|
lpNewFileName - Supplies the name where a copy of the existing
|
|
files data and attributes are to be stored.
|
|
|
|
bFailIfExists - Supplies a flag that indicates how this operation is
|
|
to proceed if the specified new file already exists. A value of
|
|
TRUE specifies that this call is to fail. A value of FALSE
|
|
causes the call to the function to succeed whether or not the
|
|
specified new file exists.
|
|
|
|
Return Value:
|
|
|
|
TRUE - The operation was successful.
|
|
|
|
FALSE/NULL - The operation failed. Extended error status is available
|
|
using GetLastError.
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL b;
|
|
|
|
b = CopyFileEx(
|
|
lpExistingFileName,
|
|
lpNewFileName,
|
|
(LPPROGRESS_ROUTINE)NULL,
|
|
(LPVOID)NULL,
|
|
(LPBOOL)NULL,
|
|
bFailIfExists ? COPY_FILE_FAIL_IF_EXISTS : 0
|
|
);
|
|
|
|
return b;
|
|
}
|
|
|
|
BOOL
|
|
CopyFileEx(
|
|
PCOSTR lpExistingFileName,
|
|
PCOSTR lpNewFileName,
|
|
LPPROGRESS_ROUTINE lpProgressRoutine OPTIONAL,
|
|
LPVOID lpData OPTIONAL,
|
|
LPBOOL pbCancel OPTIONAL,
|
|
DWORD dwCopyFlags
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
A file, its extended attributes, alternate data streams, and any other
|
|
attributes can be copied using CopyFileEx. CopyFileEx also provides
|
|
callbacks and cancellability.
|
|
|
|
Arguments:
|
|
|
|
lpExistingFileName - Supplies the name of an existing file that is to be
|
|
copied.
|
|
|
|
lpNewFileName - Supplies the name where a copy of the existing
|
|
files data and attributes are to be stored.
|
|
|
|
lpProgressRoutine - Optionally supplies the address of a callback routine
|
|
to be called as the copy operation progresses.
|
|
|
|
lpData - Optionally supplies a context to be passed to the progress callback
|
|
routine.
|
|
|
|
lpCancel - Optionally supplies the address of a boolean to be set to TRUE
|
|
if the caller would like the copy to abort.
|
|
|
|
dwCopyFlags - Specifies flags that modify how the file is to be copied:
|
|
|
|
COPY_FILE_FAIL_IF_EXISTS - Indicates that the copy operation should
|
|
fail immediately if the target file already exists.
|
|
|
|
Return Value:
|
|
|
|
TRUE - The operation was successful.
|
|
|
|
FALSE/NULL - The operation failed. Extended error status is available
|
|
using GetLastError.
|
|
|
|
--*/
|
|
{
|
|
LPVOID CopyBuffer;
|
|
NTSTATUS status;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
OBJECT_STRING PathName;
|
|
HANDLE SourceHandle;
|
|
FILE_NETWORK_OPEN_INFORMATION NetworkInfo;
|
|
HANDLE DestinationHandle;
|
|
LARGE_INTEGER ByteOffset;
|
|
DWORD dwProgress;
|
|
ULONG BytesThisPass;
|
|
FILE_BASIC_INFORMATION BasicInfo;
|
|
FILE_END_OF_FILE_INFORMATION EndOfFileInfo;
|
|
FILE_DISPOSITION_INFORMATION DispositionInfo;
|
|
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&PathName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
ObDosDevicesDirectory(),
|
|
NULL
|
|
);
|
|
|
|
//
|
|
// Allocate a buffer to copy the file stream.
|
|
//
|
|
|
|
CopyBuffer = MmAllocateSystemMemory(BASE_COPY_FILE_CHUNK, PAGE_READWRITE);
|
|
|
|
if (CopyBuffer == NULL) {
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Open the source file.
|
|
//
|
|
|
|
RtlInitObjectString(&PathName, lpExistingFileName);
|
|
|
|
status = NtOpenFile(&SourceHandle,
|
|
GENERIC_READ | SYNCHRONIZE,
|
|
&ObjectAttributes,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_READ,
|
|
FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
MmFreeSystemMemory(CopyBuffer, BASE_COPY_FILE_CHUNK);
|
|
XapiSetLastNTError(status);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Get the attributes for the source file.
|
|
//
|
|
|
|
status = NtQueryInformationFile(SourceHandle,
|
|
&IoStatusBlock,
|
|
&NetworkInfo,
|
|
sizeof(NetworkInfo),
|
|
FileNetworkOpenInformation
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
NtClose(SourceHandle);
|
|
MmFreeSystemMemory(CopyBuffer, BASE_COPY_FILE_CHUNK);
|
|
XapiSetLastNTError(status);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Create the destination file. The file is created with the attributes of
|
|
// the source file and given an initial file allocation equal to the size of
|
|
// the source file.
|
|
//
|
|
|
|
RtlInitObjectString(&PathName, lpNewFileName);
|
|
|
|
status = NtCreateFile(&DestinationHandle,
|
|
GENERIC_WRITE | SYNCHRONIZE,
|
|
&ObjectAttributes,
|
|
&IoStatusBlock,
|
|
&NetworkInfo.EndOfFile,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
0,
|
|
(dwCopyFlags & COPY_FILE_FAIL_IF_EXISTS) ?
|
|
FILE_CREATE : FILE_OVERWRITE_IF,
|
|
FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE | FILE_SEQUENTIAL_ONLY
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
NtClose(SourceHandle);
|
|
MmFreeSystemMemory(CopyBuffer, BASE_COPY_FILE_CHUNK);
|
|
XapiSetLastNTError(status);
|
|
return FALSE;
|
|
}
|
|
|
|
ByteOffset.QuadPart = 0;
|
|
|
|
//
|
|
// Invoke the callback to inform it of the start of the copy process. Note
|
|
// that for simplicity, we treat PROGRESS_STOP or any unknow return value as
|
|
// PROGRESS_CANCEL.
|
|
//
|
|
|
|
if (lpProgressRoutine != NULL) {
|
|
|
|
dwProgress = lpProgressRoutine(NetworkInfo.EndOfFile,
|
|
ByteOffset,
|
|
NetworkInfo.EndOfFile,
|
|
ByteOffset,
|
|
1,
|
|
CALLBACK_STREAM_SWITCH,
|
|
SourceHandle,
|
|
DestinationHandle,
|
|
lpData
|
|
);
|
|
|
|
if (dwProgress == PROGRESS_QUIET) {
|
|
lpProgressRoutine = NULL;
|
|
} else if (dwProgress != PROGRESS_CONTINUE) {
|
|
status = STATUS_REQUEST_ABORTED;
|
|
goto CancelOperation;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set the end of file for the destination file to the size of the source
|
|
// file.
|
|
//
|
|
|
|
EndOfFileInfo.EndOfFile = NetworkInfo.EndOfFile;
|
|
|
|
status = NtSetInformationFile(DestinationHandle,
|
|
&IoStatusBlock,
|
|
&EndOfFileInfo,
|
|
sizeof(EndOfFileInfo),
|
|
FileEndOfFileInformation
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto CancelOperation;
|
|
}
|
|
|
|
//
|
|
// Copy the bytes from the source file to the destination file.
|
|
//
|
|
|
|
while (ByteOffset.QuadPart < NetworkInfo.EndOfFile.QuadPart) {
|
|
|
|
status = NtReadFile(SourceHandle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatusBlock,
|
|
CopyBuffer,
|
|
BASE_COPY_FILE_CHUNK,
|
|
&ByteOffset
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
break;
|
|
}
|
|
|
|
BytesThisPass = IoStatusBlock.Information;
|
|
|
|
status = NtWriteFile(DestinationHandle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatusBlock,
|
|
CopyBuffer,
|
|
BytesThisPass,
|
|
&ByteOffset
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
break;
|
|
}
|
|
|
|
ByteOffset.QuadPart += BytesThisPass;
|
|
|
|
//
|
|
// Invoke the callback to inform it that another chunk has been copied.
|
|
//
|
|
|
|
if (lpProgressRoutine != NULL) {
|
|
|
|
dwProgress = lpProgressRoutine(NetworkInfo.EndOfFile,
|
|
ByteOffset,
|
|
NetworkInfo.EndOfFile,
|
|
ByteOffset,
|
|
1,
|
|
CALLBACK_CHUNK_FINISHED,
|
|
SourceHandle,
|
|
DestinationHandle,
|
|
lpData
|
|
);
|
|
|
|
if (dwProgress == PROGRESS_QUIET) {
|
|
lpProgressRoutine = NULL;
|
|
} else if (dwProgress != PROGRESS_CONTINUE) {
|
|
status = STATUS_REQUEST_ABORTED;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Apply the time stamps from the source file to the destination file.
|
|
//
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
BasicInfo.CreationTime = NetworkInfo.CreationTime;
|
|
BasicInfo.LastAccessTime = NetworkInfo.LastAccessTime;
|
|
BasicInfo.LastWriteTime = NetworkInfo.LastWriteTime;
|
|
BasicInfo.ChangeTime = NetworkInfo.ChangeTime;
|
|
BasicInfo.FileAttributes = NetworkInfo.FileAttributes;
|
|
|
|
status = NtSetInformationFile(DestinationHandle,
|
|
&IoStatusBlock,
|
|
&BasicInfo,
|
|
sizeof(BasicInfo),
|
|
FileBasicInformation
|
|
);
|
|
}
|
|
|
|
//
|
|
// If the copy operation failed or a progress callback canceled the
|
|
// operation, then delete the file.
|
|
//
|
|
|
|
CancelOperation:
|
|
MmFreeSystemMemory(CopyBuffer, BASE_COPY_FILE_CHUNK);
|
|
NtClose(SourceHandle);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
#undef DeleteFile
|
|
DispositionInfo.DeleteFile = TRUE;
|
|
|
|
NtSetInformationFile(DestinationHandle,
|
|
&IoStatusBlock,
|
|
&DispositionInfo,
|
|
sizeof(DispositionInfo),
|
|
FileDispositionInformation
|
|
);
|
|
|
|
XapiSetLastNTError(status);
|
|
NtClose(DestinationHandle);
|
|
return FALSE;
|
|
}
|
|
|
|
NtClose(DestinationHandle);
|
|
return TRUE;
|
|
}
|