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