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