xref: /reactos/ntoskrnl/io/iomgr/device.c (revision 161b6728)
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 BOOLEAN
691 NTAPI
692 IopVerifyDeviceObjectOnStack(IN PDEVICE_OBJECT BaseDeviceObject,
693                              IN PDEVICE_OBJECT TopDeviceObjectHint)
694 {
695     KIRQL OldIrql;
696     BOOLEAN Result;
697     PDEVICE_OBJECT LoopObject;
698 
699     ASSERT(BaseDeviceObject != NULL);
700 
701     Result = FALSE;
702     /* Simply loop on the device stack and try to find our hint */
703     OldIrql = KeAcquireQueuedSpinLock(LockQueueIoDatabaseLock);
704     for (LoopObject = BaseDeviceObject; ; LoopObject = LoopObject->AttachedDevice)
705     {
706         /* It was found, it's a success */
707         if (LoopObject == TopDeviceObjectHint)
708         {
709             Result = TRUE;
710             break;
711         }
712 
713         /* End of the stack, that's a failure - default */
714         if (LoopObject == NULL)
715         {
716             break;
717         }
718     }
719     KeReleaseQueuedSpinLock(LockQueueIoDatabaseLock, OldIrql);
720 
721     return Result;
722 }
723 
724 /* PUBLIC FUNCTIONS ***********************************************************/
725 
726 /*
727  * IoAttachDevice
728  *
729  * Layers a device over the highest device in a device stack.
730  *
731  * Parameters
732  *    SourceDevice
733  *       Device to be attached.
734  *
735  *    TargetDevice
736  *       Name of the target device.
737  *
738  *    AttachedDevice
739  *       Caller storage for the device attached to.
740  *
741  * Status
742  *    @implemented
743  */
744 NTSTATUS
745 NTAPI
746 IoAttachDevice(PDEVICE_OBJECT SourceDevice,
747                PUNICODE_STRING TargetDeviceName,
748                PDEVICE_OBJECT *AttachedDevice)
749 {
750    NTSTATUS Status;
751    PFILE_OBJECT FileObject = NULL;
752    PDEVICE_OBJECT TargetDevice = NULL;
753 
754     /* Call the helper routine for an attach operation */
755     Status = IopGetDeviceObjectPointer(TargetDeviceName,
756                                        FILE_READ_ATTRIBUTES,
757                                        &FileObject,
758                                        &TargetDevice,
759                                        IO_ATTACH_DEVICE_API);
760     if (!NT_SUCCESS(Status)) return Status;
761 
762     /* Attach the device */
763     Status = IoAttachDeviceToDeviceStackSafe(SourceDevice,
764                                              TargetDevice,
765                                              AttachedDevice);
766 
767     /* Dereference it */
768     ObDereferenceObject(FileObject);
769     return Status;
770 }
771 
772 /*
773  * IoAttachDeviceByPointer
774  *
775  * Status
776  *    @implemented
777  */
778 NTSTATUS
779 NTAPI
780 IoAttachDeviceByPointer(IN PDEVICE_OBJECT SourceDevice,
781                         IN PDEVICE_OBJECT TargetDevice)
782 {
783     PDEVICE_OBJECT AttachedDevice;
784     NTSTATUS Status = STATUS_SUCCESS;
785 
786     /* Do the Attach */
787     AttachedDevice = IoAttachDeviceToDeviceStack(SourceDevice, TargetDevice);
788     if (!AttachedDevice) Status = STATUS_NO_SUCH_DEVICE;
789 
790     /* Return the status */
791     return Status;
792 }
793 
794 /*
795  * @implemented
796  */
797 PDEVICE_OBJECT
798 NTAPI
799 IoAttachDeviceToDeviceStack(IN PDEVICE_OBJECT SourceDevice,
800                             IN PDEVICE_OBJECT TargetDevice)
801 {
802     /* Attach it safely */
803     return IopAttachDeviceToDeviceStackSafe(SourceDevice,
804                                             TargetDevice,
805                                             NULL);
806 }
807 
808 /*
809  * @implemented
810  */
811 NTSTATUS
812 NTAPI
813 IoAttachDeviceToDeviceStackSafe(IN PDEVICE_OBJECT SourceDevice,
814                                 IN PDEVICE_OBJECT TargetDevice,
815                                 IN OUT PDEVICE_OBJECT *AttachedToDeviceObject)
816 {
817     /* Call the internal function */
818     if (!IopAttachDeviceToDeviceStackSafe(SourceDevice,
819                                           TargetDevice,
820                                           AttachedToDeviceObject))
821     {
822         /* Nothing found */
823         return STATUS_NO_SUCH_DEVICE;
824     }
825 
826     /* Success! */
827     return STATUS_SUCCESS;
828 }
829 
830 /*
831  * IoCreateDevice
832  *
833  * Allocates memory for and intializes a device object for use for
834  * a driver.
835  *
836  * Parameters
837  *    DriverObject
838  *       Driver object passed by IO Manager when the driver was loaded.
839  *
840  *    DeviceExtensionSize
841  *       Number of bytes for the device extension.
842  *
843  *    DeviceName
844  *       Unicode name of device.
845  *
846  *    DeviceType
847  *       Device type of the new device.
848  *
849  *    DeviceCharacteristics
850  *       Bit mask of device characteristics.
851  *
852  *    Exclusive
853  *       TRUE if only one thread can access the device at a time.
854  *
855  *    DeviceObject
856  *       On successful return this parameter is filled by pointer to
857  *       allocated device object.
858  *
859  * Status
860  *    @implemented
861  */
862 NTSTATUS
863 NTAPI
864 IoCreateDevice(IN PDRIVER_OBJECT DriverObject,
865                IN ULONG DeviceExtensionSize,
866                IN PUNICODE_STRING DeviceName,
867                IN DEVICE_TYPE DeviceType,
868                IN ULONG DeviceCharacteristics,
869                IN BOOLEAN Exclusive,
870                OUT PDEVICE_OBJECT *DeviceObject)
871 {
872     WCHAR AutoNameBuffer[20];
873     UNICODE_STRING AutoName;
874     PDEVICE_OBJECT CreatedDeviceObject;
875     PDEVOBJ_EXTENSION DeviceObjectExtension;
876     OBJECT_ATTRIBUTES ObjectAttributes;
877     NTSTATUS Status;
878     ULONG AlignedDeviceExtensionSize;
879     ULONG TotalSize;
880     HANDLE TempHandle;
881     PAGED_CODE();
882 
883     /* Check if we have to generate a name */
884     if (DeviceCharacteristics & FILE_AUTOGENERATED_DEVICE_NAME)
885     {
886         /* Generate it */
887         swprintf(AutoNameBuffer,
888                  L"\\Device\\%08lx",
889                  InterlockedIncrementUL(&IopDeviceObjectNumber));
890 
891         /* Initialize the name */
892         RtlInitUnicodeString(&AutoName, AutoNameBuffer);
893         DeviceName = &AutoName;
894     }
895 
896     /* Initialize the Object Attributes */
897     InitializeObjectAttributes(&ObjectAttributes,
898                                DeviceName,
899                                OBJ_KERNEL_HANDLE,
900                                NULL,
901                                SePublicOpenUnrestrictedSd);
902 
903     /* Honor exclusive flag */
904     if (Exclusive) ObjectAttributes.Attributes |= OBJ_EXCLUSIVE;
905 
906     /* Create a permanent object for named devices */
907     if (DeviceName) ObjectAttributes.Attributes |= OBJ_PERMANENT;
908 
909     /* Align the Extension Size to 8-bytes */
910     AlignedDeviceExtensionSize = (DeviceExtensionSize + 7) &~ 7;
911 
912     /* Total Size */
913     TotalSize = AlignedDeviceExtensionSize +
914                 sizeof(DEVICE_OBJECT) +
915                 sizeof(EXTENDED_DEVOBJ_EXTENSION);
916 
917     /* Create the Device Object */
918     *DeviceObject = NULL;
919     Status = ObCreateObject(KernelMode,
920                             IoDeviceObjectType,
921                             &ObjectAttributes,
922                             KernelMode,
923                             NULL,
924                             TotalSize,
925                             0,
926                             0,
927                             (PVOID*)&CreatedDeviceObject);
928     if (!NT_SUCCESS(Status)) return Status;
929 
930     /* Clear the whole Object and extension so we don't null stuff manually */
931     RtlZeroMemory(CreatedDeviceObject, TotalSize);
932 
933     /*
934      * Setup the Type and Size. Note that we don't use the aligned size,
935      * because that's only padding for the DevObjExt and not part of the Object.
936      */
937     CreatedDeviceObject->Type = IO_TYPE_DEVICE;
938     CreatedDeviceObject->Size = sizeof(DEVICE_OBJECT) + (USHORT)DeviceExtensionSize;
939 
940     /* The kernel extension is after the driver internal extension */
941     DeviceObjectExtension = (PDEVOBJ_EXTENSION)
942                             ((ULONG_PTR)(CreatedDeviceObject + 1) +
943                              AlignedDeviceExtensionSize);
944 
945     /* Set the Type and Size. Question: why is Size 0 on Windows? */
946     DeviceObjectExtension->Type = IO_TYPE_DEVICE_OBJECT_EXTENSION;
947     DeviceObjectExtension->Size = 0;
948 
949     /* Initialize with Power Manager */
950     PoInitializeDeviceObject(DeviceObjectExtension);
951 
952     /* Link the Object and Extension */
953     DeviceObjectExtension->DeviceObject = CreatedDeviceObject;
954     CreatedDeviceObject->DeviceObjectExtension = DeviceObjectExtension;
955 
956     /* Set Device Object Data */
957     CreatedDeviceObject->DeviceType = DeviceType;
958     CreatedDeviceObject->Characteristics = DeviceCharacteristics;
959     CreatedDeviceObject->DeviceExtension = DeviceExtensionSize ?
960                                            CreatedDeviceObject + 1 :
961                                            NULL;
962     CreatedDeviceObject->StackSize = 1;
963     CreatedDeviceObject->AlignmentRequirement = 0;
964 
965     /* Set the Flags */
966     CreatedDeviceObject->Flags = DO_DEVICE_INITIALIZING;
967     if (Exclusive) CreatedDeviceObject->Flags |= DO_EXCLUSIVE;
968     if (DeviceName) CreatedDeviceObject->Flags |= DO_DEVICE_HAS_NAME;
969 
970     /* Attach a Vpb for Disks and Tapes, and create the Device Lock */
971     if ((CreatedDeviceObject->DeviceType == FILE_DEVICE_DISK) ||
972         (CreatedDeviceObject->DeviceType == FILE_DEVICE_VIRTUAL_DISK) ||
973         (CreatedDeviceObject->DeviceType == FILE_DEVICE_CD_ROM) ||
974         (CreatedDeviceObject->DeviceType == FILE_DEVICE_TAPE))
975     {
976         /* Create Vpb */
977         Status = IopCreateVpb(CreatedDeviceObject);
978         if (!NT_SUCCESS(Status))
979         {
980             /* Dereference the device object and fail */
981             ObDereferenceObject(CreatedDeviceObject);
982             return Status;
983         }
984 
985         /* Initialize Lock Event */
986         KeInitializeEvent(&CreatedDeviceObject->DeviceLock,
987                           SynchronizationEvent,
988                           TRUE);
989     }
990 
991     /* Set the right Sector Size */
992     switch (DeviceType)
993     {
994         /* All disk systems */
995         case FILE_DEVICE_DISK_FILE_SYSTEM:
996         case FILE_DEVICE_DISK:
997         case FILE_DEVICE_VIRTUAL_DISK:
998 
999             /* The default is 512 bytes */
1000             CreatedDeviceObject->SectorSize  = 512;
1001             break;
1002 
1003         /* CD-ROM file systems */
1004         case FILE_DEVICE_CD_ROM_FILE_SYSTEM:
1005 
1006             /* The default is 2048 bytes */
1007             CreatedDeviceObject->SectorSize = 2048;
1008     }
1009 
1010     /* Create the Device Queue */
1011     if ((CreatedDeviceObject->DeviceType == FILE_DEVICE_DISK_FILE_SYSTEM) ||
1012         (CreatedDeviceObject->DeviceType == FILE_DEVICE_FILE_SYSTEM) ||
1013         (CreatedDeviceObject->DeviceType == FILE_DEVICE_CD_ROM_FILE_SYSTEM) ||
1014         (CreatedDeviceObject->DeviceType == FILE_DEVICE_NETWORK_FILE_SYSTEM) ||
1015         (CreatedDeviceObject->DeviceType == FILE_DEVICE_TAPE_FILE_SYSTEM))
1016     {
1017         /* Simple FS Devices, they don't need a real Device Queue */
1018         InitializeListHead(&CreatedDeviceObject->Queue.ListEntry);
1019     }
1020     else
1021     {
1022         /* An actual Device, initialize its DQ */
1023         KeInitializeDeviceQueue(&CreatedDeviceObject->DeviceQueue);
1024     }
1025 
1026     /* Insert the Object */
1027     Status = ObInsertObject(CreatedDeviceObject,
1028                             NULL,
1029                             FILE_READ_DATA | FILE_WRITE_DATA,
1030                             1,
1031                             (PVOID*)&CreatedDeviceObject,
1032                             &TempHandle);
1033     if (!NT_SUCCESS(Status)) return Status;
1034 
1035     /* Now do the final linking */
1036     ObReferenceObject(DriverObject);
1037     ASSERT((DriverObject->Flags & DRVO_UNLOAD_INVOKED) == 0);
1038     CreatedDeviceObject->DriverObject = DriverObject;
1039     IopEditDeviceList(DriverObject, CreatedDeviceObject, IopAdd);
1040 
1041     /* Link with the power manager */
1042     if (CreatedDeviceObject->Vpb) PoVolumeDevice(CreatedDeviceObject);
1043 
1044     /* Close the temporary handle and return to caller */
1045     ObCloseHandle(TempHandle, KernelMode);
1046     *DeviceObject = CreatedDeviceObject;
1047     return STATUS_SUCCESS;
1048 }
1049 
1050 /*
1051  * IoDeleteDevice
1052  *
1053  * Status
1054  *    @implemented
1055  */
1056 VOID
1057 NTAPI
1058 IoDeleteDevice(IN PDEVICE_OBJECT DeviceObject)
1059 {
1060     PIO_TIMER Timer;
1061 
1062     /* Check if the device is registered for shutdown notifications */
1063     if (DeviceObject->Flags & DO_SHUTDOWN_REGISTERED)
1064     {
1065         /* Call the shutdown notifications */
1066         IoUnregisterShutdownNotification(DeviceObject);
1067     }
1068 
1069     /* Check if it has a timer */
1070     Timer = DeviceObject->Timer;
1071     if (Timer)
1072     {
1073         /* Remove it and free it */
1074         IopRemoveTimerFromTimerList(Timer);
1075         ExFreePoolWithTag(Timer, TAG_IO_TIMER);
1076     }
1077 
1078     /* Check if the device has a name */
1079     if (DeviceObject->Flags & DO_DEVICE_HAS_NAME)
1080     {
1081         /* It does, make it temporary so we can remove it */
1082         ObMakeTemporaryObject(DeviceObject);
1083     }
1084 
1085     /* Set the pending delete flag */
1086     IoGetDevObjExtension(DeviceObject)->ExtensionFlags |= DOE_DELETE_PENDING;
1087 
1088     /* Unlink with the power manager */
1089     if (DeviceObject->Vpb) PoRemoveVolumeDevice(DeviceObject);
1090 
1091     /* Check if the device object can be unloaded */
1092     if (!DeviceObject->ReferenceCount) IopUnloadDevice(DeviceObject);
1093 }
1094 
1095 /*
1096  * IoDetachDevice
1097  *
1098  * Status
1099  *    @implemented
1100  */
1101 VOID
1102 NTAPI
1103 IoDetachDevice(IN PDEVICE_OBJECT TargetDevice)
1104 {
1105     PEXTENDED_DEVOBJ_EXTENSION DeviceExtension;
1106 
1107     /* Sanity check */
1108     DeviceExtension = IoGetDevObjExtension(TargetDevice->AttachedDevice);
1109     ASSERT(DeviceExtension->AttachedTo == TargetDevice);
1110 
1111     /* Remove the attachment */
1112     DeviceExtension->AttachedTo = NULL;
1113     TargetDevice->AttachedDevice = NULL;
1114 
1115     /* Check if it's ok to delete this device */
1116     if ((IoGetDevObjExtension(TargetDevice)->ExtensionFlags & DOE_DELETE_PENDING) &&
1117         !(TargetDevice->ReferenceCount))
1118     {
1119         /* It is, do it */
1120         IopUnloadDevice(TargetDevice);
1121     }
1122 }
1123 
1124 /*
1125  * @implemented
1126  */
1127 NTSTATUS
1128 NTAPI
1129 IoEnumerateDeviceObjectList(IN  PDRIVER_OBJECT DriverObject,
1130                             IN  PDEVICE_OBJECT *DeviceObjectList,
1131                             IN  ULONG DeviceObjectListSize,
1132                             OUT PULONG ActualNumberDeviceObjects)
1133 {
1134     ULONG ActualDevices = 1;
1135     PDEVICE_OBJECT CurrentDevice = DriverObject->DeviceObject;
1136     KIRQL OldIrql;
1137 
1138     /* Lock the Device list while we enumerate it */
1139     OldIrql = KeAcquireQueuedSpinLock(LockQueueIoDatabaseLock);
1140 
1141     /* Find out how many devices we'll enumerate */
1142     while ((CurrentDevice = CurrentDevice->NextDevice)) ActualDevices++;
1143 
1144     /* Go back to the first */
1145     CurrentDevice = DriverObject->DeviceObject;
1146 
1147     /* Start by at least returning this */
1148     *ActualNumberDeviceObjects = ActualDevices;
1149 
1150     /* Check if we can support so many */
1151     if ((ActualDevices * sizeof(PDEVICE_OBJECT)) > DeviceObjectListSize)
1152     {
1153         /* Fail because the buffer was too small */
1154         KeReleaseQueuedSpinLock(LockQueueIoDatabaseLock, OldIrql);
1155         return STATUS_BUFFER_TOO_SMALL;
1156     }
1157 
1158     /* Check if the caller wanted the device list */
1159     if (DeviceObjectList)
1160     {
1161         /* Loop through all the devices */
1162         while (ActualDevices)
1163         {
1164             /* Reference each Device */
1165             ObReferenceObject(CurrentDevice);
1166 
1167             /* Add it to the list */
1168             *DeviceObjectList = CurrentDevice;
1169 
1170             /* Go to the next one */
1171             CurrentDevice = CurrentDevice->NextDevice;
1172             ActualDevices--;
1173             DeviceObjectList++;
1174         }
1175     }
1176 
1177     /* Release the device list lock */
1178     KeReleaseQueuedSpinLock(LockQueueIoDatabaseLock, OldIrql);
1179 
1180     /* Return the status */
1181     return STATUS_SUCCESS;
1182 }
1183 
1184 /*
1185  * IoGetAttachedDevice
1186  *
1187  * Status
1188  *    @implemented
1189  */
1190 PDEVICE_OBJECT
1191 NTAPI
1192 IoGetAttachedDevice(PDEVICE_OBJECT DeviceObject)
1193 {
1194     /* Get the last attached device */
1195     while (DeviceObject->AttachedDevice)
1196     {
1197         /* Move to the next one */
1198         DeviceObject = DeviceObject->AttachedDevice;
1199     }
1200 
1201     /* Return it */
1202     return DeviceObject;
1203 }
1204 
1205 /*
1206  * IoGetAttachedDeviceReference
1207  *
1208  * Status
1209  *    @implemented
1210  */
1211 PDEVICE_OBJECT
1212 NTAPI
1213 IoGetAttachedDeviceReference(PDEVICE_OBJECT DeviceObject)
1214 {
1215     /* Reference the Attached Device */
1216     DeviceObject = IoGetAttachedDevice(DeviceObject);
1217     ObReferenceObject(DeviceObject);
1218     return DeviceObject;
1219 }
1220 
1221 /*
1222  * @implemented
1223  */
1224 PDEVICE_OBJECT
1225 NTAPI
1226 IoGetDeviceAttachmentBaseRef(IN PDEVICE_OBJECT DeviceObject)
1227 {
1228     /* Reference the lowest attached device */
1229     DeviceObject = IopGetLowestDevice(DeviceObject);
1230     ObReferenceObject(DeviceObject);
1231     return DeviceObject;
1232 }
1233 
1234 /*
1235  * IoGetDeviceObjectPointer
1236  *
1237  * Status
1238  *    @implemented
1239  */
1240 NTSTATUS
1241 NTAPI
1242 IoGetDeviceObjectPointer(IN PUNICODE_STRING ObjectName,
1243                          IN ACCESS_MASK DesiredAccess,
1244                          OUT PFILE_OBJECT *FileObject,
1245                          OUT PDEVICE_OBJECT *DeviceObject)
1246 {
1247     /* Call the helper routine for a normal operation */
1248     return IopGetDeviceObjectPointer(ObjectName,
1249                                      DesiredAccess,
1250                                      FileObject,
1251                                      DeviceObject,
1252                                      0);
1253 }
1254 
1255 /*
1256  * @implemented
1257  */
1258 NTSTATUS
1259 NTAPI
1260 IoGetDiskDeviceObject(IN PDEVICE_OBJECT FileSystemDeviceObject,
1261                       OUT PDEVICE_OBJECT *DiskDeviceObject)
1262 {
1263     PEXTENDED_DEVOBJ_EXTENSION DeviceExtension;
1264     PVPB Vpb;
1265     KIRQL OldIrql;
1266     NTSTATUS Status;
1267 
1268     /* Make sure there's a VPB */
1269     if (!FileSystemDeviceObject->Vpb) return STATUS_INVALID_PARAMETER;
1270 
1271     /* Acquire it */
1272     IoAcquireVpbSpinLock(&OldIrql);
1273 
1274     /* Get the Device Extension */
1275     DeviceExtension = IoGetDevObjExtension(FileSystemDeviceObject);
1276 
1277     /* Make sure this one has a VPB too */
1278     Vpb = DeviceExtension->Vpb;
1279     if (Vpb)
1280     {
1281         /* Make sure that it's mounted */
1282         if ((Vpb->ReferenceCount) &&
1283             (Vpb->Flags & VPB_MOUNTED))
1284         {
1285             /* Return the Disk Device Object */
1286             *DiskDeviceObject = Vpb->RealDevice;
1287 
1288             /* Reference it and return success */
1289             ObReferenceObject(Vpb->RealDevice);
1290             Status = STATUS_SUCCESS;
1291         }
1292         else
1293         {
1294             /* It's not, so return failure */
1295             Status = STATUS_VOLUME_DISMOUNTED;
1296         }
1297     }
1298     else
1299     {
1300         /* Fail */
1301         Status = STATUS_INVALID_PARAMETER;
1302     }
1303 
1304     /* Release the lock */
1305     IoReleaseVpbSpinLock(OldIrql);
1306     return Status;
1307 }
1308 
1309 /*
1310  * @implemented
1311  */
1312 PDEVICE_OBJECT
1313 NTAPI
1314 IoGetLowerDeviceObject(IN PDEVICE_OBJECT DeviceObject)
1315 {
1316     PEXTENDED_DEVOBJ_EXTENSION DeviceExtension;
1317     PDEVICE_OBJECT LowerDeviceObject = NULL;
1318 
1319     /* Make sure it's not getting deleted */
1320     DeviceExtension = IoGetDevObjExtension(DeviceObject);
1321     if (!(DeviceExtension->ExtensionFlags & (DOE_UNLOAD_PENDING |
1322                                            DOE_DELETE_PENDING |
1323                                            DOE_REMOVE_PENDING |
1324                                            DOE_REMOVE_PROCESSED)))
1325     {
1326         /* Get the Lower Device Object */
1327         LowerDeviceObject = DeviceExtension->AttachedTo;
1328 
1329         /* Check that we got a valid device object */
1330         if (LowerDeviceObject)
1331         {
1332             /* We did so let's reference it */
1333             ObReferenceObject(LowerDeviceObject);
1334         }
1335     }
1336 
1337     /* Return it */
1338     return LowerDeviceObject;
1339 }
1340 
1341 /*
1342  * @implemented
1343  */
1344 PDEVICE_OBJECT
1345 NTAPI
1346 IoGetRelatedDeviceObject(IN PFILE_OBJECT FileObject)
1347 {
1348     PDEVICE_OBJECT DeviceObject = FileObject->DeviceObject;
1349 
1350     /* Check if we have a VPB with a device object */
1351     if ((FileObject->Vpb) && (FileObject->Vpb->DeviceObject))
1352     {
1353         /* Then use the DO from the VPB */
1354         ASSERT(!(FileObject->Flags & FO_DIRECT_DEVICE_OPEN));
1355         DeviceObject = FileObject->Vpb->DeviceObject;
1356     }
1357     else if (!(FileObject->Flags & FO_DIRECT_DEVICE_OPEN) &&
1358               (FileObject->DeviceObject->Vpb) &&
1359               (FileObject->DeviceObject->Vpb->DeviceObject))
1360     {
1361         /* The disk device actually has a VPB, so get the DO from there */
1362         DeviceObject = FileObject->DeviceObject->Vpb->DeviceObject;
1363     }
1364     else
1365     {
1366         /* Otherwise, this was a direct open */
1367         DeviceObject = FileObject->DeviceObject;
1368     }
1369 
1370     /* Sanity check */
1371     ASSERT(DeviceObject != NULL);
1372 
1373     /* Check if we were attached */
1374     if (DeviceObject->AttachedDevice)
1375     {
1376         /* Check if the file object has an extension present */
1377         if (FileObject->Flags & FO_FILE_OBJECT_HAS_EXTENSION)
1378         {
1379             /* Sanity check, direct open files can't have this */
1380             ASSERT(!(FileObject->Flags & FO_DIRECT_DEVICE_OPEN));
1381 
1382             /* Check if the extension is really present */
1383             if (FileObject->FileObjectExtension)
1384             {
1385                 PFILE_OBJECT_EXTENSION FileObjectExtension;
1386 
1387                 /* Cast the buffer to something we understand */
1388                 FileObjectExtension = FileObject->FileObjectExtension;
1389 
1390                 /* Check if have a replacement top level device */
1391                 if (FileObjectExtension->TopDeviceObjectHint)
1392                 {
1393                     /* Use this instead of returning the top level device */
1394                     return FileObjectExtension->TopDeviceObjectHint;
1395                 }
1396             }
1397         }
1398 
1399         /* Return the highest attached device */
1400         DeviceObject = IoGetAttachedDevice(DeviceObject);
1401     }
1402 
1403     /* Return the DO we found */
1404     return DeviceObject;
1405 }
1406 
1407 /*
1408  * @implemented
1409  */
1410 NTSTATUS
1411 NTAPI
1412 IoGetRelatedTargetDevice(IN PFILE_OBJECT FileObject,
1413                          OUT PDEVICE_OBJECT *DeviceObject)
1414 {
1415     NTSTATUS Status;
1416     PDEVICE_NODE DeviceNode = NULL;
1417 
1418     /* Call the internal helper function */
1419     Status = IopGetRelatedTargetDevice(FileObject, &DeviceNode);
1420     if (NT_SUCCESS(Status) && DeviceNode)
1421     {
1422         *DeviceObject = DeviceNode->PhysicalDeviceObject;
1423     }
1424     return Status;
1425 }
1426 
1427 /*
1428  * @implemented
1429  */
1430 PDEVICE_OBJECT
1431 NTAPI
1432 IoGetBaseFileSystemDeviceObject(IN PFILE_OBJECT FileObject)
1433 {
1434     PDEVICE_OBJECT DeviceObject;
1435 
1436     /*
1437     * If the FILE_OBJECT's VPB is defined,
1438     * get the device from it.
1439     */
1440     if ((FileObject->Vpb) && (FileObject->Vpb->DeviceObject))
1441     {
1442         /* Use the VPB's Device Object's */
1443         DeviceObject = FileObject->Vpb->DeviceObject;
1444     }
1445     else if (!(FileObject->Flags & FO_DIRECT_DEVICE_OPEN) &&
1446              (FileObject->DeviceObject->Vpb) &&
1447              (FileObject->DeviceObject->Vpb->DeviceObject))
1448     {
1449         /* Use the VPB's File System Object */
1450         DeviceObject = FileObject->DeviceObject->Vpb->DeviceObject;
1451     }
1452     else
1453     {
1454         /* Use the FO's Device Object */
1455         DeviceObject = FileObject->DeviceObject;
1456     }
1457 
1458     /* Return the device object we found */
1459     ASSERT(DeviceObject != NULL);
1460     return DeviceObject;
1461 }
1462 
1463 /*
1464  * @implemented
1465  */
1466 NTSTATUS
1467 NTAPI
1468 IoRegisterLastChanceShutdownNotification(IN PDEVICE_OBJECT DeviceObject)
1469 {
1470     PSHUTDOWN_ENTRY Entry;
1471 
1472     /* Allocate the shutdown entry */
1473     Entry = ExAllocatePoolWithTag(NonPagedPool,
1474                                   sizeof(SHUTDOWN_ENTRY),
1475                                   TAG_SHUTDOWN_ENTRY);
1476     if (!Entry) return STATUS_INSUFFICIENT_RESOURCES;
1477 
1478     /* Set the DO */
1479     Entry->DeviceObject = DeviceObject;
1480 
1481     /* Reference it so it doesn't go away */
1482     ObReferenceObject(DeviceObject);
1483 
1484     /* Insert it into the list */
1485     ExInterlockedInsertHeadList(&LastChanceShutdownListHead,
1486                                 &Entry->ShutdownList,
1487                                 &ShutdownListLock);
1488 
1489     /* Set the shutdown registered flag */
1490     DeviceObject->Flags |= DO_SHUTDOWN_REGISTERED;
1491     return STATUS_SUCCESS;
1492 }
1493 
1494 /*
1495  * @implemented
1496  */
1497 NTSTATUS
1498 NTAPI
1499 IoRegisterShutdownNotification(PDEVICE_OBJECT DeviceObject)
1500 {
1501     PSHUTDOWN_ENTRY Entry;
1502 
1503     /* Allocate the shutdown entry */
1504     Entry = ExAllocatePoolWithTag(NonPagedPool,
1505                                   sizeof(SHUTDOWN_ENTRY),
1506                                   TAG_SHUTDOWN_ENTRY);
1507     if (!Entry) return STATUS_INSUFFICIENT_RESOURCES;
1508 
1509     /* Set the DO */
1510     Entry->DeviceObject = DeviceObject;
1511 
1512     /* Reference it so it doesn't go away */
1513     ObReferenceObject(DeviceObject);
1514 
1515     /* Insert it into the list */
1516     ExInterlockedInsertHeadList(&ShutdownListHead,
1517                                 &Entry->ShutdownList,
1518                                 &ShutdownListLock);
1519 
1520     /* Set the shutdown registered flag */
1521     DeviceObject->Flags |= DO_SHUTDOWN_REGISTERED;
1522     return STATUS_SUCCESS;
1523 }
1524 
1525 /*
1526  * @implemented
1527  */
1528 VOID
1529 NTAPI
1530 IoUnregisterShutdownNotification(PDEVICE_OBJECT DeviceObject)
1531 {
1532     PSHUTDOWN_ENTRY ShutdownEntry;
1533     PLIST_ENTRY NextEntry;
1534     KIRQL OldIrql;
1535 
1536     /* Remove the flag */
1537     DeviceObject->Flags &= ~DO_SHUTDOWN_REGISTERED;
1538 
1539     /* Acquire the shutdown lock and loop the shutdown list */
1540     KeAcquireSpinLock(&ShutdownListLock, &OldIrql);
1541     NextEntry = ShutdownListHead.Flink;
1542     while (NextEntry != &ShutdownListHead)
1543     {
1544         /* Get the entry */
1545         ShutdownEntry = CONTAINING_RECORD(NextEntry,
1546                                           SHUTDOWN_ENTRY,
1547                                           ShutdownList);
1548 
1549         /* Get if the DO matches */
1550         if (ShutdownEntry->DeviceObject == DeviceObject)
1551         {
1552             /* Remove it from the list */
1553             RemoveEntryList(NextEntry);
1554             NextEntry = NextEntry->Blink;
1555 
1556             /* Free the entry */
1557             ExFreePoolWithTag(ShutdownEntry, TAG_SHUTDOWN_ENTRY);
1558 
1559             /* Get rid of our reference to it */
1560             ObDereferenceObject(DeviceObject);
1561         }
1562 
1563         /* Go to the next entry */
1564         NextEntry = NextEntry->Flink;
1565     }
1566 
1567     /* Now loop the last chance list */
1568     NextEntry = LastChanceShutdownListHead.Flink;
1569     while (NextEntry != &LastChanceShutdownListHead)
1570     {
1571         /* Get the entry */
1572         ShutdownEntry = CONTAINING_RECORD(NextEntry,
1573                                           SHUTDOWN_ENTRY,
1574                                           ShutdownList);
1575 
1576         /* Get if the DO matches */
1577         if (ShutdownEntry->DeviceObject == DeviceObject)
1578         {
1579             /* Remove it from the list */
1580             RemoveEntryList(NextEntry);
1581             NextEntry = NextEntry->Blink;
1582 
1583             /* Free the entry */
1584             ExFreePoolWithTag(ShutdownEntry, TAG_SHUTDOWN_ENTRY);
1585 
1586             /* Get rid of our reference to it */
1587             ObDereferenceObject(DeviceObject);
1588         }
1589 
1590         /* Go to the next entry */
1591         NextEntry = NextEntry->Flink;
1592     }
1593 
1594     /* Release the shutdown lock */
1595     KeReleaseSpinLock(&ShutdownListLock, OldIrql);
1596 }
1597 
1598 /*
1599  * @implemented
1600  */
1601 VOID
1602 NTAPI
1603 IoSetStartIoAttributes(IN PDEVICE_OBJECT DeviceObject,
1604                        IN BOOLEAN DeferredStartIo,
1605                        IN BOOLEAN NonCancelable)
1606 {
1607     PEXTENDED_DEVOBJ_EXTENSION DeviceExtension;
1608 
1609     /* Get the Device Extension */
1610     DeviceExtension = IoGetDevObjExtension(DeviceObject);
1611 
1612     /* Set the flags the caller requested */
1613     DeviceExtension->StartIoFlags |= (DeferredStartIo) ? DOE_SIO_DEFERRED : 0;
1614     DeviceExtension->StartIoFlags |= (NonCancelable) ? DOE_SIO_NO_CANCEL : 0;
1615 }
1616 
1617 /*
1618  * @implemented
1619  */
1620 VOID
1621 NTAPI
1622 IoStartNextPacketByKey(IN PDEVICE_OBJECT DeviceObject,
1623                        IN BOOLEAN Cancelable,
1624                        IN ULONG Key)
1625 {
1626     PEXTENDED_DEVOBJ_EXTENSION DeviceExtension;
1627 
1628     /* Get the Device Extension */
1629     DeviceExtension = IoGetDevObjExtension(DeviceObject);
1630 
1631     /* Check if deferred start was requested */
1632     if (DeviceExtension->StartIoFlags & DOE_SIO_DEFERRED)
1633     {
1634         /* Call our internal function to handle the defered case */
1635         IopStartNextPacketByKeyEx(DeviceObject,
1636                                   Key,
1637                                   DOE_SIO_WITH_KEY |
1638                                   (Cancelable ? DOE_SIO_CANCELABLE : 0));
1639     }
1640     else
1641     {
1642         /* Call the normal routine */
1643         IopStartNextPacketByKey(DeviceObject, Cancelable, Key);
1644     }
1645 }
1646 
1647 /*
1648  * @implemented
1649  */
1650 VOID
1651 NTAPI
1652 IoStartNextPacket(IN PDEVICE_OBJECT DeviceObject,
1653                   IN BOOLEAN Cancelable)
1654 {
1655     PEXTENDED_DEVOBJ_EXTENSION DeviceExtension;
1656 
1657     /* Get the Device Extension */
1658     DeviceExtension = IoGetDevObjExtension(DeviceObject);
1659 
1660     /* Check if deferred start was requested */
1661     if (DeviceExtension->StartIoFlags & DOE_SIO_DEFERRED)
1662     {
1663         /* Call our internal function to handle the defered case */
1664         IopStartNextPacketByKeyEx(DeviceObject,
1665                                   0,
1666                                   DOE_SIO_NO_KEY |
1667                                   (Cancelable ? DOE_SIO_CANCELABLE : 0));
1668     }
1669     else
1670     {
1671         /* Call the normal routine */
1672         IopStartNextPacket(DeviceObject, Cancelable);
1673     }
1674 }
1675 
1676 /*
1677  * @implemented
1678  */
1679 VOID
1680 NTAPI
1681 IoStartPacket(IN PDEVICE_OBJECT DeviceObject,
1682               IN PIRP Irp,
1683               IN PULONG Key,
1684               IN PDRIVER_CANCEL CancelFunction)
1685 {
1686     BOOLEAN Stat;
1687     KIRQL OldIrql, CancelIrql;
1688 
1689     /* Raise to dispatch level */
1690     KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
1691 
1692     /* Check if we should acquire the cancel lock */
1693     if (CancelFunction)
1694     {
1695         /* Acquire and set it */
1696         IoAcquireCancelSpinLock(&CancelIrql);
1697         Irp->CancelRoutine = CancelFunction;
1698     }
1699 
1700     /* Check if we have a key */
1701     if (Key)
1702     {
1703         /* Insert by key */
1704         Stat = KeInsertByKeyDeviceQueue(&DeviceObject->DeviceQueue,
1705                                         &Irp->Tail.Overlay.DeviceQueueEntry,
1706                                         *Key);
1707     }
1708     else
1709     {
1710         /* Insert without a key */
1711         Stat = KeInsertDeviceQueue(&DeviceObject->DeviceQueue,
1712                                    &Irp->Tail.Overlay.DeviceQueueEntry);
1713     }
1714 
1715     /* Check if this was a first insert */
1716     if (!Stat)
1717     {
1718         /* Set the IRP */
1719         DeviceObject->CurrentIrp = Irp;
1720 
1721         /* Check if this is a cancelable packet */
1722         if (CancelFunction)
1723         {
1724             /* Check if the caller requested no cancellation */
1725             if (IoGetDevObjExtension(DeviceObject)->StartIoFlags &
1726                 DOE_SIO_NO_CANCEL)
1727             {
1728                 /* He did, so remove the cancel routine */
1729                 Irp->CancelRoutine = NULL;
1730             }
1731 
1732             /* Release the cancel lock */
1733             IoReleaseCancelSpinLock(CancelIrql);
1734         }
1735 
1736         /* Call the Start I/O function */
1737         DeviceObject->DriverObject->DriverStartIo(DeviceObject, Irp);
1738     }
1739     else
1740     {
1741         /* The packet was inserted... check if we have a cancel function */
1742         if (CancelFunction)
1743         {
1744             /* Check if the IRP got cancelled */
1745             if (Irp->Cancel)
1746             {
1747                 /*
1748                  * Set the cancel IRQL, clear the currnet cancel routine and
1749                  * call ours
1750                  */
1751                 Irp->CancelIrql = CancelIrql;
1752                 Irp->CancelRoutine = NULL;
1753                 CancelFunction(DeviceObject, Irp);
1754             }
1755             else
1756             {
1757                 /* Otherwise, release the lock */
1758                 IoReleaseCancelSpinLock(CancelIrql);
1759             }
1760         }
1761     }
1762 
1763     /* Return back to previous IRQL */
1764     KeLowerIrql(OldIrql);
1765 }
1766 
1767 #if defined (_WIN64)
1768 ULONG
1769 NTAPI
1770 IoWMIDeviceObjectToProviderId(
1771     IN PDEVICE_OBJECT DeviceObject)
1772 {
1773     UNIMPLEMENTED;
1774     return 0;
1775 }
1776 #endif
1777 
1778 /* EOF */
1779