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