mirror of
https://github.com/reactos/reactos.git
synced 2024-11-18 13:01:40 +00:00
1744 lines
51 KiB
C
1744 lines
51 KiB
C
/*
|
|
* PROJECT: ReactOS Kernel
|
|
* LICENSE: GPL - See COPYING in the top level directory
|
|
* FILE: ntoskrnl/io/iomgr/device.c
|
|
* PURPOSE: Device Object Management, including Notifications and Queues.
|
|
* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
|
|
* Filip Navara (navaraf@reactos.org)
|
|
* Hervé Poussineau (hpoussin@reactos.org)
|
|
*/
|
|
|
|
/* INCLUDES *******************************************************************/
|
|
|
|
#include <ntoskrnl.h>
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
/* GLOBALS ********************************************************************/
|
|
|
|
ULONG IopDeviceObjectNumber = 0;
|
|
LIST_ENTRY ShutdownListHead, LastChanceShutdownListHead;
|
|
KSPIN_LOCK ShutdownListLock;
|
|
extern LIST_ENTRY IopDiskFileSystemQueueHead;
|
|
extern LIST_ENTRY IopCdRomFileSystemQueueHead;
|
|
extern LIST_ENTRY IopTapeFileSystemQueueHead;
|
|
extern ERESOURCE IopDatabaseResource;
|
|
|
|
/* PRIVATE FUNCTIONS **********************************************************/
|
|
|
|
VOID
|
|
NTAPI
|
|
IopReadyDeviceObjects(IN PDRIVER_OBJECT Driver)
|
|
{
|
|
PDEVICE_OBJECT DeviceObject;
|
|
PAGED_CODE();
|
|
|
|
/* Set the driver as initialized */
|
|
Driver->Flags |= DRVO_INITIALIZED;
|
|
DeviceObject = Driver->DeviceObject;
|
|
while (DeviceObject)
|
|
{
|
|
/* Set every device as initialized too */
|
|
DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
|
|
DeviceObject = DeviceObject->NextDevice;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
IopDeleteDevice(IN PVOID ObjectBody)
|
|
{
|
|
PDEVICE_OBJECT DeviceObject = ObjectBody;
|
|
PDEVICE_NODE DeviceNode = IopGetDeviceNode(DeviceObject);
|
|
PAGED_CODE();
|
|
|
|
/* Cleanup and free the device node */
|
|
if (DeviceNode)
|
|
IopFreeDeviceNode(DeviceNode);
|
|
|
|
/* Dereference the driver object, referenced in IoCreateDevice */
|
|
if (DeviceObject->DriverObject)
|
|
ObDereferenceObject(DeviceObject->DriverObject);
|
|
}
|
|
|
|
|
|
PDEVICE_OBJECT
|
|
NTAPI
|
|
IopAttachDeviceToDeviceStackSafe(IN PDEVICE_OBJECT SourceDevice,
|
|
IN PDEVICE_OBJECT TargetDevice,
|
|
OUT PDEVICE_OBJECT *AttachedToDeviceObject OPTIONAL)
|
|
{
|
|
PDEVICE_OBJECT AttachedDevice;
|
|
PEXTENDED_DEVOBJ_EXTENSION SourceDeviceExtension;
|
|
|
|
/* Get the Attached Device and source extension */
|
|
AttachedDevice = IoGetAttachedDevice(TargetDevice);
|
|
SourceDeviceExtension = IoGetDevObjExtension(SourceDevice);
|
|
ASSERT(SourceDeviceExtension->AttachedTo == NULL);
|
|
|
|
/* Make sure that it's in a correct state */
|
|
if ((AttachedDevice->Flags & DO_DEVICE_INITIALIZING) ||
|
|
(IoGetDevObjExtension(AttachedDevice)->ExtensionFlags &
|
|
(DOE_UNLOAD_PENDING |
|
|
DOE_DELETE_PENDING |
|
|
DOE_REMOVE_PENDING |
|
|
DOE_REMOVE_PROCESSED)))
|
|
{
|
|
/* Device was unloading or being removed */
|
|
AttachedDevice = NULL;
|
|
}
|
|
else
|
|
{
|
|
/* Update atached device fields */
|
|
AttachedDevice->AttachedDevice = SourceDevice;
|
|
AttachedDevice->Spare1++;
|
|
|
|
/* Update the source with the attached data */
|
|
SourceDevice->StackSize = AttachedDevice->StackSize + 1;
|
|
SourceDevice->AlignmentRequirement = AttachedDevice->
|
|
AlignmentRequirement;
|
|
SourceDevice->SectorSize = AttachedDevice->SectorSize;
|
|
|
|
/* Check for pending start flag */
|
|
if (IoGetDevObjExtension(AttachedDevice)->ExtensionFlags &
|
|
DOE_START_PENDING)
|
|
{
|
|
/* Propagate */
|
|
IoGetDevObjExtension(SourceDevice)->ExtensionFlags |=
|
|
DOE_START_PENDING;
|
|
}
|
|
|
|
/* Set the attachment in the device extension */
|
|
SourceDeviceExtension->AttachedTo = AttachedDevice;
|
|
}
|
|
|
|
/* Return the attached device */
|
|
if (AttachedToDeviceObject) *AttachedToDeviceObject = AttachedDevice;
|
|
return AttachedDevice;
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
IoShutdownPnpDevices(VOID)
|
|
{
|
|
/* This routine is only used by Driver Verifier to validate shutdown */
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
IoShutdownSystem(IN ULONG Phase)
|
|
{
|
|
PLIST_ENTRY ListEntry;
|
|
PDEVICE_OBJECT DeviceObject;
|
|
PSHUTDOWN_ENTRY ShutdownEntry;
|
|
IO_STATUS_BLOCK StatusBlock;
|
|
PIRP Irp;
|
|
KEVENT Event;
|
|
NTSTATUS Status;
|
|
|
|
/* Initialize an event to wait on */
|
|
KeInitializeEvent(&Event, NotificationEvent, FALSE);
|
|
|
|
/* What phase? */
|
|
if (Phase == 0)
|
|
{
|
|
/* Shutdown PnP */
|
|
IoShutdownPnpDevices();
|
|
|
|
/* Loop first-chance shutdown notifications */
|
|
ListEntry = ExInterlockedRemoveHeadList(&ShutdownListHead,
|
|
&ShutdownListLock);
|
|
while (ListEntry)
|
|
{
|
|
/* Get the shutdown entry */
|
|
ShutdownEntry = CONTAINING_RECORD(ListEntry,
|
|
SHUTDOWN_ENTRY,
|
|
ShutdownList);
|
|
|
|
/* Get the attached device */
|
|
DeviceObject = IoGetAttachedDevice(ShutdownEntry->DeviceObject);
|
|
|
|
/* Build the shutdown IRP and call the driver */
|
|
Irp = IoBuildSynchronousFsdRequest(IRP_MJ_SHUTDOWN,
|
|
DeviceObject,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
&Event,
|
|
&StatusBlock);
|
|
if (Irp)
|
|
{
|
|
Status = IoCallDriver(DeviceObject, Irp);
|
|
if (Status == STATUS_PENDING)
|
|
{
|
|
/* Wait on the driver */
|
|
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
|
|
}
|
|
}
|
|
|
|
/* Remove the flag */
|
|
ShutdownEntry->DeviceObject->Flags &= ~DO_SHUTDOWN_REGISTERED;
|
|
|
|
/* Get rid of our reference to it */
|
|
ObDereferenceObject(ShutdownEntry->DeviceObject);
|
|
|
|
/* Free the shutdown entry and reset the event */
|
|
ExFreePoolWithTag(ShutdownEntry, TAG_SHUTDOWN_ENTRY);
|
|
KeClearEvent(&Event);
|
|
|
|
/* Go to the next entry */
|
|
ListEntry = ExInterlockedRemoveHeadList(&ShutdownListHead,
|
|
&ShutdownListLock);
|
|
}
|
|
}
|
|
else if (Phase == 1)
|
|
{
|
|
/* Acquire resource forever */
|
|
ExAcquireResourceExclusiveLite(&IopDatabaseResource, TRUE);
|
|
|
|
/* Shutdown disk file systems */
|
|
IopShutdownBaseFileSystems(&IopDiskFileSystemQueueHead);
|
|
|
|
/* Shutdown cdrom file systems */
|
|
IopShutdownBaseFileSystems(&IopCdRomFileSystemQueueHead);
|
|
|
|
/* Shutdown tape filesystems */
|
|
IopShutdownBaseFileSystems(&IopTapeFileSystemQueueHead);
|
|
|
|
/* Loop last-chance shutdown notifications */
|
|
ListEntry = ExInterlockedRemoveHeadList(&LastChanceShutdownListHead,
|
|
&ShutdownListLock);
|
|
while (ListEntry)
|
|
{
|
|
/* Get the shutdown entry */
|
|
ShutdownEntry = CONTAINING_RECORD(ListEntry,
|
|
SHUTDOWN_ENTRY,
|
|
ShutdownList);
|
|
|
|
/* Get the attached device */
|
|
DeviceObject = IoGetAttachedDevice(ShutdownEntry->DeviceObject);
|
|
|
|
/* Build the shutdown IRP and call the driver */
|
|
Irp = IoBuildSynchronousFsdRequest(IRP_MJ_SHUTDOWN,
|
|
DeviceObject,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
&Event,
|
|
&StatusBlock);
|
|
if (Irp)
|
|
{
|
|
Status = IoCallDriver(DeviceObject, Irp);
|
|
if (Status == STATUS_PENDING)
|
|
{
|
|
/* Wait on the driver */
|
|
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
|
|
}
|
|
}
|
|
|
|
/* Remove the flag */
|
|
ShutdownEntry->DeviceObject->Flags &= ~DO_SHUTDOWN_REGISTERED;
|
|
|
|
/* Get rid of our reference to it */
|
|
ObDereferenceObject(ShutdownEntry->DeviceObject);
|
|
|
|
/* Free the shutdown entry and reset the event */
|
|
ExFreePoolWithTag(ShutdownEntry, TAG_SHUTDOWN_ENTRY);
|
|
KeClearEvent(&Event);
|
|
|
|
/* Go to the next entry */
|
|
ListEntry = ExInterlockedRemoveHeadList(&LastChanceShutdownListHead,
|
|
&ShutdownListLock);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
IopGetDeviceObjectPointer(IN PUNICODE_STRING ObjectName,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
OUT PFILE_OBJECT *FileObject,
|
|
OUT PDEVICE_OBJECT *DeviceObject,
|
|
IN ULONG AttachFlag)
|
|
{
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
IO_STATUS_BLOCK StatusBlock;
|
|
PFILE_OBJECT LocalFileObject;
|
|
HANDLE FileHandle;
|
|
NTSTATUS Status;
|
|
|
|
/* Open the Device */
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
ObjectName,
|
|
OBJ_KERNEL_HANDLE,
|
|
NULL,
|
|
NULL);
|
|
Status = ZwOpenFile(&FileHandle,
|
|
DesiredAccess,
|
|
&ObjectAttributes,
|
|
&StatusBlock,
|
|
0,
|
|
FILE_NON_DIRECTORY_FILE | AttachFlag);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
/* Get File Object */
|
|
Status = ObReferenceObjectByHandle(FileHandle,
|
|
0,
|
|
IoFileObjectType,
|
|
KernelMode,
|
|
(PVOID*)&LocalFileObject,
|
|
NULL);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* Return the requested data */
|
|
*DeviceObject = IoGetRelatedDeviceObject(LocalFileObject);
|
|
*FileObject = LocalFileObject;
|
|
}
|
|
|
|
/* Close the handle */
|
|
ZwClose(FileHandle);
|
|
|
|
return Status;
|
|
}
|
|
|
|
PDEVICE_OBJECT
|
|
NTAPI
|
|
IopGetLowestDevice(IN PDEVICE_OBJECT DeviceObject)
|
|
{
|
|
PDEVICE_OBJECT LowestDevice;
|
|
PEXTENDED_DEVOBJ_EXTENSION DeviceExtension;
|
|
|
|
/* Get the current device and its extension */
|
|
LowestDevice = DeviceObject;
|
|
DeviceExtension = IoGetDevObjExtension(LowestDevice);
|
|
|
|
/* Keep looping as long as we're attached */
|
|
while (DeviceExtension->AttachedTo)
|
|
{
|
|
/* Get the lowest device and its extension */
|
|
LowestDevice = DeviceExtension->AttachedTo;
|
|
DeviceExtension = IoGetDevObjExtension(LowestDevice);
|
|
}
|
|
|
|
/* Return the lowest device */
|
|
return LowestDevice;
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
IopEditDeviceList(IN PDRIVER_OBJECT DriverObject,
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN IOP_DEVICE_LIST_OPERATION Type)
|
|
{
|
|
PDEVICE_OBJECT Previous;
|
|
KIRQL OldIrql;
|
|
|
|
/* Lock the Device list while we edit it */
|
|
OldIrql = KeAcquireQueuedSpinLock(LockQueueIoDatabaseLock);
|
|
|
|
/* Check the type of operation */
|
|
if (Type == IopRemove)
|
|
{
|
|
/* Get the current device and check if it's the current one */
|
|
Previous = DeviceObject->DriverObject->DeviceObject;
|
|
if (Previous == DeviceObject)
|
|
{
|
|
/* It is, simply unlink this one directly */
|
|
DeviceObject->DriverObject->DeviceObject =
|
|
DeviceObject->NextDevice;
|
|
}
|
|
else
|
|
{
|
|
/* It's not, so loop until we find the device */
|
|
while (Previous->NextDevice != DeviceObject)
|
|
{
|
|
/* Not this one, keep moving */
|
|
if (!Previous->NextDevice)
|
|
{
|
|
DPRINT1("Failed to remove PDO %p (not found)\n",
|
|
DeviceObject);
|
|
|
|
ASSERT(FALSE);
|
|
KeReleaseQueuedSpinLock(LockQueueIoDatabaseLock, OldIrql);
|
|
return;
|
|
}
|
|
Previous = Previous->NextDevice;
|
|
}
|
|
|
|
/* We found it, now unlink us */
|
|
Previous->NextDevice = DeviceObject->NextDevice;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Link the device object and the driver object */
|
|
DeviceObject->NextDevice = DriverObject->DeviceObject;
|
|
DriverObject->DeviceObject = DeviceObject;
|
|
}
|
|
|
|
/* Release the device list lock */
|
|
KeReleaseQueuedSpinLock(LockQueueIoDatabaseLock, OldIrql);
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
IopUnloadDevice(IN PDEVICE_OBJECT DeviceObject)
|
|
{
|
|
PDRIVER_OBJECT DriverObject = DeviceObject->DriverObject;
|
|
PEXTENDED_DEVOBJ_EXTENSION ThisExtension = IoGetDevObjExtension(DeviceObject);
|
|
|
|
/* Check if deletion is pending */
|
|
if (ThisExtension->ExtensionFlags & DOE_DELETE_PENDING)
|
|
{
|
|
if (DeviceObject->AttachedDevice)
|
|
{
|
|
DPRINT("Device object is in the middle of a device stack\n");
|
|
return;
|
|
}
|
|
|
|
if (DeviceObject->ReferenceCount)
|
|
{
|
|
DPRINT("Device object still has %d references\n", DeviceObject->ReferenceCount);
|
|
return;
|
|
}
|
|
|
|
/* Check if we have a Security Descriptor */
|
|
if (DeviceObject->SecurityDescriptor)
|
|
{
|
|
/* Dereference it */
|
|
ObDereferenceSecurityDescriptor(DeviceObject->SecurityDescriptor, 1);
|
|
}
|
|
|
|
/* Remove the device from the list */
|
|
IopEditDeviceList(DeviceObject->DriverObject, DeviceObject, IopRemove);
|
|
|
|
/* Dereference the keep-alive */
|
|
ObDereferenceObject(DeviceObject);
|
|
}
|
|
|
|
/* We can't unload a non-PnP driver here */
|
|
if (DriverObject->Flags & DRVO_LEGACY_DRIVER)
|
|
{
|
|
DPRINT("Not a PnP driver! '%wZ' will not be unloaded!\n", &DriverObject->DriverName);
|
|
return;
|
|
}
|
|
|
|
/* Return if we've already called unload (maybe we're in it?) */
|
|
if (DriverObject->Flags & DRVO_UNLOAD_INVOKED) return;
|
|
|
|
/* We can't unload unless there's an unload handler */
|
|
if (!DriverObject->DriverUnload)
|
|
{
|
|
DPRINT1("No DriverUnload function on PnP driver! '%wZ' will not be unloaded!\n", &DriverObject->DriverName);
|
|
return;
|
|
}
|
|
|
|
/* Bail if there are still devices present */
|
|
if (DriverObject->DeviceObject)
|
|
{
|
|
DPRINT("Devices still present! '%wZ' will not be unloaded!\n", &DriverObject->DriverName);
|
|
return;
|
|
}
|
|
|
|
DPRINT1("Unloading driver '%wZ' (automatic)\n", &DriverObject->DriverName);
|
|
|
|
/* Set the unload invoked flag */
|
|
DriverObject->Flags |= DRVO_UNLOAD_INVOKED;
|
|
|
|
/* Unload it */
|
|
DriverObject->DriverUnload(DriverObject);
|
|
|
|
/* Make object temporary so it can be deleted */
|
|
ObMakeTemporaryObject(DriverObject);
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
IopDereferenceDeviceObject(IN PDEVICE_OBJECT DeviceObject,
|
|
IN BOOLEAN ForceUnload)
|
|
{
|
|
/* Sanity check */
|
|
ASSERT(DeviceObject->ReferenceCount);
|
|
|
|
/* Dereference the device */
|
|
InterlockedDecrement(&DeviceObject->ReferenceCount);
|
|
|
|
/*
|
|
* Check if we can unload it and it's safe to unload (or if we're forcing
|
|
* an unload, which is OK too).
|
|
*/
|
|
ASSERT(!ForceUnload);
|
|
if (!(DeviceObject->ReferenceCount) &&
|
|
(IoGetDevObjExtension(DeviceObject)->ExtensionFlags & DOE_DELETE_PENDING))
|
|
{
|
|
/* Unload it */
|
|
IopUnloadDevice(DeviceObject);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
IopStartNextPacketByKey(IN PDEVICE_OBJECT DeviceObject,
|
|
IN BOOLEAN Cancelable,
|
|
IN ULONG Key)
|
|
{
|
|
PKDEVICE_QUEUE_ENTRY Entry;
|
|
PIRP Irp;
|
|
KIRQL OldIrql;
|
|
|
|
/* Acquire the cancel lock if this is cancelable */
|
|
if (Cancelable) IoAcquireCancelSpinLock(&OldIrql);
|
|
|
|
/* Clear the current IRP */
|
|
DeviceObject->CurrentIrp = NULL;
|
|
|
|
/* Remove an entry from the queue */
|
|
Entry = KeRemoveByKeyDeviceQueue(&DeviceObject->DeviceQueue, Key);
|
|
if (Entry)
|
|
{
|
|
/* Get the IRP and set it */
|
|
Irp = CONTAINING_RECORD(Entry, IRP, Tail.Overlay.DeviceQueueEntry);
|
|
DeviceObject->CurrentIrp = Irp;
|
|
|
|
/* Check if this is a cancelable packet */
|
|
if (Cancelable)
|
|
{
|
|
/* Check if the caller requested no cancellation */
|
|
if (IoGetDevObjExtension(DeviceObject)->StartIoFlags &
|
|
DOE_SIO_NO_CANCEL)
|
|
{
|
|
/* He did, so remove the cancel routine */
|
|
Irp->CancelRoutine = NULL;
|
|
}
|
|
|
|
/* Release the cancel lock */
|
|
IoReleaseCancelSpinLock(OldIrql);
|
|
}
|
|
|
|
/* Call the Start I/O Routine */
|
|
DeviceObject->DriverObject->DriverStartIo(DeviceObject, Irp);
|
|
}
|
|
else
|
|
{
|
|
/* Otherwise, release the cancel lock if we had acquired it */
|
|
if (Cancelable) IoReleaseCancelSpinLock(OldIrql);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
IopStartNextPacket(IN PDEVICE_OBJECT DeviceObject,
|
|
IN BOOLEAN Cancelable)
|
|
{
|
|
PKDEVICE_QUEUE_ENTRY Entry;
|
|
PIRP Irp;
|
|
KIRQL OldIrql;
|
|
|
|
/* Acquire the cancel lock if this is cancelable */
|
|
if (Cancelable) IoAcquireCancelSpinLock(&OldIrql);
|
|
|
|
/* Clear the current IRP */
|
|
DeviceObject->CurrentIrp = NULL;
|
|
|
|
/* Remove an entry from the queue */
|
|
Entry = KeRemoveDeviceQueue(&DeviceObject->DeviceQueue);
|
|
if (Entry)
|
|
{
|
|
/* Get the IRP and set it */
|
|
Irp = CONTAINING_RECORD(Entry, IRP, Tail.Overlay.DeviceQueueEntry);
|
|
DeviceObject->CurrentIrp = Irp;
|
|
|
|
/* Check if this is a cancelable packet */
|
|
if (Cancelable)
|
|
{
|
|
/* Check if the caller requested no cancellation */
|
|
if (IoGetDevObjExtension(DeviceObject)->StartIoFlags &
|
|
DOE_SIO_NO_CANCEL)
|
|
{
|
|
/* He did, so remove the cancel routine */
|
|
Irp->CancelRoutine = NULL;
|
|
}
|
|
|
|
/* Release the cancel lock */
|
|
IoReleaseCancelSpinLock(OldIrql);
|
|
}
|
|
|
|
/* Call the Start I/O Routine */
|
|
DeviceObject->DriverObject->DriverStartIo(DeviceObject, Irp);
|
|
}
|
|
else
|
|
{
|
|
/* Otherwise, release the cancel lock if we had acquired it */
|
|
if (Cancelable) IoReleaseCancelSpinLock(OldIrql);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
IopStartNextPacketByKeyEx(IN PDEVICE_OBJECT DeviceObject,
|
|
IN ULONG Key,
|
|
IN ULONG Flags)
|
|
{
|
|
PEXTENDED_DEVOBJ_EXTENSION DeviceExtension;
|
|
ULONG CurrentKey = Key;
|
|
ULONG CurrentFlags = Flags;
|
|
|
|
/* Get the device extension and start the packet loop */
|
|
DeviceExtension = IoGetDevObjExtension(DeviceObject);
|
|
while (TRUE)
|
|
{
|
|
/* Increase the count */
|
|
if (InterlockedIncrement(&DeviceExtension->StartIoCount) > 1)
|
|
{
|
|
/*
|
|
* We've already called the routine once...
|
|
* All we have to do is save the key and add the new flags
|
|
*/
|
|
DeviceExtension->StartIoFlags |= CurrentFlags;
|
|
DeviceExtension->StartIoKey = CurrentKey;
|
|
}
|
|
else
|
|
{
|
|
/* Mask out the current packet flags and key */
|
|
DeviceExtension->StartIoFlags &= ~(DOE_SIO_WITH_KEY |
|
|
DOE_SIO_NO_KEY |
|
|
DOE_SIO_CANCELABLE);
|
|
DeviceExtension->StartIoKey = 0;
|
|
|
|
/* Check if this is a packet start with key */
|
|
if (Flags & DOE_SIO_WITH_KEY)
|
|
{
|
|
/* Start the packet with a key */
|
|
IopStartNextPacketByKey(DeviceObject,
|
|
(Flags & DOE_SIO_CANCELABLE) ?
|
|
TRUE : FALSE,
|
|
CurrentKey);
|
|
}
|
|
else if (Flags & DOE_SIO_NO_KEY)
|
|
{
|
|
/* Start the packet */
|
|
IopStartNextPacket(DeviceObject,
|
|
(Flags & DOE_SIO_CANCELABLE) ?
|
|
TRUE : FALSE);
|
|
}
|
|
}
|
|
|
|
/* Decrease the Start I/O count and check if it's 0 now */
|
|
if (!InterlockedDecrement(&DeviceExtension->StartIoCount))
|
|
{
|
|
/* Get the current active key and flags */
|
|
CurrentKey = DeviceExtension->StartIoKey;
|
|
CurrentFlags = DeviceExtension->StartIoFlags & (DOE_SIO_WITH_KEY |
|
|
DOE_SIO_NO_KEY |
|
|
DOE_SIO_CANCELABLE);
|
|
|
|
/* Check if we should still loop */
|
|
if (!(CurrentFlags & (DOE_SIO_WITH_KEY | DOE_SIO_NO_KEY))) break;
|
|
}
|
|
else
|
|
{
|
|
/* There are still Start I/Os active, so quit this loop */
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
IopGetRelatedTargetDevice(IN PFILE_OBJECT FileObject,
|
|
OUT PDEVICE_NODE *DeviceNode)
|
|
{
|
|
NTSTATUS Status;
|
|
IO_STACK_LOCATION Stack = {0};
|
|
PDEVICE_RELATIONS DeviceRelations;
|
|
PDEVICE_OBJECT DeviceObject = NULL;
|
|
|
|
ASSERT(FileObject);
|
|
|
|
/* Get DeviceObject related to given FileObject */
|
|
DeviceObject = IoGetRelatedDeviceObject(FileObject);
|
|
if (!DeviceObject) return STATUS_NO_SUCH_DEVICE;
|
|
|
|
/* Define input parameters */
|
|
Stack.MajorFunction = IRP_MJ_PNP;
|
|
Stack.MinorFunction = IRP_MN_QUERY_DEVICE_RELATIONS;
|
|
Stack.Parameters.QueryDeviceRelations.Type = TargetDeviceRelation;
|
|
Stack.FileObject = FileObject;
|
|
|
|
/* Call the driver to query all relations (IRP_MJ_PNP) */
|
|
Status = IopSynchronousCall(DeviceObject,
|
|
&Stack,
|
|
(PVOID)&DeviceRelations);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
/* Make sure it's not NULL and contains only one object */
|
|
ASSERT(DeviceRelations);
|
|
ASSERT(DeviceRelations->Count == 1);
|
|
|
|
/* Finally get the device node */
|
|
*DeviceNode = IopGetDeviceNode(DeviceRelations->Objects[0]);
|
|
if (!*DeviceNode) Status = STATUS_NO_SUCH_DEVICE;
|
|
|
|
/* Free the DEVICE_RELATIONS structure, it's not needed anymore */
|
|
ExFreePool(DeviceRelations);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/* PUBLIC FUNCTIONS ***********************************************************/
|
|
|
|
/*
|
|
* IoAttachDevice
|
|
*
|
|
* Layers a device over the highest device in a device stack.
|
|
*
|
|
* Parameters
|
|
* SourceDevice
|
|
* Device to be attached.
|
|
*
|
|
* TargetDevice
|
|
* Name of the target device.
|
|
*
|
|
* AttachedDevice
|
|
* Caller storage for the device attached to.
|
|
*
|
|
* Status
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
IoAttachDevice(PDEVICE_OBJECT SourceDevice,
|
|
PUNICODE_STRING TargetDeviceName,
|
|
PDEVICE_OBJECT *AttachedDevice)
|
|
{
|
|
NTSTATUS Status;
|
|
PFILE_OBJECT FileObject = NULL;
|
|
PDEVICE_OBJECT TargetDevice = NULL;
|
|
|
|
/* Call the helper routine for an attach operation */
|
|
Status = IopGetDeviceObjectPointer(TargetDeviceName,
|
|
FILE_READ_ATTRIBUTES,
|
|
&FileObject,
|
|
&TargetDevice,
|
|
IO_ATTACH_DEVICE_API);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
/* Attach the device */
|
|
Status = IoAttachDeviceToDeviceStackSafe(SourceDevice,
|
|
TargetDevice,
|
|
AttachedDevice);
|
|
|
|
/* Dereference it */
|
|
ObDereferenceObject(FileObject);
|
|
return Status;
|
|
}
|
|
|
|
/*
|
|
* IoAttachDeviceByPointer
|
|
*
|
|
* Status
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
IoAttachDeviceByPointer(IN PDEVICE_OBJECT SourceDevice,
|
|
IN PDEVICE_OBJECT TargetDevice)
|
|
{
|
|
PDEVICE_OBJECT AttachedDevice;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
/* Do the Attach */
|
|
AttachedDevice = IoAttachDeviceToDeviceStack(SourceDevice, TargetDevice);
|
|
if (!AttachedDevice) Status = STATUS_NO_SUCH_DEVICE;
|
|
|
|
/* Return the status */
|
|
return Status;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
PDEVICE_OBJECT
|
|
NTAPI
|
|
IoAttachDeviceToDeviceStack(IN PDEVICE_OBJECT SourceDevice,
|
|
IN PDEVICE_OBJECT TargetDevice)
|
|
{
|
|
/* Attach it safely */
|
|
return IopAttachDeviceToDeviceStackSafe(SourceDevice,
|
|
TargetDevice,
|
|
NULL);
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
IoAttachDeviceToDeviceStackSafe(IN PDEVICE_OBJECT SourceDevice,
|
|
IN PDEVICE_OBJECT TargetDevice,
|
|
IN OUT PDEVICE_OBJECT *AttachedToDeviceObject)
|
|
{
|
|
/* Call the internal function */
|
|
if (!IopAttachDeviceToDeviceStackSafe(SourceDevice,
|
|
TargetDevice,
|
|
AttachedToDeviceObject))
|
|
{
|
|
/* Nothing found */
|
|
return STATUS_NO_SUCH_DEVICE;
|
|
}
|
|
|
|
/* Success! */
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* IoCreateDevice
|
|
*
|
|
* Allocates memory for and intializes a device object for use for
|
|
* a driver.
|
|
*
|
|
* Parameters
|
|
* DriverObject
|
|
* Driver object passed by IO Manager when the driver was loaded.
|
|
*
|
|
* DeviceExtensionSize
|
|
* Number of bytes for the device extension.
|
|
*
|
|
* DeviceName
|
|
* Unicode name of device.
|
|
*
|
|
* DeviceType
|
|
* Device type of the new device.
|
|
*
|
|
* DeviceCharacteristics
|
|
* Bit mask of device characteristics.
|
|
*
|
|
* Exclusive
|
|
* TRUE if only one thread can access the device at a time.
|
|
*
|
|
* DeviceObject
|
|
* On successful return this parameter is filled by pointer to
|
|
* allocated device object.
|
|
*
|
|
* Status
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
IoCreateDevice(IN PDRIVER_OBJECT DriverObject,
|
|
IN ULONG DeviceExtensionSize,
|
|
IN PUNICODE_STRING DeviceName,
|
|
IN DEVICE_TYPE DeviceType,
|
|
IN ULONG DeviceCharacteristics,
|
|
IN BOOLEAN Exclusive,
|
|
OUT PDEVICE_OBJECT *DeviceObject)
|
|
{
|
|
WCHAR AutoNameBuffer[20];
|
|
UNICODE_STRING AutoName;
|
|
PDEVICE_OBJECT CreatedDeviceObject;
|
|
PDEVOBJ_EXTENSION DeviceObjectExtension;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
NTSTATUS Status;
|
|
ULONG AlignedDeviceExtensionSize;
|
|
ULONG TotalSize;
|
|
HANDLE TempHandle;
|
|
PAGED_CODE();
|
|
|
|
/* Check if we have to generate a name */
|
|
if (DeviceCharacteristics & FILE_AUTOGENERATED_DEVICE_NAME)
|
|
{
|
|
/* Generate it */
|
|
swprintf(AutoNameBuffer,
|
|
L"\\Device\\%08lx",
|
|
InterlockedIncrementUL(&IopDeviceObjectNumber));
|
|
|
|
/* Initialize the name */
|
|
RtlInitUnicodeString(&AutoName, AutoNameBuffer);
|
|
DeviceName = &AutoName;
|
|
}
|
|
|
|
/* Initialize the Object Attributes */
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
DeviceName,
|
|
OBJ_KERNEL_HANDLE,
|
|
NULL,
|
|
SePublicOpenUnrestrictedSd);
|
|
|
|
/* Honor exclusive flag */
|
|
if (Exclusive) ObjectAttributes.Attributes |= OBJ_EXCLUSIVE;
|
|
|
|
/* Create a permanent object for named devices */
|
|
if (DeviceName) ObjectAttributes.Attributes |= OBJ_PERMANENT;
|
|
|
|
/* Align the Extension Size to 8-bytes */
|
|
AlignedDeviceExtensionSize = (DeviceExtensionSize + 7) &~ 7;
|
|
|
|
/* Total Size */
|
|
TotalSize = AlignedDeviceExtensionSize +
|
|
sizeof(DEVICE_OBJECT) +
|
|
sizeof(EXTENDED_DEVOBJ_EXTENSION);
|
|
|
|
/* Create the Device Object */
|
|
*DeviceObject = NULL;
|
|
Status = ObCreateObject(KernelMode,
|
|
IoDeviceObjectType,
|
|
&ObjectAttributes,
|
|
KernelMode,
|
|
NULL,
|
|
TotalSize,
|
|
0,
|
|
0,
|
|
(PVOID*)&CreatedDeviceObject);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
/* Clear the whole Object and extension so we don't null stuff manually */
|
|
RtlZeroMemory(CreatedDeviceObject, TotalSize);
|
|
|
|
/*
|
|
* Setup the Type and Size. Note that we don't use the aligned size,
|
|
* because that's only padding for the DevObjExt and not part of the Object.
|
|
*/
|
|
CreatedDeviceObject->Type = IO_TYPE_DEVICE;
|
|
CreatedDeviceObject->Size = sizeof(DEVICE_OBJECT) + (USHORT)DeviceExtensionSize;
|
|
|
|
/* The kernel extension is after the driver internal extension */
|
|
DeviceObjectExtension = (PDEVOBJ_EXTENSION)
|
|
((ULONG_PTR)(CreatedDeviceObject + 1) +
|
|
AlignedDeviceExtensionSize);
|
|
|
|
/* Set the Type and Size. Question: why is Size 0 on Windows? */
|
|
DeviceObjectExtension->Type = IO_TYPE_DEVICE_OBJECT_EXTENSION;
|
|
DeviceObjectExtension->Size = 0;
|
|
|
|
/* Initialize with Power Manager */
|
|
PoInitializeDeviceObject(DeviceObjectExtension);
|
|
|
|
/* Link the Object and Extension */
|
|
DeviceObjectExtension->DeviceObject = CreatedDeviceObject;
|
|
CreatedDeviceObject->DeviceObjectExtension = DeviceObjectExtension;
|
|
|
|
/* Set Device Object Data */
|
|
CreatedDeviceObject->DeviceType = DeviceType;
|
|
CreatedDeviceObject->Characteristics = DeviceCharacteristics;
|
|
CreatedDeviceObject->DeviceExtension = DeviceExtensionSize ?
|
|
CreatedDeviceObject + 1 :
|
|
NULL;
|
|
CreatedDeviceObject->StackSize = 1;
|
|
CreatedDeviceObject->AlignmentRequirement = 0;
|
|
|
|
/* Set the Flags */
|
|
CreatedDeviceObject->Flags = DO_DEVICE_INITIALIZING;
|
|
if (Exclusive) CreatedDeviceObject->Flags |= DO_EXCLUSIVE;
|
|
if (DeviceName) CreatedDeviceObject->Flags |= DO_DEVICE_HAS_NAME;
|
|
|
|
/* Attach a Vpb for Disks and Tapes, and create the Device Lock */
|
|
if ((CreatedDeviceObject->DeviceType == FILE_DEVICE_DISK) ||
|
|
(CreatedDeviceObject->DeviceType == FILE_DEVICE_VIRTUAL_DISK) ||
|
|
(CreatedDeviceObject->DeviceType == FILE_DEVICE_CD_ROM) ||
|
|
(CreatedDeviceObject->DeviceType == FILE_DEVICE_TAPE))
|
|
{
|
|
/* Create Vpb */
|
|
Status = IopCreateVpb(CreatedDeviceObject);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* Dereference the device object and fail */
|
|
ObDereferenceObject(CreatedDeviceObject);
|
|
return Status;
|
|
}
|
|
|
|
/* Initialize Lock Event */
|
|
KeInitializeEvent(&CreatedDeviceObject->DeviceLock,
|
|
SynchronizationEvent,
|
|
TRUE);
|
|
}
|
|
|
|
/* Set the right Sector Size */
|
|
switch (DeviceType)
|
|
{
|
|
/* All disk systems */
|
|
case FILE_DEVICE_DISK_FILE_SYSTEM:
|
|
case FILE_DEVICE_DISK:
|
|
case FILE_DEVICE_VIRTUAL_DISK:
|
|
|
|
/* The default is 512 bytes */
|
|
CreatedDeviceObject->SectorSize = 512;
|
|
break;
|
|
|
|
/* CD-ROM file systems */
|
|
case FILE_DEVICE_CD_ROM_FILE_SYSTEM:
|
|
|
|
/* The default is 2048 bytes */
|
|
CreatedDeviceObject->SectorSize = 2048;
|
|
}
|
|
|
|
/* Create the Device Queue */
|
|
if ((CreatedDeviceObject->DeviceType == FILE_DEVICE_DISK_FILE_SYSTEM) ||
|
|
(CreatedDeviceObject->DeviceType == FILE_DEVICE_FILE_SYSTEM) ||
|
|
(CreatedDeviceObject->DeviceType == FILE_DEVICE_CD_ROM_FILE_SYSTEM) ||
|
|
(CreatedDeviceObject->DeviceType == FILE_DEVICE_NETWORK_FILE_SYSTEM) ||
|
|
(CreatedDeviceObject->DeviceType == FILE_DEVICE_TAPE_FILE_SYSTEM))
|
|
{
|
|
/* Simple FS Devices, they don't need a real Device Queue */
|
|
InitializeListHead(&CreatedDeviceObject->Queue.ListEntry);
|
|
}
|
|
else
|
|
{
|
|
/* An actual Device, initialize its DQ */
|
|
KeInitializeDeviceQueue(&CreatedDeviceObject->DeviceQueue);
|
|
}
|
|
|
|
/* Insert the Object */
|
|
Status = ObInsertObject(CreatedDeviceObject,
|
|
NULL,
|
|
FILE_READ_DATA | FILE_WRITE_DATA,
|
|
1,
|
|
(PVOID*)&CreatedDeviceObject,
|
|
&TempHandle);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
/* Now do the final linking */
|
|
ObReferenceObject(DriverObject);
|
|
ASSERT((DriverObject->Flags & DRVO_UNLOAD_INVOKED) == 0);
|
|
CreatedDeviceObject->DriverObject = DriverObject;
|
|
IopEditDeviceList(DriverObject, CreatedDeviceObject, IopAdd);
|
|
|
|
/* Link with the power manager */
|
|
if (CreatedDeviceObject->Vpb) PoVolumeDevice(CreatedDeviceObject);
|
|
|
|
/* Close the temporary handle and return to caller */
|
|
ObCloseHandle(TempHandle, KernelMode);
|
|
*DeviceObject = CreatedDeviceObject;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* IoDeleteDevice
|
|
*
|
|
* Status
|
|
* @implemented
|
|
*/
|
|
VOID
|
|
NTAPI
|
|
IoDeleteDevice(IN PDEVICE_OBJECT DeviceObject)
|
|
{
|
|
PIO_TIMER Timer;
|
|
|
|
/* Check if the device is registered for shutdown notifications */
|
|
if (DeviceObject->Flags & DO_SHUTDOWN_REGISTERED)
|
|
{
|
|
/* Call the shutdown notifications */
|
|
IoUnregisterShutdownNotification(DeviceObject);
|
|
}
|
|
|
|
/* Check if it has a timer */
|
|
Timer = DeviceObject->Timer;
|
|
if (Timer)
|
|
{
|
|
/* Remove it and free it */
|
|
IopRemoveTimerFromTimerList(Timer);
|
|
ExFreePoolWithTag(Timer, TAG_IO_TIMER);
|
|
}
|
|
|
|
/* Check if the device has a name */
|
|
if (DeviceObject->Flags & DO_DEVICE_HAS_NAME)
|
|
{
|
|
/* It does, make it temporary so we can remove it */
|
|
ObMakeTemporaryObject(DeviceObject);
|
|
}
|
|
|
|
/* Set the pending delete flag */
|
|
IoGetDevObjExtension(DeviceObject)->ExtensionFlags |= DOE_DELETE_PENDING;
|
|
|
|
/* Unlink with the power manager */
|
|
if (DeviceObject->Vpb) PoRemoveVolumeDevice(DeviceObject);
|
|
|
|
/* Check if the device object can be unloaded */
|
|
if (!DeviceObject->ReferenceCount) IopUnloadDevice(DeviceObject);
|
|
}
|
|
|
|
/*
|
|
* IoDetachDevice
|
|
*
|
|
* Status
|
|
* @implemented
|
|
*/
|
|
VOID
|
|
NTAPI
|
|
IoDetachDevice(IN PDEVICE_OBJECT TargetDevice)
|
|
{
|
|
PEXTENDED_DEVOBJ_EXTENSION DeviceExtension;
|
|
|
|
/* Sanity check */
|
|
DeviceExtension = IoGetDevObjExtension(TargetDevice->AttachedDevice);
|
|
ASSERT(DeviceExtension->AttachedTo == TargetDevice);
|
|
|
|
/* Remove the attachment */
|
|
DeviceExtension->AttachedTo = NULL;
|
|
TargetDevice->AttachedDevice = NULL;
|
|
|
|
/* Check if it's ok to delete this device */
|
|
if ((IoGetDevObjExtension(TargetDevice)->ExtensionFlags & DOE_DELETE_PENDING) &&
|
|
!(TargetDevice->ReferenceCount))
|
|
{
|
|
/* It is, do it */
|
|
IopUnloadDevice(TargetDevice);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
IoEnumerateDeviceObjectList(IN PDRIVER_OBJECT DriverObject,
|
|
IN PDEVICE_OBJECT *DeviceObjectList,
|
|
IN ULONG DeviceObjectListSize,
|
|
OUT PULONG ActualNumberDeviceObjects)
|
|
{
|
|
ULONG ActualDevices = 1;
|
|
PDEVICE_OBJECT CurrentDevice = DriverObject->DeviceObject;
|
|
KIRQL OldIrql;
|
|
|
|
/* Lock the Device list while we enumerate it */
|
|
OldIrql = KeAcquireQueuedSpinLock(LockQueueIoDatabaseLock);
|
|
|
|
/* Find out how many devices we'll enumerate */
|
|
while ((CurrentDevice = CurrentDevice->NextDevice)) ActualDevices++;
|
|
|
|
/* Go back to the first */
|
|
CurrentDevice = DriverObject->DeviceObject;
|
|
|
|
/* Start by at least returning this */
|
|
*ActualNumberDeviceObjects = ActualDevices;
|
|
|
|
/* Check if we can support so many */
|
|
if ((ActualDevices * sizeof(PDEVICE_OBJECT)) > DeviceObjectListSize)
|
|
{
|
|
/* Fail because the buffer was too small */
|
|
KeReleaseQueuedSpinLock(LockQueueIoDatabaseLock, OldIrql);
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
/* Check if the caller wanted the device list */
|
|
if (DeviceObjectList)
|
|
{
|
|
/* Loop through all the devices */
|
|
while (ActualDevices)
|
|
{
|
|
/* Reference each Device */
|
|
ObReferenceObject(CurrentDevice);
|
|
|
|
/* Add it to the list */
|
|
*DeviceObjectList = CurrentDevice;
|
|
|
|
/* Go to the next one */
|
|
CurrentDevice = CurrentDevice->NextDevice;
|
|
ActualDevices--;
|
|
DeviceObjectList++;
|
|
}
|
|
}
|
|
|
|
/* Release the device list lock */
|
|
KeReleaseQueuedSpinLock(LockQueueIoDatabaseLock, OldIrql);
|
|
|
|
/* Return the status */
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* IoGetAttachedDevice
|
|
*
|
|
* Status
|
|
* @implemented
|
|
*/
|
|
PDEVICE_OBJECT
|
|
NTAPI
|
|
IoGetAttachedDevice(PDEVICE_OBJECT DeviceObject)
|
|
{
|
|
/* Get the last attached device */
|
|
while (DeviceObject->AttachedDevice)
|
|
{
|
|
/* Move to the next one */
|
|
DeviceObject = DeviceObject->AttachedDevice;
|
|
}
|
|
|
|
/* Return it */
|
|
return DeviceObject;
|
|
}
|
|
|
|
/*
|
|
* IoGetAttachedDeviceReference
|
|
*
|
|
* Status
|
|
* @implemented
|
|
*/
|
|
PDEVICE_OBJECT
|
|
NTAPI
|
|
IoGetAttachedDeviceReference(PDEVICE_OBJECT DeviceObject)
|
|
{
|
|
/* Reference the Attached Device */
|
|
DeviceObject = IoGetAttachedDevice(DeviceObject);
|
|
ObReferenceObject(DeviceObject);
|
|
return DeviceObject;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
PDEVICE_OBJECT
|
|
NTAPI
|
|
IoGetDeviceAttachmentBaseRef(IN PDEVICE_OBJECT DeviceObject)
|
|
{
|
|
/* Reference the lowest attached device */
|
|
DeviceObject = IopGetLowestDevice(DeviceObject);
|
|
ObReferenceObject(DeviceObject);
|
|
return DeviceObject;
|
|
}
|
|
|
|
/*
|
|
* IoGetDeviceObjectPointer
|
|
*
|
|
* Status
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
IoGetDeviceObjectPointer(IN PUNICODE_STRING ObjectName,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
OUT PFILE_OBJECT *FileObject,
|
|
OUT PDEVICE_OBJECT *DeviceObject)
|
|
{
|
|
/* Call the helper routine for a normal operation */
|
|
return IopGetDeviceObjectPointer(ObjectName,
|
|
DesiredAccess,
|
|
FileObject,
|
|
DeviceObject,
|
|
0);
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
IoGetDiskDeviceObject(IN PDEVICE_OBJECT FileSystemDeviceObject,
|
|
OUT PDEVICE_OBJECT *DiskDeviceObject)
|
|
{
|
|
PEXTENDED_DEVOBJ_EXTENSION DeviceExtension;
|
|
PVPB Vpb;
|
|
KIRQL OldIrql;
|
|
NTSTATUS Status;
|
|
|
|
/* Make sure there's a VPB */
|
|
if (!FileSystemDeviceObject->Vpb) return STATUS_INVALID_PARAMETER;
|
|
|
|
/* Acquire it */
|
|
IoAcquireVpbSpinLock(&OldIrql);
|
|
|
|
/* Get the Device Extension */
|
|
DeviceExtension = IoGetDevObjExtension(FileSystemDeviceObject);
|
|
|
|
/* Make sure this one has a VPB too */
|
|
Vpb = DeviceExtension->Vpb;
|
|
if (Vpb)
|
|
{
|
|
/* Make sure that it's mounted */
|
|
if ((Vpb->ReferenceCount) &&
|
|
(Vpb->Flags & VPB_MOUNTED))
|
|
{
|
|
/* Return the Disk Device Object */
|
|
*DiskDeviceObject = Vpb->RealDevice;
|
|
|
|
/* Reference it and return success */
|
|
ObReferenceObject(Vpb->RealDevice);
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
/* It's not, so return failure */
|
|
Status = STATUS_VOLUME_DISMOUNTED;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Fail */
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Release the lock */
|
|
IoReleaseVpbSpinLock(OldIrql);
|
|
return Status;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
PDEVICE_OBJECT
|
|
NTAPI
|
|
IoGetLowerDeviceObject(IN PDEVICE_OBJECT DeviceObject)
|
|
{
|
|
PEXTENDED_DEVOBJ_EXTENSION DeviceExtension;
|
|
PDEVICE_OBJECT LowerDeviceObject = NULL;
|
|
|
|
/* Make sure it's not getting deleted */
|
|
DeviceExtension = IoGetDevObjExtension(DeviceObject);
|
|
if (!(DeviceExtension->ExtensionFlags & (DOE_UNLOAD_PENDING |
|
|
DOE_DELETE_PENDING |
|
|
DOE_REMOVE_PENDING |
|
|
DOE_REMOVE_PROCESSED)))
|
|
{
|
|
/* Get the Lower Device Object */
|
|
LowerDeviceObject = DeviceExtension->AttachedTo;
|
|
|
|
/* Check that we got a valid device object */
|
|
if (LowerDeviceObject)
|
|
{
|
|
/* We did so let's reference it */
|
|
ObReferenceObject(LowerDeviceObject);
|
|
}
|
|
}
|
|
|
|
/* Return it */
|
|
return LowerDeviceObject;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
PDEVICE_OBJECT
|
|
NTAPI
|
|
IoGetRelatedDeviceObject(IN PFILE_OBJECT FileObject)
|
|
{
|
|
PDEVICE_OBJECT DeviceObject = FileObject->DeviceObject;
|
|
|
|
/* Check if we have a VPB with a device object */
|
|
if ((FileObject->Vpb) && (FileObject->Vpb->DeviceObject))
|
|
{
|
|
/* Then use the DO from the VPB */
|
|
ASSERT(!(FileObject->Flags & FO_DIRECT_DEVICE_OPEN));
|
|
DeviceObject = FileObject->Vpb->DeviceObject;
|
|
}
|
|
else if (!(FileObject->Flags & FO_DIRECT_DEVICE_OPEN) &&
|
|
(FileObject->DeviceObject->Vpb) &&
|
|
(FileObject->DeviceObject->Vpb->DeviceObject))
|
|
{
|
|
/* The disk device actually has a VPB, so get the DO from there */
|
|
DeviceObject = FileObject->DeviceObject->Vpb->DeviceObject;
|
|
}
|
|
else
|
|
{
|
|
/* Otherwise, this was a direct open */
|
|
DeviceObject = FileObject->DeviceObject;
|
|
}
|
|
|
|
/* Sanity check */
|
|
ASSERT(DeviceObject != NULL);
|
|
|
|
/* Check if we were attached */
|
|
if (DeviceObject->AttachedDevice)
|
|
{
|
|
/* Check if the file object has an extension present */
|
|
if (FileObject->Flags & FO_FILE_OBJECT_HAS_EXTENSION)
|
|
{
|
|
/* Sanity check, direct open files can't have this */
|
|
ASSERT(!(FileObject->Flags & FO_DIRECT_DEVICE_OPEN));
|
|
|
|
/* Check if the extension is really present */
|
|
if (FileObject->FileObjectExtension)
|
|
{
|
|
PFILE_OBJECT_EXTENSION FileObjectExtension;
|
|
|
|
/* Cast the buffer to something we understand */
|
|
FileObjectExtension = FileObject->FileObjectExtension;
|
|
|
|
/* Check if have a replacement top level device */
|
|
if (FileObjectExtension->TopDeviceObjectHint)
|
|
{
|
|
/* Use this instead of returning the top level device */
|
|
return FileObjectExtension->TopDeviceObjectHint;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Return the highest attached device */
|
|
DeviceObject = IoGetAttachedDevice(DeviceObject);
|
|
}
|
|
|
|
/* Return the DO we found */
|
|
return DeviceObject;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
IoGetRelatedTargetDevice(IN PFILE_OBJECT FileObject,
|
|
OUT PDEVICE_OBJECT *DeviceObject)
|
|
{
|
|
NTSTATUS Status;
|
|
PDEVICE_NODE DeviceNode = NULL;
|
|
|
|
/* Call the internal helper function */
|
|
Status = IopGetRelatedTargetDevice(FileObject, &DeviceNode);
|
|
if (NT_SUCCESS(Status) && DeviceNode)
|
|
{
|
|
*DeviceObject = DeviceNode->PhysicalDeviceObject;
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
PDEVICE_OBJECT
|
|
NTAPI
|
|
IoGetBaseFileSystemDeviceObject(IN PFILE_OBJECT FileObject)
|
|
{
|
|
PDEVICE_OBJECT DeviceObject;
|
|
|
|
/*
|
|
* If the FILE_OBJECT's VPB is defined,
|
|
* get the device from it.
|
|
*/
|
|
if ((FileObject->Vpb) && (FileObject->Vpb->DeviceObject))
|
|
{
|
|
/* Use the VPB's Device Object's */
|
|
DeviceObject = FileObject->Vpb->DeviceObject;
|
|
}
|
|
else if (!(FileObject->Flags & FO_DIRECT_DEVICE_OPEN) &&
|
|
(FileObject->DeviceObject->Vpb) &&
|
|
(FileObject->DeviceObject->Vpb->DeviceObject))
|
|
{
|
|
/* Use the VPB's File System Object */
|
|
DeviceObject = FileObject->DeviceObject->Vpb->DeviceObject;
|
|
}
|
|
else
|
|
{
|
|
/* Use the FO's Device Object */
|
|
DeviceObject = FileObject->DeviceObject;
|
|
}
|
|
|
|
/* Return the device object we found */
|
|
ASSERT(DeviceObject != NULL);
|
|
return DeviceObject;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
IoRegisterLastChanceShutdownNotification(IN PDEVICE_OBJECT DeviceObject)
|
|
{
|
|
PSHUTDOWN_ENTRY Entry;
|
|
|
|
/* Allocate the shutdown entry */
|
|
Entry = ExAllocatePoolWithTag(NonPagedPool,
|
|
sizeof(SHUTDOWN_ENTRY),
|
|
TAG_SHUTDOWN_ENTRY);
|
|
if (!Entry) return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
/* Set the DO */
|
|
Entry->DeviceObject = DeviceObject;
|
|
|
|
/* Reference it so it doesn't go away */
|
|
ObReferenceObject(DeviceObject);
|
|
|
|
/* Insert it into the list */
|
|
ExInterlockedInsertHeadList(&LastChanceShutdownListHead,
|
|
&Entry->ShutdownList,
|
|
&ShutdownListLock);
|
|
|
|
/* Set the shutdown registered flag */
|
|
DeviceObject->Flags |= DO_SHUTDOWN_REGISTERED;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
IoRegisterShutdownNotification(PDEVICE_OBJECT DeviceObject)
|
|
{
|
|
PSHUTDOWN_ENTRY Entry;
|
|
|
|
/* Allocate the shutdown entry */
|
|
Entry = ExAllocatePoolWithTag(NonPagedPool,
|
|
sizeof(SHUTDOWN_ENTRY),
|
|
TAG_SHUTDOWN_ENTRY);
|
|
if (!Entry) return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
/* Set the DO */
|
|
Entry->DeviceObject = DeviceObject;
|
|
|
|
/* Reference it so it doesn't go away */
|
|
ObReferenceObject(DeviceObject);
|
|
|
|
/* Insert it into the list */
|
|
ExInterlockedInsertHeadList(&ShutdownListHead,
|
|
&Entry->ShutdownList,
|
|
&ShutdownListLock);
|
|
|
|
/* Set the shutdown registered flag */
|
|
DeviceObject->Flags |= DO_SHUTDOWN_REGISTERED;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
VOID
|
|
NTAPI
|
|
IoUnregisterShutdownNotification(PDEVICE_OBJECT DeviceObject)
|
|
{
|
|
PSHUTDOWN_ENTRY ShutdownEntry;
|
|
PLIST_ENTRY NextEntry;
|
|
KIRQL OldIrql;
|
|
|
|
/* Remove the flag */
|
|
DeviceObject->Flags &= ~DO_SHUTDOWN_REGISTERED;
|
|
|
|
/* Acquire the shutdown lock and loop the shutdown list */
|
|
KeAcquireSpinLock(&ShutdownListLock, &OldIrql);
|
|
NextEntry = ShutdownListHead.Flink;
|
|
while (NextEntry != &ShutdownListHead)
|
|
{
|
|
/* Get the entry */
|
|
ShutdownEntry = CONTAINING_RECORD(NextEntry,
|
|
SHUTDOWN_ENTRY,
|
|
ShutdownList);
|
|
|
|
/* Get if the DO matches */
|
|
if (ShutdownEntry->DeviceObject == DeviceObject)
|
|
{
|
|
/* Remove it from the list */
|
|
RemoveEntryList(NextEntry);
|
|
NextEntry = NextEntry->Blink;
|
|
|
|
/* Free the entry */
|
|
ExFreePoolWithTag(ShutdownEntry, TAG_SHUTDOWN_ENTRY);
|
|
|
|
/* Get rid of our reference to it */
|
|
ObDereferenceObject(DeviceObject);
|
|
}
|
|
|
|
/* Go to the next entry */
|
|
NextEntry = NextEntry->Flink;
|
|
}
|
|
|
|
/* Now loop the last chance list */
|
|
NextEntry = LastChanceShutdownListHead.Flink;
|
|
while (NextEntry != &LastChanceShutdownListHead)
|
|
{
|
|
/* Get the entry */
|
|
ShutdownEntry = CONTAINING_RECORD(NextEntry,
|
|
SHUTDOWN_ENTRY,
|
|
ShutdownList);
|
|
|
|
/* Get if the DO matches */
|
|
if (ShutdownEntry->DeviceObject == DeviceObject)
|
|
{
|
|
/* Remove it from the list */
|
|
RemoveEntryList(NextEntry);
|
|
NextEntry = NextEntry->Blink;
|
|
|
|
/* Free the entry */
|
|
ExFreePoolWithTag(ShutdownEntry, TAG_SHUTDOWN_ENTRY);
|
|
|
|
/* Get rid of our reference to it */
|
|
ObDereferenceObject(DeviceObject);
|
|
}
|
|
|
|
/* Go to the next entry */
|
|
NextEntry = NextEntry->Flink;
|
|
}
|
|
|
|
/* Release the shutdown lock */
|
|
KeReleaseSpinLock(&ShutdownListLock, OldIrql);
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
VOID
|
|
NTAPI
|
|
IoSetStartIoAttributes(IN PDEVICE_OBJECT DeviceObject,
|
|
IN BOOLEAN DeferredStartIo,
|
|
IN BOOLEAN NonCancelable)
|
|
{
|
|
PEXTENDED_DEVOBJ_EXTENSION DeviceExtension;
|
|
|
|
/* Get the Device Extension */
|
|
DeviceExtension = IoGetDevObjExtension(DeviceObject);
|
|
|
|
/* Set the flags the caller requested */
|
|
DeviceExtension->StartIoFlags |= (DeferredStartIo) ? DOE_SIO_DEFERRED : 0;
|
|
DeviceExtension->StartIoFlags |= (NonCancelable) ? DOE_SIO_NO_CANCEL : 0;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
VOID
|
|
NTAPI
|
|
IoStartNextPacketByKey(IN PDEVICE_OBJECT DeviceObject,
|
|
IN BOOLEAN Cancelable,
|
|
IN ULONG Key)
|
|
{
|
|
PEXTENDED_DEVOBJ_EXTENSION DeviceExtension;
|
|
|
|
/* Get the Device Extension */
|
|
DeviceExtension = IoGetDevObjExtension(DeviceObject);
|
|
|
|
/* Check if deferred start was requested */
|
|
if (DeviceExtension->StartIoFlags & DOE_SIO_DEFERRED)
|
|
{
|
|
/* Call our internal function to handle the defered case */
|
|
IopStartNextPacketByKeyEx(DeviceObject,
|
|
Key,
|
|
DOE_SIO_WITH_KEY |
|
|
(Cancelable ? DOE_SIO_CANCELABLE : 0));
|
|
}
|
|
else
|
|
{
|
|
/* Call the normal routine */
|
|
IopStartNextPacketByKey(DeviceObject, Cancelable, Key);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
VOID
|
|
NTAPI
|
|
IoStartNextPacket(IN PDEVICE_OBJECT DeviceObject,
|
|
IN BOOLEAN Cancelable)
|
|
{
|
|
PEXTENDED_DEVOBJ_EXTENSION DeviceExtension;
|
|
|
|
/* Get the Device Extension */
|
|
DeviceExtension = IoGetDevObjExtension(DeviceObject);
|
|
|
|
/* Check if deferred start was requested */
|
|
if (DeviceExtension->StartIoFlags & DOE_SIO_DEFERRED)
|
|
{
|
|
/* Call our internal function to handle the defered case */
|
|
IopStartNextPacketByKeyEx(DeviceObject,
|
|
0,
|
|
DOE_SIO_NO_KEY |
|
|
(Cancelable ? DOE_SIO_CANCELABLE : 0));
|
|
}
|
|
else
|
|
{
|
|
/* Call the normal routine */
|
|
IopStartNextPacket(DeviceObject, Cancelable);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
VOID
|
|
NTAPI
|
|
IoStartPacket(IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PULONG Key,
|
|
IN PDRIVER_CANCEL CancelFunction)
|
|
{
|
|
BOOLEAN Stat;
|
|
KIRQL OldIrql, CancelIrql;
|
|
|
|
/* Raise to dispatch level */
|
|
KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
|
|
|
|
/* Check if we should acquire the cancel lock */
|
|
if (CancelFunction)
|
|
{
|
|
/* Acquire and set it */
|
|
IoAcquireCancelSpinLock(&CancelIrql);
|
|
Irp->CancelRoutine = CancelFunction;
|
|
}
|
|
|
|
/* Check if we have a key */
|
|
if (Key)
|
|
{
|
|
/* Insert by key */
|
|
Stat = KeInsertByKeyDeviceQueue(&DeviceObject->DeviceQueue,
|
|
&Irp->Tail.Overlay.DeviceQueueEntry,
|
|
*Key);
|
|
}
|
|
else
|
|
{
|
|
/* Insert without a key */
|
|
Stat = KeInsertDeviceQueue(&DeviceObject->DeviceQueue,
|
|
&Irp->Tail.Overlay.DeviceQueueEntry);
|
|
}
|
|
|
|
/* Check if this was a first insert */
|
|
if (!Stat)
|
|
{
|
|
/* Set the IRP */
|
|
DeviceObject->CurrentIrp = Irp;
|
|
|
|
/* Check if this is a cancelable packet */
|
|
if (CancelFunction)
|
|
{
|
|
/* Check if the caller requested no cancellation */
|
|
if (IoGetDevObjExtension(DeviceObject)->StartIoFlags &
|
|
DOE_SIO_NO_CANCEL)
|
|
{
|
|
/* He did, so remove the cancel routine */
|
|
Irp->CancelRoutine = NULL;
|
|
}
|
|
|
|
/* Release the cancel lock */
|
|
IoReleaseCancelSpinLock(CancelIrql);
|
|
}
|
|
|
|
/* Call the Start I/O function */
|
|
DeviceObject->DriverObject->DriverStartIo(DeviceObject, Irp);
|
|
}
|
|
else
|
|
{
|
|
/* The packet was inserted... check if we have a cancel function */
|
|
if (CancelFunction)
|
|
{
|
|
/* Check if the IRP got cancelled */
|
|
if (Irp->Cancel)
|
|
{
|
|
/*
|
|
* Set the cancel IRQL, clear the currnet cancel routine and
|
|
* call ours
|
|
*/
|
|
Irp->CancelIrql = CancelIrql;
|
|
Irp->CancelRoutine = NULL;
|
|
CancelFunction(DeviceObject, Irp);
|
|
}
|
|
else
|
|
{
|
|
/* Otherwise, release the lock */
|
|
IoReleaseCancelSpinLock(CancelIrql);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Return back to previous IRQL */
|
|
KeLowerIrql(OldIrql);
|
|
}
|
|
|
|
#if defined (_WIN64)
|
|
ULONG
|
|
NTAPI
|
|
IoWMIDeviceObjectToProviderId(
|
|
IN PDEVICE_OBJECT DeviceObject)
|
|
{
|
|
UNIMPLEMENTED;
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
/* EOF */
|