reactos/ntoskrnl/io/iomgr/irp.c
Pierre Schweitzer a3c58ca76f
[NTOSKRNL] Implement the support for reserve IRP in IO
The reserve IRP is an IRP which is allocated on system boot and kept during
the whole system life. Its purpose is to allow page reads in case of
low-memory situations where the system doesn't have enough memory left
to allocate an IRP to read from the page file (would be catastrophic situation).
2018-02-24 14:52:04 +01:00

2016 lines
57 KiB
C

/*
* PROJECT: ReactOS Kernel
* LICENSE: GPL - See COPYING in the top level directory
* FILE: ntoskrnl/io/iomgr/irp.c
* PURPOSE: IRP Handling Functions
* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
* Gunnar Dalsnes
* Filip Navara (navaraf@reactos.org)
* Pierre Schweitzer (pierre@reactos.org)
*/
/* INCLUDES ****************************************************************/
#include <ntoskrnl.h>
#define NDEBUG
#include <debug.h>
PIRP IopDeadIrp;
RESERVE_IRP_ALLOCATOR IopReserveIrpAllocator;
/* PRIVATE FUNCTIONS ********************************************************/
VOID
NTAPI
IopFreeIrpKernelApc(IN PKAPC Apc,
IN PKNORMAL_ROUTINE *NormalRoutine,
IN PVOID *NormalContext,
IN PVOID *SystemArgument1,
IN PVOID *SystemArgument2)
{
/* Free the IRP */
IoFreeIrp(CONTAINING_RECORD(Apc, IRP, Tail.Apc));
}
VOID
NTAPI
IopAbortIrpKernelApc(IN PKAPC Apc)
{
/* Free the IRP */
IoFreeIrp(CONTAINING_RECORD(Apc, IRP, Tail.Apc));
}
NTSTATUS
NTAPI
IopCleanupFailedIrp(IN PFILE_OBJECT FileObject,
IN PKEVENT EventObject OPTIONAL,
IN PVOID Buffer OPTIONAL)
{
PAGED_CODE();
/* Dereference the event */
if (EventObject) ObDereferenceObject(EventObject);
/* Free a buffer, if any */
if (Buffer) ExFreePool(Buffer);
/* If this was a file opened for synch I/O, then unlock it */
if (FileObject->Flags & FO_SYNCHRONOUS_IO) IopUnlockFileObject(FileObject);
/* Now dereference it and return */
ObDereferenceObject(FileObject);
return STATUS_INSUFFICIENT_RESOURCES;
}
VOID
NTAPI
IopAbortInterruptedIrp(IN PKEVENT EventObject,
IN PIRP Irp)
{
KIRQL OldIrql;
BOOLEAN CancelResult;
LARGE_INTEGER Wait;
PAGED_CODE();
/* Raise IRQL to APC */
KeRaiseIrql(APC_LEVEL, &OldIrql);
/* Check if nobody completed it yet */
if (!KeReadStateEvent(EventObject))
{
/* First, cancel it */
CancelResult = IoCancelIrp(Irp);
KeLowerIrql(OldIrql);
/* Check if we cancelled it */
if (CancelResult)
{
/* Wait for the IRP to be cancelled */
Wait.QuadPart = -100000;
while (!KeReadStateEvent(EventObject))
{
/* Delay indefintely */
KeDelayExecutionThread(KernelMode, FALSE, &Wait);
}
}
else
{
/* No cancellation done, so wait for the I/O system to kill it */
KeWaitForSingleObject(EventObject,
Executive,
KernelMode,
FALSE,
NULL);
}
}
else
{
/* We got preempted, so give up */
KeLowerIrql(OldIrql);
}
}
VOID
NTAPI
IopDisassociateThreadIrp(VOID)
{
KIRQL OldIrql, LockIrql;
PETHREAD IrpThread;
PLIST_ENTRY IrpEntry;
PIO_ERROR_LOG_PACKET ErrorLogEntry;
PDEVICE_OBJECT DeviceObject = NULL;
PIO_STACK_LOCATION IoStackLocation;
/* First, raise to APC to protect IrpList */
KeRaiseIrql(APC_LEVEL, &OldIrql);
/* Get the Thread and check the list */
IrpThread = PsGetCurrentThread();
if (IsListEmpty(&IrpThread->IrpList))
{
/* It got completed now, so quit */
KeLowerIrql(OldIrql);
return;
}
/* Ensure no one will come disturb */
LockIrql = KeAcquireQueuedSpinLock(LockQueueIoCompletionLock);
/* Get the misbehaving IRP */
IrpEntry = IrpThread->IrpList.Flink;
IopDeadIrp = CONTAINING_RECORD(IrpEntry, IRP, ThreadListEntry);
IOTRACE(IO_IRP_DEBUG,
"%s - Deassociating IRP %p for %p\n",
__FUNCTION__,
IopDeadIrp,
IrpThread);
/* Don't cancel the IRP if it's already been completed far */
if (IopDeadIrp->CurrentLocation == (IopDeadIrp->StackCount + 2))
{
/* Return */
KeReleaseQueuedSpinLock(LockQueueIoCompletionLock, LockIrql);
KeLowerIrql(OldIrql);
return;
}
/* Disown the IRP! */
IopDeadIrp->Tail.Overlay.Thread = NULL;
RemoveHeadList(&IrpThread->IrpList);
InitializeListHead(&IopDeadIrp->ThreadListEntry);
/* Get the stack location and check if it's valid */
IoStackLocation = IoGetCurrentIrpStackLocation(IopDeadIrp);
if (IopDeadIrp->CurrentLocation <= IopDeadIrp->StackCount)
{
/* Get the device object */
DeviceObject = IoStackLocation->DeviceObject;
}
KeReleaseQueuedSpinLock(LockQueueIoCompletionLock, LockIrql);
/* Lower IRQL now, since we have the pointers we need */
KeLowerIrql(OldIrql);
/* Check if we can send an Error Log Entry*/
if (DeviceObject)
{
/* Allocate an entry */
ErrorLogEntry = IoAllocateErrorLogEntry(DeviceObject,
sizeof(IO_ERROR_LOG_PACKET));
if (ErrorLogEntry)
{
/* Write the entry */
ErrorLogEntry->ErrorCode = IO_DRIVER_CANCEL_TIMEOUT;
IoWriteErrorLogEntry(ErrorLogEntry);
}
}
}
VOID
NTAPI
IopCleanupIrp(IN PIRP Irp,
IN PFILE_OBJECT FileObject)
{
PMDL Mdl;
IOTRACE(IO_IRP_DEBUG,
"%s - Cleaning IRP %p for %p\n",
__FUNCTION__,
Irp,
FileObject);
/* Check if there's an MDL */
while ((Mdl = Irp->MdlAddress))
{
/* Clear all of them */
Irp->MdlAddress = Mdl->Next;
IoFreeMdl(Mdl);
}
/* Check if the IRP has system buffer */
if (Irp->Flags & IRP_DEALLOCATE_BUFFER)
{
/* Free the buffer */
ExFreePoolWithTag(Irp->AssociatedIrp.SystemBuffer, TAG_SYS_BUF);
}
/* Check if this IRP has a user event, a file object, and is async */
if ((Irp->UserEvent) &&
!(Irp->Flags & IRP_SYNCHRONOUS_API) &&
(FileObject))
{
/* Dereference the User Event */
ObDereferenceObject(Irp->UserEvent);
}
/* Check if we have a file object and this isn't a create operation */
if ((FileObject) && !(Irp->Flags & IRP_CREATE_OPERATION))
{
/* Dereference the file object */
ObDereferenceObject(FileObject);
}
/* Free the IRP */
IoFreeIrp(Irp);
}
VOID
NTAPI
IopCompleteRequest(IN PKAPC Apc,
IN PKNORMAL_ROUTINE* NormalRoutine,
IN PVOID* NormalContext,
IN PVOID* SystemArgument1,
IN PVOID* SystemArgument2)
{
PFILE_OBJECT FileObject;
PIRP Irp;
PMDL Mdl, NextMdl;
PVOID Port = NULL, Key = NULL;
BOOLEAN SignaledCreateRequest = FALSE;
/* Get data from the APC */
FileObject = (PFILE_OBJECT)*SystemArgument1;
Irp = CONTAINING_RECORD(Apc, IRP, Tail.Apc);
IOTRACE(IO_IRP_DEBUG,
"%s - Completing IRP %p for %p\n",
__FUNCTION__,
Irp,
FileObject);
/* Sanity check */
ASSERT(Irp->IoStatus.Status != (NTSTATUS)0xFFFFFFFF);
/* Check if we have a file object */
if (*SystemArgument2)
{
/* Check if we're reparsing */
if ((Irp->IoStatus.Status == STATUS_REPARSE) &&
(Irp->IoStatus.Information == IO_REPARSE_TAG_MOUNT_POINT))
{
PREPARSE_DATA_BUFFER ReparseData;
ReparseData = (PREPARSE_DATA_BUFFER)*SystemArgument2;
ASSERT(ReparseData->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT);
ASSERT(ReparseData->ReparseDataLength < MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
ASSERT(ReparseData->Reserved < MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
IopDoNameTransmogrify(Irp, FileObject, ReparseData);
}
}
/* Handle Buffered case first */
if (Irp->Flags & IRP_BUFFERED_IO)
{
/* Check if we have an input buffer and if we succeeded */
if ((Irp->Flags & IRP_INPUT_OPERATION) &&
(Irp->IoStatus.Status != STATUS_VERIFY_REQUIRED) &&
!(NT_ERROR(Irp->IoStatus.Status)))
{
_SEH2_TRY
{
/* Copy the buffer back to the user */
RtlCopyMemory(Irp->UserBuffer,
Irp->AssociatedIrp.SystemBuffer,
Irp->IoStatus.Information);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Fail the IRP */
Irp->IoStatus.Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
}
/* Also check if we should de-allocate it */
if (Irp->Flags & IRP_DEALLOCATE_BUFFER)
{
/* Deallocate it */
ExFreePool(Irp->AssociatedIrp.SystemBuffer);
}
}
/* Now we got rid of these two... */
Irp->Flags &= ~(IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER);
/* Check if there's an MDL */
for (Mdl = Irp->MdlAddress; Mdl; Mdl = NextMdl)
{
/* Free it */
NextMdl = Mdl->Next;
IoFreeMdl(Mdl);
}
/* No MDLs left */
Irp->MdlAddress = NULL;
/*
* Check if either the request was completed without any errors
* (but warnings are OK!), or if it was completed with an error, but
* did return from a pending I/O Operation and is not synchronous.
*/
if (!NT_ERROR(Irp->IoStatus.Status) ||
(Irp->PendingReturned &&
!IsIrpSynchronous(Irp, FileObject)))
{
/* Get any information we need from the FO before we kill it */
if ((FileObject) && (FileObject->CompletionContext))
{
/* Save Completion Data */
Port = FileObject->CompletionContext->Port;
Key = FileObject->CompletionContext->Key;
}
/* Check for UserIos */
if (Irp->UserIosb != NULL)
{
/* Use SEH to make sure we don't write somewhere invalid */
_SEH2_TRY
{
/* Save the IOSB Information */
*Irp->UserIosb = Irp->IoStatus;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Ignore any error */
}
_SEH2_END;
}
/* Check if we have an event or a file object */
if (Irp->UserEvent)
{
/* At the very least, this is a PKEVENT, so signal it always */
KeSetEvent(Irp->UserEvent, 0, FALSE);
/* Check if we also have a File Object */
if (FileObject)
{
/* Check if this is an Asynch API */
if (!(Irp->Flags & IRP_SYNCHRONOUS_API))
{
/* Dereference the event */
ObDereferenceObject(Irp->UserEvent);
}
/*
* Now, if this is a Synch I/O File Object, then this event is
* NOT an actual Executive Event, so we won't dereference it,
* and instead, we will signal the File Object
*/
if ((FileObject->Flags & FO_SYNCHRONOUS_IO) &&
!(Irp->Flags & IRP_OB_QUERY_NAME))
{
/* Signal the file object and set the status */
KeSetEvent(&FileObject->Event, 0, FALSE);
FileObject->FinalStatus = Irp->IoStatus.Status;
}
/*
* This could also be a create operation, in which case we want
* to make sure there's no APC fired.
*/
if (Irp->Flags & IRP_CREATE_OPERATION)
{
/* Clear the APC Routine and remember this */
Irp->Overlay.AsynchronousParameters.UserApcRoutine = NULL;
SignaledCreateRequest = TRUE;
}
}
}
else if (FileObject)
{
/* Signal the file object and set the status */
KeSetEvent(&FileObject->Event, 0, FALSE);
FileObject->FinalStatus = Irp->IoStatus.Status;
/*
* This could also be a create operation, in which case we want
* to make sure there's no APC fired.
*/
if (Irp->Flags & IRP_CREATE_OPERATION)
{
/* Clear the APC Routine and remember this */
Irp->Overlay.AsynchronousParameters.UserApcRoutine = NULL;
SignaledCreateRequest = TRUE;
}
}
/* Update transfer count for everything but create operation */
if (!(Irp->Flags & IRP_CREATE_OPERATION))
{
if (Irp->Flags & IRP_WRITE_OPERATION)
{
/* Update write transfer count */
IopUpdateTransferCount(IopWriteTransfer,
(ULONG)Irp->IoStatus.Information);
}
else if (Irp->Flags & IRP_READ_OPERATION)
{
/* Update read transfer count */
IopUpdateTransferCount(IopReadTransfer,
(ULONG)Irp->IoStatus.Information);
}
else
{
/* Update other transfer count */
IopUpdateTransferCount(IopOtherTransfer,
(ULONG)Irp->IoStatus.Information);
}
}
/* Now that we've signaled the events, de-associate the IRP */
IopUnQueueIrpFromThread(Irp);
/* Now check if a User APC Routine was requested */
if (Irp->Overlay.AsynchronousParameters.UserApcRoutine)
{
/* Initialize it */
KeInitializeApc(&Irp->Tail.Apc,
KeGetCurrentThread(),
CurrentApcEnvironment,
IopFreeIrpKernelApc,
IopAbortIrpKernelApc,
(PKNORMAL_ROUTINE)Irp->
Overlay.AsynchronousParameters.UserApcRoutine,
Irp->RequestorMode,
Irp->
Overlay.AsynchronousParameters.UserApcContext);
/* Queue it */
KeInsertQueueApc(&Irp->Tail.Apc, Irp->UserIosb, NULL, 2);
}
else if ((Port) &&
(Irp->Overlay.AsynchronousParameters.UserApcContext))
{
/* We have an I/O Completion setup... create the special Overlay */
Irp->Tail.CompletionKey = Key;
Irp->Tail.Overlay.PacketType = IopCompletionPacketIrp;
KeInsertQueue(Port, &Irp->Tail.Overlay.ListEntry);
}
else
{
/* Free the IRP since we don't need it anymore */
IoFreeIrp(Irp);
}
/* Check if we have a file object that wasn't part of a create */
if ((FileObject) && !(SignaledCreateRequest))
{
/* Dereference it, since it's not needed anymore either */
ObDereferenceObjectDeferDelete(FileObject);
}
}
else
{
/*
* Either we didn't return from the request, or we did return but this
* request was synchronous.
*/
if ((Irp->PendingReturned) && (FileObject))
{
/* So we did return with a synch operation, was it the IRP? */
if (Irp->Flags & IRP_SYNCHRONOUS_API)
{
/* Yes, this IRP was synchronous, so return the I/O Status */
*Irp->UserIosb = Irp->IoStatus;
/* Now check if the user gave an event */
if (Irp->UserEvent)
{
/* Signal it */
KeSetEvent(Irp->UserEvent, 0, FALSE);
}
else
{
/* No event was given, so signal the FO instead */
KeSetEvent(&FileObject->Event, 0, FALSE);
}
}
else
{
/*
* It's not the IRP that was synchronous, it was the FO
* that was opened this way. Signal its event.
*/
FileObject->FinalStatus = Irp->IoStatus.Status;
KeSetEvent(&FileObject->Event, 0, FALSE);
}
}
/* Now that we got here, we do this for incomplete I/Os as well */
if ((FileObject) && !(Irp->Flags & IRP_CREATE_OPERATION))
{
/* Dereference the File Object unless this was a create */
ObDereferenceObjectDeferDelete(FileObject);
}
/*
* Check if this was an Executive Event (remember that we know this
* by checking if the IRP is synchronous)
*/
if ((Irp->UserEvent) &&
(FileObject) &&
!(Irp->Flags & IRP_SYNCHRONOUS_API))
{
/* This isn't a PKEVENT, so dereference it */
ObDereferenceObject(Irp->UserEvent);
}
/* Now that we've signaled the events, de-associate the IRP */
IopUnQueueIrpFromThread(Irp);
/* Free the IRP as well */
IoFreeIrp(Irp);
}
}
BOOLEAN
NTAPI
IopInitializeReserveIrp(IN PRESERVE_IRP_ALLOCATOR ReserveIrpAllocator)
{
/* Our allocated stack size */
ReserveIrpAllocator->StackSize = 20;
/* Allocate the IRP now */
ReserveIrpAllocator->ReserveIrp = IoAllocateIrp(ReserveIrpAllocator->StackSize, FALSE);
/* If we cannot, abort system boot */
if (ReserveIrpAllocator->ReserveIrp == NULL)
{
return FALSE;
}
/* It's not in use */
ReserveIrpAllocator->ReserveIrpInUse = 0;
/* And init the event */
KeInitializeEvent(&ReserveIrpAllocator->WaitEvent, SynchronizationEvent, FALSE);
/* All good, keep booting */
return TRUE;
}
PIRP
NTAPI
IopAllocateReserveIrp(IN CCHAR StackSize)
{
/* If we need a stack size higher than what was allocated, then fail */
if (StackSize > IopReserveIrpAllocator.StackSize)
{
return NULL;
}
/* Now, wait until the IRP becomes available and reserve it immediately */
while (InterlockedExchange(&IopReserveIrpAllocator.ReserveIrpInUse, 1) == 1)
{
KeWaitForSingleObject(&IopReserveIrpAllocator.WaitEvent,
Executive,
KernelMode,
FALSE,
NULL);
}
/* It's ours! Initialize it */
IoInitializeIrp(IopReserveIrpAllocator.ReserveIrp, IoSizeOfIrp(StackSize), StackSize);
/* And return it to the caller */
return IopReserveIrpAllocator.ReserveIrp;
}
VOID
IopFreeReserveIrp(IN CCHAR PriorityBoost)
{
/* Mark we don't use the IRP anymore */
InterlockedExchange(&IopReserveIrpAllocator.ReserveIrpInUse, 0);
/* And set the event if someone is waiting on the IRP */
KeSetEvent(&IopReserveIrpAllocator.WaitEvent, PriorityBoost, FALSE);
}
/* FUNCTIONS *****************************************************************/
/*
* @implemented
*/
PIRP
NTAPI
IoAllocateIrp(IN CCHAR StackSize,
IN BOOLEAN ChargeQuota)
{
PIRP Irp = NULL;
USHORT Size = IoSizeOfIrp(StackSize);
PKPRCB Prcb;
UCHAR Flags = 0;
PNPAGED_LOOKASIDE_LIST List = NULL;
PP_NPAGED_LOOKASIDE_NUMBER ListType = LookasideSmallIrpList;
/* Set Charge Quota Flag */
if (ChargeQuota) Flags |= IRP_QUOTA_CHARGED;
/* Get the PRCB */
Prcb = KeGetCurrentPrcb();
/* Figure out which Lookaside List to use */
if ((StackSize <= 8) && (ChargeQuota == FALSE || Prcb->LookasideIrpFloat > 0))
{
/* Set Fixed Size Flag */
Flags |= IRP_ALLOCATED_FIXED_SIZE;
/* See if we should use big list */
if (StackSize != 1)
{
Size = IoSizeOfIrp(8);
ListType = LookasideLargeIrpList;
}
/* Get the P List First */
List = (PNPAGED_LOOKASIDE_LIST)Prcb->PPLookasideList[ListType].P;
/* Attempt allocation */
List->L.TotalAllocates++;
Irp = (PIRP)InterlockedPopEntrySList(&List->L.ListHead);
/* Check if the P List failed */
if (!Irp)
{
/* Let the balancer know */
List->L.AllocateMisses++;
/* Try the L List */
List = (PNPAGED_LOOKASIDE_LIST)Prcb->PPLookasideList[ListType].L;
List->L.TotalAllocates++;
Irp = (PIRP)InterlockedPopEntrySList(&List->L.ListHead);
}
}
/* Check if we have to use the pool */
if (!Irp)
{
/* Did we try lookaside and fail? */
if (Flags & IRP_ALLOCATED_FIXED_SIZE) List->L.AllocateMisses++;
/* Check if we should charge quota */
if (ChargeQuota)
{
Irp = ExAllocatePoolWithQuotaTag(NonPagedPool | POOL_QUOTA_FAIL_INSTEAD_OF_RAISE,
Size,
TAG_IRP);
}
else
{
/* Allocate the IRP with no quota charge */
Irp = ExAllocatePoolWithTag(NonPagedPool, Size, TAG_IRP);
}
/* Make sure it was sucessful */
if (!Irp) return NULL;
}
else if (Flags & IRP_QUOTA_CHARGED)
{
/* Decrement lookaside float */
InterlockedDecrement(&Prcb->LookasideIrpFloat);
Flags |= IRP_LOOKASIDE_ALLOCATION;
/* In this case there is no charge quota */
Flags &= ~IRP_QUOTA_CHARGED;
}
/* Now Initialize it */
IoInitializeIrp(Irp, Size, StackSize);
/* Set the Allocation Flags */
Irp->AllocationFlags = Flags;
/* Return it */
IOTRACE(IO_IRP_DEBUG,
"%s - Allocated IRP %p with allocation flags %lx\n",
__FUNCTION__,
Irp,
Flags);
return Irp;
}
/*
* @implemented
*/
PIRP
NTAPI
IopAllocateIrpMustSucceed(IN CCHAR StackSize)
{
LONG i;
PIRP Irp;
LARGE_INTEGER Sleep;
/* Try to get an IRP */
Irp = IoAllocateIrp(StackSize, FALSE);
if (Irp)
return Irp;
/* If we fail, start looping till we may get one */
i = LONG_MAX;
do {
i--;
/* First, sleep for 10ms */
Sleep.QuadPart = -10 * 1000 * 10;
KeDelayExecutionThread(KernelMode, FALSE, &Sleep);
/* Then, retry allocation */
Irp = IoAllocateIrp(StackSize, FALSE);
if (Irp)
return Irp;
} while (i > 0);
return Irp;
}
/*
* @implemented
*/
PIRP
NTAPI
IoBuildAsynchronousFsdRequest(IN ULONG MajorFunction,
IN PDEVICE_OBJECT DeviceObject,
IN PVOID Buffer,
IN ULONG Length,
IN PLARGE_INTEGER StartingOffset,
IN PIO_STATUS_BLOCK IoStatusBlock)
{
PIRP Irp;
PIO_STACK_LOCATION StackPtr;
/* Allocate IRP */
Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
if (!Irp) return NULL;
/* Get the Stack */
StackPtr = IoGetNextIrpStackLocation(Irp);
/* Write the Major function and then deal with it */
StackPtr->MajorFunction = (UCHAR)MajorFunction;
/* Do not handle the following here */
if ((MajorFunction != IRP_MJ_FLUSH_BUFFERS) &&
(MajorFunction != IRP_MJ_SHUTDOWN) &&
(MajorFunction != IRP_MJ_PNP) &&
(MajorFunction != IRP_MJ_POWER))
{
/* Check if this is Buffered IO */
if (DeviceObject->Flags & DO_BUFFERED_IO)
{
/* Allocate the System Buffer */
Irp->AssociatedIrp.SystemBuffer =
ExAllocatePoolWithTag(NonPagedPool, Length, TAG_SYS_BUF);
if (!Irp->AssociatedIrp.SystemBuffer)
{
/* Free the IRP and fail */
IoFreeIrp(Irp);
return NULL;
}
/* Set flags */
Irp->Flags = IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER;
/* Handle special IRP_MJ_WRITE Case */
if (MajorFunction == IRP_MJ_WRITE)
{
/* Copy the buffer data */
RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer, Buffer, Length);
}
else
{
/* Set the Input Operation flag and set this as a User Buffer */
Irp->Flags |= IRP_INPUT_OPERATION;
Irp->UserBuffer = Buffer;
}
}
else if (DeviceObject->Flags & DO_DIRECT_IO)
{
/* Use an MDL for Direct I/O */
Irp->MdlAddress = IoAllocateMdl(Buffer,
Length,
FALSE,
FALSE,
NULL);
if (!Irp->MdlAddress)
{
/* Free the IRP and fail */
IoFreeIrp(Irp);
return NULL;
}
/* Probe and Lock */
_SEH2_TRY
{
/* Do the probe */
MmProbeAndLockPages(Irp->MdlAddress,
KernelMode,
MajorFunction == IRP_MJ_READ ?
IoWriteAccess : IoReadAccess);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Free the IRP and its MDL */
IoFreeMdl(Irp->MdlAddress);
IoFreeIrp(Irp);
/* Fail */
_SEH2_YIELD(return NULL);
}
_SEH2_END;
}
else
{
/* Neither, use the buffer */
Irp->UserBuffer = Buffer;
}
/* Check if this is a read */
if (MajorFunction == IRP_MJ_READ)
{
/* Set the parameters for a read */
StackPtr->Parameters.Read.Length = Length;
StackPtr->Parameters.Read.ByteOffset = *StartingOffset;
}
else if (MajorFunction == IRP_MJ_WRITE)
{
/* Otherwise, set write parameters */
StackPtr->Parameters.Write.Length = Length;
StackPtr->Parameters.Write.ByteOffset = *StartingOffset;
}
}
/* Set the Current Thread and IOSB */
Irp->UserIosb = IoStatusBlock;
Irp->Tail.Overlay.Thread = PsGetCurrentThread();
/* Return the IRP */
IOTRACE(IO_IRP_DEBUG,
"%s - Built IRP %p with Major, Buffer, DO %lx %p %p\n",
__FUNCTION__,
Irp,
MajorFunction,
Buffer,
DeviceObject);
return Irp;
}
/*
* @implemented
*/
PIRP
NTAPI
IoBuildDeviceIoControlRequest(IN ULONG IoControlCode,
IN PDEVICE_OBJECT DeviceObject,
IN PVOID InputBuffer,
IN ULONG InputBufferLength,
IN PVOID OutputBuffer,
IN ULONG OutputBufferLength,
IN BOOLEAN InternalDeviceIoControl,
IN PKEVENT Event,
IN PIO_STATUS_BLOCK IoStatusBlock)
{
PIRP Irp;
PIO_STACK_LOCATION StackPtr;
ULONG BufferLength;
/* Allocate IRP */
Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
if (!Irp) return NULL;
/* Get the Stack */
StackPtr = IoGetNextIrpStackLocation(Irp);
/* Set the DevCtl Type */
StackPtr->MajorFunction = InternalDeviceIoControl ?
IRP_MJ_INTERNAL_DEVICE_CONTROL :
IRP_MJ_DEVICE_CONTROL;
/* Set the IOCTL Data */
StackPtr->Parameters.DeviceIoControl.IoControlCode = IoControlCode;
StackPtr->Parameters.DeviceIoControl.InputBufferLength = InputBufferLength;
StackPtr->Parameters.DeviceIoControl.OutputBufferLength =
OutputBufferLength;
/* Handle the Methods */
switch (IO_METHOD_FROM_CTL_CODE(IoControlCode))
{
/* Buffered I/O */
case METHOD_BUFFERED:
/* Select the right Buffer Length */
BufferLength = InputBufferLength > OutputBufferLength ?
InputBufferLength : OutputBufferLength;
/* Make sure there is one */
if (BufferLength)
{
/* Allocate the System Buffer */
Irp->AssociatedIrp.SystemBuffer =
ExAllocatePoolWithTag(NonPagedPool,
BufferLength,
TAG_SYS_BUF);
if (!Irp->AssociatedIrp.SystemBuffer)
{
/* Free the IRP and fail */
IoFreeIrp(Irp);
return NULL;
}
/* Check if we got a buffer */
if (InputBuffer)
{
/* Copy into the System Buffer */
RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer,
InputBuffer,
InputBufferLength);
}
/* Write the flags */
Irp->Flags = IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER;
if (OutputBuffer) Irp->Flags |= IRP_INPUT_OPERATION;
/* Save the Buffer */
Irp->UserBuffer = OutputBuffer;
}
else
{
/* Clear the Flags and Buffer */
Irp->Flags = 0;
Irp->UserBuffer = NULL;
}
break;
/* Direct I/O */
case METHOD_IN_DIRECT:
case METHOD_OUT_DIRECT:
/* Check if we got an input buffer */
if (InputBuffer)
{
/* Allocate the System Buffer */
Irp->AssociatedIrp.SystemBuffer =
ExAllocatePoolWithTag(NonPagedPool,
InputBufferLength,
TAG_SYS_BUF);
if (!Irp->AssociatedIrp.SystemBuffer)
{
/* Free the IRP and fail */
IoFreeIrp(Irp);
return NULL;
}
/* Copy into the System Buffer */
RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer,
InputBuffer,
InputBufferLength);
/* Write the flags */
Irp->Flags = IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER;
}
else
{
/* Clear the flags */
Irp->Flags = 0;
}
/* Check if we got an output buffer */
if (OutputBuffer)
{
/* Allocate the System Buffer */
Irp->MdlAddress = IoAllocateMdl(OutputBuffer,
OutputBufferLength,
FALSE,
FALSE,
Irp);
if (!Irp->MdlAddress)
{
/* Free the IRP and fail */
IoFreeIrp(Irp);
return NULL;
}
/* Probe and Lock */
_SEH2_TRY
{
/* Do the probe */
MmProbeAndLockPages(Irp->MdlAddress,
KernelMode,
IO_METHOD_FROM_CTL_CODE(IoControlCode) ==
METHOD_IN_DIRECT ?
IoReadAccess : IoWriteAccess);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Free the MDL */
IoFreeMdl(Irp->MdlAddress);
/* Free the input buffer and IRP */
if (InputBuffer) ExFreePool(Irp->AssociatedIrp.SystemBuffer);
IoFreeIrp(Irp);
/* Fail */
_SEH2_YIELD(return NULL);
}
_SEH2_END;
}
break;
case METHOD_NEITHER:
/* Just save the Buffer */
Irp->UserBuffer = OutputBuffer;
StackPtr->Parameters.DeviceIoControl.Type3InputBuffer = InputBuffer;
}
/* Now write the Event and IoSB */
Irp->UserIosb = IoStatusBlock;
Irp->UserEvent = Event;
/* Sync IRPs are queued to requestor thread's irp cancel/cleanup list */
Irp->Tail.Overlay.Thread = PsGetCurrentThread();
IoQueueThreadIrp(Irp);
/* Return the IRP */
IOTRACE(IO_IRP_DEBUG,
"%s - Built IRP %p with IOCTL, Buffers, DO %lx %p %p %p\n",
__FUNCTION__,
Irp,
IoControlCode,
InputBuffer,
OutputBuffer,
DeviceObject);
return Irp;
}
/*
* @implemented
*/
PIRP
NTAPI
IoBuildSynchronousFsdRequest(IN ULONG MajorFunction,
IN PDEVICE_OBJECT DeviceObject,
IN PVOID Buffer,
IN ULONG Length,
IN PLARGE_INTEGER StartingOffset,
IN PKEVENT Event,
IN PIO_STATUS_BLOCK IoStatusBlock)
{
PIRP Irp;
/* Do the big work to set up the IRP */
Irp = IoBuildAsynchronousFsdRequest(MajorFunction,
DeviceObject,
Buffer,
Length,
StartingOffset,
IoStatusBlock );
if (!Irp) return NULL;
/* Set the Event which makes it Syncronous */
Irp->UserEvent = Event;
/* Sync IRPs are queued to requestor thread's irp cancel/cleanup list */
IoQueueThreadIrp(Irp);
return Irp;
}
/*
* @implemented
*/
BOOLEAN
NTAPI
IoCancelIrp(IN PIRP Irp)
{
KIRQL OldIrql;
PDRIVER_CANCEL CancelRoutine;
IOTRACE(IO_IRP_DEBUG,
"%s - Canceling IRP %p\n",
__FUNCTION__,
Irp);
ASSERT(Irp->Type == IO_TYPE_IRP);
/* Acquire the cancel lock and cancel the IRP */
IoAcquireCancelSpinLock(&OldIrql);
Irp->Cancel = TRUE;
/* Clear the cancel routine and get the old one */
CancelRoutine = IoSetCancelRoutine(Irp, NULL);
if (CancelRoutine)
{
/* We had a routine, make sure the IRP isn't completed */
if (Irp->CurrentLocation > (Irp->StackCount + 1))
{
/* It is, bugcheck */
KeBugCheckEx(CANCEL_STATE_IN_COMPLETED_IRP,
(ULONG_PTR)Irp,
(ULONG_PTR)CancelRoutine,
0,
0);
}
/* Set the cancel IRQL And call the routine */
Irp->CancelIrql = OldIrql;
CancelRoutine(IoGetCurrentIrpStackLocation(Irp)->DeviceObject, Irp);
return TRUE;
}
/* Otherwise, release the cancel lock and fail */
IoReleaseCancelSpinLock(OldIrql);
return FALSE;
}
/*
* @implemented
*/
VOID
NTAPI
IoCancelThreadIo(IN PETHREAD Thread)
{
KIRQL OldIrql;
ULONG Retries = 3000;
LARGE_INTEGER Interval;
PLIST_ENTRY ListHead, NextEntry;
PIRP Irp;
PAGED_CODE();
/* Windows isn't using given thread, but using current. */
Thread = PsGetCurrentThread();
IOTRACE(IO_IRP_DEBUG,
"%s - Canceling IRPs for Thread %p\n",
__FUNCTION__,
Thread);
/* Raise to APC to protect the IrpList */
KeRaiseIrql(APC_LEVEL, &OldIrql);
/* Start by cancelling all the IRPs in the current thread queue. */
ListHead = &Thread->IrpList;
NextEntry = ListHead->Flink;
while (ListHead != NextEntry)
{
/* Get the IRP */
Irp = CONTAINING_RECORD(NextEntry, IRP, ThreadListEntry);
/* Cancel it */
IoCancelIrp(Irp);
/* Move to the next entry */
NextEntry = NextEntry->Flink;
}
/* Wait 100 milliseconds */
Interval.QuadPart = -1000000;
/* Wait till all the IRPs are completed or cancelled. */
while (!IsListEmpty(&Thread->IrpList))
{
/* Now we can lower */
KeLowerIrql(OldIrql);
/* Wait a short while and then look if all our IRPs were completed. */
KeDelayExecutionThread(KernelMode, FALSE, &Interval);
/*
* Don't stay here forever if some broken driver doesn't complete
* the IRP.
*/
if (!(Retries--))
{
/* Print out a message and remove the IRP */
DPRINT1("Broken driver did not complete!\n");
IopDisassociateThreadIrp();
}
/* Raise the IRQL Again */
KeRaiseIrql(APC_LEVEL, &OldIrql);
}
/* We're done, lower the IRQL */
KeLowerIrql(OldIrql);
}
/*
* @implemented
*/
#undef IoCallDriver
NTSTATUS
NTAPI
IoCallDriver(IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
{
/* Call fastcall */
return IofCallDriver(DeviceObject, Irp);
}
#define IoCallDriver IofCallDriver
/*
* @implemented
*/
#undef IoCompleteRequest
VOID
NTAPI
IoCompleteRequest(IN PIRP Irp,
IN CCHAR PriorityBoost)
{
/* Call the fastcall */
IofCompleteRequest(Irp, PriorityBoost);
}
#define IoCompleteRequest IofCompleteRequest
/*
* @implemented
*/
VOID
NTAPI
IoEnqueueIrp(IN PIRP Irp)
{
/* This is the same as calling IoQueueThreadIrp */
IoQueueThreadIrp(Irp);
}
/*
* @implemented
*/
NTSTATUS
FASTCALL
IofCallDriver(IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
{
PDRIVER_OBJECT DriverObject;
PIO_STACK_LOCATION StackPtr;
/* Make sure this is a valid IRP */
ASSERT(Irp->Type == IO_TYPE_IRP);
/* Get the Driver Object */
DriverObject = DeviceObject->DriverObject;
/* Decrease the current location and check if */
Irp->CurrentLocation--;
if (Irp->CurrentLocation <= 0)
{
/* This IRP ran out of stack, bugcheck */
KeBugCheckEx(NO_MORE_IRP_STACK_LOCATIONS, (ULONG_PTR)Irp, 0, 0, 0);
}
/* Now update the stack location */
StackPtr = IoGetNextIrpStackLocation(Irp);
Irp->Tail.Overlay.CurrentStackLocation = StackPtr;
/* Get the Device Object */
StackPtr->DeviceObject = DeviceObject;
/* Call it */
return DriverObject->MajorFunction[StackPtr->MajorFunction](DeviceObject,
Irp);
}
FORCEINLINE
VOID
IopClearStackLocation(IN PIO_STACK_LOCATION IoStackLocation)
{
IoStackLocation->MinorFunction = 0;
IoStackLocation->Flags = 0;
IoStackLocation->Control &= SL_ERROR_RETURNED;
IoStackLocation->Parameters.Others.Argument1 = 0;
IoStackLocation->Parameters.Others.Argument2 = 0;
IoStackLocation->Parameters.Others.Argument3 = 0;
IoStackLocation->FileObject = NULL;
}
/*
* @implemented
*/
VOID
FASTCALL
IofCompleteRequest(IN PIRP Irp,
IN CCHAR PriorityBoost)
{
PIO_STACK_LOCATION StackPtr, LastStackPtr;
PDEVICE_OBJECT DeviceObject;
PFILE_OBJECT FileObject;
PETHREAD Thread;
NTSTATUS Status;
PMDL Mdl, NextMdl;
ULONG MasterCount;
PIRP MasterIrp;
ULONG Flags;
NTSTATUS ErrorCode = STATUS_SUCCESS;
PREPARSE_DATA_BUFFER DataBuffer = NULL;
IOTRACE(IO_IRP_DEBUG,
"%s - Completing IRP %p\n",
__FUNCTION__,
Irp);
/* Make sure this IRP isn't getting completed twice or is invalid */
if ((Irp->CurrentLocation) > (Irp->StackCount + 1))
{
/* Bugcheck */
KeBugCheckEx(MULTIPLE_IRP_COMPLETE_REQUESTS, (ULONG_PTR)Irp, 0, 0, 0);
}
/* Some sanity checks */
ASSERT(Irp->Type == IO_TYPE_IRP);
ASSERT(!Irp->CancelRoutine);
ASSERT(Irp->IoStatus.Status != STATUS_PENDING);
ASSERT(Irp->IoStatus.Status != (NTSTATUS)0xFFFFFFFF);
/* Get the last stack */
LastStackPtr = (PIO_STACK_LOCATION)(Irp + 1);
if (LastStackPtr->Control & SL_ERROR_RETURNED)
{
/* Get the error code */
ErrorCode = PtrToUlong(LastStackPtr->Parameters.Others.Argument4);
}
/*
* Start the loop with the current stack and point the IRP to the next stack
* and then keep incrementing the stack as we loop through. The IRP should
* always point to the next stack location w.r.t the one currently being
* analyzed, so completion routine code will see the appropriate value.
* Because of this, we must loop until the current stack location is +1 of
* the stack count, because when StackPtr is at the end, CurrentLocation is +1.
*/
for (StackPtr = IoGetCurrentIrpStackLocation(Irp),
Irp->CurrentLocation++,
Irp->Tail.Overlay.CurrentStackLocation++;
Irp->CurrentLocation <= (Irp->StackCount + 1);
StackPtr++,
Irp->CurrentLocation++,
Irp->Tail.Overlay.CurrentStackLocation++)
{
/* Set Pending Returned */
Irp->PendingReturned = StackPtr->Control & SL_PENDING_RETURNED;
/* Check if we failed */
if (!NT_SUCCESS(Irp->IoStatus.Status))
{
/* Check if it was changed by a completion routine */
if (Irp->IoStatus.Status != ErrorCode)
{
/* Update the error for the current stack */
ErrorCode = Irp->IoStatus.Status;
StackPtr->Control |= SL_ERROR_RETURNED;
LastStackPtr->Parameters.Others.Argument4 = UlongToPtr(ErrorCode);
LastStackPtr->Control |= SL_ERROR_RETURNED;
}
}
/* Check if there is a Completion Routine to Call */
if ((NT_SUCCESS(Irp->IoStatus.Status) &&
(StackPtr->Control & SL_INVOKE_ON_SUCCESS)) ||
(!NT_SUCCESS(Irp->IoStatus.Status) &&
(StackPtr->Control & SL_INVOKE_ON_ERROR)) ||
(Irp->Cancel &&
(StackPtr->Control & SL_INVOKE_ON_CANCEL)))
{
/* Clear the stack location */
IopClearStackLocation(StackPtr);
/* Check for highest-level device completion routines */
if (Irp->CurrentLocation == (Irp->StackCount + 1))
{
/* Clear the DO, since the current stack location is invalid */
DeviceObject = NULL;
}
else
{
/* Otherwise, return the real one */
DeviceObject = IoGetCurrentIrpStackLocation(Irp)->DeviceObject;
}
/* Call the completion routine */
Status = StackPtr->CompletionRoutine(DeviceObject,
Irp,
StackPtr->Context);
/* Don't touch the Packet in this case, since it might be gone! */
if (Status == STATUS_MORE_PROCESSING_REQUIRED) return;
}
else
{
/* Otherwise, check if this is a completed IRP */
if ((Irp->CurrentLocation <= Irp->StackCount) &&
(Irp->PendingReturned))
{
/* Mark it as pending */
IoMarkIrpPending(Irp);
}
/* Clear the stack location */
IopClearStackLocation(StackPtr);
}
}
/* Check if the IRP is an associated IRP */
if (Irp->Flags & IRP_ASSOCIATED_IRP)
{
/* Get the master IRP and count */
MasterIrp = Irp->AssociatedIrp.MasterIrp;
MasterCount = InterlockedDecrement(&MasterIrp->AssociatedIrp.IrpCount);
/* Free the MDLs */
for (Mdl = Irp->MdlAddress; Mdl; Mdl = NextMdl)
{
/* Go to the next one */
NextMdl = Mdl->Next;
IoFreeMdl(Mdl);
}
/* Free the IRP itself */
IoFreeIrp(Irp);
/* Complete the Master IRP */
if (!MasterCount) IofCompleteRequest(MasterIrp, PriorityBoost);
return;
}
/* Check whether we have to reparse */
if (Irp->IoStatus.Status == STATUS_REPARSE)
{
if (Irp->IoStatus.Information > IO_REMOUNT)
{
/* If that's a reparse tag we understand, save the buffer from deletion */
if (Irp->IoStatus.Information == IO_REPARSE_TAG_MOUNT_POINT)
{
ASSERT(Irp->Tail.Overlay.AuxiliaryBuffer != NULL);
DataBuffer = (PREPARSE_DATA_BUFFER)Irp->Tail.Overlay.AuxiliaryBuffer;
Irp->Tail.Overlay.AuxiliaryBuffer = NULL;
}
else
{
Irp->IoStatus.Status = STATUS_IO_REPARSE_TAG_NOT_HANDLED;
}
}
}
/* Check if we have an auxiliary buffer */
if (Irp->Tail.Overlay.AuxiliaryBuffer)
{
/* Free it */
ExFreePool(Irp->Tail.Overlay.AuxiliaryBuffer);
Irp->Tail.Overlay.AuxiliaryBuffer = NULL;
}
/* Check if this is a Paging I/O or Close Operation */
if (Irp->Flags & (IRP_PAGING_IO | IRP_CLOSE_OPERATION))
{
/* Handle a Close Operation or Sync Paging I/O */
if (Irp->Flags & (IRP_SYNCHRONOUS_PAGING_IO | IRP_CLOSE_OPERATION))
{
/* Set the I/O Status and Signal the Event */
Flags = Irp->Flags & (IRP_SYNCHRONOUS_PAGING_IO | IRP_PAGING_IO);
*Irp->UserIosb = Irp->IoStatus;
KeSetEvent(Irp->UserEvent, PriorityBoost, FALSE);
/* Free the IRP for a Paging I/O Only, Close is handled by us */
if (Flags)
{
/* If we were using the reserve IRP, then call the appropriate
* free function (to make the IRP available again)
*/
if (Irp == IopReserveIrpAllocator.ReserveIrp)
{
IopFreeReserveIrp(PriorityBoost);
}
/* Otherwise, free for real! */
else
{
IoFreeIrp(Irp);
}
}
}
else
{
#if 0
/* Page 166 */
KeInitializeApc(&Irp->Tail.Apc
&Irp->Tail.Overlay.Thread->Tcb,
Irp->ApcEnvironment,
IopCompletePageWrite,
NULL,
NULL,
KernelMode,
NULL);
KeInsertQueueApc(&Irp->Tail.Apc,
NULL,
NULL,
PriorityBoost);
#else
/* Not implemented yet. */
UNIMPLEMENTED_DBGBREAK("Not supported!\n");
#endif
}
/* Get out of here */
return;
}
/* Unlock MDL Pages, page 167. */
Mdl = Irp->MdlAddress;
while (Mdl)
{
MmUnlockPages(Mdl);
Mdl = Mdl->Next;
}
/* Check if we should exit because of a Deferred I/O (page 168) */
if ((Irp->Flags & IRP_DEFER_IO_COMPLETION) && !(Irp->PendingReturned))
{
/* Restore the saved reparse buffer for the caller */
if (Irp->IoStatus.Status == STATUS_REPARSE &&
Irp->IoStatus.Information == IO_REPARSE_TAG_MOUNT_POINT)
{
Irp->Tail.Overlay.AuxiliaryBuffer = (PCHAR)DataBuffer;
}
/*
* Return without queuing the completion APC, since the caller will
* take care of doing its own optimized completion at PASSIVE_LEVEL.
*/
return;
}
/* Get the thread and file object */
Thread = Irp->Tail.Overlay.Thread;
FileObject = Irp->Tail.Overlay.OriginalFileObject;
/* Make sure the IRP isn't canceled */
if (!Irp->Cancel)
{
/* Initialize the APC */
KeInitializeApc(&Irp->Tail.Apc,
&Thread->Tcb,
Irp->ApcEnvironment,
IopCompleteRequest,
NULL,
NULL,
KernelMode,
NULL);
/* Queue it */
KeInsertQueueApc(&Irp->Tail.Apc,
FileObject,
DataBuffer,
PriorityBoost);
}
else
{
/* The IRP just got canceled... does a thread still own it? */
if (Thread)
{
/* Yes! There is still hope! Initialize the APC */
KeInitializeApc(&Irp->Tail.Apc,
&Thread->Tcb,
Irp->ApcEnvironment,
IopCompleteRequest,
NULL,
NULL,
KernelMode,
NULL);
/* Queue it */
KeInsertQueueApc(&Irp->Tail.Apc,
FileObject,
DataBuffer,
PriorityBoost);
}
else
{
/* Nothing left for us to do, kill it */
ASSERT(Irp->Cancel);
IopCleanupIrp(Irp, FileObject);
}
}
}
NTSTATUS
NTAPI
IopSynchronousCompletion(IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context)
{
if (Irp->PendingReturned)
KeSetEvent((PKEVENT)Context, IO_NO_INCREMENT, FALSE);
return STATUS_MORE_PROCESSING_REQUIRED;
}
/*
* @implemented
*/
BOOLEAN
NTAPI
IoForwardIrpSynchronously(IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
{
KEVENT Event;
NTSTATUS Status;
/* Check if next stack location is available */
if (Irp->CurrentLocation < Irp->StackCount)
{
/* No more stack location */
return FALSE;
}
/* Initialize event */
KeInitializeEvent(&Event, NotificationEvent, FALSE);
/* Copy stack location for next driver */
IoCopyCurrentIrpStackLocationToNext(Irp);
/* Set a completion routine, which will signal the event */
IoSetCompletionRoutine(Irp, IopSynchronousCompletion, &Event, TRUE, TRUE, TRUE);
/* Call next driver */
Status = IoCallDriver(DeviceObject, Irp);
/* Check if irp is pending */
if (Status == STATUS_PENDING)
{
/* Yes, wait for its completion */
KeWaitForSingleObject(&Event, Suspended, KernelMode, FALSE, NULL);
}
/* Return success */
return TRUE;
}
/*
* @implemented
*/
VOID
NTAPI
IoFreeIrp(IN PIRP Irp)
{
PNPAGED_LOOKASIDE_LIST List;
PP_NPAGED_LOOKASIDE_NUMBER ListType = LookasideSmallIrpList;
PKPRCB Prcb;
IOTRACE(IO_IRP_DEBUG,
"%s - Freeing IRPs %p\n",
__FUNCTION__,
Irp);
/* Make sure the Thread IRP list is empty and that it OK to free it */
ASSERT(Irp->Type == IO_TYPE_IRP);
ASSERT(IsListEmpty(&Irp->ThreadListEntry));
ASSERT(Irp->CurrentLocation >= Irp->StackCount);
/* Get the PRCB */
Prcb = KeGetCurrentPrcb();
/* If this was a lookaside alloc, increment lookaside float */
if (Irp->AllocationFlags & IRP_LOOKASIDE_ALLOCATION)
{
Irp->AllocationFlags &= ~IRP_LOOKASIDE_ALLOCATION;
InterlockedIncrement(&Prcb->LookasideIrpFloat);
}
/* If this was a pool alloc, free it with the pool */
if (!(Irp->AllocationFlags & IRP_ALLOCATED_FIXED_SIZE))
{
/* Free it */
ExFreePoolWithTag(Irp, TAG_IRP);
}
else
{
/* Check if this was a Big IRP */
if (Irp->StackCount != 1) ListType = LookasideLargeIrpList;
/* Use the P List */
List = (PNPAGED_LOOKASIDE_LIST)Prcb->PPLookasideList[ListType].P;
List->L.TotalFrees++;
/* Check if the Free was within the Depth or not */
if (ExQueryDepthSList(&List->L.ListHead) >= List->L.Depth)
{
/* Let the balancer know */
List->L.FreeMisses++;
/* Use the L List */
List = (PNPAGED_LOOKASIDE_LIST)Prcb->PPLookasideList[ListType].L;
List->L.TotalFrees++;
/* Check if the Free was within the Depth or not */
if (ExQueryDepthSList(&List->L.ListHead) >= List->L.Depth)
{
/* All lists failed, use the pool */
List->L.FreeMisses++;
ExFreePoolWithTag(Irp, TAG_IRP);
Irp = NULL;
}
}
/* The free was within the Depth */
if (Irp)
{
/* Remove the association with the process */
if (Irp->AllocationFlags & IRP_QUOTA_CHARGED)
{
ExReturnPoolQuota(Irp);
Irp->AllocationFlags &= ~IRP_QUOTA_CHARGED;
}
/* Add it to the lookaside list */
InterlockedPushEntrySList(&List->L.ListHead,
(PSLIST_ENTRY)Irp);
}
}
}
/*
* @implemented
*/
IO_PAGING_PRIORITY
FASTCALL
IoGetPagingIoPriority(IN PIRP Irp)
{
IO_PAGING_PRIORITY Priority;
ULONG Flags;
/* Get the flags */
Flags = Irp->Flags;
/* Check what priority it has */
if (Flags & IRP_CLASS_CACHE_OPERATION)
{
/* High priority */
Priority = IoPagingPriorityHigh;
}
else if (Flags & IRP_PAGING_IO)
{
/* Normal priority */
Priority = IoPagingPriorityNormal;
}
else
{
/* Invalid -- not a paging IRP */
Priority = IoPagingPriorityInvalid;
}
/* Return the priority */
return Priority;
}
/*
* @implemented
*/
PEPROCESS
NTAPI
IoGetRequestorProcess(IN PIRP Irp)
{
/* Return the requestor process */
if (Irp->Tail.Overlay.Thread)
{
if (Irp->ApcEnvironment == OriginalApcEnvironment)
{
return Irp->Tail.Overlay.Thread->ThreadsProcess;
}
else if (Irp->ApcEnvironment == AttachedApcEnvironment)
{
return (PEPROCESS)Irp->Tail.Overlay.Thread->Tcb.ApcState.Process;
}
}
return NULL;
}
/*
* @implemented
*/
ULONG
NTAPI
IoGetRequestorProcessId(IN PIRP Irp)
{
PEPROCESS Process;
/* Return the requestor process' id */
Process = IoGetRequestorProcess(Irp);
if (Process) return PtrToUlong(Process->UniqueProcessId);
return 0;
}
/*
* @implemented
*/
NTSTATUS
NTAPI
IoGetRequestorSessionId(IN PIRP Irp,
OUT PULONG pSessionId)
{
PEPROCESS Process;
/* Return the session */
if (Irp->Tail.Overlay.Thread)
{
Process = Irp->Tail.Overlay.Thread->ThreadsProcess;
*pSessionId = MmGetSessionId(Process);
return STATUS_SUCCESS;
}
*pSessionId = (ULONG)-1;
return STATUS_UNSUCCESSFUL;
}
/*
* @implemented
*/
PIRP
NTAPI
IoGetTopLevelIrp(VOID)
{
/* Return the IRP */
return (PIRP)PsGetCurrentThread()->TopLevelIrp;
}
/*
* @implemented
*/
VOID
NTAPI
IoInitializeIrp(IN PIRP Irp,
IN USHORT PacketSize,
IN CCHAR StackSize)
{
/* Clear it */
IOTRACE(IO_IRP_DEBUG,
"%s - Initializing IRP %p\n",
__FUNCTION__,
Irp);
RtlZeroMemory(Irp, PacketSize);
/* Set the Header and other data */
Irp->Type = IO_TYPE_IRP;
Irp->Size = PacketSize;
Irp->StackCount = StackSize;
Irp->CurrentLocation = StackSize + 1;
Irp->ApcEnvironment = KeGetCurrentThread()->ApcStateIndex;
Irp->Tail.Overlay.CurrentStackLocation = (PIO_STACK_LOCATION)(Irp + 1) + StackSize;
/* Initialize the Thread List */
InitializeListHead(&Irp->ThreadListEntry);
}
/*
* @implemented
*/
BOOLEAN
NTAPI
IoIsOperationSynchronous(IN PIRP Irp)
{
BOOLEAN SynchIO;
BOOLEAN ForceAsync;
/* If the IRP requests synchronous paging I/O, if the file object was opened
for synchronous I/O, if the IRP_SYNCHRONOUS_API flag is set in the IRP
the operation is synchronous */
SynchIO = (IoGetCurrentIrpStackLocation(Irp)->FileObject->Flags & FO_SYNCHRONOUS_IO) ||
(Irp->Flags & IRP_SYNCHRONOUS_API) || (Irp->Flags & IRP_SYNCHRONOUS_PAGING_IO);
/* If the IRP requests asynchronous paging I/O, the operation is asynchronous,
even if one of the above conditions is true */
ForceAsync = (Irp->Flags & IRP_PAGING_IO) && !(Irp->Flags & IRP_SYNCHRONOUS_PAGING_IO);
/* Check the flags */
if (SynchIO && !ForceAsync)
{
/* Synch API or Paging I/O is OK, as is Sync File I/O */
return TRUE;
}
/* Otherwise, it is an asynchronous operation. */
return FALSE;
}
/*
* @unimplemented
*/
BOOLEAN
NTAPI
IoIsValidNameGraftingBuffer(IN PIRP Irp,
IN PREPARSE_DATA_BUFFER ReparseBuffer)
{
UNIMPLEMENTED;
return FALSE;
}
/*
* @implemented
*/
PIRP
NTAPI
IoMakeAssociatedIrp(IN PIRP Irp,
IN CCHAR StackSize)
{
PIRP AssocIrp;
IOTRACE(IO_IRP_DEBUG,
"%s - Associating IRP %p\n",
__FUNCTION__,
Irp);
/* Allocate the IRP */
AssocIrp = IoAllocateIrp(StackSize, FALSE);
if (!AssocIrp) return NULL;
/* Set the Flags */
AssocIrp->Flags |= IRP_ASSOCIATED_IRP;
/* Set the Thread */
AssocIrp->Tail.Overlay.Thread = Irp->Tail.Overlay.Thread;
/* Associate them */
AssocIrp->AssociatedIrp.MasterIrp = Irp;
return AssocIrp;
}
/*
* @implemented
*/
VOID
NTAPI
IoQueueThreadIrp(IN PIRP Irp)
{
IOTRACE(IO_IRP_DEBUG,
"%s - Queueing IRP %p\n",
__FUNCTION__,
Irp);
/* Use our inlined routine */
IopQueueIrpToThread(Irp);
}
/*
* @implemented
* Reference: Chris Cant's "Writing WDM Device Drivers"
*/
VOID
NTAPI
IoReuseIrp(IN OUT PIRP Irp,
IN NTSTATUS Status)
{
UCHAR AllocationFlags;
IOTRACE(IO_IRP_DEBUG,
"%s - Reusing IRP %p\n",
__FUNCTION__,
Irp);
/* Make sure it's OK to reuse it */
ASSERT(!Irp->CancelRoutine);
ASSERT(IsListEmpty(&Irp->ThreadListEntry));
/* Get the old flags */
AllocationFlags = Irp->AllocationFlags;
/* Reinitialize the IRP */
IoInitializeIrp(Irp, Irp->Size, Irp->StackCount);
/* Duplicate the data */
Irp->IoStatus.Status = Status;
Irp->AllocationFlags = AllocationFlags;
}
/*
* @implemented
*/
VOID
NTAPI
IoSetTopLevelIrp(IN PIRP Irp)
{
/* Set the IRP */
PsGetCurrentThread()->TopLevelIrp = (ULONG_PTR)Irp;
}
#if defined (_WIN64)
BOOLEAN
NTAPI
IoIs32bitProcess(
IN PIRP Irp OPTIONAL)
{
UNIMPLEMENTED;
return FALSE;
}
#endif