Author: ion
Date: Sat Jul  1 21:40:03 2006
New Revision: 22740
URL: 
http://svn.reactos.org/svn/reactos?rev=22740&view=rev
Log:
- Add IoGetDevObjExtension for getting the PEXTENDED_DEVOBJ_EXTENSION from a device object
instead of always doing a huge typecast.
- Implement device unloading through IopUnloadDevice and device referencing through
IopReferenceDeviceObject and IopDereferenceDeviceObject, and make IoDeleteDevice and
IoDetachDevice unload the device on 0 reference count. Doesn't fully work yet.
- Simplify IoGetAttachedDEvice/IoGetAttachedDeviceReference not to use an extra stack
variable.
Modified:
    trunk/reactos/ntoskrnl/include/internal/io.h
    trunk/reactos/ntoskrnl/io/device.c
    trunk/reactos/ntoskrnl/io/file.c
Modified: trunk/reactos/ntoskrnl/include/internal/io.h
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/include/internal/…
==============================================================================
--- trunk/reactos/ntoskrnl/include/internal/io.h (original)
+++ trunk/reactos/ntoskrnl/include/internal/io.h Sat Jul  1 21:40:03 2006
@@ -79,6 +79,13 @@
      (!(FileObject) ?                                   \
         FALSE :                                         \
         FileObject->Flags & FO_SYNCHRONOUS_IO))         \
+
+//
+// Returns the internal Device Object Extension
+//
+#define IoGetDevObjExtension(DeviceObject)              \
+    ((PEXTENDED_DEVOBJ_EXTENSION)                       \
+     (DeviceObject->DeviceObjectExtension))             \
 /*
  * VOID
@@ -176,6 +183,15 @@
         (_DeviceNode);                                  \
     (_DeviceTreeTraverseContext)->Action = (_Action);   \
     (_DeviceTreeTraverseContext)->Context = (_Context); }
+
+//
+// Device List Operations
+//
+typedef enum _IOP_DEVICE_LIST_OPERATION
+{
+    IopRemove,
+    IopAdd
+} IOP_DEVICE_LIST_OPERATION, *PIOP_DEVICE_LIST_OPERATION;
 //
 // Special version of the IRP Overlay used to optimize I/O completion
@@ -563,6 +579,12 @@
     VOID
 );
+NTSTATUS
+NTAPI
+IopReferenceDeviceObject(
+    IN PDEVICE_OBJECT DeviceObject
+);
+
 //
 // Shutdown routines
 //
Modified: trunk/reactos/ntoskrnl/io/device.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/io/device.c?rev=2…
==============================================================================
--- trunk/reactos/ntoskrnl/io/device.c (original)
+++ trunk/reactos/ntoskrnl/io/device.c Sat Jul  1 21:40:03 2006
@@ -171,7 +171,7 @@
 }
 NTSTATUS
-STDCALL
+NTAPI
 IopGetDeviceObjectPointer(IN PUNICODE_STRING ObjectName,
                           IN ACCESS_MASK DesiredAccess,
                           OUT PFILE_OBJECT *FileObject,
@@ -217,6 +217,235 @@
     return Status;
 }
+PDEVICE_OBJECT
+NTAPI
+IopGetLowestDevice(IN PDEVICE_OBJECT DeviceObject)
+{
+    PDEVICE_OBJECT LowestDevice;
+    PEXTENDED_DEVOBJ_EXTENSION DeviceExtension;
+
+    /* Get the current device and its extension */
+    LowestDevice = DeviceObject;
+    DeviceExtension = IoGetDevObjExtension(LowestDevice);
+
+    /* Keep looping as long as we're attached */
+    while (DeviceExtension->AttachedTo)
+    {
+        /* Get the lowest device and its extension */
+        LowestDevice = DeviceExtension->AttachedTo;
+        DeviceExtension = IoGetDevObjExtension(LowestDevice);
+    }
+
+    /* Return the lowest device */
+    return LowestDevice;
+}
+
+VOID
+NTAPI
+IopEditDeviceList(IN PDRIVER_OBJECT DriverObject,
+                  IN PDEVICE_OBJECT DeviceObject,
+                  IN IOP_DEVICE_LIST_OPERATION Type)
+{
+    PDEVICE_OBJECT Previous;
+
+    /* Check the type of operation */
+    if (Type == IopRemove)
+    {
+        /* Get the current device and check if it's the current one */
+        Previous = DeviceObject->DriverObject->DeviceObject;
+        if (Previous == DeviceObject)
+        {
+            /* It is, simply unlink this one directly */
+            DeviceObject->DriverObject->DeviceObject =
+                DeviceObject->NextDevice;
+        }
+        else
+        {
+            /* It's not, so loop until we find the device */
+            while (Previous->NextDevice != DeviceObject)
+            {
+                /* Not this one, keep moving */
+                Previous = Previous->NextDevice;
+            }
+
+            /* We found it, now unlink us */
+            Previous->NextDevice = DeviceObject->NextDevice;
+        }
+    }
+    else
+    {
+        /* Link the device object and the driver object */
+        DeviceObject->NextDevice = DriverObject->DeviceObject;
+        DriverObject->DeviceObject = DeviceObject;
+    }
+}
+
+VOID
+NTAPI
+IopUnloadDevice(IN PDEVICE_OBJECT DeviceObject)
+{
+    PDRIVER_OBJECT DriverObject = DeviceObject->DriverObject;
+    PDEVICE_OBJECT AttachedDeviceObject, LowestDeviceObject;
+    PEXTENDED_DEVOBJ_EXTENSION ThisExtension, DeviceExtension;
+    PDEVICE_NODE DeviceNode;
+    BOOLEAN SafeToUnload = TRUE;
+
+    /* Check if removal is pending */
+    ThisExtension = IoGetDevObjExtension(DeviceObject);
+    if (ThisExtension->ExtensionFlags & DOE_REMOVE_PENDING)
+    {
+        /* Get the PDO, extension, and node */
+        LowestDeviceObject = IopGetLowestDevice(DeviceObject);
+        DeviceExtension = IoGetDevObjExtension(LowestDeviceObject);
+        DeviceNode = DeviceExtension->DeviceNode;
+
+        /* The PDO needs a device node */
+        ASSERT(DeviceNode != NULL);
+
+        /* Loop all attached objects */
+        AttachedDeviceObject = LowestDeviceObject;
+        while (AttachedDeviceObject)
+        {
+            /* Make sure they're dereferenced */
+            if (AttachedDeviceObject->ReferenceCount) return;
+            AttachedDeviceObject = AttachedDeviceObject->AttachedDevice;
+        }
+
+        /* Loop all attached objects */
+        AttachedDeviceObject = LowestDeviceObject;
+        while (AttachedDeviceObject)
+        {
+            /* Get the device extension */
+            DeviceExtension = IoGetDevObjExtension(AttachedDeviceObject);
+
+            /* Remove the pending flag and set processed */
+            DeviceExtension->ExtensionFlags &= ~DOE_REMOVE_PENDING;
+            DeviceExtension->ExtensionFlags |= DOE_REMOVE_PROCESSED;
+            AttachedDeviceObject = AttachedDeviceObject->AttachedDevice;
+        }
+
+        /*
+         * FIXME: TODO HPOUSSIN
+         * We need to parse/lock the device node, and if we have any pending
+         * surprise removals, query all relationships and send IRP_MN_REMOVE_
+         * _DEVICE to the devices related...
+         */
+        return;
+    }
+
+    /* Check if deletion is pending */
+    if (ThisExtension->ExtensionFlags & DOE_DELETE_PENDING)
+    {
+        /* Make sure unload is pending */
+        if (!(ThisExtension->ExtensionFlags & DOE_UNLOAD_PENDING) ||
+            (DriverObject->Flags & DRVO_UNLOAD_INVOKED))
+        {
+            /* We can't unload anymore */
+            SafeToUnload = FALSE;
+        }
+
+        /*
+         * Check if we have an attached device and fail if we're attached
+         * and still have a reference count.
+         */
+        AttachedDeviceObject = DeviceObject->AttachedDevice;
+        if ((AttachedDeviceObject) && (DeviceObject->ReferenceCount)) return;
+
+        /* Check if we have a Security Descriptor */
+        if (DeviceObject->SecurityDescriptor)
+        {
+            /* Free it */
+            ExFreePool(DeviceObject->SecurityDescriptor);
+        }
+
+        /* Remove the device from the list */
+        IopEditDeviceList(DeviceObject->DriverObject, DeviceObject, IopRemove);
+
+        /* Dereference the keep-alive */
+        ObDereferenceObject(DeviceObject);
+
+        /* If we're not unloading, stop here */
+        if (!SafeToUnload) return;
+    }
+
+    /* Loop all the device objects */
+    DeviceObject = DriverObject->DeviceObject;
+    while (DeviceObject)
+    {
+        /*
+         * Make sure we're not attached, having a reference count
+         * or already deleting
+         */
+        if ((DeviceObject->ReferenceCount) ||
+             (DeviceObject->AttachedDevice) ||
+             (IoGetDevObjExtension(DeviceObject)->ExtensionFlags &
+              (DOE_DELETE_PENDING | DOE_REMOVE_PENDING)))
+        {
+            /* We're not safe to unload, quit */
+            return;
+        }
+
+        /* Check the next device */
+        DeviceObject = DeviceObject->NextDevice;
+    }
+
+    /* Set the unload invoked flag */
+    DriverObject->Flags |= DRVO_UNLOAD_INVOKED;
+
+    /* Unload it */
+    if (DriverObject->DriverUnload) DriverObject->DriverUnload(DriverObject);
+}
+
+VOID
+NTAPI
+IopDereferenceDeviceObject(IN PDEVICE_OBJECT DeviceObject,
+                           IN BOOLEAN ForceUnload)
+{
+    /* Sanity check */
+    ASSERT(DeviceObject->ReferenceCount);
+
+    /* Dereference the device */
+    DeviceObject->ReferenceCount--;
+
+    /*
+     * Check if we can unload it and it's safe to unload (or if we're forcing
+     * an unload, which is OK too).
+     */
+    if (!(DeviceObject->ReferenceCount) &&
+        ((ForceUnload) || (IoGetDevObjExtension(DeviceObject)->ExtensionFlags &
+                           (DOE_UNLOAD_PENDING |
+                            DOE_DELETE_PENDING |
+                            DOE_REMOVE_PENDING |
+                            DOE_REMOVE_PROCESSED))))
+    {
+        /* Unload it */
+        IopUnloadDevice(DeviceObject);
+    }
+}
+
+NTSTATUS
+NTAPI
+IopReferenceDeviceObject(IN PDEVICE_OBJECT DeviceObject)
+{
+    /* Make sure the object is valid */
+    if ((IoGetDevObjExtension(DeviceObject)->ExtensionFlags &
+        (DOE_UNLOAD_PENDING |
+         DOE_DELETE_PENDING |
+         DOE_REMOVE_PENDING |
+         DOE_REMOVE_PROCESSED)) ||
+        (DeviceObject->Flags & DO_DEVICE_INITIALIZING))
+    {
+        /* It's unloading or initializing, so fail */
+        return STATUS_NO_SUCH_DEVICE;
+    }
+    else
+    {
+        /* Increase reference count */
+        DeviceObject->ReferenceCount++;
+        return STATUS_SUCCESS;
+    }
+}
+
 /* PUBLIC FUNCTIONS ***********************************************************/
 /*
@@ -238,7 +467,7 @@
  *    @implemented
  */
 NTSTATUS
-STDCALL
+NTAPI
 IoAttachDevice(PDEVICE_OBJECT SourceDevice,
                PUNICODE_STRING TargetDeviceName,
                PDEVICE_OBJECT *AttachedDevice)
@@ -273,7 +502,7 @@
  *    @implemented
  */
 NTSTATUS
-STDCALL
+NTAPI
 IoAttachDeviceByPointer(IN PDEVICE_OBJECT SourceDevice,
                         IN PDEVICE_OBJECT TargetDevice)
 {
@@ -295,7 +524,7 @@
  *    @implemented
  */
 PDEVICE_OBJECT
-STDCALL
+NTAPI
 IoAttachDeviceToDeviceStack(PDEVICE_OBJECT SourceDevice,
                             PDEVICE_OBJECT TargetDevice)
 {
@@ -315,7 +544,7 @@
  * @implemented
  */
 NTSTATUS
-STDCALL
+NTAPI
 IoAttachDeviceToDeviceStackSafe(IN PDEVICE_OBJECT SourceDevice,
                                 IN PDEVICE_OBJECT TargetDevice,
                                 OUT PDEVICE_OBJECT *AttachedToDeviceObject)
@@ -325,15 +554,14 @@
     /* Get the Attached Device and source extension */
     AttachedDevice = IoGetAttachedDevice(TargetDevice);
-    SourceDeviceExtension = (PEXTENDED_DEVOBJ_EXTENSION)SourceDevice->
-                            DeviceObjectExtension;
+    SourceDeviceExtension = IoGetDevObjExtension(SourceDevice);
     /* Make sure that it's in a correct state */
-    if (!(((PEXTENDED_DEVOBJ_EXTENSION)AttachedDevice->DeviceObjectExtension)->
-            ExtensionFlags & (DOE_UNLOAD_PENDING |
-                              DOE_DELETE_PENDING |
-                              DOE_REMOVE_PENDING |
-                              DOE_REMOVE_PROCESSED)))
+    if (!IoGetDevObjExtension(AttachedDevice)->ExtensionFlags &
+        (DOE_UNLOAD_PENDING |
+         DOE_DELETE_PENDING |
+         DOE_REMOVE_PENDING |
+         DOE_REMOVE_PROCESSED))
     {
         /* Update atached device fields */
         AttachedDevice->AttachedDevice = SourceDevice;
@@ -392,7 +620,7 @@
  *    @implemented
  */
 NTSTATUS
-STDCALL
+NTAPI
 IoCreateDevice(IN PDRIVER_OBJECT DriverObject,
                IN ULONG DeviceExtensionSize,
                IN PUNICODE_STRING DeviceName,
@@ -559,8 +787,7 @@
     /* Now do the final linking */
     ObReferenceObject(DriverObject);
     CreatedDeviceObject->DriverObject = DriverObject;
-    CreatedDeviceObject->NextDevice = DriverObject->DeviceObject;
-    DriverObject->DeviceObject = CreatedDeviceObject;
+    IopEditDeviceList(DriverObject, CreatedDeviceObject, IopAdd);
     /* Close the temporary handle and return to caller */
     NtClose(TempHandle);
@@ -575,46 +802,39 @@
  *    @implemented
  */
 VOID
-STDCALL
-IoDeleteDevice(PDEVICE_OBJECT DeviceObject)
-{
-   PDEVICE_OBJECT Previous;
-
-   if (DeviceObject->Flags & DO_SHUTDOWN_REGISTERED)
-      IoUnregisterShutdownNotification(DeviceObject);
-
-   /* Remove the timer if it exists */
-   if (DeviceObject->Timer)
-   {
-      IopRemoveTimerFromTimerList(DeviceObject->Timer);
-      ExFreePoolWithTag(DeviceObject->Timer, TAG_IO_TIMER);
-   }
-
-   /* Remove device from driver device list */
-   Previous = DeviceObject->DriverObject->DeviceObject;
-   if (Previous == DeviceObject)
-   {
-      DeviceObject->DriverObject->DeviceObject = DeviceObject->NextDevice;
-   }
-   else
-   {
-      while (Previous->NextDevice != DeviceObject)
-         Previous = Previous->NextDevice;
-      Previous->NextDevice = DeviceObject->NextDevice;
-   }
-
-   /* I guess this should be removed later... but it shouldn't cause problems */
-
((PEXTENDED_DEVOBJ_EXTENSION)DeviceObject->DeviceObjectExtension)->ExtensionFlags |=
DOE_DELETE_PENDING;
-
-   /* Make the object temporary. This should automatically remove the device
-      from the namespace */
-   ObMakeTemporaryObject(DeviceObject);
-
-   /* Dereference the driver object */
-   ObDereferenceObject(DeviceObject->DriverObject);
-
-   /* Remove the keep-alive reference */
-   ObDereferenceObject(DeviceObject);
+NTAPI
+IoDeleteDevice(IN PDEVICE_OBJECT DeviceObject)
+{
+    PIO_TIMER Timer;
+
+    /* Check if the device is registered for shutdown notifications */
+    if (DeviceObject->Flags & DO_SHUTDOWN_REGISTERED)
+    {
+        /* Call the shutdown notifications */
+        IoUnregisterShutdownNotification(DeviceObject);
+    }
+
+    /* Check if it has a timer */
+    Timer = DeviceObject->Timer;
+    if (Timer)
+    {
+        /* Remove it and free it */
+        IopRemoveTimerFromTimerList(Timer);
+        ExFreePoolWithTag(Timer, TAG_IO_TIMER);
+    }
+
+    /* Check if the device has a name */
+    if (DeviceObject->Flags & DO_DEVICE_HAS_NAME)
+    {
+        /* It does, make it temporary so we can remove it */
+        ObMakeTemporaryObject(DeviceObject);
+    }
+
+    /* Set the pending delete flag */
+    IoGetDevObjExtension(DeviceObject)->ExtensionFlags |= DOE_DELETE_PENDING;
+
+    /* Check if the device object can be unloaded */
+    if (!DeviceObject->ReferenceCount) IopUnloadDevice(DeviceObject);
 }
 /*
@@ -624,21 +844,28 @@
  *    @implemented
  */
 VOID
-STDCALL
-IoDetachDevice(PDEVICE_OBJECT TargetDevice)
-{
-    DPRINT("IoDetachDevice(TargetDevice 0x%p)\n", TargetDevice);
-
+NTAPI
+IoDetachDevice(IN PDEVICE_OBJECT TargetDevice)
+{
     /* Remove the attachment */
-
((PEXTENDED_DEVOBJ_EXTENSION)TargetDevice->AttachedDevice->DeviceObjectExtension)->AttachedTo
= NULL;
+    IoGetDevObjExtension(TargetDevice->AttachedDevice)->AttachedTo = NULL;
     TargetDevice->AttachedDevice = NULL;
+
+    /* Check if it's ok to delete this device */
+    if ((IoGetDevObjExtension(TargetDevice)->ExtensionFlags &
+        (DOE_UNLOAD_PENDING | DOE_DELETE_PENDING | DOE_REMOVE_PENDING)) &&
+        !(TargetDevice->ReferenceCount))
+    {
+        /* It is, do it */
+        IopUnloadDevice(TargetDevice);
+    }
 }
 /*
  * @implemented
  */
 NTSTATUS
-STDCALL
+NTAPI
 IoEnumerateDeviceObjectList(IN  PDRIVER_OBJECT DriverObject,
                             IN  PDEVICE_OBJECT *DeviceObjectList,
                             IN  ULONG DeviceObjectListSize,
@@ -647,13 +874,8 @@
     ULONG ActualDevices = 1;
     PDEVICE_OBJECT CurrentDevice = DriverObject->DeviceObject;
-    DPRINT1("IoEnumerateDeviceObjectList\n");
-
     /* Find out how many devices we'll enumerate */
-    while ((CurrentDevice = CurrentDevice->NextDevice))
-    {
-        ActualDevices++;
-    }
+    while ((CurrentDevice = CurrentDevice->NextDevice)) ActualDevices++;
     /* Go back to the first */
     CurrentDevice = DriverObject->DeviceObject;
@@ -698,19 +920,18 @@
  *    @implemented
  */
 PDEVICE_OBJECT
-STDCALL
+NTAPI
 IoGetAttachedDevice(PDEVICE_OBJECT DeviceObject)
 {
-    PDEVICE_OBJECT Current = DeviceObject;
-
     /* Get the last attached device */
-    while (Current->AttachedDevice)
-    {
-        Current = Current->AttachedDevice;
+    while (DeviceObject->AttachedDevice)
+    {
+        /* Move to the next one */
+        DeviceObject = DeviceObject->AttachedDevice;
     }
     /* Return it */
-    return Current;
+    return DeviceObject;
 }
 /*
@@ -720,14 +941,13 @@
  *    @implemented
  */
 PDEVICE_OBJECT
-STDCALL
+NTAPI
 IoGetAttachedDeviceReference(PDEVICE_OBJECT DeviceObject)
 {
-    PDEVICE_OBJECT Current = IoGetAttachedDevice(DeviceObject);
-
-    /* Reference the ATtached Device */
-    ObReferenceObject(Current);
-    return Current;
+    /* Reference the Attached Device */
+    DeviceObject = IoGetAttachedDevice(DeviceObject);
+    ObReferenceObject(DeviceObject);
+    return DeviceObject;
 }
 /*
Modified: trunk/reactos/ntoskrnl/io/file.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/io/file.c?rev=227…
==============================================================================
--- trunk/reactos/ntoskrnl/io/file.c (original)
+++ trunk/reactos/ntoskrnl/io/file.c Sat Jul  1 21:40:03 2006
@@ -55,6 +55,9 @@
     if (!*RemainingName->Buffer)
     {
         DeviceObject = ParseObject;
+        Status = IopReferenceDeviceObject(DeviceObject);
+        // fixme: NT wouldn't allow this
+        //if (!NT_SUCCESS(Status)) return Status;// KEBUGCHECK(0);
         Status = ObCreateObject(AccessMode,
                                 IoFileObjectType,
@@ -116,6 +119,9 @@
         DeviceObject = OpenPacket->RelatedFileObject->DeviceObject;
     }
+    Status = IopReferenceDeviceObject(DeviceObject);
+    // fixme: NT wouldn't allow this
+    //if (!NT_SUCCESS(Status)) return Status;// KEBUGCHECK(0);
     RtlCreateUnicodeString(&FileObject->FileName, RemainingName->Buffer);
     FileObject->DeviceObject = DeviceObject;
     *Object = FileObject;