[NTOSKRNL] Use captured variable to check parameters in NtRead/WriteFile
[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
1353 /* Cast the buffer to something we understand */
1354 FileObjectExtension = FileObject->FileObjectExtension;
1355
1356 /* Check if have a replacement top level device */
1357 if (FileObjectExtension->TopDeviceObjectHint)
1358 {
1359 /* Use this instead of returning the top level device */
1360 return FileObjectExtension->TopDeviceObjectHint;
1361 }
1362 }
1363 }
1364
1365 /* Return the highest attached device */
1366 DeviceObject = IoGetAttachedDevice(DeviceObject);
1367 }
1368
1369 /* Return the DO we found */
1370 return DeviceObject;
1371 }
1372
1373 /*
1374 * @implemented
1375 */
1376 NTSTATUS
1377 NTAPI
1378 IoGetRelatedTargetDevice(IN PFILE_OBJECT FileObject,
1379 OUT PDEVICE_OBJECT *DeviceObject)
1380 {
1381 NTSTATUS Status;
1382 PDEVICE_NODE DeviceNode = NULL;
1383
1384 /* Call the internal helper function */
1385 Status = IopGetRelatedTargetDevice(FileObject, &DeviceNode);
1386 if (NT_SUCCESS(Status) && DeviceNode)
1387 {
1388 *DeviceObject = DeviceNode->PhysicalDeviceObject;
1389 }
1390 return Status;
1391 }
1392
1393 /*
1394 * @implemented
1395 */
1396 PDEVICE_OBJECT
1397 NTAPI
1398 IoGetBaseFileSystemDeviceObject(IN PFILE_OBJECT FileObject)
1399 {
1400 PDEVICE_OBJECT DeviceObject;
1401
1402 /*
1403 * If the FILE_OBJECT's VPB is defined,
1404 * get the device from it.
1405 */
1406 if ((FileObject->Vpb) && (FileObject->Vpb->DeviceObject))
1407 {
1408 /* Use the VPB's Device Object's */
1409 DeviceObject = FileObject->Vpb->DeviceObject;
1410 }
1411 else if (!(FileObject->Flags & FO_DIRECT_DEVICE_OPEN) &&
1412 (FileObject->DeviceObject->Vpb) &&
1413 (FileObject->DeviceObject->Vpb->DeviceObject))
1414 {
1415 /* Use the VPB's File System Object */
1416 DeviceObject = FileObject->DeviceObject->Vpb->DeviceObject;
1417 }
1418 else
1419 {
1420 /* Use the FO's Device Object */
1421 DeviceObject = FileObject->DeviceObject;
1422 }
1423
1424 /* Return the device object we found */
1425 ASSERT(DeviceObject != NULL);
1426 return DeviceObject;
1427 }
1428
1429 /*
1430 * @implemented
1431 */
1432 NTSTATUS
1433 NTAPI
1434 IoRegisterLastChanceShutdownNotification(IN PDEVICE_OBJECT DeviceObject)
1435 {
1436 PSHUTDOWN_ENTRY Entry;
1437
1438 /* Allocate the shutdown entry */
1439 Entry = ExAllocatePoolWithTag(NonPagedPool,
1440 sizeof(SHUTDOWN_ENTRY),
1441 TAG_SHUTDOWN_ENTRY);
1442 if (!Entry) return STATUS_INSUFFICIENT_RESOURCES;
1443
1444 /* Set the DO */
1445 Entry->DeviceObject = DeviceObject;
1446
1447 /* Reference it so it doesn't go away */
1448 ObReferenceObject(DeviceObject);
1449
1450 /* Insert it into the list */
1451 ExInterlockedInsertHeadList(&LastChanceShutdownListHead,
1452 &Entry->ShutdownList,
1453 &ShutdownListLock);
1454
1455 /* Set the shutdown registered flag */
1456 DeviceObject->Flags |= DO_SHUTDOWN_REGISTERED;
1457 return STATUS_SUCCESS;
1458 }
1459
1460 /*
1461 * @implemented
1462 */
1463 NTSTATUS
1464 NTAPI
1465 IoRegisterShutdownNotification(PDEVICE_OBJECT DeviceObject)
1466 {
1467 PSHUTDOWN_ENTRY Entry;
1468
1469 /* Allocate the shutdown entry */
1470 Entry = ExAllocatePoolWithTag(NonPagedPool,
1471 sizeof(SHUTDOWN_ENTRY),
1472 TAG_SHUTDOWN_ENTRY);
1473 if (!Entry) return STATUS_INSUFFICIENT_RESOURCES;
1474
1475 /* Set the DO */
1476 Entry->DeviceObject = DeviceObject;
1477
1478 /* Reference it so it doesn't go away */
1479 ObReferenceObject(DeviceObject);
1480
1481 /* Insert it into the list */
1482 ExInterlockedInsertHeadList(&ShutdownListHead,
1483 &Entry->ShutdownList,
1484 &ShutdownListLock);
1485
1486 /* Set the shutdown registered flag */
1487 DeviceObject->Flags |= DO_SHUTDOWN_REGISTERED;
1488 return STATUS_SUCCESS;
1489 }
1490
1491 /*
1492 * @implemented
1493 */
1494 VOID
1495 NTAPI
1496 IoUnregisterShutdownNotification(PDEVICE_OBJECT DeviceObject)
1497 {
1498 PSHUTDOWN_ENTRY ShutdownEntry;
1499 PLIST_ENTRY NextEntry;
1500 KIRQL OldIrql;
1501
1502 /* Remove the flag */
1503 DeviceObject->Flags &= ~DO_SHUTDOWN_REGISTERED;
1504
1505 /* Acquire the shutdown lock and loop the shutdown list */
1506 KeAcquireSpinLock(&ShutdownListLock, &OldIrql);
1507 NextEntry = ShutdownListHead.Flink;
1508 while (NextEntry != &ShutdownListHead)
1509 {
1510 /* Get the entry */
1511 ShutdownEntry = CONTAINING_RECORD(NextEntry,
1512 SHUTDOWN_ENTRY,
1513 ShutdownList);
1514
1515 /* Get if the DO matches */
1516 if (ShutdownEntry->DeviceObject == DeviceObject)
1517 {
1518 /* Remove it from the list */
1519 RemoveEntryList(NextEntry);
1520 NextEntry = NextEntry->Blink;
1521
1522 /* Free the entry */
1523 ExFreePoolWithTag(ShutdownEntry, TAG_SHUTDOWN_ENTRY);
1524
1525 /* Get rid of our reference to it */
1526 ObDereferenceObject(DeviceObject);
1527 }
1528
1529 /* Go to the next entry */
1530 NextEntry = NextEntry->Flink;
1531 }
1532
1533 /* Now loop the last chance list */
1534 NextEntry = LastChanceShutdownListHead.Flink;
1535 while (NextEntry != &LastChanceShutdownListHead)
1536 {
1537 /* Get the entry */
1538 ShutdownEntry = CONTAINING_RECORD(NextEntry,
1539 SHUTDOWN_ENTRY,
1540 ShutdownList);
1541
1542 /* Get if the DO matches */
1543 if (ShutdownEntry->DeviceObject == DeviceObject)
1544 {
1545 /* Remove it from the list */
1546 RemoveEntryList(NextEntry);
1547 NextEntry = NextEntry->Blink;
1548
1549 /* Free the entry */
1550 ExFreePoolWithTag(ShutdownEntry, TAG_SHUTDOWN_ENTRY);
1551
1552 /* Get rid of our reference to it */
1553 ObDereferenceObject(DeviceObject);
1554 }
1555
1556 /* Go to the next entry */
1557 NextEntry = NextEntry->Flink;
1558 }
1559
1560 /* Release the shutdown lock */
1561 KeReleaseSpinLock(&ShutdownListLock, OldIrql);
1562 }
1563
1564 /*
1565 * @implemented
1566 */
1567 VOID
1568 NTAPI
1569 IoSetStartIoAttributes(IN PDEVICE_OBJECT DeviceObject,
1570 IN BOOLEAN DeferredStartIo,
1571 IN BOOLEAN NonCancelable)
1572 {
1573 PEXTENDED_DEVOBJ_EXTENSION DeviceExtension;
1574
1575 /* Get the Device Extension */
1576 DeviceExtension = IoGetDevObjExtension(DeviceObject);
1577
1578 /* Set the flags the caller requested */
1579 DeviceExtension->StartIoFlags |= (DeferredStartIo) ? DOE_SIO_DEFERRED : 0;
1580 DeviceExtension->StartIoFlags |= (NonCancelable) ? DOE_SIO_NO_CANCEL : 0;
1581 }
1582
1583 /*
1584 * @implemented
1585 */
1586 VOID
1587 NTAPI
1588 IoStartNextPacketByKey(IN PDEVICE_OBJECT DeviceObject,
1589 IN BOOLEAN Cancelable,
1590 IN ULONG Key)
1591 {
1592 PEXTENDED_DEVOBJ_EXTENSION DeviceExtension;
1593
1594 /* Get the Device Extension */
1595 DeviceExtension = IoGetDevObjExtension(DeviceObject);
1596
1597 /* Check if deferred start was requested */
1598 if (DeviceExtension->StartIoFlags & DOE_SIO_DEFERRED)
1599 {
1600 /* Call our internal function to handle the defered case */
1601 IopStartNextPacketByKeyEx(DeviceObject,
1602 Key,
1603 DOE_SIO_WITH_KEY |
1604 (Cancelable ? DOE_SIO_CANCELABLE : 0));
1605 }
1606 else
1607 {
1608 /* Call the normal routine */
1609 IopStartNextPacketByKey(DeviceObject, Cancelable, Key);
1610 }
1611 }
1612
1613 /*
1614 * @implemented
1615 */
1616 VOID
1617 NTAPI
1618 IoStartNextPacket(IN PDEVICE_OBJECT DeviceObject,
1619 IN BOOLEAN Cancelable)
1620 {
1621 PEXTENDED_DEVOBJ_EXTENSION DeviceExtension;
1622
1623 /* Get the Device Extension */
1624 DeviceExtension = IoGetDevObjExtension(DeviceObject);
1625
1626 /* Check if deferred start was requested */
1627 if (DeviceExtension->StartIoFlags & DOE_SIO_DEFERRED)
1628 {
1629 /* Call our internal function to handle the defered case */
1630 IopStartNextPacketByKeyEx(DeviceObject,
1631 0,
1632 DOE_SIO_NO_KEY |
1633 (Cancelable ? DOE_SIO_CANCELABLE : 0));
1634 }
1635 else
1636 {
1637 /* Call the normal routine */
1638 IopStartNextPacket(DeviceObject, Cancelable);
1639 }
1640 }
1641
1642 /*
1643 * @implemented
1644 */
1645 VOID
1646 NTAPI
1647 IoStartPacket(IN PDEVICE_OBJECT DeviceObject,
1648 IN PIRP Irp,
1649 IN PULONG Key,
1650 IN PDRIVER_CANCEL CancelFunction)
1651 {
1652 BOOLEAN Stat;
1653 KIRQL OldIrql, CancelIrql;
1654
1655 /* Raise to dispatch level */
1656 KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
1657
1658 /* Check if we should acquire the cancel lock */
1659 if (CancelFunction)
1660 {
1661 /* Acquire and set it */
1662 IoAcquireCancelSpinLock(&CancelIrql);
1663 Irp->CancelRoutine = CancelFunction;
1664 }
1665
1666 /* Check if we have a key */
1667 if (Key)
1668 {
1669 /* Insert by key */
1670 Stat = KeInsertByKeyDeviceQueue(&DeviceObject->DeviceQueue,
1671 &Irp->Tail.Overlay.DeviceQueueEntry,
1672 *Key);
1673 }
1674 else
1675 {
1676 /* Insert without a key */
1677 Stat = KeInsertDeviceQueue(&DeviceObject->DeviceQueue,
1678 &Irp->Tail.Overlay.DeviceQueueEntry);
1679 }
1680
1681 /* Check if this was a first insert */
1682 if (!Stat)
1683 {
1684 /* Set the IRP */
1685 DeviceObject->CurrentIrp = Irp;
1686
1687 /* Check if this is a cancelable packet */
1688 if (CancelFunction)
1689 {
1690 /* Check if the caller requested no cancellation */
1691 if (IoGetDevObjExtension(DeviceObject)->StartIoFlags &
1692 DOE_SIO_NO_CANCEL)
1693 {
1694 /* He did, so remove the cancel routine */
1695 Irp->CancelRoutine = NULL;
1696 }
1697
1698 /* Release the cancel lock */
1699 IoReleaseCancelSpinLock(CancelIrql);
1700 }
1701
1702 /* Call the Start I/O function */
1703 DeviceObject->DriverObject->DriverStartIo(DeviceObject, Irp);
1704 }
1705 else
1706 {
1707 /* The packet was inserted... check if we have a cancel function */
1708 if (CancelFunction)
1709 {
1710 /* Check if the IRP got cancelled */
1711 if (Irp->Cancel)
1712 {
1713 /*
1714 * Set the cancel IRQL, clear the currnet cancel routine and
1715 * call ours
1716 */
1717 Irp->CancelIrql = CancelIrql;
1718 Irp->CancelRoutine = NULL;
1719 CancelFunction(DeviceObject, Irp);
1720 }
1721 else
1722 {
1723 /* Otherwise, release the lock */
1724 IoReleaseCancelSpinLock(CancelIrql);
1725 }
1726 }
1727 }
1728
1729 /* Return back to previous IRQL */
1730 KeLowerIrql(OldIrql);
1731 }
1732
1733 #if defined (_WIN64)
1734 ULONG
1735 NTAPI
1736 IoWMIDeviceObjectToProviderId(
1737 IN PDEVICE_OBJECT DeviceObject)
1738 {
1739 UNIMPLEMENTED;
1740 return 0;
1741 }
1742 #endif
1743
1744 /* EOF */