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