516 lines
15 KiB
C
516 lines
15 KiB
C
/****************************************************************************/
|
|
// io.c
|
|
//
|
|
// Kernel file I/O utility functions.
|
|
//
|
|
// Copyright (C) 1997-2000 Microsoft Corporation
|
|
/****************************************************************************/
|
|
|
|
#include <ntddk.h>
|
|
#include <ctxdd.h>
|
|
|
|
|
|
//
|
|
// External references
|
|
//
|
|
VOID IoQueueThreadIrp(IN PIRP);
|
|
|
|
|
|
/*=============================================================================
|
|
== Internal Functions Defined
|
|
=============================================================================*/
|
|
|
|
NTSTATUS
|
|
_CtxDoFileIo(
|
|
IN ULONG MajorFunction,
|
|
IN PFILE_OBJECT fileObject,
|
|
IN PVOID Buffer,
|
|
IN ULONG Length,
|
|
IN PKEVENT pEvent,
|
|
OUT PIO_STATUS_BLOCK pIosb,
|
|
OUT PIRP *ppIrp
|
|
);
|
|
|
|
NTSTATUS
|
|
_CtxDeviceControlComplete(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
);
|
|
|
|
|
|
/*******************************************************************************
|
|
* CtxReadFile
|
|
*
|
|
* Kernel read file routine.
|
|
*
|
|
* ENTRY:
|
|
* fileObject (input)
|
|
* pointer to file object for I/O
|
|
* Buffer (input)
|
|
* pointer to read buffer
|
|
* Length (input)
|
|
* length of read buffer
|
|
* pEvent (input)
|
|
* pointer to I/O event (optional)
|
|
* pIosb (output)
|
|
* pointer to IoStatus block (optional)
|
|
******************************************************************************/
|
|
NTSTATUS CtxReadFile(
|
|
IN PFILE_OBJECT fileObject,
|
|
IN PVOID Buffer,
|
|
IN ULONG Length,
|
|
IN PKEVENT pEvent OPTIONAL,
|
|
OUT PIO_STATUS_BLOCK pIosb OPTIONAL,
|
|
OUT PIRP *ppIrp OPTIONAL)
|
|
{
|
|
return _CtxDoFileIo(IRP_MJ_READ, fileObject, Buffer, Length, pEvent, pIosb, ppIrp);
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
* CtxWriteFile
|
|
*
|
|
* Kernel write file routine.
|
|
*
|
|
* ENTRY:
|
|
* fileObject (input)
|
|
* pointer to file object for I/O
|
|
* Buffer (input)
|
|
* pointer to write buffer
|
|
* Length (input)
|
|
* length of write buffer
|
|
* pEvent (input)
|
|
* pointer to I/O event (optional)
|
|
* pIosb (output)
|
|
* pointer to IoStatus block (optional)
|
|
******************************************************************************/
|
|
NTSTATUS CtxWriteFile(
|
|
IN PFILE_OBJECT fileObject,
|
|
IN PVOID Buffer,
|
|
IN ULONG Length,
|
|
IN PKEVENT pEvent OPTIONAL,
|
|
OUT PIO_STATUS_BLOCK pIosb OPTIONAL,
|
|
OUT PIRP *ppIrp OPTIONAL)
|
|
{
|
|
return _CtxDoFileIo(IRP_MJ_WRITE, fileObject, Buffer, Length, pEvent, pIosb, ppIrp);
|
|
}
|
|
|
|
|
|
NTSTATUS _CtxDoFileIo(
|
|
IN ULONG MajorFunction,
|
|
IN PFILE_OBJECT fileObject,
|
|
IN PVOID Buffer,
|
|
IN ULONG Length,
|
|
IN PKEVENT pEvent,
|
|
OUT PIO_STATUS_BLOCK pIosb,
|
|
OUT PIRP *ppIrp)
|
|
{
|
|
PDEVICE_OBJECT deviceObject;
|
|
LARGE_INTEGER Offset;
|
|
PIRP irp;
|
|
PIO_STACK_LOCATION irpSp;
|
|
NTSTATUS status;
|
|
KIRQL irql;
|
|
static IO_STATUS_BLOCK Iosb;
|
|
|
|
/*
|
|
* We don't support synchronous (i.e. locked) file I/O.
|
|
*/
|
|
ASSERT( !(fileObject->Flags & FO_SYNCHRONOUS_IO) );
|
|
if ((fileObject->Flags & FO_SYNCHRONOUS_IO)) {
|
|
return STATUS_INVALID_PARAMETER_MIX;
|
|
}
|
|
|
|
/*
|
|
* If caller specified an event, clear it before we begin.
|
|
*/
|
|
if (pEvent) {
|
|
KeClearEvent(pEvent);
|
|
}
|
|
|
|
/*
|
|
* If the caller does not supply an IOSB, supply
|
|
* a static one to avoid the overhead of the exception
|
|
* handler in the IO completion APC. Since the caller(s)
|
|
* do not care about the result, we can point all such
|
|
* callers to the same one.
|
|
*/
|
|
if (pIosb == NULL) {
|
|
pIosb = &Iosb;
|
|
}
|
|
|
|
/*
|
|
* Get the DeviceObject for this file
|
|
*/
|
|
deviceObject = IoGetRelatedDeviceObject(fileObject);
|
|
|
|
/*
|
|
* Build the IRP for this request
|
|
*/
|
|
Offset.LowPart = FILE_WRITE_TO_END_OF_FILE;
|
|
Offset.HighPart = -1;
|
|
irp = IoBuildAsynchronousFsdRequest(MajorFunction, deviceObject,
|
|
Buffer, Length, &Offset, pIosb);
|
|
if (irp == NULL)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
/*
|
|
* Save callers event pointer.
|
|
* Also, we must set IRP_SYNCHRONOUS_API in the IRP flags so that
|
|
* the I/O completion code will NOT attempt to dereference the
|
|
* event object, since it is not a real object manager object.
|
|
*/
|
|
irp->UserEvent = pEvent;
|
|
irp->Flags |= IRP_SYNCHRONOUS_API;
|
|
|
|
/*
|
|
* Reference the file object since it will be dereferenced in the
|
|
* I/O completion code, and save a pointer to it in the IRP.
|
|
*/
|
|
ObReferenceObject(fileObject);
|
|
irp->Tail.Overlay.OriginalFileObject = fileObject;
|
|
irpSp = IoGetNextIrpStackLocation(irp);
|
|
irpSp->FileObject = fileObject;
|
|
|
|
/*
|
|
* Set the address of the current thread in the packet so the
|
|
* completion code will have a context to execute in.
|
|
*/
|
|
irp->Tail.Overlay.Thread = PsGetCurrentThread();
|
|
|
|
//
|
|
// Queue the IRP to the current thread
|
|
//
|
|
IoQueueThreadIrp(irp);
|
|
|
|
//
|
|
// Call driver
|
|
//
|
|
status = IoCallDriver( deviceObject, irp );
|
|
|
|
//
|
|
// If irp->UserEvent == NULL, IO completion will set the file
|
|
// object event and status.
|
|
//
|
|
if (pEvent == NULL) {
|
|
if (status == STATUS_PENDING) {
|
|
status = KeWaitForSingleObject(&fileObject->Event,
|
|
Executive,
|
|
KernelMode, // Prevent KSTACK from paging
|
|
FALSE, // Non-alertable
|
|
(PLARGE_INTEGER)NULL);
|
|
|
|
ASSERT(status != STATUS_ALERTED);
|
|
ASSERT(status != STATUS_USER_APC);
|
|
|
|
status = fileObject->FinalStatus;
|
|
}
|
|
}
|
|
|
|
// This crappy function interface is broken -- returning the IRP
|
|
// pointer could corrupt the system, since it could be completed
|
|
// and deallocated before the return completes and the caller
|
|
// attempts to use the pointer. To get the IRP back the caller
|
|
// would have had to set a completion routine, but we use
|
|
// IoBuildAsynchronousFsdRequest() which does not allow that.
|
|
// Want to change the interface to get rid of the OPTIONAL
|
|
// junk -- TermDD only uses the write() interface and always
|
|
// passes NULL for everything -- but who knows whether Citrix
|
|
// uses this internally?
|
|
// Set the return pointer to NULL to cause the caller to fault.
|
|
if (pEvent != NULL && ppIrp != NULL)
|
|
*ppIrp = NULL;
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
* CtxDeviceIoControlFile
|
|
*
|
|
* Kernel DeviceIoControl routine
|
|
*
|
|
* ENTRY:
|
|
* fileObject (input)
|
|
* pointer to file object for I/O
|
|
* IoControlCode (input)
|
|
* Io control code
|
|
* InputBuffer (input)
|
|
* pointer to input buffer (optional)
|
|
* InputBufferLength (input)
|
|
* length of input buffer
|
|
* OutputBuffer (input)
|
|
* pointer to output buffer (optional)
|
|
* OutputBufferLength (input)
|
|
* length of output buffer
|
|
* InternalDeviceIoControl (input)
|
|
* if TRUE, use IOCTL_INTERNAL_DEVICE_IO_CONTROL
|
|
* pEvent (input)
|
|
* pointer to I/O event (optional)
|
|
* pIosb (output)
|
|
* pointer to IoStatus block (optional)
|
|
******************************************************************************/
|
|
NTSTATUS CtxDeviceIoControlFile(
|
|
IN PFILE_OBJECT fileObject,
|
|
IN ULONG IoControlCode,
|
|
IN PVOID InputBuffer OPTIONAL,
|
|
IN ULONG InputBufferLength,
|
|
OUT PVOID OutputBuffer OPTIONAL,
|
|
IN ULONG OutputBufferLength,
|
|
IN BOOLEAN InternalDeviceIoControl,
|
|
IN PKEVENT pEvent OPTIONAL,
|
|
OUT PIO_STATUS_BLOCK pIosb,
|
|
OUT PIRP *ppIrp OPTIONAL)
|
|
{
|
|
PDEVICE_OBJECT deviceObject;
|
|
PIRP irp;
|
|
PIO_STACK_LOCATION irpSp;
|
|
NTSTATUS status;
|
|
|
|
/*
|
|
* We don't support synchronous (i.e. locked) file I/O.
|
|
*/
|
|
ASSERT( !(fileObject->Flags & FO_SYNCHRONOUS_IO) );
|
|
if ( (fileObject->Flags & FO_SYNCHRONOUS_IO) ) {
|
|
return( STATUS_INVALID_PARAMETER_MIX );
|
|
}
|
|
|
|
/*
|
|
* If caller specified an event, clear it before we begin.
|
|
*/
|
|
if (pEvent) {
|
|
KeClearEvent(pEvent);
|
|
}
|
|
|
|
/*
|
|
* Get the DeviceObject for this file
|
|
*/
|
|
deviceObject = IoGetRelatedDeviceObject( fileObject );
|
|
|
|
/*
|
|
* Build the IRP for this request
|
|
*/
|
|
irp = IoBuildDeviceIoControlRequest( IoControlCode, deviceObject,
|
|
InputBuffer, InputBufferLength,
|
|
OutputBuffer, OutputBufferLength,
|
|
InternalDeviceIoControl,
|
|
pEvent, pIosb );
|
|
if ( irp == NULL )
|
|
return( STATUS_INSUFFICIENT_RESOURCES );
|
|
|
|
/*
|
|
* Reference the file object since it will be dereferenced in the
|
|
* I/O completion code, and save a pointer to it in the IRP.
|
|
* Also, we must set IRP_SYNCHRONOUS_API in the IRP flags so that
|
|
* the I/O completion code will NOT attempt to dereference the
|
|
* event object, since it is not a real object manager object.
|
|
*/
|
|
ObReferenceObject( fileObject );
|
|
irp->Tail.Overlay.OriginalFileObject = fileObject;
|
|
irpSp = IoGetNextIrpStackLocation( irp );
|
|
irpSp->FileObject = fileObject;
|
|
irp->Flags |= IRP_SYNCHRONOUS_API;
|
|
|
|
/*
|
|
* Call the driver
|
|
*/
|
|
status = IoCallDriver(deviceObject, irp);
|
|
|
|
/*
|
|
* If the caller did not specify a wait event and the I/O is pending,
|
|
* then we must wait for the I/O to complete before we return.
|
|
*/
|
|
if (pEvent == NULL) {
|
|
if (status == STATUS_PENDING) {
|
|
status = KeWaitForSingleObject(&fileObject->Event, UserRequest,
|
|
KernelMode, FALSE, NULL);
|
|
if (status == STATUS_SUCCESS)
|
|
status = fileObject->FinalStatus;
|
|
}
|
|
|
|
/*
|
|
* Caller specified a wait event.
|
|
* Return the Irp pointer if the caller specified a return pointer.
|
|
*/
|
|
} else {
|
|
if (ppIrp)
|
|
*ppIrp = irp;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
* CtxInternalDeviceIoControlFile
|
|
*
|
|
* Kernel DeviceIoControl routine
|
|
*
|
|
* ENTRY:
|
|
* fileObject (input)
|
|
* pointer to file object for I/O
|
|
* IrpParameters (input)
|
|
* information to write to the parameters section of the
|
|
* stack location of the IRP.
|
|
* IrpParametersLength (input)
|
|
* length of the parameter information. Cannot be greater than 16.
|
|
* MdlBuffer (input)
|
|
* if non-NULL, a buffer of nonpaged pool to be mapped
|
|
* into an MDL and placed in the MdlAddress field of the IRP.
|
|
* MdlBufferLength (input)
|
|
* the size of the buffer pointed to by MdlBuffer.
|
|
* MinorFunction (input)
|
|
* the minor function code for the request.
|
|
* pEvent (input)
|
|
* pointer to I/O event (optional)
|
|
* pIosb (output)
|
|
* pointer to IoStatus block (optional)
|
|
******************************************************************************/
|
|
NTSTATUS CtxInternalDeviceIoControlFile(
|
|
IN PFILE_OBJECT fileObject,
|
|
IN PVOID IrpParameters,
|
|
IN ULONG IrpParametersLength,
|
|
IN PVOID MdlBuffer OPTIONAL,
|
|
IN ULONG MdlBufferLength,
|
|
IN UCHAR MinorFunction,
|
|
IN PKEVENT pEvent OPTIONAL,
|
|
OUT PIO_STATUS_BLOCK pIosb OPTIONAL,
|
|
OUT PIRP *ppIrp OPTIONAL)
|
|
{
|
|
PDEVICE_OBJECT deviceObject;
|
|
PIRP irp;
|
|
PIO_STACK_LOCATION irpSp;
|
|
PMDL mdl;
|
|
NTSTATUS status;
|
|
|
|
/*
|
|
* We don't support synchronous (i.e. locked) file I/O.
|
|
*/
|
|
ASSERT( !(fileObject->Flags & FO_SYNCHRONOUS_IO) );
|
|
if ( (fileObject->Flags & FO_SYNCHRONOUS_IO) ) {
|
|
return( STATUS_INVALID_PARAMETER_MIX );
|
|
}
|
|
|
|
/*
|
|
* If caller specified an event, clear it before we begin.
|
|
*/
|
|
if ( pEvent ) {
|
|
KeClearEvent( pEvent );
|
|
}
|
|
|
|
/*
|
|
* Get the DeviceObject for this file
|
|
*/
|
|
deviceObject = IoGetRelatedDeviceObject( fileObject );
|
|
|
|
/*
|
|
* Build the IRP for this request
|
|
*/
|
|
irp = IoBuildDeviceIoControlRequest( 0, deviceObject,
|
|
NULL, 0,
|
|
NULL, 0,
|
|
TRUE,
|
|
pEvent, pIosb );
|
|
if ( irp == NULL )
|
|
return( STATUS_INSUFFICIENT_RESOURCES );
|
|
|
|
/*
|
|
* If an MDL buffer was specified, get an MDL, map the buffer,
|
|
* and place the MDL pointer in the IRP.
|
|
*/
|
|
if ( MdlBuffer != NULL ) {
|
|
mdl = IoAllocateMdl(
|
|
MdlBuffer,
|
|
MdlBufferLength,
|
|
FALSE,
|
|
FALSE,
|
|
irp
|
|
);
|
|
if ( mdl == NULL ) {
|
|
IoFreeIrp( irp );
|
|
ObDereferenceObject( fileObject );
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
MmBuildMdlForNonPagedPool( mdl );
|
|
} else {
|
|
irp->MdlAddress = NULL;
|
|
}
|
|
|
|
/*
|
|
* Reference the file object since it will be dereferenced in the
|
|
* I/O completion code, and save a pointer to it in the IRP.
|
|
* Also, we must set IRP_SYNCHRONOUS_API in the IRP flags so that
|
|
* the I/O completion code will NOT attempt to dereference the
|
|
* event object, since it is not a real object manager object.
|
|
*/
|
|
ObReferenceObject( fileObject );
|
|
irp->Tail.Overlay.OriginalFileObject = fileObject;
|
|
irpSp = IoGetNextIrpStackLocation( irp );
|
|
irpSp->FileObject = fileObject;
|
|
irp->Flags |= IRP_SYNCHRONOUS_API;
|
|
|
|
/*
|
|
* Fill in the service-dependent parameters for the request.
|
|
*/
|
|
irpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
|
|
irpSp->MinorFunction = MinorFunction;
|
|
|
|
ASSERT( IrpParametersLength <= sizeof(irpSp->Parameters) );
|
|
RtlCopyMemory( &irpSp->Parameters, IrpParameters, IrpParametersLength );
|
|
|
|
/*
|
|
* Set up a completion routine which we'll use to free the MDL
|
|
* allocated previously.
|
|
*/
|
|
IoSetCompletionRoutine( irp, _CtxDeviceControlComplete, NULL, TRUE, TRUE, TRUE );
|
|
|
|
/*
|
|
* Call the driver
|
|
*/
|
|
status = IoCallDriver( deviceObject, irp );
|
|
|
|
/*
|
|
* If the caller did not specify a wait event and the I/O is pending,
|
|
* then we must wait for the I/O to complete before we return.
|
|
*/
|
|
if ( pEvent == NULL ) {
|
|
if ( status == STATUS_PENDING ) {
|
|
status = KeWaitForSingleObject( &fileObject->Event, UserRequest, KernelMode, FALSE, NULL );
|
|
if ( status == STATUS_SUCCESS )
|
|
status = fileObject->FinalStatus;
|
|
}
|
|
|
|
/*
|
|
* Caller specified a wait event.
|
|
* Return the Irp pointer if the caller specified a return pointer.
|
|
*/
|
|
} else {
|
|
if ( ppIrp )
|
|
*ppIrp = irp;
|
|
}
|
|
|
|
return( status );
|
|
}
|
|
|
|
|
|
NTSTATUS _CtxDeviceControlComplete(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context)
|
|
{
|
|
//
|
|
// If there was an MDL in the IRP, free it and reset the pointer to
|
|
// NULL. The IO system can't handle a nonpaged pool MDL being freed
|
|
// in an IRP, which is why we do it here.
|
|
//
|
|
if ( Irp->MdlAddress != NULL ) {
|
|
IoFreeMdl( Irp->MdlAddress );
|
|
Irp->MdlAddress = NULL;
|
|
}
|
|
|
|
return( STATUS_SUCCESS );
|
|
}
|
|
|