windows-nt/Source/XPSP1/NT/termsrv/drivers/ctxdd/muio.c

516 lines
15 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/****************************************************************************/
// 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 );
}