mirror of https://github.com/tongzx/nt5src
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.
1103 lines
26 KiB
1103 lines
26 KiB
/*++
|
|
Copyright (c) 1991-1999 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
fpfilter.c
|
|
|
|
Abstract:
|
|
|
|
This driver is a sample that shows how a Failure Prediction Filter
|
|
driver could be written. A failure prediction filter driver allows
|
|
hardware or software to predict if a disk will fail and if so report
|
|
this fact to the operating system.
|
|
|
|
NT automatically supports failure prediction for SCSI and ATAPI disks
|
|
that follow the SMART specification. A failure prediction filter driver
|
|
can enhance or ignore this support as it sees fit.
|
|
|
|
A failure prediction filter driver is polled periodically by disk.sys
|
|
to determine if the disk might fail in the future. And it is called
|
|
whenever an application requests the current failure prediction status.
|
|
|
|
Note that the underlying disk stack can support failure prediction.
|
|
This would be the case if another failure prediction filter driver
|
|
was installed lower in the stack and/or the disk supports SMART
|
|
and the disk stack itself uses SMART to predict failure. This driver
|
|
can forward the failure prediction ioctl to the stack to find out
|
|
if the lower filter drivers and/or device stack are predicting failure
|
|
and then include that information in its results.
|
|
|
|
Environment:
|
|
|
|
kernel mode only
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
|
|
|
|
#define INITGUID
|
|
|
|
#include "ntddk.h"
|
|
#include "ntdddisk.h"
|
|
#include "stdarg.h"
|
|
#include "stdio.h"
|
|
|
|
|
|
//
|
|
// Bit Flag Macros
|
|
//
|
|
|
|
#define SET_FLAG(Flags, Bit) ((Flags) |= (Bit))
|
|
#define CLEAR_FLAG(Flags, Bit) ((Flags) &= ~(Bit))
|
|
#define TEST_FLAG(Flags, Bit) (((Flags) & (Bit)) != 0)
|
|
|
|
//
|
|
// Remove lock
|
|
//
|
|
#define REMLOCK_TAG 'lfpF'
|
|
#define REMLOCK_MAXIMUM 1 // Max minutes system allows lock to be held
|
|
#define REMLOCK_HIGHWATER 250 // Max number of irps holding lock at one time
|
|
|
|
|
|
//
|
|
// Device Extension
|
|
//
|
|
|
|
typedef struct _DEVICE_EXTENSION {
|
|
|
|
//
|
|
// Back pointer to device object
|
|
//
|
|
|
|
PDEVICE_OBJECT DeviceObject;
|
|
|
|
//
|
|
// Target Device Object
|
|
//
|
|
|
|
PDEVICE_OBJECT TargetDeviceObject;
|
|
|
|
//
|
|
// must synchronize paging path notifications
|
|
//
|
|
KEVENT PagingPathCountEvent;
|
|
ULONG PagingPathCount;
|
|
|
|
//
|
|
// Since we may hold onto irps for an arbitrarily long time
|
|
// we need a remove lock so that our device does not get removed
|
|
// while an irp is being processed.
|
|
IO_REMOVE_LOCK RemoveLock;
|
|
|
|
//
|
|
// Flag that specifies if we should predict failure or not. More
|
|
// sophisticated drivers can use statistical or proprietary hardware
|
|
// mechanisms to predict failure.
|
|
BOOLEAN PredictFailure;
|
|
|
|
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;
|
|
|
|
#define DEVICE_EXTENSION_SIZE sizeof(DEVICE_EXTENSION)
|
|
|
|
|
|
//
|
|
// Function declarations
|
|
//
|
|
|
|
NTSTATUS
|
|
DriverEntry(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PUNICODE_STRING RegistryPath
|
|
);
|
|
|
|
NTSTATUS
|
|
FPFilterForwardIrpSynchronous(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
FPFilterAddDevice(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PDEVICE_OBJECT PhysicalDeviceObject
|
|
);
|
|
|
|
|
|
NTSTATUS
|
|
FPFilterDispatchPnp(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
FPFilterDispatchPower(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
FPFilterStartDevice(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
FPFilterRemoveDevice(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
FPFilterSendToNextDriver(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
FPFilterCreate(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
FPFilterReadWrite(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
FPFilterDeviceControl(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
FPFilterShutdownFlush(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
VOID
|
|
FPFilterUnload(
|
|
IN PDRIVER_OBJECT DriverObject
|
|
);
|
|
|
|
NTSTATUS FPFilterWmi(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
FPFilterIrpCompletion(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
);
|
|
|
|
|
|
VOID
|
|
FPFilterSyncFilterWithTarget(
|
|
IN PDEVICE_OBJECT FilterDevice,
|
|
IN PDEVICE_OBJECT TargetDevice
|
|
);
|
|
|
|
#if DBG
|
|
|
|
#define DEBUG_BUFFER_LENGTH 256
|
|
|
|
ULONG FPFilterDebug = 0;
|
|
UCHAR FPFilterDebugBuffer[DEBUG_BUFFER_LENGTH];
|
|
|
|
VOID
|
|
FPFilterDebugPrint(
|
|
ULONG DebugPrintLevel,
|
|
PCCHAR DebugMessage,
|
|
...
|
|
);
|
|
|
|
#define DebugPrint(x) FPFilterDebugPrint x
|
|
|
|
#else
|
|
|
|
#define DebugPrint(x)
|
|
|
|
#endif
|
|
|
|
//
|
|
// Define the sections that allow for discarding (i.e. paging) some of
|
|
// the code.
|
|
//
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text (INIT, DriverEntry)
|
|
#pragma alloc_text (PAGE, FPFilterAddDevice)
|
|
#pragma alloc_text (PAGE, FPFilterDispatchPnp)
|
|
#pragma alloc_text (PAGE, FPFilterStartDevice)
|
|
#pragma alloc_text (PAGE, FPFilterRemoveDevice)
|
|
#pragma alloc_text (PAGE, FPFilterUnload)
|
|
#pragma alloc_text (PAGE, FPFilterWmi)
|
|
#pragma alloc_text (PAGE, FPFilterSyncFilterWithTarget)
|
|
#endif
|
|
|
|
|
|
NTSTATUS
|
|
DriverEntry(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PUNICODE_STRING RegistryPath
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Installable driver initialization entry point.
|
|
This entry point is called directly by the I/O manager to set up the disk
|
|
failure prediction filter driver. The driver object is set up and
|
|
then the Pnp manager calls FPFilterAddDevice to attach to the boot
|
|
devices.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - The disk performance driver object.
|
|
|
|
RegistryPath - pointer to a unicode string representing the path,
|
|
to driver-specific key in the registry.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS if successful
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
ULONG ulIndex;
|
|
PDRIVER_DISPATCH * dispatch;
|
|
|
|
//
|
|
// Create dispatch points
|
|
//
|
|
for (ulIndex = 0, dispatch = DriverObject->MajorFunction;
|
|
ulIndex <= IRP_MJ_MAXIMUM_FUNCTION;
|
|
ulIndex++, dispatch++) {
|
|
|
|
*dispatch = FPFilterSendToNextDriver;
|
|
}
|
|
|
|
//
|
|
// Set up the device driver entry points.
|
|
//
|
|
|
|
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = FPFilterDeviceControl;
|
|
DriverObject->MajorFunction[IRP_MJ_PNP] = FPFilterDispatchPnp;
|
|
DriverObject->MajorFunction[IRP_MJ_POWER] = FPFilterDispatchPower;
|
|
|
|
DriverObject->DriverExtension->AddDevice = FPFilterAddDevice;
|
|
DriverObject->DriverUnload = FPFilterUnload;
|
|
|
|
return(STATUS_SUCCESS);
|
|
|
|
} // end DriverEntry()
|
|
|
|
#define FILTER_DEVICE_PROPOGATE_FLAGS 0
|
|
#define FILTER_DEVICE_PROPOGATE_CHARACTERISTICS (FILE_REMOVABLE_MEDIA | \
|
|
FILE_READ_ONLY_DEVICE | \
|
|
FILE_FLOPPY_DISKETTE \
|
|
)
|
|
|
|
VOID
|
|
FPFilterSyncFilterWithTarget(
|
|
IN PDEVICE_OBJECT FilterDevice,
|
|
IN PDEVICE_OBJECT TargetDevice
|
|
)
|
|
{
|
|
ULONG propFlags;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Propogate all useful flags from target to FPFilter. MountMgr will look
|
|
// at the FPFilter object capabilities to figure out if the disk is
|
|
// a removable and perhaps other things.
|
|
//
|
|
propFlags = TargetDevice->Flags & FILTER_DEVICE_PROPOGATE_FLAGS;
|
|
SET_FLAG(FilterDevice->Flags, propFlags);
|
|
|
|
propFlags = TargetDevice->Characteristics & FILTER_DEVICE_PROPOGATE_CHARACTERISTICS;
|
|
SET_FLAG(FilterDevice->Characteristics, propFlags);
|
|
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
FPFilterAddDevice(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PDEVICE_OBJECT PhysicalDeviceObject
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
|
|
Creates and initializes a new filter device object FiDO for the
|
|
corresponding PDO. Then it attaches the device object to the device
|
|
stack of the drivers for the device.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - Disk performance driver object.
|
|
PhysicalDeviceObject - Physical Device Object from the underlying layered driver
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
PDEVICE_OBJECT filterDeviceObject;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
PIRP irp;
|
|
ULONG registrationFlag = 0;
|
|
PCHAR buffer;
|
|
ULONG buffersize;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Create a filter device object for this device (partition).
|
|
//
|
|
|
|
DebugPrint((2, "FPFilterAddDevice: Driver %p Device %p\n",
|
|
DriverObject, PhysicalDeviceObject));
|
|
|
|
status = IoCreateDevice(DriverObject,
|
|
DEVICE_EXTENSION_SIZE,
|
|
NULL,
|
|
FILE_DEVICE_DISK,
|
|
FILE_DEVICE_SECURE_OPEN,
|
|
FALSE,
|
|
&filterDeviceObject);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
DebugPrint((1, "FPFilterAddDevice: Cannot create filterDeviceObject\n"));
|
|
return status;
|
|
}
|
|
|
|
SET_FLAG(filterDeviceObject->Flags, DO_DIRECT_IO);
|
|
|
|
deviceExtension = (PDEVICE_EXTENSION) filterDeviceObject->DeviceExtension;
|
|
|
|
RtlZeroMemory(deviceExtension, DEVICE_EXTENSION_SIZE);
|
|
|
|
//
|
|
// Attaches the device object to the highest device object in the chain and
|
|
// return the previously highest device object, which is passed to
|
|
// IoCallDriver when pass IRPs down the device stack
|
|
//
|
|
|
|
deviceExtension->TargetDeviceObject =
|
|
IoAttachDeviceToDeviceStack(filterDeviceObject, PhysicalDeviceObject);
|
|
|
|
if (deviceExtension->TargetDeviceObject == NULL) {
|
|
IoDeleteDevice(filterDeviceObject);
|
|
DebugPrint((1, "FPFilterAddDevice: Unable to attach %X to target %X\n",
|
|
filterDeviceObject, PhysicalDeviceObject));
|
|
return STATUS_NO_SUCH_DEVICE;
|
|
}
|
|
|
|
//
|
|
// Save the filter device object in the device extension
|
|
//
|
|
deviceExtension->DeviceObject = filterDeviceObject;
|
|
|
|
KeInitializeEvent(&deviceExtension->PagingPathCountEvent,
|
|
NotificationEvent, TRUE);
|
|
|
|
//
|
|
// default to DO_POWER_PAGABLE
|
|
//
|
|
|
|
SET_FLAG(filterDeviceObject->Flags, DO_POWER_PAGABLE);
|
|
|
|
//
|
|
// Initialize the remove lock
|
|
//
|
|
IoInitializeRemoveLock(&deviceExtension->RemoveLock,
|
|
REMLOCK_TAG,
|
|
REMLOCK_MAXIMUM,
|
|
REMLOCK_HIGHWATER);
|
|
|
|
//
|
|
// Clear the DO_DEVICE_INITIALIZING flag
|
|
//
|
|
|
|
CLEAR_FLAG(filterDeviceObject->Flags, DO_DEVICE_INITIALIZING);
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} // end FPFilterAddDevice()
|
|
|
|
|
|
NTSTATUS
|
|
FPFilterDispatchPnp(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dispatch for PNP
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Supplies the device object.
|
|
|
|
Irp - Supplies the I/O request packet.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
NTSTATUS status;
|
|
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
BOOLEAN lockHeld;
|
|
BOOLEAN irpCompleted;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugPrint((2, "FPFilterDispatchPnp: Device %X Irp %X\n",
|
|
DeviceObject, Irp));
|
|
|
|
|
|
//
|
|
// Acquire the remove lock so that device will not be removed while
|
|
// processing this irp.
|
|
//
|
|
status = IoAcquireRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
DebugPrint((3, "FpFilterPnp: Remove lock failed PNP Irp type [%#02x]\n",
|
|
irpSp->MinorFunction));
|
|
Irp->IoStatus.Information = 0;
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
}
|
|
|
|
lockHeld = TRUE;
|
|
irpCompleted = FALSE;
|
|
|
|
switch(irpSp->MinorFunction) {
|
|
|
|
case IRP_MN_START_DEVICE:
|
|
{
|
|
//
|
|
// Call the Start Routine handler.
|
|
//
|
|
DebugPrint((3,
|
|
"FPFilterDispatchPnp: Schedule completion for START_DEVICE"));
|
|
status = FPFilterStartDevice(DeviceObject, Irp);
|
|
break;
|
|
}
|
|
|
|
case IRP_MN_REMOVE_DEVICE:
|
|
{
|
|
//
|
|
// Call the Remove Routine handler.
|
|
//
|
|
DebugPrint((3,
|
|
"FPFilterDispatchPnp: Schedule completion for REMOVE_DEVICE"));
|
|
status = FPFilterRemoveDevice(DeviceObject, Irp);
|
|
|
|
//
|
|
// Remove locked released by FpFilterRemoveDevice
|
|
//
|
|
lockHeld = FALSE;
|
|
break;
|
|
}
|
|
|
|
case IRP_MN_DEVICE_USAGE_NOTIFICATION:
|
|
{
|
|
PIO_STACK_LOCATION irpStack;
|
|
ULONG count;
|
|
BOOLEAN setPagable;
|
|
|
|
DebugPrint((3,
|
|
"FPFilterDispatchPnp: Processing DEVICE_USAGE_NOTIFICATION"));
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
if (irpStack->Parameters.UsageNotification.Type != DeviceUsageTypePaging) {
|
|
IoReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
lockHeld = FALSE;
|
|
status = FPFilterSendToNextDriver(DeviceObject, Irp);
|
|
irpCompleted = TRUE;
|
|
break; // out of case statement
|
|
}
|
|
|
|
deviceExtension = DeviceObject->DeviceExtension;
|
|
|
|
//
|
|
// wait on the paging path event
|
|
//
|
|
|
|
status = KeWaitForSingleObject(&deviceExtension->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 (!irpStack->Parameters.UsageNotification.InPath &&
|
|
deviceExtension->PagingPathCount == 1 ) {
|
|
|
|
//
|
|
// removing the last paging file
|
|
// must have DO_POWER_PAGABLE bits set
|
|
//
|
|
|
|
if (TEST_FLAG(DeviceObject->Flags, DO_POWER_INRUSH)) {
|
|
DebugPrint((3, "FPFilterDispatchPnp: last paging file "
|
|
"removed but DO_POWER_INRUSH set, so not "
|
|
"setting PAGABLE bit "
|
|
"for DO %p\n", DeviceObject));
|
|
} else {
|
|
DebugPrint((2, "FPFilterDispatchPnp: Setting PAGABLE "
|
|
"bit for DO %p\n", DeviceObject));
|
|
SET_FLAG(DeviceObject->Flags, DO_POWER_PAGABLE);
|
|
setPagable = TRUE;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// send the irp synchronously
|
|
//
|
|
|
|
status = FPFilterForwardIrpSynchronous(DeviceObject, Irp);
|
|
|
|
//
|
|
// 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(
|
|
&deviceExtension->PagingPathCount,
|
|
irpStack->Parameters.UsageNotification.InPath);
|
|
|
|
if (irpStack->Parameters.UsageNotification.InPath) {
|
|
if (deviceExtension->PagingPathCount == 1) {
|
|
|
|
//
|
|
// first paging file addition
|
|
//
|
|
|
|
DebugPrint((3, "FPFilterDispatchPnp: Clearing PAGABLE bit "
|
|
"for DO %p\n", DeviceObject));
|
|
CLEAR_FLAG(DeviceObject->Flags, DO_POWER_PAGABLE);
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// cleanup the changes done above
|
|
//
|
|
|
|
if (setPagable == TRUE) {
|
|
CLEAR_FLAG(DeviceObject->Flags, DO_POWER_PAGABLE);
|
|
setPagable = FALSE;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// set the event so the next one can occur.
|
|
//
|
|
|
|
KeSetEvent(&deviceExtension->PagingPathCountEvent,
|
|
IO_NO_INCREMENT, FALSE);
|
|
}
|
|
|
|
default:
|
|
{
|
|
DebugPrint((3,
|
|
"FPFilterDispatchPnp: Forwarding irp"));
|
|
//
|
|
// Simply forward all other Irps
|
|
//
|
|
IoReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
lockHeld = FALSE;
|
|
status = FPFilterSendToNextDriver(DeviceObject, Irp);
|
|
irpCompleted = TRUE;
|
|
}
|
|
}
|
|
|
|
if (! irpCompleted)
|
|
{
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
}
|
|
|
|
if (lockHeld)
|
|
{
|
|
IoReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
}
|
|
|
|
return status;
|
|
|
|
} // end FPFilterDispatchPnp()
|
|
|
|
|
|
NTSTATUS
|
|
FPFilterIrpCompletion(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Forwarded IRP completion routine. Set an event and return
|
|
STATUS_MORE_PROCESSING_REQUIRED. Irp forwarder will wait on this
|
|
event and then re-complete the irp after cleaning up.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject is the device object of the WMI driver
|
|
Irp is the WMI irp that was just completed
|
|
Context is a PKEVENT that forwarder will wait on
|
|
|
|
Return Value:
|
|
|
|
STATUS_MORE_PORCESSING_REQUIRED
|
|
|
|
--*/
|
|
|
|
{
|
|
PKEVENT Event = (PKEVENT) Context;
|
|
|
|
UNREFERENCED_PARAMETER(DeviceObject);
|
|
UNREFERENCED_PARAMETER(Irp);
|
|
|
|
KeSetEvent(Event, IO_NO_INCREMENT, FALSE);
|
|
|
|
return(STATUS_MORE_PROCESSING_REQUIRED);
|
|
|
|
} // end FPFilterIrpCompletion()
|
|
|
|
|
|
NTSTATUS
|
|
FPFilterStartDevice(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called when a Pnp Start Irp is received.
|
|
It will schedule a completion routine to initialize and register with WMI.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - a pointer to the device object
|
|
|
|
Irp - a pointer to the irp
|
|
|
|
|
|
Return Value:
|
|
|
|
Status of processing the Start Irp
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
KEVENT event;
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
|
|
|
status = FPFilterForwardIrpSynchronous(DeviceObject, Irp);
|
|
|
|
FPFilterSyncFilterWithTarget(DeviceObject,
|
|
deviceExtension->TargetDeviceObject);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
FPFilterRemoveDevice(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called when the device is to be removed.
|
|
It will de-register itself from WMI first, detach itself from the
|
|
stack before deleting itself.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - a pointer to the device object
|
|
|
|
Irp - a pointer to the irp
|
|
|
|
|
|
Return Value:
|
|
|
|
Status of removing the device
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
|
|
PAGED_CODE();
|
|
|
|
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
|
|
|
status = FPFilterForwardIrpSynchronous(DeviceObject, Irp);
|
|
|
|
IoReleaseRemoveLockAndWait(&deviceExtension->RemoveLock, Irp);
|
|
|
|
IoDetachDevice(deviceExtension->TargetDeviceObject);
|
|
IoDeleteDevice(DeviceObject);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
FPFilterSendToNextDriver(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine sends the Irp to the next driver in line
|
|
when the Irp is not processed by this driver.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject
|
|
Irp
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
|
|
IoSkipCurrentIrpStackLocation(Irp);
|
|
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
|
|
|
return IoCallDriver(deviceExtension->TargetDeviceObject, Irp);
|
|
|
|
} // end FPFilterSendToNextDriver()
|
|
|
|
NTSTATUS
|
|
FPFilterDispatchPower(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
|
|
PoStartNextPowerIrp(Irp);
|
|
IoSkipCurrentIrpStackLocation(Irp);
|
|
|
|
deviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
|
|
return PoCallDriver(deviceExtension->TargetDeviceObject, Irp);
|
|
|
|
} // end FPFilterDispatchPower
|
|
|
|
NTSTATUS
|
|
FPFilterForwardIrpSynchronous(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine sends the Irp to the next driver in line
|
|
when the Irp needs to be processed by the lower drivers
|
|
prior to being processed by this one.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject
|
|
Irp
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
KEVENT event;
|
|
NTSTATUS status;
|
|
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
|
|
|
//
|
|
// copy the irpstack for the next device
|
|
//
|
|
|
|
IoCopyCurrentIrpStackLocationToNext(Irp);
|
|
|
|
//
|
|
// set a completion routine
|
|
//
|
|
|
|
IoSetCompletionRoutine(Irp, FPFilterIrpCompletion,
|
|
&event, TRUE, TRUE, TRUE);
|
|
|
|
//
|
|
// call the next lower device
|
|
//
|
|
|
|
status = IoCallDriver(deviceExtension->TargetDeviceObject, Irp);
|
|
|
|
//
|
|
// wait for the actual completion
|
|
//
|
|
|
|
if (status == STATUS_PENDING) {
|
|
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
|
status = Irp->IoStatus.Status;
|
|
}
|
|
|
|
return status;
|
|
|
|
} // end FPFilterForwardIrpSynchronous()
|
|
|
|
|
|
NTSTATUS
|
|
FPFilterDeviceControl(
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This device control dispatcher handles only the failure prediction
|
|
device control. All others are passed down to the disk drivers.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Context for the activity.
|
|
Irp - The device control argument block.
|
|
|
|
Return Value:
|
|
|
|
Status is returned.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
PSTORAGE_PREDICT_FAILURE checkFailure;
|
|
NTSTATUS status;
|
|
|
|
DebugPrint((2, "FPFilterDeviceControl: DeviceObject %X Irp %X\n",
|
|
DeviceObject, Irp));
|
|
|
|
//
|
|
// Acquire the remove lock so that device will not be removed while
|
|
// processing this irp.
|
|
//
|
|
status = IoAcquireRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
DebugPrint((3, "FpFilterDeviceControl: Remove lock failed IOCTL Irp type [%x]\n",
|
|
currentIrpStack->Parameters.DeviceIoControl.IoControlCode));
|
|
Irp->IoStatus.Information = 0;
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
}
|
|
|
|
|
|
if (currentIrpStack->Parameters.DeviceIoControl.IoControlCode ==
|
|
IOCTL_STORAGE_PREDICT_FAILURE)
|
|
{
|
|
|
|
//
|
|
// Verify user buffer is large enough for the failure prediction data
|
|
//
|
|
|
|
if (currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(STORAGE_PREDICT_FAILURE))
|
|
{
|
|
//
|
|
// Indicate unsuccessful status and no data transferred.
|
|
//
|
|
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
Irp->IoStatus.Information = sizeof(STORAGE_PREDICT_FAILURE);
|
|
|
|
} else {
|
|
|
|
//
|
|
// If underlying device stack supports failure prediction then
|
|
// most likely there is another filter driver and/or support for
|
|
// SMART in the disk stack. We'll call down to get its opinion
|
|
// first before making our own decision as to if the device
|
|
// is predicting failure.
|
|
|
|
status = FPFilterForwardIrpSynchronous(DeviceObject, Irp);
|
|
|
|
//
|
|
// Here we decide if we want to predict whether the device
|
|
// will fail or not. We can do many interesting things such
|
|
// as sending a hardware request to the physical device,
|
|
// doing some statistical analysis, whatever makes sense for
|
|
// the device in question.
|
|
//
|
|
checkFailure = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
//
|
|
// Since a driver lower on the stack has an opinion then we
|
|
// abide by it. We could also do more sophisticated analysis
|
|
|
|
} else {
|
|
RtlZeroMemory(checkFailure, sizeof(STORAGE_PREDICT_FAILURE));
|
|
|
|
checkFailure->PredictFailure = (deviceExtension->PredictFailure) ?
|
|
1 : 0;
|
|
status = STATUS_SUCCESS;
|
|
Irp->IoStatus.Information = sizeof(STORAGE_PREDICT_FAILURE);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Complete request.
|
|
//
|
|
|
|
Irp->IoStatus.Status = status;
|
|
|
|
IoReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
} else {
|
|
|
|
//
|
|
// Pass unrecognized device control requests
|
|
// down to next driver layer.
|
|
//
|
|
|
|
IoReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
|
|
status = FPFilterSendToNextDriver(DeviceObject, Irp);
|
|
}
|
|
|
|
return(status);
|
|
} // end FPFilterDeviceControl()
|
|
|
|
|
|
|
|
VOID
|
|
FPFilterUnload(
|
|
IN PDRIVER_OBJECT DriverObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Free all the allocated resources, etc.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - pointer to a driver object.
|
|
|
|
Return Value:
|
|
|
|
VOID.
|
|
|
|
--*/
|
|
{
|
|
PAGED_CODE();
|
|
|
|
return;
|
|
}
|
|
|
|
#if DBG
|
|
|
|
VOID
|
|
FPFilterDebugPrint(
|
|
ULONG DebugPrintLevel,
|
|
PCCHAR DebugMessage,
|
|
...
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Debug print for all FPFilter
|
|
|
|
Arguments:
|
|
|
|
Debug print level between 0 and 3, with 3 being the most verbose.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
va_list ap;
|
|
|
|
va_start(ap, DebugMessage);
|
|
|
|
|
|
if ((DebugPrintLevel <= (FPFilterDebug & 0x0000ffff)) ||
|
|
((1 << (DebugPrintLevel + 15)) & FPFilterDebug)) {
|
|
|
|
_vsnprintf(FPFilterDebugBuffer, DEBUG_BUFFER_LENGTH, DebugMessage, ap);
|
|
|
|
DbgPrint(FPFilterDebugBuffer);
|
|
}
|
|
|
|
va_end(ap);
|
|
|
|
}
|
|
#endif
|