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