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 IopAttachDeviceToDeviceStackSafe(IN PDEVICE_OBJECT SourceDevice
,
29 IN PDEVICE_OBJECT TargetDevice
,
30 OUT PDEVICE_OBJECT
*AttachedToDeviceObject OPTIONAL
)
32 PDEVICE_OBJECT AttachedDevice
;
33 PEXTENDED_DEVOBJ_EXTENSION SourceDeviceExtension
;
35 /* Get the Attached Device and source extension */
36 AttachedDevice
= IoGetAttachedDevice(TargetDevice
);
37 SourceDeviceExtension
= IoGetDevObjExtension(SourceDevice
);
38 ASSERT(SourceDeviceExtension
->AttachedTo
== NULL
);
40 /* Make sure that it's in a correct state */
41 if ((AttachedDevice
->Flags
& DO_DEVICE_INITIALIZING
) ||
42 (IoGetDevObjExtension(AttachedDevice
)->ExtensionFlags
&
46 DOE_REMOVE_PROCESSED
)))
48 /* Device was unloading or being removed */
49 AttachedDevice
= NULL
;
53 /* Update atached device fields */
54 AttachedDevice
->AttachedDevice
= SourceDevice
;
55 AttachedDevice
->Spare1
++;
57 /* Update the source with the attached data */
58 SourceDevice
->StackSize
= AttachedDevice
->StackSize
+ 1;
59 SourceDevice
->AlignmentRequirement
= AttachedDevice
->
61 SourceDevice
->SectorSize
= AttachedDevice
->SectorSize
;
63 /* Check for pending start flag */
64 if (IoGetDevObjExtension(AttachedDevice
)->ExtensionFlags
&
68 IoGetDevObjExtension(SourceDevice
)->ExtensionFlags
|=
72 /* Set the attachment in the device extension */
73 SourceDeviceExtension
->AttachedTo
= AttachedDevice
;
76 /* Return the attached device */
77 if (AttachedToDeviceObject
) *AttachedToDeviceObject
= AttachedDevice
;
78 return AttachedDevice
;
83 IoShutdownRegisteredDevices(VOID
)
85 PLIST_ENTRY ListEntry
;
86 PDEVICE_OBJECT DeviceObject
;
87 PSHUTDOWN_ENTRY ShutdownEntry
;
88 IO_STATUS_BLOCK StatusBlock
;
93 /* Initialize an event to wait on */
94 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
96 /* Get the first entry and start looping */
97 ListEntry
= ExInterlockedRemoveHeadList(&ShutdownListHead
,
101 /* Get the shutdown entry */
102 ShutdownEntry
= CONTAINING_RECORD(ListEntry
,
106 /* Get the attached device */
107 DeviceObject
= IoGetAttachedDevice(ShutdownEntry
->DeviceObject
);
109 /* Build the shutdown IRP and call the driver */
110 Irp
= IoBuildSynchronousFsdRequest(IRP_MJ_SHUTDOWN
,
117 Status
= IoCallDriver(DeviceObject
, Irp
);
118 if (Status
== STATUS_PENDING
)
120 /* Wait on the driver */
121 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
124 /* Free the shutdown entry and reset the event */
125 ExFreePool(ShutdownEntry
);
126 KeClearEvent(&Event
);
128 /* Go to the next entry */
129 ListEntry
= ExInterlockedRemoveHeadList(&ShutdownListHead
,
136 IopGetDeviceObjectPointer(IN PUNICODE_STRING ObjectName
,
137 IN ACCESS_MASK DesiredAccess
,
138 OUT PFILE_OBJECT
*FileObject
,
139 OUT PDEVICE_OBJECT
*DeviceObject
,
142 OBJECT_ATTRIBUTES ObjectAttributes
;
143 IO_STATUS_BLOCK StatusBlock
;
144 PFILE_OBJECT LocalFileObject
;
148 /* Open the Device */
149 InitializeObjectAttributes(&ObjectAttributes
,
154 Status
= ZwOpenFile(&FileHandle
,
159 FILE_NON_DIRECTORY_FILE
| AttachFlag
);
160 if (!NT_SUCCESS(Status
)) return Status
;
162 /* Get File Object */
163 Status
= ObReferenceObjectByHandle(FileHandle
,
167 (PVOID
*)&LocalFileObject
,
169 if (NT_SUCCESS(Status
))
171 /* Return the requested data */
172 *DeviceObject
= IoGetRelatedDeviceObject(LocalFileObject
);
173 *FileObject
= LocalFileObject
;
176 /* Close the handle */
184 IopGetLowestDevice(IN PDEVICE_OBJECT DeviceObject
)
186 PDEVICE_OBJECT LowestDevice
;
187 PEXTENDED_DEVOBJ_EXTENSION DeviceExtension
;
189 /* Get the current device and its extension */
190 LowestDevice
= DeviceObject
;
191 DeviceExtension
= IoGetDevObjExtension(LowestDevice
);
193 /* Keep looping as long as we're attached */
194 while (DeviceExtension
->AttachedTo
)
196 /* Get the lowest device and its extension */
197 LowestDevice
= DeviceExtension
->AttachedTo
;
198 DeviceExtension
= IoGetDevObjExtension(LowestDevice
);
201 /* Return the lowest device */
207 IopEditDeviceList(IN PDRIVER_OBJECT DriverObject
,
208 IN PDEVICE_OBJECT DeviceObject
,
209 IN IOP_DEVICE_LIST_OPERATION Type
)
211 PDEVICE_OBJECT Previous
;
213 /* Check the type of operation */
214 if (Type
== IopRemove
)
216 /* Get the current device and check if it's the current one */
217 Previous
= DeviceObject
->DriverObject
->DeviceObject
;
218 if (Previous
== DeviceObject
)
220 /* It is, simply unlink this one directly */
221 DeviceObject
->DriverObject
->DeviceObject
=
222 DeviceObject
->NextDevice
;
226 /* It's not, so loop until we find the device */
227 while (Previous
->NextDevice
!= DeviceObject
)
229 /* Not this one, keep moving */
230 Previous
= Previous
->NextDevice
;
233 /* We found it, now unlink us */
234 Previous
->NextDevice
= DeviceObject
->NextDevice
;
239 /* Link the device object and the driver object */
240 DeviceObject
->NextDevice
= DriverObject
->DeviceObject
;
241 DriverObject
->DeviceObject
= DeviceObject
;
247 IopUnloadDevice(IN PDEVICE_OBJECT DeviceObject
)
249 PDRIVER_OBJECT DriverObject
= DeviceObject
->DriverObject
;
250 PDEVICE_OBJECT AttachedDeviceObject
, LowestDeviceObject
;
251 PEXTENDED_DEVOBJ_EXTENSION ThisExtension
, DeviceExtension
;
252 PDEVICE_NODE DeviceNode
;
253 BOOLEAN SafeToUnload
= TRUE
;
255 /* Check if removal is pending */
256 ThisExtension
= IoGetDevObjExtension(DeviceObject
);
257 if (ThisExtension
->ExtensionFlags
& DOE_REMOVE_PENDING
)
259 /* Get the PDO, extension, and node */
260 LowestDeviceObject
= IopGetLowestDevice(DeviceObject
);
261 DeviceExtension
= IoGetDevObjExtension(LowestDeviceObject
);
262 DeviceNode
= DeviceExtension
->DeviceNode
;
264 /* The PDO needs a device node */
265 ASSERT(DeviceNode
!= NULL
);
267 /* Loop all attached objects */
268 AttachedDeviceObject
= LowestDeviceObject
;
269 while (AttachedDeviceObject
)
271 /* Make sure they're dereferenced */
272 if (AttachedDeviceObject
->ReferenceCount
) return;
273 AttachedDeviceObject
= AttachedDeviceObject
->AttachedDevice
;
276 /* Loop all attached objects */
277 AttachedDeviceObject
= LowestDeviceObject
;
278 while (AttachedDeviceObject
)
280 /* Get the device extension */
281 DeviceExtension
= IoGetDevObjExtension(AttachedDeviceObject
);
283 /* Remove the pending flag and set processed */
284 DeviceExtension
->ExtensionFlags
&= ~DOE_REMOVE_PENDING
;
285 DeviceExtension
->ExtensionFlags
|= DOE_REMOVE_PROCESSED
;
286 AttachedDeviceObject
= AttachedDeviceObject
->AttachedDevice
;
290 * FIXME: TODO HPOUSSIN
291 * We need to parse/lock the device node, and if we have any pending
292 * surprise removals, query all relationships and send IRP_MN_REMOVE_
293 * _DEVICE to the devices related...
298 /* Check if deletion is pending */
299 if (ThisExtension
->ExtensionFlags
& DOE_DELETE_PENDING
)
301 /* Make sure unload is pending */
302 if (!(ThisExtension
->ExtensionFlags
& DOE_UNLOAD_PENDING
) ||
303 (DriverObject
->Flags
& DRVO_UNLOAD_INVOKED
))
305 /* We can't unload anymore */
306 SafeToUnload
= FALSE
;
310 * Check if we have an attached device and fail if we're attached
311 * and still have a reference count.
313 AttachedDeviceObject
= DeviceObject
->AttachedDevice
;
314 if ((AttachedDeviceObject
) && (DeviceObject
->ReferenceCount
)) return;
316 /* Check if we have a Security Descriptor */
317 if (DeviceObject
->SecurityDescriptor
)
320 ExFreePool(DeviceObject
->SecurityDescriptor
);
323 /* Remove the device from the list */
324 IopEditDeviceList(DeviceObject
->DriverObject
, DeviceObject
, IopRemove
);
326 /* Dereference the keep-alive */
327 ObDereferenceObject(DeviceObject
);
329 /* If we're not unloading, stop here */
330 if (!SafeToUnload
) return;
333 /* Loop all the device objects */
334 DeviceObject
= DriverObject
->DeviceObject
;
338 * Make sure we're not attached, having a reference count
339 * or already deleting
341 if ((DeviceObject
->ReferenceCount
) ||
342 (DeviceObject
->AttachedDevice
) ||
343 (IoGetDevObjExtension(DeviceObject
)->ExtensionFlags
&
344 (DOE_DELETE_PENDING
| DOE_REMOVE_PENDING
)))
346 /* We're not safe to unload, quit */
350 /* Check the next device */
351 DeviceObject
= DeviceObject
->NextDevice
;
354 /* Set the unload invoked flag */
355 DriverObject
->Flags
|= DRVO_UNLOAD_INVOKED
;
358 if (DriverObject
->DriverUnload
) DriverObject
->DriverUnload(DriverObject
);
363 IopDereferenceDeviceObject(IN PDEVICE_OBJECT DeviceObject
,
364 IN BOOLEAN ForceUnload
)
367 ASSERT(DeviceObject
->ReferenceCount
);
369 /* Dereference the device */
370 DeviceObject
->ReferenceCount
--;
373 * Check if we can unload it and it's safe to unload (or if we're forcing
374 * an unload, which is OK too).
376 if (!(DeviceObject
->ReferenceCount
) &&
377 ((ForceUnload
) || (IoGetDevObjExtension(DeviceObject
)->ExtensionFlags
&
378 (DOE_UNLOAD_PENDING
|
381 DOE_REMOVE_PROCESSED
))))
384 IopUnloadDevice(DeviceObject
);
390 IopStartNextPacketByKey(IN PDEVICE_OBJECT DeviceObject
,
391 IN BOOLEAN Cancelable
,
394 PKDEVICE_QUEUE_ENTRY Entry
;
398 /* Acquire the cancel lock if this is cancelable */
399 if (Cancelable
) IoAcquireCancelSpinLock(&OldIrql
);
401 /* Clear the current IRP */
402 DeviceObject
->CurrentIrp
= NULL
;
404 /* Remove an entry from the queue */
405 Entry
= KeRemoveByKeyDeviceQueue(&DeviceObject
->DeviceQueue
, Key
);
408 /* Get the IRP and set it */
409 Irp
= CONTAINING_RECORD(Entry
, IRP
, Tail
.Overlay
.DeviceQueueEntry
);
410 DeviceObject
->CurrentIrp
= Irp
;
412 /* Check if this is a cancelable packet */
415 /* Check if the caller requested no cancellation */
416 if (IoGetDevObjExtension(DeviceObject
)->StartIoFlags
&
419 /* He did, so remove the cancel routine */
420 Irp
->CancelRoutine
= NULL
;
423 /* Release the cancel lock */
424 IoReleaseCancelSpinLock(OldIrql
);
427 /* Call the Start I/O Routine */
428 DeviceObject
->DriverObject
->DriverStartIo(DeviceObject
, Irp
);
432 /* Otherwise, release the cancel lock if we had acquired it */
433 if (Cancelable
) IoReleaseCancelSpinLock(OldIrql
);
439 IopStartNextPacket(IN PDEVICE_OBJECT DeviceObject
,
440 IN BOOLEAN Cancelable
)
442 PKDEVICE_QUEUE_ENTRY Entry
;
446 /* Acquire the cancel lock if this is cancelable */
447 if (Cancelable
) IoAcquireCancelSpinLock(&OldIrql
);
449 /* Clear the current IRP */
450 DeviceObject
->CurrentIrp
= NULL
;
452 /* Remove an entry from the queue */
453 Entry
= KeRemoveDeviceQueue(&DeviceObject
->DeviceQueue
);
456 /* Get the IRP and set it */
457 Irp
= CONTAINING_RECORD(Entry
, IRP
, Tail
.Overlay
.DeviceQueueEntry
);
458 DeviceObject
->CurrentIrp
= Irp
;
460 /* Check if this is a cancelable packet */
463 /* Check if the caller requested no cancellation */
464 if (IoGetDevObjExtension(DeviceObject
)->StartIoFlags
&
467 /* He did, so remove the cancel routine */
468 Irp
->CancelRoutine
= NULL
;
471 /* Release the cancel lock */
472 IoReleaseCancelSpinLock(OldIrql
);
475 /* Call the Start I/O Routine */
476 DeviceObject
->DriverObject
->DriverStartIo(DeviceObject
, Irp
);
480 /* Otherwise, release the cancel lock if we had acquired it */
481 if (Cancelable
) IoReleaseCancelSpinLock(OldIrql
);
487 IopStartNextPacketByKeyEx(IN PDEVICE_OBJECT DeviceObject
,
491 PEXTENDED_DEVOBJ_EXTENSION DeviceExtension
;
492 ULONG CurrentKey
= Key
;
493 ULONG CurrentFlags
= Flags
;
495 /* Get the device extension and start the packet loop */
496 DeviceExtension
= IoGetDevObjExtension(DeviceObject
);
499 /* Increase the count */
500 if (InterlockedIncrement(&DeviceExtension
->StartIoCount
) > 1)
503 * We've already called the routine once...
504 * All we have to do is save the key and add the new flags
506 DeviceExtension
->StartIoFlags
|= CurrentFlags
;
507 DeviceExtension
->StartIoKey
= CurrentKey
;
511 /* Mask out the current packet flags and key */
512 DeviceExtension
->StartIoFlags
&= ~(DOE_SIO_WITH_KEY
|
515 DeviceExtension
->StartIoKey
= 0;
517 /* Check if this is a packet start with key */
518 if (Flags
& DOE_SIO_WITH_KEY
)
520 /* Start the packet with a key */
521 IopStartNextPacketByKey(DeviceObject
,
522 (Flags
& DOE_SIO_CANCELABLE
) ?
526 else if (Flags
& DOE_SIO_NO_KEY
)
528 /* Start the packet */
529 IopStartNextPacket(DeviceObject
,
530 (Flags
& DOE_SIO_CANCELABLE
) ?
535 /* Decrease the Start I/O count and check if it's 0 now */
536 if (!InterlockedDecrement(&DeviceExtension
->StartIoCount
))
538 /* Get the current active key and flags */
539 CurrentKey
= DeviceExtension
->StartIoKey
;
540 CurrentFlags
= DeviceExtension
->StartIoFlags
& (DOE_SIO_WITH_KEY
|
544 /* Check if we should still loop */
545 if (!(CurrentFlags
& (DOE_SIO_WITH_KEY
| DOE_SIO_NO_KEY
))) break;
549 /* There are still Start I/Os active, so quit this loop */
555 /* PUBLIC FUNCTIONS ***********************************************************/
560 * Layers a device over the highest device in a device stack.
564 * Device to be attached.
567 * Name of the target device.
570 * Caller storage for the device attached to.
577 IoAttachDevice(PDEVICE_OBJECT SourceDevice
,
578 PUNICODE_STRING TargetDeviceName
,
579 PDEVICE_OBJECT
*AttachedDevice
)
582 PFILE_OBJECT FileObject
= NULL
;
583 PDEVICE_OBJECT TargetDevice
= NULL
;
585 /* Call the helper routine for an attach operation */
586 Status
= IopGetDeviceObjectPointer(TargetDeviceName
,
587 FILE_READ_ATTRIBUTES
,
590 IO_ATTACH_DEVICE_API
);
591 if (!NT_SUCCESS(Status
)) return Status
;
593 /* Attach the device */
594 Status
= IoAttachDeviceToDeviceStackSafe(SourceDevice
,
599 ObDereferenceObject(FileObject
);
604 * IoAttachDeviceByPointer
611 IoAttachDeviceByPointer(IN PDEVICE_OBJECT SourceDevice
,
612 IN PDEVICE_OBJECT TargetDevice
)
614 PDEVICE_OBJECT AttachedDevice
;
615 NTSTATUS Status
= STATUS_SUCCESS
;
618 AttachedDevice
= IoAttachDeviceToDeviceStack(SourceDevice
, TargetDevice
);
619 if (!AttachedDevice
) Status
= STATUS_NO_SUCH_DEVICE
;
621 /* Return the status */
630 IoAttachDeviceToDeviceStack(IN PDEVICE_OBJECT SourceDevice
,
631 IN PDEVICE_OBJECT TargetDevice
)
633 /* Attach it safely */
634 return IopAttachDeviceToDeviceStackSafe(SourceDevice
,
644 IoAttachDeviceToDeviceStackSafe(IN PDEVICE_OBJECT SourceDevice
,
645 IN PDEVICE_OBJECT TargetDevice
,
646 IN OUT PDEVICE_OBJECT
*AttachedToDeviceObject
)
648 /* Call the internal function */
649 if (!IopAttachDeviceToDeviceStackSafe(SourceDevice
,
651 AttachedToDeviceObject
))
654 return STATUS_NO_SUCH_DEVICE
;
658 return STATUS_SUCCESS
;
664 * Allocates memory for and intializes a device object for use for
669 * Driver object passed by IO Manager when the driver was loaded.
671 * DeviceExtensionSize
672 * Number of bytes for the device extension.
675 * Unicode name of device.
678 * Device type of the new device.
680 * DeviceCharacteristics
681 * Bit mask of device characteristics.
684 * TRUE if only one thread can access the device at a time.
687 * On successful return this parameter is filled by pointer to
688 * allocated device object.
695 IoCreateDevice(IN PDRIVER_OBJECT DriverObject
,
696 IN ULONG DeviceExtensionSize
,
697 IN PUNICODE_STRING DeviceName
,
698 IN DEVICE_TYPE DeviceType
,
699 IN ULONG DeviceCharacteristics
,
700 IN BOOLEAN Exclusive
,
701 OUT PDEVICE_OBJECT
*DeviceObject
)
703 WCHAR AutoNameBuffer
[20];
704 UNICODE_STRING AutoName
;
705 PDEVICE_OBJECT CreatedDeviceObject
;
706 PDEVOBJ_EXTENSION DeviceObjectExtension
;
707 OBJECT_ATTRIBUTES ObjectAttributes
;
709 ULONG AlignedDeviceExtensionSize
;
714 /* Check if we have to generate a name */
715 if (DeviceCharacteristics
& FILE_AUTOGENERATED_DEVICE_NAME
)
718 swprintf(AutoNameBuffer
,
720 InterlockedIncrementUL(&IopDeviceObjectNumber
));
722 /* Initialize the name */
723 RtlInitUnicodeString(&AutoName
, AutoNameBuffer
);
724 DeviceName
= &AutoName
;
727 /* Initialize the Object Attributes */
728 InitializeObjectAttributes(&ObjectAttributes
,
734 /* Honor exclusive flag */
735 if (Exclusive
) ObjectAttributes
.Attributes
|= OBJ_EXCLUSIVE
;
737 /* Create a permanent object for named devices */
738 if (DeviceName
) ObjectAttributes
.Attributes
|= OBJ_PERMANENT
;
740 /* Align the Extension Size to 8-bytes */
741 AlignedDeviceExtensionSize
= (DeviceExtensionSize
+ 7) &~ 7;
744 TotalSize
= AlignedDeviceExtensionSize
+
745 sizeof(DEVICE_OBJECT
) +
746 sizeof(EXTENDED_DEVOBJ_EXTENSION
);
748 /* Create the Device Object */
749 *DeviceObject
= NULL
;
750 Status
= ObCreateObject(KernelMode
,
758 (PVOID
*)&CreatedDeviceObject
);
759 if (!NT_SUCCESS(Status
)) return Status
;
761 /* Clear the whole Object and extension so we don't null stuff manually */
762 RtlZeroMemory(CreatedDeviceObject
, TotalSize
);
765 * Setup the Type and Size. Note that we don't use the aligned size,
766 * because that's only padding for the DevObjExt and not part of the Object.
768 CreatedDeviceObject
->Type
= IO_TYPE_DEVICE
;
769 CreatedDeviceObject
->Size
= sizeof(DEVICE_OBJECT
) + (USHORT
)DeviceExtensionSize
;
771 /* The kernel extension is after the driver internal extension */
772 DeviceObjectExtension
= (PDEVOBJ_EXTENSION
)
773 ((ULONG_PTR
)(CreatedDeviceObject
+ 1) +
774 AlignedDeviceExtensionSize
);
776 /* Set the Type and Size. Question: why is Size 0 on Windows? */
777 DeviceObjectExtension
->Type
= IO_TYPE_DEVICE_OBJECT_EXTENSION
;
778 DeviceObjectExtension
->Size
= 0;
780 /* Link the Object and Extension */
781 DeviceObjectExtension
->DeviceObject
= CreatedDeviceObject
;
782 CreatedDeviceObject
->DeviceObjectExtension
= DeviceObjectExtension
;
784 /* Set Device Object Data */
785 CreatedDeviceObject
->DeviceType
= DeviceType
;
786 CreatedDeviceObject
->Characteristics
= DeviceCharacteristics
;
787 CreatedDeviceObject
->DeviceExtension
= DeviceExtensionSize
?
788 CreatedDeviceObject
+ 1 :
790 CreatedDeviceObject
->StackSize
= 1;
791 CreatedDeviceObject
->AlignmentRequirement
= 0;
794 CreatedDeviceObject
->Flags
= DO_DEVICE_INITIALIZING
;
795 if (Exclusive
) CreatedDeviceObject
->Flags
|= DO_EXCLUSIVE
;
796 if (DeviceName
) CreatedDeviceObject
->Flags
|= DO_DEVICE_HAS_NAME
;
798 /* Attach a Vpb for Disks and Tapes, and create the Device Lock */
799 if ((CreatedDeviceObject
->DeviceType
== FILE_DEVICE_DISK
) ||
800 (CreatedDeviceObject
->DeviceType
== FILE_DEVICE_VIRTUAL_DISK
) ||
801 (CreatedDeviceObject
->DeviceType
== FILE_DEVICE_CD_ROM
) ||
802 (CreatedDeviceObject
->DeviceType
== FILE_DEVICE_TAPE
))
805 Status
= IopCreateVpb(CreatedDeviceObject
);
806 if (!NT_SUCCESS(Status
))
808 /* Reference the device object and fail */
809 ObDereferenceObject(DeviceObject
);
813 /* Initialize Lock Event */
814 KeInitializeEvent(&CreatedDeviceObject
->DeviceLock
,
815 SynchronizationEvent
,
819 /* Set the right Sector Size */
822 /* All disk systems */
823 case FILE_DEVICE_DISK_FILE_SYSTEM
:
824 case FILE_DEVICE_DISK
:
825 case FILE_DEVICE_VIRTUAL_DISK
:
827 /* The default is 512 bytes */
828 CreatedDeviceObject
->SectorSize
= 512;
831 /* CD-ROM file systems */
832 case FILE_DEVICE_CD_ROM_FILE_SYSTEM
:
834 /* The default is 2048 bytes */
835 CreatedDeviceObject
->SectorSize
= 2048;
838 /* Create the Device Queue */
839 if ((CreatedDeviceObject
->DeviceType
== FILE_DEVICE_DISK_FILE_SYSTEM
) ||
840 (CreatedDeviceObject
->DeviceType
== FILE_DEVICE_FILE_SYSTEM
) ||
841 (CreatedDeviceObject
->DeviceType
== FILE_DEVICE_CD_ROM_FILE_SYSTEM
) ||
842 (CreatedDeviceObject
->DeviceType
== FILE_DEVICE_NETWORK_FILE_SYSTEM
) ||
843 (CreatedDeviceObject
->DeviceType
== FILE_DEVICE_TAPE_FILE_SYSTEM
))
845 /* Simple FS Devices, they don't need a real Device Queue */
846 InitializeListHead(&CreatedDeviceObject
->Queue
.ListEntry
);
850 /* An actual Device, initialize its DQ */
851 KeInitializeDeviceQueue(&CreatedDeviceObject
->DeviceQueue
);
854 /* Insert the Object */
855 Status
= ObInsertObject(CreatedDeviceObject
,
857 FILE_READ_DATA
| FILE_WRITE_DATA
,
859 (PVOID
*)&CreatedDeviceObject
,
861 if (!NT_SUCCESS(Status
)) return Status
;
863 /* Now do the final linking */
864 ObReferenceObject(DriverObject
);
865 ASSERT((DriverObject
->Flags
& DRVO_UNLOAD_INVOKED
) == 0);
866 CreatedDeviceObject
->DriverObject
= DriverObject
;
867 IopEditDeviceList(DriverObject
, CreatedDeviceObject
, IopAdd
);
869 /* Close the temporary handle and return to caller */
870 ObCloseHandle(TempHandle
, KernelMode
);
871 *DeviceObject
= CreatedDeviceObject
;
872 return STATUS_SUCCESS
;
883 IoDeleteDevice(IN PDEVICE_OBJECT DeviceObject
)
887 /* Check if the device is registered for shutdown notifications */
888 if (DeviceObject
->Flags
& DO_SHUTDOWN_REGISTERED
)
890 /* Call the shutdown notifications */
891 IoUnregisterShutdownNotification(DeviceObject
);
894 /* Check if it has a timer */
895 Timer
= DeviceObject
->Timer
;
898 /* Remove it and free it */
899 IopRemoveTimerFromTimerList(Timer
);
900 ExFreePoolWithTag(Timer
, TAG_IO_TIMER
);
903 /* Check if the device has a name */
904 if (DeviceObject
->Flags
& DO_DEVICE_HAS_NAME
)
906 /* It does, make it temporary so we can remove it */
907 ObMakeTemporaryObject(DeviceObject
);
910 /* Set the pending delete flag */
911 IoGetDevObjExtension(DeviceObject
)->ExtensionFlags
|= DOE_DELETE_PENDING
;
913 /* Check if the device object can be unloaded */
914 if (!DeviceObject
->ReferenceCount
) IopUnloadDevice(DeviceObject
);
925 IoDetachDevice(IN PDEVICE_OBJECT TargetDevice
)
927 PEXTENDED_DEVOBJ_EXTENSION DeviceExtension
;
930 DeviceExtension
= IoGetDevObjExtension(TargetDevice
->AttachedDevice
);
931 ASSERT(DeviceExtension
->AttachedTo
== TargetDevice
);
933 /* Remove the attachment */
934 DeviceExtension
->AttachedTo
= NULL
;
935 TargetDevice
->AttachedDevice
= NULL
;
937 /* Check if it's ok to delete this device */
938 if ((IoGetDevObjExtension(TargetDevice
)->ExtensionFlags
&
939 (DOE_UNLOAD_PENDING
| DOE_DELETE_PENDING
| DOE_REMOVE_PENDING
)) &&
940 !(TargetDevice
->ReferenceCount
))
943 IopUnloadDevice(TargetDevice
);
952 IoEnumerateDeviceObjectList(IN PDRIVER_OBJECT DriverObject
,
953 IN PDEVICE_OBJECT
*DeviceObjectList
,
954 IN ULONG DeviceObjectListSize
,
955 OUT PULONG ActualNumberDeviceObjects
)
957 ULONG ActualDevices
= 1;
958 PDEVICE_OBJECT CurrentDevice
= DriverObject
->DeviceObject
;
960 /* Find out how many devices we'll enumerate */
961 while ((CurrentDevice
= CurrentDevice
->NextDevice
)) ActualDevices
++;
963 /* Go back to the first */
964 CurrentDevice
= DriverObject
->DeviceObject
;
966 /* Start by at least returning this */
967 *ActualNumberDeviceObjects
= ActualDevices
;
969 /* Check if we can support so many */
970 if ((ActualDevices
* 4) > DeviceObjectListSize
)
972 /* Fail because the buffer was too small */
973 return STATUS_BUFFER_TOO_SMALL
;
976 /* Check if the caller only wanted the size */
977 if (DeviceObjectList
)
979 /* Loop through all the devices */
980 while (ActualDevices
)
982 /* Reference each Device */
983 ObReferenceObject(CurrentDevice
);
985 /* Add it to the list */
986 *DeviceObjectList
= CurrentDevice
;
988 /* Go to the next one */
989 CurrentDevice
= CurrentDevice
->NextDevice
;
995 /* Return the status */
996 return STATUS_SUCCESS
;
1000 * IoGetAttachedDevice
1007 IoGetAttachedDevice(PDEVICE_OBJECT DeviceObject
)
1009 /* Get the last attached device */
1010 while (DeviceObject
->AttachedDevice
)
1012 /* Move to the next one */
1013 DeviceObject
= DeviceObject
->AttachedDevice
;
1017 return DeviceObject
;
1021 * IoGetAttachedDeviceReference
1028 IoGetAttachedDeviceReference(PDEVICE_OBJECT DeviceObject
)
1030 /* Reference the Attached Device */
1031 DeviceObject
= IoGetAttachedDevice(DeviceObject
);
1032 ObReferenceObject(DeviceObject
);
1033 return DeviceObject
;
1041 IoGetDeviceAttachmentBaseRef(IN PDEVICE_OBJECT DeviceObject
)
1043 /* Reference the lowest attached device */
1044 DeviceObject
= IopGetLowestDevice(DeviceObject
);
1045 ObReferenceObject(DeviceObject
);
1046 return DeviceObject
;
1050 * IoGetDeviceObjectPointer
1057 IoGetDeviceObjectPointer(IN PUNICODE_STRING ObjectName
,
1058 IN ACCESS_MASK DesiredAccess
,
1059 OUT PFILE_OBJECT
*FileObject
,
1060 OUT PDEVICE_OBJECT
*DeviceObject
)
1062 /* Call the helper routine for a normal operation */
1063 return IopGetDeviceObjectPointer(ObjectName
,
1075 IoGetDiskDeviceObject(IN PDEVICE_OBJECT FileSystemDeviceObject
,
1076 OUT PDEVICE_OBJECT
*DiskDeviceObject
)
1078 PEXTENDED_DEVOBJ_EXTENSION DeviceExtension
;
1083 /* Make sure there's a VPB */
1084 if (!FileSystemDeviceObject
->Vpb
) return STATUS_INVALID_PARAMETER
;
1087 IoAcquireVpbSpinLock(&OldIrql
);
1089 /* Get the Device Extension */
1090 DeviceExtension
= IoGetDevObjExtension(FileSystemDeviceObject
);
1092 /* Make sure this one has a VPB too */
1093 Vpb
= DeviceExtension
->Vpb
;
1096 /* Make sure that it's mounted */
1097 if ((Vpb
->ReferenceCount
) &&
1098 (Vpb
->Flags
& VPB_MOUNTED
))
1100 /* Return the Disk Device Object */
1101 *DiskDeviceObject
= Vpb
->RealDevice
;
1103 /* Reference it and return success */
1104 ObReferenceObject(Vpb
->RealDevice
);
1105 Status
= STATUS_SUCCESS
;
1109 /* It's not, so return failure */
1110 Status
= STATUS_VOLUME_DISMOUNTED
;
1116 Status
= STATUS_INVALID_PARAMETER
;
1119 /* Release the lock */
1120 IoReleaseVpbSpinLock(OldIrql
);
1129 IoGetLowerDeviceObject(IN PDEVICE_OBJECT DeviceObject
)
1131 PEXTENDED_DEVOBJ_EXTENSION DeviceExtension
;
1132 PDEVICE_OBJECT LowerDeviceObject
= NULL
;
1134 /* Make sure it's not getting deleted */
1135 DeviceExtension
= IoGetDevObjExtension(DeviceObject
);
1136 if (DeviceExtension
->ExtensionFlags
& (DOE_UNLOAD_PENDING
|
1137 DOE_DELETE_PENDING
|
1138 DOE_REMOVE_PENDING
|
1139 DOE_REMOVE_PROCESSED
))
1141 /* Get the Lower Device Object */
1142 LowerDeviceObject
= DeviceExtension
->AttachedTo
;
1145 ObReferenceObject(LowerDeviceObject
);
1149 return LowerDeviceObject
;
1157 IoGetRelatedDeviceObject(IN PFILE_OBJECT FileObject
)
1159 PDEVICE_OBJECT DeviceObject
= FileObject
->DeviceObject
;
1161 /* Check if we have a VPB with a device object */
1162 if ((FileObject
->Vpb
) && (FileObject
->Vpb
->DeviceObject
))
1164 /* Then use the DO from the VPB */
1165 ASSERT(!(FileObject
->Flags
& FO_DIRECT_DEVICE_OPEN
));
1166 DeviceObject
= FileObject
->Vpb
->DeviceObject
;
1168 else if (!(FileObject
->Flags
& FO_DIRECT_DEVICE_OPEN
) &&
1169 (FileObject
->DeviceObject
->Vpb
) &&
1170 (FileObject
->DeviceObject
->Vpb
->DeviceObject
))
1172 /* The disk device actually has a VPB, so get the DO from there */
1173 DeviceObject
= FileObject
->DeviceObject
->Vpb
->DeviceObject
;
1177 /* Otherwise, this was a direct open */
1178 DeviceObject
= FileObject
->DeviceObject
;
1182 ASSERT(DeviceObject
!= NULL
);
1184 /* Check if we were attached */
1185 if (DeviceObject
->AttachedDevice
)
1187 /* Check if the file object has an extension present */
1188 if (FileObject
->Flags
& FO_FILE_OBJECT_HAS_EXTENSION
)
1190 /* Sanity check, direct open files can't have this */
1191 ASSERT(!(FileObject
->Flags
& FO_DIRECT_DEVICE_OPEN
));
1193 /* Check if the extension is really present */
1194 if (FileObject
->FileObjectExtension
)
1196 /* FIXME: Unhandled yet */
1197 DPRINT1("FOEs not supported\n");
1202 /* Return the highest attached device */
1203 DeviceObject
= IoGetAttachedDevice(DeviceObject
);
1206 /* Return the DO we found */
1207 return DeviceObject
;
1215 IoGetBaseFileSystemDeviceObject(IN PFILE_OBJECT FileObject
)
1217 PDEVICE_OBJECT DeviceObject
;
1220 * If the FILE_OBJECT's VPB is defined,
1221 * get the device from it.
1223 if ((FileObject
->Vpb
) && (FileObject
->Vpb
->DeviceObject
))
1225 /* Use the VPB's Device Object's */
1226 DeviceObject
= FileObject
->Vpb
->DeviceObject
;
1228 else if (!(FileObject
->Flags
& FO_DIRECT_DEVICE_OPEN
) &&
1229 (FileObject
->DeviceObject
->Vpb
) &&
1230 (FileObject
->DeviceObject
->Vpb
->DeviceObject
))
1232 /* Use the VPB's File System Object */
1233 DeviceObject
= FileObject
->DeviceObject
->Vpb
->DeviceObject
;
1237 /* Use the FO's Device Object */
1238 DeviceObject
= FileObject
->DeviceObject
;
1241 /* Return the device object we found */
1242 ASSERT(DeviceObject
!= NULL
);
1243 return DeviceObject
;
1251 IoRegisterLastChanceShutdownNotification(IN PDEVICE_OBJECT DeviceObject
)
1253 PSHUTDOWN_ENTRY Entry
;
1255 /* Allocate the shutdown entry */
1256 Entry
= ExAllocatePoolWithTag(NonPagedPool
,
1257 sizeof(SHUTDOWN_ENTRY
),
1258 TAG_SHUTDOWN_ENTRY
);
1259 if (!Entry
) return STATUS_INSUFFICIENT_RESOURCES
;
1262 Entry
->DeviceObject
= DeviceObject
;
1264 /* Insert it into the list */
1265 ExInterlockedInsertHeadList(&LastChanceShutdownListHead
,
1266 &Entry
->ShutdownList
,
1269 /* Set the shutdown registered flag */
1270 DeviceObject
->Flags
|= DO_SHUTDOWN_REGISTERED
;
1271 return STATUS_SUCCESS
;
1279 IoRegisterShutdownNotification(PDEVICE_OBJECT DeviceObject
)
1281 PSHUTDOWN_ENTRY Entry
;
1283 /* Allocate the shutdown entry */
1284 Entry
= ExAllocatePoolWithTag(NonPagedPool
,
1285 sizeof(SHUTDOWN_ENTRY
),
1286 TAG_SHUTDOWN_ENTRY
);
1287 if (!Entry
) return STATUS_INSUFFICIENT_RESOURCES
;
1290 Entry
->DeviceObject
= DeviceObject
;
1292 /* Insert it into the list */
1293 ExInterlockedInsertHeadList(&ShutdownListHead
,
1294 &Entry
->ShutdownList
,
1297 /* Set the shutdown registered flag */
1298 DeviceObject
->Flags
|= DO_SHUTDOWN_REGISTERED
;
1299 return STATUS_SUCCESS
;
1307 IoUnregisterShutdownNotification(PDEVICE_OBJECT DeviceObject
)
1309 PSHUTDOWN_ENTRY ShutdownEntry
;
1310 PLIST_ENTRY NextEntry
;
1313 /* Acquire the shutdown lock and loop the shutdown list */
1314 KeAcquireSpinLock(&ShutdownListLock
, &OldIrql
);
1315 NextEntry
= ShutdownListHead
.Flink
;
1316 while (NextEntry
!= &ShutdownListHead
)
1319 ShutdownEntry
= CONTAINING_RECORD(NextEntry
,
1323 /* Get if the DO matches */
1324 if (ShutdownEntry
->DeviceObject
== DeviceObject
)
1326 /* Remove it from the list */
1327 RemoveEntryList(NextEntry
);
1328 NextEntry
= NextEntry
->Blink
;
1330 /* Free the entry */
1331 ExFreePool(ShutdownEntry
);
1334 /* Go to the next entry */
1335 NextEntry
= NextEntry
->Flink
;
1338 /* Now loop the last chance list */
1339 NextEntry
= LastChanceShutdownListHead
.Flink
;
1340 while (NextEntry
!= &LastChanceShutdownListHead
)
1343 ShutdownEntry
= CONTAINING_RECORD(NextEntry
,
1347 /* Get if the DO matches */
1348 if (ShutdownEntry
->DeviceObject
== DeviceObject
)
1350 /* Remove it from the list */
1351 RemoveEntryList(NextEntry
);
1352 NextEntry
= NextEntry
->Blink
;
1354 /* Free the entry */
1355 ExFreePool(ShutdownEntry
);
1358 /* Go to the next entry */
1359 NextEntry
= NextEntry
->Flink
;
1362 /* Release the shutdown lock */
1363 KeReleaseSpinLock(&ShutdownListLock
, OldIrql
);
1365 /* Now remove the flag */
1366 DeviceObject
->Flags
&= ~DO_SHUTDOWN_REGISTERED
;
1374 IoSetStartIoAttributes(IN PDEVICE_OBJECT DeviceObject
,
1375 IN BOOLEAN DeferredStartIo
,
1376 IN BOOLEAN NonCancelable
)
1378 PEXTENDED_DEVOBJ_EXTENSION DeviceExtension
;
1380 /* Get the Device Extension */
1381 DeviceExtension
= IoGetDevObjExtension(DeviceObject
);
1383 /* Set the flags the caller requested */
1384 DeviceExtension
->StartIoFlags
|= (DeferredStartIo
) ? DOE_SIO_DEFERRED
: 0;
1385 DeviceExtension
->StartIoFlags
|= (NonCancelable
) ? DOE_SIO_NO_CANCEL
: 0;
1393 IoStartNextPacketByKey(IN PDEVICE_OBJECT DeviceObject
,
1394 IN BOOLEAN Cancelable
,
1397 PEXTENDED_DEVOBJ_EXTENSION DeviceExtension
;
1399 /* Get the Device Extension */
1400 DeviceExtension
= IoGetDevObjExtension(DeviceObject
);
1402 /* Check if deferred start was requested */
1403 if (DeviceExtension
->StartIoFlags
& DOE_SIO_DEFERRED
)
1405 /* Call our internal function to handle the defered case */
1406 IopStartNextPacketByKeyEx(DeviceObject
,
1409 (Cancelable
? DOE_SIO_CANCELABLE
: 0));
1413 /* Call the normal routine */
1414 IopStartNextPacketByKey(DeviceObject
, Cancelable
, Key
);
1423 IoStartNextPacket(IN PDEVICE_OBJECT DeviceObject
,
1424 IN BOOLEAN Cancelable
)
1426 PEXTENDED_DEVOBJ_EXTENSION DeviceExtension
;
1428 /* Get the Device Extension */
1429 DeviceExtension
= IoGetDevObjExtension(DeviceObject
);
1431 /* Check if deferred start was requested */
1432 if (DeviceExtension
->StartIoFlags
& DOE_SIO_DEFERRED
)
1434 /* Call our internal function to handle the defered case */
1435 IopStartNextPacketByKeyEx(DeviceObject
,
1438 (Cancelable
? DOE_SIO_CANCELABLE
: 0));
1442 /* Call the normal routine */
1443 IopStartNextPacket(DeviceObject
, Cancelable
);
1452 IoStartPacket(IN PDEVICE_OBJECT DeviceObject
,
1455 IN PDRIVER_CANCEL CancelFunction
)
1458 KIRQL OldIrql
, CancelIrql
;
1460 /* Raise to dispatch level */
1461 KeRaiseIrql(DISPATCH_LEVEL
, &OldIrql
);
1463 /* Check if we should acquire the cancel lock */
1466 /* Acquire and set it */
1467 IoAcquireCancelSpinLock(&CancelIrql
);
1468 Irp
->CancelRoutine
= CancelFunction
;
1471 /* Check if we have a key */
1475 Stat
= KeInsertByKeyDeviceQueue(&DeviceObject
->DeviceQueue
,
1476 &Irp
->Tail
.Overlay
.DeviceQueueEntry
,
1481 /* Insert without a key */
1482 Stat
= KeInsertDeviceQueue(&DeviceObject
->DeviceQueue
,
1483 &Irp
->Tail
.Overlay
.DeviceQueueEntry
);
1486 /* Check if this was a first insert */
1490 DeviceObject
->CurrentIrp
= Irp
;
1492 /* Check if this is a cancelable packet */
1495 /* Check if the caller requested no cancellation */
1496 if (IoGetDevObjExtension(DeviceObject
)->StartIoFlags
&
1499 /* He did, so remove the cancel routine */
1500 Irp
->CancelRoutine
= NULL
;
1503 /* Release the cancel lock */
1504 IoReleaseCancelSpinLock(OldIrql
);
1507 /* Call the Start I/O function */
1508 DeviceObject
->DriverObject
->DriverStartIo(DeviceObject
, Irp
);
1512 /* The packet was inserted... check if we have a cancel function */
1515 /* Check if the IRP got cancelled */
1519 * Set the cancel IRQL, clear the currnet cancel routine and
1522 Irp
->CancelIrql
= CancelIrql
;
1523 Irp
->CancelRoutine
= NULL
;
1524 CancelFunction(DeviceObject
, Irp
);
1528 /* Otherwise, release the lock */
1529 IoReleaseCancelSpinLock(CancelIrql
);
1534 /* Return back to previous IRQL */
1535 KeLowerIrql(OldIrql
);