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