2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/io/device.c
5 * PURPOSE: Device Object Management, including Notifications and Queues.
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
7 * Filip Navara (navaraf@reactos.org)
8 * Hervé Poussineau (hpoussin@reactos.org)
11 /* INCLUDES *******************************************************************/
15 #include <internal/debug.h>
17 /* GLOBALS ********************************************************************/
19 ULONG IopDeviceObjectNumber
= 0;
21 LIST_ENTRY ShutdownListHead
, LastChanceShutdownListHead
;
22 KSPIN_LOCK ShutdownListLock
;
24 /* PRIVATE FUNCTIONS **********************************************************/
28 IoShutdownRegisteredDevices(VOID
)
30 PLIST_ENTRY ListEntry
;
31 PDEVICE_OBJECT DeviceObject
;
32 PSHUTDOWN_ENTRY ShutdownEntry
;
33 IO_STATUS_BLOCK StatusBlock
;
38 /* Initialize an event to wait on */
39 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
41 /* Get the first entry and start looping */
42 ListEntry
= ExInterlockedRemoveHeadList(&ShutdownListHead
,
46 /* Get the shutdown entry */
47 ShutdownEntry
= CONTAINING_RECORD(ListEntry
,
51 /* Get the attached device */
52 DeviceObject
= IoGetAttachedDevice(ShutdownEntry
->DeviceObject
);
54 /* Build the shutdown IRP and call the driver */
55 Irp
= IoBuildSynchronousFsdRequest(IRP_MJ_SHUTDOWN
,
62 Status
= IoCallDriver(DeviceObject
, Irp
);
63 if (Status
== STATUS_PENDING
)
65 /* Wait on the driver */
66 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
69 /* Free the shutdown entry and reset the event */
70 ExFreePool(ShutdownEntry
);
73 /* Go to the next entry */
74 ListEntry
= ExInterlockedRemoveHeadList(&ShutdownListHead
,
81 IopGetDeviceObjectPointer(IN PUNICODE_STRING ObjectName
,
82 IN ACCESS_MASK DesiredAccess
,
83 OUT PFILE_OBJECT
*FileObject
,
84 OUT PDEVICE_OBJECT
*DeviceObject
,
87 OBJECT_ATTRIBUTES ObjectAttributes
;
88 IO_STATUS_BLOCK StatusBlock
;
89 PFILE_OBJECT LocalFileObject
;
94 InitializeObjectAttributes(&ObjectAttributes
,
99 Status
= ZwOpenFile(&FileHandle
,
104 FILE_NON_DIRECTORY_FILE
| AttachFlag
);
105 if (!NT_SUCCESS(Status
)) return Status
;
107 /* Get File Object */
108 Status
= ObReferenceObjectByHandle(FileHandle
,
112 (PVOID
*)&LocalFileObject
,
114 if (NT_SUCCESS(Status
))
116 /* Return the requested data */
117 *DeviceObject
= IoGetRelatedDeviceObject(LocalFileObject
);
118 *FileObject
= LocalFileObject
;
122 /* Close the handle */
128 IopGetLowestDevice(IN PDEVICE_OBJECT DeviceObject
)
130 PDEVICE_OBJECT LowestDevice
;
131 PEXTENDED_DEVOBJ_EXTENSION DeviceExtension
;
133 /* Get the current device and its extension */
134 LowestDevice
= DeviceObject
;
135 DeviceExtension
= IoGetDevObjExtension(LowestDevice
);
137 /* Keep looping as long as we're attached */
138 while (DeviceExtension
->AttachedTo
)
140 /* Get the lowest device and its extension */
141 LowestDevice
= DeviceExtension
->AttachedTo
;
142 DeviceExtension
= IoGetDevObjExtension(LowestDevice
);
145 /* Return the lowest device */
151 IopEditDeviceList(IN PDRIVER_OBJECT DriverObject
,
152 IN PDEVICE_OBJECT DeviceObject
,
153 IN IOP_DEVICE_LIST_OPERATION Type
)
155 PDEVICE_OBJECT Previous
;
157 /* Check the type of operation */
158 if (Type
== IopRemove
)
160 /* Get the current device and check if it's the current one */
161 Previous
= DeviceObject
->DriverObject
->DeviceObject
;
162 if (Previous
== DeviceObject
)
164 /* It is, simply unlink this one directly */
165 DeviceObject
->DriverObject
->DeviceObject
=
166 DeviceObject
->NextDevice
;
170 /* It's not, so loop until we find the device */
171 while (Previous
->NextDevice
!= DeviceObject
)
173 /* Not this one, keep moving */
174 Previous
= Previous
->NextDevice
;
177 /* We found it, now unlink us */
178 Previous
->NextDevice
= DeviceObject
->NextDevice
;
183 /* Link the device object and the driver object */
184 DeviceObject
->NextDevice
= DriverObject
->DeviceObject
;
185 DriverObject
->DeviceObject
= DeviceObject
;
191 IopUnloadDevice(IN PDEVICE_OBJECT DeviceObject
)
193 PDRIVER_OBJECT DriverObject
= DeviceObject
->DriverObject
;
194 PDEVICE_OBJECT AttachedDeviceObject
, LowestDeviceObject
;
195 PEXTENDED_DEVOBJ_EXTENSION ThisExtension
, DeviceExtension
;
196 PDEVICE_NODE DeviceNode
;
197 BOOLEAN SafeToUnload
= TRUE
;
199 /* Check if removal is pending */
200 ThisExtension
= IoGetDevObjExtension(DeviceObject
);
201 if (ThisExtension
->ExtensionFlags
& DOE_REMOVE_PENDING
)
203 /* Get the PDO, extension, and node */
204 LowestDeviceObject
= IopGetLowestDevice(DeviceObject
);
205 DeviceExtension
= IoGetDevObjExtension(LowestDeviceObject
);
206 DeviceNode
= DeviceExtension
->DeviceNode
;
208 /* The PDO needs a device node */
209 ASSERT(DeviceNode
!= NULL
);
211 /* Loop all attached objects */
212 AttachedDeviceObject
= LowestDeviceObject
;
213 while (AttachedDeviceObject
)
215 /* Make sure they're dereferenced */
216 if (AttachedDeviceObject
->ReferenceCount
) return;
217 AttachedDeviceObject
= AttachedDeviceObject
->AttachedDevice
;
220 /* Loop all attached objects */
221 AttachedDeviceObject
= LowestDeviceObject
;
222 while (AttachedDeviceObject
)
224 /* Get the device extension */
225 DeviceExtension
= IoGetDevObjExtension(AttachedDeviceObject
);
227 /* Remove the pending flag and set processed */
228 DeviceExtension
->ExtensionFlags
&= ~DOE_REMOVE_PENDING
;
229 DeviceExtension
->ExtensionFlags
|= DOE_REMOVE_PROCESSED
;
230 AttachedDeviceObject
= AttachedDeviceObject
->AttachedDevice
;
234 * FIXME: TODO HPOUSSIN
235 * We need to parse/lock the device node, and if we have any pending
236 * surprise removals, query all relationships and send IRP_MN_REMOVE_
237 * _DEVICE to the devices related...
242 /* Check if deletion is pending */
243 if (ThisExtension
->ExtensionFlags
& DOE_DELETE_PENDING
)
245 /* Make sure unload is pending */
246 if (!(ThisExtension
->ExtensionFlags
& DOE_UNLOAD_PENDING
) ||
247 (DriverObject
->Flags
& DRVO_UNLOAD_INVOKED
))
249 /* We can't unload anymore */
250 SafeToUnload
= FALSE
;
254 * Check if we have an attached device and fail if we're attached
255 * and still have a reference count.
257 AttachedDeviceObject
= DeviceObject
->AttachedDevice
;
258 if ((AttachedDeviceObject
) && (DeviceObject
->ReferenceCount
)) return;
260 /* Check if we have a Security Descriptor */
261 if (DeviceObject
->SecurityDescriptor
)
264 ExFreePool(DeviceObject
->SecurityDescriptor
);
267 /* Remove the device from the list */
268 IopEditDeviceList(DeviceObject
->DriverObject
, DeviceObject
, IopRemove
);
270 /* Dereference the keep-alive */
271 ObDereferenceObject(DeviceObject
);
273 /* If we're not unloading, stop here */
274 if (!SafeToUnload
) return;
277 /* Loop all the device objects */
278 DeviceObject
= DriverObject
->DeviceObject
;
282 * Make sure we're not attached, having a reference count
283 * or already deleting
285 if ((DeviceObject
->ReferenceCount
) ||
286 (DeviceObject
->AttachedDevice
) ||
287 (IoGetDevObjExtension(DeviceObject
)->ExtensionFlags
&
288 (DOE_DELETE_PENDING
| DOE_REMOVE_PENDING
)))
290 /* We're not safe to unload, quit */
294 /* Check the next device */
295 DeviceObject
= DeviceObject
->NextDevice
;
298 /* Set the unload invoked flag */
299 DriverObject
->Flags
|= DRVO_UNLOAD_INVOKED
;
302 if (DriverObject
->DriverUnload
) DriverObject
->DriverUnload(DriverObject
);
307 IopDereferenceDeviceObject(IN PDEVICE_OBJECT DeviceObject
,
308 IN BOOLEAN ForceUnload
)
311 ASSERT(DeviceObject
->ReferenceCount
);
313 /* Dereference the device */
314 DeviceObject
->ReferenceCount
--;
317 * Check if we can unload it and it's safe to unload (or if we're forcing
318 * an unload, which is OK too).
320 if (!(DeviceObject
->ReferenceCount
) &&
321 ((ForceUnload
) || (IoGetDevObjExtension(DeviceObject
)->ExtensionFlags
&
322 (DOE_UNLOAD_PENDING
|
325 DOE_REMOVE_PROCESSED
))))
328 IopUnloadDevice(DeviceObject
);
334 IopStartNextPacketByKey(IN PDEVICE_OBJECT DeviceObject
,
335 IN BOOLEAN Cancelable
,
338 PKDEVICE_QUEUE_ENTRY Entry
;
342 /* Acquire the cancel lock if this is cancelable */
343 if (Cancelable
) IoAcquireCancelSpinLock(&OldIrql
);
345 /* Clear the current IRP */
346 DeviceObject
->CurrentIrp
= NULL
;
348 /* Remove an entry from the queue */
349 Entry
= KeRemoveByKeyDeviceQueue(&DeviceObject
->DeviceQueue
, Key
);
352 /* Get the IRP and set it */
353 Irp
= CONTAINING_RECORD(Entry
, IRP
, Tail
.Overlay
.DeviceQueueEntry
);
354 DeviceObject
->CurrentIrp
= Irp
;
356 /* Check if this is a cancelable packet */
359 /* Check if the caller requested no cancellation */
360 if (IoGetDevObjExtension(DeviceObject
)->StartIoFlags
&
363 /* He did, so remove the cancel routine */
364 Irp
->CancelRoutine
= NULL
;
367 /* Release the cancel lock */
368 IoReleaseCancelSpinLock(OldIrql
);
371 /* Call the Start I/O Routine */
372 DeviceObject
->DriverObject
->DriverStartIo(DeviceObject
, Irp
);
376 /* Otherwise, release the cancel lock if we had acquired it */
377 if (Cancelable
) IoReleaseCancelSpinLock(OldIrql
);
383 IopStartNextPacket(IN PDEVICE_OBJECT DeviceObject
,
384 IN BOOLEAN Cancelable
)
386 PKDEVICE_QUEUE_ENTRY Entry
;
390 /* Acquire the cancel lock if this is cancelable */
391 if (Cancelable
) IoAcquireCancelSpinLock(&OldIrql
);
393 /* Clear the current IRP */
394 DeviceObject
->CurrentIrp
= NULL
;
396 /* Remove an entry from the queue */
397 Entry
= KeRemoveDeviceQueue(&DeviceObject
->DeviceQueue
);
400 /* Get the IRP and set it */
401 Irp
= CONTAINING_RECORD(Entry
, IRP
, Tail
.Overlay
.DeviceQueueEntry
);
402 DeviceObject
->CurrentIrp
= Irp
;
404 /* Check if this is a cancelable packet */
407 /* Check if the caller requested no cancellation */
408 if (IoGetDevObjExtension(DeviceObject
)->StartIoFlags
&
411 /* He did, so remove the cancel routine */
412 Irp
->CancelRoutine
= NULL
;
415 /* Release the cancel lock */
416 IoReleaseCancelSpinLock(OldIrql
);
419 /* Call the Start I/O Routine */
420 DeviceObject
->DriverObject
->DriverStartIo(DeviceObject
, Irp
);
424 /* Otherwise, release the cancel lock if we had acquired it */
425 if (Cancelable
) IoReleaseCancelSpinLock(OldIrql
);
431 IopStartNextPacketByKeyEx(IN PDEVICE_OBJECT DeviceObject
,
435 PEXTENDED_DEVOBJ_EXTENSION DeviceExtension
;
436 ULONG CurrentKey
= Key
;
437 ULONG CurrentFlags
= Flags
;
439 /* Get the device extension and start the packet loop */
440 DeviceExtension
= IoGetDevObjExtension(DeviceObject
);
443 /* Increase the count */
444 if (InterlockedIncrement(&DeviceExtension
->StartIoCount
) > 1)
447 * We've already called the routine once...
448 * All we have to do is save the key and add the new flags
450 DeviceExtension
->StartIoFlags
|= CurrentFlags
;
451 DeviceExtension
->StartIoKey
= CurrentKey
;
455 /* Mask out the current packet flags and key */
456 DeviceExtension
->StartIoFlags
&= ~(DOE_SIO_WITH_KEY
|
459 DeviceExtension
->StartIoKey
= 0;
461 /* Check if this is a packet start with key */
462 if (Flags
& DOE_SIO_WITH_KEY
)
464 /* Start the packet with a key */
465 IopStartNextPacketByKey(DeviceObject
,
466 (DOE_SIO_CANCELABLE
) ? TRUE
: FALSE
,
469 else if (Flags
& DOE_SIO_NO_KEY
)
471 /* Start the packet */
472 IopStartNextPacket(DeviceObject
,
473 (DOE_SIO_CANCELABLE
) ? TRUE
: FALSE
);
477 /* Decrease the Start I/O count and check if it's 0 now */
478 if (!InterlockedDecrement(&DeviceExtension
->StartIoCount
))
480 /* Get the current active key and flags */
481 CurrentKey
= DeviceExtension
->StartIoKey
;
482 CurrentFlags
= DeviceExtension
->StartIoFlags
& (DOE_SIO_WITH_KEY
|
486 /* Check if we should still loop */
487 if (!(CurrentFlags
& (DOE_SIO_WITH_KEY
| DOE_SIO_NO_KEY
))) break;
491 /* There are still Start I/Os active, so quit this loop */
497 /* PUBLIC FUNCTIONS ***********************************************************/
502 * Layers a device over the highest device in a device stack.
506 * Device to be attached.
509 * Name of the target device.
512 * Caller storage for the device attached to.
519 IoAttachDevice(PDEVICE_OBJECT SourceDevice
,
520 PUNICODE_STRING TargetDeviceName
,
521 PDEVICE_OBJECT
*AttachedDevice
)
524 PFILE_OBJECT FileObject
= NULL
;
525 PDEVICE_OBJECT TargetDevice
= NULL
;
527 /* Call the helper routine for an attach operation */
528 Status
= IopGetDeviceObjectPointer(TargetDeviceName
,
529 FILE_READ_ATTRIBUTES
,
532 IO_ATTACH_DEVICE_API
);
533 if (!NT_SUCCESS(Status
)) return Status
;
535 /* Attach the device */
536 Status
= IoAttachDeviceToDeviceStackSafe(SourceDevice
,
539 if (!*AttachedDevice
) Status
= STATUS_NO_SUCH_DEVICE
;
542 ObDereferenceObject(FileObject
);
547 * IoAttachDeviceByPointer
554 IoAttachDeviceByPointer(IN PDEVICE_OBJECT SourceDevice
,
555 IN PDEVICE_OBJECT TargetDevice
)
557 PDEVICE_OBJECT AttachedDevice
;
558 NTSTATUS Status
= STATUS_SUCCESS
;
561 AttachedDevice
= IoAttachDeviceToDeviceStack(SourceDevice
, TargetDevice
);
562 if (!AttachedDevice
) Status
= STATUS_NO_SUCH_DEVICE
;
564 /* Return the status */
569 * IoAttachDeviceToDeviceStack
576 IoAttachDeviceToDeviceStack(PDEVICE_OBJECT SourceDevice
,
577 PDEVICE_OBJECT TargetDevice
)
580 PDEVICE_OBJECT LocalAttach
;
582 /* Attach it safely */
583 Status
= IoAttachDeviceToDeviceStackSafe(SourceDevice
,
596 IoAttachDeviceToDeviceStackSafe(IN PDEVICE_OBJECT SourceDevice
,
597 IN PDEVICE_OBJECT TargetDevice
,
598 OUT PDEVICE_OBJECT
*AttachedToDeviceObject
)
600 PDEVICE_OBJECT AttachedDevice
;
601 PEXTENDED_DEVOBJ_EXTENSION SourceDeviceExtension
;
603 /* Get the Attached Device and source extension */
604 AttachedDevice
= IoGetAttachedDevice(TargetDevice
);
605 SourceDeviceExtension
= IoGetDevObjExtension(SourceDevice
);
607 /* Make sure that it's in a correct state */
608 if (!IoGetDevObjExtension(AttachedDevice
)->ExtensionFlags
&
609 (DOE_UNLOAD_PENDING
|
612 DOE_REMOVE_PROCESSED
))
614 /* Update atached device fields */
615 AttachedDevice
->AttachedDevice
= SourceDevice
;
616 AttachedDevice
->Spare1
++;
618 /* Update the source with the attached data */
619 SourceDevice
->StackSize
= AttachedDevice
->StackSize
+ 1;
620 SourceDevice
->AlignmentRequirement
= AttachedDevice
->
621 AlignmentRequirement
;
622 SourceDevice
->SectorSize
= AttachedDevice
->SectorSize
;
624 /* Set the attachment in the device extension */
625 SourceDeviceExtension
->AttachedTo
= AttachedDevice
;
629 /* Device was unloading or being removed */
630 AttachedDevice
= NULL
;
633 /* Return the attached device */
634 *AttachedToDeviceObject
= AttachedDevice
;
635 return STATUS_SUCCESS
;
641 * Allocates memory for and intializes a device object for use for
646 * Driver object passed by IO Manager when the driver was loaded.
648 * DeviceExtensionSize
649 * Number of bytes for the device extension.
652 * Unicode name of device.
655 * Device type of the new device.
657 * DeviceCharacteristics
658 * Bit mask of device characteristics.
661 * TRUE if only one thread can access the device at a time.
664 * On successful return this parameter is filled by pointer to
665 * allocated device object.
672 IoCreateDevice(IN PDRIVER_OBJECT DriverObject
,
673 IN ULONG DeviceExtensionSize
,
674 IN PUNICODE_STRING DeviceName
,
675 IN DEVICE_TYPE DeviceType
,
676 IN ULONG DeviceCharacteristics
,
677 IN BOOLEAN Exclusive
,
678 OUT PDEVICE_OBJECT
*DeviceObject
)
680 WCHAR AutoNameBuffer
[20];
681 UNICODE_STRING AutoName
;
682 PDEVICE_OBJECT CreatedDeviceObject
;
683 PDEVOBJ_EXTENSION DeviceObjectExtension
;
684 OBJECT_ATTRIBUTES ObjectAttributes
;
686 ULONG AlignedDeviceExtensionSize
;
691 /* Check if we have to generate a name */
692 if (DeviceCharacteristics
& FILE_AUTOGENERATED_DEVICE_NAME
)
695 swprintf(AutoNameBuffer
,
697 InterlockedIncrementUL(&IopDeviceObjectNumber
));
699 /* Initialize the name */
700 RtlInitUnicodeString(&AutoName
, AutoNameBuffer
);
701 DeviceName
= &AutoName
;
704 /* Initialize the Object Attributes */
705 InitializeObjectAttributes(&ObjectAttributes
, DeviceName
, 0, NULL
, NULL
);
707 /* Honor exclusive flag */
708 if (Exclusive
) ObjectAttributes
.Attributes
|= OBJ_EXCLUSIVE
;
710 /* Create a permanent object for named devices */
711 if (DeviceName
) ObjectAttributes
.Attributes
|= OBJ_PERMANENT
;
713 /* Align the Extension Size to 8-bytes */
714 AlignedDeviceExtensionSize
= (DeviceExtensionSize
+ 7) &~ 7;
717 TotalSize
= AlignedDeviceExtensionSize
+
718 sizeof(DEVICE_OBJECT
) +
719 sizeof(EXTENDED_DEVOBJ_EXTENSION
);
721 /* Create the Device Object */
722 *DeviceObject
= NULL
;
723 Status
= ObCreateObject(KernelMode
,
731 (PVOID
*)&CreatedDeviceObject
);
732 if (!NT_SUCCESS(Status
)) return Status
;
734 /* Clear the whole Object and extension so we don't null stuff manually */
735 RtlZeroMemory(CreatedDeviceObject
, TotalSize
);
738 * Setup the Type and Size. Note that we don't use the aligned size,
739 * because that's only padding for the DevObjExt and not part of the Object.
741 CreatedDeviceObject
->Type
= IO_TYPE_DEVICE
;
742 CreatedDeviceObject
->Size
= sizeof(DEVICE_OBJECT
) + DeviceExtensionSize
;
744 /* The kernel extension is after the driver internal extension */
745 DeviceObjectExtension
= (PDEVOBJ_EXTENSION
)
746 ((ULONG_PTR
)(CreatedDeviceObject
+ 1) +
747 AlignedDeviceExtensionSize
);
749 /* Set the Type and Size. Question: why is Size 0 on Windows? */
750 DeviceObjectExtension
->Type
= IO_TYPE_DEVICE_OBJECT_EXTENSION
;
751 DeviceObjectExtension
->Size
= 0;
753 /* Link the Object and Extension */
754 DeviceObjectExtension
->DeviceObject
= CreatedDeviceObject
;
755 CreatedDeviceObject
->DeviceObjectExtension
= DeviceObjectExtension
;
757 /* Set Device Object Data */
758 CreatedDeviceObject
->DeviceType
= DeviceType
;
759 CreatedDeviceObject
->Characteristics
= DeviceCharacteristics
;
760 CreatedDeviceObject
->DeviceExtension
= DeviceExtensionSize
?
761 CreatedDeviceObject
+ 1 :
763 CreatedDeviceObject
->StackSize
= 1;
764 CreatedDeviceObject
->AlignmentRequirement
= 0;
767 CreatedDeviceObject
->Flags
= DO_DEVICE_INITIALIZING
;
768 if (Exclusive
) CreatedDeviceObject
->Flags
|= DO_EXCLUSIVE
;
769 if (DeviceName
) CreatedDeviceObject
->Flags
|= DO_DEVICE_HAS_NAME
;
771 /* Attach a Vpb for Disks and Tapes, and create the Device Lock */
772 if (CreatedDeviceObject
->DeviceType
== FILE_DEVICE_DISK
||
773 CreatedDeviceObject
->DeviceType
== FILE_DEVICE_VIRTUAL_DISK
||
774 CreatedDeviceObject
->DeviceType
== FILE_DEVICE_CD_ROM
||
775 CreatedDeviceObject
->DeviceType
== FILE_DEVICE_TAPE
)
778 IopCreateVpb(CreatedDeviceObject
);
780 /* Initialize Lock Event */
781 KeInitializeEvent(&CreatedDeviceObject
->DeviceLock
,
782 SynchronizationEvent
,
786 /* Set the right Sector Size */
789 /* All disk systems */
790 case FILE_DEVICE_DISK_FILE_SYSTEM
:
791 case FILE_DEVICE_DISK
:
792 case FILE_DEVICE_VIRTUAL_DISK
:
794 /* The default is 512 bytes */
795 CreatedDeviceObject
->SectorSize
= 512;
798 /* CD-ROM file systems */
799 case FILE_DEVICE_CD_ROM_FILE_SYSTEM
:
801 /* The default is 2048 bytes */
802 CreatedDeviceObject
->SectorSize
= 2048;
805 /* Create the Device Queue */
806 if (CreatedDeviceObject
->DeviceType
== FILE_DEVICE_DISK_FILE_SYSTEM
||
807 CreatedDeviceObject
->DeviceType
== FILE_DEVICE_FILE_SYSTEM
||
808 CreatedDeviceObject
->DeviceType
== FILE_DEVICE_CD_ROM_FILE_SYSTEM
||
809 CreatedDeviceObject
->DeviceType
== FILE_DEVICE_NETWORK_FILE_SYSTEM
||
810 CreatedDeviceObject
->DeviceType
== FILE_DEVICE_TAPE_FILE_SYSTEM
)
812 /* Simple FS Devices, they don't need a real Device Queue */
813 InitializeListHead(&CreatedDeviceObject
->Queue
.ListEntry
);
817 /* An actual Device, initialize its DQ */
818 KeInitializeDeviceQueue(&CreatedDeviceObject
->DeviceQueue
);
821 /* Insert the Object */
822 Status
= ObInsertObject(CreatedDeviceObject
,
824 FILE_READ_DATA
| FILE_WRITE_DATA
,
826 (PVOID
*)&CreatedDeviceObject
,
828 if (!NT_SUCCESS(Status
))
830 /* Clear the device object and fail */
831 *DeviceObject
= NULL
;
835 /* Now do the final linking */
836 ObReferenceObject(DriverObject
);
837 CreatedDeviceObject
->DriverObject
= DriverObject
;
838 IopEditDeviceList(DriverObject
, CreatedDeviceObject
, IopAdd
);
840 /* Close the temporary handle and return to caller */
842 *DeviceObject
= CreatedDeviceObject
;
843 return STATUS_SUCCESS
;
854 IoDeleteDevice(IN PDEVICE_OBJECT DeviceObject
)
858 /* Check if the device is registered for shutdown notifications */
859 if (DeviceObject
->Flags
& DO_SHUTDOWN_REGISTERED
)
861 /* Call the shutdown notifications */
862 IoUnregisterShutdownNotification(DeviceObject
);
865 /* Check if it has a timer */
866 Timer
= DeviceObject
->Timer
;
869 /* Remove it and free it */
870 IopRemoveTimerFromTimerList(Timer
);
871 ExFreePoolWithTag(Timer
, TAG_IO_TIMER
);
874 /* Check if the device has a name */
875 if (DeviceObject
->Flags
& DO_DEVICE_HAS_NAME
)
877 /* It does, make it temporary so we can remove it */
878 ObMakeTemporaryObject(DeviceObject
);
881 /* Set the pending delete flag */
882 IoGetDevObjExtension(DeviceObject
)->ExtensionFlags
|= DOE_DELETE_PENDING
;
884 /* Check if the device object can be unloaded */
885 if (!DeviceObject
->ReferenceCount
) IopUnloadDevice(DeviceObject
);
896 IoDetachDevice(IN PDEVICE_OBJECT TargetDevice
)
898 PEXTENDED_DEVOBJ_EXTENSION DeviceExtension
;
901 DeviceExtension
= IoGetDevObjExtension(TargetDevice
->AttachedDevice
);
902 ASSERT(DeviceExtension
->AttachedTo
== TargetDevice
);
904 /* Remove the attachment */
905 DeviceExtension
->AttachedTo
= NULL
;
906 TargetDevice
->AttachedDevice
= NULL
;
908 /* Check if it's ok to delete this device */
909 if ((IoGetDevObjExtension(TargetDevice
)->ExtensionFlags
&
910 (DOE_UNLOAD_PENDING
| DOE_DELETE_PENDING
| DOE_REMOVE_PENDING
)) &&
911 !(TargetDevice
->ReferenceCount
))
914 IopUnloadDevice(TargetDevice
);
923 IoEnumerateDeviceObjectList(IN PDRIVER_OBJECT DriverObject
,
924 IN PDEVICE_OBJECT
*DeviceObjectList
,
925 IN ULONG DeviceObjectListSize
,
926 OUT PULONG ActualNumberDeviceObjects
)
928 ULONG ActualDevices
= 1;
929 PDEVICE_OBJECT CurrentDevice
= DriverObject
->DeviceObject
;
931 /* Find out how many devices we'll enumerate */
932 while ((CurrentDevice
= CurrentDevice
->NextDevice
)) ActualDevices
++;
934 /* Go back to the first */
935 CurrentDevice
= DriverObject
->DeviceObject
;
937 /* Start by at least returning this */
938 *ActualNumberDeviceObjects
= ActualDevices
;
940 /* Check if we can support so many */
941 if ((ActualDevices
* 4) > DeviceObjectListSize
)
943 /* Fail because the buffer was too small */
944 return STATUS_BUFFER_TOO_SMALL
;
947 /* Check if the caller only wanted the size */
948 if (DeviceObjectList
)
950 /* Loop through all the devices */
951 while (ActualDevices
)
953 /* Reference each Device */
954 ObReferenceObject(CurrentDevice
);
956 /* Add it to the list */
957 *DeviceObjectList
= CurrentDevice
;
959 /* Go to the next one */
960 CurrentDevice
= CurrentDevice
->NextDevice
;
966 /* Return the status */
967 return STATUS_SUCCESS
;
971 * IoGetAttachedDevice
978 IoGetAttachedDevice(PDEVICE_OBJECT DeviceObject
)
980 /* Get the last attached device */
981 while (DeviceObject
->AttachedDevice
)
983 /* Move to the next one */
984 DeviceObject
= DeviceObject
->AttachedDevice
;
992 * IoGetAttachedDeviceReference
999 IoGetAttachedDeviceReference(PDEVICE_OBJECT DeviceObject
)
1001 /* Reference the Attached Device */
1002 DeviceObject
= IoGetAttachedDevice(DeviceObject
);
1003 ObReferenceObject(DeviceObject
);
1004 return DeviceObject
;
1012 IoGetDeviceAttachmentBaseRef(IN PDEVICE_OBJECT DeviceObject
)
1014 /* Return the attached Device */
1015 return IoGetDevObjExtension(DeviceObject
)->AttachedTo
;
1019 * IoGetDeviceObjectPointer
1026 IoGetDeviceObjectPointer(IN PUNICODE_STRING ObjectName
,
1027 IN ACCESS_MASK DesiredAccess
,
1028 OUT PFILE_OBJECT
*FileObject
,
1029 OUT PDEVICE_OBJECT
*DeviceObject
)
1031 /* Call the helper routine for a normal operation */
1032 return IopGetDeviceObjectPointer(ObjectName
,
1044 IoGetDiskDeviceObject(IN PDEVICE_OBJECT FileSystemDeviceObject
,
1045 OUT PDEVICE_OBJECT
*DiskDeviceObject
)
1047 PEXTENDED_DEVOBJ_EXTENSION DeviceExtension
;
1052 /* Make sure there's a VPB */
1053 if (!FileSystemDeviceObject
->Vpb
) return STATUS_INVALID_PARAMETER
;
1056 IoAcquireVpbSpinLock(&OldIrql
);
1058 /* Get the Device Extension */
1059 DeviceExtension
= IoGetDevObjExtension(FileSystemDeviceObject
);
1061 /* Make sure this one has a VPB too */
1062 Vpb
= DeviceExtension
->Vpb
;
1065 /* Make sure that it's mounted */
1066 if ((Vpb
->ReferenceCount
) &&
1067 (Vpb
->Flags
& VPB_MOUNTED
))
1069 /* Return the Disk Device Object */
1070 *DiskDeviceObject
= Vpb
->RealDevice
;
1072 /* Reference it and return success */
1073 ObReferenceObject(Vpb
->RealDevice
);
1074 Status
= STATUS_SUCCESS
;
1078 /* It's not, so return failure */
1079 Status
= STATUS_VOLUME_DISMOUNTED
;
1085 Status
= STATUS_INVALID_PARAMETER
;
1088 /* Release the lock */
1089 IoReleaseVpbSpinLock(OldIrql
);
1098 IoGetLowerDeviceObject(IN PDEVICE_OBJECT DeviceObject
)
1100 PEXTENDED_DEVOBJ_EXTENSION DeviceExtension
;
1101 PDEVICE_OBJECT LowerDeviceObject
= NULL
;
1103 /* Make sure it's not getting deleted */
1104 DeviceExtension
= IoGetDevObjExtension(DeviceObject
);
1105 if (DeviceExtension
->ExtensionFlags
& (DOE_UNLOAD_PENDING
|
1106 DOE_DELETE_PENDING
|
1107 DOE_REMOVE_PENDING
|
1108 DOE_REMOVE_PROCESSED
))
1110 /* Get the Lower Device Object */
1111 LowerDeviceObject
= DeviceExtension
->AttachedTo
;
1114 ObReferenceObject(LowerDeviceObject
);
1118 return LowerDeviceObject
;
1122 * IoGetRelatedDeviceObject
1125 * See "Windows NT File System Internals", page 633 - 634.
1132 IoGetRelatedDeviceObject(IN PFILE_OBJECT FileObject
)
1134 PDEVICE_OBJECT DeviceObject
= FileObject
->DeviceObject
;
1136 /* Check if we have a VPB with a device object */
1137 if ((FileObject
->Vpb
) && (FileObject
->Vpb
->DeviceObject
))
1139 /* Then use the DO from the VPB */
1140 ASSERT(!(FileObject
->Flags
& FO_DIRECT_DEVICE_OPEN
));
1141 DeviceObject
= FileObject
->Vpb
->DeviceObject
;
1143 else if (!(FileObject
->Flags
& FO_DIRECT_DEVICE_OPEN
) &&
1144 (FileObject
->DeviceObject
->Vpb
) &&
1145 (FileObject
->DeviceObject
->Vpb
->DeviceObject
))
1147 /* The disk device actually has a VPB, so get the DO from there */
1148 DeviceObject
= FileObject
->DeviceObject
->Vpb
->DeviceObject
;
1152 /* Otherwise, this was a direct open */
1153 DeviceObject
= FileObject
->DeviceObject
;
1157 ASSERT(DeviceObject
!= NULL
);
1159 /* Check if we were attached */
1160 if (DeviceObject
->AttachedDevice
)
1162 /* Check if the file object has an extension present */
1163 if (FileObject
->Flags
& FO_FILE_OBJECT_HAS_EXTENSION
)
1165 /* Sanity check, direct open files can't have this */
1166 ASSERT(!(FileObject
->Flags
& FO_DIRECT_DEVICE_OPEN
));
1168 /* Check if the extension is really present */
1169 if (FileObject
->FileObjectExtension
)
1171 /* FIXME: Unhandled yet */
1172 DPRINT1("FOEs not supported\n");
1177 /* Return the highest attached device */
1178 DeviceObject
= IoGetAttachedDevice(DeviceObject
);
1181 /* Return the DO we found */
1182 return DeviceObject
;
1190 IoGetBaseFileSystemDeviceObject(IN PFILE_OBJECT FileObject
)
1192 PDEVICE_OBJECT DeviceObject
;
1195 * If the FILE_OBJECT's VPB is defined,
1196 * get the device from it.
1198 if ((FileObject
->Vpb
) && (FileObject
->Vpb
->DeviceObject
))
1200 /* Use the VPB's Device Object's */
1201 DeviceObject
= FileObject
->Vpb
->DeviceObject
;
1203 else if (!(FileObject
->Flags
& FO_DIRECT_DEVICE_OPEN
) &&
1204 (FileObject
->DeviceObject
->Vpb
) &&
1205 (FileObject
->DeviceObject
->Vpb
->DeviceObject
))
1207 /* Use the VPB's File System Object */
1208 DeviceObject
= FileObject
->DeviceObject
->Vpb
->DeviceObject
;
1212 /* Use the FO's Device Object */
1213 DeviceObject
= FileObject
->DeviceObject
;
1216 /* Return the device object we found */
1217 ASSERT(DeviceObject
!= NULL
);
1218 return DeviceObject
;
1226 IoRegisterLastChanceShutdownNotification(IN PDEVICE_OBJECT DeviceObject
)
1228 PSHUTDOWN_ENTRY Entry
;
1230 /* Allocate the shutdown entry */
1231 Entry
= ExAllocatePoolWithTag(NonPagedPool
,
1232 sizeof(SHUTDOWN_ENTRY
),
1233 TAG_SHUTDOWN_ENTRY
);
1234 if (!Entry
) return STATUS_INSUFFICIENT_RESOURCES
;
1237 Entry
->DeviceObject
= DeviceObject
;
1239 /* Insert it into the list */
1240 ExInterlockedInsertHeadList(&LastChanceShutdownListHead
,
1241 &Entry
->ShutdownList
,
1244 /* Set the shutdown registered flag */
1245 DeviceObject
->Flags
|= DO_SHUTDOWN_REGISTERED
;
1246 return STATUS_SUCCESS
;
1254 IoRegisterShutdownNotification(PDEVICE_OBJECT DeviceObject
)
1256 PSHUTDOWN_ENTRY Entry
;
1258 /* Allocate the shutdown entry */
1259 Entry
= ExAllocatePoolWithTag(NonPagedPool
,
1260 sizeof(SHUTDOWN_ENTRY
),
1261 TAG_SHUTDOWN_ENTRY
);
1262 if (!Entry
) return STATUS_INSUFFICIENT_RESOURCES
;
1265 Entry
->DeviceObject
= DeviceObject
;
1267 /* Insert it into the list */
1268 ExInterlockedInsertHeadList(&ShutdownListHead
,
1269 &Entry
->ShutdownList
,
1272 /* Set the shutdown registered flag */
1273 DeviceObject
->Flags
|= DO_SHUTDOWN_REGISTERED
;
1274 return STATUS_SUCCESS
;
1282 IoUnregisterShutdownNotification(PDEVICE_OBJECT DeviceObject
)
1284 PSHUTDOWN_ENTRY ShutdownEntry
;
1285 PLIST_ENTRY NextEntry
;
1288 /* Acquire the shutdown lock and loop the shutdown list */
1289 KeAcquireSpinLock(&ShutdownListLock
, &OldIrql
);
1290 NextEntry
= ShutdownListHead
.Flink
;
1291 while (NextEntry
!= &ShutdownListHead
)
1294 ShutdownEntry
= CONTAINING_RECORD(NextEntry
,
1298 /* Get if the DO matches */
1299 if (ShutdownEntry
->DeviceObject
== DeviceObject
)
1301 /* Remove it from the list */
1302 RemoveEntryList(NextEntry
);
1303 NextEntry
= NextEntry
->Blink
;
1305 /* Free the entry */
1306 ExFreePool(ShutdownEntry
);
1309 /* Go to the next entry */
1310 NextEntry
= NextEntry
->Flink
;
1313 /* Now loop the last chance list */
1314 NextEntry
= LastChanceShutdownListHead
.Flink
;
1315 while (NextEntry
!= &LastChanceShutdownListHead
)
1318 ShutdownEntry
= CONTAINING_RECORD(NextEntry
,
1322 /* Get if the DO matches */
1323 if (ShutdownEntry
->DeviceObject
== DeviceObject
)
1325 /* Remove it from the list */
1326 RemoveEntryList(NextEntry
);
1327 NextEntry
= NextEntry
->Blink
;
1329 /* Free the entry */
1330 ExFreePool(ShutdownEntry
);
1333 /* Go to the next entry */
1334 NextEntry
= NextEntry
->Flink
;
1337 /* Now remove the flag */
1338 DeviceObject
->Flags
&= ~DO_SHUTDOWN_REGISTERED
;
1346 IoSetStartIoAttributes(IN PDEVICE_OBJECT DeviceObject
,
1347 IN BOOLEAN DeferredStartIo
,
1348 IN BOOLEAN NonCancelable
)
1350 PEXTENDED_DEVOBJ_EXTENSION DeviceExtension
;
1352 /* Get the Device Extension */
1353 DeviceExtension
= IoGetDevObjExtension(DeviceObject
);
1355 /* Set the flags the caller requested */
1356 DeviceExtension
->StartIoFlags
|= (DeferredStartIo
) ? DOE_SIO_DEFERRED
: 0;
1357 DeviceExtension
->StartIoFlags
|= (NonCancelable
) ? DOE_SIO_NO_CANCEL
: 0;
1365 IoStartNextPacketByKey(IN PDEVICE_OBJECT DeviceObject
,
1366 IN BOOLEAN Cancelable
,
1369 PEXTENDED_DEVOBJ_EXTENSION DeviceExtension
;
1371 /* Get the Device Extension */
1372 DeviceExtension
= IoGetDevObjExtension(DeviceObject
);
1374 /* Check if deferred start was requested */
1375 if (DeviceExtension
->StartIoFlags
& DOE_SIO_DEFERRED
)
1377 /* Call our internal function to handle the defered case */
1378 IopStartNextPacketByKeyEx(DeviceObject
,
1381 (Cancelable
) ? DOE_SIO_CANCELABLE
: 0);
1385 /* Call the normal routine */
1386 IopStartNextPacketByKey(DeviceObject
, Cancelable
, Key
);
1395 IoStartNextPacket(IN PDEVICE_OBJECT DeviceObject
,
1396 IN BOOLEAN Cancelable
)
1398 PEXTENDED_DEVOBJ_EXTENSION DeviceExtension
;
1400 /* Get the Device Extension */
1401 DeviceExtension
= IoGetDevObjExtension(DeviceObject
);
1403 /* Check if deferred start was requested */
1404 if (DeviceExtension
->StartIoFlags
& DOE_SIO_DEFERRED
)
1406 /* Call our internal function to handle the defered case */
1407 IopStartNextPacketByKeyEx(DeviceObject
,
1410 (Cancelable
) ? DOE_SIO_CANCELABLE
: 0);
1414 /* Call the normal routine */
1415 IopStartNextPacket(DeviceObject
, Cancelable
);
1424 IoStartPacket(IN PDEVICE_OBJECT DeviceObject
,
1427 IN PDRIVER_CANCEL CancelFunction
)
1430 KIRQL OldIrql
, CancelIrql
;
1432 /* Raise to dispatch level */
1433 KeRaiseIrql(DISPATCH_LEVEL
, &OldIrql
);
1435 /* Check if we should acquire the cancel lock */
1438 /* Acquire and set it */
1439 IoAcquireCancelSpinLock(&CancelIrql
);
1440 Irp
->CancelRoutine
= CancelFunction
;
1443 /* Check if we have a key */
1447 Stat
= KeInsertByKeyDeviceQueue(&DeviceObject
->DeviceQueue
,
1448 &Irp
->Tail
.Overlay
.DeviceQueueEntry
,
1453 /* Insert without a key */
1454 Stat
= KeInsertDeviceQueue(&DeviceObject
->DeviceQueue
,
1455 &Irp
->Tail
.Overlay
.DeviceQueueEntry
);
1458 /* Check if this was a first insert */
1462 DeviceObject
->CurrentIrp
= Irp
;
1464 /* Check if this is a cancelable packet */
1467 /* Check if the caller requested no cancellation */
1468 if (IoGetDevObjExtension(DeviceObject
)->StartIoFlags
&
1471 /* He did, so remove the cancel routine */
1472 Irp
->CancelRoutine
= NULL
;
1475 /* Release the cancel lock */
1476 IoReleaseCancelSpinLock(OldIrql
);
1479 /* Call the Start I/O function */
1480 DeviceObject
->DriverObject
->DriverStartIo(DeviceObject
, Irp
);
1484 /* The packet was inserted... check if we have a cancel function */
1487 /* Check if the IRP got cancelled */
1491 * Set the cancel IRQL, clear the currnet cancel routine and
1494 Irp
->CancelIrql
= CancelIrql
;
1495 Irp
->CancelRoutine
= NULL
;
1496 CancelFunction(DeviceObject
, Irp
);
1500 /* Otherwise, release the lock */
1501 IoReleaseCancelSpinLock(CancelIrql
);
1506 /* Return back to previous IRQL */
1507 KeLowerIrql(OldIrql
);