xref: /reactos/ntoskrnl/io/iomgr/device.c (revision 0622ce17)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            ntoskrnl/io/iomgr/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)
9  */
10 
11 /* INCLUDES *******************************************************************/
12 
13 #include <ntoskrnl.h>
14 #define NDEBUG
15 #include <debug.h>
16 
17 /* GLOBALS ********************************************************************/
18 
19 ULONG IopDeviceObjectNumber = 0;
20 LIST_ENTRY ShutdownListHead, LastChanceShutdownListHead;
21 KSPIN_LOCK ShutdownListLock;
22 extern LIST_ENTRY IopDiskFileSystemQueueHead;
23 extern LIST_ENTRY IopCdRomFileSystemQueueHead;
24 extern LIST_ENTRY IopTapeFileSystemQueueHead;
25 extern ERESOURCE IopDatabaseResource;
26 
27 /* PRIVATE FUNCTIONS **********************************************************/
28 
29 VOID
30 NTAPI
31 IopReadyDeviceObjects(IN PDRIVER_OBJECT Driver)
32 {
33     PDEVICE_OBJECT DeviceObject;
34     PAGED_CODE();
35 
36     /* Set the driver as initialized */
37     Driver->Flags |= DRVO_INITIALIZED;
38     DeviceObject = Driver->DeviceObject;
39     while (DeviceObject)
40     {
41         /* Set every device as initialized too */
42         DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
43         DeviceObject = DeviceObject->NextDevice;
44     }
45 }
46 
47 VOID
48 NTAPI
49 IopDeleteDevice(IN PVOID ObjectBody)
50 {
51     PDEVICE_OBJECT DeviceObject = ObjectBody;
52     PDEVICE_NODE DeviceNode = IopGetDeviceNode(DeviceObject);
53     PAGED_CODE();
54 
55     /* Cleanup and free the device node */
56     if (DeviceNode)
57         IopFreeDeviceNode(DeviceNode);
58 
59     /* Dereference the driver object, referenced in IoCreateDevice */
60     if (DeviceObject->DriverObject)
61         ObDereferenceObject(DeviceObject->DriverObject);
62 }
63 
64 
65 PDEVICE_OBJECT
66 NTAPI
67 IopAttachDeviceToDeviceStackSafe(IN PDEVICE_OBJECT SourceDevice,
68                                  IN PDEVICE_OBJECT TargetDevice,
69                                  OUT PDEVICE_OBJECT *AttachedToDeviceObject OPTIONAL)
70 {
71     PDEVICE_OBJECT AttachedDevice;
72     PEXTENDED_DEVOBJ_EXTENSION SourceDeviceExtension;
73 
74     /* Get the Attached Device and source extension */
75     AttachedDevice = IoGetAttachedDevice(TargetDevice);
76     SourceDeviceExtension = IoGetDevObjExtension(SourceDevice);
77     ASSERT(SourceDeviceExtension->AttachedTo == NULL);
78 
79     /* Make sure that it's in a correct state */
80     if ((AttachedDevice->Flags & DO_DEVICE_INITIALIZING) ||
81         (IoGetDevObjExtension(AttachedDevice)->ExtensionFlags &
82          (DOE_UNLOAD_PENDING |
83           DOE_DELETE_PENDING |
84           DOE_REMOVE_PENDING |
85           DOE_REMOVE_PROCESSED)))
86     {
87         /* Device was unloading or being removed */
88         AttachedDevice = NULL;
89     }
90     else
91     {
92         /* Update atached device fields */
93         AttachedDevice->AttachedDevice = SourceDevice;
94         AttachedDevice->Spare1++;
95 
96         /* Update the source with the attached data */
97         SourceDevice->StackSize = AttachedDevice->StackSize + 1;
98         SourceDevice->AlignmentRequirement = AttachedDevice->
99                                              AlignmentRequirement;
100         SourceDevice->SectorSize = AttachedDevice->SectorSize;
101 
102         /* Check for pending start flag */
103         if (IoGetDevObjExtension(AttachedDevice)->ExtensionFlags &
104             DOE_START_PENDING)
105         {
106             /* Propagate */
107             IoGetDevObjExtension(SourceDevice)->ExtensionFlags |=
108                 DOE_START_PENDING;
109         }
110 
111         /* Set the attachment in the device extension */
112         SourceDeviceExtension->AttachedTo = AttachedDevice;
113     }
114 
115     /* Return the attached device */
116     if (AttachedToDeviceObject) *AttachedToDeviceObject = AttachedDevice;
117     return AttachedDevice;
118 }
119 
120 VOID
121 NTAPI
122 IoShutdownPnpDevices(VOID)
123 {
124     /* This routine is only used by Driver Verifier to validate shutdown */
125     return;
126 }
127 
128 VOID
129 NTAPI
130 IoShutdownSystem(IN ULONG Phase)
131 {
132     PLIST_ENTRY ListEntry;
133     PDEVICE_OBJECT DeviceObject;
134     PSHUTDOWN_ENTRY ShutdownEntry;
135     IO_STATUS_BLOCK StatusBlock;
136     PIRP Irp;
137     KEVENT Event;
138     NTSTATUS Status;
139 
140     /* Initialize an event to wait on */
141     KeInitializeEvent(&Event, NotificationEvent, FALSE);
142 
143     /* What phase? */
144     if (Phase == 0)
145     {
146         /* Shutdown PnP */
147         IoShutdownPnpDevices();
148 
149         /* Loop first-chance shutdown notifications */
150         ListEntry = ExInterlockedRemoveHeadList(&ShutdownListHead,
151                                                 &ShutdownListLock);
152         while (ListEntry)
153         {
154             /* Get the shutdown entry */
155             ShutdownEntry = CONTAINING_RECORD(ListEntry,
156                                               SHUTDOWN_ENTRY,
157                                               ShutdownList);
158 
159             /* Get the attached device */
160             DeviceObject = IoGetAttachedDevice(ShutdownEntry->DeviceObject);
161 
162             /* Build the shutdown IRP and call the driver */
163             Irp = IoBuildSynchronousFsdRequest(IRP_MJ_SHUTDOWN,
164                                                DeviceObject,
165                                                NULL,
166                                                0,
167                                                NULL,
168                                                &Event,
169                                                &StatusBlock);
170             if (Irp)
171             {
172                 Status = IoCallDriver(DeviceObject, Irp);
173                 if (Status == STATUS_PENDING)
174                 {
175                     /* Wait on the driver */
176                     KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
177                 }
178             }
179 
180             /* Remove the flag */
181             ShutdownEntry->DeviceObject->Flags &= ~DO_SHUTDOWN_REGISTERED;
182 
183             /* Get rid of our reference to it */
184             ObDereferenceObject(ShutdownEntry->DeviceObject);
185 
186             /* Free the shutdown entry and reset the event */
187             ExFreePoolWithTag(ShutdownEntry, TAG_SHUTDOWN_ENTRY);
188             KeClearEvent(&Event);
189 
190             /* Go to the next entry */
191             ListEntry = ExInterlockedRemoveHeadList(&ShutdownListHead,
192                                                     &ShutdownListLock);
193          }
194     }
195     else if (Phase == 1)
196     {
197         /* Acquire resource forever */
198         ExAcquireResourceExclusiveLite(&IopDatabaseResource, TRUE);
199 
200         /* Shutdown disk file systems */
201         IopShutdownBaseFileSystems(&IopDiskFileSystemQueueHead);
202 
203         /* Shutdown cdrom file systems */
204         IopShutdownBaseFileSystems(&IopCdRomFileSystemQueueHead);
205 
206         /* Shutdown tape filesystems */
207         IopShutdownBaseFileSystems(&IopTapeFileSystemQueueHead);
208 
209         /* Loop last-chance shutdown notifications */
210         ListEntry = ExInterlockedRemoveHeadList(&LastChanceShutdownListHead,
211                                                 &ShutdownListLock);
212         while (ListEntry)
213         {
214             /* Get the shutdown entry */
215             ShutdownEntry = CONTAINING_RECORD(ListEntry,
216                                               SHUTDOWN_ENTRY,
217                                               ShutdownList);
218 
219             /* Get the attached device */
220             DeviceObject = IoGetAttachedDevice(ShutdownEntry->DeviceObject);
221 
222             /* Build the shutdown IRP and call the driver */
223             Irp = IoBuildSynchronousFsdRequest(IRP_MJ_SHUTDOWN,
224                                                DeviceObject,
225                                                NULL,
226                                                0,
227                                                NULL,
228                                                &Event,
229                                                &StatusBlock);
230             if (Irp)
231             {
232                 Status = IoCallDriver(DeviceObject, Irp);
233                 if (Status == STATUS_PENDING)
234                 {
235                     /* Wait on the driver */
236                     KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
237                 }
238             }
239 
240             /* Remove the flag */
241             ShutdownEntry->DeviceObject->Flags &= ~DO_SHUTDOWN_REGISTERED;
242 
243             /* Get rid of our reference to it */
244             ObDereferenceObject(ShutdownEntry->DeviceObject);
245 
246             /* Free the shutdown entry and reset the event */
247             ExFreePoolWithTag(ShutdownEntry, TAG_SHUTDOWN_ENTRY);
248             KeClearEvent(&Event);
249 
250             /* Go to the next entry */
251             ListEntry = ExInterlockedRemoveHeadList(&LastChanceShutdownListHead,
252                                                     &ShutdownListLock);
253          }
254 
255     }
256 }
257 
258 NTSTATUS
259 NTAPI
260 IopGetDeviceObjectPointer(IN PUNICODE_STRING ObjectName,
261                           IN ACCESS_MASK DesiredAccess,
262                           OUT PFILE_OBJECT *FileObject,
263                           OUT PDEVICE_OBJECT *DeviceObject,
264                           IN ULONG AttachFlag)
265 {
266     OBJECT_ATTRIBUTES ObjectAttributes;
267     IO_STATUS_BLOCK StatusBlock;
268     PFILE_OBJECT LocalFileObject;
269     HANDLE FileHandle;
270     NTSTATUS Status;
271 
272     /* Open the Device */
273     InitializeObjectAttributes(&ObjectAttributes,
274                                ObjectName,
275                                OBJ_KERNEL_HANDLE,
276                                NULL,
277                                NULL);
278     Status = ZwOpenFile(&FileHandle,
279                         DesiredAccess,
280                         &ObjectAttributes,
281                         &StatusBlock,
282                         0,
283                         FILE_NON_DIRECTORY_FILE | AttachFlag);
284     if (!NT_SUCCESS(Status)) return Status;
285 
286     /* Get File Object */
287     Status = ObReferenceObjectByHandle(FileHandle,
288                                        0,
289                                        IoFileObjectType,
290                                        KernelMode,
291                                        (PVOID*)&LocalFileObject,
292                                        NULL);
293     if (NT_SUCCESS(Status))
294     {
295         /* Return the requested data */
296         *DeviceObject = IoGetRelatedDeviceObject(LocalFileObject);
297         *FileObject = LocalFileObject;
298     }
299 
300     /* Close the handle */
301     ZwClose(FileHandle);
302 
303     return Status;
304 }
305 
306 PDEVICE_OBJECT
307 NTAPI
308 IopGetLowestDevice(IN PDEVICE_OBJECT DeviceObject)
309 {
310     PDEVICE_OBJECT LowestDevice;
311     PEXTENDED_DEVOBJ_EXTENSION DeviceExtension;
312 
313     /* Get the current device and its extension */
314     LowestDevice = DeviceObject;
315     DeviceExtension = IoGetDevObjExtension(LowestDevice);
316 
317     /* Keep looping as long as we're attached */
318     while (DeviceExtension->AttachedTo)
319     {
320         /* Get the lowest device and its extension */
321         LowestDevice = DeviceExtension->AttachedTo;
322         DeviceExtension = IoGetDevObjExtension(LowestDevice);
323     }
324 
325     /* Return the lowest device */
326     return LowestDevice;
327 }
328 
329 VOID
330 NTAPI
331 IopEditDeviceList(IN PDRIVER_OBJECT DriverObject,
332                   IN PDEVICE_OBJECT DeviceObject,
333                   IN IOP_DEVICE_LIST_OPERATION Type)
334 {
335     PDEVICE_OBJECT Previous;
336     KIRQL OldIrql;
337 
338     /* Lock the Device list while we edit it */
339     OldIrql = KeAcquireQueuedSpinLock(LockQueueIoDatabaseLock);
340 
341     /* Check the type of operation */
342     if (Type == IopRemove)
343     {
344         /* Get the current device and check if it's the current one */
345         Previous = DeviceObject->DriverObject->DeviceObject;
346         if (Previous == DeviceObject)
347         {
348             /* It is, simply unlink this one directly */
349             DeviceObject->DriverObject->DeviceObject =
350                 DeviceObject->NextDevice;
351         }
352         else
353         {
354             /* It's not, so loop until we find the device */
355             while (Previous->NextDevice != DeviceObject)
356             {
357                 /* Not this one, keep moving */
358                 if (!Previous->NextDevice)
359                 {
360                     DPRINT1("Failed to remove PDO %p (not found)\n",
361                             DeviceObject);
362 
363                     ASSERT(FALSE);
364                     KeReleaseQueuedSpinLock(LockQueueIoDatabaseLock, OldIrql);
365                     return;
366                 }
367                 Previous = Previous->NextDevice;
368             }
369 
370             /* We found it, now unlink us */
371             Previous->NextDevice = DeviceObject->NextDevice;
372         }
373     }
374     else
375     {
376         /* Link the device object and the driver object */
377         DeviceObject->NextDevice = DriverObject->DeviceObject;
378         DriverObject->DeviceObject = DeviceObject;
379     }
380 
381     /* Release the device list lock */
382     KeReleaseQueuedSpinLock(LockQueueIoDatabaseLock, OldIrql);
383 }
384 
385 VOID
386 NTAPI
387 IopUnloadDevice(IN PDEVICE_OBJECT DeviceObject)
388 {
389     PDRIVER_OBJECT DriverObject = DeviceObject->DriverObject;
390     PEXTENDED_DEVOBJ_EXTENSION ThisExtension = IoGetDevObjExtension(DeviceObject);
391 
392     /* Check if deletion is pending */
393     if (ThisExtension->ExtensionFlags & DOE_DELETE_PENDING)
394     {
395         if (DeviceObject->AttachedDevice)
396         {
397             DPRINT("Device object is in the middle of a device stack\n");
398             return;
399         }
400 
401         if (DeviceObject->ReferenceCount)
402         {
403             DPRINT("Device object still has %d references\n", DeviceObject->ReferenceCount);
404             return;
405         }
406 
407         /* Check if we have a Security Descriptor */
408         if (DeviceObject->SecurityDescriptor)
409         {
410             /* Dereference it */
411             ObDereferenceSecurityDescriptor(DeviceObject->SecurityDescriptor, 1);
412         }
413 
414         /* Remove the device from the list */
415         IopEditDeviceList(DeviceObject->DriverObject, DeviceObject, IopRemove);
416 
417         /* Dereference the keep-alive */
418         ObDereferenceObject(DeviceObject);
419     }
420 
421     /* We can't unload a non-PnP driver here */
422     if (DriverObject->Flags & DRVO_LEGACY_DRIVER)
423     {
424         DPRINT("Not a PnP driver! '%wZ' will not be unloaded!\n", &DriverObject->DriverName);
425         return;
426     }
427 
428     /* Return if we've already called unload (maybe we're in it?) */
429     if (DriverObject->Flags & DRVO_UNLOAD_INVOKED) return;
430 
431     /* We can't unload unless there's an unload handler */
432     if (!DriverObject->DriverUnload)
433     {
434         DPRINT1("No DriverUnload function on PnP driver! '%wZ' will not be unloaded!\n", &DriverObject->DriverName);
435         return;
436     }
437 
438     /* Bail if there are still devices present */
439     if (DriverObject->DeviceObject)
440     {
441         DPRINT("Devices still present! '%wZ' will not be unloaded!\n", &DriverObject->DriverName);
442         return;
443     }
444 
445     DPRINT1("Unloading driver '%wZ' (automatic)\n", &DriverObject->DriverName);
446 
447     /* Set the unload invoked flag */
448     DriverObject->Flags |= DRVO_UNLOAD_INVOKED;
449 
450     /* Unload it */
451     DriverObject->DriverUnload(DriverObject);
452 
453     /* Make object temporary so it can be deleted */
454     ObMakeTemporaryObject(DriverObject);
455 }
456 
457 VOID
458 NTAPI
459 IopDereferenceDeviceObject(IN PDEVICE_OBJECT DeviceObject,
460                            IN BOOLEAN ForceUnload)
461 {
462     /* Sanity check */
463     ASSERT(DeviceObject->ReferenceCount);
464 
465     /* Dereference the device */
466     InterlockedDecrement(&DeviceObject->ReferenceCount);
467 
468     /*
469      * Check if we can unload it and it's safe to unload (or if we're forcing
470      * an unload, which is OK too).
471      */
472     ASSERT(!ForceUnload);
473     if (!(DeviceObject->ReferenceCount) &&
474         (IoGetDevObjExtension(DeviceObject)->ExtensionFlags & DOE_DELETE_PENDING))
475     {
476         /* Unload it */
477         IopUnloadDevice(DeviceObject);
478     }
479 }
480 
481 VOID
482 NTAPI
483 IopStartNextPacketByKey(IN PDEVICE_OBJECT DeviceObject,
484                         IN BOOLEAN Cancelable,
485                         IN ULONG Key)
486 {
487     PKDEVICE_QUEUE_ENTRY Entry;
488     PIRP Irp;
489     KIRQL OldIrql;
490 
491     /* Acquire the cancel lock if this is cancelable */
492     if (Cancelable) IoAcquireCancelSpinLock(&OldIrql);
493 
494     /* Clear the current IRP */
495     DeviceObject->CurrentIrp = NULL;
496 
497     /* Remove an entry from the queue */
498     Entry = KeRemoveByKeyDeviceQueue(&DeviceObject->DeviceQueue, Key);
499     if (Entry)
500     {
501         /* Get the IRP and set it */
502         Irp = CONTAINING_RECORD(Entry, IRP, Tail.Overlay.DeviceQueueEntry);
503         DeviceObject->CurrentIrp = Irp;
504 
505         /* Check if this is a cancelable packet */
506         if (Cancelable)
507         {
508             /* Check if the caller requested no cancellation */
509             if (IoGetDevObjExtension(DeviceObject)->StartIoFlags &
510                 DOE_SIO_NO_CANCEL)
511             {
512                 /* He did, so remove the cancel routine */
513                 Irp->CancelRoutine = NULL;
514             }
515 
516             /* Release the cancel lock */
517             IoReleaseCancelSpinLock(OldIrql);
518         }
519 
520         /* Call the Start I/O Routine */
521         DeviceObject->DriverObject->DriverStartIo(DeviceObject, Irp);
522     }
523     else
524     {
525         /* Otherwise, release the cancel lock if we had acquired it */
526         if (Cancelable) IoReleaseCancelSpinLock(OldIrql);
527     }
528 }
529 
530 VOID
531 NTAPI
532 IopStartNextPacket(IN PDEVICE_OBJECT DeviceObject,
533                    IN BOOLEAN Cancelable)
534 {
535     PKDEVICE_QUEUE_ENTRY Entry;
536     PIRP Irp;
537     KIRQL OldIrql;
538 
539     /* Acquire the cancel lock if this is cancelable */
540     if (Cancelable) IoAcquireCancelSpinLock(&OldIrql);
541 
542     /* Clear the current IRP */
543     DeviceObject->CurrentIrp = NULL;
544 
545     /* Remove an entry from the queue */
546     Entry = KeRemoveDeviceQueue(&DeviceObject->DeviceQueue);
547     if (Entry)
548     {
549         /* Get the IRP and set it */
550         Irp = CONTAINING_RECORD(Entry, IRP, Tail.Overlay.DeviceQueueEntry);
551         DeviceObject->CurrentIrp = Irp;
552 
553         /* Check if this is a cancelable packet */
554         if (Cancelable)
555         {
556             /* Check if the caller requested no cancellation */
557             if (IoGetDevObjExtension(DeviceObject)->StartIoFlags &
558                 DOE_SIO_NO_CANCEL)
559             {
560                 /* He did, so remove the cancel routine */
561                 Irp->CancelRoutine = NULL;
562             }
563 
564             /* Release the cancel lock */
565             IoReleaseCancelSpinLock(OldIrql);
566         }
567 
568         /* Call the Start I/O Routine */
569         DeviceObject->DriverObject->DriverStartIo(DeviceObject, Irp);
570     }
571     else
572     {
573         /* Otherwise, release the cancel lock if we had acquired it */
574         if (Cancelable) IoReleaseCancelSpinLock(OldIrql);
575     }
576 }
577 
578 VOID
579 NTAPI
580 IopStartNextPacketByKeyEx(IN PDEVICE_OBJECT DeviceObject,
581                           IN ULONG Key,
582                           IN ULONG Flags)
583 {
584     PEXTENDED_DEVOBJ_EXTENSION DeviceExtension;
585     ULONG CurrentKey = Key;
586     ULONG CurrentFlags = Flags;
587 
588     /* Get the device extension and start the packet loop */
589     DeviceExtension = IoGetDevObjExtension(DeviceObject);
590     while (TRUE)
591     {
592         /* Increase the count */
593         if (InterlockedIncrement(&DeviceExtension->StartIoCount) > 1)
594         {
595             /*
596              * We've already called the routine once...
597              * All we have to do is save the key and add the new flags
598              */
599             DeviceExtension->StartIoFlags |= CurrentFlags;
600             DeviceExtension->StartIoKey = CurrentKey;
601         }
602         else
603         {
604             /* Mask out the current packet flags and key */
605             DeviceExtension->StartIoFlags &= ~(DOE_SIO_WITH_KEY |
606                                                DOE_SIO_NO_KEY |
607                                                DOE_SIO_CANCELABLE);
608             DeviceExtension->StartIoKey = 0;
609 
610             /* Check if this is a packet start with key */
611             if (Flags & DOE_SIO_WITH_KEY)
612             {
613                 /* Start the packet with a key */
614                 IopStartNextPacketByKey(DeviceObject,
615                                         (Flags & DOE_SIO_CANCELABLE) ?
616                                         TRUE : FALSE,
617                                         CurrentKey);
618             }
619             else if (Flags & DOE_SIO_NO_KEY)
620             {
621                 /* Start the packet */
622                 IopStartNextPacket(DeviceObject,
623                                    (Flags & DOE_SIO_CANCELABLE) ?
624                                    TRUE : FALSE);
625             }
626         }
627 
628         /* Decrease the Start I/O count and check if it's 0 now */
629         if (!InterlockedDecrement(&DeviceExtension->StartIoCount))
630         {
631             /* Get the current active key and flags */
632             CurrentKey = DeviceExtension->StartIoKey;
633             CurrentFlags = DeviceExtension->StartIoFlags & (DOE_SIO_WITH_KEY |
634                                                             DOE_SIO_NO_KEY |
635                                                             DOE_SIO_CANCELABLE);
636 
637             /* Check if we should still loop */
638             if (!(CurrentFlags & (DOE_SIO_WITH_KEY | DOE_SIO_NO_KEY))) break;
639         }
640         else
641         {
642             /* There are still Start I/Os active, so quit this loop */
643             break;
644         }
645     }
646 }
647 
648 NTSTATUS
649 NTAPI
650 IopGetRelatedTargetDevice(IN PFILE_OBJECT FileObject,
651                           OUT PDEVICE_NODE *DeviceNode)
652 {
653     NTSTATUS Status;
654     IO_STACK_LOCATION Stack = {0};
655     PDEVICE_RELATIONS DeviceRelations;
656     PDEVICE_OBJECT DeviceObject = NULL;
657 
658     ASSERT(FileObject);
659 
660     /* Get DeviceObject related to given FileObject */
661     DeviceObject = IoGetRelatedDeviceObject(FileObject);
662     if (!DeviceObject) return STATUS_NO_SUCH_DEVICE;
663 
664     /* Define input parameters */
665     Stack.MajorFunction = IRP_MJ_PNP;
666     Stack.MinorFunction = IRP_MN_QUERY_DEVICE_RELATIONS;
667     Stack.Parameters.QueryDeviceRelations.Type = TargetDeviceRelation;
668     Stack.FileObject = FileObject;
669 
670     /* Call the driver to query all relations (IRP_MJ_PNP) */
671     Status = IopSynchronousCall(DeviceObject,
672                                 &Stack,
673                                 (PVOID)&DeviceRelations);
674     if (!NT_SUCCESS(Status)) return Status;
675 
676     /* Make sure it's not NULL and contains only one object */
677     ASSERT(DeviceRelations);
678     ASSERT(DeviceRelations->Count == 1);
679 
680     /* Finally get the device node */
681     *DeviceNode = IopGetDeviceNode(DeviceRelations->Objects[0]);
682     if (!*DeviceNode) Status = STATUS_NO_SUCH_DEVICE;
683 
684     /* Free the DEVICE_RELATIONS structure, it's not needed anymore */
685     ExFreePool(DeviceRelations);
686 
687     return Status;
688 }
689 
690 /* PUBLIC FUNCTIONS ***********************************************************/
691 
692 /*
693  * IoAttachDevice
694  *
695  * Layers a device over the highest device in a device stack.
696  *
697  * Parameters
698  *    SourceDevice
699  *       Device to be attached.
700  *
701  *    TargetDevice
702  *       Name of the target device.
703  *
704  *    AttachedDevice
705  *       Caller storage for the device attached to.
706  *
707  * Status
708  *    @implemented
709  */
710 NTSTATUS
711 NTAPI
712 IoAttachDevice(PDEVICE_OBJECT SourceDevice,
713                PUNICODE_STRING TargetDeviceName,
714                PDEVICE_OBJECT *AttachedDevice)
715 {
716    NTSTATUS Status;
717    PFILE_OBJECT FileObject = NULL;
718    PDEVICE_OBJECT TargetDevice = NULL;
719 
720     /* Call the helper routine for an attach operation */
721     Status = IopGetDeviceObjectPointer(TargetDeviceName,
722                                        FILE_READ_ATTRIBUTES,
723                                        &FileObject,
724                                        &TargetDevice,
725                                        IO_ATTACH_DEVICE_API);
726     if (!NT_SUCCESS(Status)) return Status;
727 
728     /* Attach the device */
729     Status = IoAttachDeviceToDeviceStackSafe(SourceDevice,
730                                              TargetDevice,
731                                              AttachedDevice);
732 
733     /* Dereference it */
734     ObDereferenceObject(FileObject);
735     return Status;
736 }
737 
738 /*
739  * IoAttachDeviceByPointer
740  *
741  * Status
742  *    @implemented
743  */
744 NTSTATUS
745 NTAPI
746 IoAttachDeviceByPointer(IN PDEVICE_OBJECT SourceDevice,
747                         IN PDEVICE_OBJECT TargetDevice)
748 {
749     PDEVICE_OBJECT AttachedDevice;
750     NTSTATUS Status = STATUS_SUCCESS;
751 
752     /* Do the Attach */
753     AttachedDevice = IoAttachDeviceToDeviceStack(SourceDevice, TargetDevice);
754     if (!AttachedDevice) Status = STATUS_NO_SUCH_DEVICE;
755 
756     /* Return the status */
757     return Status;
758 }
759 
760 /*
761  * @implemented
762  */
763 PDEVICE_OBJECT
764 NTAPI
765 IoAttachDeviceToDeviceStack(IN PDEVICE_OBJECT SourceDevice,
766                             IN PDEVICE_OBJECT TargetDevice)
767 {
768     /* Attach it safely */
769     return IopAttachDeviceToDeviceStackSafe(SourceDevice,
770                                             TargetDevice,
771                                             NULL);
772 }
773 
774 /*
775  * @implemented
776  */
777 NTSTATUS
778 NTAPI
779 IoAttachDeviceToDeviceStackSafe(IN PDEVICE_OBJECT SourceDevice,
780                                 IN PDEVICE_OBJECT TargetDevice,
781                                 IN OUT PDEVICE_OBJECT *AttachedToDeviceObject)
782 {
783     /* Call the internal function */
784     if (!IopAttachDeviceToDeviceStackSafe(SourceDevice,
785                                           TargetDevice,
786                                           AttachedToDeviceObject))
787     {
788         /* Nothing found */
789         return STATUS_NO_SUCH_DEVICE;
790     }
791 
792     /* Success! */
793     return STATUS_SUCCESS;
794 }
795 
796 /*
797  * IoCreateDevice
798  *
799  * Allocates memory for and intializes a device object for use for
800  * a driver.
801  *
802  * Parameters
803  *    DriverObject
804  *       Driver object passed by IO Manager when the driver was loaded.
805  *
806  *    DeviceExtensionSize
807  *       Number of bytes for the device extension.
808  *
809  *    DeviceName
810  *       Unicode name of device.
811  *
812  *    DeviceType
813  *       Device type of the new device.
814  *
815  *    DeviceCharacteristics
816  *       Bit mask of device characteristics.
817  *
818  *    Exclusive
819  *       TRUE if only one thread can access the device at a time.
820  *
821  *    DeviceObject
822  *       On successful return this parameter is filled by pointer to
823  *       allocated device object.
824  *
825  * Status
826  *    @implemented
827  */
828 NTSTATUS
829 NTAPI
830 IoCreateDevice(IN PDRIVER_OBJECT DriverObject,
831                IN ULONG DeviceExtensionSize,
832                IN PUNICODE_STRING DeviceName,
833                IN DEVICE_TYPE DeviceType,
834                IN ULONG DeviceCharacteristics,
835                IN BOOLEAN Exclusive,
836                OUT PDEVICE_OBJECT *DeviceObject)
837 {
838     WCHAR AutoNameBuffer[20];
839     UNICODE_STRING AutoName;
840     PDEVICE_OBJECT CreatedDeviceObject;
841     PDEVOBJ_EXTENSION DeviceObjectExtension;
842     OBJECT_ATTRIBUTES ObjectAttributes;
843     NTSTATUS Status;
844     ULONG AlignedDeviceExtensionSize;
845     ULONG TotalSize;
846     HANDLE TempHandle;
847     PAGED_CODE();
848 
849     /* Check if we have to generate a name */
850     if (DeviceCharacteristics & FILE_AUTOGENERATED_DEVICE_NAME)
851     {
852         /* Generate it */
853         swprintf(AutoNameBuffer,
854                  L"\\Device\\%08lx",
855                  InterlockedIncrementUL(&IopDeviceObjectNumber));
856 
857         /* Initialize the name */
858         RtlInitUnicodeString(&AutoName, AutoNameBuffer);
859         DeviceName = &AutoName;
860     }
861 
862     /* Initialize the Object Attributes */
863     InitializeObjectAttributes(&ObjectAttributes,
864                                DeviceName,
865                                OBJ_KERNEL_HANDLE,
866                                NULL,
867                                SePublicOpenUnrestrictedSd);
868 
869     /* Honor exclusive flag */
870     if (Exclusive) ObjectAttributes.Attributes |= OBJ_EXCLUSIVE;
871 
872     /* Create a permanent object for named devices */
873     if (DeviceName) ObjectAttributes.Attributes |= OBJ_PERMANENT;
874 
875     /* Align the Extension Size to 8-bytes */
876     AlignedDeviceExtensionSize = (DeviceExtensionSize + 7) &~ 7;
877 
878     /* Total Size */
879     TotalSize = AlignedDeviceExtensionSize +
880                 sizeof(DEVICE_OBJECT) +
881                 sizeof(EXTENDED_DEVOBJ_EXTENSION);
882 
883     /* Create the Device Object */
884     *DeviceObject = NULL;
885     Status = ObCreateObject(KernelMode,
886                             IoDeviceObjectType,
887                             &ObjectAttributes,
888                             KernelMode,
889                             NULL,
890                             TotalSize,
891                             0,
892                             0,
893                             (PVOID*)&CreatedDeviceObject);
894     if (!NT_SUCCESS(Status)) return Status;
895 
896     /* Clear the whole Object and extension so we don't null stuff manually */
897     RtlZeroMemory(CreatedDeviceObject, TotalSize);
898 
899     /*
900      * Setup the Type and Size. Note that we don't use the aligned size,
901      * because that's only padding for the DevObjExt and not part of the Object.
902      */
903     CreatedDeviceObject->Type = IO_TYPE_DEVICE;
904     CreatedDeviceObject->Size = sizeof(DEVICE_OBJECT) + (USHORT)DeviceExtensionSize;
905 
906     /* The kernel extension is after the driver internal extension */
907     DeviceObjectExtension = (PDEVOBJ_EXTENSION)
908                             ((ULONG_PTR)(CreatedDeviceObject + 1) +
909                              AlignedDeviceExtensionSize);
910 
911     /* Set the Type and Size. Question: why is Size 0 on Windows? */
912     DeviceObjectExtension->Type = IO_TYPE_DEVICE_OBJECT_EXTENSION;
913     DeviceObjectExtension->Size = 0;
914 
915     /* Initialize with Power Manager */
916     PoInitializeDeviceObject(DeviceObjectExtension);
917 
918     /* Link the Object and Extension */
919     DeviceObjectExtension->DeviceObject = CreatedDeviceObject;
920     CreatedDeviceObject->DeviceObjectExtension = DeviceObjectExtension;
921 
922     /* Set Device Object Data */
923     CreatedDeviceObject->DeviceType = DeviceType;
924     CreatedDeviceObject->Characteristics = DeviceCharacteristics;
925     CreatedDeviceObject->DeviceExtension = DeviceExtensionSize ?
926                                            CreatedDeviceObject + 1 :
927                                            NULL;
928     CreatedDeviceObject->StackSize = 1;
929     CreatedDeviceObject->AlignmentRequirement = 0;
930 
931     /* Set the Flags */
932     CreatedDeviceObject->Flags = DO_DEVICE_INITIALIZING;
933     if (Exclusive) CreatedDeviceObject->Flags |= DO_EXCLUSIVE;
934     if (DeviceName) CreatedDeviceObject->Flags |= DO_DEVICE_HAS_NAME;
935 
936     /* Attach a Vpb for Disks and Tapes, and create the Device Lock */
937     if ((CreatedDeviceObject->DeviceType == FILE_DEVICE_DISK) ||
938         (CreatedDeviceObject->DeviceType == FILE_DEVICE_VIRTUAL_DISK) ||
939         (CreatedDeviceObject->DeviceType == FILE_DEVICE_CD_ROM) ||
940         (CreatedDeviceObject->DeviceType == FILE_DEVICE_TAPE))
941     {
942         /* Create Vpb */
943         Status = IopCreateVpb(CreatedDeviceObject);
944         if (!NT_SUCCESS(Status))
945         {
946             /* Dereference the device object and fail */
947             ObDereferenceObject(CreatedDeviceObject);
948             return Status;
949         }
950 
951         /* Initialize Lock Event */
952         KeInitializeEvent(&CreatedDeviceObject->DeviceLock,
953                           SynchronizationEvent,
954                           TRUE);
955     }
956 
957     /* Set the right Sector Size */
958     switch (DeviceType)
959     {
960         /* All disk systems */
961         case FILE_DEVICE_DISK_FILE_SYSTEM:
962         case FILE_DEVICE_DISK:
963         case FILE_DEVICE_VIRTUAL_DISK:
964 
965             /* The default is 512 bytes */
966             CreatedDeviceObject->SectorSize  = 512;
967             break;
968 
969         /* CD-ROM file systems */
970         case FILE_DEVICE_CD_ROM_FILE_SYSTEM:
971 
972             /* The default is 2048 bytes */
973             CreatedDeviceObject->SectorSize = 2048;
974     }
975 
976     /* Create the Device Queue */
977     if ((CreatedDeviceObject->DeviceType == FILE_DEVICE_DISK_FILE_SYSTEM) ||
978         (CreatedDeviceObject->DeviceType == FILE_DEVICE_FILE_SYSTEM) ||
979         (CreatedDeviceObject->DeviceType == FILE_DEVICE_CD_ROM_FILE_SYSTEM) ||
980         (CreatedDeviceObject->DeviceType == FILE_DEVICE_NETWORK_FILE_SYSTEM) ||
981         (CreatedDeviceObject->DeviceType == FILE_DEVICE_TAPE_FILE_SYSTEM))
982     {
983         /* Simple FS Devices, they don't need a real Device Queue */
984         InitializeListHead(&CreatedDeviceObject->Queue.ListEntry);
985     }
986     else
987     {
988         /* An actual Device, initialize its DQ */
989         KeInitializeDeviceQueue(&CreatedDeviceObject->DeviceQueue);
990     }
991 
992     /* Insert the Object */
993     Status = ObInsertObject(CreatedDeviceObject,
994                             NULL,
995                             FILE_READ_DATA | FILE_WRITE_DATA,
996                             1,
997                             (PVOID*)&CreatedDeviceObject,
998                             &TempHandle);
999     if (!NT_SUCCESS(Status)) return Status;
1000 
1001     /* Now do the final linking */
1002     ObReferenceObject(DriverObject);
1003     ASSERT((DriverObject->Flags & DRVO_UNLOAD_INVOKED) == 0);
1004     CreatedDeviceObject->DriverObject = DriverObject;
1005     IopEditDeviceList(DriverObject, CreatedDeviceObject, IopAdd);
1006 
1007     /* Link with the power manager */
1008     if (CreatedDeviceObject->Vpb) PoVolumeDevice(CreatedDeviceObject);
1009 
1010     /* Close the temporary handle and return to caller */
1011     ObCloseHandle(TempHandle, KernelMode);
1012     *DeviceObject = CreatedDeviceObject;
1013     return STATUS_SUCCESS;
1014 }
1015 
1016 /*
1017  * IoDeleteDevice
1018  *
1019  * Status
1020  *    @implemented
1021  */
1022 VOID
1023 NTAPI
1024 IoDeleteDevice(IN PDEVICE_OBJECT DeviceObject)
1025 {
1026     PIO_TIMER Timer;
1027 
1028     /* Check if the device is registered for shutdown notifications */
1029     if (DeviceObject->Flags & DO_SHUTDOWN_REGISTERED)
1030     {
1031         /* Call the shutdown notifications */
1032         IoUnregisterShutdownNotification(DeviceObject);
1033     }
1034 
1035     /* Check if it has a timer */
1036     Timer = DeviceObject->Timer;
1037     if (Timer)
1038     {
1039         /* Remove it and free it */
1040         IopRemoveTimerFromTimerList(Timer);
1041         ExFreePoolWithTag(Timer, TAG_IO_TIMER);
1042     }
1043 
1044     /* Check if the device has a name */
1045     if (DeviceObject->Flags & DO_DEVICE_HAS_NAME)
1046     {
1047         /* It does, make it temporary so we can remove it */
1048         ObMakeTemporaryObject(DeviceObject);
1049     }
1050 
1051     /* Set the pending delete flag */
1052     IoGetDevObjExtension(DeviceObject)->ExtensionFlags |= DOE_DELETE_PENDING;
1053 
1054     /* Unlink with the power manager */
1055     if (DeviceObject->Vpb) PoRemoveVolumeDevice(DeviceObject);
1056 
1057     /* Check if the device object can be unloaded */
1058     if (!DeviceObject->ReferenceCount) IopUnloadDevice(DeviceObject);
1059 }
1060 
1061 /*
1062  * IoDetachDevice
1063  *
1064  * Status
1065  *    @implemented
1066  */
1067 VOID
1068 NTAPI
1069 IoDetachDevice(IN PDEVICE_OBJECT TargetDevice)
1070 {
1071     PEXTENDED_DEVOBJ_EXTENSION DeviceExtension;
1072 
1073     /* Sanity check */
1074     DeviceExtension = IoGetDevObjExtension(TargetDevice->AttachedDevice);
1075     ASSERT(DeviceExtension->AttachedTo == TargetDevice);
1076 
1077     /* Remove the attachment */
1078     DeviceExtension->AttachedTo = NULL;
1079     TargetDevice->AttachedDevice = NULL;
1080 
1081     /* Check if it's ok to delete this device */
1082     if ((IoGetDevObjExtension(TargetDevice)->ExtensionFlags & DOE_DELETE_PENDING) &&
1083         !(TargetDevice->ReferenceCount))
1084     {
1085         /* It is, do it */
1086         IopUnloadDevice(TargetDevice);
1087     }
1088 }
1089 
1090 /*
1091  * @implemented
1092  */
1093 NTSTATUS
1094 NTAPI
1095 IoEnumerateDeviceObjectList(IN  PDRIVER_OBJECT DriverObject,
1096                             IN  PDEVICE_OBJECT *DeviceObjectList,
1097                             IN  ULONG DeviceObjectListSize,
1098                             OUT PULONG ActualNumberDeviceObjects)
1099 {
1100     ULONG ActualDevices = 1;
1101     PDEVICE_OBJECT CurrentDevice = DriverObject->DeviceObject;
1102     KIRQL OldIrql;
1103 
1104     /* Lock the Device list while we enumerate it */
1105     OldIrql = KeAcquireQueuedSpinLock(LockQueueIoDatabaseLock);
1106 
1107     /* Find out how many devices we'll enumerate */
1108     while ((CurrentDevice = CurrentDevice->NextDevice)) ActualDevices++;
1109 
1110     /* Go back to the first */
1111     CurrentDevice = DriverObject->DeviceObject;
1112 
1113     /* Start by at least returning this */
1114     *ActualNumberDeviceObjects = ActualDevices;
1115 
1116     /* Check if we can support so many */
1117     if ((ActualDevices * sizeof(PDEVICE_OBJECT)) > DeviceObjectListSize)
1118     {
1119         /* Fail because the buffer was too small */
1120         KeReleaseQueuedSpinLock(LockQueueIoDatabaseLock, OldIrql);
1121         return STATUS_BUFFER_TOO_SMALL;
1122     }
1123 
1124     /* Check if the caller wanted the device list */
1125     if (DeviceObjectList)
1126     {
1127         /* Loop through all the devices */
1128         while (ActualDevices)
1129         {
1130             /* Reference each Device */
1131             ObReferenceObject(CurrentDevice);
1132 
1133             /* Add it to the list */
1134             *DeviceObjectList = CurrentDevice;
1135 
1136             /* Go to the next one */
1137             CurrentDevice = CurrentDevice->NextDevice;
1138             ActualDevices--;
1139             DeviceObjectList++;
1140         }
1141     }
1142 
1143     /* Release the device list lock */
1144     KeReleaseQueuedSpinLock(LockQueueIoDatabaseLock, OldIrql);
1145 
1146     /* Return the status */
1147     return STATUS_SUCCESS;
1148 }
1149 
1150 /*
1151  * IoGetAttachedDevice
1152  *
1153  * Status
1154  *    @implemented
1155  */
1156 PDEVICE_OBJECT
1157 NTAPI
1158 IoGetAttachedDevice(PDEVICE_OBJECT DeviceObject)
1159 {
1160     /* Get the last attached device */
1161     while (DeviceObject->AttachedDevice)
1162     {
1163         /* Move to the next one */
1164         DeviceObject = DeviceObject->AttachedDevice;
1165     }
1166 
1167     /* Return it */
1168     return DeviceObject;
1169 }
1170 
1171 /*
1172  * IoGetAttachedDeviceReference
1173  *
1174  * Status
1175  *    @implemented
1176  */
1177 PDEVICE_OBJECT
1178 NTAPI
1179 IoGetAttachedDeviceReference(PDEVICE_OBJECT DeviceObject)
1180 {
1181     /* Reference the Attached Device */
1182     DeviceObject = IoGetAttachedDevice(DeviceObject);
1183     ObReferenceObject(DeviceObject);
1184     return DeviceObject;
1185 }
1186 
1187 /*
1188  * @implemented
1189  */
1190 PDEVICE_OBJECT
1191 NTAPI
1192 IoGetDeviceAttachmentBaseRef(IN PDEVICE_OBJECT DeviceObject)
1193 {
1194     /* Reference the lowest attached device */
1195     DeviceObject = IopGetLowestDevice(DeviceObject);
1196     ObReferenceObject(DeviceObject);
1197     return DeviceObject;
1198 }
1199 
1200 /*
1201  * IoGetDeviceObjectPointer
1202  *
1203  * Status
1204  *    @implemented
1205  */
1206 NTSTATUS
1207 NTAPI
1208 IoGetDeviceObjectPointer(IN PUNICODE_STRING ObjectName,
1209                          IN ACCESS_MASK DesiredAccess,
1210                          OUT PFILE_OBJECT *FileObject,
1211                          OUT PDEVICE_OBJECT *DeviceObject)
1212 {
1213     /* Call the helper routine for a normal operation */
1214     return IopGetDeviceObjectPointer(ObjectName,
1215                                      DesiredAccess,
1216                                      FileObject,
1217                                      DeviceObject,
1218                                      0);
1219 }
1220 
1221 /*
1222  * @implemented
1223  */
1224 NTSTATUS
1225 NTAPI
1226 IoGetDiskDeviceObject(IN PDEVICE_OBJECT FileSystemDeviceObject,
1227                       OUT PDEVICE_OBJECT *DiskDeviceObject)
1228 {
1229     PEXTENDED_DEVOBJ_EXTENSION DeviceExtension;
1230     PVPB Vpb;
1231     KIRQL OldIrql;
1232     NTSTATUS Status;
1233 
1234     /* Make sure there's a VPB */
1235     if (!FileSystemDeviceObject->Vpb) return STATUS_INVALID_PARAMETER;
1236 
1237     /* Acquire it */
1238     IoAcquireVpbSpinLock(&OldIrql);
1239 
1240     /* Get the Device Extension */
1241     DeviceExtension = IoGetDevObjExtension(FileSystemDeviceObject);
1242 
1243     /* Make sure this one has a VPB too */
1244     Vpb = DeviceExtension->Vpb;
1245     if (Vpb)
1246     {
1247         /* Make sure that it's mounted */
1248         if ((Vpb->ReferenceCount) &&
1249             (Vpb->Flags & VPB_MOUNTED))
1250         {
1251             /* Return the Disk Device Object */
1252             *DiskDeviceObject = Vpb->RealDevice;
1253 
1254             /* Reference it and return success */
1255             ObReferenceObject(Vpb->RealDevice);
1256             Status = STATUS_SUCCESS;
1257         }
1258         else
1259         {
1260             /* It's not, so return failure */
1261             Status = STATUS_VOLUME_DISMOUNTED;
1262         }
1263     }
1264     else
1265     {
1266         /* Fail */
1267         Status = STATUS_INVALID_PARAMETER;
1268     }
1269 
1270     /* Release the lock */
1271     IoReleaseVpbSpinLock(OldIrql);
1272     return Status;
1273 }
1274 
1275 /*
1276  * @implemented
1277  */
1278 PDEVICE_OBJECT
1279 NTAPI
1280 IoGetLowerDeviceObject(IN PDEVICE_OBJECT DeviceObject)
1281 {
1282     PEXTENDED_DEVOBJ_EXTENSION DeviceExtension;
1283     PDEVICE_OBJECT LowerDeviceObject = NULL;
1284 
1285     /* Make sure it's not getting deleted */
1286     DeviceExtension = IoGetDevObjExtension(DeviceObject);
1287     if (!(DeviceExtension->ExtensionFlags & (DOE_UNLOAD_PENDING |
1288                                            DOE_DELETE_PENDING |
1289                                            DOE_REMOVE_PENDING |
1290                                            DOE_REMOVE_PROCESSED)))
1291     {
1292         /* Get the Lower Device Object */
1293         LowerDeviceObject = DeviceExtension->AttachedTo;
1294 
1295         /* Check that we got a valid device object */
1296         if (LowerDeviceObject)
1297         {
1298             /* We did so let's reference it */
1299             ObReferenceObject(LowerDeviceObject);
1300         }
1301     }
1302 
1303     /* Return it */
1304     return LowerDeviceObject;
1305 }
1306 
1307 /*
1308  * @implemented
1309  */
1310 PDEVICE_OBJECT
1311 NTAPI
1312 IoGetRelatedDeviceObject(IN PFILE_OBJECT FileObject)
1313 {
1314     PDEVICE_OBJECT DeviceObject = FileObject->DeviceObject;
1315 
1316     /* Check if we have a VPB with a device object */
1317     if ((FileObject->Vpb) && (FileObject->Vpb->DeviceObject))
1318     {
1319         /* Then use the DO from the VPB */
1320         ASSERT(!(FileObject->Flags & FO_DIRECT_DEVICE_OPEN));
1321         DeviceObject = FileObject->Vpb->DeviceObject;
1322     }
1323     else if (!(FileObject->Flags & FO_DIRECT_DEVICE_OPEN) &&
1324               (FileObject->DeviceObject->Vpb) &&
1325               (FileObject->DeviceObject->Vpb->DeviceObject))
1326     {
1327         /* The disk device actually has a VPB, so get the DO from there */
1328         DeviceObject = FileObject->DeviceObject->Vpb->DeviceObject;
1329     }
1330     else
1331     {
1332         /* Otherwise, this was a direct open */
1333         DeviceObject = FileObject->DeviceObject;
1334     }
1335 
1336     /* Sanity check */
1337     ASSERT(DeviceObject != NULL);
1338 
1339     /* Check if we were attached */
1340     if (DeviceObject->AttachedDevice)
1341     {
1342         /* Check if the file object has an extension present */
1343         if (FileObject->Flags & FO_FILE_OBJECT_HAS_EXTENSION)
1344         {
1345             /* Sanity check, direct open files can't have this */
1346             ASSERT(!(FileObject->Flags & FO_DIRECT_DEVICE_OPEN));
1347 
1348             /* Check if the extension is really present */
1349             if (FileObject->FileObjectExtension)
1350             {
1351                 PFILE_OBJECT_EXTENSION FileObjectExtension;
1352                 ASSERT(FALSE);
1353 
1354                 /* Cast the buffer to something we understand */
1355                 FileObjectExtension = FileObject->FileObjectExtension;
1356 
1357                 /* Check if have a replacement top level device */
1358                 if (FileObjectExtension->TopDeviceObjectHint)
1359                 {
1360                     /* Use this instead of returning the top level device */
1361                     return FileObjectExtension->TopDeviceObjectHint;
1362                 }
1363             }
1364         }
1365 
1366         /* Return the highest attached device */
1367         DeviceObject = IoGetAttachedDevice(DeviceObject);
1368     }
1369 
1370     /* Return the DO we found */
1371     return DeviceObject;
1372 }
1373 
1374 /*
1375  * @implemented
1376  */
1377 NTSTATUS
1378 NTAPI
1379 IoGetRelatedTargetDevice(IN PFILE_OBJECT FileObject,
1380                          OUT PDEVICE_OBJECT *DeviceObject)
1381 {
1382     NTSTATUS Status;
1383     PDEVICE_NODE DeviceNode = NULL;
1384 
1385     /* Call the internal helper function */
1386     Status = IopGetRelatedTargetDevice(FileObject, &DeviceNode);
1387     if (NT_SUCCESS(Status) && DeviceNode)
1388     {
1389         *DeviceObject = DeviceNode->PhysicalDeviceObject;
1390     }
1391     return Status;
1392 }
1393 
1394 /*
1395  * @implemented
1396  */
1397 PDEVICE_OBJECT
1398 NTAPI
1399 IoGetBaseFileSystemDeviceObject(IN PFILE_OBJECT FileObject)
1400 {
1401     PDEVICE_OBJECT DeviceObject;
1402 
1403     /*
1404     * If the FILE_OBJECT's VPB is defined,
1405     * get the device from it.
1406     */
1407     if ((FileObject->Vpb) && (FileObject->Vpb->DeviceObject))
1408     {
1409         /* Use the VPB's Device Object's */
1410         DeviceObject = FileObject->Vpb->DeviceObject;
1411     }
1412     else if (!(FileObject->Flags & FO_DIRECT_DEVICE_OPEN) &&
1413              (FileObject->DeviceObject->Vpb) &&
1414              (FileObject->DeviceObject->Vpb->DeviceObject))
1415     {
1416         /* Use the VPB's File System Object */
1417         DeviceObject = FileObject->DeviceObject->Vpb->DeviceObject;
1418     }
1419     else
1420     {
1421         /* Use the FO's Device Object */
1422         DeviceObject = FileObject->DeviceObject;
1423     }
1424 
1425     /* Return the device object we found */
1426     ASSERT(DeviceObject != NULL);
1427     return DeviceObject;
1428 }
1429 
1430 /*
1431  * @implemented
1432  */
1433 NTSTATUS
1434 NTAPI
1435 IoRegisterLastChanceShutdownNotification(IN PDEVICE_OBJECT DeviceObject)
1436 {
1437     PSHUTDOWN_ENTRY Entry;
1438 
1439     /* Allocate the shutdown entry */
1440     Entry = ExAllocatePoolWithTag(NonPagedPool,
1441                                   sizeof(SHUTDOWN_ENTRY),
1442                                   TAG_SHUTDOWN_ENTRY);
1443     if (!Entry) return STATUS_INSUFFICIENT_RESOURCES;
1444 
1445     /* Set the DO */
1446     Entry->DeviceObject = DeviceObject;
1447 
1448     /* Reference it so it doesn't go away */
1449     ObReferenceObject(DeviceObject);
1450 
1451     /* Insert it into the list */
1452     ExInterlockedInsertHeadList(&LastChanceShutdownListHead,
1453                                 &Entry->ShutdownList,
1454                                 &ShutdownListLock);
1455 
1456     /* Set the shutdown registered flag */
1457     DeviceObject->Flags |= DO_SHUTDOWN_REGISTERED;
1458     return STATUS_SUCCESS;
1459 }
1460 
1461 /*
1462  * @implemented
1463  */
1464 NTSTATUS
1465 NTAPI
1466 IoRegisterShutdownNotification(PDEVICE_OBJECT DeviceObject)
1467 {
1468     PSHUTDOWN_ENTRY Entry;
1469 
1470     /* Allocate the shutdown entry */
1471     Entry = ExAllocatePoolWithTag(NonPagedPool,
1472                                   sizeof(SHUTDOWN_ENTRY),
1473                                   TAG_SHUTDOWN_ENTRY);
1474     if (!Entry) return STATUS_INSUFFICIENT_RESOURCES;
1475 
1476     /* Set the DO */
1477     Entry->DeviceObject = DeviceObject;
1478 
1479     /* Reference it so it doesn't go away */
1480     ObReferenceObject(DeviceObject);
1481 
1482     /* Insert it into the list */
1483     ExInterlockedInsertHeadList(&ShutdownListHead,
1484                                 &Entry->ShutdownList,
1485                                 &ShutdownListLock);
1486 
1487     /* Set the shutdown registered flag */
1488     DeviceObject->Flags |= DO_SHUTDOWN_REGISTERED;
1489     return STATUS_SUCCESS;
1490 }
1491 
1492 /*
1493  * @implemented
1494  */
1495 VOID
1496 NTAPI
1497 IoUnregisterShutdownNotification(PDEVICE_OBJECT DeviceObject)
1498 {
1499     PSHUTDOWN_ENTRY ShutdownEntry;
1500     PLIST_ENTRY NextEntry;
1501     KIRQL OldIrql;
1502 
1503     /* Remove the flag */
1504     DeviceObject->Flags &= ~DO_SHUTDOWN_REGISTERED;
1505 
1506     /* Acquire the shutdown lock and loop the shutdown list */
1507     KeAcquireSpinLock(&ShutdownListLock, &OldIrql);
1508     NextEntry = ShutdownListHead.Flink;
1509     while (NextEntry != &ShutdownListHead)
1510     {
1511         /* Get the entry */
1512         ShutdownEntry = CONTAINING_RECORD(NextEntry,
1513                                           SHUTDOWN_ENTRY,
1514                                           ShutdownList);
1515 
1516         /* Get if the DO matches */
1517         if (ShutdownEntry->DeviceObject == DeviceObject)
1518         {
1519             /* Remove it from the list */
1520             RemoveEntryList(NextEntry);
1521             NextEntry = NextEntry->Blink;
1522 
1523             /* Free the entry */
1524             ExFreePoolWithTag(ShutdownEntry, TAG_SHUTDOWN_ENTRY);
1525 
1526             /* Get rid of our reference to it */
1527             ObDereferenceObject(DeviceObject);
1528         }
1529 
1530         /* Go to the next entry */
1531         NextEntry = NextEntry->Flink;
1532     }
1533 
1534     /* Now loop the last chance list */
1535     NextEntry = LastChanceShutdownListHead.Flink;
1536     while (NextEntry != &LastChanceShutdownListHead)
1537     {
1538         /* Get the entry */
1539         ShutdownEntry = CONTAINING_RECORD(NextEntry,
1540                                           SHUTDOWN_ENTRY,
1541                                           ShutdownList);
1542 
1543         /* Get if the DO matches */
1544         if (ShutdownEntry->DeviceObject == DeviceObject)
1545         {
1546             /* Remove it from the list */
1547             RemoveEntryList(NextEntry);
1548             NextEntry = NextEntry->Blink;
1549 
1550             /* Free the entry */
1551             ExFreePoolWithTag(ShutdownEntry, TAG_SHUTDOWN_ENTRY);
1552 
1553             /* Get rid of our reference to it */
1554             ObDereferenceObject(DeviceObject);
1555         }
1556 
1557         /* Go to the next entry */
1558         NextEntry = NextEntry->Flink;
1559     }
1560 
1561     /* Release the shutdown lock */
1562     KeReleaseSpinLock(&ShutdownListLock, OldIrql);
1563 }
1564 
1565 /*
1566  * @implemented
1567  */
1568 VOID
1569 NTAPI
1570 IoSetStartIoAttributes(IN PDEVICE_OBJECT DeviceObject,
1571                        IN BOOLEAN DeferredStartIo,
1572                        IN BOOLEAN NonCancelable)
1573 {
1574     PEXTENDED_DEVOBJ_EXTENSION DeviceExtension;
1575 
1576     /* Get the Device Extension */
1577     DeviceExtension = IoGetDevObjExtension(DeviceObject);
1578 
1579     /* Set the flags the caller requested */
1580     DeviceExtension->StartIoFlags |= (DeferredStartIo) ? DOE_SIO_DEFERRED : 0;
1581     DeviceExtension->StartIoFlags |= (NonCancelable) ? DOE_SIO_NO_CANCEL : 0;
1582 }
1583 
1584 /*
1585  * @implemented
1586  */
1587 VOID
1588 NTAPI
1589 IoStartNextPacketByKey(IN PDEVICE_OBJECT DeviceObject,
1590                        IN BOOLEAN Cancelable,
1591                        IN ULONG Key)
1592 {
1593     PEXTENDED_DEVOBJ_EXTENSION DeviceExtension;
1594 
1595     /* Get the Device Extension */
1596     DeviceExtension = IoGetDevObjExtension(DeviceObject);
1597 
1598     /* Check if deferred start was requested */
1599     if (DeviceExtension->StartIoFlags & DOE_SIO_DEFERRED)
1600     {
1601         /* Call our internal function to handle the defered case */
1602         IopStartNextPacketByKeyEx(DeviceObject,
1603                                   Key,
1604                                   DOE_SIO_WITH_KEY |
1605                                   (Cancelable ? DOE_SIO_CANCELABLE : 0));
1606     }
1607     else
1608     {
1609         /* Call the normal routine */
1610         IopStartNextPacketByKey(DeviceObject, Cancelable, Key);
1611     }
1612 }
1613 
1614 /*
1615  * @implemented
1616  */
1617 VOID
1618 NTAPI
1619 IoStartNextPacket(IN PDEVICE_OBJECT DeviceObject,
1620                   IN BOOLEAN Cancelable)
1621 {
1622     PEXTENDED_DEVOBJ_EXTENSION DeviceExtension;
1623 
1624     /* Get the Device Extension */
1625     DeviceExtension = IoGetDevObjExtension(DeviceObject);
1626 
1627     /* Check if deferred start was requested */
1628     if (DeviceExtension->StartIoFlags & DOE_SIO_DEFERRED)
1629     {
1630         /* Call our internal function to handle the defered case */
1631         IopStartNextPacketByKeyEx(DeviceObject,
1632                                   0,
1633                                   DOE_SIO_NO_KEY |
1634                                   (Cancelable ? DOE_SIO_CANCELABLE : 0));
1635     }
1636     else
1637     {
1638         /* Call the normal routine */
1639         IopStartNextPacket(DeviceObject, Cancelable);
1640     }
1641 }
1642 
1643 /*
1644  * @implemented
1645  */
1646 VOID
1647 NTAPI
1648 IoStartPacket(IN PDEVICE_OBJECT DeviceObject,
1649               IN PIRP Irp,
1650               IN PULONG Key,
1651               IN PDRIVER_CANCEL CancelFunction)
1652 {
1653     BOOLEAN Stat;
1654     KIRQL OldIrql, CancelIrql;
1655 
1656     /* Raise to dispatch level */
1657     KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
1658 
1659     /* Check if we should acquire the cancel lock */
1660     if (CancelFunction)
1661     {
1662         /* Acquire and set it */
1663         IoAcquireCancelSpinLock(&CancelIrql);
1664         Irp->CancelRoutine = CancelFunction;
1665     }
1666 
1667     /* Check if we have a key */
1668     if (Key)
1669     {
1670         /* Insert by key */
1671         Stat = KeInsertByKeyDeviceQueue(&DeviceObject->DeviceQueue,
1672                                         &Irp->Tail.Overlay.DeviceQueueEntry,
1673                                         *Key);
1674     }
1675     else
1676     {
1677         /* Insert without a key */
1678         Stat = KeInsertDeviceQueue(&DeviceObject->DeviceQueue,
1679                                    &Irp->Tail.Overlay.DeviceQueueEntry);
1680     }
1681 
1682     /* Check if this was a first insert */
1683     if (!Stat)
1684     {
1685         /* Set the IRP */
1686         DeviceObject->CurrentIrp = Irp;
1687 
1688         /* Check if this is a cancelable packet */
1689         if (CancelFunction)
1690         {
1691             /* Check if the caller requested no cancellation */
1692             if (IoGetDevObjExtension(DeviceObject)->StartIoFlags &
1693                 DOE_SIO_NO_CANCEL)
1694             {
1695                 /* He did, so remove the cancel routine */
1696                 Irp->CancelRoutine = NULL;
1697             }
1698 
1699             /* Release the cancel lock */
1700             IoReleaseCancelSpinLock(CancelIrql);
1701         }
1702 
1703         /* Call the Start I/O function */
1704         DeviceObject->DriverObject->DriverStartIo(DeviceObject, Irp);
1705     }
1706     else
1707     {
1708         /* The packet was inserted... check if we have a cancel function */
1709         if (CancelFunction)
1710         {
1711             /* Check if the IRP got cancelled */
1712             if (Irp->Cancel)
1713             {
1714                 /*
1715                  * Set the cancel IRQL, clear the currnet cancel routine and
1716                  * call ours
1717                  */
1718                 Irp->CancelIrql = CancelIrql;
1719                 Irp->CancelRoutine = NULL;
1720                 CancelFunction(DeviceObject, Irp);
1721             }
1722             else
1723             {
1724                 /* Otherwise, release the lock */
1725                 IoReleaseCancelSpinLock(CancelIrql);
1726             }
1727         }
1728     }
1729 
1730     /* Return back to previous IRQL */
1731     KeLowerIrql(OldIrql);
1732 }
1733 
1734 #if defined (_WIN64)
1735 ULONG
1736 NTAPI
1737 IoWMIDeviceObjectToProviderId(
1738     IN PDEVICE_OBJECT DeviceObject)
1739 {
1740     UNIMPLEMENTED;
1741     return 0;
1742 }
1743 #endif
1744 
1745 /* EOF */
1746