#include #include // 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(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(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); } }