Merge trunk head (r43756)
[reactos.git] / reactos / ntoskrnl / io / iomgr / irp.c
index 6bb18da..9ef5f0c 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * PROJECT:         ReactOS Kernel
  * LICENSE:         GPL - See COPYING in the top level directory
- * FILE:            ntoskrnl/io/irp.c
+ * FILE:            ntoskrnl/io/iomgr/irp.c
  * PURPOSE:         IRP Handling Functions
  * PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)
  *                  Gunnar Dalsnes
@@ -12,7 +12,7 @@
 
 #include <ntoskrnl.h>
 #define NDEBUG
-#include <internal/debug.h>
+#include <debug.h>
 
 /* Undefine some macros we implement here */
 #undef IoCallDriver
@@ -40,6 +40,76 @@ IopAbortIrpKernelApc(IN PKAPC Apc)
     IoFreeIrp(CONTAINING_RECORD(Apc, IRP, Tail.Apc));
 }
 
+NTSTATUS
+NTAPI
+IopCleanupFailedIrp(IN PFILE_OBJECT FileObject,
+                    IN PKEVENT EventObject OPTIONAL,
+                    IN PVOID Buffer OPTIONAL)
+{
+    PAGED_CODE();
+
+    /* Dereference the event */
+    if (EventObject) ObDereferenceObject(EventObject);
+
+    /* Free a buffer, if any */
+    if (Buffer) ExFreePool(Buffer);
+
+    /* If this was a file opened for synch I/O, then unlock it */
+    if (FileObject->Flags & FO_SYNCHRONOUS_IO) IopUnlockFileObject(FileObject);
+
+    /* Now dereference it and return */
+    ObDereferenceObject(FileObject);
+    return STATUS_INSUFFICIENT_RESOURCES;
+}
+
+VOID
+NTAPI
+IopAbortInterruptedIrp(IN PKEVENT EventObject,
+                       IN PIRP Irp)
+{
+    KIRQL OldIrql;
+    BOOLEAN CancelResult;
+    LARGE_INTEGER Wait;
+    PAGED_CODE();
+
+    /* Raise IRQL to APC */
+    KeRaiseIrql(APC_LEVEL, &OldIrql);
+
+    /* Check if nobody completed it yet */
+    if (!KeReadStateEvent(EventObject))
+    {
+        /* First, cancel it */
+        CancelResult = IoCancelIrp(Irp);
+        KeLowerIrql(OldIrql);
+
+        /* Check if we cancelled it */
+        if (CancelResult)
+        {
+            /* Wait for the IRP to be cancelled */
+            Wait.QuadPart = -100000;
+            while (!KeReadStateEvent(EventObject))
+            {
+                /* Delay indefintely */
+                KeDelayExecutionThread(KernelMode, FALSE, &Wait);
+            }
+        }
+        else
+        {
+            /* No cancellation done, so wait for the I/O system to kill it */
+            KeWaitForSingleObject(EventObject,
+                                  Executive,
+                                  KernelMode,
+                                  FALSE,
+                                  NULL);
+        }
+    }
+    else
+    {
+        /* We got preempted, so give up */
+        KeLowerIrql(OldIrql);
+    }
+}
+
 VOID
 NTAPI
 IopRemoveThreadIrp(VOID)
@@ -144,12 +214,16 @@ IopCleanupIrp(IN PIRP Irp,
         !(Irp->Flags & IRP_SYNCHRONOUS_API) &&
         (FileObject))
     {
-        /* Derefernce the User Event */
+        /* Dereference the User Event */
         ObDereferenceObject(Irp->UserEvent);
     }
 
-    /* Dereference the File Object */
-    if (FileObject) ObDereferenceObject(FileObject);
+    /* Check if we have a file object and this isn't a create operation */
+    if ((FileObject) && !(Irp->Flags & IRP_CREATE_OPERATION))
+    {
+        /* Dereference the file object */
+        ObDereferenceObject(FileObject);
+    }
 
     /* Free the IRP */
     IoFreeIrp(Irp);
@@ -165,7 +239,7 @@ IopCompleteRequest(IN PKAPC Apc,
 {
     PFILE_OBJECT FileObject;
     PIRP Irp;
-    PMDL Mdl;
+    PMDL Mdl, NextMdl;
     PVOID Port = NULL, Key = NULL;
     BOOLEAN SignaledCreateRequest = FALSE;
 
@@ -178,11 +252,27 @@ IopCompleteRequest(IN PKAPC Apc,
             Irp,
             FileObject);
 
+    /* Sanity check */
+    ASSERT(Irp->IoStatus.Status != (NTSTATUS)0xFFFFFFFF);
+
+    /* Check if we have a file object */
+    if (*SystemArgument2)
+    {
+        /* Check if we're reparsing */
+        if ((Irp->IoStatus.Status == STATUS_REPARSE) &&
+            (Irp->IoStatus.Information == IO_REPARSE_TAG_MOUNT_POINT))
+        {
+            /* We should never get this yet */
+            DPRINT1("Reparse support not yet present!\n");
+            while (TRUE);
+        }
+    }
+
     /* 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) && 
+        /* Check if we have an input buffer and if we succeeded */
+        if ((Irp->Flags & IRP_INPUT_OPERATION) &&
             (Irp->IoStatus.Status != STATUS_VERIFY_REQUIRED) &&
             !(NT_ERROR(Irp->IoStatus.Status)))
         {
@@ -196,7 +286,7 @@ IopCompleteRequest(IN PKAPC Apc,
         if (Irp->Flags & IRP_DEALLOCATE_BUFFER)
         {
             /* Deallocate it */
-            ExFreePoolWithTag(Irp->AssociatedIrp.SystemBuffer, TAG_SYS_BUF);
+            ExFreePool(Irp->AssociatedIrp.SystemBuffer);
         }
     }
 
@@ -204,22 +294,25 @@ IopCompleteRequest(IN PKAPC Apc,
     Irp->Flags &= ~(IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER);
 
     /* Check if there's an MDL */
-    while ((Mdl = Irp->MdlAddress))
+    for (Mdl = Irp->MdlAddress; Mdl; Mdl = NextMdl)
     {
-        /* Clear all of them */
-        Irp->MdlAddress = Mdl->Next;
+        /* Free it */
+        NextMdl = Mdl->Next;
         IoFreeMdl(Mdl);
     }
 
+    /* No MDLs left */
+    Irp->MdlAddress = NULL;
+
     /*
      * Check if either the request was completed without any errors
      * (but warnings are OK!), or if it was completed with an error, but
      * did return from a pending I/O Operation and is not synchronous.
      */
-    if ((!NT_ERROR(Irp->IoStatus.Status)) ||
-        (NT_ERROR(Irp->IoStatus.Status) &&
-        (Irp->PendingReturned) &&
-        !(IsIrpSynchronous(Irp, FileObject))))
+    if (!(NT_ERROR(Irp->IoStatus.Status)) ||
+         (NT_ERROR(Irp->IoStatus.Status) &&
+          (Irp->PendingReturned) &&
+          !(IsIrpSynchronous(Irp, FileObject))))
     {
         /* Get any information we need from the FO before we kill it */
         if ((FileObject) && (FileObject->CompletionContext))
@@ -230,16 +323,16 @@ IopCompleteRequest(IN PKAPC Apc,
         }
 
         /* Use SEH to make sure we don't write somewhere invalid */
-        _SEH_TRY
+        _SEH2_TRY
         {
             /*  Save the IOSB Information */
             *Irp->UserIosb = Irp->IoStatus;
         }
-        _SEH_HANDLE
+        _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
         {
             /* Ignore any error */
         }
-        _SEH_END;
+        _SEH2_END;
 
         /* Check if we have an event or a file object */
         if (Irp->UserEvent)
@@ -250,12 +343,20 @@ IopCompleteRequest(IN PKAPC Apc,
             /* Check if we also have a File Object */
             if (FileObject)
             {
+                /* Check if this is an Asynch API */
+                if (!(Irp->Flags & IRP_SYNCHRONOUS_API))
+                {
+                    /* Dereference the event */
+                    ObDereferenceObject(Irp->UserEvent);
+                }
+
                 /*
                  * Now, if this is a Synch I/O File Object, then this event is
                  * NOT an actual Executive Event, so we won't dereference it,
                  * and instead, we will signal the File Object
                  */
-                if (FileObject->Flags & FO_SYNCHRONOUS_IO)
+                if ((FileObject->Flags & FO_SYNCHRONOUS_IO) &&
+                    !(Irp->Flags & IRP_OB_QUERY_NAME))
                 {
                     /* Signal the file object and set the status */
                     KeSetEvent(&FileObject->Event, 0, FALSE);
@@ -268,7 +369,7 @@ IopCompleteRequest(IN PKAPC Apc,
                  */
                 if (Irp->Flags & IRP_CREATE_OPERATION)
                 {
-                    /* Clear the APC Routine and remmeber this */
+                    /* Clear the APC Routine and remember this */
                     Irp->Overlay.AsynchronousParameters.UserApcRoutine = NULL;
                     SignaledCreateRequest = TRUE;
                 }
@@ -279,11 +380,44 @@ IopCompleteRequest(IN PKAPC Apc,
             /* Signal the file object and set the status */
             KeSetEvent(&FileObject->Event, 0, FALSE);
             FileObject->FinalStatus = Irp->IoStatus.Status;
+
+            /*
+            * This could also be a create operation, in which case we want
+            * to make sure there's no APC fired.
+            */
+            if (Irp->Flags & IRP_CREATE_OPERATION)
+            {
+                /* Clear the APC Routine and remember this */
+                Irp->Overlay.AsynchronousParameters.UserApcRoutine = NULL;
+                SignaledCreateRequest = TRUE;
+            }
+        }
+
+        /* Update transfer count for everything but create operation */
+        if (!(Irp->Flags & IRP_CREATE_OPERATION))
+        {
+            if (Irp->Flags & IRP_WRITE_OPERATION)
+            {
+                /* Update write transfer count */
+                IopUpdateTransferCount(IopWriteTransfer,
+                                       (ULONG)Irp->IoStatus.Information);
+            }
+            else if (Irp->Flags & IRP_READ_OPERATION)
+            {
+                /* Update read transfer count */
+                IopUpdateTransferCount(IopReadTransfer,
+                                       (ULONG)Irp->IoStatus.Information);
+            }
+            else
+            {
+                /* Update other transfer count */
+                IopUpdateTransferCount(IopOtherTransfer,
+                                       (ULONG)Irp->IoStatus.Information);
+            }
         }
 
         /* Now that we've signaled the events, de-associate the IRP */
-        RemoveEntryList(&Irp->ThreadListEntry);
-        InitializeListHead(&Irp->ThreadListEntry);
+        IopUnQueueIrpFromThread(Irp);
 
         /* Now check if a User APC Routine was requested */
         if (Irp->Overlay.AsynchronousParameters.UserApcRoutine)
@@ -308,7 +442,7 @@ IopCompleteRequest(IN PKAPC Apc,
         {
             /* We have an I/O Completion setup... create the special Overlay */
             Irp->Tail.CompletionKey = Key;
-            Irp->Tail.Overlay.PacketType = IrpCompletionPacket;
+            Irp->Tail.Overlay.PacketType = IopCompletionPacketIrp;
             KeInsertQueue(Port, &Irp->Tail.Overlay.ListEntry);
         }
         else
@@ -321,7 +455,7 @@ IopCompleteRequest(IN PKAPC Apc,
         if ((FileObject) && !(SignaledCreateRequest))
         {
             /* Dereference it, since it's not needed anymore either */
-            ObDereferenceObject(FileObject);
+            ObDereferenceObjectDeferDelete(FileObject);
         }
     }
     else
@@ -335,7 +469,7 @@ IopCompleteRequest(IN PKAPC Apc,
             /* So we did return with a synch operation, was it the IRP? */
             if (Irp->Flags & IRP_SYNCHRONOUS_API)
             {
-                /* Yes, this IRP was synchronous, so retrn the I/O Status */
+                /* Yes, this IRP was synchronous, so return the I/O Status */
                 *Irp->UserIosb = Irp->IoStatus;
 
                 /* Now check if the user gave an event */
@@ -365,7 +499,7 @@ IopCompleteRequest(IN PKAPC Apc,
         if ((FileObject) && !(Irp->Flags & IRP_CREATE_OPERATION))
         {
             /* Dereference the File Object unless this was a create */
-            ObDereferenceObject(FileObject);
+            ObDereferenceObjectDeferDelete(FileObject);
         }
 
         /*
@@ -381,8 +515,7 @@ IopCompleteRequest(IN PKAPC Apc,
         }
 
         /* Now that we've signaled the events, de-associate the IRP */
-        RemoveEntryList(&Irp->ThreadListEntry);
-        InitializeListHead(&Irp->ThreadListEntry);
+        IopUnQueueIrpFromThread(Irp);
 
         /* Free the IRP as well */
         IoFreeIrp(Irp);
@@ -406,6 +539,11 @@ IoAllocateIrp(IN CCHAR StackSize,
     PNPAGED_LOOKASIDE_LIST List = NULL;
     PP_NPAGED_LOOKASIDE_NUMBER ListType = LookasideSmallIrpList;
 
+    /* Set Charge Quota Flag */
+    if (ChargeQuota) Flags |= IRP_QUOTA_CHARGED;
+
+    /* FIXME: Implement Lookaside Floats */
+    
     /* Figure out which Lookaside List to use */
     if ((StackSize <= 8) && (ChargeQuota == FALSE))
     {
@@ -448,7 +586,7 @@ IoAllocateIrp(IN CCHAR StackSize,
         /* Did we try lookaside and fail? */
         if (Flags & IRP_ALLOCATED_FIXED_SIZE) List->L.AllocateMisses++;
 
-        /* Check if we shoudl charge quota */
+        /* Check if we should charge quota */
         if (ChargeQuota)
         {
             /* Irp = ExAllocatePoolWithQuotaTag(NonPagedPool, Size, TAG_IRP); */
@@ -466,13 +604,10 @@ IoAllocateIrp(IN CCHAR StackSize,
     }
     else
     {
-        /* We have an IRP from Lookaside */
-        Flags |= IRP_LOOKASIDE_ALLOCATION;
+        /* In this case there is no charge quota */
+        Flags &= ~IRP_QUOTA_CHARGED;
     }
 
-    /* Set Flag */
-    if (ChargeQuota) Flags |= IRP_QUOTA_CHARGED;
-
     /* Now Initialize it */
     IoInitializeIrp(Irp, Size, StackSize);
 
@@ -505,7 +640,7 @@ IoBuildAsynchronousFsdRequest(IN ULONG MajorFunction,
 
     /* Allocate IRP */
     Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
-    if (!Irp) return Irp;
+    if (!Irp) return NULL;
 
     /* Get the Stack */
     StackPtr = IoGetNextIrpStackLocation(Irp);
@@ -563,26 +698,25 @@ IoBuildAsynchronousFsdRequest(IN ULONG MajorFunction,
                 return NULL;
             }
 
-            /* Probe and Lock */
-            _SEH_TRY
-            {
-                /* Do the probe */
-                MmProbeAndLockPages(Irp->MdlAddress,
-                                    KernelMode,
-                                    MajorFunction == IRP_MJ_READ ?
-                                    IoWriteAccess : IoReadAccess);
-            }
-            _SEH_HANDLE
-            {
-                /* Free the IRP and its MDL */
-                IoFreeMdl(Irp->MdlAddress);
-                IoFreeIrp(Irp);
-                Irp = NULL;
-            }
-            _SEH_END;
-
-            /* This is how we know if we failed during the probe */
-            if (!Irp) return NULL;
+                       /* Probe and Lock */
+                       _SEH2_TRY
+                       {
+                               /* Do the probe */
+                               MmProbeAndLockPages(Irp->MdlAddress,
+                                                                       KernelMode,
+                                                                       MajorFunction == IRP_MJ_READ ?
+                                                                       IoWriteAccess : IoReadAccess);
+                       }
+                       _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+                       {
+                               /* Free the IRP and its MDL */
+                               IoFreeMdl(Irp->MdlAddress);
+                               IoFreeIrp(Irp);
+
+                /* Fail */
+                               _SEH2_YIELD(return NULL);
+                       }
+                       _SEH2_END;
         }
         else
         {
@@ -609,7 +743,7 @@ IoBuildAsynchronousFsdRequest(IN ULONG MajorFunction,
     Irp->UserIosb = IoStatusBlock;
     Irp->Tail.Overlay.Thread = PsGetCurrentThread();
 
-    /* Set the Status Block after all is done */
+    /* Return the IRP */
     IOTRACE(IO_IRP_DEBUG,
             "%s - Built IRP %p with Major, Buffer, DO %lx %p %p\n",
             __FUNCTION__,
@@ -641,7 +775,7 @@ IoBuildDeviceIoControlRequest(IN ULONG IoControlCode,
 
     /* Allocate IRP */
     Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
-    if (!Irp) return Irp;
+    if (!Irp) return NULL;
 
     /* Get the Stack */
     StackPtr = IoGetNextIrpStackLocation(Irp);
@@ -735,6 +869,7 @@ IoBuildDeviceIoControlRequest(IN ULONG IoControlCode,
             }
             else
             {
+                /* Clear the flags */
                 Irp->Flags = 0;
             }
 
@@ -755,7 +890,7 @@ IoBuildDeviceIoControlRequest(IN ULONG IoControlCode,
                 }
 
                 /* Probe and Lock */
-                _SEH_TRY
+                _SEH2_TRY
                 {
                     /* Do the probe */
                     MmProbeAndLockPages(Irp->MdlAddress,
@@ -764,7 +899,7 @@ IoBuildDeviceIoControlRequest(IN ULONG IoControlCode,
                                         METHOD_IN_DIRECT ?
                                         IoReadAccess : IoWriteAccess);
                 }
-                _SEH_HANDLE
+                _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
                 {
                     /* Free the MDL */
                     IoFreeMdl(Irp->MdlAddress);
@@ -772,12 +907,11 @@ IoBuildDeviceIoControlRequest(IN ULONG IoControlCode,
                     /* Free the input buffer and IRP */
                     if (InputBuffer) ExFreePool(Irp->AssociatedIrp.SystemBuffer);
                     IoFreeIrp(Irp);
-                    Irp = NULL;
-                }
-                _SEH_END;
 
-                /* This is how we know if probing failed */
-                if (!Irp) return NULL;
+                    /* Fail */
+                    _SEH2_YIELD(return NULL);
+                }
+                _SEH2_END;
             }
             break;
 
@@ -848,19 +982,21 @@ NTAPI
 IoCancelIrp(IN PIRP Irp)
 {
     KIRQL OldIrql;
+    KIRQL IrqlAtEntry;
     PDRIVER_CANCEL CancelRoutine;
     IOTRACE(IO_IRP_DEBUG,
             "%s - Canceling IRP %p\n",
             __FUNCTION__,
             Irp);
     ASSERT(Irp->Type == IO_TYPE_IRP);
+    IrqlAtEntry = KeGetCurrentIrql();
 
     /* Acquire the cancel lock and cancel the IRP */
     IoAcquireCancelSpinLock(&OldIrql);
     Irp->Cancel = TRUE;
 
     /* Clear the cancel routine and get the old one */
-    CancelRoutine = IoSetCancelRoutine(Irp, NULL);
+    CancelRoutine = (PVOID)IoSetCancelRoutine(Irp, NULL);
     if (CancelRoutine)
     {
         /* We had a routine, make sure the IRP isn't completed */
@@ -877,6 +1013,7 @@ IoCancelIrp(IN PIRP Irp)
         /* Set the cancel IRQL And call the routine */
         Irp->CancelIrql = OldIrql;
         CancelRoutine(IoGetCurrentIrpStackLocation(Irp)->DeviceObject, Irp);
+       ASSERT(IrqlAtEntry == KeGetCurrentIrql());
         return TRUE;
     }
 
@@ -903,7 +1040,7 @@ IoCancelThreadIo(IN PETHREAD Thread)
             Thread);
 
     /* Raise to APC to protect the IrpList */
-    OldIrql = KfRaiseIrql(APC_LEVEL);
+    KeRaiseIrql(APC_LEVEL, &OldIrql);
 
     /* Start by cancelling all the IRPs in the current thread queue. */
     ListHead = &Thread->IrpList;
@@ -927,7 +1064,7 @@ IoCancelThreadIo(IN PETHREAD Thread)
     while (!IsListEmpty(&Thread->IrpList))
     {
         /* Now we can lower */
-        KfLowerIrql(OldIrql);
+        KeLowerIrql(OldIrql);
 
         /* Wait a short while and then look if all our IRPs were completed. */
         KeDelayExecutionThread(KernelMode, FALSE, &Interval);
@@ -936,14 +1073,19 @@ IoCancelThreadIo(IN PETHREAD Thread)
          * Don't stay here forever if some broken driver doesn't complete
          * the IRP.
          */
-        if (!(Retries--)) IopRemoveThreadIrp();
+        if (!(Retries--))
+        {
+            /* Print out a message and remove the IRP */
+            DPRINT1("Broken driver did not complete!\n");
+            IopRemoveThreadIrp();
+        }
 
         /* Raise the IRQL Again */
-        OldIrql = KfRaiseIrql(APC_LEVEL);
+        KeRaiseIrql(APC_LEVEL, &OldIrql);
     }
 
     /* We're done, lower the IRQL */
-    KfLowerIrql(OldIrql);
+    KeLowerIrql(OldIrql);
 }
 
 /*
@@ -954,7 +1096,7 @@ NTAPI
 IoCallDriver(IN PDEVICE_OBJECT DeviceObject,
              IN PIRP Irp)
 {
-    /* Call fast call */
+    /* Call fastcall */
     return IofCallDriver(DeviceObject, Irp);
 }
 
@@ -990,7 +1132,10 @@ IofCallDriver(IN PDEVICE_OBJECT DeviceObject,
               IN PIRP Irp)
 {
     PDRIVER_OBJECT DriverObject;
-    PIO_STACK_LOCATION Param;
+    PIO_STACK_LOCATION StackPtr;
+
+    /* Make sure this is a valid IRP */
+    ASSERT(Irp->Type == IO_TYPE_IRP);
 
     /* Get the Driver Object */
     DriverObject = DeviceObject->DriverObject;
@@ -1004,15 +1149,28 @@ IofCallDriver(IN PDEVICE_OBJECT DeviceObject,
     }
 
     /* Now update the stack location */
-    Param = IoGetNextIrpStackLocation(Irp);
-    Irp->Tail.Overlay.CurrentStackLocation = Param;
+    StackPtr = IoGetNextIrpStackLocation(Irp);
+    Irp->Tail.Overlay.CurrentStackLocation = StackPtr;
 
     /* Get the Device Object */
-    Param->DeviceObject = DeviceObject;
+    StackPtr->DeviceObject = DeviceObject;
 
     /* Call it */
-    return DriverObject->MajorFunction[Param->MajorFunction](DeviceObject,
-                                                             Irp);
+    return DriverObject->MajorFunction[StackPtr->MajorFunction](DeviceObject,
+                                                                Irp);
+}
+
+FORCEINLINE
+VOID
+IopClearStackLocation(IN PIO_STACK_LOCATION IoStackLocation)
+{
+    IoStackLocation->MinorFunction = 0;
+    IoStackLocation->Flags = 0;
+    IoStackLocation->Control &= SL_ERROR_RETURNED;
+    IoStackLocation->Parameters.Others.Argument1 = 0;
+    IoStackLocation->Parameters.Others.Argument2 = 0;
+    IoStackLocation->Parameters.Others.Argument3 = 0;
+    IoStackLocation->FileObject = NULL;
 }
 
 /*
@@ -1023,20 +1181,22 @@ FASTCALL
 IofCompleteRequest(IN PIRP Irp,
                    IN CCHAR PriorityBoost)
 {
-    PIO_STACK_LOCATION StackPtr;
+    PIO_STACK_LOCATION StackPtr, LastStackPtr;
     PDEVICE_OBJECT DeviceObject;
     PFILE_OBJECT FileObject;
     PETHREAD Thread;
     NTSTATUS Status;
-    PMDL Mdl;
-    ULONG MasterIrpCount;
+    PMDL Mdl, NextMdl;
+    ULONG MasterCount;
     PIRP MasterIrp;
+    ULONG Flags;
+    NTSTATUS ErrorCode = STATUS_SUCCESS;
     IOTRACE(IO_IRP_DEBUG,
             "%s - Completing IRP %p\n",
             __FUNCTION__,
             Irp);
 
-    /* Make sure this IRP isn't getting completed more then once */
+    /* Make sure this IRP isn't getting completed twice or is invalid */
     if ((Irp->CurrentLocation) > (Irp->StackCount + 1))
     {
         /* Bugcheck */
@@ -1044,9 +1204,18 @@ IofCompleteRequest(IN PIRP Irp,
     }
 
     /* Some sanity checks */
-    ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
+    ASSERT(Irp->Type == IO_TYPE_IRP);
     ASSERT(!Irp->CancelRoutine);
     ASSERT(Irp->IoStatus.Status != STATUS_PENDING);
+    ASSERT(Irp->IoStatus.Status != (NTSTATUS)0xFFFFFFFF);
+
+    /* Get the last stack */
+    LastStackPtr = (PIO_STACK_LOCATION)(Irp + 1);
+    if (LastStackPtr->Control & SL_ERROR_RETURNED)
+    {
+        /* Get the error code */
+        ErrorCode = PtrToUlong(LastStackPtr->Parameters.Others.Argument4);
+    }
 
     /* Get the Current Stack and skip it */
     StackPtr = IoGetCurrentIrpStackLocation(Irp);
@@ -1058,13 +1227,31 @@ IofCompleteRequest(IN PIRP Irp,
         /* Set Pending Returned */
         Irp->PendingReturned = StackPtr->Control & SL_PENDING_RETURNED;
 
+        /* Check if we failed */
+        if (!NT_SUCCESS(Irp->IoStatus.Status))
+        {
+            /* Check if it was changed by a completion routine */
+            if (Irp->IoStatus.Status != ErrorCode)
+            {
+                /* Update the error for the current stack */
+                ErrorCode = Irp->IoStatus.Status;
+                StackPtr->Control |= SL_ERROR_RETURNED;
+                LastStackPtr->Parameters.Others.Argument4 = UlongToPtr(ErrorCode);
+                LastStackPtr->Control |= SL_ERROR_RETURNED;
+            }
+        }
+
         /* 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)))
+            (Irp->Cancel &&
+             (StackPtr->Control & SL_INVOKE_ON_CANCEL)))
         {
+            /* Clear the stack location */
+            IopClearStackLocation(StackPtr);
+
             /* Check for highest-level device completion routines */
             if (Irp->CurrentLocation == (Irp->StackCount + 1))
             {
@@ -1094,6 +1281,9 @@ IofCompleteRequest(IN PIRP Irp,
                 /* Mark it as pending */
                 IoMarkIrpPending(Irp);
             }
+
+            /* Clear the stack location */
+            IopClearStackLocation(StackPtr);
         }
 
         /* Move to next stack location and pointer */
@@ -1104,22 +1294,15 @@ IofCompleteRequest(IN PIRP Irp,
     /* Check if the IRP is an associated IRP */
     if (Irp->Flags & IRP_ASSOCIATED_IRP)
     {
-        /* This should never happen! */
-        ASSERT(IsListEmpty(&Irp->ThreadListEntry));
-
         /* Get the master IRP and count */
         MasterIrp = Irp->AssociatedIrp.MasterIrp;
-        MasterIrpCount = InterlockedDecrement(&MasterIrp->
-                                              AssociatedIrp.IrpCount);
-
-        /* Set the thread of this IRP as the master's */
-        Irp->Tail.Overlay.Thread = MasterIrp->Tail.Overlay.Thread;
+        MasterCount = InterlockedDecrement(&MasterIrp->AssociatedIrp.IrpCount);
 
         /* Free the MDLs */
-        while ((Mdl = Irp->MdlAddress))
+        for (Mdl = Irp->MdlAddress; Mdl; Mdl = NextMdl)
         {
             /* Go to the next one */
-            Irp->MdlAddress = Mdl->Next;
+            NextMdl = Mdl->Next;
             IoFreeMdl(Mdl);
         }
 
@@ -1127,10 +1310,13 @@ IofCompleteRequest(IN PIRP Irp,
         IoFreeIrp(Irp);
 
         /* Complete the Master IRP */
-        if (!MasterIrpCount) IofCompleteRequest(MasterIrp, PriorityBoost);
+        if (!MasterCount) IofCompleteRequest(MasterIrp, PriorityBoost);
         return;
     }
 
+    /* We don't support this yet */
+    ASSERT(Irp->IoStatus.Status != STATUS_REPARSE);
+
     /* Check if we have an auxiliary buffer */
     if (Irp->Tail.Overlay.AuxiliaryBuffer)
     {
@@ -1142,18 +1328,16 @@ IofCompleteRequest(IN PIRP Irp,
     /* Check if this is a Paging I/O or Close Operation */
     if (Irp->Flags & (IRP_PAGING_IO | IRP_CLOSE_OPERATION))
     {
-        /* This should never happen! */
-        ASSERT(IsListEmpty(&Irp->ThreadListEntry));
-
-        /* Handle a Close Operation or Sync Paging I/O (see page 165) */
+        /* Handle a Close Operation or Sync Paging I/O */
         if (Irp->Flags & (IRP_SYNCHRONOUS_PAGING_IO | IRP_CLOSE_OPERATION))
         {
             /* Set the I/O Status and Signal the Event */
+            Flags = Irp->Flags & (IRP_SYNCHRONOUS_PAGING_IO | IRP_PAGING_IO);
             *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) IoFreeIrp(Irp);
+            if (Flags) IoFreeIrp(Irp);
         }
         else
         {
@@ -1173,7 +1357,8 @@ IofCompleteRequest(IN PIRP Irp,
                              PriorityBoost);
 #else
             /* Not implemented yet. */
-            ASSERT(FALSE);
+            DPRINT1("Not supported!\n");
+            while (TRUE);
 #endif
         }
 
@@ -1185,7 +1370,7 @@ IofCompleteRequest(IN PIRP Irp,
     Mdl = Irp->MdlAddress;
     while (Mdl)
     {
-        MmUnlockPages(Mdl);
+               MmUnlockPages(Mdl);
         Mdl = Mdl->Next;
     }
 
@@ -1203,7 +1388,7 @@ IofCompleteRequest(IN PIRP Irp,
     Thread = Irp->Tail.Overlay.Thread;
     FileObject = Irp->Tail.Overlay.OriginalFileObject;
 
-    /* Make sure the IRP isn't cancelled */
+    /* Make sure the IRP isn't canceled */
     if (!Irp->Cancel)
     {
         /* Initialize the APC */
@@ -1224,7 +1409,7 @@ IofCompleteRequest(IN PIRP Irp,
     }
     else
     {
-        /* The IRP just got cancelled... does a thread still own it? */
+        /* The IRP just got canceled... does a thread still own it? */
         Thread = Irp->Tail.Overlay.Thread;
         if (Thread)
         {
@@ -1247,21 +1432,62 @@ IofCompleteRequest(IN PIRP Irp,
         else
         {
             /* Nothing left for us to do, kill it */
+            ASSERT(Irp->Cancel);
             IopCleanupIrp(Irp, FileObject);
         }
     }
 }
 
+NTSTATUS
+NTAPI
+IopSynchronousCompletion(IN PDEVICE_OBJECT DeviceObject,
+                         IN PIRP Irp,
+                         IN PVOID Context)
+{
+    if (Irp->PendingReturned)
+        KeSetEvent((PKEVENT)Context, IO_NO_INCREMENT, FALSE);
+    return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
 /*
- * @unimplemented
+ * @implemented
  */
 BOOLEAN
 NTAPI
 IoForwardIrpSynchronously(IN PDEVICE_OBJECT DeviceObject,
                           IN PIRP Irp)
 {
-    UNIMPLEMENTED;
-    return FALSE;
+    KEVENT Event;
+    NTSTATUS Status;
+
+    /* Check if next stack location is available */
+    if (Irp->CurrentLocation < Irp->StackCount)
+    {
+        /* No more stack location */
+        return FALSE;
+    }
+
+    /* Initialize event */
+    KeInitializeEvent(&Event, NotificationEvent, FALSE);
+
+    /* Copy stack location for next driver */
+    IoCopyCurrentIrpStackLocationToNext(Irp);
+
+    /* Set a completion routine, which will signal the event */
+    IoSetCompletionRoutine(Irp, IopSynchronousCompletion, &Event, TRUE, TRUE, TRUE);
+
+    /* Call next driver */
+    Status = IoCallDriver(DeviceObject, Irp);
+
+    /* Check if irp is pending */
+    if (Status == STATUS_PENDING)
+    {
+        /* Yes, wait for its completion */
+        KeWaitForSingleObject(&Event, Suspended, KernelMode, FALSE, NULL);
+    }
+
+    /* Return success */
+    return TRUE;
 }
 
 /*
@@ -1280,6 +1506,7 @@ IoFreeIrp(IN PIRP Irp)
             Irp);
 
     /* Make sure the Thread IRP list is empty and that it OK to free it */
+    ASSERT(Irp->Type == IO_TYPE_IRP);
     ASSERT(IsListEmpty(&Irp->ThreadListEntry));
     ASSERT(Irp->CurrentLocation >= Irp->StackCount);
 
@@ -1287,7 +1514,7 @@ IoFreeIrp(IN PIRP Irp)
     if (!(Irp->AllocationFlags & IRP_ALLOCATED_FIXED_SIZE))
     {
         /* Free it */
-        ExFreePool(Irp);
+        ExFreePoolWithTag(Irp, TAG_IRP);
     }
     else
     {
@@ -1316,7 +1543,7 @@ IoFreeIrp(IN PIRP Irp)
             {
                 /* All lists failed, use the pool */
                 List->L.FreeMisses++;
-                ExFreePool(Irp);
+                ExFreePoolWithTag(Irp, TAG_IRP);
                 Irp = NULL;
             }
         }
@@ -1333,10 +1560,46 @@ IoFreeIrp(IN PIRP Irp)
 /*
  * @implemented
  */
-PEPROCESS NTAPI
+IO_PAGING_PRIORITY
+FASTCALL
+IoGetPagingIoPriority(IN PIRP Irp)
+{
+    IO_PAGING_PRIORITY Priority;
+    ULONG Flags;
+
+    /* Get the flags */
+    Flags = Irp->Flags;
+
+    /* Check what priority it has */
+    if (Flags & 0x8000) // FIXME: Undocumented flag
+    {
+        /* High priority */
+        Priority = IoPagingPriorityHigh;
+    }
+    else if (Flags & IRP_PAGING_IO)
+    {
+        /* Normal priority */
+        Priority = IoPagingPriorityNormal;
+    }
+    else
+    {
+        /* Invalid -- not a paging IRP */
+        Priority = IoPagingPriorityInvalid;
+    }
+
+    /* Return the priority */
+    return Priority;
+}
+
+/*
+ * @implemented
+ */
+PEPROCESS
+NTAPI
 IoGetRequestorProcess(IN PIRP Irp)
 {
-    return(Irp->Tail.Overlay.Thread->ThreadsProcess);
+    /* Return the requestor process */
+    return Irp->Tail.Overlay.Thread->ThreadsProcess;
 }
 
 /*
@@ -1346,7 +1609,8 @@ ULONG
 NTAPI
 IoGetRequestorProcessId(IN PIRP Irp)
 {
-    return (ULONG)(IoGetRequestorProcess(Irp)->UniqueProcessId);
+    /* Return the requestor process' id */
+    return PtrToUlong(IoGetRequestorProcess(Irp)->UniqueProcessId);
 }
 
 /*
@@ -1369,6 +1633,7 @@ PIRP
 NTAPI
 IoGetTopLevelIrp(VOID)
 {
+    /* Return the IRP */
     return (PIRP)PsGetCurrentThread()->TopLevelIrp;
 }
 
@@ -1408,12 +1673,11 @@ NTAPI
 IoIsOperationSynchronous(IN PIRP Irp)
 {
     /* Check the flags */
-    if (((Irp->Flags & IRP_SYNCHRONOUS_PAGING_IO) &&
-         (!(Irp->Flags & IRP_PAGING_IO) &&
-          !(Irp->Flags & IRP_SYNCHRONOUS_PAGING_IO))) ||
-        (Irp->Flags & IRP_SYNCHRONOUS_API) ||
-        (IoGetCurrentIrpStackLocation(Irp)->FileObject->Flags &
-         FO_SYNCHRONOUS_IO))
+    if (!(Irp->Flags & (IRP_PAGING_IO | IRP_SYNCHRONOUS_PAGING_IO)) &&
+        ((Irp->Flags & IRP_SYNCHRONOUS_PAGING_IO) ||
+         (Irp->Flags & IRP_SYNCHRONOUS_API) ||
+         (IoGetCurrentIrpStackLocation(Irp)->FileObject->Flags &
+          FO_SYNCHRONOUS_IO)))
     {
         /* Synch API or Paging I/O is OK, as is Sync File I/O */
         return TRUE;
@@ -1518,7 +1782,5 @@ NTAPI
 IoSetTopLevelIrp(IN PIRP Irp)
 {
     /* Set the IRP */
-    PsGetCurrentThread()->TopLevelIrp = (ULONG)Irp;
+    PsGetCurrentThread()->TopLevelIrp = (ULONG_PTR)Irp;
 }
-
-/* EOF */