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