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 *******************************************************************/
17 /* GLOBALS ********************************************************************/
19 ULONG IopDeviceObjectNumber
= 0;
21 LIST_ENTRY ShutdownListHead
, LastChanceShutdownListHead
;
22 KSPIN_LOCK ShutdownListLock
;
24 /* PRIVATE FUNCTIONS **********************************************************/
28 IopDeleteDevice(IN PVOID ObjectBody
)
30 PDEVICE_OBJECT DeviceObject
= ObjectBody
;
33 /* TODO: Delete Device Node */
35 /* Dereference the driver object, referenced in IoCreateDevice */
36 if (DeviceObject
->DriverObject
)
37 ObDereferenceObject(DeviceObject
->DriverObject
);
43 IopAttachDeviceToDeviceStackSafe(IN PDEVICE_OBJECT SourceDevice
,
44 IN PDEVICE_OBJECT TargetDevice
,
45 OUT PDEVICE_OBJECT
*AttachedToDeviceObject OPTIONAL
)
47 PDEVICE_OBJECT AttachedDevice
;
48 PEXTENDED_DEVOBJ_EXTENSION SourceDeviceExtension
;
50 /* Get the Attached Device and source extension */
51 AttachedDevice
= IoGetAttachedDevice(TargetDevice
);
52 SourceDeviceExtension
= IoGetDevObjExtension(SourceDevice
);
53 ASSERT(SourceDeviceExtension
->AttachedTo
== NULL
);
55 /* Make sure that it's in a correct state */
56 if ((AttachedDevice
->Flags
& DO_DEVICE_INITIALIZING
) ||
57 (IoGetDevObjExtension(AttachedDevice
)->ExtensionFlags
&
61 DOE_REMOVE_PROCESSED
)))
63 /* Device was unloading or being removed */
64 AttachedDevice
= NULL
;
68 /* Update atached device fields */
69 AttachedDevice
->AttachedDevice
= SourceDevice
;
70 AttachedDevice
->Spare1
++;
72 /* Update the source with the attached data */
73 SourceDevice
->StackSize
= AttachedDevice
->StackSize
+ 1;
74 SourceDevice
->AlignmentRequirement
= AttachedDevice
->
76 SourceDevice
->SectorSize
= AttachedDevice
->SectorSize
;
78 /* Check for pending start flag */
79 if (IoGetDevObjExtension(AttachedDevice
)->ExtensionFlags
&
83 IoGetDevObjExtension(SourceDevice
)->ExtensionFlags
|=
87 /* Set the attachment in the device extension */
88 SourceDeviceExtension
->AttachedTo
= AttachedDevice
;
91 /* Return the attached device */
92 if (AttachedToDeviceObject
) *AttachedToDeviceObject
= AttachedDevice
;
93 return AttachedDevice
;
98 IoShutdownRegisteredDevices(VOID
)
100 PLIST_ENTRY ListEntry
;
101 PDEVICE_OBJECT DeviceObject
;
102 PSHUTDOWN_ENTRY ShutdownEntry
;
103 IO_STATUS_BLOCK StatusBlock
;
108 /* Initialize an event to wait on */
109 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
111 /* Get the first entry and start looping */
112 ListEntry
= ExInterlockedRemoveHeadList(&ShutdownListHead
,
116 /* Get the shutdown entry */
117 ShutdownEntry
= CONTAINING_RECORD(ListEntry
,
121 /* Get the attached device */
122 DeviceObject
= IoGetAttachedDevice(ShutdownEntry
->DeviceObject
);
124 /* Build the shutdown IRP and call the driver */
125 Irp
= IoBuildSynchronousFsdRequest(IRP_MJ_SHUTDOWN
,
132 Status
= IoCallDriver(DeviceObject
, Irp
);
133 if (Status
== STATUS_PENDING
)
135 /* Wait on the driver */
136 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
139 /* Free the shutdown entry and reset the event */
140 ExFreePoolWithTag(ShutdownEntry
, TAG_SHUTDOWN_ENTRY
);
141 KeClearEvent(&Event
);
143 /* Go to the next entry */
144 ListEntry
= ExInterlockedRemoveHeadList(&ShutdownListHead
,
151 IopGetDeviceObjectPointer(IN PUNICODE_STRING ObjectName
,
152 IN ACCESS_MASK DesiredAccess
,
153 OUT PFILE_OBJECT
*FileObject
,
154 OUT PDEVICE_OBJECT
*DeviceObject
,
157 OBJECT_ATTRIBUTES ObjectAttributes
;
158 IO_STATUS_BLOCK StatusBlock
;
159 PFILE_OBJECT LocalFileObject
;
163 /* Open the Device */
164 InitializeObjectAttributes(&ObjectAttributes
,
169 Status
= ZwOpenFile(&FileHandle
,
174 FILE_NON_DIRECTORY_FILE
| AttachFlag
);
175 if (!NT_SUCCESS(Status
)) return Status
;
177 /* Get File Object */
178 Status
= ObReferenceObjectByHandle(FileHandle
,
182 (PVOID
*)&LocalFileObject
,
184 if (NT_SUCCESS(Status
))
186 /* Return the requested data */
187 *DeviceObject
= IoGetRelatedDeviceObject(LocalFileObject
);
188 *FileObject
= LocalFileObject
;
191 /* Close the handle */
199 IopGetLowestDevice(IN PDEVICE_OBJECT DeviceObject
)
201 PDEVICE_OBJECT LowestDevice
;
202 PEXTENDED_DEVOBJ_EXTENSION DeviceExtension
;
204 /* Get the current device and its extension */
205 LowestDevice
= DeviceObject
;
206 DeviceExtension
= IoGetDevObjExtension(LowestDevice
);
208 /* Keep looping as long as we're attached */
209 while (DeviceExtension
->AttachedTo
)
211 /* Get the lowest device and its extension */
212 LowestDevice
= DeviceExtension
->AttachedTo
;
213 DeviceExtension
= IoGetDevObjExtension(LowestDevice
);
216 /* Return the lowest device */
222 IopEditDeviceList(IN PDRIVER_OBJECT DriverObject
,
223 IN PDEVICE_OBJECT DeviceObject
,
224 IN IOP_DEVICE_LIST_OPERATION Type
)
226 PDEVICE_OBJECT Previous
;
228 /* Check the type of operation */
229 if (Type
== IopRemove
)
231 /* Get the current device and check if it's the current one */
232 Previous
= DeviceObject
->DriverObject
->DeviceObject
;
233 if (Previous
== DeviceObject
)
235 /* It is, simply unlink this one directly */
236 DeviceObject
->DriverObject
->DeviceObject
=
237 DeviceObject
->NextDevice
;
241 /* It's not, so loop until we find the device */
242 while (Previous
->NextDevice
!= DeviceObject
)
244 /* Not this one, keep moving */
245 Previous
= Previous
->NextDevice
;
248 /* We found it, now unlink us */
249 Previous
->NextDevice
= DeviceObject
->NextDevice
;
254 /* Link the device object and the driver object */
255 DeviceObject
->NextDevice
= DriverObject
->DeviceObject
;
256 DriverObject
->DeviceObject
= DeviceObject
;
262 IopUnloadDevice(IN PDEVICE_OBJECT DeviceObject
)
264 PDRIVER_OBJECT DriverObject
= DeviceObject
->DriverObject
;
265 PDEVICE_OBJECT AttachedDeviceObject
, LowestDeviceObject
;
266 PEXTENDED_DEVOBJ_EXTENSION ThisExtension
, DeviceExtension
;
267 PDEVICE_NODE DeviceNode
;
268 BOOLEAN SafeToUnload
= TRUE
;
270 /* Check if removal is pending */
271 ThisExtension
= IoGetDevObjExtension(DeviceObject
);
272 if (ThisExtension
->ExtensionFlags
& DOE_REMOVE_PENDING
)
274 /* Get the PDO, extension, and node */
275 LowestDeviceObject
= IopGetLowestDevice(DeviceObject
);
276 DeviceExtension
= IoGetDevObjExtension(LowestDeviceObject
);
277 DeviceNode
= DeviceExtension
->DeviceNode
;
279 /* The PDO needs a device node */
280 ASSERT(DeviceNode
!= NULL
);
282 /* Loop all attached objects */
283 AttachedDeviceObject
= LowestDeviceObject
;
284 while (AttachedDeviceObject
)
286 /* Make sure they're dereferenced */
287 if (AttachedDeviceObject
->ReferenceCount
) return;
288 AttachedDeviceObject
= AttachedDeviceObject
->AttachedDevice
;
291 /* Loop all attached objects */
292 AttachedDeviceObject
= LowestDeviceObject
;
293 while (AttachedDeviceObject
)
295 /* Get the device extension */
296 DeviceExtension
= IoGetDevObjExtension(AttachedDeviceObject
);
298 /* Remove the pending flag and set processed */
299 DeviceExtension
->ExtensionFlags
&= ~DOE_REMOVE_PENDING
;
300 DeviceExtension
->ExtensionFlags
|= DOE_REMOVE_PROCESSED
;
301 AttachedDeviceObject
= AttachedDeviceObject
->AttachedDevice
;
305 * FIXME: TODO HPOUSSIN
306 * We need to parse/lock the device node, and if we have any pending
307 * surprise removals, query all relationships and send IRP_MN_REMOVE_
308 * _DEVICE to the devices related...
313 /* Check if deletion is pending */
314 if (ThisExtension
->ExtensionFlags
& DOE_DELETE_PENDING
)
316 /* Make sure unload is pending */
317 if (!(ThisExtension
->ExtensionFlags
& DOE_UNLOAD_PENDING
) ||
318 (DriverObject
->Flags
& DRVO_UNLOAD_INVOKED
))
320 /* We can't unload anymore */
321 SafeToUnload
= FALSE
;
325 * Check if we have an attached device and fail if we're attached
326 * and still have a reference count.
328 AttachedDeviceObject
= DeviceObject
->AttachedDevice
;
329 if ((AttachedDeviceObject
) && (DeviceObject
->ReferenceCount
)) return;
331 /* Check if we have a Security Descriptor */
332 if (DeviceObject
->SecurityDescriptor
)
335 ExFreePoolWithTag(DeviceObject
->SecurityDescriptor
, TAG_SD
);
338 /* Remove the device from the list */
339 IopEditDeviceList(DeviceObject
->DriverObject
, DeviceObject
, IopRemove
);
341 /* Dereference the keep-alive */
342 ObDereferenceObject(DeviceObject
);
344 /* If we're not unloading, stop here */
345 if (!SafeToUnload
) return;
348 /* Loop all the device objects */
349 DeviceObject
= DriverObject
->DeviceObject
;
353 * Make sure we're not attached, having a reference count
354 * or already deleting
356 if ((DeviceObject
->ReferenceCount
) ||
357 (DeviceObject
->AttachedDevice
) ||
358 (IoGetDevObjExtension(DeviceObject
)->ExtensionFlags
&
359 (DOE_DELETE_PENDING
| DOE_REMOVE_PENDING
)))
361 /* We're not safe to unload, quit */
365 /* Check the next device */
366 DeviceObject
= DeviceObject
->NextDevice
;
369 /* Set the unload invoked flag */
370 DriverObject
->Flags
|= DRVO_UNLOAD_INVOKED
;
373 if (DriverObject
->DriverUnload
) DriverObject
->DriverUnload(DriverObject
);
375 /* Make object temporary so it can be deleted */
376 ObMakeTemporaryObject(DriverObject
);
378 /* Dereference once more, referenced at driver object creation */
379 ObDereferenceObject(DriverObject
);
385 IopDereferenceDeviceObject(IN PDEVICE_OBJECT DeviceObject
,
386 IN BOOLEAN ForceUnload
)
389 ASSERT(DeviceObject
->ReferenceCount
);
391 /* Dereference the device */
392 DeviceObject
->ReferenceCount
--;
395 * Check if we can unload it and it's safe to unload (or if we're forcing
396 * an unload, which is OK too).
398 if (!(DeviceObject
->ReferenceCount
) &&
399 ((ForceUnload
) || (IoGetDevObjExtension(DeviceObject
)->ExtensionFlags
&
400 (DOE_UNLOAD_PENDING
|
403 DOE_REMOVE_PROCESSED
))))
406 IopUnloadDevice(DeviceObject
);
412 IopStartNextPacketByKey(IN PDEVICE_OBJECT DeviceObject
,
413 IN BOOLEAN Cancelable
,
416 PKDEVICE_QUEUE_ENTRY Entry
;
420 /* Acquire the cancel lock if this is cancelable */
421 if (Cancelable
) IoAcquireCancelSpinLock(&OldIrql
);
423 /* Clear the current IRP */
424 DeviceObject
->CurrentIrp
= NULL
;
426 /* Remove an entry from the queue */
427 Entry
= KeRemoveByKeyDeviceQueue(&DeviceObject
->DeviceQueue
, Key
);
430 /* Get the IRP and set it */
431 Irp
= CONTAINING_RECORD(Entry
, IRP
, Tail
.Overlay
.DeviceQueueEntry
);
432 DeviceObject
->CurrentIrp
= Irp
;
434 /* Check if this is a cancelable packet */
437 /* Check if the caller requested no cancellation */
438 if (IoGetDevObjExtension(DeviceObject
)->StartIoFlags
&
441 /* He did, so remove the cancel routine */
442 Irp
->CancelRoutine
= NULL
;
445 /* Release the cancel lock */
446 IoReleaseCancelSpinLock(OldIrql
);
449 /* Call the Start I/O Routine */
450 DeviceObject
->DriverObject
->DriverStartIo(DeviceObject
, Irp
);
454 /* Otherwise, release the cancel lock if we had acquired it */
455 if (Cancelable
) IoReleaseCancelSpinLock(OldIrql
);
461 IopStartNextPacket(IN PDEVICE_OBJECT DeviceObject
,
462 IN BOOLEAN Cancelable
)
464 PKDEVICE_QUEUE_ENTRY Entry
;
468 /* Acquire the cancel lock if this is cancelable */
469 if (Cancelable
) IoAcquireCancelSpinLock(&OldIrql
);
471 /* Clear the current IRP */
472 DeviceObject
->CurrentIrp
= NULL
;
474 /* Remove an entry from the queue */
475 Entry
= KeRemoveDeviceQueue(&DeviceObject
->DeviceQueue
);
478 /* Get the IRP and set it */
479 Irp
= CONTAINING_RECORD(Entry
, IRP
, Tail
.Overlay
.DeviceQueueEntry
);
480 DeviceObject
->CurrentIrp
= Irp
;
482 /* Check if this is a cancelable packet */
485 /* Check if the caller requested no cancellation */
486 if (IoGetDevObjExtension(DeviceObject
)->StartIoFlags
&
489 /* He did, so remove the cancel routine */
490 Irp
->CancelRoutine
= NULL
;
493 /* Release the cancel lock */
494 IoReleaseCancelSpinLock(OldIrql
);
497 /* Call the Start I/O Routine */
498 DeviceObject
->DriverObject
->DriverStartIo(DeviceObject
, Irp
);
502 /* Otherwise, release the cancel lock if we had acquired it */
503 if (Cancelable
) IoReleaseCancelSpinLock(OldIrql
);
509 IopStartNextPacketByKeyEx(IN PDEVICE_OBJECT DeviceObject
,
513 PEXTENDED_DEVOBJ_EXTENSION DeviceExtension
;
514 ULONG CurrentKey
= Key
;
515 ULONG CurrentFlags
= Flags
;
517 /* Get the device extension and start the packet loop */
518 DeviceExtension
= IoGetDevObjExtension(DeviceObject
);
521 /* Increase the count */
522 if (InterlockedIncrement(&DeviceExtension
->StartIoCount
) > 1)
525 * We've already called the routine once...
526 * All we have to do is save the key and add the new flags
528 DeviceExtension
->StartIoFlags
|= CurrentFlags
;
529 DeviceExtension
->StartIoKey
= CurrentKey
;
533 /* Mask out the current packet flags and key */
534 DeviceExtension
->StartIoFlags
&= ~(DOE_SIO_WITH_KEY
|
537 DeviceExtension
->StartIoKey
= 0;
539 /* Check if this is a packet start with key */
540 if (Flags
& DOE_SIO_WITH_KEY
)
542 /* Start the packet with a key */
543 IopStartNextPacketByKey(DeviceObject
,
544 (Flags
& DOE_SIO_CANCELABLE
) ?
548 else if (Flags
& DOE_SIO_NO_KEY
)
550 /* Start the packet */
551 IopStartNextPacket(DeviceObject
,
552 (Flags
& DOE_SIO_CANCELABLE
) ?
557 /* Decrease the Start I/O count and check if it's 0 now */
558 if (!InterlockedDecrement(&DeviceExtension
->StartIoCount
))
560 /* Get the current active key and flags */
561 CurrentKey
= DeviceExtension
->StartIoKey
;
562 CurrentFlags
= DeviceExtension
->StartIoFlags
& (DOE_SIO_WITH_KEY
|
566 /* Check if we should still loop */
567 if (!(CurrentFlags
& (DOE_SIO_WITH_KEY
| DOE_SIO_NO_KEY
))) break;
571 /* There are still Start I/Os active, so quit this loop */
579 IopGetRelatedTargetDevice(IN PFILE_OBJECT FileObject
,
580 OUT PDEVICE_NODE
*DeviceNode
)
583 IO_STACK_LOCATION Stack
= {0};
584 IO_STATUS_BLOCK IoStatusBlock
;
585 PDEVICE_RELATIONS DeviceRelations
;
586 PDEVICE_OBJECT DeviceObject
= NULL
;
590 /* Get DeviceObject related to given FileObject */
591 DeviceObject
= IoGetRelatedDeviceObject(FileObject
);
592 if (!DeviceObject
) return STATUS_NO_SUCH_DEVICE
;
594 /* Define input parameters */
595 Stack
.Parameters
.QueryDeviceRelations
.Type
= TargetDeviceRelation
;
596 Stack
.FileObject
= FileObject
;
598 /* Call the driver to query all relations (IRP_MJ_PNP) */
599 Status
= IopInitiatePnpIrp(DeviceObject
,
601 IRP_MN_QUERY_DEVICE_RELATIONS
,
603 if (!NT_SUCCESS(Status
)) return Status
;
605 /* Get returned pointer to DEVICE_RELATIONS */
606 DeviceRelations
= (PDEVICE_RELATIONS
)IoStatusBlock
.Information
;
608 /* Make sure it's not NULL and contains only one object */
609 ASSERT(DeviceRelations
);
610 ASSERT(DeviceRelations
->Count
== 1);
612 /* Finally get the device node */
613 *DeviceNode
= IopGetDeviceNode(DeviceRelations
->Objects
[0]);
614 if (!*DeviceNode
) Status
= STATUS_NO_SUCH_DEVICE
;
616 /* Free the DEVICE_RELATIONS structure, it's not needed anymore */
617 ExFreePool(DeviceRelations
);
622 /* PUBLIC FUNCTIONS ***********************************************************/
627 * Layers a device over the highest device in a device stack.
631 * Device to be attached.
634 * Name of the target device.
637 * Caller storage for the device attached to.
644 IoAttachDevice(PDEVICE_OBJECT SourceDevice
,
645 PUNICODE_STRING TargetDeviceName
,
646 PDEVICE_OBJECT
*AttachedDevice
)
649 PFILE_OBJECT FileObject
= NULL
;
650 PDEVICE_OBJECT TargetDevice
= NULL
;
652 /* Call the helper routine for an attach operation */
653 Status
= IopGetDeviceObjectPointer(TargetDeviceName
,
654 FILE_READ_ATTRIBUTES
,
657 IO_ATTACH_DEVICE_API
);
658 if (!NT_SUCCESS(Status
)) return Status
;
660 /* Attach the device */
661 Status
= IoAttachDeviceToDeviceStackSafe(SourceDevice
,
666 ObDereferenceObject(FileObject
);
671 * IoAttachDeviceByPointer
678 IoAttachDeviceByPointer(IN PDEVICE_OBJECT SourceDevice
,
679 IN PDEVICE_OBJECT TargetDevice
)
681 PDEVICE_OBJECT AttachedDevice
;
682 NTSTATUS Status
= STATUS_SUCCESS
;
685 AttachedDevice
= IoAttachDeviceToDeviceStack(SourceDevice
, TargetDevice
);
686 if (!AttachedDevice
) Status
= STATUS_NO_SUCH_DEVICE
;
688 /* Return the status */
697 IoAttachDeviceToDeviceStack(IN PDEVICE_OBJECT SourceDevice
,
698 IN PDEVICE_OBJECT TargetDevice
)
700 /* Attach it safely */
701 return IopAttachDeviceToDeviceStackSafe(SourceDevice
,
711 IoAttachDeviceToDeviceStackSafe(IN PDEVICE_OBJECT SourceDevice
,
712 IN PDEVICE_OBJECT TargetDevice
,
713 IN OUT PDEVICE_OBJECT
*AttachedToDeviceObject
)
715 /* Call the internal function */
716 if (!IopAttachDeviceToDeviceStackSafe(SourceDevice
,
718 AttachedToDeviceObject
))
721 return STATUS_NO_SUCH_DEVICE
;
725 return STATUS_SUCCESS
;
731 * Allocates memory for and intializes a device object for use for
736 * Driver object passed by IO Manager when the driver was loaded.
738 * DeviceExtensionSize
739 * Number of bytes for the device extension.
742 * Unicode name of device.
745 * Device type of the new device.
747 * DeviceCharacteristics
748 * Bit mask of device characteristics.
751 * TRUE if only one thread can access the device at a time.
754 * On successful return this parameter is filled by pointer to
755 * allocated device object.
762 IoCreateDevice(IN PDRIVER_OBJECT DriverObject
,
763 IN ULONG DeviceExtensionSize
,
764 IN PUNICODE_STRING DeviceName
,
765 IN DEVICE_TYPE DeviceType
,
766 IN ULONG DeviceCharacteristics
,
767 IN BOOLEAN Exclusive
,
768 OUT PDEVICE_OBJECT
*DeviceObject
)
770 WCHAR AutoNameBuffer
[20];
771 UNICODE_STRING AutoName
;
772 PDEVICE_OBJECT CreatedDeviceObject
;
773 PDEVOBJ_EXTENSION DeviceObjectExtension
;
774 OBJECT_ATTRIBUTES ObjectAttributes
;
776 ULONG AlignedDeviceExtensionSize
;
781 /* Check if we have to generate a name */
782 if (DeviceCharacteristics
& FILE_AUTOGENERATED_DEVICE_NAME
)
785 swprintf(AutoNameBuffer
,
787 InterlockedIncrementUL(&IopDeviceObjectNumber
));
789 /* Initialize the name */
790 RtlInitUnicodeString(&AutoName
, AutoNameBuffer
);
791 DeviceName
= &AutoName
;
794 /* Initialize the Object Attributes */
795 InitializeObjectAttributes(&ObjectAttributes
,
801 /* Honor exclusive flag */
802 if (Exclusive
) ObjectAttributes
.Attributes
|= OBJ_EXCLUSIVE
;
804 /* Create a permanent object for named devices */
805 if (DeviceName
) ObjectAttributes
.Attributes
|= OBJ_PERMANENT
;
807 /* Align the Extension Size to 8-bytes */
808 AlignedDeviceExtensionSize
= (DeviceExtensionSize
+ 7) &~ 7;
811 TotalSize
= AlignedDeviceExtensionSize
+
812 sizeof(DEVICE_OBJECT
) +
813 sizeof(EXTENDED_DEVOBJ_EXTENSION
);
815 /* Create the Device Object */
816 *DeviceObject
= NULL
;
817 Status
= ObCreateObject(KernelMode
,
825 (PVOID
*)&CreatedDeviceObject
);
826 if (!NT_SUCCESS(Status
)) return Status
;
828 /* Clear the whole Object and extension so we don't null stuff manually */
829 RtlZeroMemory(CreatedDeviceObject
, TotalSize
);
832 * Setup the Type and Size. Note that we don't use the aligned size,
833 * because that's only padding for the DevObjExt and not part of the Object.
835 CreatedDeviceObject
->Type
= IO_TYPE_DEVICE
;
836 CreatedDeviceObject
->Size
= sizeof(DEVICE_OBJECT
) + (USHORT
)DeviceExtensionSize
;
838 /* The kernel extension is after the driver internal extension */
839 DeviceObjectExtension
= (PDEVOBJ_EXTENSION
)
840 ((ULONG_PTR
)(CreatedDeviceObject
+ 1) +
841 AlignedDeviceExtensionSize
);
843 /* Set the Type and Size. Question: why is Size 0 on Windows? */
844 DeviceObjectExtension
->Type
= IO_TYPE_DEVICE_OBJECT_EXTENSION
;
845 DeviceObjectExtension
->Size
= 0;
847 /* Link the Object and Extension */
848 DeviceObjectExtension
->DeviceObject
= CreatedDeviceObject
;
849 CreatedDeviceObject
->DeviceObjectExtension
= DeviceObjectExtension
;
851 /* Set Device Object Data */
852 CreatedDeviceObject
->DeviceType
= DeviceType
;
853 CreatedDeviceObject
->Characteristics
= DeviceCharacteristics
;
854 CreatedDeviceObject
->DeviceExtension
= DeviceExtensionSize
?
855 CreatedDeviceObject
+ 1 :
857 CreatedDeviceObject
->StackSize
= 1;
858 CreatedDeviceObject
->AlignmentRequirement
= 0;
861 CreatedDeviceObject
->Flags
= DO_DEVICE_INITIALIZING
;
862 if (Exclusive
) CreatedDeviceObject
->Flags
|= DO_EXCLUSIVE
;
863 if (DeviceName
) CreatedDeviceObject
->Flags
|= DO_DEVICE_HAS_NAME
;
865 /* Attach a Vpb for Disks and Tapes, and create the Device Lock */
866 if ((CreatedDeviceObject
->DeviceType
== FILE_DEVICE_DISK
) ||
867 (CreatedDeviceObject
->DeviceType
== FILE_DEVICE_VIRTUAL_DISK
) ||
868 (CreatedDeviceObject
->DeviceType
== FILE_DEVICE_CD_ROM
) ||
869 (CreatedDeviceObject
->DeviceType
== FILE_DEVICE_TAPE
))
872 Status
= IopCreateVpb(CreatedDeviceObject
);
873 if (!NT_SUCCESS(Status
))
875 /* Reference the device object and fail */
876 ObDereferenceObject(DeviceObject
);
880 /* Initialize Lock Event */
881 KeInitializeEvent(&CreatedDeviceObject
->DeviceLock
,
882 SynchronizationEvent
,
886 /* Set the right Sector Size */
889 /* All disk systems */
890 case FILE_DEVICE_DISK_FILE_SYSTEM
:
891 case FILE_DEVICE_DISK
:
892 case FILE_DEVICE_VIRTUAL_DISK
:
894 /* The default is 512 bytes */
895 CreatedDeviceObject
->SectorSize
= 512;
898 /* CD-ROM file systems */
899 case FILE_DEVICE_CD_ROM_FILE_SYSTEM
:
901 /* The default is 2048 bytes */
902 CreatedDeviceObject
->SectorSize
= 2048;
905 /* Create the Device Queue */
906 if ((CreatedDeviceObject
->DeviceType
== FILE_DEVICE_DISK_FILE_SYSTEM
) ||
907 (CreatedDeviceObject
->DeviceType
== FILE_DEVICE_FILE_SYSTEM
) ||
908 (CreatedDeviceObject
->DeviceType
== FILE_DEVICE_CD_ROM_FILE_SYSTEM
) ||
909 (CreatedDeviceObject
->DeviceType
== FILE_DEVICE_NETWORK_FILE_SYSTEM
) ||
910 (CreatedDeviceObject
->DeviceType
== FILE_DEVICE_TAPE_FILE_SYSTEM
))
912 /* Simple FS Devices, they don't need a real Device Queue */
913 InitializeListHead(&CreatedDeviceObject
->Queue
.ListEntry
);
917 /* An actual Device, initialize its DQ */
918 KeInitializeDeviceQueue(&CreatedDeviceObject
->DeviceQueue
);
921 /* Insert the Object */
922 Status
= ObInsertObject(CreatedDeviceObject
,
924 FILE_READ_DATA
| FILE_WRITE_DATA
,
926 (PVOID
*)&CreatedDeviceObject
,
928 if (!NT_SUCCESS(Status
)) return Status
;
930 /* Now do the final linking */
931 ObReferenceObject(DriverObject
);
932 ASSERT((DriverObject
->Flags
& DRVO_UNLOAD_INVOKED
) == 0);
933 CreatedDeviceObject
->DriverObject
= DriverObject
;
934 IopEditDeviceList(DriverObject
, CreatedDeviceObject
, IopAdd
);
936 /* Close the temporary handle and return to caller */
937 ObCloseHandle(TempHandle
, KernelMode
);
938 *DeviceObject
= CreatedDeviceObject
;
939 return STATUS_SUCCESS
;
950 IoDeleteDevice(IN PDEVICE_OBJECT DeviceObject
)
954 /* Check if the device is registered for shutdown notifications */
955 if (DeviceObject
->Flags
& DO_SHUTDOWN_REGISTERED
)
957 /* Call the shutdown notifications */
958 IoUnregisterShutdownNotification(DeviceObject
);
961 /* Check if it has a timer */
962 Timer
= DeviceObject
->Timer
;
965 /* Remove it and free it */
966 IopRemoveTimerFromTimerList(Timer
);
967 ExFreePoolWithTag(Timer
, TAG_IO_TIMER
);
970 /* Check if the device has a name */
971 if (DeviceObject
->Flags
& DO_DEVICE_HAS_NAME
)
973 /* It does, make it temporary so we can remove it */
974 ObMakeTemporaryObject(DeviceObject
);
977 /* Set the pending delete flag */
978 IoGetDevObjExtension(DeviceObject
)->ExtensionFlags
|= DOE_DELETE_PENDING
;
980 /* Check if the device object can be unloaded */
981 if (!DeviceObject
->ReferenceCount
) IopUnloadDevice(DeviceObject
);
992 IoDetachDevice(IN PDEVICE_OBJECT TargetDevice
)
994 PEXTENDED_DEVOBJ_EXTENSION DeviceExtension
;
997 DeviceExtension
= IoGetDevObjExtension(TargetDevice
->AttachedDevice
);
998 ASSERT(DeviceExtension
->AttachedTo
== TargetDevice
);
1000 /* Remove the attachment */
1001 DeviceExtension
->AttachedTo
= NULL
;
1002 TargetDevice
->AttachedDevice
= NULL
;
1004 /* Check if it's ok to delete this device */
1005 if ((IoGetDevObjExtension(TargetDevice
)->ExtensionFlags
&
1006 (DOE_UNLOAD_PENDING
| DOE_DELETE_PENDING
| DOE_REMOVE_PENDING
)) &&
1007 !(TargetDevice
->ReferenceCount
))
1010 IopUnloadDevice(TargetDevice
);
1019 IoEnumerateDeviceObjectList(IN PDRIVER_OBJECT DriverObject
,
1020 IN PDEVICE_OBJECT
*DeviceObjectList
,
1021 IN ULONG DeviceObjectListSize
,
1022 OUT PULONG ActualNumberDeviceObjects
)
1024 ULONG ActualDevices
= 1;
1025 PDEVICE_OBJECT CurrentDevice
= DriverObject
->DeviceObject
;
1027 /* Find out how many devices we'll enumerate */
1028 while ((CurrentDevice
= CurrentDevice
->NextDevice
)) ActualDevices
++;
1030 /* Go back to the first */
1031 CurrentDevice
= DriverObject
->DeviceObject
;
1033 /* Start by at least returning this */
1034 *ActualNumberDeviceObjects
= ActualDevices
;
1036 /* Check if we can support so many */
1037 if ((ActualDevices
* 4) > DeviceObjectListSize
)
1039 /* Fail because the buffer was too small */
1040 return STATUS_BUFFER_TOO_SMALL
;
1043 /* Check if the caller only wanted the size */
1044 if (DeviceObjectList
)
1046 /* Loop through all the devices */
1047 while (ActualDevices
)
1049 /* Reference each Device */
1050 ObReferenceObject(CurrentDevice
);
1052 /* Add it to the list */
1053 *DeviceObjectList
= CurrentDevice
;
1055 /* Go to the next one */
1056 CurrentDevice
= CurrentDevice
->NextDevice
;
1062 /* Return the status */
1063 return STATUS_SUCCESS
;
1067 * IoGetAttachedDevice
1074 IoGetAttachedDevice(PDEVICE_OBJECT DeviceObject
)
1076 /* Get the last attached device */
1077 while (DeviceObject
->AttachedDevice
)
1079 /* Move to the next one */
1080 DeviceObject
= DeviceObject
->AttachedDevice
;
1084 return DeviceObject
;
1088 * IoGetAttachedDeviceReference
1095 IoGetAttachedDeviceReference(PDEVICE_OBJECT DeviceObject
)
1097 /* Reference the Attached Device */
1098 DeviceObject
= IoGetAttachedDevice(DeviceObject
);
1099 ObReferenceObject(DeviceObject
);
1100 return DeviceObject
;
1108 IoGetDeviceAttachmentBaseRef(IN PDEVICE_OBJECT DeviceObject
)
1110 /* Reference the lowest attached device */
1111 DeviceObject
= IopGetLowestDevice(DeviceObject
);
1112 ObReferenceObject(DeviceObject
);
1113 return DeviceObject
;
1117 * IoGetDeviceObjectPointer
1124 IoGetDeviceObjectPointer(IN PUNICODE_STRING ObjectName
,
1125 IN ACCESS_MASK DesiredAccess
,
1126 OUT PFILE_OBJECT
*FileObject
,
1127 OUT PDEVICE_OBJECT
*DeviceObject
)
1129 /* Call the helper routine for a normal operation */
1130 return IopGetDeviceObjectPointer(ObjectName
,
1142 IoGetDiskDeviceObject(IN PDEVICE_OBJECT FileSystemDeviceObject
,
1143 OUT PDEVICE_OBJECT
*DiskDeviceObject
)
1145 PEXTENDED_DEVOBJ_EXTENSION DeviceExtension
;
1150 /* Make sure there's a VPB */
1151 if (!FileSystemDeviceObject
->Vpb
) return STATUS_INVALID_PARAMETER
;
1154 IoAcquireVpbSpinLock(&OldIrql
);
1156 /* Get the Device Extension */
1157 DeviceExtension
= IoGetDevObjExtension(FileSystemDeviceObject
);
1159 /* Make sure this one has a VPB too */
1160 Vpb
= DeviceExtension
->Vpb
;
1163 /* Make sure that it's mounted */
1164 if ((Vpb
->ReferenceCount
) &&
1165 (Vpb
->Flags
& VPB_MOUNTED
))
1167 /* Return the Disk Device Object */
1168 *DiskDeviceObject
= Vpb
->RealDevice
;
1170 /* Reference it and return success */
1171 ObReferenceObject(Vpb
->RealDevice
);
1172 Status
= STATUS_SUCCESS
;
1176 /* It's not, so return failure */
1177 Status
= STATUS_VOLUME_DISMOUNTED
;
1183 Status
= STATUS_INVALID_PARAMETER
;
1186 /* Release the lock */
1187 IoReleaseVpbSpinLock(OldIrql
);
1196 IoGetLowerDeviceObject(IN PDEVICE_OBJECT DeviceObject
)
1198 PEXTENDED_DEVOBJ_EXTENSION DeviceExtension
;
1199 PDEVICE_OBJECT LowerDeviceObject
= NULL
;
1201 /* Make sure it's not getting deleted */
1202 DeviceExtension
= IoGetDevObjExtension(DeviceObject
);
1203 if (!(DeviceExtension
->ExtensionFlags
& (DOE_UNLOAD_PENDING
|
1204 DOE_DELETE_PENDING
|
1205 DOE_REMOVE_PENDING
|
1206 DOE_REMOVE_PROCESSED
)))
1208 /* Get the Lower Device Object */
1209 LowerDeviceObject
= DeviceExtension
->AttachedTo
;
1211 /* Check that we got a valid device object */
1212 if (LowerDeviceObject
)
1214 /* We did so let's reference it */
1215 ObReferenceObject(LowerDeviceObject
);
1220 return LowerDeviceObject
;
1228 IoGetRelatedDeviceObject(IN PFILE_OBJECT FileObject
)
1230 PDEVICE_OBJECT DeviceObject
= FileObject
->DeviceObject
;
1232 /* Check if we have a VPB with a device object */
1233 if ((FileObject
->Vpb
) && (FileObject
->Vpb
->DeviceObject
))
1235 /* Then use the DO from the VPB */
1236 ASSERT(!(FileObject
->Flags
& FO_DIRECT_DEVICE_OPEN
));
1237 DeviceObject
= FileObject
->Vpb
->DeviceObject
;
1239 else if (!(FileObject
->Flags
& FO_DIRECT_DEVICE_OPEN
) &&
1240 (FileObject
->DeviceObject
->Vpb
) &&
1241 (FileObject
->DeviceObject
->Vpb
->DeviceObject
))
1243 /* The disk device actually has a VPB, so get the DO from there */
1244 DeviceObject
= FileObject
->DeviceObject
->Vpb
->DeviceObject
;
1248 /* Otherwise, this was a direct open */
1249 DeviceObject
= FileObject
->DeviceObject
;
1253 ASSERT(DeviceObject
!= NULL
);
1255 /* Check if we were attached */
1256 if (DeviceObject
->AttachedDevice
)
1258 /* Check if the file object has an extension present */
1259 if (FileObject
->Flags
& FO_FILE_OBJECT_HAS_EXTENSION
)
1261 /* Sanity check, direct open files can't have this */
1262 ASSERT(!(FileObject
->Flags
& FO_DIRECT_DEVICE_OPEN
));
1264 /* Check if the extension is really present */
1265 if (FileObject
->FileObjectExtension
)
1267 /* FIXME: Unhandled yet */
1268 DPRINT1("FOEs not supported\n");
1273 /* Return the highest attached device */
1274 DeviceObject
= IoGetAttachedDevice(DeviceObject
);
1277 /* Return the DO we found */
1278 return DeviceObject
;
1286 IoGetRelatedTargetDevice(IN PFILE_OBJECT FileObject
,
1287 OUT PDEVICE_OBJECT
*DeviceObject
)
1290 PDEVICE_NODE DeviceNode
= NULL
;
1292 /* Call the internal helper function */
1293 Status
= IopGetRelatedTargetDevice(FileObject
, &DeviceNode
);
1294 if (NT_SUCCESS(Status
) && DeviceNode
)
1296 *DeviceObject
= DeviceNode
->PhysicalDeviceObject
;
1306 IoGetBaseFileSystemDeviceObject(IN PFILE_OBJECT FileObject
)
1308 PDEVICE_OBJECT DeviceObject
;
1311 * If the FILE_OBJECT's VPB is defined,
1312 * get the device from it.
1314 if ((FileObject
->Vpb
) && (FileObject
->Vpb
->DeviceObject
))
1316 /* Use the VPB's Device Object's */
1317 DeviceObject
= FileObject
->Vpb
->DeviceObject
;
1319 else if (!(FileObject
->Flags
& FO_DIRECT_DEVICE_OPEN
) &&
1320 (FileObject
->DeviceObject
->Vpb
) &&
1321 (FileObject
->DeviceObject
->Vpb
->DeviceObject
))
1323 /* Use the VPB's File System Object */
1324 DeviceObject
= FileObject
->DeviceObject
->Vpb
->DeviceObject
;
1328 /* Use the FO's Device Object */
1329 DeviceObject
= FileObject
->DeviceObject
;
1332 /* Return the device object we found */
1333 ASSERT(DeviceObject
!= NULL
);
1334 return DeviceObject
;
1342 IoRegisterLastChanceShutdownNotification(IN PDEVICE_OBJECT DeviceObject
)
1344 PSHUTDOWN_ENTRY Entry
;
1346 /* Allocate the shutdown entry */
1347 Entry
= ExAllocatePoolWithTag(NonPagedPool
,
1348 sizeof(SHUTDOWN_ENTRY
),
1349 TAG_SHUTDOWN_ENTRY
);
1350 if (!Entry
) return STATUS_INSUFFICIENT_RESOURCES
;
1353 Entry
->DeviceObject
= DeviceObject
;
1355 /* Insert it into the list */
1356 ExInterlockedInsertHeadList(&LastChanceShutdownListHead
,
1357 &Entry
->ShutdownList
,
1360 /* Set the shutdown registered flag */
1361 DeviceObject
->Flags
|= DO_SHUTDOWN_REGISTERED
;
1362 return STATUS_SUCCESS
;
1370 IoRegisterShutdownNotification(PDEVICE_OBJECT DeviceObject
)
1372 PSHUTDOWN_ENTRY Entry
;
1374 /* Allocate the shutdown entry */
1375 Entry
= ExAllocatePoolWithTag(NonPagedPool
,
1376 sizeof(SHUTDOWN_ENTRY
),
1377 TAG_SHUTDOWN_ENTRY
);
1378 if (!Entry
) return STATUS_INSUFFICIENT_RESOURCES
;
1381 Entry
->DeviceObject
= DeviceObject
;
1383 /* Insert it into the list */
1384 ExInterlockedInsertHeadList(&ShutdownListHead
,
1385 &Entry
->ShutdownList
,
1388 /* Set the shutdown registered flag */
1389 DeviceObject
->Flags
|= DO_SHUTDOWN_REGISTERED
;
1390 return STATUS_SUCCESS
;
1398 IoUnregisterShutdownNotification(PDEVICE_OBJECT DeviceObject
)
1400 PSHUTDOWN_ENTRY ShutdownEntry
;
1401 PLIST_ENTRY NextEntry
;
1404 /* Acquire the shutdown lock and loop the shutdown list */
1405 KeAcquireSpinLock(&ShutdownListLock
, &OldIrql
);
1406 NextEntry
= ShutdownListHead
.Flink
;
1407 while (NextEntry
!= &ShutdownListHead
)
1410 ShutdownEntry
= CONTAINING_RECORD(NextEntry
,
1414 /* Get if the DO matches */
1415 if (ShutdownEntry
->DeviceObject
== DeviceObject
)
1417 /* Remove it from the list */
1418 RemoveEntryList(NextEntry
);
1419 NextEntry
= NextEntry
->Blink
;
1421 /* Free the entry */
1422 ExFreePoolWithTag(ShutdownEntry
, TAG_SHUTDOWN_ENTRY
);
1425 /* Go to the next entry */
1426 NextEntry
= NextEntry
->Flink
;
1429 /* Now loop the last chance list */
1430 NextEntry
= LastChanceShutdownListHead
.Flink
;
1431 while (NextEntry
!= &LastChanceShutdownListHead
)
1434 ShutdownEntry
= CONTAINING_RECORD(NextEntry
,
1438 /* Get if the DO matches */
1439 if (ShutdownEntry
->DeviceObject
== DeviceObject
)
1441 /* Remove it from the list */
1442 RemoveEntryList(NextEntry
);
1443 NextEntry
= NextEntry
->Blink
;
1445 /* Free the entry */
1446 ExFreePoolWithTag(ShutdownEntry
, TAG_SHUTDOWN_ENTRY
);
1449 /* Go to the next entry */
1450 NextEntry
= NextEntry
->Flink
;
1453 /* Release the shutdown lock */
1454 KeReleaseSpinLock(&ShutdownListLock
, OldIrql
);
1456 /* Now remove the flag */
1457 DeviceObject
->Flags
&= ~DO_SHUTDOWN_REGISTERED
;
1465 IoSetStartIoAttributes(IN PDEVICE_OBJECT DeviceObject
,
1466 IN BOOLEAN DeferredStartIo
,
1467 IN BOOLEAN NonCancelable
)
1469 PEXTENDED_DEVOBJ_EXTENSION DeviceExtension
;
1471 /* Get the Device Extension */
1472 DeviceExtension
= IoGetDevObjExtension(DeviceObject
);
1474 /* Set the flags the caller requested */
1475 DeviceExtension
->StartIoFlags
|= (DeferredStartIo
) ? DOE_SIO_DEFERRED
: 0;
1476 DeviceExtension
->StartIoFlags
|= (NonCancelable
) ? DOE_SIO_NO_CANCEL
: 0;
1484 IoStartNextPacketByKey(IN PDEVICE_OBJECT DeviceObject
,
1485 IN BOOLEAN Cancelable
,
1488 PEXTENDED_DEVOBJ_EXTENSION DeviceExtension
;
1490 /* Get the Device Extension */
1491 DeviceExtension
= IoGetDevObjExtension(DeviceObject
);
1493 /* Check if deferred start was requested */
1494 if (DeviceExtension
->StartIoFlags
& DOE_SIO_DEFERRED
)
1496 /* Call our internal function to handle the defered case */
1497 IopStartNextPacketByKeyEx(DeviceObject
,
1500 (Cancelable
? DOE_SIO_CANCELABLE
: 0));
1504 /* Call the normal routine */
1505 IopStartNextPacketByKey(DeviceObject
, Cancelable
, Key
);
1514 IoStartNextPacket(IN PDEVICE_OBJECT DeviceObject
,
1515 IN BOOLEAN Cancelable
)
1517 PEXTENDED_DEVOBJ_EXTENSION DeviceExtension
;
1519 /* Get the Device Extension */
1520 DeviceExtension
= IoGetDevObjExtension(DeviceObject
);
1522 /* Check if deferred start was requested */
1523 if (DeviceExtension
->StartIoFlags
& DOE_SIO_DEFERRED
)
1525 /* Call our internal function to handle the defered case */
1526 IopStartNextPacketByKeyEx(DeviceObject
,
1529 (Cancelable
? DOE_SIO_CANCELABLE
: 0));
1533 /* Call the normal routine */
1534 IopStartNextPacket(DeviceObject
, Cancelable
);
1543 IoStartPacket(IN PDEVICE_OBJECT DeviceObject
,
1546 IN PDRIVER_CANCEL CancelFunction
)
1549 KIRQL OldIrql
, CancelIrql
;
1551 /* Raise to dispatch level */
1552 KeRaiseIrql(DISPATCH_LEVEL
, &OldIrql
);
1554 /* Check if we should acquire the cancel lock */
1557 /* Acquire and set it */
1558 IoAcquireCancelSpinLock(&CancelIrql
);
1559 Irp
->CancelRoutine
= CancelFunction
;
1562 /* Check if we have a key */
1566 Stat
= KeInsertByKeyDeviceQueue(&DeviceObject
->DeviceQueue
,
1567 &Irp
->Tail
.Overlay
.DeviceQueueEntry
,
1572 /* Insert without a key */
1573 Stat
= KeInsertDeviceQueue(&DeviceObject
->DeviceQueue
,
1574 &Irp
->Tail
.Overlay
.DeviceQueueEntry
);
1577 /* Check if this was a first insert */
1581 DeviceObject
->CurrentIrp
= Irp
;
1583 /* Check if this is a cancelable packet */
1586 /* Check if the caller requested no cancellation */
1587 if (IoGetDevObjExtension(DeviceObject
)->StartIoFlags
&
1590 /* He did, so remove the cancel routine */
1591 Irp
->CancelRoutine
= NULL
;
1594 /* Release the cancel lock */
1595 IoReleaseCancelSpinLock(CancelIrql
);
1598 /* Call the Start I/O function */
1599 DeviceObject
->DriverObject
->DriverStartIo(DeviceObject
, Irp
);
1603 /* The packet was inserted... check if we have a cancel function */
1606 /* Check if the IRP got cancelled */
1610 * Set the cancel IRQL, clear the currnet cancel routine and
1613 Irp
->CancelIrql
= CancelIrql
;
1614 Irp
->CancelRoutine
= NULL
;
1615 CancelFunction(DeviceObject
, Irp
);
1619 /* Otherwise, release the lock */
1620 IoReleaseCancelSpinLock(CancelIrql
);
1625 /* Return back to previous IRQL */
1626 KeLowerIrql(OldIrql
);