You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
426 lines
11 KiB
426 lines
11 KiB
#include <IrpQueue.h>
|
|
#include <winerror.h> // For S_OK, S_FALSE, and E_UNEXPECTED
|
|
|
|
#pragma optimize("w",off)
|
|
#pragma optimize("a",off)
|
|
const int CGuardedIrpQueue::CANCEL_IRPS_ON_DELETE = 0x00000001;
|
|
const int CGuardedIrpQueue::PRESERVE_QUEUE_ORDER = 0x00000002;
|
|
const int CGuardedIrpQueue::LIFO_QUEUE_ORDER = 0x00000004;
|
|
|
|
PIRP CTempIrpQueue::Remove()
|
|
{
|
|
|
|
PIRP pIrp = NULL;
|
|
if(!IsListEmpty(&m_QueueHead))
|
|
{
|
|
PLIST_ENTRY pListEntry;
|
|
if(m_fLIFO)
|
|
{
|
|
pListEntry = RemoveTailList(&m_QueueHead);
|
|
}
|
|
else
|
|
{
|
|
pListEntry = RemoveHeadList(&m_QueueHead);
|
|
}
|
|
|
|
// Get the IRP from the ListEntry in the IRP
|
|
pIrp = (PIRP)CONTAINING_RECORD(pListEntry, IRP, Tail.Overlay.ListEntry);
|
|
}
|
|
return pIrp;
|
|
}
|
|
|
|
void _stdcall DriverCancel(PDEVICE_OBJECT, PIRP pIrp)
|
|
{
|
|
CGuardedIrpQueue *pGuardedIrpQueue;
|
|
pGuardedIrpQueue = reinterpret_cast<CGuardedIrpQueue *>(pIrp->Tail.Overlay.DriverContext[3]);
|
|
pGuardedIrpQueue->CancelIrp(pIrp);
|
|
}
|
|
|
|
void CGuardedIrpQueue::Init(int iFlags, PFN_DEC_IRP_COUNT pfnDecIrpCount, PVOID pvContext)
|
|
{
|
|
m_iFlags = iFlags;
|
|
m_pfnDecIrpCount = pfnDecIrpCount;
|
|
m_pvContext = pvContext;
|
|
InitializeListHead(&m_QueueHead);
|
|
KeInitializeSpinLock(&m_QueueLock);
|
|
}
|
|
|
|
void CGuardedIrpQueue::Destroy(NTSTATUS NtStatus)
|
|
{
|
|
if(m_iFlags & CANCEL_IRPS_ON_DELETE)
|
|
{
|
|
CancelAll(NtStatus);
|
|
}
|
|
ASSERT(IsListEmpty(&m_QueueHead));
|
|
}
|
|
|
|
NTSTATUS CGuardedIrpQueue::Add(PIRP pIrp)
|
|
{
|
|
KIRQL OldIrql;
|
|
KeAcquireSpinLock(&m_QueueLock, &OldIrql);
|
|
return AddImpl(pIrp, OldIrql);
|
|
}
|
|
PIRP CGuardedIrpQueue::Remove()
|
|
{
|
|
KIRQL OldIrql;
|
|
KeAcquireSpinLock(&m_QueueLock, &OldIrql);
|
|
PIRP pIrp = RemoveImpl();
|
|
KeReleaseSpinLock(&m_QueueLock, OldIrql);
|
|
return pIrp;
|
|
}
|
|
PIRP CGuardedIrpQueue::RemoveByPointer(PIRP pIrp)
|
|
{
|
|
KIRQL OldIrql;
|
|
KeAcquireSpinLock(&m_QueueLock, &OldIrql);
|
|
pIrp = RemoveByPointerImpl(pIrp);
|
|
KeReleaseSpinLock(&m_QueueLock, OldIrql);
|
|
return pIrp;
|
|
}
|
|
ULONG CGuardedIrpQueue::RemoveByFileObject(PFILE_OBJECT pFileObject, CTempIrpQueue *pTempIrpQueue)
|
|
{
|
|
KIRQL OldIrql;
|
|
KeAcquireSpinLock(&m_QueueLock, &OldIrql);
|
|
ULONG ulReturn = RemoveByFileObjectImpl(pFileObject, pTempIrpQueue);
|
|
KeReleaseSpinLock(&m_QueueLock, OldIrql);
|
|
return ulReturn;
|
|
}
|
|
ULONG CGuardedIrpQueue::RemoveAll(CTempIrpQueue *pTempIrpQueue)
|
|
{
|
|
KIRQL OldIrql;
|
|
KeAcquireSpinLock(&m_QueueLock, &OldIrql);
|
|
ULONG ulReturn = RemoveAllImpl(pTempIrpQueue);
|
|
KeReleaseSpinLock(&m_QueueLock, OldIrql);
|
|
return ulReturn;
|
|
}
|
|
|
|
NTSTATUS CGuardedIrpQueue::AddImpl(PIRP pIrp, KIRQL OldIrql)
|
|
{
|
|
|
|
BOOLEAN fCancelHere = FALSE;
|
|
|
|
// Mark incoming IRP pending
|
|
IoMarkIrpPending(pIrp);
|
|
|
|
// mark IRP in DriverContext so that we can find this instance of the queue
|
|
// in the cancel routine
|
|
pIrp->Tail.Overlay.DriverContext[3] = reinterpret_cast<PVOID>(this);
|
|
|
|
// Set our cancel routine
|
|
IoSetCancelRoutine(pIrp, DriverCancel);
|
|
|
|
//If the IRP was cancelled before it got to us, don't queue it, mark
|
|
//it to cancel after we release the lock (a few lines down)
|
|
if(pIrp->Cancel)
|
|
{
|
|
IoSetCancelRoutine(pIrp, NULL);
|
|
fCancelHere = TRUE;
|
|
}
|
|
else
|
|
//Queue IRP unless it was marked to cancel
|
|
{
|
|
// Insert Item in Queue (items always added at the tail)
|
|
InsertTailList(&m_QueueHead, &pIrp->Tail.Overlay.ListEntry);
|
|
}
|
|
|
|
//Release spin lock
|
|
KeReleaseSpinLock(&m_QueueLock, OldIrql);
|
|
|
|
//If it had been marked for cancel, do it here
|
|
if(fCancelHere)
|
|
{
|
|
pIrp->IoStatus.Information = 0;
|
|
pIrp->IoStatus.Status = STATUS_CANCELLED;
|
|
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
|
|
m_pfnDecIrpCount(m_pvContext);
|
|
return STATUS_CANCELLED;
|
|
}
|
|
|
|
// return Pending as we have queued the IRP
|
|
return STATUS_PENDING;
|
|
}
|
|
|
|
PIRP CGuardedIrpQueue::RemoveImpl()
|
|
{
|
|
KIRQL OldIrql;
|
|
PIRP pReturnIrp = NULL;
|
|
PLIST_ENTRY pListEntry;
|
|
|
|
// Skip getting the IRP and all, if queue is empty
|
|
if(!IsListEmpty(&m_QueueHead))
|
|
{
|
|
//Remove head or tail depending on LIFO or FIFO (we always add to the tail)
|
|
if(m_iFlags & LIFO_QUEUE_ORDER)
|
|
{
|
|
pListEntry = RemoveTailList(&m_QueueHead);
|
|
}
|
|
else
|
|
{
|
|
pListEntry = RemoveHeadList(&m_QueueHead);
|
|
}
|
|
|
|
// Get the IRP from the ListEntry in the IRP
|
|
pReturnIrp = (PIRP)CONTAINING_RECORD(pListEntry, IRP, Tail.Overlay.ListEntry);
|
|
|
|
// Unset the cancel routine
|
|
IoSetCancelRoutine(pReturnIrp, NULL);
|
|
}
|
|
|
|
// Return the IRP, or NULL if there weren't any
|
|
return pReturnIrp;
|
|
}
|
|
|
|
PIRP CGuardedIrpQueue::RemoveByPointerImpl(PIRP pIrp)
|
|
{
|
|
|
|
PIRP pFoundIrp = NULL;
|
|
PIRP pCurrentIrp;
|
|
PLIST_ENTRY pCurrentListEntry;
|
|
PLIST_ENTRY pQueueFirstItem = NULL;
|
|
|
|
// Pop IRPs off the queue and put them back until we find it
|
|
if( !IsListEmpty(&m_QueueHead) )
|
|
{
|
|
pCurrentListEntry = RemoveHeadList(&m_QueueHead);
|
|
pQueueFirstItem = pCurrentListEntry;
|
|
do{
|
|
//Get the IRP from the entry
|
|
pCurrentIrp = CONTAINING_RECORD(pCurrentListEntry, IRP, Tail.Overlay.ListEntry);
|
|
|
|
//Check for match
|
|
if(pCurrentIrp == pIrp)
|
|
{
|
|
|
|
ASSERT(!pFoundIrp); //serious error, means IRP was in queue twice
|
|
pFoundIrp = pCurrentIrp;
|
|
|
|
//clear the cancel routine (do it here, as we still have the spin lock)
|
|
IoSetCancelRoutine(pFoundIrp, NULL);
|
|
|
|
// If we need to preserve the queue order,
|
|
// keep removing and adding until we are through the list once
|
|
if( m_iFlags & PRESERVE_QUEUE_ORDER )
|
|
{
|
|
//If the list is now empty we are done
|
|
if(IsListEmpty(&m_QueueHead))
|
|
{
|
|
break;
|
|
}
|
|
|
|
// The found entry is not going, back in the list
|
|
// so if it was first, it no longer is.
|
|
if(pQueueFirstItem == pCurrentListEntry)
|
|
{
|
|
pQueueFirstItem = NULL;
|
|
}
|
|
|
|
//Get the next IRP
|
|
pCurrentListEntry = RemoveHeadList(&m_QueueHead);
|
|
pCurrentIrp = CONTAINING_RECORD(pCurrentListEntry, IRP, Tail.Overlay.ListEntry);
|
|
ASSERT(pFoundIrp != pCurrentIrp); //serious error, means IRP was in queue twice
|
|
|
|
//If the first item is NULL (four line up), this new entry is it
|
|
if(!pQueueFirstItem)
|
|
{
|
|
pQueueFirstItem = pCurrentListEntry;
|
|
}
|
|
}
|
|
//If the order does not need to be preserved, we are done
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//This next item cannot be a match, if it was we
|
|
//have moved on to the next one already
|
|
|
|
// Put the IRP back in the queue
|
|
InsertTailList(&m_QueueHead, pCurrentListEntry);
|
|
|
|
// Get the next item (no need to check if list is empty,
|
|
// we just put an item in
|
|
pCurrentListEntry = RemoveHeadList(&m_QueueHead);
|
|
|
|
//check if done
|
|
if (pCurrentListEntry == pQueueFirstItem)
|
|
{
|
|
//put it back, if we are done.
|
|
InsertHeadList(&m_QueueHead, pCurrentListEntry);
|
|
//Mark as NULL, so that we do not iterate again
|
|
pCurrentListEntry = NULL;
|
|
}
|
|
|
|
} while (pCurrentListEntry);
|
|
}
|
|
|
|
//Return the IRP we found, or NULL if it was not in the Queue
|
|
return pFoundIrp;
|
|
}
|
|
|
|
ULONG CGuardedIrpQueue::RemoveByFileObjectImpl(PFILE_OBJECT pFileObject, CTempIrpQueue *pTempIrpQueue)
|
|
{
|
|
|
|
PIRP pCurrentIrp;
|
|
PIO_STACK_LOCATION pIrpStack;
|
|
PLIST_ENTRY pCurrentListEntry;
|
|
PLIST_ENTRY pQueueFirstItem = NULL;
|
|
PLIST_ENTRY pTempQueueListEntry;
|
|
ULONG ulMatchCount=0;
|
|
|
|
//Get the list entry from the temp queue
|
|
pTempQueueListEntry = &pTempIrpQueue->m_QueueHead;
|
|
pTempIrpQueue->m_fLIFO = m_iFlags & LIFO_QUEUE_ORDER;
|
|
|
|
// Pop IRPs off the queue and put them back until we find it
|
|
if( !IsListEmpty(&m_QueueHead) )
|
|
{
|
|
pCurrentListEntry = RemoveHeadList(&m_QueueHead);
|
|
pQueueFirstItem = pCurrentListEntry;
|
|
do{
|
|
|
|
//Get the IRP from the entry
|
|
pCurrentIrp = CONTAINING_RECORD(pCurrentListEntry, IRP, Tail.Overlay.ListEntry);
|
|
|
|
//Get the Stack Location
|
|
pIrpStack = IoGetCurrentIrpStackLocation(pCurrentIrp);
|
|
|
|
//Check for matching file object
|
|
if(pIrpStack->FileObject == pFileObject)
|
|
{
|
|
//Increment match count
|
|
ulMatchCount++;
|
|
|
|
//clear the cancel routine
|
|
IoSetCancelRoutine(pCurrentIrp, NULL);
|
|
|
|
//Move it over to the simple queue
|
|
InsertTailList(pTempQueueListEntry, pCurrentListEntry);
|
|
|
|
//If the list is empty we are done
|
|
if( IsListEmpty(&m_QueueHead) )
|
|
{
|
|
break;
|
|
}
|
|
//If it was the first item in the list, it is no longer
|
|
if(pQueueFirstItem == pCurrentListEntry)
|
|
{
|
|
pQueueFirstItem = NULL;
|
|
}
|
|
|
|
//setup for next iteration
|
|
pCurrentListEntry = RemoveHeadList(&m_QueueHead);
|
|
|
|
//If it was the first item in the list, it is no longer
|
|
if(!pQueueFirstItem)
|
|
{
|
|
pQueueFirstItem = pCurrentListEntry;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Put the IRP back in the queue
|
|
InsertTailList(&m_QueueHead, pCurrentListEntry);
|
|
|
|
// Get the next item (no need to check if list is empty,
|
|
// we just put an item in)
|
|
pCurrentListEntry = RemoveHeadList(&m_QueueHead);
|
|
|
|
//check if done
|
|
if (pCurrentListEntry == pQueueFirstItem)
|
|
{
|
|
//put it back, if we are done.
|
|
InsertHeadList(&m_QueueHead, pCurrentListEntry);
|
|
//Mark as NULL, so that we do not iterate again
|
|
pCurrentListEntry = NULL;
|
|
}
|
|
}
|
|
} while (pCurrentListEntry);
|
|
}
|
|
|
|
//Return the IRP we found, or NULL if it was not in the Queue
|
|
return ulMatchCount;
|
|
}
|
|
|
|
ULONG CGuardedIrpQueue::RemoveAllImpl(CTempIrpQueue *pTempIrpQueue)
|
|
{
|
|
PLIST_ENTRY pCurrentListEntry;
|
|
PIRP pCurrentIrp;
|
|
PLIST_ENTRY pTempQueueListEntry;
|
|
ULONG ulCount=0;
|
|
|
|
//Get a pointer to the simple queue's list entry
|
|
pTempQueueListEntry = &pTempIrpQueue->m_QueueHead;
|
|
pTempIrpQueue->m_fLIFO = m_iFlags & LIFO_QUEUE_ORDER;
|
|
|
|
//Move all the items
|
|
while(!IsListEmpty(&m_QueueHead))
|
|
{
|
|
ulCount++;
|
|
//Get next IRP
|
|
pCurrentListEntry = RemoveHeadList(&m_QueueHead);
|
|
pCurrentIrp = CONTAINING_RECORD(pCurrentListEntry, IRP, Tail.Overlay.ListEntry);
|
|
|
|
//Clear the cancel routine
|
|
IoSetCancelRoutine(pCurrentIrp, NULL);
|
|
|
|
//Move to other list
|
|
InsertTailList(pTempQueueListEntry, pCurrentListEntry);
|
|
}
|
|
|
|
//return count
|
|
return ulCount;
|
|
}
|
|
|
|
void CGuardedIrpQueue::CancelIrp(PIRP pIrp)
|
|
{
|
|
PIRP pFoundIrp = RemoveByPointer(pIrp);
|
|
|
|
//Release the cancel lock
|
|
IoReleaseCancelSpinLock(pIrp->CancelIrql);
|
|
|
|
//If the IRP was found cancel it and decrement IRP count
|
|
if(pFoundIrp)
|
|
{
|
|
pFoundIrp->IoStatus.Information = 0;
|
|
pFoundIrp->IoStatus.Status = STATUS_CANCELLED;
|
|
IoCompleteRequest(pFoundIrp, IO_NO_INCREMENT);
|
|
m_pfnDecIrpCount(m_pvContext);
|
|
}
|
|
}
|
|
|
|
void CGuardedIrpQueue::CancelByFileObject(PFILE_OBJECT pFileObject)
|
|
{
|
|
|
|
CTempIrpQueue TempIrpQueue;
|
|
PIRP pFoundIrp;
|
|
|
|
//Get all the IRP's to cancel
|
|
RemoveByFileObject(pFileObject, &TempIrpQueue);
|
|
|
|
//If the IRP was found cancel it and decrement IRP count
|
|
while(pFoundIrp = TempIrpQueue.Remove())
|
|
{
|
|
pFoundIrp->IoStatus.Information = 0;
|
|
pFoundIrp->IoStatus.Status = STATUS_CANCELLED;
|
|
IoCompleteRequest(pFoundIrp, IO_NO_INCREMENT);
|
|
m_pfnDecIrpCount(m_pvContext);
|
|
}
|
|
}
|
|
|
|
void CGuardedIrpQueue::CancelAll(NTSTATUS NtStatus)
|
|
{
|
|
CTempIrpQueue TempIrpQueue;
|
|
PIRP pFoundIrp;
|
|
|
|
//Get all the IRP's to cancel
|
|
RemoveAll(&TempIrpQueue);
|
|
|
|
//If the IRP was found cancel it and decrement IRP count
|
|
while(pFoundIrp = TempIrpQueue.Remove())
|
|
{
|
|
pFoundIrp->IoStatus.Information = 0;
|
|
pFoundIrp->IoStatus.Status = NtStatus;
|
|
IoCompleteRequest(pFoundIrp, IO_NO_INCREMENT);
|
|
m_pfnDecIrpCount(m_pvContext);
|
|
}
|
|
}
|