1411 lines
43 KiB
C
1411 lines
43 KiB
C
/*++
|
|
Copyright (c) 2001-2002 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
Filter.c
|
|
|
|
Abstract:
|
|
|
|
A storage lower filter (to disk) driver that verifies each read/write
|
|
disk I/O on a per sector basis.
|
|
|
|
Environment:
|
|
|
|
kernel mode only
|
|
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
|
|
#include "Filter.h"
|
|
#include "Device.h"
|
|
#include "CRC.h"
|
|
#include "Util.h"
|
|
#include <safeboot.h>
|
|
|
|
#if DBG_WMI_TRACING
|
|
//
|
|
// for any file that has software tracing printouts, you must include a
|
|
// header file <filename>.tmh
|
|
// this file will be generated by the WPP processing phase
|
|
//
|
|
#include "Filter.tmh"
|
|
#endif
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text (INIT, DriverEntry)
|
|
#pragma alloc_text (PAGE, InitiateCRCTable)
|
|
#pragma alloc_text (PAGE, DataVerFilter_AddDevice)
|
|
#pragma alloc_text (PAGE, DataVerFilter_DispatchPnp)
|
|
#pragma alloc_text (PAGE, DataVerFilter_StartDevice)
|
|
#pragma alloc_text (PAGE, DataVerFilter_RemoveDevice)
|
|
#pragma alloc_text (PAGE, DataVerFilter_Unload)
|
|
#endif
|
|
|
|
|
|
/*
|
|
* Counter used to produce unique disk id.
|
|
*/
|
|
ULONG g_UniqueDiskId = 0;
|
|
|
|
#if DBG
|
|
volatile BOOLEAN DebugTrapOnWarn = FALSE;
|
|
#endif
|
|
|
|
|
|
/*
|
|
* This pointer is declared in ntoskrnl.lib.
|
|
* At load time, it is set to point to a ulong in the kernel which indicates whether we are in safe mode.
|
|
*/
|
|
extern PULONG InitSafeBootMode;
|
|
|
|
/*
|
|
* Can poke this in the debugger to cause trapping on a particular sector
|
|
*/
|
|
volatile ULONG DbgTrapSector = (ULONG)-1;
|
|
|
|
|
|
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. The driver object
|
|
is set up and then the Pnp manager calls DataVerFilter_AddDevice 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
|
|
|
|
--*/
|
|
|
|
{
|
|
if (*InitSafeBootMode == 0){
|
|
ULONG ulIndex;
|
|
|
|
#if DBG_WMI_TRACING
|
|
//
|
|
// Enable software tracing by registering using the WPP macro.
|
|
//
|
|
WPP_INIT_TRACING(DriverObject, RegistryPath);
|
|
#endif
|
|
|
|
for (ulIndex = 0; ulIndex <= IRP_MJ_MAXIMUM_FUNCTION; ulIndex++){
|
|
DriverObject->MajorFunction[ ulIndex ] = DataVerFilter_DispatchAny;
|
|
}
|
|
|
|
//
|
|
// Set up the device driver entry points.
|
|
//
|
|
DriverObject->MajorFunction[IRP_MJ_SCSI] = CrcScsi;
|
|
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DataVerFilter_DeviceControl;
|
|
DriverObject->MajorFunction[IRP_MJ_PNP] = DataVerFilter_DispatchPnp;
|
|
DriverObject->MajorFunction[IRP_MJ_POWER] = DataVerFilter_DispatchPower;
|
|
|
|
DriverObject->DriverExtension->AddDevice = DataVerFilter_AddDevice;
|
|
DriverObject->DriverUnload = DataVerFilter_Unload;
|
|
}
|
|
else {
|
|
/*
|
|
* The user chose safe boot at startup.
|
|
* By not setting AddDevice, crcdisk will not be inserted in the disk stack.
|
|
*/
|
|
ASSERT(!DriverObject->DriverExtension->AddDevice);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
InitiateCRCTable (
|
|
PDEVICE_EXTENSION DeviceExtension
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initiate the CRC Array. This will be used to store CRC's on a per
|
|
sector basis. It's a simple array of pointers, each of which points
|
|
to a memory block of fixed size.
|
|
|
|
Assumes SyncEvent is HELD.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - Device extension of the specific disk
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS iff successful; error code otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(DeviceExtension->ulNumSectors);
|
|
ASSERT(DeviceExtension->ulSectorSize);
|
|
|
|
if (!DeviceExtension->CRCMdlLists.pMdlItems){
|
|
DeviceExtension->CRCMdlLists.ulMaxItems = DeviceExtension->ulNumSectors / CRC_MDL_LOGIC_BLOCK_SIZE + 1;
|
|
|
|
DeviceExtension->CRCMdlLists.pMdlItems = AllocPool(
|
|
DeviceExtension,
|
|
NonPagedPool,
|
|
DeviceExtension->CRCMdlLists.ulMaxItems*sizeof(CRC_MDL_ITEM),
|
|
TRUE);
|
|
if (DeviceExtension->CRCMdlLists.pMdlItems){
|
|
ULONG i;
|
|
|
|
InitializeListHead(&DeviceExtension->CRCMdlLists.LockedLRUList);
|
|
for (i = 0; i < DeviceExtension->CRCMdlLists.ulMaxItems; i++){
|
|
InitializeListHead(&DeviceExtension->CRCMdlLists.pMdlItems[i].LockedLRUListEntry);
|
|
}
|
|
}
|
|
else {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
DataVerFilter_AddDevice(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PDEVICE_OBJECT PhysicalDeviceObject
|
|
)
|
|
|
|
/*++
|
|
Routine Description:
|
|
|
|
Creates and initializes a new filter device object FDO for the
|
|
corresponding PDO. Then it attaches the device object to the device
|
|
stack of the drivers for the device.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - Filter driver object.
|
|
PhysicalDeviceObject - Physical Device Object from the underlying driver
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PDEVICE_OBJECT filterDeviceObject = NULL;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Create a filter device object for this device stack.
|
|
//
|
|
|
|
status = IoCreateDevice(DriverObject,
|
|
sizeof(DEVICE_EXTENSION),
|
|
NULL,
|
|
PhysicalDeviceObject->DeviceType,
|
|
FILE_DEVICE_UNKNOWN,
|
|
FALSE,
|
|
&filterDeviceObject);
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
return status;
|
|
}
|
|
|
|
deviceExtension = (PDEVICE_EXTENSION) filterDeviceObject->DeviceExtension;
|
|
RtlZeroMemory(deviceExtension, sizeof(DEVICE_EXTENSION));
|
|
|
|
KeInitializeSpinLock(&deviceExtension->SpinLock);
|
|
KeInitializeEvent(&deviceExtension->SyncEvent, SynchronizationEvent, TRUE);
|
|
InitializeListHead(&deviceExtension->DeferredCheckSumList);
|
|
InitializeListHead(&deviceExtension->AllContextsListEntry);
|
|
InitializeListHead(&deviceExtension->CRCMdlLists.LockedLRUList);
|
|
|
|
deviceExtension->DeviceObject = filterDeviceObject;
|
|
|
|
//
|
|
// From this point forward, any error will have side effects that need to
|
|
// be cleaned up. Using a try-finally block allows us to modify the program
|
|
// easily without losing track of the side effects.
|
|
//
|
|
__try
|
|
{
|
|
|
|
//
|
|
// 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->LowerDeviceObject =
|
|
IoAttachDeviceToDeviceStack(filterDeviceObject, PhysicalDeviceObject);
|
|
|
|
if (deviceExtension->LowerDeviceObject == NULL) {
|
|
status = STATUS_DEVICE_REMOVED;
|
|
__leave;
|
|
}
|
|
|
|
deviceExtension->ReadCapacityWorkItem = IoAllocateWorkItem(PhysicalDeviceObject);
|
|
if (!deviceExtension->ReadCapacityWorkItem){
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
__leave;
|
|
}
|
|
|
|
deviceExtension->CheckSumWorkItem = IoAllocateWorkItem(PhysicalDeviceObject);
|
|
if (!deviceExtension->CheckSumWorkItem){
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// default to DIRECT_IO.
|
|
//
|
|
SET_FLAG(filterDeviceObject->Flags, DO_DIRECT_IO);
|
|
|
|
|
|
deviceExtension->ulSectorSize = 512; // default sector size
|
|
deviceExtension->ulNumSectors = 0;
|
|
deviceExtension->State = DEVSTATE_INITIALIZED;
|
|
|
|
InsertTailList(&AllContextsList, &deviceExtension->AllContextsListEntry);
|
|
|
|
//
|
|
// Clear the DO_DEVICE_INITIALIZING flag, so we can get IRPs
|
|
//
|
|
CLEAR_FLAG(filterDeviceObject->Flags, DO_DEVICE_INITIALIZING);
|
|
}
|
|
__finally
|
|
{
|
|
if ( !NT_SUCCESS( status ) )
|
|
{
|
|
//
|
|
// full clean up.
|
|
//
|
|
if (deviceExtension->ReadCapacityWorkItem){
|
|
IoFreeWorkItem(deviceExtension->ReadCapacityWorkItem);
|
|
}
|
|
if (deviceExtension->CheckSumWorkItem){
|
|
IoFreeWorkItem(deviceExtension->CheckSumWorkItem);
|
|
}
|
|
if ( deviceExtension->LowerDeviceObject ){
|
|
IoDetachDevice(deviceExtension->LowerDeviceObject);
|
|
}
|
|
IoDeleteDevice( filterDeviceObject );
|
|
}
|
|
}
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
DataVerFilter_StartDevice(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called when a Pnp Start Irp is received.
|
|
|
|
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 = DeviceObject->DeviceExtension;
|
|
NTSTATUS status;
|
|
ULONG ulPropFlags;
|
|
|
|
PAGED_CODE();
|
|
|
|
/*
|
|
* Forward the start irp down the stack and synchronize with its completion first.
|
|
* This will allow us to talk to the started stack before we return from the start.
|
|
*/
|
|
status = DataVerFilter_ForwardIrpSyn(DeviceObject, Irp);
|
|
ASSERT(NT_SUCCESS(status));
|
|
|
|
if (deviceExtension->State == DEVSTATE_INITIALIZED){
|
|
/*
|
|
* This is the first start. Do one-time initialization.
|
|
*/
|
|
|
|
status = GetDeviceDescriptor(deviceExtension, StorageDeviceProperty, &deviceExtension->StorageDeviceDesc);
|
|
if (NT_SUCCESS(status)){
|
|
|
|
deviceExtension->ulDiskId = InterlockedIncrement(&g_UniqueDiskId) - 1;
|
|
|
|
//
|
|
// Propogate all useful flags from target to Filter. MountMgr will look
|
|
// at the Filter object capabilities to figure out if the disk is
|
|
// a removable and perhaps other things.
|
|
//
|
|
|
|
ulPropFlags = deviceExtension->LowerDeviceObject->Flags & FILTER_DEVICE_PROPOGATE_FLAGS;
|
|
SET_FLAG(deviceExtension->LowerDeviceObject->Flags, ulPropFlags);
|
|
|
|
ulPropFlags = deviceExtension->LowerDeviceObject->Characteristics & FILTER_DEVICE_PROPOGATE_CHARACTERISTICS;
|
|
SET_FLAG(deviceExtension->LowerDeviceObject->Characteristics, ulPropFlags);
|
|
}
|
|
else {
|
|
ASSERT(NT_SUCCESS(status));
|
|
}
|
|
}
|
|
else if (deviceExtension->State == DEVSTATE_STOPPED){
|
|
/*
|
|
* This is a start following a stop. No need to do any one-time initialization.
|
|
*/
|
|
}
|
|
else {
|
|
ASSERT(!"unexpected pnp state");
|
|
}
|
|
|
|
deviceExtension->State = NT_SUCCESS(status) ? DEVSTATE_STARTED : DEVSTATE_START_FAILED;
|
|
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
DataVerFilter_RemoveDevice(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called when the device is to be removed.
|
|
It will detach itself from the stack before deleting itself.
|
|
Remove lock was acquired before this was called.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - a pointer to the device object
|
|
Irp - a pointer to the irp
|
|
|
|
|
|
Return Value:
|
|
|
|
Status of removing the device
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
/*
|
|
* We should not have been forwarded the remove irp if there is outstanding I/O.
|
|
*/
|
|
ASSERT(!deviceExtension->CompletedReadCapacityIrp);
|
|
|
|
/*
|
|
* The right way to handle REMOVE:
|
|
*
|
|
* 1. Synchronize with downward I/O (we don't originate I/O, so we don't need to do this)
|
|
* 2. Pass the remove down asynchronously.
|
|
* 3. Detach from the lower device object.
|
|
* 4. Free resources and delete own device object.
|
|
*/
|
|
|
|
deviceExtension->State = DEVSTATE_REMOVED;
|
|
|
|
status = DataVerFilter_ForwardIrpAsyn(DeviceObject, Irp, NULL, NULL);
|
|
|
|
IoDetachDevice(deviceExtension->LowerDeviceObject);
|
|
|
|
FreeAllPages(deviceExtension);
|
|
if (deviceExtension->CRCMdlLists.pMdlItems) FreePool(deviceExtension, deviceExtension->CRCMdlLists.pMdlItems, NonPagedPool);
|
|
if (deviceExtension->StorageDeviceDesc) FreePool(deviceExtension, deviceExtension->StorageDeviceDesc, NonPagedPool);
|
|
IoFreeWorkItem(deviceExtension->ReadCapacityWorkItem);
|
|
IoFreeWorkItem(deviceExtension->CheckSumWorkItem);
|
|
|
|
ASSERT(!deviceExtension->DbgNumPagedAllocs);
|
|
ASSERT(!deviceExtension->DbgNumNonPagedAllocs);
|
|
|
|
RemoveEntryList(&deviceExtension->AllContextsListEntry);
|
|
InitializeListHead(&deviceExtension->AllContextsListEntry);
|
|
|
|
IoDeleteDevice(DeviceObject);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
DataVerFilter_DispatchPnp(
|
|
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 irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
switch(irpStack->MinorFunction)
|
|
{
|
|
case IRP_MN_START_DEVICE:
|
|
//
|
|
// Call the Start Routine handler.
|
|
//
|
|
status = DataVerFilter_StartDevice(DeviceObject, Irp);
|
|
break;
|
|
|
|
case IRP_MN_STOP_DEVICE:
|
|
case IRP_MN_SURPRISE_REMOVAL:
|
|
deviceExtension->State = DEVSTATE_STOPPED;
|
|
status = DataVerFilter_ForwardIrpAsyn(DeviceObject, Irp, NULL, NULL);
|
|
break;
|
|
|
|
case IRP_MN_REMOVE_DEVICE:
|
|
//
|
|
// Call the Remove Routine handler.
|
|
//
|
|
status = DataVerFilter_RemoveDevice(DeviceObject, Irp);
|
|
break;
|
|
|
|
default:
|
|
//
|
|
// Simply forward all other Irps
|
|
//
|
|
status = DataVerFilter_ForwardIrpAsyn(DeviceObject, Irp, NULL, NULL);
|
|
break;
|
|
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dispatch for Power IRP
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Supplies the device object.
|
|
Irp - Supplies the I/O request packet.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
NTSTATUS
|
|
DataVerFilter_DispatchPower(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
{
|
|
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
NTSTATUS status;
|
|
|
|
if (irpSp->MinorFunction == IRP_MN_SET_POWER){
|
|
if ((irpSp->Parameters.Power.State.DeviceState == PowerDeviceD3) &&
|
|
(irpSp->Parameters.Power.ShutdownType == PowerActionHibernate)){
|
|
/*
|
|
* We are about to hibernate.
|
|
* System state will be written out via the crashdump path (i.e. not through crcdisk),
|
|
* but will be read back in via crcdisk.
|
|
* That means that we could get false positives on resume.
|
|
* So invalidate all our stored checksums.
|
|
*/
|
|
KIRQL oldIrql;
|
|
|
|
KeAcquireSpinLock(&deviceExtension->SpinLock, &oldIrql);
|
|
deviceExtension->NeedCriticalRecovery = TRUE;
|
|
if (!deviceExtension->IsCheckSumWorkItemOutstanding){
|
|
deviceExtension->IsCheckSumWorkItemOutstanding = TRUE;
|
|
IoQueueWorkItem(deviceExtension->CheckSumWorkItem, CheckSumWorkItemCallback, CriticalWorkQueue, deviceExtension);
|
|
}
|
|
deviceExtension->DbgNumHibernations++;
|
|
KeReleaseSpinLock(&deviceExtension->SpinLock, oldIrql);
|
|
}
|
|
}
|
|
|
|
PoStartNextPowerIrp(Irp);
|
|
IoSkipCurrentIrpStackLocation(Irp);
|
|
status = PoCallDriver(deviceExtension->LowerDeviceObject, Irp);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dispatch for Any Unhandled IRP
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Supplies the device object.
|
|
Irp - Supplies the I/O request packet.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
NTSTATUS
|
|
DataVerFilter_DispatchAny(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
{
|
|
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
|
|
deviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
|
|
|
|
status = DataVerFilter_ForwardIrpAsyn( DeviceObject, Irp, NULL, NULL );
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dispatch for Device Control IRP
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Supplies the device object.
|
|
Irp - Supplies the I/O request packet.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
NTSTATUS
|
|
DataVerFilter_DeviceControl(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
{
|
|
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
|
|
deviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
|
|
|
|
//
|
|
// now, we don't handle anything. But later we could use this
|
|
// or IRP_MJ_SCSI to receive controls from user mode component.
|
|
//
|
|
status = DataVerFilter_ForwardIrpAsyn( DeviceObject, Irp, NULL, NULL );
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
SCSIRead completion routine. Caculate the checksum before return.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - the device object of the WMI driver
|
|
Irp - the WMI irp that was just completed
|
|
pContext - pContext == > 0(changeId): memory is locked safe to use.
|
|
== = 0 : memory is not locked.
|
|
|
|
Return Value:
|
|
|
|
STATUS_MORE_PROCESSING_REQUIRED
|
|
|
|
--*/
|
|
NTSTATUS CrcScsiReadCompletion(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID pContext)
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
PCRC_COMPLETION_CONTEXT PCrcContext = pContext;
|
|
|
|
/*
|
|
* Pass parameters to workItem callback inside the irp
|
|
*/
|
|
Irp->Tail.Overlay.DriverContext[0] = ULongToPtr(0); // is not write
|
|
Irp->Tail.Overlay.DriverContext[1] = PCrcContext;
|
|
|
|
CompleteXfer(deviceExtension, Irp);
|
|
|
|
if (Irp->PendingReturned){
|
|
IoMarkIrpPending(Irp);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
VOID CompleteXfer(PDEVICE_EXTENSION DeviceExtension, PIRP Irp)
|
|
{
|
|
|
|
if (DeviceExtension->CRCMdlLists.mdlItemsAllocated){
|
|
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
PSCSI_REQUEST_BLOCK pSRB = irpStack->Parameters.Scsi.Srb;
|
|
ULONG ulLength = pSRB->DataTransferLength;
|
|
ULONG ulBlocks = ulLength / DeviceExtension->ulSectorSize;
|
|
UCHAR srbStat = SRB_STATUS(pSRB->SrbStatus);
|
|
PCDB pCdb = (PCDB)pSRB->Cdb;
|
|
BOOLEAN isWrite = (BOOLEAN)Irp->Tail.Overlay.DriverContext[0];
|
|
PCRC_COMPLETION_CONTEXT PCrcContext = Irp->Tail.Overlay.DriverContext[1];
|
|
PUCHAR pDataBuf = NULL;
|
|
ULONG ulLogicalBlockAddr;
|
|
NTSTATUS status;
|
|
BOOLEAN bCRCOk;
|
|
ULONG tmp;
|
|
|
|
ASSERT(DeviceExtension->ulSectorSize > 0);
|
|
ASSERT((ulLength % DeviceExtension->ulSectorSize) == 0);
|
|
|
|
/*
|
|
* Get the LBA out from our stack location where we stashed it.
|
|
*/
|
|
ulLogicalBlockAddr = (ULONG)(ULONG_PTR)irpStack->Parameters.Others.Argument4;
|
|
|
|
REVERSE_BYTES(&tmp, &pCdb->CDB10.LogicalBlockByte0);
|
|
ASSERT(tmp == ulLogicalBlockAddr);
|
|
|
|
if ((DbgTrapSector >= ulLogicalBlockAddr) && (DbgTrapSector < ulLogicalBlockAddr+ulBlocks)){
|
|
RETAIL_TRAP("hit trap sector (completion)");
|
|
}
|
|
|
|
if (isWrite){
|
|
|
|
if (DeviceExtension->ulNumSectors && DeviceExtension->CRCMdlLists.mdlItemsAllocated){
|
|
|
|
if (srbStat == SRB_STATUS_SUCCESS){
|
|
|
|
ASSERT(Irp->IoStatus.Status == STATUS_SUCCESS);
|
|
pDataBuf = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
|
|
if (!pDataBuf){
|
|
pDataBuf = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, HighPagePriority);
|
|
}
|
|
|
|
if (pDataBuf){
|
|
pDataBuf = pDataBuf + ( ((PUCHAR) pSRB->DataBuffer)
|
|
- ((PUCHAR)MmGetMdlVirtualAddress(Irp->MdlAddress)) );
|
|
|
|
bCRCOk = VerifyCheckSum(DeviceExtension, Irp, ulLogicalBlockAddr, ulLength, pDataBuf, TRUE);
|
|
ASSERT(bCRCOk);
|
|
}
|
|
else {
|
|
/*
|
|
* We couldn't read the written data block and update the checksum,
|
|
* so invalidate our checksum value.
|
|
*/
|
|
ASSERT(pDataBuf);
|
|
InvalidateChecksums(DeviceExtension, ulLogicalBlockAddr, ulLength);
|
|
}
|
|
}
|
|
else {
|
|
/*
|
|
* The contents of the disk blocks targeted by the failed write my be indeterminate,
|
|
* so invalidate our checksum for the part of the disk for which the write failed.
|
|
*/
|
|
InvalidateChecksums(DeviceExtension, ulLogicalBlockAddr, ulLength);
|
|
LogCRCWriteFailure(DeviceExtension->ulDiskId, ulLogicalBlockAddr, 0, srbStat);
|
|
DeviceExtension->DbgNumWriteFailures++;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
ULONG ulCRCIndex = ulLogicalBlockAddr / CRC_MDL_LOGIC_BLOCK_SIZE;
|
|
|
|
ASSERT(PCrcContext);
|
|
|
|
if (PCrcContext->AllocMapped){
|
|
|
|
if (srbStat == SRB_STATUS_SUCCESS){
|
|
|
|
pDataBuf = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
|
|
if (pDataBuf){
|
|
ASSERT(pDataBuf == PCrcContext->DbgDataBufPtrCopy);
|
|
pDataBuf = pDataBuf + ( (PUCHAR)(pSRB->DataBuffer)
|
|
- (PUCHAR)(MmGetMdlVirtualAddress(Irp->MdlAddress)) );
|
|
|
|
bCRCOk = VerifyCheckSum(DeviceExtension, Irp, ulLogicalBlockAddr, ulLength, pDataBuf, FALSE);
|
|
}
|
|
else {
|
|
DBGERR(("Temporary MDL Assignment Failed"));
|
|
}
|
|
}
|
|
else {
|
|
LogCRCReadFailure(DeviceExtension->ulDiskId, ulLogicalBlockAddr, ulBlocks, srbStat);
|
|
}
|
|
|
|
ASSERT(pSRB->DataBuffer == PCrcContext->DbgDataBufPtrCopy);
|
|
RtlCopyBytes(PCrcContext->VirtualDataBuff, pSRB->DataBuffer, ulLength);
|
|
IoFreeMdl(Irp->MdlAddress);
|
|
FreePool(DeviceExtension, pSRB->DataBuffer, NonPagedPool);
|
|
Irp->MdlAddress = PCrcContext->OriginalMdl;
|
|
pSRB->DataBuffer = PCrcContext->OriginalDataBuff;
|
|
}
|
|
|
|
}
|
|
|
|
FreePool(DeviceExtension, PCrcContext, NonPagedPool);
|
|
}
|
|
else {
|
|
ASSERT(DeviceExtension->CRCMdlLists.mdlItemsAllocated);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dispatch for SCSIRead
|
|
[should have acquired the remove locker before calling this funciton]
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Supplies the device object.
|
|
Irp - Supplies the I/O request packet.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
NTSTATUS
|
|
CrcScsiRead(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
{
|
|
//
|
|
// Read is simple since we have to wait for the IRP complete before
|
|
// we could calculate the checksum.
|
|
//
|
|
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
PSCSI_REQUEST_BLOCK pSRB = irpStack->Parameters.Scsi.Srb;
|
|
PCDB pCdb = (PCDB)pSRB->Cdb;
|
|
ULONG ulLogicalBlockAddr;
|
|
ULONG ulLength = pSRB->DataTransferLength;
|
|
ULONG ulBlocks = ulLength / deviceExtension->ulSectorSize;
|
|
NTSTATUS status;
|
|
PMDL TempMdl = NULL;
|
|
PUCHAR TempDataBuff = NULL;
|
|
PCRC_COMPLETION_CONTEXT PCrcContext;
|
|
|
|
REVERSE_BYTES(&ulLogicalBlockAddr, &pCdb->CDB10.LogicalBlockByte0);
|
|
ASSERT(pSRB->QueueSortKey == ulLogicalBlockAddr); // class uses LBA as QueueSortKey
|
|
|
|
if ((DbgTrapSector >= ulLogicalBlockAddr) && (DbgTrapSector < ulLogicalBlockAddr+ulBlocks)){
|
|
RETAIL_TRAP("hit trap sector (read)");
|
|
}
|
|
|
|
ASSERT(deviceExtension->ulSectorSize);
|
|
ASSERT(Irp->MdlAddress);
|
|
|
|
deviceExtension->DbgNumReads++;
|
|
if (pSRB->SrbFlags & SRB_CLASS_FLAGS_PAGING) deviceExtension->DbgNumPagingReads++;
|
|
|
|
if (!deviceExtension->ulNumSectors || !deviceExtension->CRCMdlLists.mdlItemsAllocated){
|
|
return DataVerFilter_ForwardIrpAsyn(DeviceObject, Irp, NULL, NULL);
|
|
}
|
|
|
|
/*
|
|
* Save the LBA in our stack location
|
|
*/
|
|
irpStack->Parameters.Others.Argument4 = (PVOID)(ULONG_PTR)ulLogicalBlockAddr;
|
|
|
|
/*
|
|
* VERY VERY IMPORTANT
|
|
* We double-buffer every read request, because the memory manager
|
|
* in some cases maps the same physical page for multiple linear pages,
|
|
* causing the same physical page to get written repeatedly with data
|
|
* from different sectors. A reused physical page is a 'garbage' page used by MM
|
|
* to fill in gaps in a spanning read. It may even be included in multiple outstanding
|
|
* requests at once, defeating any attempt by us to scan the MDL for this condition here.
|
|
* For our purposes, we need to make sure the data we're checksumming corresponds
|
|
* to consecutive sectors, so we have to double-buffer.
|
|
*/
|
|
PCrcContext = AllocPool(deviceExtension, NonPagedPool, sizeof(CRC_COMPLETION_CONTEXT), FALSE);
|
|
if (PCrcContext == NULL){
|
|
return DataVerFilter_ForwardIrpAsyn(DeviceObject, Irp, NULL, NULL);
|
|
}
|
|
|
|
PCrcContext->AllocMapped = TRUE;
|
|
|
|
TempDataBuff = AllocPool(deviceExtension, NonPagedPool, ulLength, FALSE);
|
|
if (TempDataBuff){
|
|
|
|
TempMdl = IoAllocateMdl(TempDataBuff,ulLength, FALSE, FALSE, NULL);
|
|
if (TempMdl){
|
|
PUCHAR sysAddr;
|
|
|
|
MmBuildMdlForNonPagedPool(TempMdl);
|
|
PCrcContext->OriginalMdl = Irp->MdlAddress;
|
|
sysAddr = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
|
|
if (sysAddr == NULL) {
|
|
PCrcContext->AllocMapped = FALSE;
|
|
FreePool(deviceExtension, TempDataBuff, NonPagedPool);
|
|
IoFreeMdl(TempMdl);
|
|
|
|
return DataVerFilter_ForwardIrpAsyn( DeviceObject,
|
|
Irp,
|
|
CrcScsiReadCompletion,
|
|
(PVOID)PCrcContext );
|
|
|
|
}
|
|
PCrcContext->OriginalDataBuff = (PUCHAR)pSRB->DataBuffer;
|
|
PCrcContext->VirtualDataBuff = sysAddr +
|
|
(ULONG)((PUCHAR)pSRB->DataBuffer -
|
|
(PCCHAR)MmGetMdlVirtualAddress(Irp->MdlAddress));
|
|
Irp->MdlAddress = TempMdl;
|
|
pSRB->DataBuffer = TempDataBuff;
|
|
PCrcContext->DbgDataBufPtrCopy = TempDataBuff;
|
|
}
|
|
else {
|
|
PCrcContext->AllocMapped = FALSE;
|
|
FreePool(deviceExtension, TempDataBuff, NonPagedPool);
|
|
}
|
|
}
|
|
else {
|
|
PCrcContext->AllocMapped = FALSE;
|
|
}
|
|
|
|
return DataVerFilter_ForwardIrpAsyn( DeviceObject,
|
|
Irp,
|
|
CrcScsiReadCompletion,
|
|
(PVOID)PCrcContext );
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
SCSIWrite completion routine. Check the flag of the WorkItem,
|
|
free WorkItem if needed.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - the device object of the WMI driver
|
|
Irp - the WMI irp that was just completed
|
|
pContext - point to the WorkItem that forwarder will wait on
|
|
|
|
Return Value:
|
|
|
|
STATUS_MORE_PROCESSING_REQUIRED
|
|
|
|
--*/
|
|
NTSTATUS CrcScsiWriteCompletion(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID pContext)
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
PCRC_COMPLETION_CONTEXT PCrcContext = pContext;
|
|
|
|
/*
|
|
* Pass parameters to workItem callback inside the irp
|
|
*/
|
|
Irp->Tail.Overlay.DriverContext[0] = ULongToPtr(1); // is write
|
|
Irp->Tail.Overlay.DriverContext[1] = PCrcContext;
|
|
|
|
CompleteXfer(deviceExtension, Irp);
|
|
|
|
if (Irp->PendingReturned){
|
|
IoMarkIrpPending(Irp);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dispatch for SCSIWrite
|
|
[removeLocker is already acquired by the DispatchScsi routine.]
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Supplies the device object.
|
|
Irp - Supplies the I/O request packet.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
NTSTATUS
|
|
CrcScsiWrite(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
KIRQL oldIrql;
|
|
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
PSCSI_REQUEST_BLOCK pSRB = irpStack->Parameters.Scsi.Srb;
|
|
ULONG ulLength = pSRB->DataTransferLength;
|
|
ULONG ulBlocks = ulLength / deviceExtension->ulSectorSize;
|
|
PCDB pCdb = (PCDB)pSRB->Cdb;
|
|
PCRC_COMPLETION_CONTEXT pCrcContext;
|
|
ULONG ulLogicalBlockAddr;
|
|
|
|
REVERSE_BYTES(&ulLogicalBlockAddr, &pCdb->CDB10.LogicalBlockByte0);
|
|
ASSERT(ulLogicalBlockAddr == pSRB->QueueSortKey); // class uses LBA as QueueSortKey
|
|
|
|
deviceExtension->DbgNumWrites++;
|
|
if (pSRB->SrbFlags & SRB_CLASS_FLAGS_PAGING) deviceExtension->DbgNumPagingWrites++;
|
|
|
|
if ((DbgTrapSector >= ulLogicalBlockAddr) && (DbgTrapSector < ulLogicalBlockAddr+ulBlocks)){
|
|
RETAIL_TRAP("hit trap sector (write)");
|
|
}
|
|
|
|
if (!deviceExtension->ulNumSectors || !deviceExtension->CRCMdlLists.mdlItemsAllocated){
|
|
status = DataVerFilter_ForwardIrpAsyn(DeviceObject, Irp, NULL, NULL);
|
|
}
|
|
else {
|
|
pCrcContext = AllocPool(deviceExtension, NonPagedPool, sizeof(CRC_COMPLETION_CONTEXT), FALSE);
|
|
if (pCrcContext){
|
|
|
|
/*
|
|
* Save the LBA in our stack location
|
|
*/
|
|
irpStack->Parameters.Others.Argument4 = (PVOID)(ULONG_PTR)ulLogicalBlockAddr;
|
|
|
|
status = DataVerFilter_ForwardIrpAsyn( DeviceObject,
|
|
Irp,
|
|
CrcScsiWriteCompletion,
|
|
(PVOID)pCrcContext);
|
|
}
|
|
else {
|
|
/*
|
|
* We won't be able to record these checksums, so invalidate them.
|
|
*/
|
|
InvalidateChecksums(deviceExtension, ulLogicalBlockAddr, ulLength);
|
|
|
|
status = DataVerFilter_ForwardIrpAsyn(DeviceObject, Irp, NULL, NULL);
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
SCSIWrite completion routine. Check the flag of the WorkItem,
|
|
free WorkItem if needed.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - the device object of the WMI driver
|
|
Irp - the WMI irp that was just completed
|
|
pContext - point to the WorkItem that forwarder will wait on
|
|
|
|
Return Value:
|
|
|
|
STATUS_MORE_PROCESSING_REQUIRED
|
|
|
|
--*/
|
|
NTSTATUS CrcScsiReadCapacityCompletion(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID pContext)
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension = pContext;
|
|
BOOLEAN queuedIrp;
|
|
KIRQL oldIrql;
|
|
NTSTATUS status;
|
|
|
|
KeAcquireSpinLock(&deviceExtension->SpinLock, &oldIrql);
|
|
|
|
if (NT_SUCCESS(Irp->IoStatus.Status) && (Irp->IoStatus.Information >= sizeof(READ_CAPACITY_DATA))){
|
|
if (deviceExtension->CompletedReadCapacityIrp){
|
|
DBGWARN(("overlapping read capacity irps"));
|
|
queuedIrp = FALSE;
|
|
}
|
|
else {
|
|
deviceExtension->CompletedReadCapacityIrp = Irp;
|
|
queuedIrp = TRUE;
|
|
}
|
|
}
|
|
else {
|
|
queuedIrp = FALSE;
|
|
}
|
|
|
|
KeReleaseSpinLock(&deviceExtension->SpinLock, oldIrql);
|
|
|
|
if (queuedIrp){
|
|
IoQueueWorkItem(deviceExtension->ReadCapacityWorkItem, ReadCapacityWorkItemCallback, DelayedWorkQueue, deviceExtension);
|
|
status = STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
else {
|
|
if (Irp->PendingReturned){
|
|
IoMarkIrpPending(Irp);
|
|
}
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
VOID ReadCapacityWorkItemCallback(PDEVICE_OBJECT DevObj, PVOID Context)
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension = Context;
|
|
PIRP readCapacityIrp;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PSCSI_REQUEST_BLOCK pSRB;
|
|
BOOLEAN needToReinitialize;
|
|
PREAD_CAPACITY_DATA pReadCapacityData;
|
|
ULONG ulSectorSize;
|
|
ULONG ulNumSectors;
|
|
KIRQL oldIrql;
|
|
|
|
/*
|
|
* Use a spinlock to synchronize with completion routine.
|
|
*/
|
|
KeAcquireSpinLock(&deviceExtension->SpinLock, &oldIrql);
|
|
ASSERT(deviceExtension->CompletedReadCapacityIrp);
|
|
readCapacityIrp = deviceExtension->CompletedReadCapacityIrp;
|
|
deviceExtension->CompletedReadCapacityIrp = NULL;
|
|
KeReleaseSpinLock(&deviceExtension->SpinLock, oldIrql);
|
|
|
|
irpStack = IoGetCurrentIrpStackLocation(readCapacityIrp);
|
|
pSRB = irpStack->Parameters.Scsi.Srb;
|
|
pReadCapacityData = pSRB->DataBuffer;
|
|
|
|
ASSERT(NT_SUCCESS(readCapacityIrp->IoStatus.Status));
|
|
ASSERT(readCapacityIrp->IoStatus.Information >= sizeof(READ_CAPACITY_DATA));
|
|
|
|
REVERSE_BYTES(&ulSectorSize, &pReadCapacityData->BytesPerBlock);
|
|
REVERSE_BYTES(&ulNumSectors, &pReadCapacityData->LogicalBlockAddress);
|
|
ulNumSectors++;
|
|
|
|
/*
|
|
* If the drive has removable media, then we have to reinitialize the checksums
|
|
* every time we see readCapacity completing, because the media may have changed.
|
|
* For fixed media, we only have to reinitialize if the readCapacity result indicates that the disk has grown,
|
|
* which is basically never (except for some specific raid boxes that allow expansion of logical drives at runtime).
|
|
*/
|
|
if (deviceExtension->StorageDeviceDesc->RemovableMedia){
|
|
needToReinitialize = TRUE;
|
|
}
|
|
else {
|
|
if (ulSectorSize != deviceExtension->ulSectorSize){
|
|
ASSERT(deviceExtension->ulSectorSize == 0);
|
|
needToReinitialize = TRUE;
|
|
}
|
|
else if (ulNumSectors != deviceExtension->ulNumSectors){
|
|
if (deviceExtension->ulNumSectors != 0){
|
|
ASSERT(!"coverage -- logical disk extended");
|
|
}
|
|
ASSERT(ulNumSectors > deviceExtension->ulNumSectors);
|
|
needToReinitialize = TRUE;
|
|
}
|
|
else {
|
|
needToReinitialize = FALSE;
|
|
}
|
|
}
|
|
|
|
if (needToReinitialize){
|
|
BOOLEAN needToFree;
|
|
|
|
/*
|
|
* Use an event (which does not raise irql like a spinlock)
|
|
* for synchronizing accesses to paged pool.
|
|
*/
|
|
AcquirePassiveLevelLock(deviceExtension);
|
|
|
|
KeAcquireSpinLock(&deviceExtension->SpinLock, &oldIrql);
|
|
needToFree = deviceExtension->CRCMdlLists.mdlItemsAllocated;
|
|
deviceExtension->CRCMdlLists.mdlItemsAllocated = FALSE;
|
|
KeReleaseSpinLock(&deviceExtension->SpinLock, oldIrql);
|
|
|
|
if (needToFree){
|
|
FreeAllPages(deviceExtension);
|
|
FreePool(deviceExtension, deviceExtension->CRCMdlLists.pMdlItems, NonPagedPool);
|
|
deviceExtension->CRCMdlLists.pMdlItems = NULL;
|
|
}
|
|
|
|
if (ulSectorSize == 0){
|
|
ulSectorSize = 512;
|
|
}
|
|
else {
|
|
//
|
|
// clear all but the highest set bit.
|
|
//
|
|
while (ulSectorSize & (ulSectorSize - 1)){
|
|
ulSectorSize &= (ulSectorSize - 1);
|
|
}
|
|
}
|
|
|
|
deviceExtension->ulSectorSize = ulSectorSize;
|
|
deviceExtension->ulNumSectors = ulNumSectors;
|
|
|
|
if ((deviceExtension->ulSectorSize > 0) && (deviceExtension->ulNumSectors > 0)){
|
|
NTSTATUS status;
|
|
|
|
status = InitiateCRCTable(deviceExtension);
|
|
|
|
deviceExtension->CRCMdlLists.mdlItemsAllocated = NT_SUCCESS(status) ? TRUE : FALSE;
|
|
deviceExtension->DbgNumReallocations++;
|
|
KeQueryTickCount(&deviceExtension->DbgLastReallocTime);
|
|
}
|
|
|
|
ReleasePassiveLevelLock(deviceExtension);
|
|
}
|
|
|
|
/*
|
|
* Complete the irp
|
|
* Note: Always mark the irp pending (not only if irp->PendingReturned is true),
|
|
* because even if the lower stack completed the irp on the same thread,
|
|
* we are causing it to complete on a different thread at this layer due to the workItem.
|
|
*/
|
|
IoMarkIrpPending(readCapacityIrp);
|
|
IoCompleteRequest(readCapacityIrp, IO_NO_INCREMENT);
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dispatch for SCSI
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Supplies the device object.
|
|
Irp - Supplies the I/O request packet.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
NTSTATUS
|
|
CrcScsi(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
PSCSI_REQUEST_BLOCK pSRB;
|
|
PCDB pCDB;
|
|
|
|
pSRB = irpStack->Parameters.Scsi.Srb;
|
|
pCDB = (PCDB)pSRB->Cdb;
|
|
|
|
if ( pSRB->Function == SRB_FUNCTION_EXECUTE_SCSI &&
|
|
pCDB->CDB10.OperationCode == SCSIOP_READ )
|
|
{
|
|
status = CrcScsiRead( DeviceObject, Irp );
|
|
}
|
|
else if ( pSRB->Function == SRB_FUNCTION_EXECUTE_SCSI &&
|
|
pCDB->CDB10.OperationCode == SCSIOP_WRITE )
|
|
{
|
|
status = CrcScsiWrite( DeviceObject, Irp );
|
|
}
|
|
else /*if ( (deviceExtension->ulSectorSize == 0 ||
|
|
deviceExtension->ulNumSectors ==0 ) &&
|
|
pSRB->Function == SRB_FUNCTION_EXECUTE_SCSI &&
|
|
pCDB->CDB10.OperationCode == SCSIOP_READ_CAPACITY )*/
|
|
|
|
if( pSRB->Function == SRB_FUNCTION_EXECUTE_SCSI &&
|
|
pCDB->CDB10.OperationCode == SCSIOP_READ_CAPACITY )
|
|
{
|
|
//
|
|
// let's setup the disk property info. (only do this once.)
|
|
//
|
|
status = DataVerFilter_ForwardIrpAsyn( DeviceObject,
|
|
Irp,
|
|
CrcScsiReadCapacityCompletion,
|
|
deviceExtension );
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// pass them done directly
|
|
//
|
|
status = DataVerFilter_ForwardIrpAsyn( DeviceObject, Irp, NULL, NULL );
|
|
}
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Free all the allocated resources, etc.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - pointer to a driver object.
|
|
|
|
Return Value:
|
|
|
|
VOID.
|
|
|
|
--*/
|
|
VOID
|
|
DataVerFilter_Unload(
|
|
IN PDRIVER_OBJECT DriverObject
|
|
)
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
ULONG ulIndex = 0;
|
|
USHORT usDepth = 0;
|
|
|
|
PAGED_CODE();
|
|
|
|
#if DBG_WMI_TRACING
|
|
//
|
|
// WPP_CLEANUP can only occur after all KdPrintEx routines
|
|
// since it deallocates any resources used by that and also
|
|
// deregisters from WMI...
|
|
//
|
|
|
|
WPP_CLEANUP(DriverObject);
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
* DoCriticalRecovery
|
|
*
|
|
* Reinitialize all checksums after a critical failure where we could not allocate a workItem entry
|
|
* to defer-write a checksum.
|
|
*
|
|
* Must be called at PASSIVE IRQL with SyncEvent HELD but SPINLOCK NOT HELD.
|
|
*/
|
|
VOID DoCriticalRecovery(PDEVICE_EXTENSION DeviceExtension)
|
|
{
|
|
NTSTATUS status;
|
|
|
|
ASSERT(DeviceExtension->NeedCriticalRecovery);
|
|
ASSERT(DeviceExtension->State != DEVSTATE_REMOVED);
|
|
|
|
DBGWARN(("> Critical recovery (devObj=%ph) ...", DeviceExtension->DeviceObject));
|
|
|
|
if (DeviceExtension->CRCMdlLists.mdlItemsAllocated){
|
|
FreeAllPages(DeviceExtension);
|
|
}
|
|
|
|
/*
|
|
* We are recovering because we could not record a checksum.
|
|
* This means previous checksums may be invalid. So drop them all.
|
|
*/
|
|
while (TRUE){
|
|
PDEFERRED_CHECKSUM_ENTRY defCheckEntry;
|
|
KIRQL oldIrql;
|
|
|
|
KeAcquireSpinLock(&DeviceExtension->SpinLock, &oldIrql);
|
|
if (IsListEmpty(&DeviceExtension->DeferredCheckSumList)){
|
|
defCheckEntry = NULL;
|
|
}
|
|
else {
|
|
PLIST_ENTRY listEntry = RemoveHeadList(&DeviceExtension->DeferredCheckSumList);
|
|
InitializeListHead(listEntry);
|
|
defCheckEntry = CONTAINING_RECORD(listEntry, DEFERRED_CHECKSUM_ENTRY, ListEntry);
|
|
}
|
|
KeReleaseSpinLock(&DeviceExtension->SpinLock, oldIrql);
|
|
|
|
if (defCheckEntry){
|
|
FreeDeferredCheckSumEntry(DeviceExtension, defCheckEntry);
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
DeviceExtension->DbgNumCriticalRecoveries++;
|
|
KeQueryTickCount(&DeviceExtension->DbgLastRecoveryTime);
|
|
|
|
DeviceExtension->NeedCriticalRecovery = FALSE;
|
|
|
|
DBGWARN(("< Critical recovery complete (devObj=%ph)", DeviceExtension->DeviceObject));
|
|
|
|
}
|
|
|
|
|