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