If you give each queued IRP a cancel routine, most normal cases are covered (i.e., the afore-mentioned Cases 1 and 2). To be thorough, however, you ought to provide a Cleanup handler to cover Case 3. As you well know, programmers are bound to forget to close a file handle sometime or other.
If you do not provide a cancel routine for IRPs but do provide a Cleanup handler, only Case 3 is handled correctly. This is an unsatisfactory solution. In addition, user mode applications will not be able to cancel IRPs with
Some drivers work by providing a cancel routine only for an IRP while it is queued. Once the IRP actually begins processing, the cancel routine is removed. If you use this technique, be sure to provide a reasonable timeout for real IRP processing. Otherwise, crashed programs will not be able to exit.
DebugPrint IRP Cancelling
Full Cancel and Cleanup support can be quite complicated. This is particularly the case for IRPs put in the device queue for processing in a StartIo routine. A full example for this case is given in Chapter 16.
The DebugPrint driver uses just a cancel routine for its one queued IRP. It does not handle the Cleanup IRP.
The
The I/O Manager uses its
A cancel routine is always called at DISPATCH_LEVEL IRQL holding this Cancel spin lock. The
Listing 14.8 DebugPrint driver cancel routine
VOID DbpCancelIrp(IN PDEVICE_OBJECT fdo, IN PIRP Irp) {
PDEBUG_PRINT_DEVICE_EXTENSION dx = (PDEBUGPRINT_DEVICE_EXTENSION)fdo->DeviceExtension;
IoReleaseCancelSpinLock(Irp->CancelIrql);
// If this is our queued read, then unqueue it
KIRQL irql ;
KeAcquireSpinLock(&dx->ReadIrpLock,&irql);
if (Irp==dx->ReadIrp) {
UnlockDevice(dx);
dx->ReadIrp = NULL;
}
KeReleaseSpinLock(&dx->ReadIrpLock,irql);
// Whatever Irp it is, just cancel it
CompleteIrp(Irp, STATUS_CANCELLED, 0);
}
The DebugPrint driver write routine
DbpWrite first gets the write parameters, and completes Write IRPs with a zero transfer length straightaway. The device file pointer is ignored.
As shown previously, the event list is stored in the device extension in field
Finally,
Listing 14.9 DebugPrint driver write routine
NTSTATUS DbpWrite(IN PDEVICE_OBJECT fdo, IN PIRP Irp) {
PDEBUGPRINT_DEVICE_EXTENSION dx = (PDEBUGPRINT_DEVICE_EXTENSION)fdo->DeviceExtension;
if (!dx->IODisabled) return CompleteIrp(Irp, STATUS_DEVICE_NOT_CONNECTED, 0);
if (!LockDevice(dx)) return CompleteIrp(Irp, STATUS_DELETE_PENDING, 0);
PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);
ULONG BytesTxd = 0;
// Check write len
ULONG WriteLen = IrpStack->Parameters.Write.Length;
if (WriteLen==0) {
UnlockDevice(dx);
return CompleteIrp(Irp, STATUS_SUCCESS, 0);
}
// Copy write data into an event
ULONG Len = sizeof(LIST_ENTRY)+sizeof(ULONG)+WriteLen;
PDEBUGPRINT_EVENT pEvent = (PDEBUGPRINT_EVENT)ExAllocatePool(NonPagedPool,Len);
if (pEvent==NULL) {
UnlockDevice(dx);
return CompleteIrp(Irp,STATUS_INSUFFICIENT_RESOURCES,0);
}
pEvent->Len = WriteLen;
RtlCopyMemory(pEvent->EventData, Irp->AssociatedIrp.SystemBuffer, WriteLen);
// Insert event into event list
ExInterlockedInsertTailList(&dx->EventList,&pEvent-> ListEntry,&dx->EventListLock);
// If read pending, then read it
KIRQL irql;
KeAcquireSpinLock(&dx->ReadIrpLock,&irql);
if (dx->ReadIrp!=NULL) if (ReadEvent(dx, dx->ReadIrp)) {
UnlockDevice(dx);
dx->ReadIrp = NULL;
}
KeReleaseSpinLock(&dx->ReadIrpLock,irql);
// Complete IRP
UnlockDevice(dx);
return CompleteIrp(Irp, STATUS_SUCCESS, WriteLen);
}
The
Listing 14.10 DebugPrint driver ReadEvent routine