More IRP Completion changes. Use proper stack count and other semantics, as documented in NT File System Internals. These changes don't break anything :P
Modified: trunk/reactos/ntoskrnl/io/irp.c

Modified: trunk/reactos/ntoskrnl/io/irp.c
--- trunk/reactos/ntoskrnl/io/irp.c	2005-05-07 00:37:48 UTC (rev 15068)
+++ trunk/reactos/ntoskrnl/io/irp.c	2005-05-07 01:13:04 UTC (rev 15069)
@@ -19,6 +19,187 @@
 #define TAG_IRP      TAG('I', 'R', 'P', ' ')
 #define TAG_SYS_BUF  TAG('S', 'Y', 'S' , 'B')
 
+/* PRIVATE FUNCTIONS  ********************************************************/
+
+VOID 
+STDCALL
+IopFreeIrpKernelApc(PKAPC Apc,
+                    PKNORMAL_ROUTINE *NormalRoutine,
+                    PVOID *NormalContext,
+                    PVOID *SystemArgument1,
+                    PVOID *SystemArgument2)
+{
+    /* Free the IRP */
+    IoFreeIrp(CONTAINING_RECORD(Apc, IRP, Tail.Apc));
+}
+
+VOID 
+STDCALL
+IopAbortIrpKernelApc(PKAPC Apc)
+{
+    /* Free the IRP */
+    IoFreeIrp(CONTAINING_RECORD(Apc, IRP, Tail.Apc));
+}
+
+/*
+ * FUNCTION: Performs the second stage of irp completion for read/write irps
+ * 
+ * Called as a special kernel APC kernel-routine or directly from IofCompleteRequest()
+ *
+ * Note that we'll never see irp's flagged IRP_PAGING_IO (IRP_MOUNT_OPERATION)
+ * or IRP_CLOSE_OPERATION (IRP_MJ_CLOSE and IRP_MJ_CLEANUP) here since their
+ * cleanup/completion is fully taken care of in IoCompleteRequest.
+ * -Gunnar
+ */
+VOID 
+STDCALL
+IopCompleteRequest(PKAPC Apc,
+                   PKNORMAL_ROUTINE* NormalRoutine,
+                   PVOID* NormalContext,
+                   PVOID* SystemArgument1,
+                   PVOID* SystemArgument2)
+{
+    PFILE_OBJECT FileObject;
+    PIRP Irp;
+    PMDL Mdl, NextMdl;
+    PKEVENT UserEvent;
+    BOOLEAN SyncIrp;
+
+    if (Apc) DPRINT("IoSecondStageCompletition with APC: %x\n", Apc);
+   
+    /* Get data from the APC */
+    FileObject = (PFILE_OBJECT)(*SystemArgument1);
+    Irp = CONTAINING_RECORD(Apc, IRP, Tail.Apc);
+    DPRINT("IoSecondStageCompletition, %x\n", Irp);
+    
+    /* Save the User Event */
+    UserEvent = Irp->UserEvent;
+    
+    /* Remember if the IRP is Sync or not */
+    SyncIrp = Irp->Flags & IRP_SYNCHRONOUS_API ? TRUE : FALSE;
+
+    /* Handle Buffered case first */
+    if (Irp->Flags & IRP_BUFFERED_IO)
+    {
+        /* Check if we have an input buffer and if we suceeded */
+        if (Irp->Flags & IRP_INPUT_OPERATION && NT_SUCCESS(Irp->IoStatus.Status))
+        {
+            /* Copy the buffer back to the user */
+            RtlCopyMemory(Irp->UserBuffer,
+                          Irp->AssociatedIrp.SystemBuffer,
+                          Irp->IoStatus.Information);
+        }
+        
+        /* Also check if we should de-allocate it */
+        if (Irp->Flags & IRP_DEALLOCATE_BUFFER)
+        {
+            ExFreePoolWithTag(Irp->AssociatedIrp.SystemBuffer, TAG_SYS_BUF);
+        }
+    }
+    
+    /* Now we got rid of these two... */
+    Irp->Flags &= ~(IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER);
+    
+    /* Check if there's an MDL */
+    if ((Mdl = Irp->MdlAddress))
+    {
+        /* Clear all of them */
+        do
+        {
+            NextMdl = Mdl->Next;
+            IoFreeMdl(Mdl);
+            Mdl = NextMdl;
+        } while (Mdl);
+    }
+    Irp->MdlAddress = NULL;
+
+    /* Remove the IRP from the list of Thread Pending IRPs */
+    RemoveEntryList(&Irp->ThreadListEntry);
+    InitializeListHead(&Irp->ThreadListEntry);  
+
+    if (NT_SUCCESS(Irp->IoStatus.Status) || Irp->PendingReturned)
+    {
+        _SEH_TRY
+        {
+            /*  Save the IOSB Information */
+            *Irp->UserIosb = Irp->IoStatus;
+        }
+        _SEH_HANDLE
+        {
+            /* Ignore any error */
+        }
+        _SEH_END;
+
+        if (FileObject)
+        {
+            if (FileObject->Flags & FO_SYNCHRONOUS_IO)
+            {
+                /* Set the Status */
+                FileObject->FinalStatus = Irp->IoStatus.Status;
+
+                /* FIXME: Remove this check when I/O code is fixed */
+                if (UserEvent != &FileObject->Event)
+                {
+                    /* Signal Event */
+                    KeSetEvent(&FileObject->Event, 0, FALSE);
+                }
+            }
+        }
+        
+        /* Signal the user event, if one exist */
+        if (UserEvent)
+        {
+            KeSetEvent(UserEvent, 0, FALSE);
+        }
+
+        /* Now call the User APC if one was requested */
+        if (Irp->Overlay.AsynchronousParameters.UserApcRoutine)
+        {
+            KeInitializeApc(&Irp->Tail.Apc,
+                            KeGetCurrentThread(),
+                            CurrentApcEnvironment,
+                            IopFreeIrpKernelApc,
+                            IopAbortIrpKernelApc,
+                            (PKNORMAL_ROUTINE)Irp->Overlay.AsynchronousParameters.UserApcRoutine,
+                            Irp->RequestorMode,
+                            Irp->Overlay.AsynchronousParameters.UserApcContext);
+
+            KeInsertQueueApc(&Irp->Tail.Apc,
+                             Irp->UserIosb,
+                             NULL,
+                             2);
+            Irp = NULL;
+        }
+        else if (FileObject && FileObject->CompletionContext)
+        {
+            /* Call the IO Completion Port if we have one, instead */
+            IoSetIoCompletion(FileObject->CompletionContext->Port,
+                              FileObject->CompletionContext->Key,
+                              Irp->Overlay.AsynchronousParameters.UserApcContext,
+                              Irp->IoStatus.Status,
+                              Irp->IoStatus.Information,
+                              FALSE);
+            Irp = NULL;
+        }
+    }
+    
+    /* Free the Irp if it hasn't already */
+    if (Irp) IoFreeIrp(Irp);
+
+    if (FileObject)
+    {
+        /* Dereference the user event, if it is an event object */
+        /* FIXME: Remove last check when I/O code is fixed */
+        if (UserEvent && !SyncIrp && UserEvent != &FileObject->Event) 
+        {
+            ObDereferenceObject(UserEvent);
+        }
+
+        /* Dereference the File Object */
+        ObDereferenceObject(FileObject);
+    }
+}
+
 /* FUNCTIONS *****************************************************************/
 
 /*
@@ -628,11 +809,7 @@
 #endif
 /*
  * @implemented
- */
-VOID FASTCALL
-IofCompleteRequest(PIRP Irp,
-         CCHAR PriorityBoost)
-/*
+ *
  * FUNCTION: Indicates the caller has finished all processing for a given
  * I/O request and is returning the given IRP to the I/O manager
  * ARGUMENTS:
@@ -640,211 +817,190 @@
  *         PriorityBoost = Increment by which to boost the priority of the
  *                         thread making the request
  */
+VOID 
+FASTCALL
+IofCompleteRequest(PIRP Irp,
+                   CCHAR PriorityBoost)
 {
-   ULONG             i;
-   NTSTATUS          Status;
-   PFILE_OBJECT      OriginalFileObject;
-   PDEVICE_OBJECT    DeviceObject;
-   KIRQL             oldIrql;
-   PMDL              Mdl;
-   PIO_STACK_LOCATION Stack = (PIO_STACK_LOCATION)(Irp + 1);
+    PIO_STACK_LOCATION StackPtr;
+    PDEVICE_OBJECT DeviceObject;
+    PFILE_OBJECT FileObject = Irp->Tail.Overlay.OriginalFileObject;
+    PETHREAD Thread = Irp->Tail.Overlay.Thread;
+    NTSTATUS Status;
+    PMDL Mdl;
 
-   DPRINT("IoCompleteRequest(Irp %x, PriorityBoost %d) Event %x THread %x\n",
-      Irp,PriorityBoost, Irp->UserEvent, PsGetCurrentThread());
+    DPRINT("IofCompleteRequest(Irp %x, PriorityBoost %d) Event %x THread %x\n",
+            Irp,PriorityBoost, Irp->UserEvent, PsGetCurrentThread());
 
-   ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
-   ASSERT(Irp->CancelRoutine == NULL);
-   ASSERT(Irp->IoStatus.Status != STATUS_PENDING);
+    ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
+    ASSERT(!Irp->CancelRoutine);
+    ASSERT(Irp->IoStatus.Status != STATUS_PENDING);
+    
+    /* Get the Current Stack */
+    StackPtr = IoGetCurrentIrpStackLocation(Irp);
+    IoSkipCurrentIrpStackLocation(Irp);
+    
+    /* Loop the Stacks and complete the IRPs */
+    for (;Irp->CurrentLocation <= (Irp->StackCount + 1); StackPtr++)
+    {
+        /* Set Pending Returned */
+        Irp->PendingReturned = StackPtr->Control & SL_PENDING_RETURNED;
+        
+        /*
+         * Completion routines expect the current irp stack location to be the same as when
+         * IoSetCompletionRoutine was called to set them. A side effect is that completion
+         * routines set by highest level drivers without their own stack location will receive
+         * an invalid current stack location (at least it should be considered as invalid).
+         * Since the DeviceObject argument passed is taken from the current stack, this value
+         * is also invalid (NULL).
+         */
+        if (Irp->CurrentLocation == (Irp->StackCount + 1))
+        {
+            DeviceObject = NULL;
+        }
+        else
+        {
+            DeviceObject = IoGetCurrentIrpStackLocation(Irp)->DeviceObject;
+        }
 
-   Irp->PendingReturned = IoGetCurrentIrpStackLocation(Irp)->Control & SL_PENDING_RETURNED;
-   
-   /*
-    * Run the completion routines.
-    */
+        /* Check if there is a Completion Routine to Call */
+        if ((NT_SUCCESS(Irp->IoStatus.Status) && 
+             (StackPtr->Control & SL_INVOKE_ON_SUCCESS)) ||
+            (!NT_SUCCESS(Irp->IoStatus.Status) && 
+             (StackPtr->Control & SL_INVOKE_ON_ERROR)) ||
+            (Irp->Cancel && (StackPtr->Control & SL_INVOKE_ON_CANCEL)))
+        {
+            /* Call it */
+            Status = StackPtr->CompletionRoutine(DeviceObject,
+                                                 Irp,
+                                                 StackPtr->Context);
 
-   for (i=Irp->CurrentLocation;i<(ULONG)Irp->StackCount;i++)
-   {
-      /*
-      Completion routines expect the current irp stack location to be the same as when
-      IoSetCompletionRoutine was called to set them. A side effect is that completion
-      routines set by highest level drivers without their own stack location will receive
-      an invalid current stack location (at least it should be considered as invalid).
-      Since the DeviceObject argument passed is taken from the current stack, this value
-      is also invalid (NULL).
-      */
-      if (Irp->CurrentLocation < Irp->StackCount - 1)
-      {
-         IoSkipCurrentIrpStackLocation(Irp);
-         DeviceObject = IoGetCurrentIrpStackLocation(Irp)->DeviceObject;
-      }
-      else
-      {
-         DeviceObject = NULL;
-      }
+            /* Don't touch the Packet if this was returned. It might be gone! */
+            if (Status == STATUS_MORE_PROCESSING_REQUIRED) return;
+        }
+        else
+        {
+            if ((Irp->CurrentLocation <= Irp->StackCount) && (Irp->PendingReturned))
+            {
+                if (IoGetCurrentIrpStackLocation(Irp)->Control & SL_PENDING_RETURNED)
+                {
+                    Irp->PendingReturned = TRUE;
+                }  
+            }
+        }
+        
+        /* Move to next stack */
+        IoSkipCurrentIrpStackLocation(Irp);
+    }
 
-      if (Stack[i].CompletionRoutine != NULL &&
-         ((NT_SUCCESS(Irp->IoStatus.Status) && (Stack[i].Control & SL_INVOKE_ON_SUCCESS)) ||
-         (!NT_SUCCESS(Irp->IoStatus.Status) && (Stack[i].Control & SL_INVOKE_ON_ERROR)) ||
-         (Irp->Cancel && (Stack[i].Control & SL_INVOKE_ON_CANCEL))))
-      {
-         Status = Stack[i].CompletionRoutine(DeviceObject,
-                                                  Irp,
-                                                  Stack[i].Context);
+    /* Windows NT File System Internals, page 165 */
+    if (Irp->Flags & IRP_ASSOCIATED_IRP)
+    {
+        ULONG MasterIrpCount;
+        PIRP MasterIrp = Irp->AssociatedIrp.MasterIrp;
 
-         if (Status == STATUS_MORE_PROCESSING_REQUIRED)
-         {
-            return;
-         }
-      }
-   
-      if (IoGetCurrentIrpStackLocation(Irp)->Control & SL_PENDING_RETURNED)
-      {
-         Irp->PendingReturned = TRUE;
-      }
-   }
+        DPRINT("Handling Associated IRP\n");
+        /* This should never happen! */
+        ASSERT(IsListEmpty(&Irp->ThreadListEntry));
 
-   /* Windows NT File System Internals, page 165 */
-   if (Irp->Flags & IRP_ASSOCIATED_IRP)
-   {
-      ULONG MasterIrpCount;
-      PIRP MasterIrp = Irp->AssociatedIrp.MasterIrp;
-
-      /* This should never happen! */
-      ASSERT(IsListEmpty(&Irp->ThreadListEntry));
-
-      MasterIrpCount = InterlockedDecrement(&MasterIrp->AssociatedIrp.IrpCount);
-      while ((Mdl = Irp->MdlAddress))
-      {
-         Irp->MdlAddress = Mdl->Next;
-         IoFreeMdl(Mdl);
-      }
-      IoFreeIrp(Irp);
-      if (MasterIrpCount == 0)
-      {
-         IofCompleteRequest(MasterIrp, IO_NO_INCREMENT);
-      }
-      return;
-   }
-
-   /*
-    * Were done calling completion routines. Now do any cleanup that can be 
-    * done in an arbitrarily context.
-    */
-
-   /* Windows NT File System Internals, page 165 */
-   if (Irp->Flags & (IRP_PAGING_IO|IRP_CLOSE_OPERATION))
-   {
-      /* This should never happen! */
-      ASSERT(IsListEmpty(&Irp->ThreadListEntry));
-
-      /* 
-       * If MDL_IO_PAGE_READ is set, then the caller is responsible 
-       * for deallocating of the mdl. 
-       */
-      if (Irp->Flags & IRP_PAGING_IO &&
-          Irp->MdlAddress &&
-          !(Irp->MdlAddress->MdlFlags & MDL_IO_PAGE_READ))
-      {
-
-         if (Irp->MdlAddress->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA)
-         {
-            MmUnmapLockedPages(Irp->MdlAddress->MappedSystemVa, Irp->MdlAddress);
-         }
-
-         ExFreePool(Irp->MdlAddress);
-      }
-
-      if (Irp->UserIosb)
-      {
-         _SEH_TRY
-         {
-            *Irp->UserIosb = Irp->IoStatus;
-         }
-         _SEH_HANDLE
-         {
-           DPRINT1("Unable to set UserIosb (at 0x%x) to 0x%x, Error: 0x%x\n",
-                   Irp->UserIosb, Irp->IoStatus, _SEH_GetExceptionCode());
-         }
-         _SEH_END;
-      }
-
-      if (Irp->UserEvent)
-      {
-         KeSetEvent(Irp->UserEvent, PriorityBoost, FALSE);
-      }
+        /* Decrement and get the old count */
+        MasterIrpCount = InterlockedDecrement(&MasterIrp->AssociatedIrp.IrpCount);
+        
+        /* Free MDLs and IRP */
+        while ((Mdl = Irp->MdlAddress))
+        {
+            Irp->MdlAddress = Mdl->Next;
+            IoFreeMdl(Mdl);
+        }
+        IoFreeIrp(Irp);
       
-      /* io manager frees the irp for close operations */
-//      if (Irp->Flags & IRP_PAGING_IO)
-//      {
-         IoFreeIrp(Irp);
-//      }
-      
-      return;
-   }
-   
-   
-   /*
-   Hi Dave,
-   
-    I went through my old notes. You are correct and in most cases
-   IoCompleteRequest() will issue an MmUnlockPages() for each MDL in the IRP
-   chain. There are however few exceptions: one is MDLs for associated IRPs,
-   it's expected that those MDLs have been initialized with
-   IoBuildPartialMdl(). Another exception is PAGING_IO irps, the i/o completion
-   code doesn't do anything to MDLs of those IRPs.
-   
-   sara
-   
-*/
-   
+        /* Complete the Master IRP */
+        if (!MasterIrpCount) IofCompleteRequest(MasterIrp, IO_NO_INCREMENT);
+        return;
+    }
 
-   for (Mdl = Irp->MdlAddress; Mdl; Mdl = Mdl->Next)
-   {
-      /* 
-       * Undo the MmProbeAndLockPages. If MmGetSystemAddressForMdl was called
-       * on this mdl, this mapping (if any) is also undone by MmUnlockPages.
-       */
-      MmUnlockPages(Mdl);
-   }
-    
-   //Windows NT File System Internals, page 154
-   OriginalFileObject = Irp->Tail.Overlay.OriginalFileObject;
+    /* Windows NT File System Internals, page 165 */
+    if (Irp->Flags & (IRP_PAGING_IO|IRP_CLOSE_OPERATION))
+    {
+        DPRINT("Handling Paging or Close I/O\n");
+        /* This should never happen! */
+        ASSERT(IsListEmpty(&Irp->ThreadListEntry));
 
-   if (Irp->PendingReturned || KeGetCurrentIrql() == DISPATCH_LEVEL)
-   {
-      BOOLEAN bStatus;
-      
-      DPRINT("Dispatching APC\n");
-
-      KeInitializeApc(  &Irp->Tail.Apc,
-                             &Irp->Tail.Overlay.Thread->Tcb,
-                             Irp->ApcEnvironment,
-                             IoSecondStageCompletion,//kernel routine
+        /* Handle a Close Operation or Sync Paging I/O (see page 165) */
+        if (Irp->Flags & (IRP_SYNCHRONOUS_PAGING_IO | IRP_CLOSE_OPERATION))
+        {
+            /* Set the I/O Status and Signal the Event */
+            DPRINT("Handling Sync Paging or Close I/O\n");
+            *Irp->UserIosb = Irp->IoStatus;
+            KeSetEvent(Irp->UserEvent, PriorityBoost, FALSE);
+            
+            /* Free the IRP for a Paging I/O Only, Close is handled by us */
+            if (Irp->Flags & IRP_SYNCHRONOUS_PAGING_IO)
+            {
+                DPRINT("Handling Sync Paging I/O\n");
+                IoFreeIrp(Irp);
+            }
+        } 
+        else
+        {
+            DPRINT1("BUG BUG, YOU SHOULDNT BE HERE\n");
+            #if 0
+            /* Page 166 */
+            /* When we'll actually support Async Paging I/O Properly... */
+            KeInitializeApc(&Irp->Tail.Apc
+                            &Irp->tail.Overlay.Thread->Tcb,
+                            Irp->ApcEnvironment,
+                            IopCompletePageWrite,
+                            NULL,
+                            NULL,
+                            KernelMode,
+                            NULL);
+            KeInsertQueueApc(&Irp->Tail.Apc,
                              NULL,
-                             (PKNORMAL_ROUTINE) NULL,
-                             KernelMode,
-                             NULL);
-      
-      bStatus = KeInsertQueueApc(&Irp->Tail.Apc,
-                                      (PVOID)OriginalFileObject,
-                                      NULL, // This is used for REPARSE stuff
-                                      PriorityBoost);
+                             NULL,
+                             PriorityBoost);
+            #endif
+        }
+        return;
+    }
 
-      if (bStatus == FALSE)
-      {
-         DPRINT1("Error queueing APC for thread. Thread has probably exited.\n");
-      }
+    /* Unlock MDL Pages, page 167. */
+    while ((Mdl = Irp->MdlAddress))
+    {
+        DPRINT("Unlocking MDL: %x\n", Mdl);
+        Irp->MdlAddress = Mdl->Next;
+        MmUnlockPages(Mdl);
+    }        
 
-      DPRINT("Finished dispatching APC\n");
-   }
-   else
-   {
-      DPRINT("Calling IoSecondStageCompletion routine directly\n");
-      KeRaiseIrql(APC_LEVEL, &oldIrql);
-      IoSecondStageCompletion(&Irp->Tail.Apc,NULL,NULL,(PVOID)&OriginalFileObject, NULL);
-      KeLowerIrql(oldIrql);
-      DPRINT("Finished completition routine\n");
-   }
+    /* Check if we should exit because of a Deferred I/O (page 168) */
+    if (Irp->Flags & IRP_DEFER_IO_COMPLETION && !Irp->PendingReturned)
+    {
+        DPRINT("Quick return\n");
+        return;
+    }
+    
+    /* Now queue the special APC */
+    if (!Irp->Cancel)
+    {
+        DPRINT("KMODE APC QUEUE\n");
+        KeInitializeApc(&Irp->Tail.Apc,
+                        &Thread->Tcb,
+                        Irp->ApcEnvironment,
+                        IopCompleteRequest,
+                        NULL,
+                        (PKNORMAL_ROUTINE) NULL,
+                        KernelMode,
+                        NULL);
+        KeInsertQueueApc(&Irp->Tail.Apc,
+                         FileObject,
+                         NULL, /* This is used for REPARSE stuff */
+                         PriorityBoost);
+    }
+    else
+    {
+        /* The IRP just got cancelled... don't think this happens in ROS yet */
+        DPRINT1("The IRP was cancelled. Go Bug Alex\n");
+    }
 }
 
 /*
@@ -942,7 +1098,7 @@
     Irp->Type = IO_TYPE_IRP;
     Irp->Size = PacketSize;
     Irp->StackCount = StackSize;
-    Irp->CurrentLocation = StackSize;
+    Irp->CurrentLocation = StackSize + 1;
     Irp->ApcEnvironment =  KeGetCurrentThread()->ApcStateIndex;
     Irp->Tail.Overlay.CurrentStackLocation = (PIO_STACK_LOCATION)(Irp + 1) + StackSize;
     
@@ -1181,183 +1337,4 @@
     return IofCallDriver(DeviceObject, Irp);
 }
 
-VOID 
-STDCALL
-IoSecondStageCompletion_KernelApcRoutine(PKAPC Apc,
-                                         PKNORMAL_ROUTINE *NormalRoutine,
-                                         PVOID *NormalContext,
-                                         PVOID *SystemArgument1,
-                                         PVOID *SystemArgument2)
-{
-    /* Free the IRP */
-    IoFreeIrp(CONTAINING_RECORD(Apc, IRP, Tail.Apc));
-}
-
-VOID 
-STDCALL
-IoSecondStageCompletion_RundownApcRoutine(PKAPC Apc)
-{
-    /* Free the IRP */
-    IoFreeIrp(CONTAINING_RECORD(Apc, IRP, Tail.Apc));
-}
-
-/*
- * FUNCTION: Performs the second stage of irp completion for read/write irps
- * 
- * Called as a special kernel APC kernel-routine or directly from IofCompleteRequest()
- *
- * Note that we'll never see irp's flagged IRP_PAGING_IO (IRP_MOUNT_OPERATION)
- * or IRP_CLOSE_OPERATION (IRP_MJ_CLOSE and IRP_MJ_CLEANUP) here since their
- * cleanup/completion is fully taken care of in IoCompleteRequest.
- * -Gunnar
- */
-VOID 
-STDCALL
-IoSecondStageCompletion(PKAPC Apc,
-                        PKNORMAL_ROUTINE* NormalRoutine,
-                        PVOID* NormalContext,
-                        PVOID* SystemArgument1,
-                        PVOID* SystemArgument2)
-{
-    PFILE_OBJECT FileObject;
-    PIRP Irp;
-    PMDL Mdl, NextMdl;
-    PKEVENT UserEvent;
-    BOOLEAN SyncIrp;
-
-    if (Apc) DPRINT("IoSecondStageCompletition with APC: %x\n", Apc);
-   
-    /* Get data from the APC */
-    FileObject = (PFILE_OBJECT)(*SystemArgument1);
-    Irp = CONTAINING_RECORD(Apc, IRP, Tail.Apc);
-    DPRINT("IoSecondStageCompletition, %x\n", Irp);
-    
-    /* Save the User Event */
-    UserEvent = Irp->UserEvent;
-    
-    /* Remember if the IRP is Sync or not */
-    SyncIrp = Irp->Flags & IRP_SYNCHRONOUS_API ? TRUE : FALSE;
-
-    /* Handle Buffered case first */
-    if (Irp->Flags & IRP_BUFFERED_IO)
-    {
-        /* Check if we have an input buffer and if we suceeded */
-        if (Irp->Flags & IRP_INPUT_OPERATION && NT_SUCCESS(Irp->IoStatus.Status))
-        {
-            /* Copy the buffer back to the user */
-            RtlCopyMemory(Irp->UserBuffer,
-                          Irp->AssociatedIrp.SystemBuffer,
-                          Irp->IoStatus.Information);
-        }
-        
-        /* Also check if we should de-allocate it */
-        if (Irp->Flags & IRP_DEALLOCATE_BUFFER)
-        {
-            ExFreePoolWithTag(Irp->AssociatedIrp.SystemBuffer, TAG_SYS_BUF);
-        }
-    }
-    
-    /* Now we got rid of these two... */
-    Irp->Flags &= ~(IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER);
-    
-    /* Check if there's an MDL */
-    if ((Mdl = Irp->MdlAddress))
-    {
-        /* Clear all of them */
-        do
-        {
-            NextMdl = Mdl->Next;
-            IoFreeMdl(Mdl);
-            Mdl = NextMdl;
-        } while (Mdl);
-    }
-    Irp->MdlAddress = NULL;
-
-    /* Remove the IRP from the list of Thread Pending IRPs */
-    RemoveEntryList(&Irp->ThreadListEntry);
-    InitializeListHead(&Irp->ThreadListEntry);  
-
-    if (NT_SUCCESS(Irp->IoStatus.Status) || Irp->PendingReturned)
-    {
-        _SEH_TRY
-        {
-            /*  Save the IOSB Information */
-            *Irp->UserIosb = Irp->IoStatus;
-        }
-        _SEH_HANDLE
-        {
-            /* Ignore any error */
-        }
-        _SEH_END;
-
-        if (FileObject)
-        {
-            if (FileObject->Flags & FO_SYNCHRONOUS_IO)
-            {
-                /* Set the Status */
-                FileObject->FinalStatus = Irp->IoStatus.Status;
-
-                /* FIXME: Remove this check when I/O code is fixed */
-                if (UserEvent != &FileObject->Event)
-                {
-                    /* Signal Event */
-                    KeSetEvent(&FileObject->Event, 0, FALSE);
-                }
-            }
-        }
-        
-        /* Signal the user event, if one exist */
-        if (UserEvent)
-        {
-            KeSetEvent(UserEvent, 0, FALSE);
-        }
-
-        /* Now call the User APC if one was requested */
-        if (Irp->Overlay.AsynchronousParameters.UserApcRoutine)
-        {
-            KeInitializeApc(&Irp->Tail.Apc,
-                            KeGetCurrentThread(),
-                            CurrentApcEnvironment,
-                            IoSecondStageCompletion_KernelApcRoutine,
-                            IoSecondStageCompletion_RundownApcRoutine,
-                            (PKNORMAL_ROUTINE)Irp->Overlay.AsynchronousParameters.UserApcRoutine,
-                            Irp->RequestorMode,
-                            Irp->Overlay.AsynchronousParameters.UserApcContext);
-
-            KeInsertQueueApc(&Irp->Tail.Apc,
-                             Irp->UserIosb,
-                             NULL,
-                             2);
-            Irp = NULL;
-        }
-        else if (FileObject && FileObject->CompletionContext)
-        {
-            /* Call the IO Completion Port if we have one, instead */
-            IoSetIoCompletion(FileObject->CompletionContext->Port,
-                              FileObject->CompletionContext->Key,
-                              Irp->Overlay.AsynchronousParameters.UserApcContext,
-                              Irp->IoStatus.Status,
-                              Irp->IoStatus.Information,
-                              FALSE);
-            Irp = NULL;
-        }
-    }
-    
-    /* Free the Irp if it hasn't already */
-    if (Irp) IoFreeIrp(Irp);
-
-    if (FileObject)
-    {
-        /* Dereference the user event, if it is an event object */
-        /* FIXME: Remove last check when I/O code is fixed */
-        if (UserEvent && !SyncIrp && UserEvent != &FileObject->Event) 
-        {
-            ObDereferenceObject(UserEvent);
-        }
-
-        /* Dereference the File Object */
-        ObDereferenceObject(FileObject);
-    }
-}
-
 /* EOF */