You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4975 lines
142 KiB
4975 lines
142 KiB
/*++
|
|
|
|
Copyright (C) Microsoft Corporation, 1997 - 1999
|
|
|
|
Module Name:
|
|
|
|
partmgr.c
|
|
|
|
Abstract:
|
|
|
|
This driver manages hides partitions from user mode, allowing access
|
|
only from other device drivers. This driver implements a notification
|
|
protocol for other drivers that want to be alerted of changes to the
|
|
partitions in the system.
|
|
|
|
Author:
|
|
|
|
Norbert Kusters 18-Apr-1997
|
|
|
|
Environment:
|
|
|
|
kernel mode only
|
|
|
|
Notes:
|
|
PartMgr calls out to the volume managers using the IOCTL_INTERNAL_VOLMGR_XXXX
|
|
IOCTLs in a number of places having acquired the driver extension mutex.
|
|
The Volume manager may then call back into PartMgr with that lock still held.
|
|
In these call backs manipulation of the structures utilised in the code which
|
|
issued the IOCTL_INTERNAL_VOLMGR_XXXXs should be avoided. This includes the
|
|
following:-
|
|
|
|
DriverExtension->VolumeManagerList
|
|
DriverExtension->DeviceExtensionList
|
|
DeviceExtension->PartitionList
|
|
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#define RTL_USE_AVL_TABLES 0
|
|
|
|
#include <ntosp.h>
|
|
#include <stdio.h>
|
|
#include <ntddvol.h>
|
|
#include <initguid.h>
|
|
#include <ntdddisk.h>
|
|
#include <volmgr.h>
|
|
#include <wmikm.h>
|
|
#include <wmilib.h>
|
|
#include <pmwmireg.h>
|
|
#include <pmwmicnt.h>
|
|
#include <partmgr.h>
|
|
#include <ntiologc.h>
|
|
#include <ioevent.h>
|
|
|
|
|
|
#ifndef UMAX
|
|
#define UMAX(_P1, _P2) (((_P1) > (_P2)) ? (_P1) : (_P2))
|
|
#endif
|
|
|
|
#define LockDriver(_DriverExtension) (KeWaitForSingleObject (&(_DriverExtension)->Mutex, \
|
|
Executive, \
|
|
KernelMode, \
|
|
FALSE, \
|
|
NULL))
|
|
|
|
#define UnlockDriver(_DriverExtension) (KeReleaseMutex (&(_DriverExtension)->Mutex, FALSE))
|
|
|
|
|
|
|
|
NTSTATUS
|
|
PmReadPartitionTableEx(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PDRIVE_LAYOUT_INFORMATION_EX* DriveLayout
|
|
);
|
|
|
|
|
|
NTSTATUS
|
|
PmWritePartitionTableEx(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PDRIVE_LAYOUT_INFORMATION_EX DriveLayout
|
|
);
|
|
|
|
NTSTATUS
|
|
DriverEntry(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PUNICODE_STRING RegistryPath
|
|
);
|
|
|
|
VOID
|
|
PmTakePartition(
|
|
IN PVOLMGR_LIST_ENTRY VolumeManagerEntry,
|
|
IN PDEVICE_OBJECT Partition,
|
|
IN PDEVICE_OBJECT WholeDiskPdo
|
|
);
|
|
|
|
NTSTATUS
|
|
PmGivePartition(
|
|
IN PVOLMGR_LIST_ENTRY VolumeManagerEntry,
|
|
IN PDEVICE_OBJECT Partition,
|
|
IN PDEVICE_OBJECT WholeDiskPdo
|
|
);
|
|
|
|
NTSTATUS
|
|
PmFindPartition(
|
|
IN PDEVICE_EXTENSION Extension,
|
|
IN ULONG PartitionNumber,
|
|
OUT PPARTITION_LIST_ENTRY *PartitionEntry
|
|
);
|
|
|
|
NTSTATUS
|
|
PmQueryDeviceRelations(
|
|
IN PDEVICE_EXTENSION Extension,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
PmRemoveDevice(
|
|
IN PDEVICE_EXTENSION Extension,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
PmCreateClose(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
PmPnp(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
PmPower(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
VOID
|
|
PmPowerNotify (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PVOID WorkItem
|
|
);
|
|
|
|
NTSTATUS
|
|
PmAddDevice(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PDEVICE_OBJECT PhysicalDeviceObject
|
|
);
|
|
|
|
NTSTATUS
|
|
PmVolumeManagerNotification(
|
|
IN PVOID NotificationStructure,
|
|
IN PVOID DriverExtension
|
|
);
|
|
|
|
NTSTATUS
|
|
PmNotifyPartitions(
|
|
IN PDEVICE_EXTENSION DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
PmBuildDependantVolumeRelations(
|
|
IN PDEVICE_EXTENSION Extension,
|
|
OUT PDEVICE_RELATIONS *Relations
|
|
);
|
|
|
|
NTSTATUS
|
|
PmQueryDependantVolumeList(
|
|
IN PDEVICE_OBJECT VolumeManager,
|
|
IN PDEVICE_OBJECT Partition,
|
|
IN PDEVICE_OBJECT WholeDiskPdo,
|
|
OUT PDEVICE_RELATIONS *DependantVolumes
|
|
);
|
|
|
|
NTSTATUS
|
|
PmQueryRemovalRelations(
|
|
IN PDEVICE_EXTENSION Extension,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
PmStartPartition(
|
|
IN PDEVICE_OBJECT Partition
|
|
);
|
|
|
|
NTSTATUS
|
|
PmRemovePartition(
|
|
IN PPARTITION_LIST_ENTRY Partition
|
|
);
|
|
|
|
NTSTATUS
|
|
PmDeviceControl(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
PmGetPartitionInformation(
|
|
IN PDEVICE_OBJECT Partition,
|
|
IN PFILE_OBJECT FileObject,
|
|
OUT PLONGLONG PartitionOffset,
|
|
OUT PLONGLONG PartitionLength
|
|
);
|
|
|
|
NTSTATUS
|
|
PmDiskGrowPartition(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
PmCheckForUnclaimedPartitions(
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
);
|
|
|
|
NTSTATUS
|
|
PmChangePartitionIoctl(
|
|
IN PDEVICE_EXTENSION Extension,
|
|
IN PPARTITION_LIST_ENTRY Partition,
|
|
IN ULONG IoctlCode
|
|
);
|
|
|
|
NTSTATUS
|
|
PmEjectVolumeManagers(
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
);
|
|
|
|
VOID
|
|
PmAddSignatures(
|
|
IN PDEVICE_EXTENSION Extension,
|
|
IN PDRIVE_LAYOUT_INFORMATION_EX Layout
|
|
);
|
|
|
|
RTL_GENERIC_COMPARE_RESULTS
|
|
PmTableSignatureCompareRoutine(
|
|
IN PRTL_GENERIC_TABLE Table,
|
|
IN PVOID First,
|
|
IN PVOID Second
|
|
);
|
|
|
|
RTL_GENERIC_COMPARE_RESULTS
|
|
PmTableGuidCompareRoutine(
|
|
IN PRTL_GENERIC_TABLE Table,
|
|
IN PVOID First,
|
|
IN PVOID Second
|
|
);
|
|
|
|
PVOID
|
|
PmTableAllocateRoutine(
|
|
IN PRTL_GENERIC_TABLE Table,
|
|
IN CLONG Size
|
|
);
|
|
|
|
VOID
|
|
PmTableFreeRoutine(
|
|
IN PRTL_GENERIC_TABLE Table,
|
|
IN PVOID Buffer
|
|
);
|
|
|
|
NTSTATUS
|
|
PmQueryDiskSignature(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
PmReadWrite(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
PmIoCompletion(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
);
|
|
|
|
NTSTATUS PmWmi(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
PmWmiFunctionControl(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN ULONG GuidIndex,
|
|
IN WMIENABLEDISABLECONTROL Function,
|
|
IN BOOLEAN Enable
|
|
);
|
|
|
|
VOID
|
|
PmDriverReinit(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PVOID DriverExtension,
|
|
IN ULONG Count
|
|
);
|
|
|
|
BOOLEAN
|
|
PmIsRedundantPath(
|
|
IN PDEVICE_EXTENSION Extension,
|
|
IN PDEVICE_EXTENSION WinningExtension,
|
|
IN ULONG Signature,
|
|
IN GUID* Guid
|
|
);
|
|
|
|
VOID
|
|
PmLogError(
|
|
IN PDEVICE_EXTENSION Extension,
|
|
IN PDEVICE_EXTENSION WinningExtension,
|
|
IN NTSTATUS SpecificIoStatus
|
|
);
|
|
|
|
ULONG
|
|
PmQueryRegistrySignature(
|
|
);
|
|
|
|
VOID
|
|
PmQueryRegistryGuid(
|
|
IN PDO_EXTENSION DriverExtension
|
|
);
|
|
|
|
NTSTATUS
|
|
PmQueryRegistryGuidQueryRoutine(
|
|
IN PWSTR ValueName,
|
|
IN ULONG ValueType,
|
|
IN PVOID ValueData,
|
|
IN ULONG ValueLength,
|
|
IN PVOID Context,
|
|
IN PVOID EntryContext
|
|
);
|
|
|
|
VOID
|
|
PmSigCheckUpdateEpoch (
|
|
IN PDEVICE_EXTENSION DeviceExtension,
|
|
IN PLIST_ENTRY CompletionList
|
|
);
|
|
|
|
VOID
|
|
PmSigCheckCompleteNotificationIrps(
|
|
IN PLIST_ENTRY CompletionList
|
|
);
|
|
|
|
NTSTATUS
|
|
PmSigCheckFillInNotificationIrp (
|
|
IN PDO_EXTENSION DriverExtension,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
PmSigCheckNotificationInsert (
|
|
IN PDEVICE_EXTENSION DeviceExtension,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
PmSigCheckNotificationCancel (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
PmCheckAndUpdateSignature (
|
|
IN PDEVICE_EXTENSION DeviceExtension,
|
|
IN BOOLEAN IssueSigCheckNotifications,
|
|
IN BOOLEAN ForceSignatureCheck
|
|
);
|
|
|
|
BOOLEAN
|
|
LockDriverWithTimeout(
|
|
IN PDO_EXTENSION DriverExtension
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(INIT, DriverEntry)
|
|
#pragma alloc_text(INIT, PmQueryRegistrySignature)
|
|
#pragma alloc_text(INIT, PmQueryRegistryGuid)
|
|
#pragma alloc_text(INIT, PmQueryRegistryGuidQueryRoutine)
|
|
#pragma alloc_text(PAGE, PmTakePartition)
|
|
#pragma alloc_text(PAGE, PmGivePartition)
|
|
#pragma alloc_text(PAGE, PmFindPartition)
|
|
#pragma alloc_text(PAGE, PmQueryDeviceRelations)
|
|
#pragma alloc_text(PAGE, PmRemoveDevice)
|
|
#pragma alloc_text(PAGE, PmCreateClose)
|
|
#pragma alloc_text(PAGE, PmPnp)
|
|
#pragma alloc_text(PAGE, PmAddDevice)
|
|
#pragma alloc_text(PAGE, PmVolumeManagerNotification)
|
|
#pragma alloc_text(PAGE, PmNotifyPartitions)
|
|
#pragma alloc_text(PAGE, PmBuildDependantVolumeRelations)
|
|
#pragma alloc_text(PAGE, PmQueryDependantVolumeList)
|
|
#pragma alloc_text(PAGE, PmQueryRemovalRelations)
|
|
#pragma alloc_text(PAGE, PmStartPartition)
|
|
#pragma alloc_text(PAGE, PmDeviceControl)
|
|
#pragma alloc_text(PAGE, PmGetPartitionInformation)
|
|
#pragma alloc_text(PAGE, PmDiskGrowPartition)
|
|
#pragma alloc_text(PAGE, PmCheckForUnclaimedPartitions)
|
|
#pragma alloc_text(PAGE, PmChangePartitionIoctl)
|
|
#pragma alloc_text(PAGE, PmEjectVolumeManagers)
|
|
#pragma alloc_text(PAGE, PmAddSignatures)
|
|
#pragma alloc_text(PAGE, PmTableSignatureCompareRoutine)
|
|
#pragma alloc_text(PAGE, PmTableGuidCompareRoutine)
|
|
#pragma alloc_text(PAGE, PmTableAllocateRoutine)
|
|
#pragma alloc_text(PAGE, PmTableFreeRoutine)
|
|
#pragma alloc_text(PAGE, PmQueryDiskSignature)
|
|
#pragma alloc_text(PAGE, PmWmi)
|
|
#pragma alloc_text(PAGE, PmWmiFunctionControl)
|
|
#pragma alloc_text(PAGE, PmReadPartitionTableEx)
|
|
#pragma alloc_text(PAGE, PmWritePartitionTableEx)
|
|
#pragma alloc_text(PAGE, PmDriverReinit)
|
|
#pragma alloc_text(PAGE, PmIsRedundantPath)
|
|
#pragma alloc_text(PAGE, PmLogError)
|
|
#pragma alloc_text(PAGE, PmSigCheckCompleteNotificationIrps)
|
|
#pragma alloc_text(PAGE, PmSigCheckFillInNotificationIrp)
|
|
#pragma alloc_text(PAGE, PmCheckAndUpdateSignature)
|
|
#pragma alloc_text(PAGE, LockDriverWithTimeout)
|
|
#endif
|
|
|
|
const GUID guidNull = { 0 };
|
|
|
|
|
|
BOOLEAN
|
|
LockDriverWithTimeout(
|
|
IN PDO_EXTENSION DriverExtension
|
|
)
|
|
{
|
|
LARGE_INTEGER timeout;
|
|
NTSTATUS status;
|
|
|
|
timeout.QuadPart = -10*1000*1000*6; // 6 seconds.
|
|
status = KeWaitForSingleObject(&DriverExtension->Mutex, Executive,
|
|
KernelMode, FALSE, &timeout);
|
|
|
|
return (status != STATUS_TIMEOUT);
|
|
}
|
|
|
|
NTSTATUS
|
|
PmPassThrough(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the dispatch for IRP_MJ_PNP_POWER.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Supplies the device object.
|
|
|
|
Irp - Supplies the IO request packet.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION extension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
|
|
|
IoSkipCurrentIrpStackLocation(Irp);
|
|
return IoCallDriver(extension->TargetObject, Irp);
|
|
}
|
|
|
|
NTSTATUS
|
|
PmSignalCompletion(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Event
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will signal the event given as context.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Supplies the device object.
|
|
|
|
Irp - Supplies the IO request packet.
|
|
|
|
Event - Supplies the event to signal.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
KeSetEvent((PKEVENT) Event, IO_NO_INCREMENT, FALSE);
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
VOID
|
|
PmTakePartition(
|
|
IN PVOLMGR_LIST_ENTRY VolumeManagerEntry,
|
|
IN PDEVICE_OBJECT Partition,
|
|
IN PDEVICE_OBJECT WholeDiskPdo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine passes the given partition to the given volume manager
|
|
and waits to see if the volume manager accepts the partition. A success
|
|
status indicates that the volume manager has accepted the partition.
|
|
|
|
Arguments:
|
|
|
|
VolumeManager - Supplies a volume manager.
|
|
|
|
Partition - Supplies a partition.
|
|
|
|
WholeDiskPdo - Supplies the whole disk PDO.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
KEVENT event;
|
|
VOLMGR_PARTITION_INFORMATION input;
|
|
PIRP irp;
|
|
IO_STATUS_BLOCK ioStatus;
|
|
NTSTATUS status;
|
|
|
|
if (!VolumeManagerEntry) {
|
|
return;
|
|
}
|
|
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
input.PartitionDeviceObject = Partition;
|
|
input.WholeDiskPdo = WholeDiskPdo;
|
|
|
|
irp = IoBuildDeviceIoControlRequest(IOCTL_INTERNAL_VOLMGR_PARTITION_REMOVED,
|
|
VolumeManagerEntry->VolumeManager,
|
|
&input, sizeof(input), NULL, 0, TRUE,
|
|
&event, &ioStatus);
|
|
if (!irp) {
|
|
return;
|
|
}
|
|
|
|
status = IoCallDriver(VolumeManagerEntry->VolumeManager, irp);
|
|
if (status == STATUS_PENDING) {
|
|
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
|
}
|
|
|
|
VolumeManagerEntry->RefCount--;
|
|
if (!VolumeManagerEntry->RefCount) {
|
|
VolumeManagerEntry->VolumeManager = NULL;
|
|
ObDereferenceObject(VolumeManagerEntry->VolumeManagerFileObject);
|
|
VolumeManagerEntry->VolumeManagerFileObject = NULL;
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
PmGivePartition(
|
|
IN PVOLMGR_LIST_ENTRY VolumeManagerEntry,
|
|
IN PDEVICE_OBJECT Partition,
|
|
IN PDEVICE_OBJECT WholeDiskPdo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine passes the given partition to the given volume manager
|
|
and waits to see if the volume manager accepts the partition. A success
|
|
status indicates that the volume manager has accepted the partition.
|
|
|
|
Arguments:
|
|
|
|
VolumeManager - Supplies a volume manager.
|
|
|
|
Partition - Supplies a partition.
|
|
|
|
WholeDiskPdo - Supplies the whole disk PDO.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
KEVENT event;
|
|
VOLMGR_PARTITION_INFORMATION input;
|
|
PIRP irp;
|
|
IO_STATUS_BLOCK ioStatus;
|
|
|
|
if (!VolumeManagerEntry->RefCount) {
|
|
status = IoGetDeviceObjectPointer(
|
|
&VolumeManagerEntry->VolumeManagerName, FILE_READ_DATA,
|
|
&VolumeManagerEntry->VolumeManagerFileObject,
|
|
&VolumeManagerEntry->VolumeManager);
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
}
|
|
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
input.PartitionDeviceObject = Partition;
|
|
input.WholeDiskPdo = WholeDiskPdo;
|
|
|
|
irp = IoBuildDeviceIoControlRequest(IOCTL_INTERNAL_VOLMGR_PARTITION_ARRIVED,
|
|
VolumeManagerEntry->VolumeManager,
|
|
&input, sizeof(input),
|
|
NULL, 0, TRUE, &event, &ioStatus);
|
|
if (!irp) {
|
|
if (!VolumeManagerEntry->RefCount) {
|
|
VolumeManagerEntry->VolumeManager = NULL;
|
|
ObDereferenceObject(VolumeManagerEntry->VolumeManagerFileObject);
|
|
VolumeManagerEntry->VolumeManagerFileObject = NULL;
|
|
}
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
status = IoCallDriver(VolumeManagerEntry->VolumeManager, irp);
|
|
if (status == STATUS_PENDING) {
|
|
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
|
status = ioStatus.Status;
|
|
}
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
VolumeManagerEntry->RefCount++;
|
|
} else {
|
|
if (!VolumeManagerEntry->RefCount) {
|
|
VolumeManagerEntry->VolumeManager = NULL;
|
|
ObDereferenceObject(VolumeManagerEntry->VolumeManagerFileObject);
|
|
VolumeManagerEntry->VolumeManagerFileObject = NULL;
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
PmFindPartition(
|
|
IN PDEVICE_EXTENSION Extension,
|
|
IN ULONG PartitionNumber,
|
|
OUT PPARTITION_LIST_ENTRY* Partition
|
|
)
|
|
|
|
{
|
|
PLIST_ENTRY l;
|
|
PPARTITION_LIST_ENTRY partition;
|
|
KEVENT event;
|
|
PIRP irp;
|
|
STORAGE_DEVICE_NUMBER deviceNumber;
|
|
IO_STATUS_BLOCK ioStatus;
|
|
NTSTATUS status;
|
|
|
|
ASSERT(Partition);
|
|
*Partition = NULL;
|
|
|
|
for (l = Extension->PartitionList.Flink; l != &Extension->PartitionList;
|
|
l = l->Flink) {
|
|
|
|
partition = CONTAINING_RECORD(l, PARTITION_LIST_ENTRY, ListEntry);
|
|
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
irp = IoBuildDeviceIoControlRequest(IOCTL_STORAGE_GET_DEVICE_NUMBER,
|
|
partition->TargetObject, NULL, 0,
|
|
&deviceNumber,
|
|
sizeof(deviceNumber), FALSE,
|
|
&event, &ioStatus);
|
|
if (!irp) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
status = IoCallDriver(partition->TargetObject, irp);
|
|
if (status == STATUS_PENDING) {
|
|
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
|
status = ioStatus.Status;
|
|
}
|
|
|
|
if (NT_SUCCESS(status) &&
|
|
PartitionNumber == deviceNumber.PartitionNumber) {
|
|
|
|
*Partition = partition;
|
|
return status;
|
|
}
|
|
}
|
|
|
|
return STATUS_NOT_FOUND;
|
|
}
|
|
|
|
NTSTATUS
|
|
PmChangePartitionIoctl(
|
|
IN PDEVICE_EXTENSION Extension,
|
|
IN PPARTITION_LIST_ENTRY Partition,
|
|
IN ULONG IoctlCode
|
|
)
|
|
|
|
{
|
|
PVOLMGR_LIST_ENTRY volumeEntry;
|
|
KEVENT event;
|
|
VOLMGR_PARTITION_INFORMATION input;
|
|
PIRP irp;
|
|
IO_STATUS_BLOCK ioStatus;
|
|
NTSTATUS status;
|
|
|
|
volumeEntry = Partition->VolumeManagerEntry;
|
|
if (!volumeEntry) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
|
|
input.PartitionDeviceObject = Partition->TargetObject;
|
|
input.WholeDiskPdo = Partition->WholeDiskPdo;
|
|
|
|
irp = IoBuildDeviceIoControlRequest(
|
|
IoctlCode, volumeEntry->VolumeManager, &input, sizeof(input),
|
|
NULL, 0, TRUE, &event, &ioStatus);
|
|
if (!irp) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
status = IoCallDriver(volumeEntry->VolumeManager, irp);
|
|
if (status == STATUS_PENDING) {
|
|
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
|
status = ioStatus.Status;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
PmStartPartition(
|
|
IN PDEVICE_OBJECT Partition
|
|
)
|
|
|
|
{
|
|
PIRP irp;
|
|
KEVENT event;
|
|
PIO_STACK_LOCATION irpSp;
|
|
NTSTATUS status;
|
|
|
|
irp = IoAllocateIrp(Partition->StackSize, FALSE);
|
|
if (!irp) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
irpSp = IoGetNextIrpStackLocation(irp);
|
|
irpSp->MajorFunction = IRP_MJ_PNP;
|
|
irpSp->MinorFunction = IRP_MN_START_DEVICE;
|
|
irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
|
|
IoSetCompletionRoutine(irp, PmSignalCompletion, &event, TRUE, TRUE, TRUE);
|
|
|
|
IoCallDriver(Partition, irp);
|
|
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
|
|
|
status = irp->IoStatus.Status;
|
|
|
|
IoFreeIrp(irp);
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
PmQueryDeviceRelations(
|
|
IN PDEVICE_EXTENSION Extension,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine processes the query device relations request.
|
|
|
|
Arguments:
|
|
|
|
Extension - Supplies the device extension.
|
|
|
|
Irp - Supplies the IO request packet.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
PDRIVE_LAYOUT_INFORMATION_EX newLayout;
|
|
KEVENT event;
|
|
PDEVICE_RELATIONS deviceRelations;
|
|
PLIST_ENTRY l, b;
|
|
PPARTITION_LIST_ENTRY partition;
|
|
ULONG i;
|
|
PVOLMGR_LIST_ENTRY volmgrEntry;
|
|
LIST_ENTRY completionList;
|
|
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
IoCopyCurrentIrpStackLocationToNext(Irp);
|
|
IoSetCompletionRoutine(Irp, PmSignalCompletion, &event, TRUE, TRUE, TRUE);
|
|
IoCallDriver(Extension->TargetObject, Irp);
|
|
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
|
|
|
if (!NT_SUCCESS(Irp->IoStatus.Status)) {
|
|
return Irp->IoStatus.Status;
|
|
}
|
|
|
|
deviceRelations = (PDEVICE_RELATIONS) Irp->IoStatus.Information;
|
|
|
|
|
|
PmCheckAndUpdateSignature (Extension, TRUE, FALSE);
|
|
|
|
|
|
|
|
//
|
|
// First notify clients of partitions that have gone away.
|
|
//
|
|
|
|
LockDriver (Extension->DriverExtension);
|
|
|
|
|
|
for (l = Extension->PartitionList.Flink; l != &Extension->PartitionList;
|
|
l = l->Flink) {
|
|
|
|
partition = CONTAINING_RECORD(l, PARTITION_LIST_ENTRY, ListEntry);
|
|
|
|
for (i = 0; i < deviceRelations->Count; i++) {
|
|
if (partition->TargetObject == deviceRelations->Objects[i]) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i < deviceRelations->Count) {
|
|
continue;
|
|
}
|
|
|
|
PmTakePartition(partition->VolumeManagerEntry,
|
|
partition->TargetObject, partition->WholeDiskPdo);
|
|
|
|
//
|
|
// We're pretending to be pnp. Send a remove to the partition
|
|
// object so it can delete it.
|
|
//
|
|
|
|
PmRemovePartition(partition);
|
|
|
|
b = l->Blink;
|
|
RemoveEntryList(l);
|
|
l = b;
|
|
|
|
ExFreePool(partition);
|
|
}
|
|
|
|
//
|
|
// Then notify clients of new partitions.
|
|
//
|
|
|
|
for (i = 0; i < deviceRelations->Count; i++) {
|
|
|
|
for (l = Extension->PartitionList.Flink;
|
|
l != &Extension->PartitionList; l = l->Flink) {
|
|
|
|
partition = CONTAINING_RECORD(l, PARTITION_LIST_ENTRY, ListEntry);
|
|
|
|
if (deviceRelations->Objects[i] == partition->TargetObject) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (l != &Extension->PartitionList) {
|
|
|
|
//
|
|
// Must attempt to start the partition even if it is in our list
|
|
// because it may be stopped and need restarting.
|
|
//
|
|
|
|
PmStartPartition(deviceRelations->Objects[i]);
|
|
continue;
|
|
}
|
|
|
|
if (Extension->DriverExtension->PastReinit) {
|
|
|
|
//
|
|
// Now that this partition is owned by the partition manager
|
|
// make sure that nobody can open it another way.
|
|
//
|
|
|
|
deviceRelations->Objects[i]->Flags |= DO_DEVICE_INITIALIZING;
|
|
}
|
|
|
|
status = PmStartPartition(deviceRelations->Objects[i]);
|
|
if (!NT_SUCCESS(status)) {
|
|
continue;
|
|
}
|
|
|
|
partition = ExAllocatePoolWithTag(NonPagedPool,
|
|
sizeof(PARTITION_LIST_ENTRY),
|
|
PARTMGR_TAG_PARTITION_ENTRY);
|
|
if (!partition) {
|
|
continue;
|
|
}
|
|
|
|
partition->TargetObject = deviceRelations->Objects[i];
|
|
partition->WholeDiskPdo = Extension->Pdo;
|
|
partition->VolumeManagerEntry = NULL;
|
|
InsertHeadList(&Extension->PartitionList, &partition->ListEntry);
|
|
|
|
if (Extension->IsRedundantPath) {
|
|
continue;
|
|
}
|
|
|
|
for (l = Extension->DriverExtension->VolumeManagerList.Flink;
|
|
l != &Extension->DriverExtension->VolumeManagerList; l = l->Flink) {
|
|
|
|
volmgrEntry = CONTAINING_RECORD(l, VOLMGR_LIST_ENTRY, ListEntry);
|
|
|
|
status = PmGivePartition(volmgrEntry,
|
|
partition->TargetObject,
|
|
partition->WholeDiskPdo);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
partition->VolumeManagerEntry = volmgrEntry;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
UnlockDriver (Extension->DriverExtension);
|
|
|
|
deviceRelations->Count = 0;
|
|
|
|
return Irp->IoStatus.Status;
|
|
}
|
|
|
|
VOID
|
|
PmTakeWholeDisk(
|
|
IN PVOLMGR_LIST_ENTRY VolumeManagerEntry,
|
|
IN PDEVICE_OBJECT WholeDiskPdo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine alerts the volume manager that the given PDO is going away.
|
|
|
|
Arguments:
|
|
|
|
VolumeManager - Supplies a volume manager.
|
|
|
|
WholeDiskPdo - Supplies the whole disk PDO.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
KEVENT event;
|
|
VOLMGR_WHOLE_DISK_INFORMATION input;
|
|
PIRP irp;
|
|
IO_STATUS_BLOCK ioStatus;
|
|
|
|
if (!VolumeManagerEntry) {
|
|
return;
|
|
}
|
|
|
|
if (!VolumeManagerEntry->RefCount) {
|
|
status = IoGetDeviceObjectPointer(
|
|
&VolumeManagerEntry->VolumeManagerName, FILE_READ_DATA,
|
|
&VolumeManagerEntry->VolumeManagerFileObject,
|
|
&VolumeManagerEntry->VolumeManager);
|
|
if (!NT_SUCCESS(status)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
input.WholeDiskPdo = WholeDiskPdo;
|
|
|
|
irp = IoBuildDeviceIoControlRequest(IOCTL_INTERNAL_VOLMGR_WHOLE_DISK_REMOVED,
|
|
VolumeManagerEntry->VolumeManager,
|
|
&input, sizeof(input), NULL, 0, TRUE,
|
|
&event, &ioStatus);
|
|
if (!irp) {
|
|
if (!VolumeManagerEntry->RefCount) {
|
|
VolumeManagerEntry->VolumeManager = NULL;
|
|
ObDereferenceObject(VolumeManagerEntry->VolumeManagerFileObject);
|
|
VolumeManagerEntry->VolumeManagerFileObject = NULL;
|
|
}
|
|
return;
|
|
}
|
|
|
|
status = IoCallDriver(VolumeManagerEntry->VolumeManager, irp);
|
|
if (status == STATUS_PENDING) {
|
|
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
|
}
|
|
|
|
if (!VolumeManagerEntry->RefCount) {
|
|
VolumeManagerEntry->VolumeManager = NULL;
|
|
ObDereferenceObject(VolumeManagerEntry->VolumeManagerFileObject);
|
|
VolumeManagerEntry->VolumeManagerFileObject = NULL;
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
PmNotifyPartitions(
|
|
IN PDEVICE_EXTENSION Extension,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine notifies each partition stolen by the partmgr that it is
|
|
about to be removed.
|
|
|
|
Arguments:
|
|
|
|
Extension - Supplies the device extension.
|
|
|
|
Irp - Supplies the IO request packet.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
PLIST_ENTRY l;
|
|
PPARTITION_LIST_ENTRY partition;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
KEVENT event;
|
|
|
|
PAGED_CODE();
|
|
|
|
KeInitializeEvent(&event, SynchronizationEvent, FALSE);
|
|
|
|
LockDriver (Extension->DriverExtension);
|
|
|
|
for(l = Extension->PartitionList.Flink;
|
|
l != &(Extension->PartitionList);
|
|
l = l->Flink) {
|
|
|
|
partition = CONTAINING_RECORD(l, PARTITION_LIST_ENTRY, ListEntry);
|
|
|
|
IoCopyCurrentIrpStackLocationToNext(Irp);
|
|
IoSetCompletionRoutine(Irp,
|
|
PmSignalCompletion,
|
|
&event,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE);
|
|
|
|
status = IoCallDriver(partition->TargetObject, Irp);
|
|
|
|
if(status == STATUS_PENDING) {
|
|
|
|
KeWaitForSingleObject(&event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
|
|
status = Irp->IoStatus.Status;
|
|
}
|
|
|
|
if(!NT_SUCCESS(status)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
UnlockDriver (Extension->DriverExtension);
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
PmRemoveDevice(
|
|
IN PDEVICE_EXTENSION Extension,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine processes the query device relations request.
|
|
|
|
Arguments:
|
|
|
|
Extension - Supplies the device extension.
|
|
|
|
Irp - Supplies the I/O request packet.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
PDRIVE_LAYOUT_INFORMATION_EX layout;
|
|
PLIST_ENTRY l;
|
|
PPARTITION_LIST_ENTRY partition;
|
|
PVOLMGR_LIST_ENTRY volmgrEntry;
|
|
|
|
|
|
LockDriver (Extension->DriverExtension);
|
|
|
|
if (Extension->RemoveProcessed) {
|
|
UnlockDriver (Extension->DriverExtension);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
Extension->RemoveProcessed = TRUE;
|
|
|
|
Extension->IsStarted = FALSE;
|
|
|
|
PmAddSignatures(Extension, NULL);
|
|
|
|
for (l = Extension->PartitionList.Flink;
|
|
l != &Extension->PartitionList; l = l->Flink) {
|
|
|
|
partition = CONTAINING_RECORD(l, PARTITION_LIST_ENTRY, ListEntry);
|
|
PmTakePartition(partition->VolumeManagerEntry,
|
|
partition->TargetObject, NULL);
|
|
}
|
|
|
|
status = PmNotifyPartitions(Extension, Irp);
|
|
|
|
ASSERT(NT_SUCCESS(status));
|
|
|
|
while (!IsListEmpty(&Extension->PartitionList)) {
|
|
l = RemoveHeadList(&Extension->PartitionList);
|
|
partition = CONTAINING_RECORD(l, PARTITION_LIST_ENTRY, ListEntry);
|
|
ExFreePool(partition);
|
|
}
|
|
|
|
for (l = Extension->DriverExtension->VolumeManagerList.Flink;
|
|
l != &Extension->DriverExtension->VolumeManagerList; l = l->Flink) {
|
|
|
|
volmgrEntry = CONTAINING_RECORD(l, VOLMGR_LIST_ENTRY, ListEntry);
|
|
PmTakeWholeDisk(volmgrEntry, Extension->Pdo);
|
|
}
|
|
|
|
RemoveEntryList(&Extension->ListEntry);
|
|
|
|
if (Extension->WmilibContext != NULL) { // just to be safe
|
|
IoWMIRegistrationControl(Extension->DeviceObject,
|
|
WMIREG_ACTION_DEREGISTER);
|
|
ExFreePool(Extension->WmilibContext);
|
|
Extension->WmilibContext = NULL;
|
|
PmWmiCounterDisable(&Extension->PmWmiCounterContext,
|
|
TRUE, TRUE);
|
|
Extension->CountersEnabled = FALSE;
|
|
}
|
|
|
|
|
|
UnlockDriver (Extension->DriverExtension);
|
|
|
|
return status;
|
|
}
|
|
|
|
VOID
|
|
PmPowerNotify (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PVOID PublicWorkItem
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine notifies volume managers about changes in
|
|
the power state of the given disk.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Supplies the device object.
|
|
|
|
PublicWorkItem - Supplies the public work item
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
PDO_EXTENSION driverExtension = deviceExtension->DriverExtension;
|
|
KEVENT event;
|
|
KIRQL irql;
|
|
LIST_ENTRY q;
|
|
PLIST_ENTRY l;
|
|
BOOLEAN empty;
|
|
PPM_POWER_WORK_ITEM privateWorkItem;
|
|
PPARTITION_LIST_ENTRY partition;
|
|
VOLMGR_POWER_STATE input;
|
|
PIRP irp;
|
|
IO_STATUS_BLOCK ioStatus;
|
|
NTSTATUS status;
|
|
|
|
|
|
LockDriver (driverExtension);
|
|
|
|
KeAcquireSpinLock(&deviceExtension->SpinLock, &irql);
|
|
if (IsListEmpty(&deviceExtension->PowerQueue)) {
|
|
empty = TRUE;
|
|
} else {
|
|
empty = FALSE;
|
|
q = deviceExtension->PowerQueue;
|
|
InitializeListHead(&deviceExtension->PowerQueue);
|
|
}
|
|
KeReleaseSpinLock(&deviceExtension->SpinLock, irql);
|
|
|
|
if (empty) {
|
|
UnlockDriver (driverExtension);
|
|
IoReleaseRemoveLock(&deviceExtension->RemoveLock, NULL);
|
|
IoFreeWorkItem((PIO_WORKITEM) PublicWorkItem);
|
|
return;
|
|
}
|
|
|
|
q.Flink->Blink = &q;
|
|
q.Blink->Flink = &q;
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
|
|
while (!IsListEmpty(&q)) {
|
|
|
|
l = RemoveHeadList(&q);
|
|
privateWorkItem = CONTAINING_RECORD(l, PM_POWER_WORK_ITEM, ListEntry);
|
|
|
|
for (l = deviceExtension->PartitionList.Flink;
|
|
l != &deviceExtension->PartitionList; l = l->Flink) {
|
|
|
|
partition = CONTAINING_RECORD(l, PARTITION_LIST_ENTRY, ListEntry);
|
|
if (!partition->VolumeManagerEntry) {
|
|
continue;
|
|
}
|
|
|
|
input.PartitionDeviceObject = partition->TargetObject;
|
|
input.WholeDiskPdo = partition->WholeDiskPdo;
|
|
input.PowerState = privateWorkItem->DevicePowerState;
|
|
|
|
irp = IoBuildDeviceIoControlRequest(
|
|
IOCTL_INTERNAL_VOLMGR_SET_POWER_STATE,
|
|
partition->VolumeManagerEntry->VolumeManager,
|
|
&input, sizeof(input), NULL, 0, TRUE,
|
|
&event, &ioStatus);
|
|
if (!irp) {
|
|
continue;
|
|
}
|
|
status = IoCallDriver(partition->VolumeManagerEntry->VolumeManager,
|
|
irp);
|
|
if (status == STATUS_PENDING) {
|
|
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
|
}
|
|
KeClearEvent(&event);
|
|
}
|
|
|
|
ExFreePool(privateWorkItem);
|
|
}
|
|
|
|
UnlockDriver (driverExtension);
|
|
IoReleaseRemoveLock(&deviceExtension->RemoveLock, NULL);
|
|
IoFreeWorkItem((PIO_WORKITEM) PublicWorkItem);
|
|
}
|
|
|
|
NTSTATUS
|
|
PmPowerCompletion(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine handles completion of an IRP_MN_SET_POWER irp.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Supplies the device object.
|
|
|
|
Irp - Supplies the IO request packet.
|
|
|
|
Context - Not used.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
PIO_WORKITEM publicWorkItem;
|
|
PPM_POWER_WORK_ITEM privateWorkItem;
|
|
KIRQL irql;
|
|
|
|
ASSERT(irpStack->MajorFunction == IRP_MJ_POWER &&
|
|
irpStack->MinorFunction == IRP_MN_SET_POWER);
|
|
ASSERT(irpStack->Parameters.Power.Type == DevicePowerState);
|
|
|
|
if (!NT_SUCCESS(Irp->IoStatus.Status)) {
|
|
PoStartNextPowerIrp(Irp);
|
|
IoReleaseRemoveLock(&deviceExtension->RemoveLock, NULL);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
publicWorkItem = IoAllocateWorkItem(DeviceObject);
|
|
if (!publicWorkItem) {
|
|
PoStartNextPowerIrp(Irp);
|
|
IoReleaseRemoveLock(&deviceExtension->RemoveLock, NULL);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
privateWorkItem = (PPM_POWER_WORK_ITEM) ExAllocatePoolWithTag(NonPagedPool,
|
|
sizeof(PM_POWER_WORK_ITEM),
|
|
PARTMGR_TAG_POWER_WORK_ITEM);
|
|
if (!privateWorkItem) {
|
|
PoStartNextPowerIrp(Irp);
|
|
IoReleaseRemoveLock(&deviceExtension->RemoveLock, NULL);
|
|
IoFreeWorkItem(publicWorkItem);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
privateWorkItem->DevicePowerState =
|
|
irpStack->Parameters.Power.State.DeviceState;
|
|
|
|
KeAcquireSpinLock(&deviceExtension->SpinLock, &irql);
|
|
InsertTailList(&deviceExtension->PowerQueue, &privateWorkItem->ListEntry);
|
|
KeReleaseSpinLock(&deviceExtension->SpinLock, irql);
|
|
|
|
IoQueueWorkItem(publicWorkItem, PmPowerNotify, DelayedWorkQueue,
|
|
publicWorkItem);
|
|
|
|
PoStartNextPowerIrp(Irp);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
PmPower(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the dispatch for IRP_MJ_POWER.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Supplies the device object.
|
|
|
|
Irp - Supplies the IO request packet.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
PDEVICE_EXTENSION deviceExtension =
|
|
(PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
|
|
NTSTATUS status;
|
|
|
|
status = IoAcquireRemoveLock (&deviceExtension->RemoveLock, NULL);
|
|
if (!NT_SUCCESS (status)) {
|
|
PoStartNextPowerIrp (Irp);
|
|
Irp->IoStatus.Information = 0;
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest (Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
}
|
|
|
|
if (irpSp->MinorFunction == IRP_MN_SET_POWER &&
|
|
irpSp->Parameters.Power.Type == DevicePowerState) {
|
|
|
|
IoCopyCurrentIrpStackLocationToNext(Irp);
|
|
IoSetCompletionRoutine(Irp, PmPowerCompletion, NULL, TRUE,
|
|
TRUE, TRUE);
|
|
IoMarkIrpPending(Irp);
|
|
PoCallDriver(deviceExtension->TargetObject, Irp);
|
|
return STATUS_PENDING;
|
|
}
|
|
|
|
PoStartNextPowerIrp(Irp);
|
|
IoSkipCurrentIrpStackLocation(Irp);
|
|
status = PoCallDriver(deviceExtension->TargetObject, Irp);
|
|
IoReleaseRemoveLock(&deviceExtension->RemoveLock, NULL);
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
PmQueryRemovalRelations(
|
|
IN PDEVICE_EXTENSION Extension,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine processes the query removal relations request.
|
|
|
|
Arguments:
|
|
|
|
Extension - Supplies the device extension.
|
|
|
|
Irp - Supplies the IO request packet.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
KEVENT event;
|
|
PDEVICE_RELATIONS childRelationsList;
|
|
PDEVICE_RELATIONS volumeRelationsList;
|
|
PDEVICE_RELATIONS combinedRelationsList;
|
|
ULONG i;
|
|
ULONG j;
|
|
ULONG combinedCount;
|
|
|
|
|
|
status = Irp->IoStatus.Status;
|
|
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
IoCopyCurrentIrpStackLocationToNext(Irp);
|
|
IoSetCompletionRoutine(Irp, PmSignalCompletion, &event, TRUE, TRUE, TRUE);
|
|
IoCallDriver(Extension->TargetObject, Irp);
|
|
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
|
|
|
if (!NT_SUCCESS(Irp->IoStatus.Status)) {
|
|
Irp->IoStatus.Information = 0;
|
|
if (status != Irp->IoStatus.Status) {
|
|
return Irp->IoStatus.Status;
|
|
}
|
|
}
|
|
|
|
|
|
childRelationsList = (PDEVICE_RELATIONS)Irp->IoStatus.Information;
|
|
status = PmBuildDependantVolumeRelations(Extension, &volumeRelationsList);
|
|
|
|
|
|
//
|
|
// Enumerate and combine the two lists. For lack of a better
|
|
// tag, use PARTMGR_TAG_DEPENDANT_VOLUME_LIST to represent
|
|
// the combined list
|
|
//
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
volumeRelationsList = NULL;
|
|
|
|
} else {
|
|
|
|
combinedCount = ((NULL == volumeRelationsList) ? 0 : volumeRelationsList->Count)
|
|
+ ((NULL == childRelationsList) ? 0 : childRelationsList->Count);
|
|
|
|
combinedRelationsList = ExAllocatePoolWithTag (
|
|
PagedPool,
|
|
(sizeof (DEVICE_RELATIONS) +
|
|
(sizeof (PDEVICE_OBJECT) * combinedCount)),
|
|
PARTMGR_TAG_DEPENDANT_VOLUME_LIST);
|
|
|
|
|
|
if (NULL == combinedRelationsList) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
RtlZeroMemory (combinedRelationsList,
|
|
(sizeof(DEVICE_RELATIONS) +
|
|
(sizeof(PDEVICE_OBJECT) * combinedCount)));
|
|
|
|
combinedRelationsList->Count = combinedCount;
|
|
|
|
i = j = 0;
|
|
|
|
if (NULL != volumeRelationsList) {
|
|
for (; i < volumeRelationsList->Count; i++) {
|
|
combinedRelationsList->Objects [i] = volumeRelationsList->Objects [i];
|
|
}
|
|
}
|
|
|
|
|
|
if (NULL != childRelationsList) {
|
|
for (; j < childRelationsList->Count; i++, j++) {
|
|
combinedRelationsList->Objects [i] = childRelationsList->Objects [j];
|
|
}
|
|
}
|
|
|
|
|
|
status = STATUS_SUCCESS;
|
|
Irp->IoStatus.Information = (ULONG_PTR) combinedRelationsList;
|
|
|
|
} else {
|
|
|
|
if (NULL != childRelationsList) {
|
|
for (i = 0; i < childRelationsList->Count; i++) {
|
|
ObDereferenceObject (childRelationsList->Objects [i]);
|
|
}
|
|
}
|
|
|
|
if (NULL != volumeRelationsList) {
|
|
for (i = 0; i < volumeRelationsList->Count; i++) {
|
|
ObDereferenceObject (volumeRelationsList->Objects [i]);
|
|
}
|
|
}
|
|
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
}
|
|
|
|
|
|
|
|
if (NULL != childRelationsList) {
|
|
ExFreePool (childRelationsList);
|
|
}
|
|
|
|
if (NULL != volumeRelationsList) {
|
|
ExFreePool (volumeRelationsList);
|
|
}
|
|
|
|
return (status);
|
|
}
|
|
|
|
BOOLEAN
|
|
PmIsRedundantPath(
|
|
IN PDEVICE_EXTENSION Extension,
|
|
IN PDEVICE_EXTENSION WinningExtension,
|
|
IN ULONG Signature,
|
|
IN GUID* Guid
|
|
)
|
|
|
|
{
|
|
PDO_EXTENSION driverExtension = Extension->DriverExtension;
|
|
PDEVICE_EXTENSION extension = WinningExtension;
|
|
PGUID_TABLE_ENTRY guidEntry;
|
|
PSIGNATURE_TABLE_ENTRY sigEntry;
|
|
KEVENT event;
|
|
PIRP irp;
|
|
DISK_GEOMETRY geometry, geometry2;
|
|
IO_STATUS_BLOCK ioStatus;
|
|
NTSTATUS status;
|
|
ULONG bufferSize;
|
|
ULONG readSize;
|
|
PVOID buffer;
|
|
LARGE_INTEGER byteOffset;
|
|
PUSHORT testWord;
|
|
BOOLEAN isRedundant;
|
|
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_DRIVE_GEOMETRY,
|
|
Extension->TargetObject, NULL, 0,
|
|
&geometry, sizeof(geometry), FALSE,
|
|
&event, &ioStatus);
|
|
if (!irp) {
|
|
return TRUE;
|
|
}
|
|
status = IoCallDriver(Extension->TargetObject, irp);
|
|
if (status == STATUS_PENDING) {
|
|
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
|
status = ioStatus.Status;
|
|
}
|
|
if (!NT_SUCCESS(status)) {
|
|
return TRUE;
|
|
}
|
|
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_DRIVE_GEOMETRY,
|
|
extension->TargetObject, NULL, 0,
|
|
&geometry2, sizeof(geometry2), FALSE,
|
|
&event, &ioStatus);
|
|
if (!irp) {
|
|
return TRUE;
|
|
}
|
|
status = IoCallDriver(extension->TargetObject, irp);
|
|
if (status == STATUS_PENDING) {
|
|
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
|
status = ioStatus.Status;
|
|
}
|
|
if (!NT_SUCCESS(status)) {
|
|
return TRUE;
|
|
}
|
|
|
|
if (geometry2.BytesPerSector > geometry.BytesPerSector) {
|
|
geometry.BytesPerSector = geometry2.BytesPerSector;
|
|
}
|
|
|
|
byteOffset.QuadPart = 0;
|
|
readSize = 512;
|
|
if (readSize < geometry.BytesPerSector) {
|
|
readSize = geometry.BytesPerSector;
|
|
}
|
|
|
|
bufferSize = 2*readSize;
|
|
buffer = ExAllocatePoolWithTag(NonPagedPool, bufferSize < PAGE_SIZE ?
|
|
PAGE_SIZE : bufferSize,
|
|
PARTMGR_TAG_IOCTL_BUFFER);
|
|
if (!buffer) {
|
|
return TRUE;
|
|
}
|
|
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ, Extension->TargetObject,
|
|
buffer, readSize, &byteOffset, &event,
|
|
&ioStatus);
|
|
if (!irp) {
|
|
ExFreePool(buffer);
|
|
return TRUE;
|
|
}
|
|
status = IoCallDriver(Extension->TargetObject, irp);
|
|
if (status == STATUS_PENDING) {
|
|
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
|
status = ioStatus.Status;
|
|
}
|
|
if (!NT_SUCCESS(status)) {
|
|
ExFreePool(buffer);
|
|
return TRUE;
|
|
}
|
|
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ, extension->TargetObject,
|
|
(PCHAR) buffer + readSize, readSize,
|
|
&byteOffset, &event, &ioStatus);
|
|
if (!irp) {
|
|
ExFreePool(buffer);
|
|
return TRUE;
|
|
}
|
|
status = IoCallDriver(extension->TargetObject, irp);
|
|
if (status == STATUS_PENDING) {
|
|
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
|
status = ioStatus.Status;
|
|
}
|
|
if (!NT_SUCCESS(status)) {
|
|
ExFreePool(buffer);
|
|
return TRUE;
|
|
}
|
|
|
|
if (RtlCompareMemory(buffer, (PCHAR) buffer + readSize, readSize) !=
|
|
readSize) {
|
|
|
|
ExFreePool(buffer);
|
|
return FALSE;
|
|
}
|
|
|
|
testWord = (PUSHORT) ((PCHAR) buffer + 0x1BC);
|
|
(*testWord)++;
|
|
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
irp = IoBuildSynchronousFsdRequest(IRP_MJ_WRITE, Extension->TargetObject,
|
|
buffer, readSize, &byteOffset, &event,
|
|
&ioStatus);
|
|
if (!irp) {
|
|
ExFreePool(buffer);
|
|
return TRUE;
|
|
}
|
|
status = IoCallDriver(Extension->TargetObject, irp);
|
|
if (status == STATUS_PENDING) {
|
|
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
|
status = ioStatus.Status;
|
|
}
|
|
if (!NT_SUCCESS(status)) {
|
|
ExFreePool(buffer);
|
|
return TRUE;
|
|
}
|
|
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ, extension->TargetObject,
|
|
(PCHAR) buffer + readSize, readSize,
|
|
&byteOffset, &event, &ioStatus);
|
|
if (!irp) {
|
|
isRedundant = TRUE;
|
|
goto ResetTestWord;
|
|
}
|
|
status = IoCallDriver(extension->TargetObject, irp);
|
|
if (status == STATUS_PENDING) {
|
|
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
|
status = ioStatus.Status;
|
|
}
|
|
if (!NT_SUCCESS(status)) {
|
|
isRedundant = TRUE;
|
|
goto ResetTestWord;
|
|
}
|
|
|
|
if (RtlCompareMemory(buffer, (PCHAR) buffer + readSize, readSize) !=
|
|
readSize) {
|
|
|
|
isRedundant = FALSE;
|
|
goto ResetTestWord;
|
|
}
|
|
|
|
isRedundant = TRUE;
|
|
|
|
ResetTestWord:
|
|
(*testWord)--;
|
|
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
irp = IoBuildSynchronousFsdRequest(IRP_MJ_WRITE, Extension->TargetObject,
|
|
buffer, readSize, &byteOffset, &event,
|
|
&ioStatus);
|
|
if (!irp) {
|
|
ExFreePool(buffer);
|
|
return isRedundant;
|
|
}
|
|
status = IoCallDriver(Extension->TargetObject, irp);
|
|
if (status == STATUS_PENDING) {
|
|
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
|
status = ioStatus.Status;
|
|
}
|
|
if (!NT_SUCCESS(status)) {
|
|
ExFreePool(buffer);
|
|
return isRedundant;
|
|
}
|
|
|
|
ExFreePool(buffer);
|
|
|
|
return isRedundant;
|
|
}
|
|
|
|
VOID
|
|
PmLogError(
|
|
IN PDEVICE_EXTENSION Extension,
|
|
IN PDEVICE_EXTENSION WinningExtension,
|
|
IN NTSTATUS SpecificIoStatus
|
|
)
|
|
|
|
{
|
|
KEVENT event;
|
|
PIRP irp;
|
|
STORAGE_DEVICE_NUMBER deviceNumber, winningDeviceNumber;
|
|
IO_STATUS_BLOCK ioStatus;
|
|
NTSTATUS status;
|
|
WCHAR buffer1[30], buffer2[30];
|
|
UNICODE_STRING number, winningNumber;
|
|
ULONG extraSpace;
|
|
PIO_ERROR_LOG_PACKET errorLogPacket;
|
|
PWCHAR p;
|
|
GUID_IO_DISK_CLONE_ARRIVAL_INFORMATION diskCloneArrivalInfo;
|
|
UCHAR notificationBuffer[sizeof(TARGET_DEVICE_CUSTOM_NOTIFICATION) + sizeof(GUID_IO_DISK_CLONE_ARRIVAL_INFORMATION)];
|
|
PTARGET_DEVICE_CUSTOM_NOTIFICATION notification = (PTARGET_DEVICE_CUSTOM_NOTIFICATION) notificationBuffer;
|
|
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
irp = IoBuildDeviceIoControlRequest(IOCTL_STORAGE_GET_DEVICE_NUMBER,
|
|
Extension->TargetObject, NULL, 0,
|
|
&deviceNumber, sizeof(deviceNumber),
|
|
FALSE, &event, &ioStatus);
|
|
if (!irp) {
|
|
return;
|
|
}
|
|
|
|
status = IoCallDriver(Extension->TargetObject, irp);
|
|
if (status == STATUS_PENDING) {
|
|
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
|
status = ioStatus.Status;
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
return;
|
|
}
|
|
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
irp = IoBuildDeviceIoControlRequest(IOCTL_STORAGE_GET_DEVICE_NUMBER,
|
|
WinningExtension->TargetObject, NULL,
|
|
0, &winningDeviceNumber,
|
|
sizeof(winningDeviceNumber),
|
|
FALSE, &event, &ioStatus);
|
|
if (!irp) {
|
|
return;
|
|
}
|
|
|
|
status = IoCallDriver(WinningExtension->TargetObject, irp);
|
|
if (status == STATUS_PENDING) {
|
|
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
|
status = ioStatus.Status;
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
return;
|
|
}
|
|
|
|
swprintf(buffer1, L"%d", deviceNumber.DeviceNumber);
|
|
RtlInitUnicodeString(&number, buffer1);
|
|
swprintf(buffer2, L"%d", winningDeviceNumber.DeviceNumber);
|
|
RtlInitUnicodeString(&winningNumber, buffer2);
|
|
extraSpace = number.Length + winningNumber.Length + 2*sizeof(WCHAR);
|
|
extraSpace += sizeof(IO_ERROR_LOG_PACKET);
|
|
if (extraSpace > 0xFF) {
|
|
return;
|
|
}
|
|
|
|
errorLogPacket = (PIO_ERROR_LOG_PACKET)
|
|
IoAllocateErrorLogEntry(Extension->DeviceObject,
|
|
(UCHAR) extraSpace);
|
|
if (!errorLogPacket) {
|
|
return;
|
|
}
|
|
|
|
errorLogPacket->ErrorCode = SpecificIoStatus;
|
|
errorLogPacket->SequenceNumber = 0;
|
|
errorLogPacket->FinalStatus = 0;
|
|
errorLogPacket->UniqueErrorValue = 0;
|
|
errorLogPacket->DumpDataSize = 0;
|
|
errorLogPacket->RetryCount = 0;
|
|
errorLogPacket->NumberOfStrings = 2;
|
|
errorLogPacket->StringOffset = sizeof(IO_ERROR_LOG_PACKET);
|
|
p = (PWCHAR) ((PCHAR) errorLogPacket + sizeof(IO_ERROR_LOG_PACKET));
|
|
RtlCopyMemory(p, number.Buffer, number.Length);
|
|
p[number.Length/sizeof(WCHAR)] = 0;
|
|
p = (PWCHAR) ((PCHAR) errorLogPacket + sizeof(IO_ERROR_LOG_PACKET) +
|
|
number.Length + sizeof(WCHAR));
|
|
RtlCopyMemory(p, winningNumber.Buffer, winningNumber.Length);
|
|
p[winningNumber.Length/sizeof(WCHAR)] = 0;
|
|
|
|
IoWriteErrorLogEntry(errorLogPacket);
|
|
|
|
if (SpecificIoStatus == IO_WARNING_DUPLICATE_SIGNATURE) {
|
|
diskCloneArrivalInfo.DiskNumber = deviceNumber.DeviceNumber;
|
|
notification->Version = 1;
|
|
notification->Size = (USHORT)
|
|
(FIELD_OFFSET(TARGET_DEVICE_CUSTOM_NOTIFICATION,
|
|
CustomDataBuffer) +
|
|
sizeof(diskCloneArrivalInfo));
|
|
RtlCopyMemory(¬ification->Event, &GUID_IO_DISK_CLONE_ARRIVAL,
|
|
sizeof(GUID_IO_DISK_CLONE_ARRIVAL));
|
|
notification->FileObject = NULL;
|
|
notification->NameBufferOffset = -1;
|
|
RtlCopyMemory(notification->CustomDataBuffer, &diskCloneArrivalInfo,
|
|
sizeof(diskCloneArrivalInfo));
|
|
|
|
IoReportTargetDeviceChangeAsynchronous(WinningExtension->Pdo,
|
|
notification, NULL, NULL);
|
|
}
|
|
}
|
|
|
|
ULONG
|
|
PmQueryRegistrySignature(
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine checks a registry value for an MBR signature to be used
|
|
for the bad signature case. This is to facilitate OEM pre-install.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
A valid signature or 0.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG zero, signature;
|
|
RTL_QUERY_REGISTRY_TABLE queryTable[2];
|
|
NTSTATUS status;
|
|
|
|
zero = 0;
|
|
signature = 0;
|
|
|
|
RtlZeroMemory(queryTable, 2*sizeof(RTL_QUERY_REGISTRY_TABLE));
|
|
queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
queryTable[0].Name = L"BootDiskSig";
|
|
queryTable[0].EntryContext = &signature;
|
|
queryTable[0].DefaultType = REG_DWORD;
|
|
queryTable[0].DefaultData = &zero;
|
|
queryTable[0].DefaultLength = sizeof(ULONG);
|
|
|
|
status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
|
|
L"\\Registry\\Machine\\System\\Setup",
|
|
queryTable, NULL, NULL);
|
|
if (!NT_SUCCESS(status)) {
|
|
signature = zero;
|
|
}
|
|
|
|
RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE,
|
|
L"\\Registry\\Machine\\System\\Setup",
|
|
L"BootDiskSig");
|
|
|
|
return signature;
|
|
}
|
|
|
|
NTSTATUS
|
|
PmQueryRegistryGuidQueryRoutine(
|
|
IN PWSTR ValueName,
|
|
IN ULONG ValueType,
|
|
IN PVOID ValueData,
|
|
IN ULONG ValueLength,
|
|
IN PVOID Context,
|
|
IN PVOID EntryContext
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine queries the unique id for the given value.
|
|
|
|
Arguments:
|
|
|
|
ValueName - Supplies the name of the registry value.
|
|
|
|
ValueType - Supplies the type of the registry value.
|
|
|
|
ValueData - Supplies the data of the registry value.
|
|
|
|
ValueLength - Supplies the length of the registry value.
|
|
|
|
Context - Pointer to the DeviceExtension->BootPartitionGuid field.
|
|
|
|
EntryContext - Not used.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
if ((REG_BINARY == ValueType) && (sizeof (GUID) == ValueLength)) {
|
|
RtlCopyMemory (Context, ValueData, sizeof (GUID));
|
|
} else {
|
|
RtlCopyMemory (Context, &guidNull, sizeof (GUID));
|
|
}
|
|
|
|
return (STATUS_SUCCESS);
|
|
}
|
|
VOID
|
|
PmQueryRegistryGuid(
|
|
IN PDO_EXTENSION DriverExtension
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine checks a registry value for an GPT partition GUID to be used
|
|
for the bad signature case. This is to facilitate OEM pre-install.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
A valid signature or 0.
|
|
|
|
--*/
|
|
|
|
{
|
|
RTL_QUERY_REGISTRY_TABLE queryTable[2];
|
|
NTSTATUS status;
|
|
|
|
|
|
RtlZeroMemory(queryTable, 2*sizeof(RTL_QUERY_REGISTRY_TABLE));
|
|
|
|
queryTable[0].QueryRoutine = PmQueryRegistryGuidQueryRoutine;
|
|
queryTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED;
|
|
queryTable[0].Name = L"BootPartitionGuid";
|
|
|
|
status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
|
|
L"\\Registry\\Machine\\System\\Setup",
|
|
queryTable,
|
|
&DriverExtension->BootPartitionGuid,
|
|
NULL);
|
|
|
|
if (NT_SUCCESS (status) && !IsEqualGUID (&guidNull, &DriverExtension->BootPartitionGuid)) {
|
|
DriverExtension->BootPartitionGuidPresent = TRUE;
|
|
} else {
|
|
DriverExtension->BootPartitionGuid = guidNull;
|
|
DriverExtension->BootPartitionGuidPresent = FALSE;
|
|
}
|
|
|
|
RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE,
|
|
L"\\Registry\\Machine\\System\\Setup",
|
|
L"BootPartitionGuid");
|
|
}
|
|
|
|
VOID
|
|
PmAddSignatures(
|
|
IN PDEVICE_EXTENSION Extension,
|
|
IN PDRIVE_LAYOUT_INFORMATION_EX Layout
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine adds the disk and partition signature to their
|
|
respective tables. If a collision is detected, the signatures are
|
|
changed and written back out.
|
|
|
|
Arguments:
|
|
|
|
Extension - Supplies the device extension.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PDO_EXTENSION driverExtension = Extension->DriverExtension;
|
|
PLIST_ENTRY l;
|
|
PSIGNATURE_TABLE_ENTRY s;
|
|
PGUID_TABLE_ENTRY g;
|
|
SIGNATURE_TABLE_ENTRY sigEntry;
|
|
GUID_TABLE_ENTRY guidEntry;
|
|
NTSTATUS status;
|
|
PVOID nodeOrParent, nodeOrParent2;
|
|
TABLE_SEARCH_RESULT searchResult, searchResult2;
|
|
UUID uuid;
|
|
PULONG p;
|
|
ULONG i;
|
|
BOOLEAN hasBootPartitionType;
|
|
|
|
while (!IsListEmpty(&Extension->SignatureList)) {
|
|
l = RemoveHeadList(&Extension->SignatureList);
|
|
s = CONTAINING_RECORD(l, SIGNATURE_TABLE_ENTRY, ListEntry);
|
|
RtlDeleteElementGenericTable(&driverExtension->SignatureTable, s);
|
|
}
|
|
|
|
while (!IsListEmpty(&Extension->GuidList)) {
|
|
l = RemoveHeadList(&Extension->GuidList);
|
|
g = CONTAINING_RECORD(l, GUID_TABLE_ENTRY, ListEntry);
|
|
RtlDeleteElementGenericTable(&driverExtension->GuidTable, g);
|
|
}
|
|
|
|
if (!Layout) {
|
|
return;
|
|
}
|
|
|
|
if (Layout->PartitionStyle == PARTITION_STYLE_MBR) {
|
|
|
|
if (!Layout->PartitionCount && !Layout->Mbr.Signature) {
|
|
// RAW disk. No signature to validate.
|
|
return;
|
|
}
|
|
|
|
if (Layout->PartitionCount > 0 &&
|
|
Layout->PartitionEntry[0].PartitionLength.QuadPart > 0 &&
|
|
Layout->PartitionEntry[0].StartingOffset.QuadPart == 0) {
|
|
|
|
// Super floppy. No signature to validate.
|
|
return;
|
|
}
|
|
|
|
sigEntry.Signature = Layout->Mbr.Signature;
|
|
s = RtlLookupElementGenericTableFull(
|
|
&driverExtension->SignatureTable, &sigEntry,
|
|
&nodeOrParent, &searchResult);
|
|
if (s || !sigEntry.Signature ||
|
|
Extension->DriverExtension->BootDiskSig) {
|
|
|
|
if (s) {
|
|
if (PmIsRedundantPath(Extension, s->Extension,
|
|
sigEntry.Signature, NULL)) {
|
|
|
|
PmLogError(Extension, s->Extension,
|
|
IO_WARNING_DUPLICATE_PATH);
|
|
|
|
Extension->IsRedundantPath = TRUE;
|
|
return;
|
|
}
|
|
|
|
PmLogError(Extension, s->Extension,
|
|
IO_WARNING_DUPLICATE_SIGNATURE);
|
|
}
|
|
|
|
if (Extension->DriverExtension->BootDiskSig) {
|
|
Layout->Mbr.Signature =
|
|
Extension->DriverExtension->BootDiskSig;
|
|
Extension->DriverExtension->BootDiskSig = 0;
|
|
} else {
|
|
status = ExUuidCreate(&uuid);
|
|
if (!NT_SUCCESS(status)) {
|
|
Extension->IsRedundantPath = TRUE;
|
|
return;
|
|
}
|
|
|
|
p = (PULONG) &uuid;
|
|
Layout->Mbr.Signature = p[0] ^ p[1] ^ p[2] ^ p[3];
|
|
}
|
|
sigEntry.Signature = Layout->Mbr.Signature;
|
|
|
|
if (driverExtension->PastReinit) {
|
|
status = PmWritePartitionTableEx(Extension->TargetObject,
|
|
Layout);
|
|
if (!NT_SUCCESS(status)) {
|
|
Extension->IsRedundantPath = TRUE;
|
|
return;
|
|
}
|
|
} else {
|
|
Extension->DiskSignature = Layout->Mbr.Signature;
|
|
}
|
|
|
|
RtlLookupElementGenericTableFull(
|
|
&driverExtension->SignatureTable, &sigEntry,
|
|
&nodeOrParent, &searchResult);
|
|
}
|
|
|
|
s = RtlInsertElementGenericTableFull(&driverExtension->SignatureTable,
|
|
&sigEntry, sizeof(sigEntry), NULL,
|
|
nodeOrParent, searchResult);
|
|
if (!s) {
|
|
return;
|
|
}
|
|
|
|
InsertTailList(&Extension->SignatureList, &s->ListEntry);
|
|
s->Extension = Extension;
|
|
|
|
return;
|
|
}
|
|
|
|
ASSERT(Layout->PartitionStyle == PARTITION_STYLE_GPT);
|
|
|
|
if (Layout->PartitionStyle != PARTITION_STYLE_GPT) {
|
|
return;
|
|
}
|
|
|
|
if (Extension->DriverExtension->PastReinit) {
|
|
|
|
p = (PULONG) &Layout->Gpt.DiskId;
|
|
sigEntry.Signature = p[0] ^ p[1] ^ p[2] ^ p[3];
|
|
guidEntry.Guid = Layout->Gpt.DiskId;
|
|
|
|
s = RtlLookupElementGenericTableFull(
|
|
&driverExtension->SignatureTable, &sigEntry,
|
|
&nodeOrParent, &searchResult);
|
|
g = RtlLookupElementGenericTableFull(
|
|
&driverExtension->GuidTable, &guidEntry,
|
|
&nodeOrParent2, &searchResult2);
|
|
if (s || g || !sigEntry.Signature) {
|
|
|
|
if (g) {
|
|
if (PmIsRedundantPath(Extension, g->Extension, 0,
|
|
&guidEntry.Guid)) {
|
|
|
|
PmLogError(Extension, g->Extension,
|
|
IO_WARNING_DUPLICATE_PATH);
|
|
|
|
Extension->IsRedundantPath = TRUE;
|
|
return;
|
|
}
|
|
|
|
PmLogError(Extension, g->Extension,
|
|
IO_WARNING_DUPLICATE_SIGNATURE);
|
|
}
|
|
|
|
status = ExUuidCreate(&uuid);
|
|
if (!NT_SUCCESS(status)) {
|
|
Extension->IsRedundantPath = TRUE;
|
|
return;
|
|
}
|
|
|
|
Layout->Gpt.DiskId = uuid;
|
|
status = PmWritePartitionTableEx(Extension->TargetObject, Layout);
|
|
if (!NT_SUCCESS(status)) {
|
|
Extension->IsRedundantPath = TRUE;
|
|
return;
|
|
}
|
|
|
|
p = (PULONG) &Layout->Gpt.DiskId;
|
|
sigEntry.Signature = p[0] ^ p[1] ^ p[2] ^ p[3];
|
|
guidEntry.Guid = Layout->Gpt.DiskId;
|
|
|
|
RtlLookupElementGenericTableFull(
|
|
&driverExtension->SignatureTable, &sigEntry,
|
|
&nodeOrParent, &searchResult);
|
|
RtlLookupElementGenericTableFull(
|
|
&driverExtension->GuidTable, &guidEntry,
|
|
&nodeOrParent2, &searchResult2);
|
|
}
|
|
|
|
s = RtlInsertElementGenericTableFull(
|
|
&driverExtension->SignatureTable, &sigEntry,
|
|
sizeof(sigEntry), NULL, nodeOrParent, searchResult);
|
|
if (!s) {
|
|
return;
|
|
}
|
|
|
|
InsertTailList(&Extension->SignatureList, &s->ListEntry);
|
|
s->Extension = Extension;
|
|
|
|
g = RtlInsertElementGenericTableFull(
|
|
&driverExtension->GuidTable, &guidEntry,
|
|
sizeof(guidEntry), NULL, nodeOrParent2, searchResult2);
|
|
if (!g) {
|
|
return;
|
|
}
|
|
|
|
InsertTailList(&Extension->GuidList, &g->ListEntry);
|
|
g->Extension = Extension;
|
|
}
|
|
|
|
for (i = 0; i < Layout->PartitionCount; i++) {
|
|
|
|
p = (PULONG) &Layout->PartitionEntry[i].Gpt.PartitionId;
|
|
sigEntry.Signature = p[0] | p[1] | p[2] | p[3];
|
|
guidEntry.Guid = Layout->PartitionEntry[i].Gpt.PartitionId;
|
|
|
|
if (driverExtension->BootPartitionGuidPresent) {
|
|
|
|
hasBootPartitionType =
|
|
IsEqualGUID(&Layout->PartitionEntry[i].Gpt.PartitionType, &PARTITION_BASIC_DATA_GUID) ||
|
|
IsEqualGUID(&Layout->PartitionEntry[i].Gpt.PartitionType, &PARTITION_LDM_DATA_GUID);
|
|
}
|
|
|
|
|
|
g = RtlLookupElementGenericTableFull(
|
|
&driverExtension->GuidTable, &guidEntry,
|
|
&nodeOrParent, &searchResult);
|
|
if (g || !sigEntry.Signature ||
|
|
(driverExtension->BootPartitionGuidPresent && hasBootPartitionType)) {
|
|
|
|
if (driverExtension->BootPartitionGuidPresent && hasBootPartitionType) {
|
|
uuid = Extension->DriverExtension->BootPartitionGuid;
|
|
driverExtension->BootPartitionGuidPresent = FALSE;
|
|
} else {
|
|
status = ExUuidCreate(&uuid);
|
|
if (!NT_SUCCESS(status)) {
|
|
Extension->IsRedundantPath = TRUE;
|
|
return;
|
|
}
|
|
}
|
|
|
|
Layout->PartitionEntry[i].Gpt.PartitionId = uuid;
|
|
status = PmWritePartitionTableEx(Extension->TargetObject, Layout);
|
|
if (!NT_SUCCESS(status)) {
|
|
Extension->IsRedundantPath = TRUE;
|
|
return;
|
|
}
|
|
|
|
guidEntry.Guid = Layout->PartitionEntry[i].Gpt.PartitionId;
|
|
|
|
RtlLookupElementGenericTableFull(
|
|
&driverExtension->GuidTable, &guidEntry,
|
|
&nodeOrParent, &searchResult);
|
|
}
|
|
|
|
g = RtlInsertElementGenericTableFull(
|
|
&driverExtension->GuidTable, &guidEntry,
|
|
sizeof(guidEntry), NULL, nodeOrParent, searchResult);
|
|
if (!g) {
|
|
return;
|
|
}
|
|
|
|
InsertTailList(&Extension->GuidList, &g->ListEntry);
|
|
g->Extension = Extension;
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
PmPnp(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the dispatch for IRP_MJ_PNP.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Supplies the device object.
|
|
|
|
Irp - Supplies the IO request packet.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION extension = DeviceObject->DeviceExtension;
|
|
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
KEVENT event;
|
|
NTSTATUS status;
|
|
PDEVICE_OBJECT targetObject;
|
|
PDRIVE_LAYOUT_INFORMATION_EX layout;
|
|
LIST_ENTRY completionList;
|
|
ULONG wmiRegistrationFlags;
|
|
|
|
if (irpSp->MinorFunction == IRP_MN_DEVICE_USAGE_NOTIFICATION &&
|
|
irpSp->Parameters.UsageNotification.Type == DeviceUsageTypePaging) {
|
|
|
|
ULONG count;
|
|
BOOLEAN setPagable;
|
|
|
|
//
|
|
// synchronize these irps.
|
|
//
|
|
|
|
KeWaitForSingleObject(&extension->PagingPathCountEvent,
|
|
Executive, KernelMode, FALSE, NULL);
|
|
|
|
//
|
|
// if removing last paging device, need to set DO_POWER_PAGABLE
|
|
// bit here, and possible re-set it below on failure.
|
|
//
|
|
|
|
setPagable = FALSE;
|
|
if (!irpSp->Parameters.UsageNotification.InPath &&
|
|
extension->PagingPathCount == 0 ) {
|
|
|
|
//
|
|
// removing a paging file. if last removal,
|
|
// must have DO_POWER_PAGABLE bits set
|
|
//
|
|
|
|
if (extension->PagingPathCount == 0) {
|
|
if (!(DeviceObject->Flags & DO_POWER_INRUSH)) {
|
|
DeviceObject->Flags |= DO_POWER_PAGABLE;
|
|
setPagable = TRUE;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// send the irp synchronously
|
|
//
|
|
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
IoCopyCurrentIrpStackLocationToNext(Irp);
|
|
IoSetCompletionRoutine(Irp, PmSignalCompletion, &event, TRUE, TRUE, TRUE);
|
|
status = IoCallDriver(extension->TargetObject, Irp);
|
|
if (status == STATUS_PENDING) {
|
|
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
|
status = Irp->IoStatus.Status;
|
|
}
|
|
|
|
//
|
|
// now deal with the failure and success cases.
|
|
// note that we are not allowed to fail the irp
|
|
// once it is sent to the lower drivers.
|
|
//
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
IoAdjustPagingPathCount(
|
|
&extension->PagingPathCount,
|
|
irpSp->Parameters.UsageNotification.InPath);
|
|
|
|
if (irpSp->Parameters.UsageNotification.InPath) {
|
|
|
|
if (extension->PagingPathCount == 1) {
|
|
DeviceObject->Flags &= ~DO_POWER_PAGABLE;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// cleanup the changes done above
|
|
//
|
|
|
|
if (setPagable == TRUE) {
|
|
DeviceObject->Flags &= ~DO_POWER_PAGABLE;
|
|
setPagable = FALSE;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// set the event so the next one can occur.
|
|
//
|
|
|
|
KeSetEvent(&extension->PagingPathCountEvent,
|
|
IO_NO_INCREMENT, FALSE);
|
|
|
|
//
|
|
// and complete the irp
|
|
//
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
|
|
}
|
|
|
|
if (extension->TargetObject->Characteristics & FILE_REMOVABLE_MEDIA) {
|
|
|
|
targetObject = extension->TargetObject;
|
|
|
|
if (IRP_MN_REMOVE_DEVICE == irpSp->MinorFunction) {
|
|
|
|
KeWaitForSingleObject (&extension->DriverExtension->Mutex,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
|
|
if (extension->WmilibContext != NULL) {
|
|
IoWMIRegistrationControl (extension->DeviceObject, WMIREG_ACTION_DEREGISTER);
|
|
ExFreePool (extension->WmilibContext);
|
|
extension->WmilibContext = NULL;
|
|
PmWmiCounterDisable (&extension->PmWmiCounterContext, TRUE, TRUE);
|
|
extension->CountersEnabled = FALSE;
|
|
}
|
|
|
|
RemoveEntryList (&extension->ListEntry);
|
|
KeReleaseMutex (&extension->DriverExtension->Mutex, FALSE);
|
|
|
|
IoDetachDevice (targetObject);
|
|
IoDeleteDevice (extension->DeviceObject);
|
|
}
|
|
|
|
IoSkipCurrentIrpStackLocation(Irp);
|
|
return IoCallDriver(targetObject, Irp);
|
|
}
|
|
|
|
switch (irpSp->MinorFunction) {
|
|
|
|
case IRP_MN_QUERY_DEVICE_RELATIONS:
|
|
switch (irpSp->Parameters.QueryDeviceRelations.Type) {
|
|
case BusRelations:
|
|
status = PmQueryDeviceRelations(extension, Irp);
|
|
break;
|
|
|
|
case RemovalRelations:
|
|
status = PmQueryRemovalRelations(extension, Irp);
|
|
break;
|
|
|
|
default:
|
|
IoSkipCurrentIrpStackLocation(Irp);
|
|
return IoCallDriver(extension->TargetObject, Irp);
|
|
|
|
}
|
|
break;
|
|
|
|
case IRP_MN_START_DEVICE:
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
IoCopyCurrentIrpStackLocationToNext(Irp);
|
|
IoSetCompletionRoutine(Irp, PmSignalCompletion, &event,
|
|
TRUE, TRUE, TRUE);
|
|
IoCallDriver(extension->TargetObject, Irp);
|
|
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
|
status = Irp->IoStatus.Status;
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
break;
|
|
}
|
|
|
|
if (extension->TargetObject->Characteristics&
|
|
FILE_REMOVABLE_MEDIA) {
|
|
|
|
break;
|
|
}
|
|
|
|
PmDetermineDeviceNameAndNumber (DeviceObject, &wmiRegistrationFlags);
|
|
|
|
|
|
LockDriver (extension->DriverExtension);
|
|
extension->IsStarted = TRUE;
|
|
UnlockDriver (extension->DriverExtension);
|
|
|
|
PmCheckAndUpdateSignature (extension, TRUE, TRUE);
|
|
|
|
|
|
//
|
|
// Register its existence with WMI
|
|
//
|
|
PmRegisterDevice(DeviceObject, wmiRegistrationFlags);
|
|
break;
|
|
|
|
case IRP_MN_QUERY_STOP_DEVICE:
|
|
case IRP_MN_CANCEL_STOP_DEVICE:
|
|
case IRP_MN_STOP_DEVICE:
|
|
case IRP_MN_QUERY_REMOVE_DEVICE:
|
|
case IRP_MN_CANCEL_REMOVE_DEVICE:
|
|
status = PmNotifyPartitions(extension, Irp);
|
|
if (!NT_SUCCESS(status)) {
|
|
break;
|
|
}
|
|
|
|
IoSkipCurrentIrpStackLocation(Irp);
|
|
return IoCallDriver(extension->TargetObject, Irp);
|
|
|
|
case IRP_MN_SURPRISE_REMOVAL:
|
|
case IRP_MN_REMOVE_DEVICE:
|
|
|
|
//
|
|
// Notify all the children of their imminent removal.
|
|
//
|
|
|
|
PmRemoveDevice(extension, Irp);
|
|
targetObject = extension->TargetObject;
|
|
|
|
if (irpSp->MinorFunction == IRP_MN_REMOVE_DEVICE) {
|
|
status = IoAcquireRemoveLock (&extension->RemoveLock, Irp);
|
|
ASSERT(NT_SUCCESS(status));
|
|
IoReleaseRemoveLockAndWait(&extension->RemoveLock, Irp);
|
|
IoDetachDevice(targetObject);
|
|
IoDeleteDevice(extension->DeviceObject);
|
|
}
|
|
|
|
IoSkipCurrentIrpStackLocation(Irp);
|
|
return IoCallDriver(targetObject, Irp);
|
|
|
|
default:
|
|
IoSkipCurrentIrpStackLocation(Irp);
|
|
return IoCallDriver(extension->TargetObject, Irp);
|
|
}
|
|
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
PmAddDevice(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PDEVICE_OBJECT PhysicalDeviceObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine creates and initializes a new FDO for the corresponding
|
|
PDO.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - Supplies the FTDISK driver object.
|
|
|
|
PhysicalDeviceObject - Supplies the physical device object.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_OBJECT attachedDevice;
|
|
NTSTATUS status;
|
|
PDEVICE_OBJECT deviceObject;
|
|
PDEVICE_EXTENSION extension;
|
|
|
|
attachedDevice = IoGetAttachedDeviceReference(PhysicalDeviceObject);
|
|
if (attachedDevice) {
|
|
if (attachedDevice->Characteristics&FILE_REMOVABLE_MEDIA) {
|
|
ObDereferenceObject(attachedDevice);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
ObDereferenceObject(attachedDevice);
|
|
}
|
|
|
|
status = IoCreateDevice(DriverObject, sizeof(DEVICE_EXTENSION),
|
|
NULL, FILE_DEVICE_UNKNOWN, 0, FALSE, &deviceObject);
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
deviceObject->Flags |= DO_DIRECT_IO;
|
|
|
|
//
|
|
// the storage stack explicitly requires DO_POWER_PAGABLE to be
|
|
// set in all filter drivers *unless* DO_POWER_INRUSH is set.
|
|
// this is true even if the attached device doesn't set DO_POWER_PAGABLE
|
|
//
|
|
if (attachedDevice->Flags & DO_POWER_INRUSH) {
|
|
deviceObject->Flags |= DO_POWER_INRUSH;
|
|
} else {
|
|
deviceObject->Flags |= DO_POWER_PAGABLE;
|
|
}
|
|
|
|
extension = deviceObject->DeviceExtension;
|
|
RtlZeroMemory(extension, sizeof(DEVICE_EXTENSION));
|
|
extension->DeviceObject = deviceObject;
|
|
extension->DriverExtension = IoGetDriverObjectExtension(DriverObject,
|
|
PmAddDevice);
|
|
extension->TargetObject =
|
|
IoAttachDeviceToDeviceStack(deviceObject, PhysicalDeviceObject);
|
|
if (!extension->TargetObject) {
|
|
IoDeleteDevice(deviceObject);
|
|
return STATUS_NO_SUCH_DEVICE;
|
|
}
|
|
extension->Pdo = PhysicalDeviceObject;
|
|
InitializeListHead(&extension->PartitionList);
|
|
KeInitializeEvent(&extension->PagingPathCountEvent,
|
|
SynchronizationEvent, TRUE);
|
|
InitializeListHead(&extension->SignatureList);
|
|
InitializeListHead(&extension->GuidList);
|
|
|
|
LockDriver (extension->DriverExtension);
|
|
InsertTailList(&extension->DriverExtension->DeviceExtensionList,
|
|
&extension->ListEntry);
|
|
UnlockDriver (extension->DriverExtension);
|
|
|
|
deviceObject->DeviceType = extension->TargetObject->DeviceType;
|
|
|
|
deviceObject->AlignmentRequirement =
|
|
extension->TargetObject->AlignmentRequirement;
|
|
|
|
extension->PhysicalDeviceName.Buffer
|
|
= extension->PhysicalDeviceNameBuffer;
|
|
|
|
// Allocate WMI library context
|
|
|
|
extension->WmilibContext =
|
|
ExAllocatePoolWithTag(PagedPool, sizeof(WMILIB_CONTEXT),
|
|
PARTMGR_TAG_PARTITION_ENTRY);
|
|
if (extension->WmilibContext != NULL)
|
|
{
|
|
RtlZeroMemory(extension->WmilibContext, sizeof(WMILIB_CONTEXT));
|
|
extension->WmilibContext->GuidCount = DiskperfGuidCount;
|
|
extension->WmilibContext->GuidList = DiskperfGuidList;
|
|
extension->WmilibContext->QueryWmiRegInfo = PmQueryWmiRegInfo;
|
|
extension->WmilibContext->QueryWmiDataBlock = PmQueryWmiDataBlock;
|
|
extension->WmilibContext->WmiFunctionControl = PmWmiFunctionControl;
|
|
}
|
|
|
|
|
|
KeInitializeSpinLock(&extension->SpinLock);
|
|
InitializeListHead(&extension->PowerQueue);
|
|
|
|
deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
|
|
|
|
IoInitializeRemoveLock (&extension->RemoveLock, PARTMGR_TAG_REMOVE_LOCK,
|
|
2, 5);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
PmUnload(
|
|
IN PDRIVER_OBJECT DriverObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine unloads.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - Supplies the driver object.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDO_EXTENSION driverExtension;
|
|
PDEVICE_OBJECT deviceObject;
|
|
|
|
while (deviceObject = DriverObject->DeviceObject) {
|
|
IoDeleteDevice(deviceObject);
|
|
}
|
|
|
|
driverExtension = IoGetDriverObjectExtension(DriverObject, PmAddDevice);
|
|
if (driverExtension) {
|
|
IoUnregisterPlugPlayNotification(driverExtension->NotificationEntry);
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
PmVolumeManagerNotification(
|
|
IN PVOID NotificationStructure,
|
|
IN PVOID DriverExtension
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called whenever a volume comes or goes.
|
|
|
|
Arguments:
|
|
|
|
NotificationStructure - Supplies the notification structure.
|
|
|
|
RootExtension - Supplies the root extension.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_INTERFACE_CHANGE_NOTIFICATION notification = (PDEVICE_INTERFACE_CHANGE_NOTIFICATION) NotificationStructure;
|
|
PDO_EXTENSION driverExtension = (PDO_EXTENSION) DriverExtension;
|
|
PVOLMGR_LIST_ENTRY volmgrEntry;
|
|
NTSTATUS status;
|
|
PLIST_ENTRY l, ll;
|
|
PDEVICE_EXTENSION extension;
|
|
PPARTITION_LIST_ENTRY partition;
|
|
PMWMICOUNTERLIB_CONTEXT input;
|
|
PIRP irp;
|
|
KEVENT event;
|
|
IO_STATUS_BLOCK ioStatus;
|
|
PFILE_OBJECT fileObject;
|
|
PDEVICE_OBJECT deviceObject;
|
|
|
|
LockDriver (driverExtension);
|
|
|
|
if (IsEqualGUID(¬ification->Event, &GUID_DEVICE_INTERFACE_ARRIVAL)) {
|
|
|
|
for (l = driverExtension->VolumeManagerList.Flink;
|
|
l != &driverExtension->VolumeManagerList; l = l->Flink) {
|
|
|
|
volmgrEntry = CONTAINING_RECORD(l, VOLMGR_LIST_ENTRY, ListEntry);
|
|
if (RtlEqualUnicodeString(notification->SymbolicLinkName,
|
|
&volmgrEntry->VolumeManagerName,
|
|
TRUE)) {
|
|
|
|
UnlockDriver (driverExtension);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
volmgrEntry = (PVOLMGR_LIST_ENTRY)
|
|
ExAllocatePoolWithTag(NonPagedPool,
|
|
sizeof(VOLMGR_LIST_ENTRY),
|
|
PARTMGR_TAG_VOLUME_ENTRY
|
|
);
|
|
if (!volmgrEntry) {
|
|
UnlockDriver (driverExtension);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
volmgrEntry->VolumeManagerName.Length =
|
|
notification->SymbolicLinkName->Length;
|
|
volmgrEntry->VolumeManagerName.MaximumLength =
|
|
volmgrEntry->VolumeManagerName.Length + sizeof(WCHAR);
|
|
volmgrEntry->VolumeManagerName.Buffer = ExAllocatePoolWithTag(
|
|
PagedPool, volmgrEntry->VolumeManagerName.MaximumLength,
|
|
PARTMGR_TAG_VOLUME_ENTRY);
|
|
if (!volmgrEntry->VolumeManagerName.Buffer) {
|
|
ExFreePool(volmgrEntry);
|
|
UnlockDriver (driverExtension);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
RtlCopyMemory(volmgrEntry->VolumeManagerName.Buffer,
|
|
notification->SymbolicLinkName->Buffer,
|
|
volmgrEntry->VolumeManagerName.Length);
|
|
volmgrEntry->VolumeManagerName.Buffer[
|
|
volmgrEntry->VolumeManagerName.Length/sizeof(WCHAR)] = 0;
|
|
|
|
volmgrEntry->RefCount = 0;
|
|
InsertTailList(&driverExtension->VolumeManagerList,
|
|
&volmgrEntry->ListEntry);
|
|
volmgrEntry->VolumeManager = NULL;
|
|
volmgrEntry->VolumeManagerFileObject = NULL;
|
|
|
|
status = IoGetDeviceObjectPointer(&volmgrEntry->VolumeManagerName,
|
|
FILE_READ_DATA, &fileObject,
|
|
&deviceObject);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
input.PmWmiCounterEnable = PmWmiCounterEnable;
|
|
input.PmWmiCounterDisable = PmWmiCounterDisable;
|
|
input.PmWmiCounterIoStart = PmWmiCounterIoStart;
|
|
input.PmWmiCounterIoComplete = PmWmiCounterIoComplete;
|
|
input.PmWmiCounterQuery = PmWmiCounterQuery;
|
|
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
|
|
irp = IoBuildDeviceIoControlRequest(
|
|
IOCTL_INTERNAL_VOLMGR_PMWMICOUNTERLIB_CONTEXT,
|
|
deviceObject, &input, sizeof(input), NULL, 0, TRUE,
|
|
&event, &ioStatus);
|
|
|
|
if (irp) {
|
|
status = IoCallDriver(deviceObject, irp);
|
|
if (status == STATUS_PENDING) {
|
|
KeWaitForSingleObject(&event, Executive, KernelMode,
|
|
FALSE, NULL);
|
|
}
|
|
}
|
|
|
|
ObDereferenceObject(fileObject);
|
|
}
|
|
|
|
for (l = driverExtension->DeviceExtensionList.Flink;
|
|
l != &driverExtension->DeviceExtensionList; l = l->Flink) {
|
|
|
|
extension = CONTAINING_RECORD(l, DEVICE_EXTENSION, ListEntry);
|
|
|
|
if (extension->IsRedundantPath) {
|
|
continue;
|
|
}
|
|
|
|
for (ll = extension->PartitionList.Flink;
|
|
ll != &extension->PartitionList; ll = ll->Flink) {
|
|
|
|
partition = CONTAINING_RECORD(ll, PARTITION_LIST_ENTRY,
|
|
ListEntry);
|
|
|
|
if (!partition->VolumeManagerEntry) {
|
|
status = PmGivePartition(volmgrEntry,
|
|
partition->TargetObject,
|
|
partition->WholeDiskPdo);
|
|
if (NT_SUCCESS(status)) {
|
|
partition->VolumeManagerEntry = volmgrEntry;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
} else if (IsEqualGUID(¬ification->Event,
|
|
&GUID_DEVICE_INTERFACE_REMOVAL)) {
|
|
|
|
for (l = driverExtension->VolumeManagerList.Flink;
|
|
l != &driverExtension->VolumeManagerList; l = l->Flink) {
|
|
|
|
volmgrEntry = CONTAINING_RECORD(l, VOLMGR_LIST_ENTRY, ListEntry);
|
|
if (RtlEqualUnicodeString(&volmgrEntry->VolumeManagerName,
|
|
notification->SymbolicLinkName, TRUE)) {
|
|
|
|
if (!volmgrEntry->RefCount) {
|
|
RemoveEntryList(l);
|
|
ExFreePool(volmgrEntry->VolumeManagerName.Buffer);
|
|
ExFreePool(volmgrEntry);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
} else {
|
|
status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
UnlockDriver (driverExtension);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
PmGetPartitionInformation(
|
|
IN PDEVICE_OBJECT Partition,
|
|
IN PFILE_OBJECT FileObject,
|
|
OUT PLONGLONG PartitionOffset,
|
|
OUT PLONGLONG PartitionLength
|
|
)
|
|
|
|
{
|
|
KEVENT event;
|
|
PIRP irp;
|
|
PIO_STACK_LOCATION irpSp;
|
|
PARTITION_INFORMATION partInfo;
|
|
IO_STATUS_BLOCK ioStatus;
|
|
NTSTATUS status;
|
|
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
|
|
irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_PARTITION_INFO,
|
|
Partition, NULL, 0, &partInfo,
|
|
sizeof(partInfo), FALSE, &event,
|
|
&ioStatus);
|
|
if (!irp) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
irpSp = IoGetNextIrpStackLocation(irp);
|
|
irpSp->FileObject = FileObject;
|
|
|
|
status = IoCallDriver(Partition, irp);
|
|
if (status == STATUS_PENDING) {
|
|
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
|
status = ioStatus.Status;
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
*PartitionOffset = partInfo.StartingOffset.QuadPart;
|
|
*PartitionLength = partInfo.PartitionLength.QuadPart;
|
|
|
|
return status;
|
|
}
|
|
|
|
VOID
|
|
PmDriverReinit(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PVOID DriverExtension,
|
|
IN ULONG Count
|
|
)
|
|
|
|
{
|
|
PDO_EXTENSION driverExtension = DriverExtension;
|
|
PLIST_ENTRY l, ll;
|
|
PDEVICE_EXTENSION extension;
|
|
PPARTITION_LIST_ENTRY partition;
|
|
NTSTATUS status;
|
|
PDRIVE_LAYOUT_INFORMATION_EX layout;
|
|
|
|
|
|
LockDriver (driverExtension);
|
|
|
|
InterlockedExchange(&driverExtension->PastReinit, TRUE);
|
|
|
|
for (l = driverExtension->DeviceExtensionList.Flink;
|
|
l != &driverExtension->DeviceExtensionList; l = l->Flink) {
|
|
|
|
extension = CONTAINING_RECORD(l, DEVICE_EXTENSION, ListEntry);
|
|
|
|
if (extension->TargetObject->Characteristics&FILE_REMOVABLE_MEDIA) {
|
|
continue;
|
|
}
|
|
|
|
if (!extension->IsStarted) {
|
|
continue;
|
|
}
|
|
|
|
for (ll = extension->PartitionList.Flink;
|
|
ll != &extension->PartitionList; ll = ll->Flink) {
|
|
|
|
partition = CONTAINING_RECORD(ll, PARTITION_LIST_ENTRY,
|
|
ListEntry);
|
|
|
|
partition->TargetObject->Flags |= DO_DEVICE_INITIALIZING;
|
|
}
|
|
|
|
status = PmReadPartitionTableEx(extension->TargetObject, &layout);
|
|
if (!NT_SUCCESS(status)) {
|
|
continue;
|
|
}
|
|
|
|
if (extension->DiskSignature) {
|
|
if (layout->PartitionStyle == PARTITION_STYLE_MBR) {
|
|
layout->Mbr.Signature = extension->DiskSignature;
|
|
PmWritePartitionTableEx(extension->TargetObject, layout);
|
|
}
|
|
extension->DiskSignature = 0;
|
|
}
|
|
|
|
if (layout->PartitionStyle == PARTITION_STYLE_GPT) {
|
|
PmAddSignatures(extension, layout);
|
|
}
|
|
|
|
ExFreePool(layout);
|
|
}
|
|
|
|
UnlockDriver (driverExtension);
|
|
}
|
|
|
|
VOID
|
|
PmBootDriverReinit(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PVOID DriverExtension,
|
|
IN ULONG Count
|
|
)
|
|
|
|
{
|
|
IoRegisterDriverReinitialization(DriverObject, PmDriverReinit,
|
|
DriverExtension);
|
|
}
|
|
|
|
NTSTATUS
|
|
PmCheckForUnclaimedPartitions(
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
)
|
|
|
|
{
|
|
PDEVICE_EXTENSION extension = DeviceObject->DeviceExtension;
|
|
PDO_EXTENSION driverExtension = extension->DriverExtension;
|
|
NTSTATUS status, status2;
|
|
PLIST_ENTRY l, ll;
|
|
PPARTITION_LIST_ENTRY partition;
|
|
PVOLMGR_LIST_ENTRY volmgrEntry;
|
|
|
|
LockDriver (driverExtension);
|
|
|
|
if (extension->IsRedundantPath) {
|
|
UnlockDriver (driverExtension);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
status = STATUS_SUCCESS;
|
|
for (l = extension->PartitionList.Flink; l != &extension->PartitionList;
|
|
l = l->Flink) {
|
|
|
|
partition = CONTAINING_RECORD(l, PARTITION_LIST_ENTRY, ListEntry);
|
|
if (partition->VolumeManagerEntry) {
|
|
continue;
|
|
}
|
|
|
|
for (ll = driverExtension->VolumeManagerList.Flink;
|
|
ll != &driverExtension->VolumeManagerList; ll = ll->Flink) {
|
|
|
|
volmgrEntry = CONTAINING_RECORD(ll, VOLMGR_LIST_ENTRY, ListEntry);
|
|
|
|
status2 = PmGivePartition(volmgrEntry,
|
|
partition->TargetObject,
|
|
partition->WholeDiskPdo);
|
|
|
|
if (NT_SUCCESS(status2)) {
|
|
partition->VolumeManagerEntry = volmgrEntry;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ll == &driverExtension->VolumeManagerList) {
|
|
status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
}
|
|
|
|
UnlockDriver (driverExtension);
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
PmDiskGrowPartition(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
{
|
|
PDEVICE_EXTENSION extension = DeviceObject->DeviceExtension;
|
|
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
PDISK_GROW_PARTITION input;
|
|
NTSTATUS status;
|
|
PPARTITION_LIST_ENTRY partition;
|
|
KEVENT event;
|
|
|
|
if (irpSp->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(DISK_GROW_PARTITION)) {
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
LockDriver (extension->DriverExtension);
|
|
|
|
input = (PDISK_GROW_PARTITION) Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
status = PmFindPartition(extension, input->PartitionNumber, &partition);
|
|
if (!NT_SUCCESS(status)) {
|
|
UnlockDriver (extension->DriverExtension);
|
|
return status;
|
|
}
|
|
|
|
status = PmChangePartitionIoctl(extension, partition,
|
|
IOCTL_INTERNAL_VOLMGR_QUERY_CHANGE_PARTITION);
|
|
if (!NT_SUCCESS(status)) {
|
|
UnlockDriver (extension->DriverExtension);
|
|
return status;
|
|
}
|
|
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
IoCopyCurrentIrpStackLocationToNext(Irp);
|
|
IoSetCompletionRoutine(Irp, PmSignalCompletion, &event, TRUE, TRUE, TRUE);
|
|
IoCallDriver(extension->TargetObject, Irp);
|
|
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
|
status = Irp->IoStatus.Status;
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
PmChangePartitionIoctl(extension, partition,
|
|
IOCTL_INTERNAL_VOLMGR_PARTITION_CHANGED);
|
|
} else {
|
|
PmChangePartitionIoctl(extension, partition,
|
|
IOCTL_INTERNAL_VOLMGR_CANCEL_CHANGE_PARTITION);
|
|
}
|
|
|
|
UnlockDriver (extension->DriverExtension);
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
PmEjectVolumeManagers(
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine goes through the list of partitions for the given disk
|
|
and takes the partition away from the owning volume managers. It then
|
|
goes through initial arbitration for each partition on the volume.
|
|
|
|
This has the effect that each volume manager forgets any cached disk
|
|
information and then start fresh on the disk as it may have been changed
|
|
by another cluster member.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Supplies the device object.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION extension = DeviceObject->DeviceExtension;
|
|
PDO_EXTENSION driverExtension = extension->DriverExtension;
|
|
PPARTITION_LIST_ENTRY partition;
|
|
PLIST_ENTRY l;
|
|
PVOLMGR_LIST_ENTRY volmgrEntry;
|
|
|
|
LockDriver (driverExtension);
|
|
|
|
for (l = extension->PartitionList.Flink; l != &extension->PartitionList;
|
|
l = l->Flink) {
|
|
|
|
partition = CONTAINING_RECORD(l, PARTITION_LIST_ENTRY, ListEntry);
|
|
if (!partition->VolumeManagerEntry) {
|
|
continue;
|
|
}
|
|
|
|
PmTakePartition(partition->VolumeManagerEntry,
|
|
partition->TargetObject, NULL);
|
|
|
|
partition->VolumeManagerEntry = NULL;
|
|
}
|
|
|
|
for (l = driverExtension->VolumeManagerList.Flink;
|
|
l != &driverExtension->VolumeManagerList; l = l->Flink) {
|
|
|
|
volmgrEntry = CONTAINING_RECORD(l, VOLMGR_LIST_ENTRY, ListEntry);
|
|
PmTakeWholeDisk(volmgrEntry, extension->Pdo);
|
|
}
|
|
|
|
UnlockDriver (driverExtension);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
PmQueryDiskSignature(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine returns the disk signature for the disk. If the
|
|
volume is not an MBR disk then this call will fail.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Supplies the device object.
|
|
|
|
Irp - Supplies the IO request packet.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION extension = DeviceObject->DeviceExtension;
|
|
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
PPARTMGR_DISK_SIGNATURE partSig = Irp->AssociatedIrp.SystemBuffer;
|
|
NTSTATUS status;
|
|
PDRIVE_LAYOUT_INFORMATION_EX layout;
|
|
|
|
if (irpSp->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(PARTMGR_DISK_SIGNATURE)) {
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
Irp->IoStatus.Information = sizeof(PARTMGR_DISK_SIGNATURE);
|
|
|
|
if (extension->DiskSignature) {
|
|
partSig->Signature = extension->DiskSignature;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
status = PmReadPartitionTableEx(extension->TargetObject, &layout);
|
|
if (!NT_SUCCESS(status)) {
|
|
Irp->IoStatus.Information = 0;
|
|
return status;
|
|
}
|
|
|
|
if (layout->PartitionStyle != PARTITION_STYLE_MBR) {
|
|
ExFreePool(layout);
|
|
Irp->IoStatus.Information = 0;
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
partSig->Signature = layout->Mbr.Signature;
|
|
|
|
ExFreePool(layout);
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
PmDeviceControl(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the dispatch for IRP_MJ_PNP.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Supplies the device object.
|
|
|
|
Irp - Supplies the IO request packet.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION extension = DeviceObject->DeviceExtension;
|
|
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
KEVENT event;
|
|
NTSTATUS status;
|
|
PDRIVE_LAYOUT_INFORMATION layout;
|
|
PDRIVE_LAYOUT_INFORMATION_EX newLayout;
|
|
BOOLEAN issueSigCheckNotification;
|
|
LIST_ENTRY completionList;
|
|
|
|
if (extension->TargetObject->Characteristics&FILE_REMOVABLE_MEDIA) {
|
|
IoSkipCurrentIrpStackLocation(Irp);
|
|
return IoCallDriver(extension->TargetObject, Irp);
|
|
}
|
|
|
|
|
|
|
|
switch (irpSp->Parameters.DeviceIoControl.IoControlCode) {
|
|
case IOCTL_DISK_SET_DRIVE_LAYOUT:
|
|
case IOCTL_DISK_SET_DRIVE_LAYOUT_EX:
|
|
case IOCTL_DISK_CREATE_DISK:
|
|
case IOCTL_DISK_DELETE_DRIVE_LAYOUT:
|
|
case IOCTL_DISK_UPDATE_PROPERTIES:
|
|
|
|
|
|
LockDriver (extension->DriverExtension);
|
|
|
|
issueSigCheckNotification = !extension->SignaturesNotChecked ||
|
|
(IOCTL_DISK_UPDATE_PROPERTIES ==
|
|
irpSp->Parameters.DeviceIoControl.IoControlCode);
|
|
|
|
extension->SignaturesNotChecked = TRUE;
|
|
|
|
UnlockDriver (extension->DriverExtension);
|
|
|
|
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
IoCopyCurrentIrpStackLocationToNext(Irp);
|
|
IoSetCompletionRoutine(Irp, PmSignalCompletion, &event, TRUE,
|
|
TRUE, TRUE);
|
|
IoCallDriver(extension->TargetObject, Irp);
|
|
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE,
|
|
NULL);
|
|
|
|
status = Irp->IoStatus.Status;
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
status = PmCheckAndUpdateSignature (extension,
|
|
issueSigCheckNotification,
|
|
TRUE);
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
case IOCTL_PARTMGR_CHECK_UNCLAIMED_PARTITIONS:
|
|
status = PmCheckForUnclaimedPartitions(DeviceObject);
|
|
Irp->IoStatus.Information = 0;
|
|
break;
|
|
|
|
case IOCTL_DISK_GROW_PARTITION:
|
|
status = PmDiskGrowPartition(DeviceObject, Irp);
|
|
break;
|
|
|
|
case IOCTL_PARTMGR_EJECT_VOLUME_MANAGERS:
|
|
status = PmEjectVolumeManagers(DeviceObject);
|
|
break;
|
|
|
|
case IOCTL_PARTMGR_NOTIFY_SIGNATURE_CHECK:
|
|
status = PmSigCheckNotificationInsert (extension, Irp);
|
|
|
|
if (STATUS_PENDING == status) {
|
|
return (STATUS_PENDING);
|
|
}
|
|
|
|
break;
|
|
|
|
case IOCTL_DISK_PERFORMANCE:
|
|
//
|
|
// Verify user buffer is large enough for the performance data.
|
|
//
|
|
|
|
status = STATUS_SUCCESS;
|
|
if (irpSp->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(DISK_PERFORMANCE)) {
|
|
|
|
//
|
|
// Indicate unsuccessful status and no data transferred.
|
|
//
|
|
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
Irp->IoStatus.Information = 0;
|
|
}
|
|
else if (!(extension->CountersEnabled)) {
|
|
if (!PmQueryEnableAlways(DeviceObject)) {
|
|
status = STATUS_UNSUCCESSFUL;
|
|
Irp->IoStatus.Information = 0;
|
|
}
|
|
}
|
|
if (status == STATUS_SUCCESS) {
|
|
PmWmiCounterQuery(extension->PmWmiCounterContext,
|
|
(PDISK_PERFORMANCE) Irp->AssociatedIrp.SystemBuffer,
|
|
L"Partmgr ",
|
|
extension->DiskNumber);
|
|
Irp->IoStatus.Information = sizeof(DISK_PERFORMANCE);
|
|
}
|
|
break;
|
|
|
|
case IOCTL_DISK_PERFORMANCE_OFF:
|
|
//
|
|
// Turns off counting
|
|
//
|
|
if (extension->CountersEnabled) {
|
|
if (InterlockedCompareExchange(&extension->EnableAlways, 0, 1) == 1) {
|
|
if (!PmWmiCounterDisable(&extension->PmWmiCounterContext, FALSE, FALSE)) {
|
|
extension->CountersEnabled = FALSE;
|
|
}
|
|
}
|
|
}
|
|
Irp->IoStatus.Information = 0;
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
case IOCTL_PARTMGR_QUERY_DISK_SIGNATURE:
|
|
case IOCTL_DISK_GET_DRIVE_LAYOUT:
|
|
case IOCTL_DISK_GET_DRIVE_LAYOUT_EX:
|
|
|
|
status = PmCheckAndUpdateSignature (extension, TRUE, FALSE);
|
|
|
|
if (NT_SUCCESS (status) &&
|
|
(irpSp->Parameters.DeviceIoControl.IoControlCode ==
|
|
IOCTL_PARTMGR_QUERY_DISK_SIGNATURE)) {
|
|
|
|
status = PmQueryDiskSignature(DeviceObject, Irp);
|
|
break;
|
|
}
|
|
|
|
// Fall through.
|
|
|
|
default:
|
|
IoSkipCurrentIrpStackLocation(Irp);
|
|
return IoCallDriver(extension->TargetObject, Irp);
|
|
}
|
|
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
}
|
|
|
|
RTL_GENERIC_COMPARE_RESULTS
|
|
PmTableSignatureCompareRoutine(
|
|
IN PRTL_GENERIC_TABLE Table,
|
|
IN PVOID First,
|
|
IN PVOID Second
|
|
)
|
|
|
|
{
|
|
PSIGNATURE_TABLE_ENTRY f = First;
|
|
PSIGNATURE_TABLE_ENTRY s = Second;
|
|
|
|
if (f->Signature < s->Signature) {
|
|
return GenericLessThan;
|
|
}
|
|
|
|
if (f->Signature > s->Signature) {
|
|
return GenericGreaterThan;
|
|
}
|
|
|
|
return GenericEqual;
|
|
}
|
|
|
|
RTL_GENERIC_COMPARE_RESULTS
|
|
PmTableGuidCompareRoutine(
|
|
IN PRTL_GENERIC_TABLE Table,
|
|
IN PVOID First,
|
|
IN PVOID Second
|
|
)
|
|
|
|
{
|
|
PGUID_TABLE_ENTRY f = First;
|
|
PGUID_TABLE_ENTRY s = Second;
|
|
PULONGLONG p1, p2;
|
|
|
|
p1 = (PULONGLONG) &f->Guid;
|
|
p2 = (PULONGLONG) &s->Guid;
|
|
|
|
if (p1[0] < p2[0]) {
|
|
return GenericLessThan;
|
|
}
|
|
|
|
if (p1[0] > p2[0]) {
|
|
return GenericGreaterThan;
|
|
}
|
|
|
|
if (p1[1] < p2[1]) {
|
|
return GenericLessThan;
|
|
}
|
|
|
|
if (p1[1] > p2[1]) {
|
|
return GenericGreaterThan;
|
|
}
|
|
|
|
return GenericEqual;
|
|
}
|
|
|
|
PVOID
|
|
PmTableAllocateRoutine(
|
|
IN PRTL_GENERIC_TABLE Table,
|
|
IN CLONG Size
|
|
)
|
|
|
|
{
|
|
return ExAllocatePoolWithTag(PagedPool, Size, PARTMGR_TAG_TABLE_ENTRY);
|
|
}
|
|
|
|
VOID
|
|
PmTableFreeRoutine(
|
|
IN PRTL_GENERIC_TABLE Table,
|
|
IN PVOID Buffer
|
|
)
|
|
|
|
{
|
|
ExFreePool(Buffer);
|
|
}
|
|
|
|
NTSTATUS
|
|
DriverEntry(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PUNICODE_STRING RegistryPath
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the entry point for the driver.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - Supplies the driver object.
|
|
|
|
RegistryPath - Supplies the registry path for this driver.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG i;
|
|
NTSTATUS status;
|
|
PDO_EXTENSION driverExtension;
|
|
PDEVICE_OBJECT deviceObject;
|
|
|
|
for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) {
|
|
DriverObject->MajorFunction[i] = PmPassThrough;
|
|
}
|
|
|
|
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = PmDeviceControl;
|
|
DriverObject->MajorFunction[IRP_MJ_PNP] = PmPnp;
|
|
DriverObject->MajorFunction[IRP_MJ_POWER] = PmPower;
|
|
DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = PmWmi;
|
|
DriverObject->MajorFunction[IRP_MJ_READ] = PmReadWrite;
|
|
DriverObject->MajorFunction[IRP_MJ_WRITE] = PmReadWrite;
|
|
|
|
DriverObject->DriverExtension->AddDevice = PmAddDevice;
|
|
DriverObject->DriverUnload = PmUnload;
|
|
|
|
status = IoAllocateDriverObjectExtension(DriverObject, PmAddDevice,
|
|
sizeof(DO_EXTENSION),
|
|
&driverExtension);
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
driverExtension->DiskPerfRegistryPath.MaximumLength =
|
|
RegistryPath->Length + sizeof(UNICODE_NULL);
|
|
driverExtension->DiskPerfRegistryPath.Buffer =
|
|
ExAllocatePoolWithTag(
|
|
PagedPool,
|
|
driverExtension->DiskPerfRegistryPath.MaximumLength,
|
|
PARTMGR_TAG_PARTITION_ENTRY);
|
|
if (driverExtension->DiskPerfRegistryPath.Buffer != NULL)
|
|
{
|
|
RtlCopyUnicodeString(&driverExtension->DiskPerfRegistryPath,
|
|
RegistryPath);
|
|
} else {
|
|
driverExtension->DiskPerfRegistryPath.Length = 0;
|
|
driverExtension->DiskPerfRegistryPath.MaximumLength = 0;
|
|
}
|
|
|
|
driverExtension->DriverObject = DriverObject;
|
|
driverExtension->CurrentEpochNumber = 0;
|
|
InitializeListHead(&driverExtension->VolumeManagerList);
|
|
InitializeListHead(&driverExtension->DeviceExtensionList);
|
|
InitializeListHead(&driverExtension->SignatureCheckNotificationIrpQueue);
|
|
|
|
KeInitializeMutex(&driverExtension->Mutex, 0);
|
|
driverExtension->PastReinit = FALSE;
|
|
|
|
RtlInitializeGenericTable(&driverExtension->SignatureTable,
|
|
PmTableSignatureCompareRoutine,
|
|
PmTableAllocateRoutine,
|
|
PmTableFreeRoutine, driverExtension);
|
|
|
|
RtlInitializeGenericTable(&driverExtension->GuidTable,
|
|
PmTableGuidCompareRoutine,
|
|
PmTableAllocateRoutine,
|
|
PmTableFreeRoutine, driverExtension);
|
|
|
|
IoRegisterBootDriverReinitialization(DriverObject, PmBootDriverReinit,
|
|
driverExtension);
|
|
|
|
status = IoRegisterPlugPlayNotification(
|
|
EventCategoryDeviceInterfaceChange,
|
|
PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES,
|
|
(PVOID) &VOLMGR_VOLUME_MANAGER_GUID, DriverObject,
|
|
PmVolumeManagerNotification, driverExtension,
|
|
&driverExtension->NotificationEntry);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
driverExtension->BootDiskSig = PmQueryRegistrySignature();
|
|
PmQueryRegistryGuid(driverExtension);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
PmBuildDependantVolumeRelations(
|
|
IN PDEVICE_EXTENSION Extension,
|
|
OUT PDEVICE_RELATIONS *Relations
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine builds a list of volumes which are dependant on a given
|
|
physical disk. This list can be used for reporting removal relations
|
|
to the pnp system.
|
|
|
|
Arguments:
|
|
|
|
Extension - Supplies the device extension for the pm filter.
|
|
|
|
Relations - Supplies a location to store the relations list.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG partitionCount;
|
|
PIRP irp;
|
|
PDEVICE_RELATIONS relationsList;
|
|
PDEVICE_RELATIONS combinedList;
|
|
|
|
ULONG dependantVolumeCount = 0;
|
|
|
|
PLIST_ENTRY l;
|
|
PPARTITION_LIST_ENTRY partition;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
ULONG i;
|
|
|
|
KEVENT event;
|
|
|
|
PAGED_CODE();
|
|
|
|
LockDriver (Extension->DriverExtension);
|
|
|
|
//
|
|
// Count the number of partitions we know about. If there aren't any then
|
|
// there aren't any relations either.
|
|
//
|
|
|
|
for(l = Extension->PartitionList.Flink, partitionCount = 0;
|
|
l != &(Extension->PartitionList);
|
|
l = l->Flink, partitionCount++);
|
|
|
|
//
|
|
// Allocate the relations list.
|
|
//
|
|
|
|
relationsList = ExAllocatePoolWithTag(
|
|
PagedPool,
|
|
(sizeof(DEVICE_RELATIONS) +
|
|
(sizeof(PDEVICE_RELATIONS) * partitionCount)),
|
|
PARTMGR_TAG_DEPENDANT_VOLUME_LIST);
|
|
|
|
if(relationsList== NULL) {
|
|
UnlockDriver (Extension->DriverExtension);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlZeroMemory(relationsList, (sizeof(DEVICE_RELATIONS) +
|
|
sizeof(PDEVICE_OBJECT) * partitionCount));
|
|
|
|
if(partitionCount == 0) {
|
|
*Relations = relationsList;
|
|
UnlockDriver (Extension->DriverExtension);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
KeInitializeEvent(&event, SynchronizationEvent, FALSE);
|
|
|
|
for(l = Extension->PartitionList.Flink, i = 0, dependantVolumeCount = 0;
|
|
l != &(Extension->PartitionList);
|
|
l = l->Flink, i++) {
|
|
|
|
PDEVICE_RELATIONS dependantVolumes;
|
|
|
|
partition = CONTAINING_RECORD(l, PARTITION_LIST_ENTRY, ListEntry);
|
|
|
|
//
|
|
// Check to make sure the volume has a volume manager. If it doesn't
|
|
// then just skip to the next one.
|
|
//
|
|
|
|
if(partition->VolumeManagerEntry == NULL) {
|
|
continue;
|
|
}
|
|
|
|
status = PmQueryDependantVolumeList(
|
|
partition->VolumeManagerEntry->VolumeManager,
|
|
partition->TargetObject,
|
|
partition->WholeDiskPdo,
|
|
&dependantVolumes);
|
|
|
|
if(!NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Error getting this list. We'll need to release the lists from
|
|
// the other partitions.
|
|
//
|
|
|
|
break;
|
|
}
|
|
|
|
dependantVolumeCount += dependantVolumes->Count;
|
|
relationsList->Objects[i] = (PDEVICE_OBJECT) dependantVolumes;
|
|
}
|
|
|
|
UnlockDriver (Extension->DriverExtension);
|
|
|
|
relationsList->Count = i;
|
|
|
|
if(NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Allocate a new device relations list which can hold all the dependant
|
|
// volumes for all the partitions.
|
|
//
|
|
|
|
combinedList = ExAllocatePoolWithTag(
|
|
PagedPool,
|
|
(sizeof(DEVICE_RELATIONS) +
|
|
(sizeof(PDEVICE_OBJECT) * dependantVolumeCount)),
|
|
PARTMGR_TAG_DEPENDANT_VOLUME_LIST);
|
|
} else {
|
|
combinedList = NULL;
|
|
}
|
|
|
|
if(combinedList != NULL) {
|
|
|
|
RtlZeroMemory(combinedList,
|
|
(sizeof(DEVICE_RELATIONS) +
|
|
(sizeof(PDEVICE_OBJECT) * dependantVolumeCount)));
|
|
|
|
//
|
|
// For each partition list ...
|
|
//
|
|
|
|
for(i = 0; i < relationsList->Count; i++) {
|
|
|
|
PDEVICE_RELATIONS volumeList;
|
|
ULONG j;
|
|
|
|
volumeList = (PDEVICE_RELATIONS) relationsList->Objects[i];
|
|
|
|
//
|
|
// We might have skipped this volume above. If we did the object
|
|
// list should be NULL;
|
|
//
|
|
|
|
if(volumeList == NULL) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// For each dependant volume in that list ...
|
|
//
|
|
|
|
for(j = 0; j < volumeList->Count; j++) {
|
|
|
|
PDEVICE_OBJECT volume;
|
|
ULONG k;
|
|
|
|
volume = volumeList->Objects[j];
|
|
|
|
//
|
|
// Check to see if there's a duplicate in our combined list.
|
|
//
|
|
|
|
for(k = 0; k < combinedList->Count; k++) {
|
|
if(combinedList->Objects[k] == volume) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(k == combinedList->Count) {
|
|
|
|
//
|
|
// We found no match - shove this object onto the end of
|
|
// the set.
|
|
//
|
|
|
|
combinedList->Objects[k] = volume;
|
|
combinedList->Count++;
|
|
|
|
} else {
|
|
|
|
//
|
|
// We've got a spare reference to this device object.
|
|
// release it.
|
|
//
|
|
|
|
ObDereferenceObject(volume);
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Free the list.
|
|
//
|
|
|
|
ExFreePool(volumeList);
|
|
relationsList->Objects[i] = NULL;
|
|
}
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
} else {
|
|
|
|
|
|
//
|
|
// For each partition list ...
|
|
//
|
|
|
|
for(i = 0; i < relationsList->Count; i++) {
|
|
|
|
PDEVICE_RELATIONS volumeList;
|
|
ULONG j;
|
|
|
|
volumeList = (PDEVICE_RELATIONS) relationsList->Objects[i];
|
|
|
|
//
|
|
// For each dependant volume in that list ...
|
|
//
|
|
|
|
for(j = 0; j < volumeList->Count; j++) {
|
|
|
|
PDEVICE_OBJECT volume;
|
|
|
|
volume = volumeList->Objects[j];
|
|
|
|
//
|
|
// Dereference the volume.
|
|
//
|
|
|
|
ObDereferenceObject(volume);
|
|
}
|
|
|
|
//
|
|
// Free the list.
|
|
//
|
|
|
|
ExFreePool(volumeList);
|
|
relationsList->Objects[i] = NULL;
|
|
}
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Free the list of lists.
|
|
//
|
|
|
|
ExFreePool(relationsList);
|
|
|
|
*Relations = combinedList;
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
PmQueryDependantVolumeList(
|
|
IN PDEVICE_OBJECT VolumeManager,
|
|
IN PDEVICE_OBJECT Partition,
|
|
IN PDEVICE_OBJECT WholeDiskPdo,
|
|
OUT PDEVICE_RELATIONS *DependantVolumes
|
|
)
|
|
{
|
|
KEVENT event;
|
|
PIRP irp;
|
|
|
|
IO_STATUS_BLOCK ioStatus;
|
|
NTSTATUS status;
|
|
VOLMGR_PARTITION_INFORMATION input;
|
|
VOLMGR_DEPENDANT_VOLUMES_INFORMATION output;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(DependantVolumes != NULL);
|
|
|
|
if (!VolumeManager) {
|
|
*DependantVolumes = ExAllocatePoolWithTag(PagedPool,
|
|
sizeof(DEVICE_RELATIONS),
|
|
PARTMGR_TAG_DEPENDANT_VOLUME_LIST);
|
|
if (*DependantVolumes == NULL) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
(*DependantVolumes)->Count = 0;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
|
|
input.PartitionDeviceObject = Partition;
|
|
input.WholeDiskPdo = WholeDiskPdo;
|
|
|
|
irp = IoBuildDeviceIoControlRequest(
|
|
IOCTL_INTERNAL_VOLMGR_REFERENCE_DEPENDANT_VOLUMES, VolumeManager,
|
|
&input, sizeof(input), &output, sizeof(output), TRUE, &event,
|
|
&ioStatus);
|
|
|
|
if (!irp) {
|
|
*DependantVolumes = NULL;
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
status = IoCallDriver(VolumeManager, irp);
|
|
if (status == STATUS_PENDING) {
|
|
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
|
status = ioStatus.Status;
|
|
}
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
*DependantVolumes = output.DependantVolumeReferences;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
PmRemovePartition(
|
|
IN PPARTITION_LIST_ENTRY Partition
|
|
)
|
|
{
|
|
PIRP irp;
|
|
KEVENT event;
|
|
|
|
PIO_STACK_LOCATION nextStack;
|
|
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
irp = IoAllocateIrp(Partition->TargetObject->StackSize, FALSE);
|
|
|
|
if(irp == NULL) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
KeInitializeEvent(&event, SynchronizationEvent, FALSE);
|
|
|
|
nextStack = IoGetNextIrpStackLocation(irp);
|
|
|
|
nextStack->MajorFunction = IRP_MJ_PNP;
|
|
nextStack->MinorFunction = IRP_MN_REMOVE_DEVICE;
|
|
|
|
irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
|
|
|
|
IoSetCompletionRoutine(irp,
|
|
PmSignalCompletion,
|
|
&event,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE);
|
|
|
|
IoCallDriver(Partition->TargetObject, irp);
|
|
|
|
KeWaitForSingleObject(&event,
|
|
KernelMode,
|
|
Executive,
|
|
FALSE,
|
|
NULL);
|
|
|
|
status = irp->IoStatus.Status;
|
|
|
|
IoFreeIrp(irp);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
PmReadWrite(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the dispatch for IRP_MJ_READ & _WRITE.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Supplies the device object.
|
|
|
|
Irp - Supplies the IO request packet.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION extension =
|
|
(PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
|
|
|
if (extension->CountersEnabled || extension->PhysicalDiskIoNotifyRoutine) {
|
|
PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
PIO_STACK_LOCATION nextIrpStack = IoGetNextIrpStackLocation(Irp);
|
|
|
|
*nextIrpStack = *currentIrpStack;
|
|
|
|
if (extension->CountersEnabled) {
|
|
PmWmiCounterIoStart(extension->PmWmiCounterContext,
|
|
(PLARGE_INTEGER) ¤tIrpStack->Parameters.Read);
|
|
}
|
|
else { // need to calculate response time for tracing
|
|
PmWmiGetClock(
|
|
*((PLARGE_INTEGER)¤tIrpStack->Parameters.Read), NULL);
|
|
}
|
|
|
|
IoMarkIrpPending(Irp);
|
|
IoSetCompletionRoutine(Irp, PmIoCompletion, DeviceObject,
|
|
TRUE, TRUE, TRUE);
|
|
IoCallDriver(extension->TargetObject, Irp);
|
|
return STATUS_PENDING;
|
|
}
|
|
|
|
IoSkipCurrentIrpStackLocation(Irp);
|
|
return IoCallDriver(extension->TargetObject, Irp);
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
PmIoCompletion(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will get control from the system at the completion of an IRP.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - for the IRP.
|
|
|
|
Irp - The I/O request that just completed.
|
|
|
|
Context - Not used.
|
|
|
|
Return Value:
|
|
|
|
The IRP status.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
PPHYSICAL_DISK_IO_NOTIFY_ROUTINE notifyRoutine;
|
|
|
|
UNREFERENCED_PARAMETER(Context);
|
|
|
|
if (deviceExtension->CountersEnabled) {
|
|
PmWmiCounterIoComplete(deviceExtension->PmWmiCounterContext, Irp,
|
|
(PLARGE_INTEGER) &irpStack->Parameters.Read);
|
|
}
|
|
|
|
notifyRoutine = deviceExtension->PhysicalDiskIoNotifyRoutine;
|
|
if (notifyRoutine) {
|
|
#ifdef NTPERF
|
|
//
|
|
// For now, only NTPERF needs this for tracing. Remove ifdef if it
|
|
// is required for tracing in retail build
|
|
//
|
|
if (deviceExtension->CountersEnabled) {
|
|
DISK_PERFORMANCE PerfCounters;
|
|
PmWmiCounterQuery(deviceExtension->PmWmiCounterContext,
|
|
&PerfCounters,
|
|
L"Partmgr ",
|
|
deviceExtension->DiskNumber);
|
|
(*notifyRoutine) (deviceExtension->DiskNumber, Irp, &PerfCounters);
|
|
} else {
|
|
(*notifyRoutine) (deviceExtension->DiskNumber, Irp, NULL);
|
|
}
|
|
#else
|
|
if (!deviceExtension->CountersEnabled) {
|
|
LARGE_INTEGER completeTime;
|
|
PLARGE_INTEGER response;
|
|
response = (PLARGE_INTEGER) &irpStack->Parameters.Read;
|
|
PmWmiGetClock(completeTime, NULL);
|
|
response->QuadPart = completeTime.QuadPart - response->QuadPart;
|
|
}
|
|
(*notifyRoutine) (deviceExtension->DiskNumber, Irp, NULL);
|
|
#endif
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
PmWmi(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine handles any WMI requests for information.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Context for the activity.
|
|
|
|
Irp - The device control argument block.
|
|
|
|
Return Value:
|
|
|
|
Status is returned.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION irpSp;
|
|
NTSTATUS status;
|
|
SYSCTL_IRP_DISPOSITION disposition;
|
|
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
|
|
PAGED_CODE();
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
if (irpSp->MinorFunction == IRP_MN_SET_TRACE_NOTIFY)
|
|
{
|
|
PVOID buffer = irpSp->Parameters.WMI.Buffer;
|
|
ULONG bufferSize = irpSp->Parameters.WMI.BufferSize;
|
|
|
|
if (bufferSize < sizeof(PPHYSICAL_DISK_IO_NOTIFY_ROUTINE))
|
|
{
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
} else {
|
|
#ifdef NTPERF
|
|
//
|
|
// For NTPERF Build, automatically turn on counters for tracing
|
|
//
|
|
if ((deviceExtension->PhysicalDiskIoNotifyRoutine == NULL) &&
|
|
(*((PVOID *)buffer) != NULL)) {
|
|
PmWmiCounterEnable(&deviceExtension->PmWmiCounterContext);
|
|
deviceExtension->CountersEnabled = TRUE;
|
|
} else
|
|
if ((deviceExtension->PhysicalDiskIoNotifyRoutine != NULL) &&
|
|
(*((PVOID *)buffer) == NULL)) {
|
|
deviceExtension->CountersEnabled =
|
|
PmWmiCounterDisable(&deviceExtension->PmWmiCounterContext,
|
|
FALSE, FALSE);
|
|
}
|
|
#endif
|
|
deviceExtension->PhysicalDiskIoNotifyRoutine
|
|
= (PPHYSICAL_DISK_IO_NOTIFY_ROUTINE)
|
|
*((PVOID *)buffer);
|
|
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
Irp->IoStatus.Status = status;
|
|
Irp->IoStatus.Information = 0;
|
|
IoCompleteRequest( Irp, IO_NO_INCREMENT );
|
|
} else {
|
|
if (deviceExtension->WmilibContext == NULL) {
|
|
disposition = IrpForward;
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
else {
|
|
status = WmiSystemControl(deviceExtension->WmilibContext,
|
|
DeviceObject,
|
|
Irp,
|
|
&disposition);
|
|
}
|
|
switch (disposition)
|
|
{
|
|
case IrpProcessed:
|
|
{
|
|
break;
|
|
}
|
|
|
|
case IrpNotCompleted:
|
|
{
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
IoSkipCurrentIrpStackLocation(Irp);
|
|
|
|
status = IoCallDriver(deviceExtension->TargetObject, Irp);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return(status);
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
PmWmiFunctionControl(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN ULONG GuidIndex,
|
|
IN WMIENABLEDISABLECONTROL Function,
|
|
IN BOOLEAN Enable
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is a callback into the driver to query for enabling or
|
|
disabling events and data collection. When the driver has finished it
|
|
must call WmiCompleteRequest to complete the irp. The driver can return
|
|
STATUS_PENDING if the irp cannot be completed immediately.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject is the device whose events or data collection are being
|
|
enabled or disabled
|
|
|
|
Irp is the Irp that makes this request
|
|
|
|
GuidIndex is the index into the list of guids provided when the
|
|
device registered
|
|
|
|
Function differentiates between event and data collection operations
|
|
|
|
Enable indicates whether to enable or disable
|
|
|
|
|
|
Return Value:
|
|
|
|
status
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
|
|
PAGED_CODE();
|
|
|
|
deviceExtension = DeviceObject->DeviceExtension;
|
|
|
|
if (GuidIndex == 0)
|
|
{
|
|
status = STATUS_SUCCESS;
|
|
if (Function == WmiDataBlockControl) {
|
|
if (!Enable) {
|
|
deviceExtension->CountersEnabled =
|
|
PmWmiCounterDisable(&deviceExtension->PmWmiCounterContext,
|
|
FALSE, FALSE);
|
|
} else
|
|
if (NT_SUCCESS(status =
|
|
PmWmiCounterEnable(&deviceExtension->PmWmiCounterContext))) {
|
|
deviceExtension->CountersEnabled = TRUE;
|
|
}
|
|
}
|
|
} else {
|
|
status = STATUS_WMI_GUID_NOT_FOUND;
|
|
}
|
|
|
|
status = WmiCompleteRequest(
|
|
DeviceObject,
|
|
Irp,
|
|
status,
|
|
0,
|
|
IO_NO_INCREMENT);
|
|
return(status);
|
|
}
|
|
|
|
NTSTATUS
|
|
PmReadPartitionTableEx(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PDRIVE_LAYOUT_INFORMATION_EX* DriveLayout
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine reads the partition table for the disk.
|
|
|
|
The partition list is built in nonpaged pool that is allocated by this
|
|
routine. It is the caller's responsability to free this memory when it
|
|
is finished with the data.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer for device object for this disk.
|
|
|
|
DriveLayout - Pointer to the pointer that will return the patition list.
|
|
This buffer is allocated in nonpaged pool by this routine. It is
|
|
the responsability of the caller to free this memory if this
|
|
routine is successful.
|
|
|
|
Return Values:
|
|
|
|
Status.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
IO_STATUS_BLOCK IoStatus;
|
|
PIRP Irp;
|
|
KEVENT Event;
|
|
PVOID IoCtlBuffer;
|
|
ULONG IoCtlBufferSize;
|
|
ULONG NewAllocationSize;
|
|
ULONG NumTries;
|
|
|
|
//
|
|
// Initialize locals.
|
|
//
|
|
|
|
NumTries = 0;
|
|
IoCtlBuffer = NULL;
|
|
IoCtlBufferSize = 0;
|
|
KeInitializeEvent(&Event, NotificationEvent, FALSE);
|
|
|
|
//
|
|
// Initialize the IOCTL buffer.
|
|
//
|
|
|
|
IoCtlBuffer = ExAllocatePoolWithTag(NonPagedPool,
|
|
PAGE_SIZE,
|
|
PARTMGR_TAG_IOCTL_BUFFER);
|
|
|
|
if (!IoCtlBuffer) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto cleanup;
|
|
}
|
|
|
|
IoCtlBufferSize = PAGE_SIZE;
|
|
|
|
//
|
|
// First try to get the partition table by issuing an IOCTL.
|
|
//
|
|
|
|
do {
|
|
|
|
//
|
|
// Make sure the event is reset.
|
|
//
|
|
|
|
KeClearEvent(&Event);
|
|
|
|
//
|
|
// Build an IOCTL Irp.
|
|
//
|
|
|
|
Irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_DRIVE_LAYOUT_EX,
|
|
DeviceObject,
|
|
NULL,
|
|
0,
|
|
IoCtlBuffer,
|
|
IoCtlBufferSize,
|
|
FALSE,
|
|
&Event,
|
|
&IoStatus);
|
|
if (!Irp) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Call the driver.
|
|
//
|
|
|
|
Status = IoCallDriver(DeviceObject, Irp);
|
|
|
|
if (Status == STATUS_PENDING) {
|
|
KeWaitForSingleObject(&Event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
|
|
//
|
|
// Update status.
|
|
//
|
|
|
|
Status = IoStatus.Status;
|
|
}
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// We got it!
|
|
//
|
|
|
|
break;
|
|
}
|
|
|
|
if (Status != STATUS_BUFFER_TOO_SMALL) {
|
|
|
|
//
|
|
// It is a real error.
|
|
//
|
|
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Resize IOCTL buffer. We should not enter the loop with a
|
|
// NULL buffer.
|
|
//
|
|
|
|
ASSERT(IoCtlBuffer && IoCtlBufferSize);
|
|
|
|
NewAllocationSize = IoCtlBufferSize * 2;
|
|
|
|
ExFreePool(IoCtlBuffer);
|
|
IoCtlBufferSize = 0;
|
|
|
|
IoCtlBuffer = ExAllocatePoolWithTag(NonPagedPool,
|
|
NewAllocationSize,
|
|
PARTMGR_TAG_IOCTL_BUFFER);
|
|
|
|
if (!IoCtlBuffer) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto cleanup;
|
|
}
|
|
|
|
IoCtlBufferSize = NewAllocationSize;
|
|
|
|
//
|
|
// Try again with the new buffer but do not loop forever.
|
|
//
|
|
|
|
NumTries++;
|
|
|
|
if (NumTries > 32) {
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
goto cleanup;
|
|
}
|
|
|
|
} while (TRUE);
|
|
|
|
//
|
|
// If we came here we should have acquired the partition tables in
|
|
// IoCtlBuffer.
|
|
//
|
|
|
|
ASSERT(NT_SUCCESS(Status));
|
|
ASSERT(IoCtlBuffer && IoCtlBufferSize);
|
|
|
|
//
|
|
// Set the output parameter and clear IoCtlBuffer so we don't free
|
|
// it when we are cleaning up.
|
|
//
|
|
|
|
(*DriveLayout) = (PDRIVE_LAYOUT_INFORMATION_EX) IoCtlBuffer;
|
|
|
|
IoCtlBuffer = NULL;
|
|
IoCtlBufferSize = 0;
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
cleanup:
|
|
|
|
if (IoCtlBuffer) {
|
|
ASSERT(IoCtlBufferSize);
|
|
ExFreePool(IoCtlBuffer);
|
|
}
|
|
|
|
//
|
|
// If we were not successful with the IOCTL, pass the request off
|
|
// to IoReadPartitionTableEx.
|
|
//
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
Status = IoReadPartitionTableEx(DeviceObject,
|
|
DriveLayout);
|
|
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
PmWritePartitionTableEx(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PDRIVE_LAYOUT_INFORMATION_EX DriveLayout
|
|
)
|
|
|
|
{
|
|
ULONG layoutSize;
|
|
KEVENT event;
|
|
PIRP irp;
|
|
IO_STATUS_BLOCK ioStatus;
|
|
NTSTATUS status;
|
|
|
|
layoutSize = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry) +
|
|
DriveLayout->PartitionCount*sizeof(PARTITION_INFORMATION_EX);
|
|
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_SET_DRIVE_LAYOUT_EX,
|
|
DeviceObject, DriveLayout,
|
|
layoutSize, NULL, 0, FALSE, &event,
|
|
&ioStatus);
|
|
if (!irp) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
status = IoCallDriver(DeviceObject, irp);
|
|
if (status == STATUS_PENDING) {
|
|
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
|
status = ioStatus.Status;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
VOID
|
|
PmSigCheckUpdateEpoch (
|
|
IN PDEVICE_EXTENSION DeviceExtension,
|
|
IN PLIST_ENTRY CompletionList
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
PIRP irp;
|
|
PIO_STACK_LOCATION irpSp;
|
|
KIRQL oldIrql;
|
|
PLIST_ENTRY listElement;
|
|
|
|
|
|
//
|
|
// Update the epoch for this disk and move it to the head of the queue
|
|
//
|
|
DeviceExtension->EpochNumber = ++DeviceExtension->DriverExtension->CurrentEpochNumber;
|
|
|
|
RemoveEntryList (&DeviceExtension->ListEntry);
|
|
|
|
InsertHeadList (&DeviceExtension->DriverExtension->DeviceExtensionList,
|
|
&DeviceExtension->ListEntry);
|
|
|
|
|
|
//
|
|
// Now scan the list of outstanding notification irps and for those that
|
|
// are not yet cancelled move them to the completion list and fill in the
|
|
// buffers with the appropriate contents. Separating out the completion
|
|
// of the irps from their preparation means that we can avoid all the
|
|
// mucking around with the mutex and opening shynchronisation holes.
|
|
//
|
|
InitializeListHead (CompletionList);
|
|
|
|
listElement = DeviceExtension->DriverExtension->SignatureCheckNotificationIrpQueue.Flink;
|
|
|
|
|
|
while (listElement != &DeviceExtension->DriverExtension->SignatureCheckNotificationIrpQueue) {
|
|
|
|
irp = CONTAINING_RECORD (listElement, IRP, Tail.Overlay.ListEntry);
|
|
|
|
listElement = listElement->Flink;
|
|
|
|
|
|
IoAcquireCancelSpinLock (&oldIrql);
|
|
|
|
//
|
|
// Check if the cancel routine has been called. If so
|
|
// don't complete this irp.
|
|
//
|
|
status = (NULL == IoSetCancelRoutine (irp, NULL))
|
|
? STATUS_CANCELLED
|
|
: STATUS_SUCCESS;
|
|
|
|
if (NT_SUCCESS (status)) {
|
|
RemoveEntryList (&irp->Tail.Overlay.ListEntry);
|
|
}
|
|
|
|
IoReleaseCancelSpinLock (oldIrql);
|
|
|
|
|
|
if (NT_SUCCESS (status)) {
|
|
InsertTailList (CompletionList, &irp->Tail.Overlay.ListEntry);
|
|
|
|
status = PmSigCheckFillInNotificationIrp (DeviceExtension->DriverExtension, irp);
|
|
|
|
irp->IoStatus.Status = status;
|
|
}
|
|
}
|
|
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
PmSigCheckCompleteNotificationIrps(
|
|
IN PLIST_ENTRY CompletionList
|
|
)
|
|
{
|
|
PIRP irp;
|
|
PLIST_ENTRY listElement;
|
|
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
while (!IsListEmpty (CompletionList)) {
|
|
//
|
|
// There's an Irp waiting on the completion queue.
|
|
// The notification buffer is already filled in so
|
|
// we just need to send it back to the caller.
|
|
//
|
|
listElement = RemoveHeadList (CompletionList);
|
|
|
|
irp = CONTAINING_RECORD (listElement, IRP, Tail.Overlay.ListEntry);
|
|
|
|
IoCompleteRequest (irp, IO_NO_INCREMENT);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
PmSigCheckFillInNotificationIrp (
|
|
IN PDO_EXTENSION DriverExtension,
|
|
IN PIRP Irp
|
|
)
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation (Irp);
|
|
PPARTMGR_SIGNATURE_CHECK_EPOCH sigCheckEpoch = Irp->AssociatedIrp.SystemBuffer;
|
|
PPARTMGR_SIGNATURE_CHECK_DISKS sigCheckDisks = Irp->AssociatedIrp.SystemBuffer;
|
|
ULONG highestDiskEpochNumber = 0;
|
|
ULONG disksInBuffer = 0;
|
|
ULONG maxDisksInBuffer;
|
|
PLIST_ENTRY listElement;
|
|
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
maxDisksInBuffer = ((irpSp->Parameters.DeviceIoControl.OutputBufferLength -
|
|
FIELD_OFFSET (PARTMGR_SIGNATURE_CHECK_DISKS, DiskNumber))
|
|
/ sizeof (sigCheckDisks->DiskNumber[0])) ;
|
|
|
|
ASSERT (maxDisksInBuffer >= 1);
|
|
|
|
|
|
//
|
|
// Walk down the list of disks until either we run out of disks with
|
|
// an epoch higher than the request or we reach the head. Then we
|
|
// turn around and start filling in the buffer until we either
|
|
// run out of disks or run out of buffer.
|
|
//
|
|
listElement = DriverExtension->DeviceExtensionList.Flink;
|
|
|
|
while (listElement != &DriverExtension->DeviceExtensionList) {
|
|
|
|
deviceExtension = CONTAINING_RECORD (listElement, DEVICE_EXTENSION, ListEntry);
|
|
|
|
if (deviceExtension->EpochNumber <= sigCheckEpoch->RequestEpoch) {
|
|
//
|
|
// We have stepped past the device of interest so
|
|
// back up one place. Note that for single entries
|
|
// this will in fact take us back to the queue head
|
|
// so make sure we can handle that case in the
|
|
// following loop.
|
|
//
|
|
listElement = listElement->Blink;
|
|
break;
|
|
} else {
|
|
listElement = listElement->Flink;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Now move back towards the head of the list recording entries
|
|
// in the buffer as we go
|
|
//
|
|
while ((listElement != &DriverExtension->DeviceExtensionList) &&
|
|
(disksInBuffer < maxDisksInBuffer)) {
|
|
|
|
deviceExtension = CONTAINING_RECORD (listElement, DEVICE_EXTENSION, ListEntry);
|
|
|
|
listElement = listElement->Blink;
|
|
|
|
sigCheckDisks->DiskNumber [disksInBuffer++] = deviceExtension->DiskNumber;
|
|
|
|
highestDiskEpochNumber = UMAX (highestDiskEpochNumber, deviceExtension->EpochNumber);
|
|
}
|
|
|
|
|
|
if ((listElement != &DriverExtension->DeviceExtensionList) &&
|
|
(disksInBuffer == maxDisksInBuffer)) {
|
|
//
|
|
// We ran out of buffer before we ran out of disks.
|
|
// Return STATUS_BUFFER_OVERFLOW
|
|
//
|
|
status = STATUS_BUFFER_OVERFLOW;
|
|
|
|
} else {
|
|
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
sigCheckDisks->CurrentEpoch = DriverExtension->CurrentEpochNumber;
|
|
sigCheckDisks->HighestDiskEpochReturned = highestDiskEpochNumber;
|
|
sigCheckDisks->DiskNumbersReturned = disksInBuffer;
|
|
|
|
Irp->IoStatus.Information = UMAX (sizeof (PARTMGR_SIGNATURE_CHECK_DISKS),
|
|
FIELD_OFFSET (PARTMGR_SIGNATURE_CHECK_DISKS, DiskNumber) +
|
|
(sizeof (sigCheckDisks->DiskNumber[0]) * disksInBuffer));
|
|
|
|
return (status);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
PmSigCheckNotificationInsert (
|
|
IN PDEVICE_EXTENSION DeviceExtension,
|
|
IN PIRP Irp)
|
|
|
|
{
|
|
NTSTATUS status = STATUS_CANCELLED;
|
|
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation (Irp);
|
|
PPARTMGR_SIGNATURE_CHECK_EPOCH sigCheckEpoch;
|
|
PPARTMGR_SIGNATURE_CHECK_DISKS sigCheckDisks;
|
|
|
|
|
|
if ((irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof (PARTMGR_SIGNATURE_CHECK_EPOCH)) ||
|
|
(irpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof (PARTMGR_SIGNATURE_CHECK_DISKS))) {
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
|
|
} else {
|
|
|
|
sigCheckEpoch = Irp->AssociatedIrp.SystemBuffer;
|
|
sigCheckDisks = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
LockDriver (DeviceExtension->DriverExtension);
|
|
|
|
if (PARTMGR_REQUEST_CURRENT_DISK_EPOCH == sigCheckEpoch->RequestEpoch) {
|
|
|
|
//
|
|
// Caller requesting the current epoch number. Return the epoch
|
|
// number but no disks
|
|
//
|
|
|
|
sigCheckDisks->CurrentEpoch = DeviceExtension->DriverExtension->CurrentEpochNumber;
|
|
sigCheckDisks->HighestDiskEpochReturned = 0;
|
|
sigCheckDisks->DiskNumbersReturned = 0;
|
|
sigCheckDisks->DiskNumber [0] = 0;
|
|
|
|
|
|
Irp->IoStatus.Information = sizeof (PARTMGR_SIGNATURE_CHECK_DISKS);
|
|
status = STATUS_SUCCESS;
|
|
|
|
} else if (sigCheckEpoch->RequestEpoch > DeviceExtension->DriverExtension->CurrentEpochNumber) {
|
|
|
|
//
|
|
// Something wrong here. Caller asking about an epoch which hasn't
|
|
// happened yet.
|
|
//
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
status = STATUS_NOT_FOUND;
|
|
|
|
} else if (sigCheckEpoch->RequestEpoch < DeviceExtension->DriverExtension->CurrentEpochNumber) {
|
|
|
|
//
|
|
// Caller asking about an epoch which ended a while back. Fill in the
|
|
// request now with as much information as we can.
|
|
//
|
|
status = PmSigCheckFillInNotificationIrp (DeviceExtension->DriverExtension, Irp);
|
|
|
|
} else {
|
|
KIRQL oldIrql;
|
|
|
|
//
|
|
// The only thing left is that the request epoch is the same as the current
|
|
// epoch. Queue this Irp which will be completed when the epoch changes.
|
|
//
|
|
|
|
IoAcquireCancelSpinLock (&oldIrql);
|
|
|
|
InsertTailList (&DeviceExtension->DriverExtension->SignatureCheckNotificationIrpQueue,
|
|
&Irp->Tail.Overlay.ListEntry);
|
|
|
|
if (!Irp->Cancel) {
|
|
IoSetCancelRoutine (Irp, PmSigCheckNotificationCancel);
|
|
IoMarkIrpPending (Irp);
|
|
status = STATUS_PENDING;
|
|
} else {
|
|
RemoveEntryList (&Irp->Tail.Overlay.ListEntry);
|
|
Irp->IoStatus.Information = 0;
|
|
status = STATUS_CANCELLED;
|
|
}
|
|
|
|
IoReleaseCancelSpinLock (oldIrql);
|
|
|
|
}
|
|
|
|
|
|
UnlockDriver (DeviceExtension->DriverExtension);
|
|
}
|
|
|
|
|
|
return (status);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
PmSigCheckNotificationCancel (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp)
|
|
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
This function filters cancels an outstanding IOCTL IRP
|
|
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to the target device object of the create/open.
|
|
Irp - Pointer to the I/O Request Packet that represents the operation.
|
|
|
|
Return Value:
|
|
|
|
The function value is the status of the call to the file system's entry
|
|
point.
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
NTSTATUS status = STATUS_CANCELLED;
|
|
KIRQL oldIrql;
|
|
|
|
|
|
RemoveEntryList (&Irp->Tail.Overlay.ListEntry);
|
|
|
|
IoReleaseCancelSpinLock (Irp->CancelIrql);
|
|
|
|
Irp->IoStatus.Status = STATUS_CANCELLED;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
IoCompleteRequest (Irp, IO_NO_INCREMENT);
|
|
|
|
return (STATUS_SUCCESS);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
PmCheckAndUpdateSignature (
|
|
IN PDEVICE_EXTENSION DeviceExtension,
|
|
IN BOOLEAN IssueSigCheckNotifications,
|
|
IN BOOLEAN ForceSignatureCheck)
|
|
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
This function ...
|
|
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension
|
|
|
|
|
|
Return Value:
|
|
|
|
The function value is the status of the call to the file system's entry
|
|
point.
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
BOOLEAN processCompletionList = FALSE;
|
|
PDRIVE_LAYOUT_INFORMATION_EX newLayout;
|
|
LIST_ENTRY completionList;
|
|
|
|
|
|
if (ForceSignatureCheck || DeviceExtension->SignaturesNotChecked) {
|
|
|
|
status = PmReadPartitionTableEx (DeviceExtension->TargetObject, &newLayout);
|
|
|
|
|
|
/*
|
|
** If by the time we complete the read of the partition table
|
|
** the SignaturesNotChecked flag has been cleared there is no
|
|
** need to proceed further.
|
|
**
|
|
** Alternatively if we failed to read the partition table (eg
|
|
** due to a timeout from the disk) and the
|
|
** SignaturesNotChecked flag is already set then bail and
|
|
** leave a future pass to fix up the signatures.
|
|
*/
|
|
if (!(ForceSignatureCheck || DeviceExtension->SignaturesNotChecked)) {
|
|
|
|
if (NT_SUCCESS (status)) {
|
|
ExFreePool (newLayout);
|
|
}
|
|
|
|
return (STATUS_SUCCESS);
|
|
|
|
} else if (!NT_SUCCESS(status) && DeviceExtension->SignaturesNotChecked) {
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
** If we can't get the lock avoid the deadlock and just
|
|
** leave. Shouldn't happen with FtDisk and basic disks
|
|
** (recursive acquire) and dmio with dynamic disks doesn't
|
|
** really care about the (potentially bad) signature anyway.
|
|
*/
|
|
if (!LockDriverWithTimeout (DeviceExtension->DriverExtension)) {
|
|
|
|
if (NT_SUCCESS (status)) {
|
|
ExFreePool (newLayout);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
if (NT_SUCCESS (status)) {
|
|
|
|
if (IssueSigCheckNotifications) {
|
|
PmSigCheckUpdateEpoch (DeviceExtension, &completionList);
|
|
processCompletionList = TRUE;
|
|
}
|
|
|
|
PmAddSignatures (DeviceExtension, newLayout);
|
|
|
|
ExFreePool(newLayout);
|
|
}
|
|
|
|
DeviceExtension->SignaturesNotChecked = NT_SUCCESS (status) ? FALSE : TRUE;
|
|
|
|
UnlockDriver (DeviceExtension->DriverExtension);
|
|
|
|
|
|
if (processCompletionList) {
|
|
PmSigCheckCompleteNotificationIrps (&completionList);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return (status);
|
|
}
|