Example #1
0
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);
    }
}
Example #2
0
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;
}
Example #3
0
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;;
}
Example #4
0
// 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;
}
Example #5
0
// 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;
}
Example #6
0
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;

}
Example #7
0
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, &notifyIrql);
  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");
}