1537 lines
50 KiB
C
1537 lines
50 KiB
C
/*++
|
||
|
||
Copyright (c) 1989-1993 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
write.c
|
||
|
||
Abstract:
|
||
|
||
This module contains the code to implement the NtWriteFile system service.
|
||
|
||
Author:
|
||
|
||
Darryl E. Havens (darrylh) 14-Apr-1989
|
||
|
||
Environment:
|
||
|
||
Kernel mode
|
||
|
||
Revision History:
|
||
|
||
|
||
--*/
|
||
|
||
#include "iomgr.h"
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE, NtWriteFile)
|
||
#pragma alloc_text(PAGE, NtWriteFile64)
|
||
#pragma alloc_text(PAGE, NtWriteFileGather)
|
||
#endif
|
||
|
||
NTSTATUS
|
||
NtWriteFile(
|
||
IN HANDLE FileHandle,
|
||
IN HANDLE Event OPTIONAL,
|
||
IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
|
||
IN PVOID ApcContext OPTIONAL,
|
||
OUT PIO_STATUS_BLOCK IoStatusBlock,
|
||
IN PVOID Buffer,
|
||
IN ULONG Length,
|
||
IN PLARGE_INTEGER ByteOffset OPTIONAL,
|
||
IN PULONG Key OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This service writes Length bytes of data from the caller's Buffer to the
|
||
file associated with FileHandle starting at StartingBlock|ByteOffset.
|
||
The actual number of bytes written to the file will be returned in the
|
||
second longword of the IoStatusBlock.
|
||
|
||
If the writer has the file open for APPEND access, then the data will be
|
||
written to the current EOF mark. The StartingBlock and ByteOffset are
|
||
ignored if the caller has APPEND access.
|
||
|
||
Arguments:
|
||
|
||
FileHandle - Supplies a handle to the file to be written.
|
||
|
||
Event - Optionally supplies an event to be set to the Signaled state when
|
||
the write operation is complete.
|
||
|
||
ApcRoutine - Optionally supplies an APC routine to be executed when the
|
||
write operation is complete.
|
||
|
||
ApcContext - Supplies a context parameter to be passed to the APC routine
|
||
when it is invoked, if an APC routine was specified.
|
||
|
||
IoStatusBlock - Supplies the address of the caller's I/O status block.
|
||
|
||
Buffer - Supplies the address of the buffer containing data to be written
|
||
to the file.
|
||
|
||
Length - Length, in bytes, of the data to be written to the file.
|
||
|
||
ByteOffset - Specifies the starting byte offset within the file to begin
|
||
the write operation. If not specified and the file is open for
|
||
synchronous I/O, then the current file position is used. If the
|
||
file is not opened for synchronous I/O and the parameter is not
|
||
specified, then it is in error.
|
||
|
||
Key - Optionally specifies a key to be used if there are locks associated
|
||
with the file.
|
||
|
||
Return Value:
|
||
|
||
The status returned is success if the write operation was properly queued
|
||
to the I/O system. Once the write completes the status of the operation
|
||
can be determined by examining the Status field of the I/O status block.
|
||
|
||
--*/
|
||
|
||
{
|
||
PIRP irp;
|
||
NTSTATUS status;
|
||
PFILE_OBJECT fileObject;
|
||
PDEVICE_OBJECT deviceObject;
|
||
PFAST_IO_DISPATCH fastIoDispatch;
|
||
KPROCESSOR_MODE requestorMode;
|
||
PMDL mdl;
|
||
PIO_STACK_LOCATION irpSp;
|
||
ACCESS_MASK grantedAccess;
|
||
ACCESS_MASK desiredAccess = (ACCESS_MASK)0;
|
||
OBJECT_HANDLE_INFORMATION handleInformation;
|
||
NTSTATUS exceptionCode;
|
||
BOOLEAN synchronousIo;
|
||
PKEVENT eventObject = (PKEVENT) NULL;
|
||
ULONG keyValue = 0;
|
||
LARGE_INTEGER fileOffset = {0,0};
|
||
PULONG majorFunction;
|
||
PETHREAD CurrentThread;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Get the previous mode; i.e., the mode of the caller.
|
||
//
|
||
|
||
CurrentThread = PsGetCurrentThread ();
|
||
requestorMode = KeGetPreviousModeByThread(&CurrentThread->Tcb);
|
||
|
||
//
|
||
// Reference the file object so the target device can be found and the
|
||
// access rights mask can be used in the following checks for callers in
|
||
// user mode. Note that if the handle does not refer to a file object,
|
||
// then it will fail.
|
||
//
|
||
|
||
status = ObReferenceFileObjectForWrite( FileHandle,
|
||
requestorMode,
|
||
(PVOID *) &fileObject,
|
||
&handleInformation);
|
||
|
||
if (!NT_SUCCESS( status )) {
|
||
return status;
|
||
}
|
||
|
||
grantedAccess = handleInformation.GrantedAccess;
|
||
|
||
//
|
||
// Get the address of the target device object.
|
||
//
|
||
|
||
deviceObject = IoGetRelatedDeviceObject( fileObject );
|
||
|
||
//
|
||
// Check to see if the requestor mode was user. If so, perform a bunch
|
||
// of extra checks.
|
||
//
|
||
|
||
if (requestorMode != KernelMode) {
|
||
|
||
//
|
||
// The caller's access mode is not kernel so probe each of the arguments
|
||
// and capture them as necessary. If any failures occur, the condition
|
||
// handler will be invoked to handle them. It will simply cleanup and
|
||
// return an access violation status code back to the system service
|
||
// dispatcher.
|
||
//
|
||
|
||
//
|
||
// Attempt to probe the caller's parameters within the exception
|
||
// handler block.
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// The IoStatusBlock parameter must be writeable by the caller.
|
||
//
|
||
|
||
ProbeForWriteIoStatusEx( IoStatusBlock , ApcRoutine);
|
||
|
||
//
|
||
// The caller's data buffer must be readable from the caller's
|
||
// mode. This check ensures that this is the case. Since the
|
||
// buffer address is captured, the caller cannot change it,
|
||
// even though he/she can change the protection from another
|
||
// thread. This error will be caught by the probe/lock or
|
||
// buffer copy operations later.
|
||
//
|
||
|
||
ProbeForRead( Buffer, Length, sizeof( UCHAR ) );
|
||
|
||
//
|
||
// If this file has an I/O completion port associated w/it, then
|
||
// ensure that the caller did not supply an APC routine, as the
|
||
// two are mutually exclusive methods for I/O completion
|
||
// notification.
|
||
//
|
||
|
||
if (fileObject->CompletionContext && IopApcRoutinePresent( ApcRoutine )) {
|
||
ObDereferenceObject( fileObject );
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
//
|
||
// Check that the ByteOffset parameter is readable from the
|
||
// caller's mode, if one was specified, and capture it.
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT( ByteOffset )) {
|
||
ProbeForReadSmallStructure( ByteOffset,
|
||
sizeof( LARGE_INTEGER ),
|
||
sizeof( ULONG ) );
|
||
fileOffset = *ByteOffset;
|
||
}
|
||
|
||
//
|
||
// Check to see whether the caller has opened the file without
|
||
// intermediate buffering. If so, perform the following Buffer
|
||
// and ByteOffset parameter checks differently.
|
||
//
|
||
|
||
if (fileObject->Flags & FO_NO_INTERMEDIATE_BUFFERING) {
|
||
|
||
//
|
||
// The file was opened without intermediate buffering enabled.
|
||
// Check that the Buffer is properly aligned, and that the
|
||
// length is an integral number of the block size.
|
||
//
|
||
|
||
if ((deviceObject->SectorSize &&
|
||
(Length & (deviceObject->SectorSize - 1))) ||
|
||
(ULONG_PTR) Buffer & deviceObject->AlignmentRequirement) {
|
||
|
||
//
|
||
// Check for sector sizes that are not a power of two.
|
||
//
|
||
|
||
if ((deviceObject->SectorSize &&
|
||
Length % deviceObject->SectorSize) ||
|
||
(ULONG_PTR) Buffer & deviceObject->AlignmentRequirement) {
|
||
ObDereferenceObject( fileObject );
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
}
|
||
|
||
//
|
||
// If a ByteOffset parameter was specified, ensure that it is
|
||
// is of the proper type.
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT( ByteOffset )) {
|
||
if (fileOffset.LowPart == FILE_WRITE_TO_END_OF_FILE &&
|
||
fileOffset.HighPart == -1) {
|
||
NOTHING;
|
||
} else if (fileOffset.LowPart == FILE_USE_FILE_POINTER_POSITION &&
|
||
fileOffset.HighPart == -1 &&
|
||
(fileObject->Flags & FO_SYNCHRONOUS_IO)) {
|
||
NOTHING;
|
||
} else if (deviceObject->SectorSize &&
|
||
(fileOffset.LowPart & (deviceObject->SectorSize - 1))) {
|
||
ObDereferenceObject( fileObject );
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Finally, ensure that if there is a key parameter specified it
|
||
// is readable by the caller.
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT( Key )) {
|
||
keyValue = ProbeAndReadUlong( Key );
|
||
}
|
||
|
||
} except(IopExceptionFilter( GetExceptionInformation(), &exceptionCode )) {
|
||
|
||
//
|
||
// An exception was incurred while attempting to probe the
|
||
// caller's parameters. Simply cleanup, dereference the file
|
||
// object, and return with the appropriate status code.
|
||
//
|
||
|
||
ObDereferenceObject( fileObject );
|
||
return exceptionCode;
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// The caller's mode is kernel. Get the appropriate parameters to
|
||
// their expected locations without making all of the checks.
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT( ByteOffset )) {
|
||
fileOffset = *ByteOffset;
|
||
}
|
||
|
||
if (ARGUMENT_PRESENT( Key )) {
|
||
keyValue = *Key;
|
||
}
|
||
#if DBG
|
||
if (fileObject->Flags & FO_NO_INTERMEDIATE_BUFFERING) {
|
||
|
||
//
|
||
// The file was opened without intermediate buffering enabled.
|
||
// Check that the Buffer is properly aligned, and that the
|
||
// length is an integral number of the block size.
|
||
//
|
||
|
||
if ((deviceObject->SectorSize &&
|
||
(Length & (deviceObject->SectorSize - 1))) ||
|
||
(ULONG_PTR) Buffer & deviceObject->AlignmentRequirement) {
|
||
|
||
//
|
||
// Check for sector sizes that are not a power of two.
|
||
//
|
||
|
||
if ((deviceObject->SectorSize &&
|
||
Length % deviceObject->SectorSize) ||
|
||
(ULONG_PTR) Buffer & deviceObject->AlignmentRequirement) {
|
||
ObDereferenceObject( fileObject );
|
||
ASSERT( FALSE );
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
}
|
||
|
||
//
|
||
// If a ByteOffset parameter was specified, ensure that it is
|
||
// is of the proper type.
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT( ByteOffset )) {
|
||
if (fileOffset.LowPart == FILE_WRITE_TO_END_OF_FILE &&
|
||
fileOffset.HighPart == -1) {
|
||
NOTHING;
|
||
} else if (fileOffset.LowPart == FILE_USE_FILE_POINTER_POSITION &&
|
||
fileOffset.HighPart == -1 &&
|
||
(fileObject->Flags & FO_SYNCHRONOUS_IO)) {
|
||
NOTHING;
|
||
} else if (deviceObject->SectorSize &&
|
||
(fileOffset.LowPart & (deviceObject->SectorSize - 1))) {
|
||
ObDereferenceObject( fileObject );
|
||
ASSERT( FALSE );
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
}
|
||
}
|
||
#endif // DBG
|
||
|
||
}
|
||
|
||
//
|
||
// If the caller has only append access to the file, ignore the input
|
||
// parameters and set the ByteOffset to indicate that this write is
|
||
// to the end of the file. Otherwise, ensure that the parameters are
|
||
// valid.
|
||
//
|
||
|
||
if (SeComputeGrantedAccesses( grantedAccess, FILE_APPEND_DATA | FILE_WRITE_DATA ) == FILE_APPEND_DATA) {
|
||
|
||
//
|
||
// This is an append operation to the end of a file. Set the
|
||
// ByteOffset parameter to give drivers a consistent view of
|
||
// this type of call.
|
||
//
|
||
|
||
fileOffset.LowPart = FILE_WRITE_TO_END_OF_FILE;
|
||
fileOffset.HighPart = -1;
|
||
}
|
||
|
||
//
|
||
// Get the address of the event object and set the event to the Not-
|
||
// Signaled state, if an event was specified. Note here too, that if
|
||
// the handle does not refer to an event, then the reference will fail.
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT( Event )) {
|
||
status = ObReferenceObjectByHandle( Event,
|
||
EVENT_MODIFY_STATE,
|
||
ExEventObjectType,
|
||
requestorMode,
|
||
(PVOID *) &eventObject,
|
||
NULL );
|
||
if (!NT_SUCCESS( status )) {
|
||
ObDereferenceObject( fileObject );
|
||
return status;
|
||
} else {
|
||
KeClearEvent( eventObject );
|
||
}
|
||
}
|
||
|
||
//
|
||
// Get the address of the fast io dispatch structure.
|
||
//
|
||
|
||
fastIoDispatch = deviceObject->DriverObject->FastIoDispatch;
|
||
|
||
//
|
||
// Make a special check here to determine whether this is a synchronous
|
||
// I/O operation. If it is, then wait here until the file is owned by
|
||
// the current thread. If the wait terminates with an alerted status,
|
||
// then cleanup and return the alerted status. This allows the caller
|
||
// specify FILE_SYNCHRONOUS_IO_ALERT as a synchronous I/O option.
|
||
//
|
||
// If everything works, then check to see whether a ByteOffset parameter
|
||
// was supplied. If not, or if it was and it is set to the "use file
|
||
// pointer position", then initialize the file offset to be whatever
|
||
// the current byte offset into the file is according to the file pointer
|
||
// context information in the file object.
|
||
//
|
||
|
||
if (fileObject->Flags & FO_SYNCHRONOUS_IO) {
|
||
|
||
BOOLEAN interrupted;
|
||
|
||
if (!IopAcquireFastLock( fileObject )) {
|
||
status = IopAcquireFileObjectLock( fileObject,
|
||
requestorMode,
|
||
(BOOLEAN) ((fileObject->Flags & FO_ALERTABLE_IO) != 0),
|
||
&interrupted );
|
||
if (interrupted) {
|
||
if (eventObject) {
|
||
ObDereferenceObject( eventObject );
|
||
}
|
||
ObDereferenceObject( fileObject );
|
||
return status;
|
||
}
|
||
}
|
||
|
||
synchronousIo = TRUE;
|
||
|
||
if ((!ARGUMENT_PRESENT( ByteOffset ) && !fileOffset.LowPart ) ||
|
||
(fileOffset.LowPart == FILE_USE_FILE_POINTER_POSITION &&
|
||
fileOffset.HighPart == -1 )) {
|
||
fileOffset = fileObject->CurrentByteOffset;
|
||
}
|
||
|
||
//
|
||
// Turbo write support. If the file is currently cached on this
|
||
// file object, then call the Cache Manager directly via FsRtl
|
||
// and try to successfully complete the request here. Note if
|
||
// FastIoWrite returns FALSE or we get an I/O error, we simply
|
||
// fall through and go the "long way" and create an Irp.
|
||
//
|
||
|
||
if (fileObject->PrivateCacheMap) {
|
||
|
||
IO_STATUS_BLOCK localIoStatus;
|
||
|
||
ASSERT(fastIoDispatch && fastIoDispatch->FastIoWrite);
|
||
|
||
//
|
||
// Negative file offsets are illegal.
|
||
//
|
||
|
||
if (fileOffset.HighPart < 0 &&
|
||
(fileOffset.HighPart != -1 ||
|
||
fileOffset.LowPart != FILE_WRITE_TO_END_OF_FILE)) {
|
||
|
||
if (eventObject) {
|
||
ObDereferenceObject( eventObject );
|
||
}
|
||
IopReleaseFileObjectLock( fileObject );
|
||
ObDereferenceObject( fileObject );
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
if (fastIoDispatch->FastIoWrite( fileObject,
|
||
&fileOffset,
|
||
Length,
|
||
TRUE,
|
||
keyValue,
|
||
Buffer,
|
||
&localIoStatus,
|
||
deviceObject )
|
||
|
||
&&
|
||
|
||
(localIoStatus.Status == STATUS_SUCCESS)) {
|
||
|
||
IopUpdateWriteOperationCount( );
|
||
IopUpdateWriteTransferCount( (ULONG)localIoStatus.Information );
|
||
|
||
//
|
||
// Carefully return the I/O status.
|
||
|
||
try {
|
||
*IoStatusBlock = localIoStatus;
|
||
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
||
localIoStatus.Status = GetExceptionCode();
|
||
localIoStatus.Information = 0;
|
||
}
|
||
|
||
//
|
||
// If an event was specified, set it.
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT( Event )) {
|
||
KeSetEvent( eventObject, 0, FALSE );
|
||
ObDereferenceObject( eventObject );
|
||
}
|
||
|
||
//
|
||
// Note that the file object event need not be set to the
|
||
// Signaled state, as it is already set.
|
||
//
|
||
|
||
//
|
||
// Cleanup and return.
|
||
//
|
||
|
||
IopReleaseFileObjectLock( fileObject );
|
||
ObDereferenceObject( fileObject );
|
||
return localIoStatus.Status;
|
||
}
|
||
}
|
||
|
||
} else if (!ARGUMENT_PRESENT( ByteOffset ) && !(fileObject->Flags & (FO_NAMED_PIPE | FO_MAILSLOT))) {
|
||
|
||
//
|
||
// The file is not open for synchronous I/O operations, but the
|
||
// caller did not specify a ByteOffset parameter. This is an error
|
||
// situation, so cleanup and return with the appropriate status.
|
||
//
|
||
|
||
if (eventObject) {
|
||
ObDereferenceObject( eventObject );
|
||
}
|
||
ObDereferenceObject( fileObject );
|
||
return STATUS_INVALID_PARAMETER;
|
||
|
||
} else {
|
||
|
||
//
|
||
// This is not a synchronous I/O operation.
|
||
//
|
||
|
||
synchronousIo = FALSE;
|
||
}
|
||
|
||
//
|
||
// Negative file offsets are illegal.
|
||
//
|
||
|
||
if (fileOffset.HighPart < 0 &&
|
||
(fileOffset.HighPart != -1 ||
|
||
fileOffset.LowPart != FILE_WRITE_TO_END_OF_FILE)) {
|
||
|
||
if (eventObject) {
|
||
ObDereferenceObject( eventObject );
|
||
}
|
||
if (synchronousIo) {
|
||
IopReleaseFileObjectLock( fileObject );
|
||
}
|
||
ObDereferenceObject( fileObject );
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
//
|
||
// Set the file object to the Not-Signaled state.
|
||
//
|
||
|
||
KeClearEvent( &fileObject->Event );
|
||
|
||
//
|
||
// Allocate and initialize the I/O Request Packet (IRP) for this operation.
|
||
// The allocation is performed with an exception handler in case the
|
||
// caller does not have enough quota to allocate the packet.
|
||
//
|
||
|
||
irp = IopAllocateIrp( deviceObject->StackSize, TRUE );
|
||
if (!irp) {
|
||
|
||
//
|
||
// An IRP could not be allocated. Cleanup and return an appropriate
|
||
// error status code.
|
||
//
|
||
|
||
IopAllocateIrpCleanup( fileObject, eventObject );
|
||
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
irp->Tail.Overlay.OriginalFileObject = fileObject;
|
||
irp->Tail.Overlay.Thread = CurrentThread;
|
||
irp->Tail.Overlay.AuxiliaryBuffer = (PVOID) NULL;
|
||
irp->RequestorMode = requestorMode;
|
||
irp->PendingReturned = FALSE;
|
||
irp->Cancel = FALSE;
|
||
irp->CancelRoutine = (PDRIVER_CANCEL) NULL;
|
||
|
||
//
|
||
// Fill in the service independent parameters in the IRP.
|
||
//
|
||
|
||
irp->UserEvent = eventObject;
|
||
irp->UserIosb = IoStatusBlock;
|
||
irp->Overlay.AsynchronousParameters.UserApcRoutine = ApcRoutine;
|
||
irp->Overlay.AsynchronousParameters.UserApcContext = ApcContext;
|
||
|
||
//
|
||
// Get a pointer to the stack location for the first driver. This will be
|
||
// used to pass the original function codes and parameters. Note that
|
||
// setting the major function code here also sets:
|
||
//
|
||
// MinorFunction = 0;
|
||
// Flags = 0;
|
||
// Control = 0;
|
||
//
|
||
|
||
irpSp = IoGetNextIrpStackLocation( irp );
|
||
majorFunction = (PULONG) irpSp;
|
||
*majorFunction = IRP_MJ_WRITE;
|
||
irpSp->FileObject = fileObject;
|
||
if (fileObject->Flags & FO_WRITE_THROUGH) {
|
||
irpSp->Flags = SL_WRITE_THROUGH;
|
||
}
|
||
|
||
//
|
||
// Now determine whether this device expects to have data buffered to it
|
||
// or whether it performs direct I/O. This is based on the DO_BUFFERED_IO
|
||
// flag in the device object. If the flag is set, then a system buffer is
|
||
// allocated and the caller's data is copied into it. Otherwise, a Memory
|
||
// Descriptor List (MDL) is allocated and the caller's buffer is locked
|
||
// down using it.
|
||
//
|
||
|
||
irp->AssociatedIrp.SystemBuffer = (PVOID) NULL;
|
||
irp->MdlAddress = (PMDL) NULL;
|
||
|
||
if (deviceObject->Flags & DO_BUFFERED_IO) {
|
||
|
||
//
|
||
// The device does not support direct I/O. Allocate a system buffer,
|
||
// and copy the caller's data into it. This is done using an
|
||
// exception handler that will perform cleanup if the operation
|
||
// fails. Note that this is only done if the operation has a non-zero
|
||
// length.
|
||
//
|
||
|
||
if (Length) {
|
||
|
||
try {
|
||
|
||
//
|
||
// Allocate the intermediary system buffer from nonpaged pool,
|
||
// charge quota for it, and copy the caller's data into it.
|
||
//
|
||
|
||
irp->AssociatedIrp.SystemBuffer =
|
||
ExAllocatePoolWithQuota( NonPagedPoolCacheAligned, Length );
|
||
RtlCopyMemory( irp->AssociatedIrp.SystemBuffer, Buffer, Length );
|
||
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
|
||
//
|
||
// An exception was incurred while either probing the caller's
|
||
// buffer, allocating the system buffer, or copying the data
|
||
// from the caller's buffer to the system buffer. Determine
|
||
// what actually happened, clean everything up, and return an
|
||
// appropriate error status code.
|
||
//
|
||
|
||
IopExceptionCleanup( fileObject,
|
||
irp,
|
||
eventObject,
|
||
(PKEVENT) NULL );
|
||
|
||
return GetExceptionCode();
|
||
|
||
}
|
||
|
||
//
|
||
// Set the IRP_BUFFERED_IO flag in the IRP so that I/O completion
|
||
// will know that this is not a direct I/O operation. Also set the
|
||
// IRP_DEALLOCATE_BUFFER flag so it will deallocate the buffer.
|
||
//
|
||
|
||
irp->Flags = IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER;
|
||
|
||
} else {
|
||
|
||
//
|
||
// This is a zero-length write. Simply indicate that this is
|
||
// buffered I/O, and pass along the request. The buffer will
|
||
// not be set to deallocate so the completion path does not
|
||
// have to special-case the length.
|
||
//
|
||
|
||
irp->Flags = IRP_BUFFERED_IO;
|
||
}
|
||
|
||
} else if (deviceObject->Flags & DO_DIRECT_IO) {
|
||
|
||
//
|
||
// This is a direct I/O operation. Allocate an MDL and invoke the
|
||
// memory management routine to lock the buffer into memory. This
|
||
// is done using an exception handler that will perform cleanup if
|
||
// the operation fails. Note that no MDL is allocated, nor is any
|
||
// memory probed or locked if the length of the request was zero.
|
||
//
|
||
|
||
mdl = (PMDL) NULL;
|
||
irp->Flags = 0;
|
||
|
||
if (Length) {
|
||
|
||
try {
|
||
|
||
//
|
||
// Allocate an MDL, charging quota for it, and hang it off of
|
||
// the IRP. Probe and lock the pages associated with the
|
||
// caller's buffer for read access and fill in the MDL with
|
||
// the PFNs of those pages.
|
||
//
|
||
|
||
mdl = IoAllocateMdl( Buffer, Length, FALSE, TRUE, irp );
|
||
if (mdl == NULL) {
|
||
ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
|
||
}
|
||
|
||
MmProbeAndLockPages( mdl, requestorMode, IoReadAccess );
|
||
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
|
||
//
|
||
// An exception was incurred while either allocating the MDL
|
||
// or while attempting to probe and lock the caller's buffer.
|
||
// Determine what actually happened, clean everything up, and
|
||
// return an appropriate error status code.
|
||
//
|
||
|
||
IopExceptionCleanup( fileObject,
|
||
irp,
|
||
eventObject,
|
||
(PKEVENT) NULL );
|
||
|
||
return GetExceptionCode();
|
||
}
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// Pass the address of the caller's buffer to the device driver. It
|
||
// is now up to the driver to do everything.
|
||
//
|
||
|
||
irp->Flags = 0;
|
||
irp->UserBuffer = Buffer;
|
||
|
||
}
|
||
|
||
//
|
||
// If this write operation is to be performed without any caching, set the
|
||
// appropriate flag in the IRP so no caching is performed.
|
||
//
|
||
|
||
if (fileObject->Flags & FO_NO_INTERMEDIATE_BUFFERING) {
|
||
irp->Flags |= IRP_NOCACHE | IRP_WRITE_OPERATION | IRP_DEFER_IO_COMPLETION;
|
||
} else {
|
||
irp->Flags |= IRP_WRITE_OPERATION | IRP_DEFER_IO_COMPLETION;
|
||
}
|
||
|
||
//
|
||
// Copy the caller's parameters to the service-specific portion of the
|
||
// IRP.
|
||
//
|
||
|
||
irpSp->Parameters.Write.Length = Length;
|
||
irpSp->Parameters.Write.Key = keyValue;
|
||
irpSp->Parameters.Write.ByteOffset = fileOffset;
|
||
|
||
//
|
||
// Queue the packet, call the driver, and synchronize appopriately with
|
||
// I/O completion.
|
||
//
|
||
|
||
status = IopSynchronousServiceTail( deviceObject,
|
||
irp,
|
||
fileObject,
|
||
TRUE,
|
||
requestorMode,
|
||
synchronousIo,
|
||
WriteTransfer );
|
||
|
||
return status;
|
||
}
|
||
|
||
NTSTATUS
|
||
NtWriteFileGather(
|
||
IN HANDLE FileHandle,
|
||
IN HANDLE Event OPTIONAL,
|
||
IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
|
||
IN PVOID ApcContext OPTIONAL,
|
||
OUT PIO_STATUS_BLOCK IoStatusBlock,
|
||
IN PFILE_SEGMENT_ELEMENT SegmentArray,
|
||
IN ULONG Length,
|
||
IN PLARGE_INTEGER ByteOffset OPTIONAL,
|
||
IN PULONG Key OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This service writes Length bytes of data from the caller's segment
|
||
buffers to the file associated with FileHandle starting at
|
||
StartingBlock|ByteOffset. The actual number of bytes written to the file
|
||
will be returned in the second longword of the IoStatusBlock.
|
||
|
||
If the writer has the file open for APPEND access, then the data will be
|
||
written to the current EOF mark. The StartingBlock and ByteOffset are
|
||
ignored if the caller has APPEND access.
|
||
|
||
Arguments:
|
||
|
||
FileHandle - Supplies a handle to the file to be written.
|
||
|
||
Event - Optionally supplies an event to be set to the Signaled state when
|
||
the write operation is complete.
|
||
|
||
ApcRoutine - Optionally supplies an APC routine to be executed when the
|
||
write operation is complete.
|
||
|
||
ApcContext - Supplies a context parameter to be passed to the APC routine
|
||
when it is invoked, if an APC routine was specified.
|
||
|
||
IoStatusBlock - Supplies the address of the caller's I/O status block.
|
||
|
||
SegmentArray - An array of buffer segment pointers that specify
|
||
where the data should be read from.
|
||
|
||
Length - Length, in bytes, of the data to be written to the file.
|
||
|
||
ByteOffset - Specifies the starting byte offset within the file to begin
|
||
the write operation. If not specified and the file is open for
|
||
synchronous I/O, then the current file position is used. If the
|
||
file is not opened for synchronous I/O and the parameter is not
|
||
specified, then it is in error.
|
||
|
||
Key - Optionally specifies a key to be used if there are locks associated
|
||
with the file.
|
||
|
||
Return Value:
|
||
|
||
The status returned is success if the write operation was properly queued
|
||
to the I/O system. Once the write completes the status of the operation
|
||
can be determined by examining the Status field of the I/O status block.
|
||
|
||
Notes:
|
||
This interface is only supported for no buffering and asynchronous I/O.
|
||
|
||
--*/
|
||
|
||
{
|
||
PIRP irp;
|
||
NTSTATUS status;
|
||
PFILE_OBJECT fileObject;
|
||
PDEVICE_OBJECT deviceObject;
|
||
PFAST_IO_DISPATCH fastIoDispatch;
|
||
PFILE_SEGMENT_ELEMENT capturedArray = NULL;
|
||
KPROCESSOR_MODE requestorMode;
|
||
PMDL mdl;
|
||
PIO_STACK_LOCATION irpSp;
|
||
ACCESS_MASK grantedAccess;
|
||
OBJECT_HANDLE_INFORMATION handleInformation;
|
||
NTSTATUS exceptionCode;
|
||
PKEVENT eventObject = (PKEVENT) NULL;
|
||
ULONG elementCount;
|
||
ULONG keyValue = 0;
|
||
LARGE_INTEGER fileOffset = {0,0};
|
||
PULONG majorFunction;
|
||
ULONG i;
|
||
BOOLEAN synchronousIo;
|
||
PETHREAD CurrentThread;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Get the previous mode; i.e., the mode of the caller.
|
||
//
|
||
|
||
CurrentThread = PsGetCurrentThread ();
|
||
requestorMode = KeGetPreviousModeByThread(&CurrentThread->Tcb);
|
||
|
||
//
|
||
// Reference the file object so the target device can be found and the
|
||
// access rights mask can be used in the following checks for callers in
|
||
// user mode. Note that if the handle does not refer to a file object,
|
||
// then it will fail.
|
||
//
|
||
|
||
status = ObReferenceObjectByHandle( FileHandle,
|
||
0L,
|
||
IoFileObjectType,
|
||
requestorMode,
|
||
(PVOID *) &fileObject,
|
||
&handleInformation);
|
||
if (!NT_SUCCESS( status )) {
|
||
return status;
|
||
}
|
||
|
||
grantedAccess = handleInformation.GrantedAccess;
|
||
|
||
//
|
||
// Get the address of the target device object.
|
||
//
|
||
|
||
deviceObject = IoGetRelatedDeviceObject( fileObject );
|
||
|
||
//
|
||
// Verify this is a valid gather write request. In particular it must
|
||
// be non cached, asynchronous, use completion ports, non buffer I/O
|
||
// device and directed at a file system device.
|
||
//
|
||
|
||
if (!(fileObject->Flags & FO_NO_INTERMEDIATE_BUFFERING) ||
|
||
(fileObject->Flags & FO_SYNCHRONOUS_IO) ||
|
||
deviceObject->Flags & DO_BUFFERED_IO ||
|
||
(deviceObject->DeviceType != FILE_DEVICE_DISK_FILE_SYSTEM &&
|
||
deviceObject->DeviceType != FILE_DEVICE_DFS &&
|
||
deviceObject->DeviceType != FILE_DEVICE_TAPE_FILE_SYSTEM &&
|
||
deviceObject->DeviceType != FILE_DEVICE_CD_ROM_FILE_SYSTEM &&
|
||
deviceObject->DeviceType != FILE_DEVICE_NETWORK_FILE_SYSTEM &&
|
||
deviceObject->DeviceType != FILE_DEVICE_FILE_SYSTEM &&
|
||
deviceObject->DeviceType != FILE_DEVICE_DFS_VOLUME)) {
|
||
|
||
ObDereferenceObject( fileObject );
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
elementCount = BYTES_TO_PAGES( Length );
|
||
|
||
//
|
||
// Check to see if the requestor mode was user. If so, perform a bunch
|
||
// of extra checks.
|
||
//
|
||
|
||
if (requestorMode != KernelMode) {
|
||
|
||
//
|
||
// The caller's access mode is not kernel so probe each of the arguments
|
||
// and capture them as necessary. If any failures occur, the condition
|
||
// handler will be invoked to handle them. It will simply cleanup and
|
||
// return an access violation status code back to the system service
|
||
// dispatcher.
|
||
//
|
||
|
||
//
|
||
// Check to ensure that the caller has either WRITE_DATA or APPEND_DATA
|
||
// access to the file. If not, cleanup and return an access denied
|
||
// error status value. Note that if this is a pipe then the APPEND_DATA
|
||
// access check may not be made since this access code is overlaid with
|
||
// CREATE_PIPE_INSTANCE access.
|
||
//
|
||
|
||
if (!SeComputeGrantedAccesses( grantedAccess, (!(fileObject->Flags & FO_NAMED_PIPE) ? FILE_APPEND_DATA : 0) | FILE_WRITE_DATA )) {
|
||
ObDereferenceObject( fileObject );
|
||
return STATUS_ACCESS_DENIED;
|
||
}
|
||
|
||
//
|
||
// Attempt to probe the caller's parameters within the exception
|
||
// handler block.
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// The IoStatusBlock parameter must be writeable by the caller.
|
||
//
|
||
|
||
ProbeForWriteIoStatusEx( IoStatusBlock , ApcRoutine);
|
||
|
||
//
|
||
// The SegmentArray paramter must be accessible.
|
||
//
|
||
|
||
#ifdef _X86_
|
||
ProbeForRead( SegmentArray,
|
||
elementCount * sizeof( FILE_SEGMENT_ELEMENT ),
|
||
sizeof( ULONG )
|
||
);
|
||
#elif defined(_WIN64)
|
||
|
||
//
|
||
// If we are a wow64 process, follow the X86 rules
|
||
//
|
||
|
||
if (PsGetCurrentProcess()->Wow64Process) {
|
||
ProbeForRead( SegmentArray,
|
||
elementCount * sizeof( FILE_SEGMENT_ELEMENT ),
|
||
sizeof( ULONG )
|
||
);
|
||
} else {
|
||
ProbeForRead( SegmentArray,
|
||
elementCount * sizeof( FILE_SEGMENT_ELEMENT ),
|
||
TYPE_ALIGNMENT( FILE_SEGMENT_ELEMENT )
|
||
);
|
||
}
|
||
#else
|
||
ProbeForRead( SegmentArray,
|
||
elementCount * sizeof( FILE_SEGMENT_ELEMENT ),
|
||
TYPE_ALIGNMENT( FILE_SEGMENT_ELEMENT )
|
||
);
|
||
#endif
|
||
|
||
if (Length != 0) {
|
||
|
||
//
|
||
// Capture the segment array so it cannot be changed after
|
||
// it has been looked at.
|
||
//
|
||
|
||
capturedArray = ExAllocatePoolWithQuota( PagedPool,
|
||
elementCount * sizeof( FILE_SEGMENT_ELEMENT )
|
||
);
|
||
|
||
RtlCopyMemory( capturedArray,
|
||
SegmentArray,
|
||
elementCount * sizeof( FILE_SEGMENT_ELEMENT )
|
||
);
|
||
|
||
SegmentArray = capturedArray;
|
||
|
||
//
|
||
// Verify that all the addresses are page aligned.
|
||
//
|
||
|
||
for (i = 0; i < elementCount; i++) {
|
||
|
||
if ( SegmentArray[i].Alignment & (PAGE_SIZE - 1)) {
|
||
ExRaiseStatus(STATUS_INVALID_PARAMETER);
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// If this file has an I/O completion port associated w/it, then
|
||
// ensure that the caller did not supply an APC routine, as the
|
||
// two are mutually exclusive methods for I/O completion
|
||
// notification.
|
||
//
|
||
|
||
if (fileObject->CompletionContext && IopApcRoutinePresent( ApcRoutine )) {
|
||
|
||
ExRaiseStatus(STATUS_INVALID_PARAMETER);
|
||
|
||
}
|
||
|
||
//
|
||
// Check that the ByteOffset parameter is readable from the
|
||
// caller's mode, if one was specified, and capture it.
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT( ByteOffset )) {
|
||
ProbeForReadSmallStructure( ByteOffset,
|
||
sizeof( LARGE_INTEGER ),
|
||
sizeof( ULONG ) );
|
||
fileOffset = *ByteOffset;
|
||
}
|
||
|
||
//
|
||
// Check to see whether the caller has opened the file without
|
||
// intermediate buffering. If so, perform the following ByteOffset
|
||
// parameter check differently.
|
||
//
|
||
|
||
if (fileObject->Flags & FO_NO_INTERMEDIATE_BUFFERING) {
|
||
|
||
//
|
||
// The file was opened without intermediate buffering enabled.
|
||
// Check that the Buffer is properly aligned, and that the
|
||
// length is an integral number of 512-byte blocks.
|
||
//
|
||
|
||
if ((deviceObject->SectorSize &&
|
||
(Length & (deviceObject->SectorSize - 1)))) {
|
||
|
||
//
|
||
// Check for sector sizes that are not a power of two.
|
||
//
|
||
|
||
if ((deviceObject->SectorSize &&
|
||
Length % deviceObject->SectorSize) ) {
|
||
|
||
ExRaiseStatus(STATUS_INVALID_PARAMETER);
|
||
}
|
||
}
|
||
|
||
//
|
||
// If a ByteOffset parameter was specified, ensure that it is
|
||
// is of the proper type.
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT( ByteOffset )) {
|
||
if (fileOffset.LowPart == FILE_WRITE_TO_END_OF_FILE &&
|
||
fileOffset.HighPart == -1) {
|
||
NOTHING;
|
||
} else if (fileOffset.LowPart == FILE_USE_FILE_POINTER_POSITION &&
|
||
fileOffset.HighPart == -1 &&
|
||
(fileObject->Flags & FO_SYNCHRONOUS_IO)) {
|
||
NOTHING;
|
||
} else if (deviceObject->SectorSize &&
|
||
(fileOffset.LowPart & (deviceObject->SectorSize - 1))) {
|
||
|
||
ExRaiseStatus(STATUS_INVALID_PARAMETER);
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Finally, ensure that if there is a key parameter specified it
|
||
// is readable by the caller.
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT( Key )) {
|
||
keyValue = ProbeAndReadUlong( Key );
|
||
}
|
||
|
||
} except(IopExceptionFilter( GetExceptionInformation(), &exceptionCode )) {
|
||
|
||
//
|
||
// An exception was incurred while attempting to probe the
|
||
// caller's parameters. Simply cleanup, dereference the file
|
||
// object, and return with the appropriate status code.
|
||
//
|
||
|
||
ObDereferenceObject( fileObject );
|
||
|
||
if (capturedArray != NULL) {
|
||
ExFreePool( capturedArray );
|
||
}
|
||
|
||
return exceptionCode;
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// The caller's mode is kernel. Get the appropriate parameters to
|
||
// their expected locations without making all of the checks.
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT( ByteOffset )) {
|
||
fileOffset = *ByteOffset;
|
||
}
|
||
|
||
if (ARGUMENT_PRESENT( Key )) {
|
||
keyValue = *Key;
|
||
}
|
||
#if DBG
|
||
if (fileObject->Flags & FO_NO_INTERMEDIATE_BUFFERING) {
|
||
|
||
//
|
||
// The file was opened without intermediate buffering enabled.
|
||
// Check that the the length is an integral number of the block
|
||
// size.
|
||
//
|
||
|
||
if ((deviceObject->SectorSize &&
|
||
(Length & (deviceObject->SectorSize - 1)))) {
|
||
|
||
//
|
||
// Check for sector sizes that are not a power of two.
|
||
//
|
||
|
||
if ((deviceObject->SectorSize &&
|
||
Length % deviceObject->SectorSize)) {
|
||
ObDereferenceObject( fileObject );
|
||
ASSERT( FALSE );
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
}
|
||
|
||
//
|
||
// If a ByteOffset parameter was specified, ensure that it is
|
||
// is of the proper type.
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT( ByteOffset )) {
|
||
if (fileOffset.LowPart == FILE_WRITE_TO_END_OF_FILE &&
|
||
fileOffset.HighPart == -1) {
|
||
NOTHING;
|
||
} else if (fileOffset.LowPart == FILE_USE_FILE_POINTER_POSITION &&
|
||
fileOffset.HighPart == -1 &&
|
||
(fileObject->Flags & FO_SYNCHRONOUS_IO)) {
|
||
NOTHING;
|
||
} else if (deviceObject->SectorSize &&
|
||
(fileOffset.LowPart & (deviceObject->SectorSize - 1))) {
|
||
ObDereferenceObject( fileObject );
|
||
ASSERT( FALSE );
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (Length != 0) {
|
||
|
||
//
|
||
// Verify that all the addresses are page aligned.
|
||
//
|
||
|
||
for (i = 0; i < elementCount; i++) {
|
||
|
||
if ( SegmentArray[i].Alignment & (PAGE_SIZE - 1)) {
|
||
|
||
ObDereferenceObject( fileObject );
|
||
ASSERT(FALSE);
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
}
|
||
}
|
||
#endif // DBG
|
||
|
||
}
|
||
|
||
//
|
||
// If the caller has only append access to the file, ignore the input
|
||
// parameters and set the ByteOffset to indicate that this write is
|
||
// to the end of the file. Otherwise, ensure that the parameters are
|
||
// valid.
|
||
//
|
||
|
||
if (SeComputeGrantedAccesses( grantedAccess, FILE_APPEND_DATA | FILE_WRITE_DATA ) == FILE_APPEND_DATA) {
|
||
|
||
//
|
||
// This is an append operation to the end of a file. Set the
|
||
// ByteOffset parameter to give drivers a consistent view of
|
||
// this type of call.
|
||
//
|
||
|
||
fileOffset.LowPart = FILE_WRITE_TO_END_OF_FILE;
|
||
fileOffset.HighPart = -1;
|
||
}
|
||
|
||
//
|
||
// Get the address of the event object and set the event to the Not-
|
||
// Signaled state, if an event was specified. Note here too, that if
|
||
// the handle does not refer to an event, then the reference will fail.
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT( Event )) {
|
||
status = ObReferenceObjectByHandle( Event,
|
||
EVENT_MODIFY_STATE,
|
||
ExEventObjectType,
|
||
requestorMode,
|
||
(PVOID *) &eventObject,
|
||
NULL );
|
||
if (!NT_SUCCESS( status )) {
|
||
ObDereferenceObject( fileObject );
|
||
if (capturedArray != NULL) {
|
||
ExFreePool( capturedArray );
|
||
}
|
||
return status;
|
||
} else {
|
||
KeClearEvent( eventObject );
|
||
}
|
||
}
|
||
|
||
//
|
||
// Get the address of the fast io dispatch structure.
|
||
//
|
||
|
||
fastIoDispatch = deviceObject->DriverObject->FastIoDispatch;
|
||
|
||
//
|
||
// Make a special check here to determine whether this is a synchronous
|
||
// I/O operation. If it is, then wait here until the file is owned by
|
||
// the current thread. If the wait terminates with an alerted status,
|
||
// then cleanup and return the alerted status. This allows the caller
|
||
// specify FILE_SYNCHRONOUS_IO_ALERT as a synchronous I/O option.
|
||
//
|
||
// If everything works, then check to see whether a ByteOffset parameter
|
||
// was supplied. If not, or if it was and it is set to the "use file
|
||
// pointer position", then initialize the file offset to be whatever
|
||
// the current byte offset into the file is according to the file pointer
|
||
// context information in the file object.
|
||
//
|
||
|
||
if (fileObject->Flags & FO_SYNCHRONOUS_IO) {
|
||
|
||
BOOLEAN interrupted;
|
||
|
||
if (!IopAcquireFastLock( fileObject )) {
|
||
status = IopAcquireFileObjectLock( fileObject,
|
||
requestorMode,
|
||
(BOOLEAN) ((fileObject->Flags & FO_ALERTABLE_IO) != 0),
|
||
&interrupted );
|
||
if (interrupted) {
|
||
if (eventObject) {
|
||
ObDereferenceObject( eventObject );
|
||
}
|
||
ObDereferenceObject( fileObject );
|
||
if (capturedArray != NULL) {
|
||
ExFreePool( capturedArray );
|
||
}
|
||
return status;
|
||
}
|
||
}
|
||
|
||
synchronousIo = TRUE;
|
||
|
||
if ((!ARGUMENT_PRESENT( ByteOffset ) && !fileOffset.LowPart ) ||
|
||
(fileOffset.LowPart == FILE_USE_FILE_POINTER_POSITION &&
|
||
fileOffset.HighPart == -1 )) {
|
||
fileOffset = fileObject->CurrentByteOffset;
|
||
}
|
||
|
||
} else if (!ARGUMENT_PRESENT( ByteOffset ) && !(fileObject->Flags & (FO_NAMED_PIPE | FO_MAILSLOT))) {
|
||
|
||
//
|
||
// The file is not open for synchronous I/O operations, but the
|
||
// caller did not specify a ByteOffset parameter. This is an error
|
||
// situation, so cleanup and return with the appropriate status.
|
||
//
|
||
|
||
if (eventObject) {
|
||
ObDereferenceObject( eventObject );
|
||
}
|
||
ObDereferenceObject( fileObject );
|
||
if (capturedArray != NULL) {
|
||
ExFreePool( capturedArray );
|
||
}
|
||
return STATUS_INVALID_PARAMETER;
|
||
|
||
} else {
|
||
|
||
//
|
||
// This is not a synchronous I/O operation.
|
||
//
|
||
|
||
synchronousIo = FALSE;
|
||
}
|
||
|
||
//
|
||
// Negative file offsets are illegal.
|
||
//
|
||
|
||
if (fileOffset.HighPart < 0 &&
|
||
(fileOffset.HighPart != -1 ||
|
||
fileOffset.LowPart != FILE_WRITE_TO_END_OF_FILE)) {
|
||
|
||
if (eventObject) {
|
||
ObDereferenceObject( eventObject );
|
||
}
|
||
if (synchronousIo) {
|
||
IopReleaseFileObjectLock( fileObject );
|
||
}
|
||
ObDereferenceObject( fileObject );
|
||
if (capturedArray != NULL) {
|
||
ExFreePool( capturedArray );
|
||
}
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
//
|
||
// Set the file object to the Not-Signaled state.
|
||
//
|
||
|
||
KeClearEvent( &fileObject->Event );
|
||
|
||
//
|
||
// Allocate and initialize the I/O Request Packet (IRP) for this operation.
|
||
// The allocation is performed with an exception handler in case the
|
||
// caller does not have enough quota to allocate the packet.
|
||
//
|
||
|
||
irp = IopAllocateIrp( deviceObject->StackSize, TRUE );
|
||
if (!irp) {
|
||
|
||
//
|
||
// An IRP could not be allocated. Cleanup and return an appropriate
|
||
// error status code.
|
||
//
|
||
|
||
IopAllocateIrpCleanup( fileObject, eventObject );
|
||
|
||
if (capturedArray != NULL) {
|
||
ExFreePool( capturedArray );
|
||
}
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
irp->Tail.Overlay.OriginalFileObject = fileObject;
|
||
irp->Tail.Overlay.Thread = CurrentThread;
|
||
irp->Tail.Overlay.AuxiliaryBuffer = (PVOID) NULL;
|
||
irp->RequestorMode = requestorMode;
|
||
irp->PendingReturned = FALSE;
|
||
irp->Cancel = FALSE;
|
||
irp->CancelRoutine = (PDRIVER_CANCEL) NULL;
|
||
|
||
//
|
||
// Fill in the service independent parameters in the IRP.
|
||
//
|
||
|
||
irp->UserEvent = eventObject;
|
||
irp->UserIosb = IoStatusBlock;
|
||
irp->Overlay.AsynchronousParameters.UserApcRoutine = ApcRoutine;
|
||
irp->Overlay.AsynchronousParameters.UserApcContext = ApcContext;
|
||
|
||
//
|
||
// Get a pointer to the stack location for the first driver. This will be
|
||
// used to pass the original function codes and parameters. Note that
|
||
// setting the major function code here also sets:
|
||
//
|
||
// MinorFunction = 0;
|
||
// Flags = 0;
|
||
// Control = 0;
|
||
//
|
||
|
||
irpSp = IoGetNextIrpStackLocation( irp );
|
||
majorFunction = (PULONG) irpSp;
|
||
*majorFunction = IRP_MJ_WRITE;
|
||
irpSp->FileObject = fileObject;
|
||
if (fileObject->Flags & FO_WRITE_THROUGH) {
|
||
irpSp->Flags = SL_WRITE_THROUGH;
|
||
}
|
||
|
||
//
|
||
// Now determine whether this device expects to have data buffered to it
|
||
// or whether it performs direct I/O. This is based on the DO_BUFFERED_IO
|
||
// flag in the device object. If the flag is set, then a system buffer is
|
||
// allocated and the caller's data is copied into it. Otherwise, a Memory
|
||
// Descriptor List (MDL) is allocated and the caller's buffer is locked
|
||
// down using it.
|
||
//
|
||
|
||
irp->AssociatedIrp.SystemBuffer = (PVOID) NULL;
|
||
irp->MdlAddress = (PMDL) NULL;
|
||
|
||
//
|
||
// This is a direct I/O operation. Allocate an MDL and invoke the
|
||
// memory management routine to lock the buffer into memory. This
|
||
// is done using an exception handler that will perform cleanup if
|
||
// the operation fails. Note that no MDL is allocated, nor is any
|
||
// memory probed or locked if the length of the request was zero.
|
||
//
|
||
|
||
mdl = (PMDL) NULL;
|
||
irp->Flags = 0;
|
||
|
||
if (Length) {
|
||
|
||
try {
|
||
|
||
//
|
||
// Allocate an MDL, charging quota for it, and hang it off of
|
||
// the IRP. Probe and lock the pages associated with the
|
||
// caller's buffer for write access and fill in the MDL with
|
||
// the PFNs of those pages.
|
||
//
|
||
|
||
mdl = IoAllocateMdl( (PVOID)(ULONG_PTR) SegmentArray[0].Buffer, Length, FALSE, TRUE, irp );
|
||
if (mdl == NULL) {
|
||
ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
|
||
}
|
||
|
||
//
|
||
// The address of the first file segment is used as a base
|
||
// address.
|
||
//
|
||
|
||
MmProbeAndLockSelectedPages( mdl,
|
||
SegmentArray,
|
||
requestorMode,
|
||
IoReadAccess );
|
||
|
||
irp->UserBuffer = (PVOID)(ULONG_PTR) SegmentArray[0].Buffer;
|
||
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
|
||
//
|
||
// An exception was incurred while either allocating the MDL
|
||
// or while attempting to probe and lock the caller's buffer.
|
||
// Determine what actually happened, clean everything up, and
|
||
// return an appropriate error status code.
|
||
//
|
||
|
||
IopExceptionCleanup( fileObject,
|
||
irp,
|
||
eventObject,
|
||
(PKEVENT) NULL );
|
||
|
||
if (capturedArray != NULL) {
|
||
ExFreePool( capturedArray );
|
||
}
|
||
return GetExceptionCode();
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// We are done with the captured buffer.
|
||
//
|
||
|
||
if (capturedArray != NULL) {
|
||
ExFreePool( capturedArray );
|
||
}
|
||
|
||
//
|
||
// If this write operation is to be performed without any caching, set the
|
||
// appropriate flag in the IRP so no caching is performed.
|
||
//
|
||
|
||
if (fileObject->Flags & FO_NO_INTERMEDIATE_BUFFERING) {
|
||
irp->Flags |= IRP_NOCACHE | IRP_WRITE_OPERATION | IRP_DEFER_IO_COMPLETION;
|
||
} else {
|
||
irp->Flags |= IRP_WRITE_OPERATION | IRP_DEFER_IO_COMPLETION;
|
||
}
|
||
|
||
//
|
||
// Copy the caller's parameters to the service-specific portion of the
|
||
// IRP.
|
||
//
|
||
|
||
irpSp->Parameters.Write.Length = Length;
|
||
irpSp->Parameters.Write.Key = keyValue;
|
||
irpSp->Parameters.Write.ByteOffset = fileOffset;
|
||
|
||
//
|
||
// Queue the packet, call the driver, and synchronize appopriately with
|
||
// I/O completion.
|
||
//
|
||
|
||
status = IopSynchronousServiceTail( deviceObject,
|
||
irp,
|
||
fileObject,
|
||
TRUE,
|
||
requestorMode,
|
||
synchronousIo,
|
||
WriteTransfer );
|
||
|
||
return status;
|
||
|
||
}
|