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