VOID ReleasePendingIrp( __in PIRP_LIST PendingIrp ) { PLIST_ENTRY listHead; LIST_ENTRY completeList; PIRP_ENTRY irpEntry; KIRQL oldIrql; PIRP irp; InitializeListHead(&completeList); ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); KeAcquireSpinLock(&PendingIrp->ListLock, &oldIrql); while (!IsListEmpty(&PendingIrp->ListHead)) { listHead = RemoveHeadList(&PendingIrp->ListHead); irpEntry = CONTAINING_RECORD(listHead, IRP_ENTRY, ListEntry); irp = irpEntry->Irp; if (irp == NULL) { // this IRP has already been canceled ASSERT(irpEntry->CancelRoutineFreeMemory == FALSE); DokanFreeIrpEntry(irpEntry); continue; } if (IoSetCancelRoutine(irp, NULL) == NULL) { // Cancel routine will run as soon as we release the lock InitializeListHead(&irpEntry->ListEntry); irpEntry->CancelRoutineFreeMemory = TRUE; continue; } InsertTailList(&completeList, &irpEntry->ListEntry); } KeClearEvent(&PendingIrp->NotEmpty); KeReleaseSpinLock(&PendingIrp->ListLock, oldIrql); while (!IsListEmpty(&completeList)) { listHead = RemoveHeadList(&completeList); irpEntry = CONTAINING_RECORD(listHead, IRP_ENTRY, ListEntry); irp = irpEntry->Irp; irp->IoStatus.Information = 0; irp->IoStatus.Status = STATUS_SUCCESS; DokanFreeIrpEntry(irpEntry); IoCompleteRequest(irp, IO_NO_INCREMENT); } }
NTSTATUS ReleaseTimeoutPendingIrp(__in PDokanDCB Dcb) { KIRQL oldIrql; PLIST_ENTRY thisEntry, nextEntry, listHead; PIRP_ENTRY irpEntry; LARGE_INTEGER tickCount; LIST_ENTRY completeList; PIRP irp; DDbgPrint("==> ReleaseTimeoutPendingIRP\n"); InitializeListHead(&completeList); ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); KeAcquireSpinLock(&Dcb->PendingIrp.ListLock, &oldIrql); // when IRP queue is empty, there is nothing to do if (IsListEmpty(&Dcb->PendingIrp.ListHead)) { KeReleaseSpinLock(&Dcb->PendingIrp.ListLock, oldIrql); DDbgPrint(" IrpQueue is Empty\n"); return STATUS_SUCCESS; } KeQueryTickCount(&tickCount); // search timeout IRP through pending IRP list listHead = &Dcb->PendingIrp.ListHead; for (thisEntry = listHead->Flink; thisEntry != listHead; thisEntry = nextEntry) { nextEntry = thisEntry->Flink; irpEntry = CONTAINING_RECORD(thisEntry, IRP_ENTRY, ListEntry); // this IRP is NOT timeout yet if (tickCount.QuadPart < irpEntry->TickCount.QuadPart) { break; } RemoveEntryList(thisEntry); DDbgPrint(" timeout Irp #%X\n", irpEntry->SerialNumber); irp = irpEntry->Irp; if (irp == NULL) { // this IRP has already been canceled ASSERT(irpEntry->CancelRoutineFreeMemory == FALSE); DokanFreeIrpEntry(irpEntry); continue; } // this IRP is not canceled yet if (IoSetCancelRoutine(irp, NULL) == NULL) { // Cancel routine will run as soon as we release the lock InitializeListHead(&irpEntry->ListEntry); irpEntry->CancelRoutineFreeMemory = TRUE; continue; } // IrpEntry is saved here for CancelRoutine // Clear it to prevent to be completed by CancelRoutine twice irp->Tail.Overlay.DriverContext[DRIVER_CONTEXT_IRP_ENTRY] = NULL; InsertTailList(&completeList, &irpEntry->ListEntry); } if (IsListEmpty(&Dcb->PendingIrp.ListHead)) { KeClearEvent(&Dcb->PendingIrp.NotEmpty); } KeReleaseSpinLock(&Dcb->PendingIrp.ListLock, oldIrql); while (!IsListEmpty(&completeList)) { listHead = RemoveHeadList(&completeList); irpEntry = CONTAINING_RECORD(listHead, IRP_ENTRY, ListEntry); irp = irpEntry->Irp; DokanCompleteIrpRequest(irp, STATUS_INSUFFICIENT_RESOURCES, 0); DokanFreeIrpEntry(irpEntry); } DDbgPrint("<== ReleaseTimeoutPendingIRP\n"); return STATUS_SUCCESS; }
NTSTATUS RegisterPendingIrpMain( __in PDEVICE_OBJECT DeviceObject, __in PIRP Irp, __in ULONG SerialNumber, __in PIRP_LIST IrpList, __in ULONG Flags, __in ULONG CheckMount ) { PIRP_ENTRY irpEntry; PIO_STACK_LOCATION irpSp; KIRQL oldIrql; //DDbgPrint("==> DokanRegisterPendingIrpMain\n"); if (GetIdentifierType(DeviceObject->DeviceExtension) == VCB) { PDokanVCB vcb = DeviceObject->DeviceExtension; if (CheckMount && !vcb->Dcb->Mounted) { DDbgPrint(" device is not mounted\n"); return STATUS_INSUFFICIENT_RESOURCES; } } irpSp = IoGetCurrentIrpStackLocation(Irp); // Allocate a record and save all the event context. irpEntry = DokanAllocateIrpEntry(); if (NULL == irpEntry) { DDbgPrint(" can't allocate IRP_ENTRY\n"); return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory(irpEntry, sizeof(IRP_ENTRY)); InitializeListHead(&irpEntry->ListEntry); irpEntry->SerialNumber = SerialNumber; irpEntry->FileObject = irpSp->FileObject; irpEntry->Irp = Irp; irpEntry->IrpSp = irpSp; irpEntry->IrpList = IrpList; irpEntry->Flags = Flags; DokanUpdateTimeout(&irpEntry->TickCount, DOKAN_IRP_PENDING_TIMEOUT); //DDbgPrint(" Lock IrpList.ListLock\n"); ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); KeAcquireSpinLock(&IrpList->ListLock, &oldIrql); IoSetCancelRoutine(Irp, DokanIrpCancelRoutine); if (Irp->Cancel) { if (IoSetCancelRoutine(Irp, NULL) != NULL) { //DDbgPrint(" Release IrpList.ListLock %d\n", __LINE__); KeReleaseSpinLock(&IrpList->ListLock, oldIrql); DokanFreeIrpEntry(irpEntry); return STATUS_CANCELLED; } } IoMarkIrpPending(Irp); InsertTailList(&IrpList->ListHead, &irpEntry->ListEntry); irpEntry->CancelRoutineFreeMemory = FALSE; // save the pointer in order to be accessed by cancel routine Irp->Tail.Overlay.DriverContext[DRIVER_CONTEXT_IRP_ENTRY] = irpEntry; KeSetEvent(&IrpList->NotEmpty, IO_NO_INCREMENT, FALSE); //DDbgPrint(" Release IrpList.ListLock\n"); KeReleaseSpinLock(&IrpList->ListLock, oldIrql); //DDbgPrint("<== DokanRegisterPendingIrpMain\n"); return STATUS_PENDING;; }
// user assinged bigger buffer that is enough to return WriteEventContext NTSTATUS DokanEventWrite( __in PDEVICE_OBJECT DeviceObject, __in PIRP Irp ) { KIRQL oldIrql; PLIST_ENTRY thisEntry, nextEntry, listHead; PIRP_ENTRY irpEntry; PDokanVCB vcb; PEVENT_INFORMATION eventInfo; PIRP writeIrp; eventInfo = (PEVENT_INFORMATION)Irp->AssociatedIrp.SystemBuffer; ASSERT(eventInfo != NULL); DDbgPrint("==> DokanEventWrite [EventInfo #%X]\n", eventInfo->SerialNumber); vcb = DeviceObject->DeviceExtension; if (GetIdentifierType(vcb) != VCB) { return STATUS_INVALID_PARAMETER; } ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); KeAcquireSpinLock(&vcb->Dcb->PendingIrp.ListLock, &oldIrql); // search corresponding write IRP through pending IRP list listHead = &vcb->Dcb->PendingIrp.ListHead; for (thisEntry = listHead->Flink; thisEntry != listHead; thisEntry = nextEntry) { PIO_STACK_LOCATION writeIrpSp, eventIrpSp; PEVENT_CONTEXT eventContext; ULONG info = 0; NTSTATUS status; nextEntry = thisEntry->Flink; irpEntry = CONTAINING_RECORD(thisEntry, IRP_ENTRY, ListEntry); // check whehter this is corresponding IRP //DDbgPrint("SerialNumber irpEntry %X eventInfo %X\n", irpEntry->SerialNumber, eventInfo->SerialNumber); if (irpEntry->SerialNumber != eventInfo->SerialNumber) { continue; } // do NOT free irpEntry here writeIrp = irpEntry->Irp; if (writeIrp == NULL) { // this IRP has already been canceled ASSERT(irpEntry->CancelRoutineFreeMemory == FALSE); DokanFreeIrpEntry(irpEntry); continue; } if (IoSetCancelRoutine(writeIrp, DokanIrpCancelRoutine) == NULL) { //if (IoSetCancelRoutine(writeIrp, NULL) != NULL) { // Cancel routine will run as soon as we release the lock InitializeListHead(&irpEntry->ListEntry); irpEntry->CancelRoutineFreeMemory = TRUE; continue; } writeIrpSp = irpEntry->IrpSp; eventIrpSp = IoGetCurrentIrpStackLocation(Irp); ASSERT(writeIrpSp != NULL); ASSERT(eventIrpSp != NULL); eventContext = (PEVENT_CONTEXT)writeIrp->Tail.Overlay.DriverContext[DRIVER_CONTEXT_EVENT]; ASSERT(eventContext != NULL); // short of buffer length if (eventIrpSp->Parameters.DeviceIoControl.OutputBufferLength < eventContext->Length) { DDbgPrint(" EventWrite: STATUS_INSUFFICIENT_RESOURCE\n"); status = STATUS_INSUFFICIENT_RESOURCES; } else { PVOID buffer; //DDbgPrint(" EventWrite CopyMemory\n"); //DDbgPrint(" EventLength %d, BufLength %d\n", eventContext->Length, // eventIrpSp->Parameters.DeviceIoControl.OutputBufferLength); if (Irp->MdlAddress) buffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority); else buffer = Irp->AssociatedIrp.SystemBuffer; ASSERT(buffer != NULL); RtlCopyMemory(buffer, eventContext, eventContext->Length); info = eventContext->Length; status = STATUS_SUCCESS; } DokanFreeEventContext(eventContext); writeIrp->Tail.Overlay.DriverContext[DRIVER_CONTEXT_EVENT] = 0; KeReleaseSpinLock(&vcb->Dcb->PendingIrp.ListLock, oldIrql); Irp->IoStatus.Status = status; Irp->IoStatus.Information = info; // this IRP will be completed by caller function return Irp->IoStatus.Status; } KeReleaseSpinLock(&vcb->Dcb->PendingIrp.ListLock, oldIrql); return STATUS_SUCCESS; }
// When user-mode file system application returns EventInformation, // search corresponding pending IRP and complete it NTSTATUS DokanCompleteIrp( __in PDEVICE_OBJECT DeviceObject, __in PIRP Irp ) { KIRQL oldIrql; PLIST_ENTRY thisEntry, nextEntry, listHead; PIRP_ENTRY irpEntry; PDokanVCB vcb; PEVENT_INFORMATION eventInfo; eventInfo = (PEVENT_INFORMATION)Irp->AssociatedIrp.SystemBuffer; ASSERT(eventInfo != NULL); //DDbgPrint("==> DokanCompleteIrp [EventInfo #%X]\n", eventInfo->SerialNumber); vcb = DeviceObject->DeviceExtension; if (GetIdentifierType(vcb) != VCB) { return STATUS_INVALID_PARAMETER; } //DDbgPrint(" Lock IrpList.ListLock\n"); ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); KeAcquireSpinLock(&vcb->Dcb->PendingIrp.ListLock, &oldIrql); // search corresponding IRP through pending IRP list listHead = &vcb->Dcb->PendingIrp.ListHead; for (thisEntry = listHead->Flink; thisEntry != listHead; thisEntry = nextEntry) { PIRP irp; PIO_STACK_LOCATION irpSp; nextEntry = thisEntry->Flink; irpEntry = CONTAINING_RECORD(thisEntry, IRP_ENTRY, ListEntry); // check whether this is corresponding IRP //DDbgPrint("SerialNumber irpEntry %X eventInfo %X\n", irpEntry->SerialNumber, eventInfo->SerialNumber); // this irpEntry must be freed in this if statement if (irpEntry->SerialNumber != eventInfo->SerialNumber) { continue; } RemoveEntryList(thisEntry); irp = irpEntry->Irp; if (irp == NULL) { // this IRP is already canceled ASSERT(irpEntry->CancelRoutineFreeMemory == FALSE); DokanFreeIrpEntry(irpEntry); irpEntry = NULL; break; } if (IoSetCancelRoutine(irp, NULL) == NULL) { // Cancel routine will run as soon as we release the lock InitializeListHead(&irpEntry->ListEntry); irpEntry->CancelRoutineFreeMemory = TRUE; break; } // IRP is not canceled yet irpSp = irpEntry->IrpSp; ASSERT(irpSp != NULL); // IrpEntry is saved here for CancelRoutine // Clear it to prevent to be completed by CancelRoutine twice irp->Tail.Overlay.DriverContext[DRIVER_CONTEXT_IRP_ENTRY] = NULL; KeReleaseSpinLock(&vcb->Dcb->PendingIrp.ListLock, oldIrql); switch (irpSp->MajorFunction) { case IRP_MJ_DIRECTORY_CONTROL: DokanCompleteDirectoryControl(irpEntry, eventInfo); break; case IRP_MJ_READ: DokanCompleteRead(irpEntry, eventInfo); break; case IRP_MJ_WRITE: DokanCompleteWrite(irpEntry, eventInfo); break; case IRP_MJ_QUERY_INFORMATION: DokanCompleteQueryInformation(irpEntry, eventInfo); break; case IRP_MJ_QUERY_VOLUME_INFORMATION: DokanCompleteQueryVolumeInformation(irpEntry, eventInfo); break; case IRP_MJ_CREATE: DokanCompleteCreate(irpEntry, eventInfo); break; case IRP_MJ_CLEANUP: DokanCompleteCleanup(irpEntry, eventInfo); break; case IRP_MJ_LOCK_CONTROL: DokanCompleteLock(irpEntry, eventInfo); break; case IRP_MJ_SET_INFORMATION: DokanCompleteSetInformation(irpEntry, eventInfo); break; case IRP_MJ_FLUSH_BUFFERS: DokanCompleteFlush(irpEntry, eventInfo); break; case IRP_MJ_QUERY_SECURITY: DokanCompleteQuerySecurity(irpEntry, eventInfo); break; case IRP_MJ_SET_SECURITY: DokanCompleteSetSecurity(irpEntry, eventInfo); break; default: DDbgPrint("Unknown IRP %d\n", irpSp->MajorFunction); // TODO: in this case, should complete this IRP break; } DokanFreeIrpEntry(irpEntry); irpEntry = NULL; return STATUS_SUCCESS; } KeReleaseSpinLock(&vcb->Dcb->PendingIrp.ListLock, oldIrql); //DDbgPrint("<== AACompleteIrp [EventInfo #%X]\n", eventInfo->SerialNumber); // TODO: should return error return STATUS_SUCCESS; }
VOID DokanIrpCancelRoutine( __in PDEVICE_OBJECT DeviceObject, __in PIRP Irp ) { KIRQL oldIrql; PIRP_ENTRY irpEntry; ULONG serialNumber; PIO_STACK_LOCATION irpSp; DDbgPrint("==> DokanIrpCancelRoutine\n"); // Release the cancel spinlock IoReleaseCancelSpinLock(Irp->CancelIrql); irpEntry = Irp->Tail.Overlay.DriverContext[DRIVER_CONTEXT_IRP_ENTRY]; if (irpEntry != NULL) { PKSPIN_LOCK lock = &irpEntry->IrpList->ListLock; // Acquire the queue spinlock ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); KeAcquireSpinLock(lock, &oldIrql); irpSp = IoGetCurrentIrpStackLocation(Irp); ASSERT(irpSp != NULL); serialNumber = irpEntry->SerialNumber; RemoveEntryList(&irpEntry->ListEntry); // If Write is canceld before completion and buffer that saves writing // content is not freed, free it here if (irpSp->MajorFunction == IRP_MJ_WRITE) { PVOID eventContext = Irp->Tail.Overlay.DriverContext[DRIVER_CONTEXT_EVENT]; if (eventContext != NULL) { DokanFreeEventContext(eventContext); } Irp->Tail.Overlay.DriverContext[DRIVER_CONTEXT_EVENT] = NULL; } if (IsListEmpty(&irpEntry->IrpList->ListHead)) { //DDbgPrint(" list is empty ClearEvent\n"); KeClearEvent(&irpEntry->IrpList->NotEmpty); } irpEntry->Irp = NULL; if (irpEntry->CancelRoutineFreeMemory == FALSE) { InitializeListHead(&irpEntry->ListEntry); } else { DokanFreeIrpEntry(irpEntry); irpEntry = NULL; } Irp->Tail.Overlay.DriverContext[DRIVER_CONTEXT_IRP_ENTRY] = NULL; KeReleaseSpinLock(lock, oldIrql); } DDbgPrint(" canceled IRP #%X\n", serialNumber); Irp->IoStatus.Status = STATUS_CANCELLED; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); DDbgPrint("<== DokanIrpCancelRoutine\n"); return; }
VOID NotificationLoop(__in PIRP_LIST PendingIrp, __in PIRP_LIST NotifyEvent) { PDRIVER_EVENT_CONTEXT driverEventContext; PLIST_ENTRY listHead; PIRP_ENTRY irpEntry; LIST_ENTRY completeList; KIRQL irpIrql; KIRQL notifyIrql; PIRP irp; ULONG eventLen; ULONG bufferLen; PVOID buffer; DDbgPrint("=> NotificationLoop\n"); InitializeListHead(&completeList); ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); DDbgPrint("Try acquire SpinLock...\n"); KeAcquireSpinLock(&PendingIrp->ListLock, &irpIrql); DDbgPrint("SpinLock irp Acquired\n"); KeAcquireSpinLock(&NotifyEvent->ListLock, ¬ifyIrql); DDbgPrint("SpinLock notify Acquired\n"); while (!IsListEmpty(&PendingIrp->ListHead) && !IsListEmpty(&NotifyEvent->ListHead)) { listHead = RemoveHeadList(&NotifyEvent->ListHead); driverEventContext = CONTAINING_RECORD(listHead, DRIVER_EVENT_CONTEXT, ListEntry); listHead = RemoveHeadList(&PendingIrp->ListHead); irpEntry = CONTAINING_RECORD(listHead, IRP_ENTRY, ListEntry); eventLen = driverEventContext->EventContext.Length; // ensure this eventIrp is not cancelled irp = irpEntry->Irp; if (irp == NULL) { // this IRP has already been canceled DDbgPrint("Irp canceled\n"); ASSERT(irpEntry->CancelRoutineFreeMemory == FALSE); DokanFreeIrpEntry(irpEntry); // push back InsertTailList(&NotifyEvent->ListHead, &driverEventContext->ListEntry); continue; } if (IoSetCancelRoutine(irp, NULL) == NULL) { DDbgPrint("IoSetCancelRoutine return NULL\n"); // Cancel routine will run as soon as we release the lock InitializeListHead(&irpEntry->ListEntry); irpEntry->CancelRoutineFreeMemory = TRUE; // push back InsertTailList(&NotifyEvent->ListHead, &driverEventContext->ListEntry); continue; } // available size that is used for event notification bufferLen = irpEntry->IrpSp->Parameters.DeviceIoControl.OutputBufferLength; // buffer that is used to inform Event buffer = irp->AssociatedIrp.SystemBuffer; // buffer is not specified or short of length if (bufferLen == 0 || buffer == NULL || bufferLen < eventLen) { DDbgPrint("EventNotice : STATUS_INSUFFICIENT_RESOURCES\n"); DDbgPrint(" bufferLen: %d, eventLen: %d\n", bufferLen, eventLen); // push back InsertTailList(&NotifyEvent->ListHead, &driverEventContext->ListEntry); // marks as STATUS_INSUFFICIENT_RESOURCES irpEntry->SerialNumber = 0; } else { // let's copy EVENT_CONTEXT RtlCopyMemory(buffer, &driverEventContext->EventContext, eventLen); // save event length irpEntry->SerialNumber = eventLen; if (driverEventContext->Completed) { KeSetEvent(driverEventContext->Completed, IO_NO_INCREMENT, FALSE); } ExFreePool(driverEventContext); } InsertTailList(&completeList, &irpEntry->ListEntry); } DDbgPrint("Clear Events...\n"); KeClearEvent(&NotifyEvent->NotEmpty); DDbgPrint("Notify event cleared\n"); KeClearEvent(&PendingIrp->NotEmpty); DDbgPrint("Pending event cleared\n"); DDbgPrint("Release SpinLock...\n"); KeReleaseSpinLock(&NotifyEvent->ListLock, notifyIrql); DDbgPrint("SpinLock notify Released\n"); KeReleaseSpinLock(&PendingIrp->ListLock, irpIrql); DDbgPrint("SpinLock irp Released\n"); while (!IsListEmpty(&completeList)) { listHead = RemoveHeadList(&completeList); irpEntry = CONTAINING_RECORD(listHead, IRP_ENTRY, ListEntry); irp = irpEntry->Irp; if (irpEntry->SerialNumber == 0) { irp->IoStatus.Information = 0; irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; } else { irp->IoStatus.Information = irpEntry->SerialNumber; irp->IoStatus.Status = STATUS_SUCCESS; } DokanFreeIrpEntry(irpEntry); DokanCompleteIrpRequest(irp, irp->IoStatus.Status, irp->IoStatus.Information); } DDbgPrint("<= NotificationLoop\n"); }