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