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.
6771 lines
194 KiB
6771 lines
194 KiB
/*++
|
|
|
|
Copyright (C) Microsoft Corporation, 1991 - 1999
|
|
|
|
Module Name:
|
|
|
|
disk.c
|
|
|
|
Abstract:
|
|
|
|
SCSI disk class driver
|
|
|
|
Environment:
|
|
|
|
kernel mode only
|
|
|
|
Notes:
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "disk.h"
|
|
|
|
//
|
|
// Now instantiate the GUIDs
|
|
//
|
|
|
|
#include "initguid.h"
|
|
#include "ntddstor.h"
|
|
#include "ntddvol.h"
|
|
#include "ioevent.h"
|
|
|
|
NTSTATUS
|
|
DiskDetermineMediaTypes(
|
|
IN PDEVICE_OBJECT Fdo,
|
|
IN PIRP Irp,
|
|
IN UCHAR MediumType,
|
|
IN UCHAR DensityCode,
|
|
IN BOOLEAN MediaPresent,
|
|
IN BOOLEAN IsWritable
|
|
);
|
|
|
|
PPARTITION_INFORMATION_EX
|
|
DiskPdoFindPartitionEntry(
|
|
IN PPHYSICAL_DEVICE_EXTENSION Pdo,
|
|
IN PDRIVE_LAYOUT_INFORMATION_EX LayoutInfo
|
|
);
|
|
|
|
PPARTITION_INFORMATION_EX
|
|
DiskFindAdjacentPartition(
|
|
IN PDRIVE_LAYOUT_INFORMATION_EX LayoutInfo,
|
|
IN PPARTITION_INFORMATION_EX BasePartition
|
|
);
|
|
|
|
PPARTITION_INFORMATION_EX
|
|
DiskFindContainingPartition(
|
|
IN PDRIVE_LAYOUT_INFORMATION_EX LayoutInfo,
|
|
IN PPARTITION_INFORMATION_EX BasePartition,
|
|
IN BOOLEAN SearchTopToBottom
|
|
);
|
|
|
|
NTSTATUS
|
|
DiskIoctlCreateDisk(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
DiskIoctlGetDriveLayout(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
DiskIoctlGetDriveLayoutEx(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
DiskIoctlSetDriveLayout(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
DiskIoctlSetDriveLayoutEx(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
DiskIoctlGetPartitionInfo(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
DiskIoctlGetPartitionInfoEx(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
DiskIoctlGetLengthInfo(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
DiskIoctlSetPartitionInfo(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
DiskIoctlSetPartitionInfoEx(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
DiskIoctlSetPartitionInfoEx(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
DiskIoctlGetDriveGeometryEx(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
|
|
#pragma alloc_text(INIT, DriverEntry)
|
|
#pragma alloc_text(PAGE, DiskUnload)
|
|
#pragma alloc_text(PAGE, DiskCreateFdo)
|
|
#pragma alloc_text(PAGE, DiskDetermineMediaTypes)
|
|
#pragma alloc_text(PAGE, DiskShutdownFlush)
|
|
#pragma alloc_text(PAGE, DiskFlushDispatch)
|
|
#pragma alloc_text(PAGE, DiskModeSelect)
|
|
#pragma alloc_text(PAGE, DisableWriteCache)
|
|
#pragma alloc_text(PAGE, DiskIoctlVerify)
|
|
#pragma alloc_text(PAGE, DiskSetSpecialHacks)
|
|
#pragma alloc_text(PAGE, DiskQueryPnpCapabilities)
|
|
#pragma alloc_text(PAGE, DiskGetCacheInformation)
|
|
#pragma alloc_text(PAGE, DiskSetCacheInformation)
|
|
#pragma alloc_text(PAGE, DiskLogCacheInformation)
|
|
#pragma alloc_text(PAGE, DiskSetInfoExceptionInformation)
|
|
#pragma alloc_text(PAGE, DiskGetInfoExceptionInformation)
|
|
|
|
#pragma alloc_text(PAGE, DiskPdoFindPartitionEntry)
|
|
#pragma alloc_text(PAGE, DiskFindAdjacentPartition)
|
|
#pragma alloc_text(PAGE, DiskFindContainingPartition)
|
|
|
|
#pragma alloc_text(PAGE, DiskIoctlCreateDisk)
|
|
#pragma alloc_text(PAGE, DiskIoctlGetDriveLayout)
|
|
#pragma alloc_text(PAGE, DiskIoctlGetDriveLayoutEx)
|
|
#pragma alloc_text(PAGE, DiskIoctlSetDriveLayout)
|
|
#pragma alloc_text(PAGE, DiskIoctlSetDriveLayoutEx)
|
|
#pragma alloc_text(PAGE, DiskIoctlGetPartitionInfo)
|
|
#pragma alloc_text(PAGE, DiskIoctlGetPartitionInfoEx)
|
|
#pragma alloc_text(PAGE, DiskIoctlGetLengthInfo)
|
|
#pragma alloc_text(PAGE, DiskIoctlSetPartitionInfo)
|
|
#pragma alloc_text(PAGE, DiskIoctlSetPartitionInfoEx)
|
|
#pragma alloc_text(PAGE, DiskIoctlGetDriveGeometryEx)
|
|
#endif
|
|
|
|
extern ULONG DiskDisableGpt;
|
|
|
|
const GUID GUID_NULL = { 0 };
|
|
#define DiskCompareGuid(_First,_Second) \
|
|
(memcmp ((_First),(_Second), sizeof (GUID)))
|
|
|
|
//
|
|
// This macro is used to work around a bug in the definition of
|
|
// DISK_CACHE_RETENTION_PRIORITY. The value KeepReadData should be
|
|
// assigned 0xf rather than 0x2. Since the interface was already published
|
|
// when this was discovered the disk driver has been modified to translate
|
|
// between the interface value and the correct scsi value.
|
|
//
|
|
// 0x2 is turned into 0xf
|
|
// 0xf is turned into 0x2 - this ensures that future SCSI defintions can be
|
|
// accomodated.
|
|
//
|
|
|
|
#define TRANSLATE_RETENTION_PRIORITY(_x)\
|
|
((_x) == 0xf ? 0x2 : \
|
|
((_x) == 0x2 ? 0xf : _x) \
|
|
)
|
|
|
|
#define IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS_ADMIN CTL_CODE(IOCTL_VOLUME_BASE, 0, METHOD_BUFFERED, FILE_READ_ACCESS)
|
|
|
|
|
|
NTSTATUS
|
|
DriverEntry(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PUNICODE_STRING RegistryPath
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initializes the SCSI hard disk class driver.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - Pointer to driver object created by system.
|
|
|
|
RegistryPath - Pointer to the name of the services node for this driver.
|
|
|
|
Return Value:
|
|
|
|
The function value is the final status from the initialization operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
CLASS_INIT_DATA InitializationData = { 0 };
|
|
CLASS_QUERY_WMI_REGINFO_EX_LIST classQueryWmiRegInfoExList = { 0 };
|
|
GUID guidQueryRegInfoEx = GUID_CLASSPNP_QUERY_REGINFOEX;
|
|
|
|
NTSTATUS status;
|
|
|
|
#if defined(_X86_)
|
|
|
|
//
|
|
// Read the information NtDetect squirreled away about the disks in this
|
|
// system.
|
|
//
|
|
|
|
DiskSaveDetectInfo(DriverObject);
|
|
|
|
#endif
|
|
|
|
InitializationData.InitializationDataSize = sizeof(CLASS_INIT_DATA);
|
|
|
|
//
|
|
// Setup sizes and entry points for functional device objects
|
|
//
|
|
|
|
InitializationData.FdoData.DeviceExtensionSize = FUNCTIONAL_EXTENSION_SIZE;
|
|
InitializationData.FdoData.DeviceType = FILE_DEVICE_DISK;
|
|
InitializationData.FdoData.DeviceCharacteristics = FILE_DEVICE_SECURE_OPEN;
|
|
|
|
InitializationData.FdoData.ClassInitDevice = DiskInitFdo;
|
|
InitializationData.FdoData.ClassStartDevice = DiskStartFdo;
|
|
InitializationData.FdoData.ClassStopDevice = DiskStopDevice;
|
|
InitializationData.FdoData.ClassRemoveDevice = DiskRemoveDevice;
|
|
InitializationData.FdoData.ClassPowerDevice = ClassSpinDownPowerHandler;
|
|
|
|
InitializationData.FdoData.ClassError = DiskFdoProcessError;
|
|
InitializationData.FdoData.ClassReadWriteVerification = DiskReadWriteVerification;
|
|
InitializationData.FdoData.ClassDeviceControl = DiskDeviceControl;
|
|
InitializationData.FdoData.ClassShutdownFlush = DiskShutdownFlush;
|
|
InitializationData.FdoData.ClassCreateClose = NULL;
|
|
|
|
//
|
|
// Setup sizes and entry points for physical device objects
|
|
//
|
|
|
|
InitializationData.PdoData.DeviceExtensionSize = PHYSICAL_EXTENSION_SIZE;
|
|
InitializationData.PdoData.DeviceType = FILE_DEVICE_DISK;
|
|
InitializationData.PdoData.DeviceCharacteristics = FILE_DEVICE_SECURE_OPEN;
|
|
|
|
InitializationData.PdoData.ClassInitDevice = DiskInitPdo;
|
|
InitializationData.PdoData.ClassStartDevice = DiskStartPdo;
|
|
InitializationData.PdoData.ClassStopDevice = DiskStopDevice;
|
|
InitializationData.PdoData.ClassRemoveDevice = DiskRemoveDevice;
|
|
|
|
//
|
|
// Use default power routine for PDOs
|
|
//
|
|
|
|
InitializationData.PdoData.ClassPowerDevice = NULL;
|
|
|
|
InitializationData.PdoData.ClassError = NULL;
|
|
InitializationData.PdoData.ClassReadWriteVerification = DiskReadWriteVerification;
|
|
InitializationData.PdoData.ClassDeviceControl = DiskDeviceControl;
|
|
InitializationData.PdoData.ClassShutdownFlush = DiskShutdownFlush;
|
|
InitializationData.PdoData.ClassCreateClose = NULL;
|
|
InitializationData.PdoData.ClassQueryPnpCapabilities = DiskQueryPnpCapabilities;
|
|
|
|
InitializationData.ClassAddDevice = DiskAddDevice;
|
|
InitializationData.ClassEnumerateDevice = DiskEnumerateDevice;
|
|
InitializationData.ClassQueryId = DiskQueryId;
|
|
|
|
InitializationData.FdoData.ClassWmiInfo.GuidCount = 7;
|
|
InitializationData.FdoData.ClassWmiInfo.GuidRegInfo = DiskWmiFdoGuidList;
|
|
InitializationData.FdoData.ClassWmiInfo.ClassQueryWmiRegInfo = DiskFdoQueryWmiRegInfo;
|
|
InitializationData.FdoData.ClassWmiInfo.ClassQueryWmiDataBlock = DiskFdoQueryWmiDataBlock;
|
|
InitializationData.FdoData.ClassWmiInfo.ClassSetWmiDataBlock = DiskFdoSetWmiDataBlock;
|
|
InitializationData.FdoData.ClassWmiInfo.ClassSetWmiDataItem = DiskFdoSetWmiDataItem;
|
|
InitializationData.FdoData.ClassWmiInfo.ClassExecuteWmiMethod = DiskFdoExecuteWmiMethod;
|
|
InitializationData.FdoData.ClassWmiInfo.ClassWmiFunctionControl = DiskWmiFunctionControl;
|
|
|
|
|
|
#if 0
|
|
//
|
|
// Enable this to add WMI support for PDOs
|
|
InitializationData.PdoData.ClassWmiInfo.GuidCount = 1;
|
|
InitializationData.PdoData.ClassWmiInfo.GuidRegInfo = DiskWmiPdoGuidList;
|
|
InitializationData.PdoData.ClassWmiInfo.ClassQueryWmiRegInfo = DiskPdoQueryWmiRegInfo;
|
|
InitializationData.PdoData.ClassWmiInfo.ClassQueryWmiDataBlock = DiskPdoQueryWmiDataBlock;
|
|
InitializationData.PdoData.ClassWmiInfo.ClassSetWmiDataBlock = DiskPdoSetWmiDataBlock;
|
|
InitializationData.PdoData.ClassWmiInfo.ClassSetWmiDataItem = DiskPdoSetWmiDataItem;
|
|
InitializationData.PdoData.ClassWmiInfo.ClassExecuteWmiMethod = DiskPdoExecuteWmiMethod;
|
|
InitializationData.PdoData.ClassWmiInfo.ClassWmiFunctionControl = DiskWmiFunctionControl;
|
|
#endif
|
|
|
|
InitializationData.ClassUnload = DiskUnload;
|
|
|
|
//
|
|
// Initialize regregistration data structures
|
|
//
|
|
|
|
DiskInitializeReregistration();
|
|
|
|
//
|
|
// Call the class init routine
|
|
//
|
|
|
|
status = ClassInitialize(DriverObject, RegistryPath, &InitializationData);
|
|
|
|
#if defined(_X86_)
|
|
|
|
if(NT_SUCCESS(status)) {
|
|
|
|
IoRegisterBootDriverReinitialization(DriverObject,
|
|
DiskDriverReinitialization,
|
|
NULL);
|
|
}
|
|
|
|
#endif
|
|
|
|
//
|
|
// Call class init Ex routine to register a
|
|
// PCLASS_QUERY_WMI_REGINFO_EX routine
|
|
//
|
|
|
|
classQueryWmiRegInfoExList.Size = sizeof(CLASS_QUERY_WMI_REGINFO_EX_LIST);
|
|
classQueryWmiRegInfoExList.ClassFdoQueryWmiRegInfoEx = DiskFdoQueryWmiRegInfoEx;
|
|
|
|
ClassInitializeEx(DriverObject,
|
|
&guidQueryRegInfoEx,
|
|
&classQueryWmiRegInfoExList);
|
|
|
|
return status;
|
|
|
|
} // end DriverEntry()
|
|
|
|
|
|
VOID
|
|
DiskUnload(
|
|
IN PDRIVER_OBJECT DriverObject
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
#if defined(_X86_)
|
|
DiskCleanupDetectInfo(DriverObject);
|
|
#endif
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
DiskCreateFdo(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PDEVICE_OBJECT PhysicalDeviceObject,
|
|
IN PULONG DeviceCount,
|
|
IN BOOLEAN DasdAccessOnly
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine creates an object for the functional device
|
|
|
|
Arguments:
|
|
|
|
DriverObject - Pointer to driver object created by system.
|
|
|
|
PhysicalDeviceObject - Lower level driver we should attach to
|
|
|
|
DeviceCount - Number of previously installed devices.
|
|
|
|
DasdAccessOnly - indicates whether or not a file system is allowed to mount
|
|
on this device object. Used to avoid double-mounting of
|
|
file systems on super-floppies (which can unfortunately be
|
|
fixed disks). If set the i/o system will only allow rawfs
|
|
to be mounted.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PUCHAR deviceName = NULL;
|
|
HANDLE handle = NULL;
|
|
PDEVICE_OBJECT lowerDevice = NULL;
|
|
PDEVICE_OBJECT deviceObject = NULL;
|
|
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
*DeviceCount = 0;
|
|
|
|
//
|
|
// Set up an object directory to contain the objects for this
|
|
// device and all its partitions.
|
|
//
|
|
|
|
do {
|
|
|
|
WCHAR dirBuffer[64] = { 0 };
|
|
UNICODE_STRING dirName;
|
|
OBJECT_ATTRIBUTES objectAttribs;
|
|
|
|
_snwprintf(dirBuffer, sizeof(dirBuffer) / sizeof(dirBuffer[0]) - 1, L"\\Device\\Harddisk%d", *DeviceCount);
|
|
|
|
RtlInitUnicodeString(&dirName, dirBuffer);
|
|
|
|
InitializeObjectAttributes(&objectAttribs,
|
|
&dirName,
|
|
OBJ_CASE_INSENSITIVE | OBJ_PERMANENT,
|
|
NULL,
|
|
NULL);
|
|
|
|
status = ZwCreateDirectoryObject(&handle,
|
|
DIRECTORY_ALL_ACCESS,
|
|
&objectAttribs);
|
|
|
|
(*DeviceCount)++;
|
|
|
|
} while((status == STATUS_OBJECT_NAME_COLLISION) ||
|
|
(status == STATUS_OBJECT_NAME_EXISTS));
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
DebugPrint((1, "DiskCreateFdo: Could not create directory - %lx\n",
|
|
status));
|
|
|
|
return(status);
|
|
}
|
|
|
|
//
|
|
// When this loop exits the count is inflated by one - fix that.
|
|
//
|
|
|
|
(*DeviceCount)--;
|
|
|
|
//
|
|
// Claim the device.
|
|
//
|
|
|
|
lowerDevice = IoGetAttachedDeviceReference(PhysicalDeviceObject);
|
|
|
|
status = ClassClaimDevice(lowerDevice, FALSE);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
ZwMakeTemporaryObject(handle);
|
|
ZwClose(handle);
|
|
ObDereferenceObject(lowerDevice);
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Create a device object for this device. Each physical disk will
|
|
// have at least one device object. The required device object
|
|
// describes the entire device. Its directory path is
|
|
// \Device\HarddiskN\Partition0, where N = device number.
|
|
//
|
|
|
|
status = DiskGenerateDeviceName(TRUE,
|
|
*DeviceCount,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
&deviceName);
|
|
|
|
if(!NT_SUCCESS(status)) {
|
|
DebugPrint((1, "DiskCreateFdo - couldn't create name %lx\n",
|
|
status));
|
|
|
|
goto DiskCreateFdoExit;
|
|
|
|
}
|
|
|
|
status = ClassCreateDeviceObject(DriverObject,
|
|
deviceName,
|
|
PhysicalDeviceObject,
|
|
TRUE,
|
|
&deviceObject);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
DebugPrint((1,
|
|
"DiskCreateFdo: Can not create device object %s\n",
|
|
deviceName));
|
|
|
|
goto DiskCreateFdoExit;
|
|
}
|
|
|
|
//
|
|
// Indicate that IRPs should include MDLs for data transfers.
|
|
//
|
|
|
|
SET_FLAG(deviceObject->Flags, DO_DIRECT_IO);
|
|
|
|
fdoExtension = deviceObject->DeviceExtension;
|
|
|
|
if(DasdAccessOnly) {
|
|
|
|
//
|
|
// Inidicate that only RAW should be allowed to mount on the root
|
|
// partition object. This ensures that a file system can't doubly
|
|
// mount on a super-floppy by mounting once on P0 and once on P1.
|
|
//
|
|
|
|
SET_FLAG(deviceObject->Vpb->Flags, VPB_RAW_MOUNT);
|
|
}
|
|
|
|
//
|
|
// Initialize lock count to zero. The lock count is used to
|
|
// disable the ejection mechanism on devices that support
|
|
// removable media. Only the lock count in the physical
|
|
// device extension is used.
|
|
//
|
|
|
|
fdoExtension->LockCount = 0;
|
|
|
|
//
|
|
// Save system disk number.
|
|
//
|
|
|
|
fdoExtension->DeviceNumber = *DeviceCount;
|
|
|
|
//
|
|
// Set the alignment requirements for the device based on the
|
|
// host adapter requirements
|
|
//
|
|
|
|
if (lowerDevice->AlignmentRequirement > deviceObject->AlignmentRequirement) {
|
|
deviceObject->AlignmentRequirement = lowerDevice->AlignmentRequirement;
|
|
}
|
|
|
|
//
|
|
// Finally, attach to the pdo
|
|
//
|
|
|
|
fdoExtension->LowerPdo = PhysicalDeviceObject;
|
|
|
|
fdoExtension->CommonExtension.LowerDeviceObject =
|
|
IoAttachDeviceToDeviceStack(
|
|
deviceObject,
|
|
PhysicalDeviceObject);
|
|
|
|
if(fdoExtension->CommonExtension.LowerDeviceObject == NULL) {
|
|
|
|
//
|
|
// Uh - oh, we couldn't attach
|
|
// cleanup and return
|
|
//
|
|
|
|
status = STATUS_UNSUCCESSFUL;
|
|
goto DiskCreateFdoExit;
|
|
}
|
|
|
|
{
|
|
PDISK_DATA diskData = fdoExtension->CommonExtension.DriverData;
|
|
|
|
//
|
|
// Initialize the partitioning lock as it may be used in the remove
|
|
// code.
|
|
//
|
|
|
|
KeInitializeEvent(&(diskData->PartitioningEvent),
|
|
SynchronizationEvent,
|
|
TRUE);
|
|
}
|
|
|
|
|
|
//
|
|
// Clear the init flag.
|
|
//
|
|
|
|
CLEAR_FLAG(deviceObject->Flags, DO_DEVICE_INITIALIZING);
|
|
|
|
//
|
|
// Store a handle to the device object directory for this disk
|
|
//
|
|
|
|
fdoExtension->DeviceDirectory = handle;
|
|
|
|
ObDereferenceObject(lowerDevice);
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
DiskCreateFdoExit:
|
|
|
|
if (deviceObject != NULL)
|
|
{
|
|
IoDeleteDevice(deviceObject);
|
|
}
|
|
|
|
if (deviceName != NULL)
|
|
{
|
|
ExFreePool(deviceName);
|
|
}
|
|
|
|
ObDereferenceObject(lowerDevice);
|
|
|
|
ZwMakeTemporaryObject(handle);
|
|
ZwClose(handle);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
DiskReadWriteVerification(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
I/O System entry for read and write requests to SCSI disks.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to driver object created by system.
|
|
Irp - IRP involved.
|
|
|
|
Return Value:
|
|
|
|
NT Status
|
|
|
|
--*/
|
|
|
|
{
|
|
PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
|
|
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
ULONG residualBytes;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Make sure that the request is within the bounds of the partition and
|
|
// that the number of bytes to transfer is a multiple of the sector size
|
|
//
|
|
|
|
residualBytes = irpSp->Parameters.Read.Length & (commonExtension->PartitionZeroExtension->DiskGeometry.BytesPerSector - 1);
|
|
|
|
if ((irpSp->Parameters.Read.ByteOffset.QuadPart > commonExtension->PartitionLength.QuadPart) ||
|
|
(irpSp->Parameters.Read.ByteOffset.QuadPart < 0) ||
|
|
(residualBytes != 0))
|
|
{
|
|
status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
ULONGLONG bytesRemaining = commonExtension->PartitionLength.QuadPart - irpSp->Parameters.Read.ByteOffset.QuadPart;
|
|
|
|
if ((ULONGLONG)irpSp->Parameters.Read.Length > bytesRemaining)
|
|
{
|
|
status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
//
|
|
// This error may be caused by the fact that the drive is not ready.
|
|
//
|
|
|
|
status = ((PDISK_DATA) commonExtension->DriverData)->ReadyStatus;
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Flag this as a user errror so that a popup is generated.
|
|
//
|
|
|
|
DebugPrint((1, "DiskReadWriteVerification: ReadyStatus is %lx\n",
|
|
status));
|
|
|
|
IoSetHardErrorOrVerifyDevice(Irp, DeviceObject);
|
|
|
|
//
|
|
// status will keep the current error
|
|
//
|
|
|
|
ASSERT( status != STATUS_INSUFFICIENT_RESOURCES );
|
|
|
|
} else if((commonExtension->IsFdo == TRUE) && (residualBytes == 0)) {
|
|
|
|
//
|
|
// This failed because we think the physical disk is too small.
|
|
// Send it down to the drive and let the hardware decide for
|
|
// itself.
|
|
//
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Note fastfat depends on this parameter to determine when to
|
|
// remount due to a sector size change.
|
|
//
|
|
|
|
status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
Irp->IoStatus.Status = status;
|
|
|
|
return status;
|
|
|
|
} // end DiskReadWrite()
|
|
|
|
|
|
NTSTATUS
|
|
DiskDetermineMediaTypes(
|
|
IN PDEVICE_OBJECT Fdo,
|
|
IN PIRP Irp,
|
|
IN UCHAR MediumType,
|
|
IN UCHAR DensityCode,
|
|
IN BOOLEAN MediaPresent,
|
|
IN BOOLEAN IsWritable
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determines number of types based on the physical device, validates the user buffer
|
|
and builds the MEDIA_TYPE information.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to functional device object created by system.
|
|
Irp - IOCTL_STORAGE_GET_MEDIA_TYPES_EX Irp.
|
|
MediumType - byte returned in mode data header.
|
|
DensityCode - byte returned in mode data block descriptor.
|
|
NumberOfTypes - pointer to be updated based on actual device.
|
|
|
|
Return Value:
|
|
|
|
Status is returned.
|
|
|
|
--*/
|
|
|
|
{
|
|
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
|
|
PPHYSICAL_DEVICE_EXTENSION pdoExtension = Fdo->DeviceExtension;
|
|
PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension;
|
|
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
PGET_MEDIA_TYPES mediaTypes = Irp->AssociatedIrp.SystemBuffer;
|
|
PDEVICE_MEDIA_INFO mediaInfo = &mediaTypes->MediaInfo[0];
|
|
BOOLEAN deviceMatched = FALSE;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// this should be checked prior to calling into this routine
|
|
// as we use the buffer as mediaTypes
|
|
//
|
|
|
|
ASSERT(irpStack->Parameters.DeviceIoControl.OutputBufferLength >=
|
|
sizeof(GET_MEDIA_TYPES));
|
|
|
|
//
|
|
// Determine if this device is removable or fixed.
|
|
//
|
|
|
|
if (!TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)) {
|
|
|
|
//
|
|
// Fixed disk.
|
|
//
|
|
|
|
mediaTypes->DeviceType = FILE_DEVICE_DISK;
|
|
mediaTypes->MediaInfoCount = 1;
|
|
|
|
mediaInfo->DeviceSpecific.DiskInfo.Cylinders.QuadPart = fdoExtension->DiskGeometry.Cylinders.QuadPart;
|
|
mediaInfo->DeviceSpecific.DiskInfo.MediaType = FixedMedia;
|
|
mediaInfo->DeviceSpecific.DiskInfo.TracksPerCylinder = fdoExtension->DiskGeometry.TracksPerCylinder;
|
|
mediaInfo->DeviceSpecific.DiskInfo.SectorsPerTrack = fdoExtension->DiskGeometry.SectorsPerTrack;
|
|
mediaInfo->DeviceSpecific.DiskInfo.BytesPerSector = fdoExtension->DiskGeometry.BytesPerSector;
|
|
mediaInfo->DeviceSpecific.DiskInfo.NumberMediaSides = 1;
|
|
mediaInfo->DeviceSpecific.DiskInfo.MediaCharacteristics = (MEDIA_CURRENTLY_MOUNTED | MEDIA_READ_WRITE);
|
|
|
|
if (!IsWritable) {
|
|
|
|
SET_FLAG(mediaInfo->DeviceSpecific.DiskInfo.MediaCharacteristics,
|
|
MEDIA_WRITE_PROTECTED);
|
|
}
|
|
|
|
} else {
|
|
|
|
PUCHAR vendorId = (PUCHAR) fdoExtension->DeviceDescriptor + fdoExtension->DeviceDescriptor->VendorIdOffset;
|
|
PUCHAR productId = (PUCHAR) fdoExtension->DeviceDescriptor + fdoExtension->DeviceDescriptor->ProductIdOffset;
|
|
PUCHAR productRevision = (PUCHAR) fdoExtension->DeviceDescriptor + fdoExtension->DeviceDescriptor->ProductRevisionOffset;
|
|
DISK_MEDIA_TYPES_LIST const *mediaListEntry;
|
|
ULONG currentMedia;
|
|
ULONG i;
|
|
ULONG j;
|
|
ULONG sizeNeeded;
|
|
|
|
DebugPrint((1,
|
|
"DiskDetermineMediaTypes: Vendor %s, Product %s\n",
|
|
vendorId,
|
|
productId));
|
|
|
|
//
|
|
// Run through the list until we find the entry with a NULL Vendor Id.
|
|
//
|
|
|
|
for (i = 0; DiskMediaTypes[i].VendorId != NULL; i++) {
|
|
|
|
mediaListEntry = &DiskMediaTypes[i];
|
|
|
|
if (strncmp(mediaListEntry->VendorId,vendorId,strlen(mediaListEntry->VendorId))) {
|
|
continue;
|
|
}
|
|
|
|
if ((mediaListEntry->ProductId != NULL) &&
|
|
strncmp(mediaListEntry->ProductId, productId, strlen(mediaListEntry->ProductId))) {
|
|
continue;
|
|
}
|
|
|
|
if ((mediaListEntry->Revision != NULL) &&
|
|
strncmp(mediaListEntry->Revision, productRevision, strlen(mediaListEntry->Revision))) {
|
|
continue;
|
|
}
|
|
|
|
deviceMatched = TRUE;
|
|
|
|
mediaTypes->DeviceType = FILE_DEVICE_DISK;
|
|
mediaTypes->MediaInfoCount = mediaListEntry->NumberOfTypes;
|
|
|
|
//
|
|
// Ensure that buffer is large enough.
|
|
//
|
|
|
|
sizeNeeded = FIELD_OFFSET(GET_MEDIA_TYPES, MediaInfo[0]) +
|
|
(mediaListEntry->NumberOfTypes *
|
|
sizeof(DEVICE_MEDIA_INFO)
|
|
);
|
|
|
|
if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeNeeded) {
|
|
|
|
//
|
|
// Buffer too small
|
|
//
|
|
|
|
Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
for (j = 0; j < mediaListEntry->NumberOfTypes; j++) {
|
|
|
|
mediaInfo->DeviceSpecific.RemovableDiskInfo.Cylinders.QuadPart = fdoExtension->DiskGeometry.Cylinders.QuadPart;
|
|
mediaInfo->DeviceSpecific.RemovableDiskInfo.TracksPerCylinder = fdoExtension->DiskGeometry.TracksPerCylinder;
|
|
mediaInfo->DeviceSpecific.RemovableDiskInfo.SectorsPerTrack = fdoExtension->DiskGeometry.SectorsPerTrack;
|
|
mediaInfo->DeviceSpecific.RemovableDiskInfo.BytesPerSector = fdoExtension->DiskGeometry.BytesPerSector;
|
|
mediaInfo->DeviceSpecific.RemovableDiskInfo.NumberMediaSides = mediaListEntry->NumberOfSides;
|
|
|
|
//
|
|
// Set the type.
|
|
//
|
|
|
|
mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaType = mediaListEntry->MediaTypes[j];
|
|
|
|
if (mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaType == MO_5_WO) {
|
|
mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics = MEDIA_WRITE_ONCE;
|
|
} else {
|
|
mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics = MEDIA_READ_WRITE;
|
|
}
|
|
|
|
//
|
|
// Status will either be success, if media is present, or no media.
|
|
// It would be optimal to base from density code and medium type, but not all devices
|
|
// have values for these fields.
|
|
//
|
|
|
|
if (MediaPresent) {
|
|
|
|
//
|
|
// The usage of MediumType and DensityCode is device specific, so this may need
|
|
// to be extended to further key off of product/vendor ids.
|
|
// Currently, the MO units are the only devices that return this information.
|
|
//
|
|
|
|
if (MediumType == 2) {
|
|
currentMedia = MO_5_WO;
|
|
} else if (MediumType == 3) {
|
|
currentMedia = MO_5_RW;
|
|
|
|
if (DensityCode == 0x87) {
|
|
|
|
//
|
|
// Indicate that the pinnacle 4.6 G media
|
|
// is present. Other density codes will default to normal
|
|
// RW MO media.
|
|
//
|
|
|
|
currentMedia = PINNACLE_APEX_5_RW;
|
|
}
|
|
} else {
|
|
currentMedia = 0;
|
|
}
|
|
|
|
if (currentMedia) {
|
|
if (mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaType == (STORAGE_MEDIA_TYPE)currentMedia) {
|
|
SET_FLAG(mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics, MEDIA_CURRENTLY_MOUNTED);
|
|
}
|
|
|
|
} else {
|
|
SET_FLAG(mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics, MEDIA_CURRENTLY_MOUNTED);
|
|
}
|
|
}
|
|
|
|
if (!IsWritable) {
|
|
SET_FLAG(mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics, MEDIA_WRITE_PROTECTED);
|
|
}
|
|
|
|
//
|
|
// Advance to next entry.
|
|
//
|
|
|
|
mediaInfo++;
|
|
}
|
|
}
|
|
|
|
if (!deviceMatched) {
|
|
|
|
DebugPrint((1,
|
|
"DiskDetermineMediaTypes: Unknown device. Vendor: %s Product: %s Revision: %s\n",
|
|
vendorId,
|
|
productId,
|
|
productRevision));
|
|
//
|
|
// Build an entry for unknown.
|
|
//
|
|
|
|
mediaTypes->DeviceType = FILE_DEVICE_DISK;
|
|
mediaTypes->MediaInfoCount = 1;
|
|
|
|
mediaInfo->DeviceSpecific.RemovableDiskInfo.Cylinders.QuadPart = fdoExtension->DiskGeometry.Cylinders.QuadPart;
|
|
mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaType = RemovableMedia;
|
|
mediaInfo->DeviceSpecific.RemovableDiskInfo.TracksPerCylinder = fdoExtension->DiskGeometry.TracksPerCylinder;
|
|
mediaInfo->DeviceSpecific.RemovableDiskInfo.SectorsPerTrack = fdoExtension->DiskGeometry.SectorsPerTrack;
|
|
mediaInfo->DeviceSpecific.RemovableDiskInfo.BytesPerSector = fdoExtension->DiskGeometry.BytesPerSector;
|
|
mediaInfo->DeviceSpecific.RemovableDiskInfo.NumberMediaSides = 1;
|
|
mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics = MEDIA_READ_WRITE;
|
|
|
|
if (MediaPresent) {
|
|
|
|
SET_FLAG(mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics, MEDIA_CURRENTLY_MOUNTED);
|
|
}
|
|
|
|
if (!IsWritable) {
|
|
|
|
SET_FLAG(mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics, MEDIA_WRITE_PROTECTED);
|
|
}
|
|
}
|
|
}
|
|
|
|
Irp->IoStatus.Information =
|
|
FIELD_OFFSET(GET_MEDIA_TYPES, MediaInfo[0]) +
|
|
(mediaTypes->MediaInfoCount * sizeof(DEVICE_MEDIA_INFO));
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
DiskDeviceControl(
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
I/O system entry for device controls to SCSI disks.
|
|
|
|
Arguments:
|
|
|
|
Fdo - Pointer to functional device object created by system.
|
|
Irp - IRP involved.
|
|
|
|
Return Value:
|
|
|
|
Status is returned.
|
|
|
|
--*/
|
|
|
|
#define SendToFdo(Dev, Irp, Rval) { \
|
|
PCOMMON_DEVICE_EXTENSION ce = Dev->DeviceExtension; \
|
|
ASSERT_PDO(Dev); \
|
|
IoCopyCurrentIrpStackLocationToNext(Irp); \
|
|
Rval = IoCallDriver(ce->LowerDeviceObject, Irp); \
|
|
}
|
|
|
|
{
|
|
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
|
|
PPHYSICAL_DEVICE_EXTENSION pdoExtension = DeviceObject->DeviceExtension;
|
|
PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
|
|
|
|
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
|
|
PSCSI_REQUEST_BLOCK srb;
|
|
PCDB cdb;
|
|
PMODE_PARAMETER_HEADER modeData;
|
|
PIRP irp2;
|
|
ULONG length;
|
|
NTSTATUS status;
|
|
KEVENT event;
|
|
IO_STATUS_BLOCK ioStatus = { 0 };
|
|
|
|
BOOLEAN b = FALSE;
|
|
|
|
srb = ExAllocatePoolWithTag(NonPagedPool,
|
|
SCSI_REQUEST_BLOCK_SIZE,
|
|
DISK_TAG_SRB);
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
if (srb == NULL) {
|
|
|
|
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
ClassReleaseRemoveLock(DeviceObject, Irp);
|
|
ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
RtlZeroMemory(srb, SCSI_REQUEST_BLOCK_SIZE);
|
|
|
|
cdb = (PCDB)srb->Cdb;
|
|
|
|
switch (irpStack->Parameters.DeviceIoControl.IoControlCode) {
|
|
|
|
case IOCTL_DISK_GET_CACHE_INFORMATION:
|
|
b = TRUE;
|
|
case IOCTL_DISK_SET_CACHE_INFORMATION: {
|
|
|
|
BOOLEAN getCaching = b;
|
|
PDISK_CACHE_INFORMATION cacheInfo = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
if(!commonExtension->IsFdo) {
|
|
|
|
ClassReleaseRemoveLock(DeviceObject, Irp);
|
|
ExFreePool(srb);
|
|
SendToFdo(DeviceObject, Irp, status);
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Validate the request.
|
|
//
|
|
|
|
if((getCaching) &&
|
|
(irpStack->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(DISK_CACHE_INFORMATION))
|
|
) {
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
if ((!getCaching) &&
|
|
(irpStack->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(DISK_CACHE_INFORMATION))
|
|
) {
|
|
|
|
status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
ASSERT(Irp->AssociatedIrp.SystemBuffer != NULL);
|
|
|
|
if (getCaching) {
|
|
|
|
status = DiskGetCacheInformation(fdoExtension, cacheInfo);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
Irp->IoStatus.Information = sizeof(DISK_CACHE_INFORMATION);
|
|
}
|
|
|
|
} else {
|
|
|
|
status = DiskSetCacheInformation(fdoExtension, cacheInfo);
|
|
|
|
//
|
|
// Save away the user-defined override in our extension and the registry
|
|
//
|
|
if (cacheInfo->WriteCacheEnabled)
|
|
{
|
|
diskData->WriteCacheOverride = DiskWriteCacheEnable;
|
|
}
|
|
else
|
|
{
|
|
diskData->WriteCacheOverride = DiskWriteCacheDisable;
|
|
}
|
|
|
|
ClassSetDeviceParameter(fdoExtension, DiskDeviceParameterSubkey, DiskDeviceUserWriteCacheSetting, diskData->WriteCacheOverride);
|
|
|
|
DiskLogCacheInformation(fdoExtension, cacheInfo, status);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case IOCTL_DISK_GET_CACHE_SETTING: {
|
|
|
|
if (!commonExtension->IsFdo) {
|
|
|
|
ClassReleaseRemoveLock(DeviceObject, Irp);
|
|
ExFreePool(srb);
|
|
SendToFdo(DeviceObject, Irp, status);
|
|
return status;
|
|
}
|
|
|
|
status = DiskIoctlGetCacheSetting(fdoExtension, Irp);
|
|
break;
|
|
}
|
|
|
|
case IOCTL_DISK_SET_CACHE_SETTING: {
|
|
|
|
if (!commonExtension->IsFdo)
|
|
{
|
|
ClassReleaseRemoveLock(DeviceObject, Irp);
|
|
ExFreePool(srb);
|
|
SendToFdo(DeviceObject, Irp, status);
|
|
return status;
|
|
}
|
|
|
|
status = DiskIoctlSetCacheSetting(fdoExtension, Irp);
|
|
break;
|
|
}
|
|
|
|
case SMART_GET_VERSION: {
|
|
|
|
PUCHAR buffer;
|
|
PSRB_IO_CONTROL srbControl;
|
|
PGETVERSIONINPARAMS versionParams;
|
|
|
|
if(!commonExtension->IsFdo) {
|
|
ClassReleaseRemoveLock(DeviceObject, Irp);
|
|
ExFreePool(srb);
|
|
SendToFdo(DeviceObject, Irp, status);
|
|
return status;
|
|
}
|
|
|
|
if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(GETVERSIONINPARAMS)) {
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
srbControl = ExAllocatePoolWithTag(NonPagedPool,
|
|
sizeof(SRB_IO_CONTROL) +
|
|
sizeof(GETVERSIONINPARAMS),
|
|
DISK_TAG_SMART);
|
|
|
|
if (!srbControl) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
break;
|
|
}
|
|
|
|
RtlZeroMemory(srbControl,
|
|
sizeof(SRB_IO_CONTROL) + sizeof(GETVERSIONINPARAMS)
|
|
);
|
|
|
|
//
|
|
// fill in srbControl fields
|
|
//
|
|
|
|
srbControl->HeaderLength = sizeof(SRB_IO_CONTROL);
|
|
RtlMoveMemory (srbControl->Signature, "SCSIDISK", 8);
|
|
srbControl->Timeout = fdoExtension->TimeOutValue;
|
|
srbControl->Length = sizeof(GETVERSIONINPARAMS);
|
|
srbControl->ControlCode = IOCTL_SCSI_MINIPORT_SMART_VERSION;
|
|
|
|
//
|
|
// Point to the 'buffer' portion of the SRB_CONTROL
|
|
//
|
|
|
|
buffer = (PUCHAR)srbControl;
|
|
(ULONG_PTR)buffer += srbControl->HeaderLength;
|
|
|
|
//
|
|
// Ensure correct target is set in the cmd parameters.
|
|
//
|
|
|
|
versionParams = (PGETVERSIONINPARAMS)buffer;
|
|
versionParams->bIDEDeviceMap = diskData->ScsiAddress.TargetId;
|
|
|
|
ClassSendDeviceIoControlSynchronous(
|
|
IOCTL_SCSI_MINIPORT,
|
|
commonExtension->LowerDeviceObject,
|
|
srbControl,
|
|
sizeof(SRB_IO_CONTROL) + sizeof(GETVERSIONINPARAMS),
|
|
sizeof(SRB_IO_CONTROL) + sizeof(GETVERSIONINPARAMS),
|
|
FALSE,
|
|
&ioStatus);
|
|
|
|
status = ioStatus.Status;
|
|
|
|
//
|
|
// If successful, copy the data received into the output buffer.
|
|
// This should only fail in the event that the IDE driver is older
|
|
// than this driver.
|
|
//
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
buffer = (PUCHAR)srbControl;
|
|
(ULONG_PTR)buffer += srbControl->HeaderLength;
|
|
|
|
RtlMoveMemory (Irp->AssociatedIrp.SystemBuffer, buffer,
|
|
sizeof(GETVERSIONINPARAMS));
|
|
Irp->IoStatus.Information = sizeof(GETVERSIONINPARAMS);
|
|
}
|
|
|
|
ExFreePool(srbControl);
|
|
break;
|
|
}
|
|
|
|
case SMART_RCV_DRIVE_DATA: {
|
|
|
|
PSENDCMDINPARAMS cmdInParameters = ((PSENDCMDINPARAMS)Irp->AssociatedIrp.SystemBuffer);
|
|
ULONG controlCode = 0;
|
|
PSRB_IO_CONTROL srbControl;
|
|
PUCHAR buffer;
|
|
|
|
if(!commonExtension->IsFdo) {
|
|
ClassReleaseRemoveLock(DeviceObject, Irp);
|
|
ExFreePool(srb);
|
|
SendToFdo(DeviceObject, Irp, status);
|
|
return status;
|
|
}
|
|
|
|
if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
|
|
(sizeof(SENDCMDINPARAMS) - 1)) {
|
|
status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
|
|
} else if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
|
|
(sizeof(SENDCMDOUTPARAMS) + 512 - 1)) {
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Create notification event object to be used to signal the
|
|
// request completion.
|
|
//
|
|
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
|
|
//
|
|
// use controlCode as a sort of 'STATUS_SUCCESS' to see if it's
|
|
// a valid request type
|
|
//
|
|
|
|
if (cmdInParameters->irDriveRegs.bCommandReg == ID_CMD) {
|
|
|
|
length = IDENTIFY_BUFFER_SIZE + sizeof(SENDCMDOUTPARAMS);
|
|
controlCode = IOCTL_SCSI_MINIPORT_IDENTIFY;
|
|
|
|
} else if (cmdInParameters->irDriveRegs.bCommandReg == SMART_CMD) {
|
|
|
|
switch (cmdInParameters->irDriveRegs.bFeaturesReg) {
|
|
|
|
case READ_ATTRIBUTES:
|
|
controlCode = IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS;
|
|
length = READ_ATTRIBUTE_BUFFER_SIZE + sizeof(SENDCMDOUTPARAMS);
|
|
break;
|
|
|
|
case READ_THRESHOLDS:
|
|
controlCode = IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS;
|
|
length = READ_THRESHOLD_BUFFER_SIZE + sizeof(SENDCMDOUTPARAMS);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (controlCode == 0) {
|
|
|
|
status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
|
|
srbControl = ExAllocatePoolWithTag(NonPagedPool,
|
|
sizeof(SRB_IO_CONTROL) + length,
|
|
DISK_TAG_SMART);
|
|
|
|
if (!srbControl) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// fill in srbControl fields
|
|
//
|
|
|
|
srbControl->HeaderLength = sizeof(SRB_IO_CONTROL);
|
|
RtlMoveMemory (srbControl->Signature, "SCSIDISK", 8);
|
|
srbControl->Timeout = fdoExtension->TimeOutValue;
|
|
srbControl->Length = length;
|
|
srbControl->ControlCode = controlCode;
|
|
|
|
//
|
|
// Point to the 'buffer' portion of the SRB_CONTROL
|
|
//
|
|
|
|
buffer = (PUCHAR)srbControl;
|
|
(ULONG_PTR)buffer += srbControl->HeaderLength;
|
|
|
|
//
|
|
// Ensure correct target is set in the cmd parameters.
|
|
//
|
|
|
|
cmdInParameters->bDriveNumber = diskData->ScsiAddress.TargetId;
|
|
|
|
//
|
|
// Copy the IOCTL parameters to the srb control buffer area.
|
|
//
|
|
|
|
RtlMoveMemory(buffer,
|
|
Irp->AssociatedIrp.SystemBuffer,
|
|
sizeof(SENDCMDINPARAMS) - 1);
|
|
|
|
irp2 = IoBuildDeviceIoControlRequest(IOCTL_SCSI_MINIPORT,
|
|
commonExtension->LowerDeviceObject,
|
|
srbControl,
|
|
sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS) - 1,
|
|
srbControl,
|
|
sizeof(SRB_IO_CONTROL) + length,
|
|
FALSE,
|
|
&event,
|
|
&ioStatus);
|
|
|
|
if (irp2 == NULL) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
ExFreePool(srbControl);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Call the port driver with the request and wait for it to complete.
|
|
//
|
|
|
|
status = IoCallDriver(commonExtension->LowerDeviceObject, irp2);
|
|
|
|
if (status == STATUS_PENDING) {
|
|
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
|
status = ioStatus.Status;
|
|
}
|
|
|
|
//
|
|
// Copy the data received into the output buffer. Since the status buffer
|
|
// contains error information also, always perform this copy. IO will will
|
|
// either pass this back to the app, or zero it, in case of error.
|
|
//
|
|
|
|
buffer = (PUCHAR)srbControl;
|
|
(ULONG_PTR)buffer += srbControl->HeaderLength;
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
RtlMoveMemory ( Irp->AssociatedIrp.SystemBuffer, buffer, length - 1);
|
|
Irp->IoStatus.Information = length - 1;
|
|
|
|
} else {
|
|
|
|
RtlMoveMemory ( Irp->AssociatedIrp.SystemBuffer, buffer, (sizeof(SENDCMDOUTPARAMS) - 1));
|
|
Irp->IoStatus.Information = sizeof(SENDCMDOUTPARAMS) - 1;
|
|
|
|
}
|
|
|
|
ExFreePool(srbControl);
|
|
break;
|
|
|
|
}
|
|
|
|
case SMART_SEND_DRIVE_COMMAND: {
|
|
|
|
PSENDCMDINPARAMS cmdInParameters = ((PSENDCMDINPARAMS)Irp->AssociatedIrp.SystemBuffer);
|
|
PSRB_IO_CONTROL srbControl;
|
|
ULONG controlCode = 0;
|
|
PUCHAR buffer;
|
|
|
|
if(!commonExtension->IsFdo) {
|
|
ClassReleaseRemoveLock(DeviceObject, Irp);
|
|
ExFreePool(srb);
|
|
SendToFdo(DeviceObject, Irp, status);
|
|
return status;
|
|
}
|
|
|
|
if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
|
|
(sizeof(SENDCMDINPARAMS) - 1)) {
|
|
status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
|
|
} else if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
|
|
(sizeof(SENDCMDOUTPARAMS) - 1)) {
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Create notification event object to be used to signal the
|
|
// request completion.
|
|
//
|
|
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
|
|
length = 0;
|
|
|
|
if (cmdInParameters->irDriveRegs.bCommandReg == SMART_CMD) {
|
|
|
|
switch (cmdInParameters->irDriveRegs.bFeaturesReg) {
|
|
|
|
case ENABLE_SMART:
|
|
controlCode = IOCTL_SCSI_MINIPORT_ENABLE_SMART;
|
|
break;
|
|
|
|
case DISABLE_SMART:
|
|
controlCode = IOCTL_SCSI_MINIPORT_DISABLE_SMART;
|
|
break;
|
|
|
|
case RETURN_SMART_STATUS:
|
|
|
|
//
|
|
// Ensure bBuffer is at least 2 bytes (to hold the values of
|
|
// cylinderLow and cylinderHigh).
|
|
//
|
|
|
|
if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
|
|
(sizeof(SENDCMDOUTPARAMS) - 1 + sizeof(IDEREGS))) {
|
|
|
|
/*
|
|
* Don't set controlCode; that'll cause us to break out below.
|
|
*/
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
controlCode = IOCTL_SCSI_MINIPORT_RETURN_STATUS;
|
|
length = sizeof(IDEREGS);
|
|
break;
|
|
|
|
case ENABLE_DISABLE_AUTOSAVE:
|
|
controlCode = IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE;
|
|
break;
|
|
|
|
case SAVE_ATTRIBUTE_VALUES:
|
|
controlCode = IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES;
|
|
break;
|
|
|
|
case EXECUTE_OFFLINE_DIAGS:
|
|
//
|
|
// Validate that this is an ok self test command
|
|
//
|
|
if (DiskIsValidSmartSelfTest(cmdInParameters->irDriveRegs.bSectorNumberReg))
|
|
{
|
|
controlCode = IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS;
|
|
}
|
|
break;
|
|
|
|
case ENABLE_DISABLE_AUTO_OFFLINE:
|
|
controlCode = IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTO_OFFLINE;
|
|
break;
|
|
|
|
default:
|
|
status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (controlCode == 0) {
|
|
break;
|
|
}
|
|
|
|
length += max(sizeof(SENDCMDOUTPARAMS), sizeof(SENDCMDINPARAMS));
|
|
srbControl = ExAllocatePoolWithTag(NonPagedPool,
|
|
sizeof(SRB_IO_CONTROL) + length,
|
|
DISK_TAG_SMART);
|
|
|
|
if (!srbControl) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// fill in srbControl fields
|
|
//
|
|
|
|
srbControl->HeaderLength = sizeof(SRB_IO_CONTROL);
|
|
RtlMoveMemory (srbControl->Signature, "SCSIDISK", 8);
|
|
srbControl->Timeout = fdoExtension->TimeOutValue;
|
|
srbControl->Length = length;
|
|
srbControl->ControlCode = controlCode;
|
|
|
|
//
|
|
// Point to the 'buffer' portion of the SRB_CONTROL
|
|
//
|
|
|
|
buffer = (PUCHAR)srbControl;
|
|
(ULONG_PTR)buffer += srbControl->HeaderLength;
|
|
|
|
//
|
|
// Ensure correct target is set in the cmd parameters.
|
|
//
|
|
|
|
cmdInParameters->bDriveNumber = diskData->ScsiAddress.TargetId;
|
|
|
|
//
|
|
// Copy the IOCTL parameters to the srb control buffer area.
|
|
//
|
|
|
|
RtlMoveMemory(buffer, Irp->AssociatedIrp.SystemBuffer, sizeof(SENDCMDINPARAMS) - 1);
|
|
|
|
irp2 = IoBuildDeviceIoControlRequest(IOCTL_SCSI_MINIPORT,
|
|
commonExtension->LowerDeviceObject,
|
|
srbControl,
|
|
sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS) - 1,
|
|
srbControl,
|
|
sizeof(SRB_IO_CONTROL) + length,
|
|
FALSE,
|
|
&event,
|
|
&ioStatus);
|
|
|
|
if (irp2 == NULL) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
ExFreePool(srbControl);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Call the port driver with the request and wait for it to complete.
|
|
//
|
|
|
|
status = IoCallDriver(commonExtension->LowerDeviceObject, irp2);
|
|
|
|
if (status == STATUS_PENDING) {
|
|
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
|
status = ioStatus.Status;
|
|
}
|
|
|
|
//
|
|
// Copy the data received into the output buffer. Since the status buffer
|
|
// contains error information also, always perform this copy. IO will will
|
|
// either pass this back to the app, or zero it, in case of error.
|
|
//
|
|
|
|
buffer = (PUCHAR)srbControl;
|
|
(ULONG_PTR)buffer += srbControl->HeaderLength;
|
|
|
|
//
|
|
// Update the return buffer size based on the sub-command.
|
|
//
|
|
|
|
if (cmdInParameters->irDriveRegs.bFeaturesReg == RETURN_SMART_STATUS) {
|
|
length = sizeof(SENDCMDOUTPARAMS) - 1 + sizeof(IDEREGS);
|
|
} else {
|
|
length = sizeof(SENDCMDOUTPARAMS) - 1;
|
|
}
|
|
|
|
RtlMoveMemory ( Irp->AssociatedIrp.SystemBuffer, buffer, length);
|
|
Irp->IoStatus.Information = length;
|
|
|
|
ExFreePool(srbControl);
|
|
break;
|
|
|
|
}
|
|
|
|
case IOCTL_STORAGE_GET_MEDIA_TYPES_EX: {
|
|
|
|
PMODE_PARAMETER_BLOCK blockDescriptor;
|
|
ULONG modeLength;
|
|
ULONG retries = 4;
|
|
UCHAR densityCode = 0;
|
|
BOOLEAN writable = TRUE;
|
|
BOOLEAN mediaPresent = FALSE;
|
|
|
|
DebugPrint((3,
|
|
"Disk.DiskDeviceControl: GetMediaTypes\n"));
|
|
|
|
if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(GET_MEDIA_TYPES)) {
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
if(!commonExtension->IsFdo) {
|
|
ClassReleaseRemoveLock(DeviceObject, Irp);
|
|
ExFreePool(srb);
|
|
SendToFdo(DeviceObject, Irp, status);
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Send a TUR to determine if media is present.
|
|
//
|
|
|
|
srb->CdbLength = 6;
|
|
cdb = (PCDB)srb->Cdb;
|
|
cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY;
|
|
|
|
//
|
|
// Set timeout value.
|
|
//
|
|
|
|
srb->TimeOutValue = fdoExtension->TimeOutValue;
|
|
|
|
status = ClassSendSrbSynchronous(DeviceObject,
|
|
srb,
|
|
NULL,
|
|
0,
|
|
FALSE);
|
|
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
mediaPresent = TRUE;
|
|
}
|
|
|
|
RtlZeroMemory(srb, SCSI_REQUEST_BLOCK_SIZE);
|
|
|
|
//
|
|
// Allocate memory for mode header and block descriptor.
|
|
//
|
|
|
|
modeLength = sizeof(MODE_PARAMETER_HEADER) + sizeof(MODE_PARAMETER_BLOCK);
|
|
modeData = ExAllocatePoolWithTag(NonPagedPoolCacheAligned,
|
|
modeLength,
|
|
DISK_TAG_MODE_DATA);
|
|
|
|
if (modeData == NULL) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
break;
|
|
}
|
|
|
|
RtlZeroMemory(modeData, modeLength);
|
|
|
|
//
|
|
// Build the MODE SENSE CDB.
|
|
//
|
|
|
|
srb->CdbLength = 6;
|
|
cdb = (PCDB)srb->Cdb;
|
|
|
|
//
|
|
// Set timeout value from device extension.
|
|
//
|
|
|
|
srb->TimeOutValue = fdoExtension->TimeOutValue;
|
|
|
|
//
|
|
// Page code of 0 will return header and block descriptor only.
|
|
//
|
|
|
|
cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE;
|
|
cdb->MODE_SENSE.PageCode = 0;
|
|
cdb->MODE_SENSE.AllocationLength = (UCHAR)modeLength;
|
|
|
|
Retry:
|
|
status = ClassSendSrbSynchronous(DeviceObject,
|
|
srb,
|
|
modeData,
|
|
modeLength,
|
|
FALSE);
|
|
|
|
|
|
if (status == STATUS_VERIFY_REQUIRED) {
|
|
|
|
if (retries--) {
|
|
|
|
//
|
|
// Retry request.
|
|
//
|
|
|
|
goto Retry;
|
|
}
|
|
} else if (SRB_STATUS(srb->SrbStatus) == SRB_STATUS_DATA_OVERRUN) {
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
if (NT_SUCCESS(status) || (status == STATUS_NO_MEDIA_IN_DEVICE)) {
|
|
|
|
//
|
|
// Get the block descriptor.
|
|
//
|
|
|
|
if (modeData->BlockDescriptorLength != 0)
|
|
{
|
|
blockDescriptor = (PMODE_PARAMETER_BLOCK)modeData;
|
|
(ULONG_PTR)blockDescriptor += sizeof(MODE_PARAMETER_HEADER);
|
|
|
|
densityCode = blockDescriptor->DensityCode;
|
|
}
|
|
|
|
if (TEST_FLAG(modeData->DeviceSpecificParameter,
|
|
MODE_DSP_WRITE_PROTECT)) {
|
|
|
|
writable = FALSE;
|
|
}
|
|
|
|
status = DiskDetermineMediaTypes(DeviceObject,
|
|
Irp,
|
|
modeData->MediumType,
|
|
densityCode,
|
|
mediaPresent,
|
|
writable);
|
|
|
|
//
|
|
// If the buffer was too small, DetermineMediaTypes updated the status and information and the request will fail.
|
|
//
|
|
|
|
} else {
|
|
DebugPrint((1,
|
|
"DiskDeviceControl: Mode sense for header/bd failed. %lx\n",
|
|
status));
|
|
}
|
|
|
|
ExFreePool(modeData);
|
|
break;
|
|
}
|
|
|
|
case IOCTL_DISK_GET_DRIVE_GEOMETRY: {
|
|
|
|
DebugPrint((2, "IOCTL_DISK_GET_DRIVE_GEOMETRY to device %p through irp %p\n",
|
|
DeviceObject, Irp));
|
|
DebugPrint((2, "Device is a%s.\n",
|
|
commonExtension->IsFdo ? "n fdo" : " pdo"));
|
|
|
|
if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(DISK_GEOMETRY)) {
|
|
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
if(!commonExtension->IsFdo) {
|
|
|
|
//
|
|
// Pdo should issue this request to the lower device object
|
|
//
|
|
|
|
ClassReleaseRemoveLock(DeviceObject, Irp);
|
|
ExFreePool(srb);
|
|
SendToFdo(DeviceObject, Irp, status);
|
|
return status;
|
|
}
|
|
|
|
if (TEST_FLAG(DeviceObject->Characteristics, FILE_REMOVABLE_MEDIA)) {
|
|
|
|
//
|
|
// Issue ReadCapacity to update device extension
|
|
// with information for current media.
|
|
//
|
|
|
|
status = DiskReadDriveCapacity(
|
|
commonExtension->PartitionZeroExtension->DeviceObject);
|
|
|
|
//
|
|
// Note whether the drive is ready.
|
|
//
|
|
|
|
diskData->ReadyStatus = status;
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Copy drive geometry information from device extension.
|
|
//
|
|
|
|
RtlMoveMemory(Irp->AssociatedIrp.SystemBuffer,
|
|
&(fdoExtension->DiskGeometry),
|
|
sizeof(DISK_GEOMETRY));
|
|
|
|
status = STATUS_SUCCESS;
|
|
Irp->IoStatus.Information = sizeof(DISK_GEOMETRY);
|
|
break;
|
|
}
|
|
|
|
case IOCTL_DISK_GET_DRIVE_GEOMETRY_EX: {
|
|
DebugPrint((1, "IOCTL_DISK_GET_DRIVE_GEOMETRY_EX to device %p through irp %p\n",
|
|
DeviceObject, Irp));
|
|
DebugPrint((1, "Device Is a%s.\n",
|
|
commonExtension->IsFdo ? "n fdo" : " pdo"));
|
|
|
|
|
|
if (!commonExtension->IsFdo) {
|
|
|
|
//
|
|
// Pdo should issue this request to the lower device object
|
|
//
|
|
|
|
ClassReleaseRemoveLock (DeviceObject, Irp);
|
|
ExFreePool (srb);
|
|
SendToFdo (DeviceObject, Irp, status);
|
|
return status;
|
|
|
|
} else {
|
|
|
|
status = DiskIoctlGetDriveGeometryEx( DeviceObject, Irp );
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case IOCTL_STORAGE_PREDICT_FAILURE : {
|
|
|
|
PSTORAGE_PREDICT_FAILURE checkFailure;
|
|
STORAGE_FAILURE_PREDICT_STATUS diskSmartStatus;
|
|
|
|
DebugPrint((2, "IOCTL_STORAGE_PREDICT_FAILURE to device %p through irp %p\n",
|
|
DeviceObject, Irp));
|
|
DebugPrint((2, "Device is a%s.\n",
|
|
commonExtension->IsFdo ? "n fdo" : " pdo"));
|
|
|
|
checkFailure = (PSTORAGE_PREDICT_FAILURE)Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(STORAGE_PREDICT_FAILURE)) {
|
|
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
if(!commonExtension->IsFdo) {
|
|
|
|
//
|
|
// Pdo should issue this request to the lower device object
|
|
//
|
|
|
|
ClassReleaseRemoveLock(DeviceObject, Irp);
|
|
ExFreePool(srb);
|
|
SendToFdo(DeviceObject, Irp, status);
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// See if the disk is predicting failure
|
|
//
|
|
|
|
if (diskData->FailurePredictionCapability == FailurePredictionSense) {
|
|
ULONG readBufferSize;
|
|
PUCHAR readBuffer;
|
|
PIRP readIrp;
|
|
PDEVICE_OBJECT topOfStack;
|
|
|
|
checkFailure->PredictFailure = 0;
|
|
|
|
KeInitializeEvent(&event, SynchronizationEvent, FALSE);
|
|
|
|
topOfStack = IoGetAttachedDeviceReference(DeviceObject);
|
|
|
|
//
|
|
// SCSI disks need to have a read sent down to provoke any
|
|
// failures to be reported.
|
|
//
|
|
// Issue a normal read operation. The error-handling code in
|
|
// classpnp will take care of a failure prediction by logging the
|
|
// correct event.
|
|
//
|
|
|
|
readBufferSize = fdoExtension->DiskGeometry.BytesPerSector;
|
|
readBuffer = ExAllocatePoolWithTag(NonPagedPool,
|
|
readBufferSize,
|
|
DISK_TAG_SMART);
|
|
|
|
if (readBuffer != NULL) {
|
|
LARGE_INTEGER offset;
|
|
|
|
offset.QuadPart = 0;
|
|
readIrp = IoBuildSynchronousFsdRequest(
|
|
IRP_MJ_READ,
|
|
topOfStack,
|
|
readBuffer,
|
|
readBufferSize,
|
|
&offset,
|
|
&event,
|
|
&ioStatus);
|
|
|
|
|
|
if (readIrp != NULL) {
|
|
status = IoCallDriver(topOfStack, readIrp);
|
|
if (status == STATUS_PENDING) {
|
|
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
|
status = ioStatus.Status;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
ExFreePool(readBuffer);
|
|
}
|
|
else
|
|
{
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
ObDereferenceObject(topOfStack);
|
|
}
|
|
|
|
if ((diskData->FailurePredictionCapability == FailurePredictionSmart) ||
|
|
(diskData->FailurePredictionCapability == FailurePredictionSense))
|
|
{
|
|
status = DiskReadFailurePredictStatus(fdoExtension,
|
|
&diskSmartStatus);
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
status = DiskReadFailurePredictData(fdoExtension,
|
|
Irp->AssociatedIrp.SystemBuffer);
|
|
|
|
if (diskSmartStatus.PredictFailure)
|
|
{
|
|
checkFailure->PredictFailure = 1;
|
|
} else {
|
|
checkFailure->PredictFailure = 0;
|
|
}
|
|
|
|
Irp->IoStatus.Information = sizeof(STORAGE_PREDICT_FAILURE);
|
|
}
|
|
} else {
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case IOCTL_DISK_VERIFY: {
|
|
|
|
PVERIFY_INFORMATION verifyInfo = Irp->AssociatedIrp.SystemBuffer;
|
|
LARGE_INTEGER byteOffset;
|
|
|
|
DebugPrint((2, "IOCTL_DISK_VERIFY to device %p through irp %p\n",
|
|
DeviceObject, Irp));
|
|
DebugPrint((2, "Device is a%s.\n",
|
|
commonExtension->IsFdo ? "n fdo" : " pdo"));
|
|
|
|
//
|
|
// Validate buffer length.
|
|
//
|
|
|
|
if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(VERIFY_INFORMATION)) {
|
|
|
|
status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Add disk offset to starting sector.
|
|
//
|
|
|
|
byteOffset.QuadPart = commonExtension->StartingOffset.QuadPart +
|
|
verifyInfo->StartingOffset.QuadPart;
|
|
|
|
if(!commonExtension->IsFdo) {
|
|
|
|
//
|
|
// Adjust the request and forward it down
|
|
//
|
|
|
|
verifyInfo->StartingOffset.QuadPart = byteOffset.QuadPart;
|
|
|
|
ClassReleaseRemoveLock(DeviceObject, Irp);
|
|
SendToFdo(DeviceObject, Irp, status);
|
|
ExFreePool(srb);
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Perform a bounds check on the sector range
|
|
//
|
|
|
|
if ((verifyInfo->StartingOffset.QuadPart > commonExtension->PartitionLength.QuadPart) ||
|
|
(verifyInfo->StartingOffset.QuadPart < 0))
|
|
{
|
|
status = STATUS_NONEXISTENT_SECTOR;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
ULONGLONG bytesRemaining = commonExtension->PartitionLength.QuadPart - verifyInfo->StartingOffset.QuadPart;
|
|
|
|
if ((ULONGLONG)verifyInfo->Length > bytesRemaining)
|
|
{
|
|
status = STATUS_NONEXISTENT_SECTOR;
|
|
break;
|
|
}
|
|
}
|
|
|
|
{
|
|
PDISK_VERIFY_WORKITEM_CONTEXT Context = NULL;
|
|
|
|
Context = ExAllocatePoolWithTag(NonPagedPool,
|
|
sizeof(DISK_VERIFY_WORKITEM_CONTEXT),
|
|
DISK_TAG_WI_CONTEXT);
|
|
|
|
if (Context)
|
|
{
|
|
Context->Irp = Irp;
|
|
Context->Srb = srb;
|
|
Context->WorkItem = IoAllocateWorkItem(DeviceObject);
|
|
|
|
if (Context->WorkItem)
|
|
{
|
|
IoMarkIrpPending(Irp);
|
|
|
|
IoQueueWorkItem(Context->WorkItem,
|
|
DiskIoctlVerify,
|
|
DelayedWorkQueue,
|
|
Context);
|
|
|
|
return STATUS_PENDING;
|
|
}
|
|
|
|
ExFreePool(Context);
|
|
}
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case IOCTL_DISK_CREATE_DISK: {
|
|
|
|
if (!commonExtension->IsFdo) {
|
|
ClassReleaseRemoveLock(DeviceObject, Irp);
|
|
ExFreePool(srb);
|
|
SendToFdo(DeviceObject, Irp, status);
|
|
return status;
|
|
}
|
|
|
|
status = DiskIoctlCreateDisk (
|
|
DeviceObject,
|
|
Irp
|
|
);
|
|
|
|
//
|
|
// Notify everyone that the disk layout has changed
|
|
//
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
TARGET_DEVICE_CUSTOM_NOTIFICATION Notification = {0};
|
|
|
|
Notification.Event = GUID_IO_DISK_LAYOUT_CHANGE;
|
|
Notification.Version = 1;
|
|
Notification.Size = (USHORT)FIELD_OFFSET(TARGET_DEVICE_CUSTOM_NOTIFICATION, CustomDataBuffer);
|
|
Notification.FileObject = NULL;
|
|
Notification.NameBufferOffset = -1;
|
|
|
|
IoReportTargetDeviceChangeAsynchronous(fdoExtension->LowerPdo,
|
|
&Notification,
|
|
NULL,
|
|
NULL);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case IOCTL_DISK_GET_DRIVE_LAYOUT: {
|
|
|
|
DebugPrint((1, "IOCTL_DISK_GET_DRIVE_LAYOUT to device %p through irp %p\n",
|
|
DeviceObject, Irp));
|
|
DebugPrint((1, "Device is a%s.\n",
|
|
commonExtension->IsFdo ? "n fdo" : " pdo"));
|
|
|
|
if (!commonExtension->IsFdo) {
|
|
ClassReleaseRemoveLock(DeviceObject, Irp);
|
|
ExFreePool(srb);
|
|
SendToFdo(DeviceObject, Irp, status);
|
|
return status;
|
|
}
|
|
|
|
status = DiskIoctlGetDriveLayout(
|
|
DeviceObject,
|
|
Irp);
|
|
break;
|
|
}
|
|
|
|
case IOCTL_DISK_GET_DRIVE_LAYOUT_EX: {
|
|
|
|
DebugPrint((1, "IOCTL_DISK_GET_DRIVE_LAYOUT_EX to device %p through irp %p\n",
|
|
DeviceObject, Irp));
|
|
DebugPrint((1, "Device is a%s.\n",
|
|
commonExtension->IsFdo ? "n fdo" : " pdo"));
|
|
|
|
if (!commonExtension->IsFdo) {
|
|
ClassReleaseRemoveLock(DeviceObject, Irp);
|
|
ExFreePool(srb);
|
|
SendToFdo(DeviceObject, Irp, status);
|
|
return status;
|
|
}
|
|
|
|
status = DiskIoctlGetDriveLayoutEx(
|
|
DeviceObject,
|
|
Irp);
|
|
break;
|
|
|
|
}
|
|
|
|
case IOCTL_DISK_SET_DRIVE_LAYOUT: {
|
|
|
|
DebugPrint((1, "IOCTL_DISK_SET_DRIVE_LAYOUT to device %p through irp %p\n",
|
|
DeviceObject, Irp));
|
|
DebugPrint((1, "Device is a%s.\n",
|
|
commonExtension->IsFdo ? "n fdo" : " pdo"));
|
|
|
|
if(!commonExtension->IsFdo) {
|
|
ClassReleaseRemoveLock(DeviceObject, Irp);
|
|
ExFreePool(srb);
|
|
SendToFdo(DeviceObject, Irp, status);
|
|
return status;
|
|
}
|
|
|
|
status = DiskIoctlSetDriveLayout(DeviceObject, Irp);
|
|
|
|
//
|
|
// Notify everyone that the disk layout has changed
|
|
//
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
TARGET_DEVICE_CUSTOM_NOTIFICATION Notification = {0};
|
|
|
|
Notification.Event = GUID_IO_DISK_LAYOUT_CHANGE;
|
|
Notification.Version = 1;
|
|
Notification.Size = (USHORT)FIELD_OFFSET(TARGET_DEVICE_CUSTOM_NOTIFICATION, CustomDataBuffer);
|
|
Notification.FileObject = NULL;
|
|
Notification.NameBufferOffset = -1;
|
|
|
|
IoReportTargetDeviceChangeAsynchronous(fdoExtension->LowerPdo,
|
|
&Notification,
|
|
NULL,
|
|
NULL);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case IOCTL_DISK_SET_DRIVE_LAYOUT_EX: {
|
|
|
|
DebugPrint((1, "IOCTL_DISK_SET_DRIVE_LAYOUT_EX to device %p through irp %p\n",
|
|
DeviceObject, Irp));
|
|
DebugPrint((1, "Device is a%s.\n",
|
|
commonExtension->IsFdo ? "n fdo" : " pdo"));
|
|
|
|
if (!commonExtension->IsFdo) {
|
|
ClassReleaseRemoveLock(DeviceObject, Irp);
|
|
ExFreePool(srb);
|
|
SendToFdo(DeviceObject, Irp, status);
|
|
|
|
return status;
|
|
}
|
|
|
|
status = DiskIoctlSetDriveLayoutEx(
|
|
DeviceObject,
|
|
Irp);
|
|
|
|
//
|
|
// Notify everyone that the disk layout has changed
|
|
//
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
TARGET_DEVICE_CUSTOM_NOTIFICATION Notification = {0};
|
|
|
|
Notification.Event = GUID_IO_DISK_LAYOUT_CHANGE;
|
|
Notification.Version = 1;
|
|
Notification.Size = (USHORT)FIELD_OFFSET(TARGET_DEVICE_CUSTOM_NOTIFICATION, CustomDataBuffer);
|
|
Notification.FileObject = NULL;
|
|
Notification.NameBufferOffset = -1;
|
|
|
|
IoReportTargetDeviceChangeAsynchronous(fdoExtension->LowerPdo,
|
|
&Notification,
|
|
NULL,
|
|
NULL);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case IOCTL_DISK_GET_PARTITION_INFO: {
|
|
|
|
DebugPrint((1, "IOCTL_DISK_GET_PARTITION_INFO to device %p through irp %p\n",
|
|
DeviceObject, Irp));
|
|
DebugPrint((1, "Device is a%s.\n",
|
|
commonExtension->IsFdo ? "n fdo" : " pdo"));
|
|
|
|
status = DiskIoctlGetPartitionInfo(
|
|
DeviceObject,
|
|
Irp);
|
|
break;
|
|
}
|
|
|
|
case IOCTL_DISK_GET_PARTITION_INFO_EX: {
|
|
|
|
DebugPrint((1, "IOCTL_DISK_GET_PARTITION_INFO to device %p through irp %p\n",
|
|
DeviceObject, Irp));
|
|
DebugPrint((1, "Device is a%s.\n",
|
|
commonExtension->IsFdo ? "n fdo" : " pdo"));
|
|
|
|
status = DiskIoctlGetPartitionInfoEx(
|
|
DeviceObject,
|
|
Irp);
|
|
break;
|
|
}
|
|
|
|
case IOCTL_DISK_GET_LENGTH_INFO: {
|
|
DebugPrint((1, "IOCTL_DISK_GET_LENGTH_INFO to device %p through irp %p\n",
|
|
DeviceObject, Irp));
|
|
DebugPrint((1, "Device is a%s.\n",
|
|
commonExtension->IsFdo ? "n fdo" : " pdo"));
|
|
|
|
status = DiskIoctlGetLengthInfo(
|
|
DeviceObject,
|
|
Irp);
|
|
break;
|
|
}
|
|
|
|
case IOCTL_DISK_SET_PARTITION_INFO: {
|
|
|
|
DebugPrint((1, "IOCTL_DISK_SET_PARTITION_INFO to device %p through irp %p\n",
|
|
DeviceObject, Irp));
|
|
DebugPrint((1, "Device is a%s.\n",
|
|
commonExtension->IsFdo ? "n fdo" : " pdo"));
|
|
|
|
status = DiskIoctlSetPartitionInfo (
|
|
DeviceObject,
|
|
Irp);
|
|
|
|
//
|
|
// Notify everyone that the disk layout has changed
|
|
//
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
TARGET_DEVICE_CUSTOM_NOTIFICATION Notification = {0};
|
|
|
|
Notification.Event = GUID_IO_DISK_LAYOUT_CHANGE;
|
|
Notification.Version = 1;
|
|
Notification.Size = (USHORT)FIELD_OFFSET(TARGET_DEVICE_CUSTOM_NOTIFICATION, CustomDataBuffer);
|
|
Notification.FileObject = NULL;
|
|
Notification.NameBufferOffset = -1;
|
|
|
|
//
|
|
// We can safely assume that we are in the context of a PDO
|
|
//
|
|
IoReportTargetDeviceChangeAsynchronous((commonExtension->PartitionZeroExtension)->LowerPdo,
|
|
&Notification,
|
|
NULL,
|
|
NULL);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
|
|
case IOCTL_DISK_SET_PARTITION_INFO_EX: {
|
|
|
|
DebugPrint((1, "IOCTL_DISK_SET_PARTITION_INFO_EX to device %p through irp %p\n",
|
|
DeviceObject, Irp));
|
|
DebugPrint((1, "Device is a%s.\n",
|
|
commonExtension->IsFdo ? "n fdo" : " pdo"));
|
|
|
|
status = DiskIoctlSetPartitionInfoEx(
|
|
DeviceObject,
|
|
Irp);
|
|
|
|
//
|
|
// Notify everyone that the disk layout has changed
|
|
//
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
TARGET_DEVICE_CUSTOM_NOTIFICATION Notification = {0};
|
|
|
|
Notification.Event = GUID_IO_DISK_LAYOUT_CHANGE;
|
|
Notification.Version = 1;
|
|
Notification.Size = (USHORT)FIELD_OFFSET(TARGET_DEVICE_CUSTOM_NOTIFICATION, CustomDataBuffer);
|
|
Notification.FileObject = NULL;
|
|
Notification.NameBufferOffset = -1;
|
|
|
|
//
|
|
// We can safely assume that we are in the context of a PDO
|
|
//
|
|
IoReportTargetDeviceChangeAsynchronous((commonExtension->PartitionZeroExtension)->LowerPdo,
|
|
&Notification,
|
|
NULL,
|
|
NULL);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case IOCTL_DISK_DELETE_DRIVE_LAYOUT: {
|
|
|
|
CREATE_DISK CreateDiskInfo = { 0 };
|
|
|
|
//
|
|
// Update the disk with new partition information.
|
|
//
|
|
|
|
DebugPrint((1, "IOCTL_DISK_DELETE_DRIVE_LAYOUT to device %p through irp %p\n",
|
|
DeviceObject, Irp));
|
|
DebugPrint((1, "Device is a%s.\n",
|
|
commonExtension->IsFdo ? "n fdo" : " pdo"));
|
|
|
|
if(!commonExtension->IsFdo) {
|
|
|
|
ClassReleaseRemoveLock(DeviceObject, Irp);
|
|
ExFreePool(srb);
|
|
SendToFdo(DeviceObject, Irp, status);
|
|
return status;
|
|
}
|
|
|
|
DiskAcquirePartitioningLock(fdoExtension);
|
|
|
|
DiskInvalidatePartitionTable(fdoExtension, TRUE);
|
|
|
|
//
|
|
// IoCreateDisk called with a partition style of raw
|
|
// will remove any partition tables from the disk.
|
|
//
|
|
|
|
CreateDiskInfo.PartitionStyle = PARTITION_STYLE_RAW;
|
|
|
|
status = IoCreateDisk(
|
|
DeviceObject,
|
|
&CreateDiskInfo);
|
|
|
|
//
|
|
// Notify everyone that the disk layout has changed
|
|
//
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
TARGET_DEVICE_CUSTOM_NOTIFICATION Notification = {0};
|
|
|
|
Notification.Event = GUID_IO_DISK_LAYOUT_CHANGE;
|
|
Notification.Version = 1;
|
|
Notification.Size = (USHORT)FIELD_OFFSET(TARGET_DEVICE_CUSTOM_NOTIFICATION, CustomDataBuffer);
|
|
Notification.FileObject = NULL;
|
|
Notification.NameBufferOffset = -1;
|
|
|
|
IoReportTargetDeviceChangeAsynchronous(fdoExtension->LowerPdo,
|
|
&Notification,
|
|
NULL,
|
|
NULL);
|
|
}
|
|
|
|
DiskReleasePartitioningLock(fdoExtension);
|
|
|
|
ClassInvalidateBusRelations(DeviceObject);
|
|
|
|
Irp->IoStatus.Status = status;
|
|
|
|
break;
|
|
}
|
|
|
|
case IOCTL_DISK_REASSIGN_BLOCKS: {
|
|
|
|
//
|
|
// Map defective blocks to new location on disk.
|
|
//
|
|
|
|
PREASSIGN_BLOCKS badBlocks = Irp->AssociatedIrp.SystemBuffer;
|
|
ULONG bufferSize;
|
|
ULONG blockNumber;
|
|
ULONG blockCount;
|
|
|
|
DebugPrint((2, "IOCTL_DISK_REASSIGN_BLOCKS to device %p through irp %p\n",
|
|
DeviceObject, Irp));
|
|
DebugPrint((2, "Device is a%s.\n",
|
|
commonExtension->IsFdo ? "n fdo" : " pdo"));
|
|
|
|
//
|
|
// Validate buffer length.
|
|
//
|
|
|
|
if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(REASSIGN_BLOCKS)) {
|
|
|
|
status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
bufferSize = sizeof(REASSIGN_BLOCKS) +
|
|
((badBlocks->Count - 1) * sizeof(ULONG));
|
|
|
|
if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
|
|
bufferSize) {
|
|
|
|
status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Send to FDO
|
|
//
|
|
|
|
if(!commonExtension->IsFdo) {
|
|
|
|
ClassReleaseRemoveLock(DeviceObject, Irp);
|
|
ExFreePool(srb);
|
|
SendToFdo(DeviceObject, Irp, status);
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Build the data buffer to be transferred in the input buffer.
|
|
// The format of the data to the device is:
|
|
//
|
|
// 2 bytes Reserved
|
|
// 2 bytes Length
|
|
// x * 4 btyes Block Address
|
|
//
|
|
// All values are big endian.
|
|
//
|
|
|
|
badBlocks->Reserved = 0;
|
|
blockCount = badBlocks->Count;
|
|
|
|
//
|
|
// Convert # of entries to # of bytes.
|
|
//
|
|
|
|
blockCount *= 4;
|
|
badBlocks->Count = (USHORT) ((blockCount >> 8) & 0XFF);
|
|
badBlocks->Count |= (USHORT) ((blockCount << 8) & 0XFF00);
|
|
|
|
//
|
|
// Convert back to number of entries.
|
|
//
|
|
|
|
blockCount /= 4;
|
|
|
|
for (; blockCount > 0; blockCount--) {
|
|
|
|
blockNumber = badBlocks->BlockNumber[blockCount-1];
|
|
|
|
REVERSE_BYTES((PFOUR_BYTE) &badBlocks->BlockNumber[blockCount-1],
|
|
(PFOUR_BYTE) &blockNumber);
|
|
}
|
|
|
|
srb->CdbLength = 6;
|
|
|
|
cdb->CDB6GENERIC.OperationCode = SCSIOP_REASSIGN_BLOCKS;
|
|
|
|
//
|
|
// Set timeout value.
|
|
//
|
|
|
|
srb->TimeOutValue = fdoExtension->TimeOutValue;
|
|
|
|
status = ClassSendSrbSynchronous(DeviceObject,
|
|
srb,
|
|
badBlocks,
|
|
bufferSize,
|
|
TRUE);
|
|
break;
|
|
}
|
|
|
|
case IOCTL_DISK_IS_WRITABLE: {
|
|
|
|
//
|
|
// This routine mimics IOCTL_STORAGE_GET_MEDIA_TYPES_EX
|
|
//
|
|
|
|
ULONG modeLength;
|
|
ULONG retries = 4;
|
|
|
|
DebugPrint((3, "Disk.DiskDeviceControl: IOCTL_DISK_IS_WRITABLE\n"));
|
|
|
|
if (!commonExtension->IsFdo)
|
|
{
|
|
ClassReleaseRemoveLock(DeviceObject, Irp);
|
|
ExFreePool(srb);
|
|
SendToFdo(DeviceObject, Irp, status);
|
|
return status;
|
|
}
|
|
|
|
RtlZeroMemory(srb, SCSI_REQUEST_BLOCK_SIZE);
|
|
|
|
//
|
|
// Allocate memory for a mode header and then some
|
|
// for port drivers that need to convert to MODE10
|
|
// or always return the MODE_PARAMETER_BLOCK (even
|
|
// when memory was not allocated for this purpose)
|
|
//
|
|
|
|
modeLength = sizeof(MODE_PARAMETER_HEADER) + sizeof(MODE_PARAMETER_BLOCK);
|
|
modeData = ExAllocatePoolWithTag(NonPagedPoolCacheAligned,
|
|
modeLength,
|
|
DISK_TAG_MODE_DATA);
|
|
|
|
if (modeData == NULL)
|
|
{
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
break;
|
|
}
|
|
|
|
RtlZeroMemory(modeData, modeLength);
|
|
|
|
//
|
|
// Build the MODE SENSE CDB
|
|
//
|
|
|
|
srb->CdbLength = 6;
|
|
cdb = (PCDB)srb->Cdb;
|
|
|
|
//
|
|
// Set the timeout value from the device extension
|
|
//
|
|
|
|
srb->TimeOutValue = fdoExtension->TimeOutValue;
|
|
|
|
cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE;
|
|
cdb->MODE_SENSE.PageCode = MODE_SENSE_RETURN_ALL;
|
|
cdb->MODE_SENSE.AllocationLength = (UCHAR)modeLength;
|
|
|
|
while (retries != 0)
|
|
{
|
|
status = ClassSendSrbSynchronous(DeviceObject,
|
|
srb,
|
|
modeData,
|
|
modeLength,
|
|
FALSE);
|
|
|
|
if (status != STATUS_VERIFY_REQUIRED)
|
|
{
|
|
if (SRB_STATUS(srb->SrbStatus) == SRB_STATUS_DATA_OVERRUN)
|
|
{
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
retries--;
|
|
}
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
if (TEST_FLAG(modeData->DeviceSpecificParameter, MODE_DSP_WRITE_PROTECT))
|
|
{
|
|
status = STATUS_MEDIA_WRITE_PROTECTED;
|
|
}
|
|
}
|
|
|
|
ExFreePool(modeData);
|
|
break;
|
|
}
|
|
|
|
case IOCTL_DISK_INTERNAL_SET_VERIFY: {
|
|
|
|
//
|
|
// If the caller is kernel mode, set the verify bit.
|
|
//
|
|
|
|
if (Irp->RequestorMode == KernelMode) {
|
|
|
|
SET_FLAG(DeviceObject->Flags, DO_VERIFY_VOLUME);
|
|
}
|
|
|
|
DiskInvalidatePartitionTable(fdoExtension, FALSE);
|
|
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
case IOCTL_DISK_INTERNAL_CLEAR_VERIFY: {
|
|
|
|
//
|
|
// If the caller is kernel mode, clear the verify bit.
|
|
//
|
|
|
|
if (Irp->RequestorMode == KernelMode) {
|
|
CLEAR_FLAG(DeviceObject->Flags, DO_VERIFY_VOLUME);
|
|
}
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
case IOCTL_DISK_UPDATE_DRIVE_SIZE: {
|
|
|
|
DebugPrint((2, "IOCTL_DISK_UPDATE_DRIVE_SIZE to device %p "
|
|
"through irp %p\n",
|
|
DeviceObject, Irp));
|
|
|
|
DebugPrint((2, "Device is a%s.\n",
|
|
commonExtension->IsFdo ? "n fdo" : " pdo"));
|
|
|
|
if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(DISK_GEOMETRY)) {
|
|
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
if(!commonExtension->IsFdo) {
|
|
|
|
//
|
|
// Pdo should issue this request to the lower device object.
|
|
//
|
|
|
|
ClassReleaseRemoveLock(DeviceObject, Irp);
|
|
ExFreePool(srb);
|
|
SendToFdo(DeviceObject, Irp, status);
|
|
return status;
|
|
}
|
|
|
|
DiskAcquirePartitioningLock(fdoExtension);
|
|
|
|
//
|
|
// Invalidate the cached partition table.
|
|
//
|
|
|
|
DiskInvalidatePartitionTable(fdoExtension, TRUE);
|
|
|
|
//
|
|
// At this point, commonExtension *is* the FDO extension. This
|
|
// should be the same as PartitionZeroExtension.
|
|
//
|
|
|
|
ASSERT(commonExtension ==
|
|
&(commonExtension->PartitionZeroExtension->CommonExtension));
|
|
|
|
//
|
|
// Issue ReadCapacity to update device extension with information
|
|
// for current media.
|
|
//
|
|
|
|
status = DiskReadDriveCapacity(DeviceObject);
|
|
|
|
//
|
|
// Note whether the drive is ready.
|
|
//
|
|
|
|
diskData->ReadyStatus = status;
|
|
|
|
//
|
|
// The disk's partition tables may be invalid after the drive geometry
|
|
// has been updated. The call to IoValidatePartitionTable (below) will
|
|
// fix it if this is the case.
|
|
//
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
status = DiskVerifyPartitionTable (fdoExtension, TRUE);
|
|
}
|
|
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Copy drive geometry information from the device extension.
|
|
//
|
|
|
|
RtlMoveMemory(Irp->AssociatedIrp.SystemBuffer,
|
|
&(fdoExtension->DiskGeometry),
|
|
sizeof(DISK_GEOMETRY));
|
|
|
|
Irp->IoStatus.Information = sizeof(DISK_GEOMETRY);
|
|
status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Notify everyone that the disk layout may have changed
|
|
//
|
|
{
|
|
TARGET_DEVICE_CUSTOM_NOTIFICATION Notification = {0};
|
|
|
|
Notification.Event = GUID_IO_DISK_LAYOUT_CHANGE;
|
|
Notification.Version = 1;
|
|
Notification.Size = (USHORT)FIELD_OFFSET(TARGET_DEVICE_CUSTOM_NOTIFICATION, CustomDataBuffer);
|
|
Notification.FileObject = NULL;
|
|
Notification.NameBufferOffset = -1;
|
|
|
|
IoReportTargetDeviceChangeAsynchronous(fdoExtension->LowerPdo,
|
|
&Notification,
|
|
NULL,
|
|
NULL);
|
|
}
|
|
}
|
|
|
|
DiskReleasePartitioningLock(fdoExtension);
|
|
|
|
break;
|
|
}
|
|
|
|
case IOCTL_DISK_GROW_PARTITION: {
|
|
|
|
PDISK_GROW_PARTITION inputBuffer;
|
|
PCOMMON_DEVICE_EXTENSION pdoCommonExtension = &commonExtension->ChildList->CommonExtension;
|
|
LARGE_INTEGER bytesPerCylinder;
|
|
LARGE_INTEGER newStoppingOffset;
|
|
LARGE_INTEGER newPartitionLength;
|
|
|
|
PPHYSICAL_DEVICE_EXTENSION sibling;
|
|
|
|
PDRIVE_LAYOUT_INFORMATION_EX layoutInfo;
|
|
PPARTITION_INFORMATION_EX pdoPartition;
|
|
PPARTITION_INFORMATION_EX containerPartition;
|
|
ULONG partitionIndex;
|
|
|
|
DebugPrint((2, "IOCTL_DISK_GROW_PARTITION to device %p through "
|
|
"irp %p\n",
|
|
DeviceObject, Irp));
|
|
|
|
DebugPrint((2, "Device is a%s.\n",
|
|
commonExtension->IsFdo ? "n fdo" : " pdo"));
|
|
|
|
if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(DISK_GROW_PARTITION)) {
|
|
|
|
status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
if(!commonExtension->IsFdo) {
|
|
|
|
//
|
|
// Pdo should issue this request to the lower device object
|
|
//
|
|
|
|
ClassReleaseRemoveLock(DeviceObject, Irp);
|
|
ExFreePool(srb);
|
|
SendToFdo(DeviceObject, Irp, status);
|
|
return status;
|
|
}
|
|
|
|
DiskAcquirePartitioningLock(fdoExtension);
|
|
|
|
ClassAcquireChildLock(fdoExtension);
|
|
|
|
//
|
|
// At this point, commonExtension *is* the FDO extension. This should
|
|
// be the same as PartitionZeroExtension.
|
|
//
|
|
|
|
ASSERT(commonExtension ==
|
|
&(commonExtension->PartitionZeroExtension->CommonExtension));
|
|
|
|
//
|
|
// Get the input parameters
|
|
//
|
|
|
|
inputBuffer = (PDISK_GROW_PARTITION) Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
ASSERT(inputBuffer);
|
|
|
|
//
|
|
// Make sure that we are actually being asked to grow the partition.
|
|
//
|
|
|
|
if(inputBuffer->BytesToGrow.QuadPart <= 0) {
|
|
|
|
status = STATUS_INVALID_PARAMETER;
|
|
ClassReleaseChildLock(fdoExtension);
|
|
DiskReleasePartitioningLock(fdoExtension);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Find the partition that matches the supplied number
|
|
//
|
|
|
|
|
|
while (pdoCommonExtension){
|
|
|
|
//
|
|
// Is this the partition we are searching for?
|
|
//
|
|
|
|
if(inputBuffer->PartitionNumber == pdoCommonExtension->PartitionNumber) {
|
|
break;
|
|
}
|
|
|
|
pdoCommonExtension = &pdoCommonExtension->ChildList->CommonExtension;
|
|
}
|
|
|
|
// Did we find the partition?
|
|
|
|
if (pdoCommonExtension == NULL){
|
|
status = STATUS_INVALID_PARAMETER;
|
|
ClassReleaseChildLock(fdoExtension);
|
|
DiskReleasePartitioningLock(fdoExtension);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Compute the new values for the partition to grow.
|
|
//
|
|
|
|
newPartitionLength.QuadPart =
|
|
(pdoCommonExtension->PartitionLength.QuadPart +
|
|
inputBuffer->BytesToGrow.QuadPart);
|
|
|
|
newStoppingOffset.QuadPart =
|
|
(pdoCommonExtension->StartingOffset.QuadPart +
|
|
newPartitionLength.QuadPart - 1);
|
|
|
|
//
|
|
// The below alignment requirement only applies to the MBR layout
|
|
//
|
|
|
|
if (diskData->PartitionStyle == PARTITION_STYLE_MBR) {
|
|
|
|
//
|
|
// Test the partition alignment before getting to involved.
|
|
//
|
|
// NOTE:
|
|
// All partition stopping offsets should be one byte less
|
|
// than a cylinder boundary offset. Also, all first partitions
|
|
// (within partition0 and within an extended partition) start
|
|
// on the second track while all other partitions start on a
|
|
// cylinder boundary.
|
|
//
|
|
bytesPerCylinder.QuadPart =
|
|
((LONGLONG) fdoExtension->DiskGeometry.TracksPerCylinder *
|
|
(LONGLONG) fdoExtension->DiskGeometry.SectorsPerTrack *
|
|
(LONGLONG) fdoExtension->DiskGeometry.BytesPerSector);
|
|
|
|
// Temporarily adjust up to cylinder boundary.
|
|
newStoppingOffset.QuadPart += 1;
|
|
|
|
if(newStoppingOffset.QuadPart % bytesPerCylinder.QuadPart) {
|
|
|
|
// Adjust the length first...
|
|
newPartitionLength.QuadPart -=
|
|
(newStoppingOffset.QuadPart % bytesPerCylinder.QuadPart);
|
|
|
|
// ...and then the stopping offset.
|
|
newStoppingOffset.QuadPart -=
|
|
(newStoppingOffset.QuadPart % bytesPerCylinder.QuadPart);
|
|
|
|
DebugPrint((2, "IOCTL_DISK_GROW_PARTITION: "
|
|
"Adjusted the requested partition size to cylinder boundary"));
|
|
}
|
|
|
|
// Restore to one byte less than a cylinder boundary.
|
|
newStoppingOffset.QuadPart -= 1;
|
|
}
|
|
|
|
//
|
|
// Will the new partition fit within Partition0?
|
|
// Remember: commonExtension == &PartitionZeroExtension->CommonExtension
|
|
//
|
|
|
|
if(newStoppingOffset.QuadPart >
|
|
(commonExtension->StartingOffset.QuadPart +
|
|
commonExtension->PartitionLength.QuadPart - 1)) {
|
|
|
|
//
|
|
// The new partition falls outside Partition0
|
|
//
|
|
|
|
status = STATUS_UNSUCCESSFUL;
|
|
ClassReleaseChildLock(fdoExtension);
|
|
DiskReleasePartitioningLock(fdoExtension);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Search for any partition that will conflict with the new partition.
|
|
// This is done before testing for any containing partitions to
|
|
// simplify the container handling.
|
|
//
|
|
|
|
sibling = commonExtension->ChildList;
|
|
|
|
while(sibling != NULL) {
|
|
LARGE_INTEGER sibStoppingOffset;
|
|
PCOMMON_DEVICE_EXTENSION siblingExtension;
|
|
|
|
siblingExtension = &(sibling->CommonExtension);
|
|
|
|
ASSERT( siblingExtension );
|
|
|
|
sibStoppingOffset.QuadPart =
|
|
(siblingExtension->StartingOffset.QuadPart +
|
|
siblingExtension->PartitionLength.QuadPart - 1);
|
|
|
|
//
|
|
// Only check the siblings that start beyond the new partition
|
|
// starting offset. Also, assume that since the starting offset
|
|
// has not changed, it will not be in conflict with any other
|
|
// partitions; only the new stopping offset needs to be tested.
|
|
//
|
|
|
|
if((inputBuffer->PartitionNumber !=
|
|
siblingExtension->PartitionNumber) &&
|
|
|
|
(siblingExtension->StartingOffset.QuadPart >
|
|
pdoCommonExtension->StartingOffset.QuadPart) &&
|
|
|
|
(newStoppingOffset.QuadPart >=
|
|
siblingExtension->StartingOffset.QuadPart)) {
|
|
|
|
//
|
|
// We have a conflict; bail out leaving pdoSibling set.
|
|
//
|
|
|
|
break;
|
|
}
|
|
sibling = siblingExtension->ChildList;
|
|
}
|
|
|
|
|
|
//
|
|
// If there is a sibling that conflicts, it will be in pdoSibling; there
|
|
// could be more than one, but this is the first one detected.
|
|
//
|
|
|
|
if(sibling != NULL) {
|
|
//
|
|
// Report the conflict and abort the grow request.
|
|
//
|
|
|
|
status = STATUS_UNSUCCESSFUL;
|
|
ClassReleaseChildLock(fdoExtension);
|
|
DiskReleasePartitioningLock(fdoExtension);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Read the partition table. Since we're planning on modifying it
|
|
// we should bypass the cache.
|
|
//
|
|
|
|
status = DiskReadPartitionTableEx(fdoExtension, TRUE, &layoutInfo );
|
|
|
|
if( !NT_SUCCESS(status) ) {
|
|
ClassReleaseChildLock(fdoExtension);
|
|
DiskReleasePartitioningLock(fdoExtension);
|
|
break;
|
|
}
|
|
|
|
ASSERT( layoutInfo );
|
|
|
|
//
|
|
// Search the layout for the partition that matches the
|
|
// PDO in hand.
|
|
//
|
|
|
|
pdoPartition =
|
|
DiskPdoFindPartitionEntry(
|
|
(PPHYSICAL_DEVICE_EXTENSION)pdoCommonExtension,
|
|
layoutInfo);
|
|
|
|
if(pdoPartition == NULL) {
|
|
// Looks like something is wrong interally-- error ok?
|
|
status = STATUS_DRIVER_INTERNAL_ERROR;
|
|
layoutInfo = NULL;
|
|
ClassReleaseChildLock(fdoExtension);
|
|
DiskReleasePartitioningLock(fdoExtension);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Search the on-disk partition information to find the root containing
|
|
// partition (top-to-bottom).
|
|
//
|
|
// Remember: commonExtension == &PartitionZeroExtension->CommonExtension
|
|
//
|
|
|
|
//
|
|
// All affected containers will have a new stopping offset
|
|
// that is equal to the new partition (logical drive)
|
|
// stopping offset. Walk the layout information from
|
|
// bottom-to-top searching for logical drive containers and
|
|
// propagating the change.
|
|
//
|
|
|
|
containerPartition =
|
|
DiskFindContainingPartition(
|
|
layoutInfo,
|
|
pdoPartition,
|
|
FALSE);
|
|
|
|
//
|
|
// This loop should only execute at most 2 times; once for
|
|
// the logical drive container, and once for the root
|
|
// extended partition container. If the growing partition
|
|
// is not contained, the loop does not run.
|
|
//
|
|
|
|
while(containerPartition != NULL) {
|
|
LARGE_INTEGER containerStoppingOffset;
|
|
PPARTITION_INFORMATION_EX nextContainerPartition;
|
|
|
|
//
|
|
// Plan ahead and get the container's container before
|
|
// modifing the current size.
|
|
//
|
|
|
|
nextContainerPartition =
|
|
DiskFindContainingPartition(
|
|
layoutInfo,
|
|
containerPartition,
|
|
FALSE);
|
|
|
|
//
|
|
// Figure out where the current container ends and test
|
|
// to see if it already encompasses the containee.
|
|
//
|
|
|
|
containerStoppingOffset.QuadPart =
|
|
(containerPartition->StartingOffset.QuadPart +
|
|
containerPartition->PartitionLength.QuadPart - 1);
|
|
|
|
if(newStoppingOffset.QuadPart <=
|
|
containerStoppingOffset.QuadPart) {
|
|
|
|
//
|
|
// No need to continue since this container fits
|
|
//
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Adjust the container to have a stopping offset that
|
|
// matches the grown partition stopping offset.
|
|
//
|
|
|
|
containerPartition->PartitionLength.QuadPart =
|
|
newStoppingOffset.QuadPart + 1 -
|
|
containerPartition->StartingOffset.QuadPart;
|
|
|
|
containerPartition->RewritePartition = TRUE;
|
|
|
|
// Continue with the next container
|
|
containerPartition = nextContainerPartition;
|
|
}
|
|
|
|
//
|
|
// Wait until after searching the containers to update the
|
|
// partition size.
|
|
//
|
|
|
|
pdoPartition->PartitionLength.QuadPart =
|
|
newPartitionLength.QuadPart;
|
|
|
|
pdoPartition->RewritePartition = TRUE;
|
|
|
|
//
|
|
// Commit the changes to disk
|
|
//
|
|
|
|
status = DiskWritePartitionTableEx(fdoExtension, layoutInfo );
|
|
|
|
if( NT_SUCCESS(status) ) {
|
|
|
|
//
|
|
// Everything looks good so commit the new length to the
|
|
// PDO. This has to be done carefully. We may potentially
|
|
// grow the partition in three steps:
|
|
// * increase the high-word of the partition length
|
|
// to be just below the new size - the high word should
|
|
// be greater than or equal to the current length.
|
|
//
|
|
// * change the low-word of the partition length to the
|
|
// new value - this value may potentially be lower than
|
|
// the current value (if the high part was changed which
|
|
// is why we changed that first)
|
|
//
|
|
// * change the high part to the correct value.
|
|
//
|
|
|
|
if(newPartitionLength.HighPart >
|
|
pdoCommonExtension->PartitionLength.HighPart) {
|
|
|
|
//
|
|
// Swap in one less than the high word.
|
|
//
|
|
|
|
InterlockedExchange(
|
|
&pdoCommonExtension->PartitionLength.HighPart,
|
|
(newPartitionLength.HighPart - 1));
|
|
}
|
|
|
|
//
|
|
// Swap in the low part.
|
|
//
|
|
|
|
InterlockedExchange(
|
|
&(pdoCommonExtension->PartitionLength.LowPart),
|
|
newPartitionLength.LowPart);
|
|
|
|
if(newPartitionLength.HighPart !=
|
|
pdoCommonExtension->PartitionLength.HighPart) {
|
|
|
|
//
|
|
// Swap in one less than the high word.
|
|
//
|
|
|
|
InterlockedExchange(
|
|
&(pdoCommonExtension->PartitionLength.HighPart),
|
|
newPartitionLength.HighPart);
|
|
}
|
|
|
|
//
|
|
// Notify everyone that the disk layout has changed
|
|
//
|
|
{
|
|
TARGET_DEVICE_CUSTOM_NOTIFICATION Notification = {0};
|
|
|
|
Notification.Event = GUID_IO_DISK_LAYOUT_CHANGE;
|
|
Notification.Version = 1;
|
|
Notification.Size = (USHORT)FIELD_OFFSET(TARGET_DEVICE_CUSTOM_NOTIFICATION, CustomDataBuffer);
|
|
Notification.FileObject = NULL;
|
|
Notification.NameBufferOffset = -1;
|
|
|
|
IoReportTargetDeviceChangeAsynchronous(fdoExtension->LowerPdo,
|
|
&Notification,
|
|
NULL,
|
|
NULL);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Invalidate and free the cached partition table.
|
|
//
|
|
|
|
DiskInvalidatePartitionTable(fdoExtension, TRUE);
|
|
|
|
//
|
|
// Free the partition buffer regardless of the status
|
|
//
|
|
|
|
ClassReleaseChildLock(fdoExtension);
|
|
DiskReleasePartitioningLock(fdoExtension);
|
|
|
|
break;
|
|
}
|
|
|
|
case IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS:
|
|
case IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS_ADMIN:
|
|
{
|
|
if (TEST_FLAG(DeviceObject->Characteristics, FILE_REMOVABLE_MEDIA))
|
|
{
|
|
if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(VOLUME_DISK_EXTENTS))
|
|
{
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
status = DiskReadDriveCapacity(commonExtension->PartitionZeroExtension->DeviceObject);
|
|
|
|
//
|
|
// Note whether the drive is ready.
|
|
//
|
|
|
|
diskData->ReadyStatus = status;
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
PVOLUME_DISK_EXTENTS pVolExt = (PVOLUME_DISK_EXTENTS)Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
pVolExt->NumberOfDiskExtents = 1;
|
|
pVolExt->Extents[0].DiskNumber = commonExtension->PartitionZeroExtension->DeviceNumber;
|
|
pVolExt->Extents[0].StartingOffset = commonExtension->StartingOffset;
|
|
pVolExt->Extents[0].ExtentLength = commonExtension->PartitionLength;
|
|
|
|
Irp->IoStatus.Information = sizeof(VOLUME_DISK_EXTENTS);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case IOCTL_DISK_UPDATE_PROPERTIES: {
|
|
|
|
if (!commonExtension->IsFdo)
|
|
{
|
|
ClassReleaseRemoveLock(DeviceObject, Irp);
|
|
ExFreePool(srb);
|
|
SendToFdo(DeviceObject,Irp,status);
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Invalidate the partition table and re-enumerate the device.
|
|
//
|
|
|
|
if (DiskInvalidatePartitionTable(fdoExtension, FALSE))
|
|
{
|
|
IoInvalidateDeviceRelations(fdoExtension->LowerPdo, BusRelations);
|
|
|
|
//
|
|
// Notify everyone that the disk layout may have changed
|
|
//
|
|
{
|
|
TARGET_DEVICE_CUSTOM_NOTIFICATION Notification = {0};
|
|
|
|
Notification.Event = GUID_IO_DISK_LAYOUT_CHANGE;
|
|
Notification.Version = 1;
|
|
Notification.Size = (USHORT)FIELD_OFFSET(TARGET_DEVICE_CUSTOM_NOTIFICATION, CustomDataBuffer);
|
|
Notification.FileObject = NULL;
|
|
Notification.NameBufferOffset = -1;
|
|
|
|
IoReportTargetDeviceChangeAsynchronous(fdoExtension->LowerPdo,
|
|
&Notification,
|
|
NULL,
|
|
NULL);
|
|
}
|
|
}
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
break;
|
|
}
|
|
|
|
default: {
|
|
|
|
//
|
|
// Free the Srb, since it is not needed.
|
|
//
|
|
|
|
ExFreePool(srb);
|
|
|
|
//
|
|
// Pass the request to the common device control routine.
|
|
//
|
|
|
|
return(ClassDeviceControl(DeviceObject, Irp));
|
|
|
|
break;
|
|
}
|
|
|
|
} // end switch
|
|
|
|
Irp->IoStatus.Status = status;
|
|
|
|
if (!NT_SUCCESS(status) && IoIsErrorUserInduced(status)) {
|
|
|
|
IoSetHardErrorOrVerifyDevice(Irp, DeviceObject);
|
|
}
|
|
|
|
ClassReleaseRemoveLock(DeviceObject, Irp);
|
|
ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
|
|
ExFreePool(srb);
|
|
return(status);
|
|
} // end DiskDeviceControl()
|
|
|
|
|
|
NTSTATUS
|
|
DiskShutdownFlush(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the handler for shutdown and flush requests. It sends
|
|
down a synch cache command to the device if its cache is enabled. If
|
|
the request is a shutdown and the media is removable, it sends down
|
|
an unlock request
|
|
|
|
Finally, an SRB_FUNCTION_SHUTDOWN or SRB_FUNCTION_FLUSH is sent down
|
|
the stack
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - The device object processing the request
|
|
Irp - The shutdown | flush request being serviced
|
|
|
|
Return Value:
|
|
|
|
STATUS_PENDING if successful, an error code otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
|
|
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = commonExtension->PartitionZeroExtension;
|
|
PDISK_DATA diskData = (PDISK_DATA) commonExtension->DriverData;
|
|
PIO_STACK_LOCATION irpStack;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Send partition flush requests to the FDO
|
|
//
|
|
|
|
if(!commonExtension->IsFdo) {
|
|
|
|
PDEVICE_OBJECT lowerDevice = commonExtension->LowerDeviceObject;
|
|
|
|
ClassReleaseRemoveLock(DeviceObject, Irp);
|
|
IoMarkIrpPending(Irp);
|
|
IoCopyCurrentIrpStackLocationToNext(Irp);
|
|
IoCallDriver(lowerDevice, Irp);
|
|
return STATUS_PENDING;
|
|
}
|
|
|
|
//
|
|
// Flush requests are combined and need to be handled in a special manner
|
|
//
|
|
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
if (irpStack->MajorFunction == IRP_MJ_FLUSH_BUFFERS) {
|
|
|
|
KeWaitForMutexObject(&diskData->FlushContext.Mutex, Executive, KernelMode, FALSE, NULL);
|
|
|
|
//
|
|
// This request will most likely be completed asynchronously
|
|
//
|
|
IoMarkIrpPending(Irp);
|
|
|
|
//
|
|
// Look to see if a flush is in progress
|
|
//
|
|
|
|
if (diskData->FlushContext.CurrIrp != NULL) {
|
|
|
|
//
|
|
// There is an outstanding flush. Queue this
|
|
// request to the group that is next in line
|
|
//
|
|
|
|
if (diskData->FlushContext.NextIrp != NULL) {
|
|
|
|
#if DBG
|
|
diskData->FlushContext.DbgTagCount++;
|
|
#endif
|
|
|
|
InsertTailList(&diskData->FlushContext.NextList, &Irp->Tail.Overlay.ListEntry);
|
|
|
|
KeReleaseMutex(&diskData->FlushContext.Mutex, FALSE);
|
|
|
|
//
|
|
// This request will be completed by its representative
|
|
//
|
|
|
|
} else {
|
|
|
|
#if DBG
|
|
if (diskData->FlushContext.DbgTagCount < 64) {
|
|
|
|
diskData->FlushContext.DbgRefCount[diskData->FlushContext.DbgTagCount]++;
|
|
}
|
|
|
|
diskData->FlushContext.DbgSavCount += diskData->FlushContext.DbgTagCount;
|
|
diskData->FlushContext.DbgTagCount = 0;
|
|
#endif
|
|
|
|
diskData->FlushContext.NextIrp = Irp;
|
|
ASSERT(IsListEmpty(&diskData->FlushContext.NextList));
|
|
|
|
KeReleaseMutex(&diskData->FlushContext.Mutex, FALSE);
|
|
|
|
//
|
|
// Wait for the outstanding flush to complete
|
|
//
|
|
KeWaitForSingleObject(&diskData->FlushContext.Event, Executive, KernelMode, FALSE, NULL);
|
|
|
|
//
|
|
// Make this group the outstanding one and free up the next slot
|
|
//
|
|
|
|
KeWaitForMutexObject(&diskData->FlushContext.Mutex, Executive, KernelMode, FALSE, NULL);
|
|
|
|
ASSERT(IsListEmpty(&diskData->FlushContext.CurrList));
|
|
|
|
while (!IsListEmpty(&diskData->FlushContext.NextList)) {
|
|
|
|
PLIST_ENTRY listEntry = RemoveHeadList(&diskData->FlushContext.NextList);
|
|
InsertTailList(&diskData->FlushContext.CurrList, listEntry);
|
|
}
|
|
|
|
diskData->FlushContext.CurrIrp = diskData->FlushContext.NextIrp;
|
|
diskData->FlushContext.NextIrp = NULL;
|
|
|
|
KeReleaseMutex(&diskData->FlushContext.Mutex, FALSE);
|
|
|
|
//
|
|
// Send this request down to the device
|
|
//
|
|
DiskFlushDispatch(DeviceObject, &diskData->FlushContext);
|
|
}
|
|
|
|
} else {
|
|
|
|
diskData->FlushContext.CurrIrp = Irp;
|
|
ASSERT(IsListEmpty(&diskData->FlushContext.CurrList));
|
|
|
|
ASSERT(diskData->FlushContext.NextIrp == NULL);
|
|
ASSERT(IsListEmpty(&diskData->FlushContext.NextList));
|
|
|
|
KeReleaseMutex(&diskData->FlushContext.Mutex, FALSE);
|
|
|
|
//
|
|
// Send this request down to the device
|
|
//
|
|
DiskFlushDispatch(DeviceObject, &diskData->FlushContext);
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Allocate SCSI request block.
|
|
//
|
|
|
|
PSCSI_REQUEST_BLOCK srb = ExAllocatePoolWithTag(NonPagedPool,
|
|
sizeof(SCSI_REQUEST_BLOCK),
|
|
DISK_TAG_SRB);
|
|
if (srb == NULL) {
|
|
|
|
//
|
|
// Set the status and complete the request.
|
|
//
|
|
|
|
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
ClassReleaseRemoveLock(DeviceObject, Irp);
|
|
ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
RtlZeroMemory(srb, SCSI_REQUEST_BLOCK_SIZE);
|
|
|
|
//
|
|
// Write length to SRB.
|
|
//
|
|
|
|
srb->Length = SCSI_REQUEST_BLOCK_SIZE;
|
|
|
|
//
|
|
// Set timeout value and mark the request as not being a tagged request.
|
|
//
|
|
|
|
srb->TimeOutValue = fdoExtension->TimeOutValue * 4;
|
|
srb->QueueTag = SP_UNTAGGED;
|
|
srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
|
|
srb->SrbFlags = fdoExtension->SrbFlags;
|
|
|
|
//
|
|
// If the write cache is enabled then send a synchronize cache request.
|
|
//
|
|
|
|
if (TEST_FLAG(fdoExtension->DeviceFlags, DEV_WRITE_CACHE)) {
|
|
|
|
srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
|
|
srb->CdbLength = 10;
|
|
|
|
srb->Cdb[0] = SCSIOP_SYNCHRONIZE_CACHE;
|
|
|
|
status = ClassSendSrbSynchronous(DeviceObject,
|
|
srb,
|
|
NULL,
|
|
0,
|
|
TRUE);
|
|
|
|
DebugPrint((1, "DiskShutdownFlush: Synchonize cache sent. Status = %lx\n", status ));
|
|
}
|
|
|
|
//
|
|
// Unlock the device if it contains removable media
|
|
//
|
|
|
|
if (TEST_FLAG(DeviceObject->Characteristics, FILE_REMOVABLE_MEDIA))
|
|
{
|
|
PCDB cdb;
|
|
|
|
srb->CdbLength = 6;
|
|
cdb = (PVOID) srb->Cdb;
|
|
cdb->MEDIA_REMOVAL.OperationCode = SCSIOP_MEDIUM_REMOVAL;
|
|
cdb->MEDIA_REMOVAL.Prevent = FALSE;
|
|
|
|
//
|
|
// Set timeout value.
|
|
//
|
|
|
|
srb->TimeOutValue = fdoExtension->TimeOutValue;
|
|
status = ClassSendSrbSynchronous(DeviceObject,
|
|
srb,
|
|
NULL,
|
|
0,
|
|
TRUE);
|
|
|
|
DebugPrint((1, "DiskShutdownFlush: Unlock device request sent. Status = %lx\n", status ));
|
|
}
|
|
|
|
srb->CdbLength = 0;
|
|
|
|
//
|
|
// Save a few parameters in the current stack location.
|
|
//
|
|
|
|
srb->Function = SRB_FUNCTION_SHUTDOWN;
|
|
|
|
//
|
|
// Set the retry count to zero.
|
|
//
|
|
|
|
irpStack->Parameters.Others.Argument4 = (PVOID) 0;
|
|
|
|
//
|
|
// Set up IoCompletion routine address.
|
|
//
|
|
|
|
IoSetCompletionRoutine(Irp, ClassIoComplete, srb, TRUE, TRUE, TRUE);
|
|
|
|
//
|
|
// Get next stack location and
|
|
// set major function code.
|
|
//
|
|
|
|
irpStack = IoGetNextIrpStackLocation(Irp);
|
|
|
|
irpStack->MajorFunction = IRP_MJ_SCSI;
|
|
|
|
//
|
|
// Set up SRB for execute scsi request.
|
|
// Save SRB address in next stack for port driver.
|
|
//
|
|
|
|
irpStack->Parameters.Scsi.Srb = srb;
|
|
|
|
//
|
|
// Set up Irp Address.
|
|
//
|
|
|
|
srb->OriginalRequest = Irp;
|
|
|
|
//
|
|
// Call the port driver to process the request.
|
|
//
|
|
|
|
IoMarkIrpPending(Irp);
|
|
IoCallDriver(commonExtension->LowerDeviceObject, Irp);
|
|
}
|
|
|
|
return STATUS_PENDING;
|
|
}
|
|
|
|
|
|
VOID
|
|
DiskFlushDispatch(
|
|
IN PDEVICE_OBJECT Fdo,
|
|
IN PDISK_GROUP_CONTEXT FlushContext
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the handler for flush requests. It sends down a synch
|
|
cache command to the device if its cache is enabled. This is followed
|
|
by an SRB_FUNCTION_FLUSH
|
|
|
|
Arguments:
|
|
|
|
Fdo - The device object processing the flush request
|
|
FlushContext - The flush group context
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
|
|
PSCSI_REQUEST_BLOCK srb = &FlushContext->Srb;
|
|
PIO_STACK_LOCATION irpSp = NULL;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT_FDO(Fdo);
|
|
|
|
//
|
|
// Fill in the srb fields appropriately
|
|
//
|
|
RtlZeroMemory(srb, SCSI_REQUEST_BLOCK_SIZE);
|
|
|
|
srb->Length = SCSI_REQUEST_BLOCK_SIZE;
|
|
srb->TimeOutValue = fdoExt->TimeOutValue * 4;
|
|
srb->QueueTag = SP_UNTAGGED;
|
|
srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
|
|
srb->SrbFlags = fdoExt->SrbFlags;
|
|
|
|
//
|
|
// If write caching is enabled then send down a synchronize cache request
|
|
//
|
|
if (TEST_FLAG(fdoExt->DeviceFlags, DEV_WRITE_CACHE) && !TEST_FLAG(fdoExt->DeviceFlags, DEV_POWER_PROTECTED))
|
|
{
|
|
srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
|
|
srb->CdbLength = 10;
|
|
srb->Cdb[0] = SCSIOP_SYNCHRONIZE_CACHE;
|
|
|
|
ClassSendSrbSynchronous(Fdo, srb, NULL, 0, TRUE);
|
|
}
|
|
|
|
srb->Function = SRB_FUNCTION_FLUSH;
|
|
srb->CdbLength = 0;
|
|
srb->OriginalRequest = FlushContext->CurrIrp;
|
|
|
|
//
|
|
// Make sure that this srb does not get freed
|
|
//
|
|
SET_FLAG(srb->SrbFlags, SRB_CLASS_FLAGS_PERSISTANT);
|
|
|
|
//
|
|
// Make sure that this request does not get retried
|
|
//
|
|
irpSp = IoGetCurrentIrpStackLocation(FlushContext->CurrIrp);
|
|
|
|
irpSp->Parameters.Others.Argument4 = (PVOID) 0;
|
|
|
|
//
|
|
// Fill in the irp fields appropriately
|
|
//
|
|
irpSp = IoGetNextIrpStackLocation(FlushContext->CurrIrp);
|
|
|
|
irpSp->MajorFunction = IRP_MJ_SCSI;
|
|
irpSp->Parameters.Scsi.Srb = srb;
|
|
|
|
IoSetCompletionRoutine(FlushContext->CurrIrp, DiskFlushComplete, FlushContext, TRUE, TRUE, TRUE);
|
|
|
|
//
|
|
// Send down the flush request
|
|
//
|
|
IoCallDriver(((PCOMMON_DEVICE_EXTENSION)fdoExt)->LowerDeviceObject, FlushContext->CurrIrp);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
DiskFlushComplete(
|
|
IN PDEVICE_OBJECT Fdo,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This completion routine is a wrapper around ClassIoComplete. It
|
|
will complete all the flush requests that are tagged to it, set
|
|
an event to signal the next group to proceed and return
|
|
|
|
Arguments:
|
|
|
|
Fdo - The device object which requested the completion routine
|
|
Irp - The irp that is being completed
|
|
Context - The flush group context
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS if successful, an error code otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
PDISK_GROUP_CONTEXT FlushContext = Context;
|
|
NTSTATUS status;
|
|
|
|
DebugPrint((ClassDebugInfo, "DiskFlushComplete: %p %p %p\n", Fdo, Irp, FlushContext));
|
|
|
|
//
|
|
// Make sure everything is in order
|
|
//
|
|
ASSERT(Irp == FlushContext->CurrIrp);
|
|
|
|
status = ClassIoComplete(Fdo, Irp, &FlushContext->Srb);
|
|
|
|
//
|
|
// Make sure that ClassIoComplete did not decide to retry this request
|
|
//
|
|
ASSERT(status != STATUS_MORE_PROCESSING_REQUIRED);
|
|
|
|
//
|
|
// Complete the flush requests tagged to this one
|
|
//
|
|
|
|
while (!IsListEmpty(&FlushContext->CurrList)) {
|
|
|
|
PLIST_ENTRY listEntry = RemoveHeadList(&FlushContext->CurrList);
|
|
PIRP tempIrp = CONTAINING_RECORD(listEntry, IRP, Tail.Overlay.ListEntry);
|
|
|
|
InitializeListHead(&tempIrp->Tail.Overlay.ListEntry);
|
|
tempIrp->IoStatus = Irp->IoStatus;
|
|
|
|
ClassReleaseRemoveLock(Fdo, tempIrp);
|
|
ClassCompleteRequest(Fdo, tempIrp, IO_NO_INCREMENT);
|
|
}
|
|
|
|
//
|
|
// Notify the next group's representative that it may go ahead now
|
|
//
|
|
KeSetEvent(&FlushContext->Event, IO_NO_INCREMENT, FALSE);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
DiskModeSelect(
|
|
IN PDEVICE_OBJECT Fdo,
|
|
IN PCHAR ModeSelectBuffer,
|
|
IN ULONG Length,
|
|
IN BOOLEAN SavePage
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine sends a mode select command.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Supplies the device object associated with this request.
|
|
|
|
ModeSelectBuffer - Supplies a buffer containing the page data.
|
|
|
|
Length - Supplies the length in bytes of the mode select buffer.
|
|
|
|
SavePage - Indicates that parameters should be written to disk.
|
|
|
|
Return Value:
|
|
|
|
Length of the transferred data is returned.
|
|
|
|
--*/
|
|
|
|
{
|
|
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
|
|
PCDB cdb;
|
|
SCSI_REQUEST_BLOCK srb = {0};
|
|
ULONG retries = 1;
|
|
ULONG length2;
|
|
NTSTATUS status;
|
|
PULONG buffer;
|
|
PMODE_PARAMETER_BLOCK blockDescriptor;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT_FDO(Fdo);
|
|
|
|
length2 = Length + sizeof(MODE_PARAMETER_HEADER) + sizeof(MODE_PARAMETER_BLOCK);
|
|
|
|
//
|
|
// Allocate buffer for mode select header, block descriptor, and mode page.
|
|
//
|
|
|
|
buffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned,
|
|
length2,
|
|
DISK_TAG_MODE_DATA);
|
|
|
|
if(buffer == NULL) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlZeroMemory(buffer, length2);
|
|
|
|
//
|
|
// Set length in header to size of mode page.
|
|
//
|
|
|
|
((PMODE_PARAMETER_HEADER)buffer)->BlockDescriptorLength = sizeof(MODE_PARAMETER_BLOCK);
|
|
|
|
(PULONG)blockDescriptor = (buffer + 1);
|
|
|
|
//
|
|
// Set size
|
|
//
|
|
|
|
blockDescriptor->BlockLength[1]=0x02;
|
|
|
|
//
|
|
// Copy mode page to buffer.
|
|
//
|
|
|
|
RtlCopyMemory(buffer + 3, ModeSelectBuffer, Length);
|
|
|
|
//
|
|
// Build the MODE SELECT CDB.
|
|
//
|
|
|
|
srb.CdbLength = 6;
|
|
cdb = (PCDB)srb.Cdb;
|
|
|
|
//
|
|
// Set timeout value from device extension.
|
|
//
|
|
|
|
srb.TimeOutValue = fdoExtension->TimeOutValue * 2;
|
|
|
|
cdb->MODE_SELECT.OperationCode = SCSIOP_MODE_SELECT;
|
|
cdb->MODE_SELECT.SPBit = SavePage;
|
|
cdb->MODE_SELECT.PFBit = 1;
|
|
cdb->MODE_SELECT.ParameterListLength = (UCHAR)(length2);
|
|
|
|
Retry:
|
|
|
|
status = ClassSendSrbSynchronous(Fdo,
|
|
&srb,
|
|
buffer,
|
|
length2,
|
|
TRUE);
|
|
|
|
if (status == STATUS_VERIFY_REQUIRED) {
|
|
|
|
//
|
|
// Routine ClassSendSrbSynchronous does not retry requests returned with
|
|
// this status.
|
|
//
|
|
|
|
if (retries--) {
|
|
|
|
//
|
|
// Retry request.
|
|
//
|
|
|
|
goto Retry;
|
|
}
|
|
|
|
} else if (SRB_STATUS(srb.SrbStatus) == SRB_STATUS_DATA_OVERRUN) {
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
ExFreePool(buffer);
|
|
|
|
return status;
|
|
} // end DiskModeSelect()
|
|
|
|
|
|
//
|
|
// This routine is structured as a work-item routine
|
|
//
|
|
VOID
|
|
DisableWriteCache(
|
|
IN PDEVICE_OBJECT Fdo,
|
|
IN PIO_WORKITEM WorkItem
|
|
)
|
|
|
|
{
|
|
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
|
|
DISK_CACHE_INFORMATION cacheInfo = { 0 };
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT_FDO(Fdo);
|
|
|
|
status = DiskGetCacheInformation(fdoExtension, &cacheInfo);
|
|
|
|
if (NT_SUCCESS(status) && (cacheInfo.WriteCacheEnabled == TRUE)) {
|
|
|
|
cacheInfo.WriteCacheEnabled = FALSE;
|
|
|
|
DiskSetCacheInformation(fdoExtension, &cacheInfo);
|
|
}
|
|
|
|
IoFreeWorkItem(WorkItem);
|
|
}
|
|
|
|
|
|
//
|
|
// This routine is structured as a work-item routine
|
|
//
|
|
VOID
|
|
DiskIoctlVerify(
|
|
IN PDEVICE_OBJECT Fdo,
|
|
IN PDISK_VERIFY_WORKITEM_CONTEXT Context
|
|
)
|
|
|
|
{
|
|
PIRP Irp = Context->Irp;
|
|
PFUNCTIONAL_DEVICE_EXTENSION FdoExtension = Fdo->DeviceExtension;
|
|
PDISK_DATA DiskData = (PDISK_DATA)FdoExtension->CommonExtension.DriverData;
|
|
PVERIFY_INFORMATION verifyInfo = Irp->AssociatedIrp.SystemBuffer;
|
|
PSCSI_REQUEST_BLOCK Srb = Context->Srb;
|
|
PCDB Cdb = (PCDB)Srb->Cdb;
|
|
LARGE_INTEGER byteOffset;
|
|
ULONG sectorOffset;
|
|
USHORT sectorCount;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(FdoExtension->CommonExtension.IsFdo);
|
|
|
|
//
|
|
// We don't need to hold on to this memory as
|
|
// the following operation may take some time
|
|
//
|
|
|
|
IoFreeWorkItem(Context->WorkItem);
|
|
|
|
DebugPrint((1, "Disk.DiskIoctlVerify: Spliting up the request\n"));
|
|
|
|
//
|
|
// Add disk offset to starting the sector
|
|
//
|
|
|
|
byteOffset.QuadPart = FdoExtension->CommonExtension.StartingOffset.QuadPart +
|
|
verifyInfo->StartingOffset.QuadPart;
|
|
|
|
//
|
|
// Convert byte offset to the sector offset
|
|
//
|
|
|
|
sectorOffset = (ULONG)(byteOffset.QuadPart >> FdoExtension->SectorShift);
|
|
|
|
//
|
|
// Convert ULONG byte count to USHORT sector count.
|
|
//
|
|
|
|
sectorCount = (USHORT)(verifyInfo->Length >> FdoExtension->SectorShift);
|
|
|
|
//
|
|
// Make sure that all previous verify requests have indeed completed
|
|
// This greatly reduces the possibility of a Denial-of-Service attack
|
|
//
|
|
|
|
KeWaitForMutexObject(&DiskData->VerifyMutex,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
|
|
while (NT_SUCCESS(status) && (sectorCount != 0))
|
|
{
|
|
USHORT numSectors = min(sectorCount, MAX_SECTORS_PER_VERIFY);
|
|
|
|
RtlZeroMemory(Srb, SCSI_REQUEST_BLOCK_SIZE);
|
|
|
|
Srb->CdbLength = 10;
|
|
|
|
Cdb->CDB10.OperationCode = SCSIOP_VERIFY;
|
|
|
|
//
|
|
// Move little endian values into CDB in big endian format
|
|
//
|
|
|
|
Cdb->CDB10.LogicalBlockByte0 = ((PFOUR_BYTE)§orOffset)->Byte3;
|
|
Cdb->CDB10.LogicalBlockByte1 = ((PFOUR_BYTE)§orOffset)->Byte2;
|
|
Cdb->CDB10.LogicalBlockByte2 = ((PFOUR_BYTE)§orOffset)->Byte1;
|
|
Cdb->CDB10.LogicalBlockByte3 = ((PFOUR_BYTE)§orOffset)->Byte0;
|
|
|
|
Cdb->CDB10.TransferBlocksMsb = ((PFOUR_BYTE)&numSectors)->Byte1;
|
|
Cdb->CDB10.TransferBlocksLsb = ((PFOUR_BYTE)&numSectors)->Byte0;
|
|
|
|
//
|
|
// Calculate the request timeout value based
|
|
// on the number of sectors being verified
|
|
//
|
|
|
|
Srb->TimeOutValue = ((numSectors + 0x7F) >> 7) * FdoExtension->TimeOutValue;
|
|
|
|
status = ClassSendSrbSynchronous(Fdo,
|
|
Srb,
|
|
NULL,
|
|
0,
|
|
FALSE);
|
|
|
|
ASSERT(status != STATUS_NONEXISTENT_SECTOR);
|
|
|
|
sectorCount -= numSectors;
|
|
sectorOffset += numSectors;
|
|
}
|
|
|
|
KeReleaseMutex(&DiskData->VerifyMutex, FALSE);
|
|
|
|
Irp->IoStatus.Status = status;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
ClassReleaseRemoveLock(Fdo, Irp);
|
|
ClassCompleteRequest(Fdo, Irp, IO_NO_INCREMENT);
|
|
|
|
ExFreePool(Srb);
|
|
ExFreePool(Context);
|
|
}
|
|
|
|
|
|
VOID
|
|
DiskFdoProcessError(
|
|
PDEVICE_OBJECT Fdo,
|
|
PSCSI_REQUEST_BLOCK Srb,
|
|
NTSTATUS *Status,
|
|
BOOLEAN *Retry
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine checks the type of error. If the error indicates an underrun
|
|
then indicate the request should be retried.
|
|
|
|
Arguments:
|
|
|
|
Fdo - Supplies a pointer to the functional device object.
|
|
|
|
Srb - Supplies a pointer to the failing Srb.
|
|
|
|
Status - Status with which the IRP will be completed.
|
|
|
|
Retry - Indication of whether the request will be retried.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
|
|
PCDB cdb = (PCDB)(Srb->Cdb);
|
|
|
|
ASSERT(fdoExtension->CommonExtension.IsFdo);
|
|
|
|
if (*Status == STATUS_DATA_OVERRUN &&
|
|
( cdb->CDB10.OperationCode == SCSIOP_WRITE ||
|
|
cdb->CDB10.OperationCode == SCSIOP_READ)) {
|
|
|
|
*Retry = TRUE;
|
|
|
|
//
|
|
// Update the error count for the device.
|
|
//
|
|
|
|
fdoExtension->ErrorCount++;
|
|
|
|
} else if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_ERROR &&
|
|
Srb->ScsiStatus == SCSISTAT_BUSY) {
|
|
|
|
//
|
|
// a disk drive should never be busy this long. Reset the scsi bus
|
|
// maybe this will clear the condition.
|
|
//
|
|
|
|
ResetBus(Fdo);
|
|
|
|
//
|
|
// Update the error count for the device.
|
|
//
|
|
|
|
fdoExtension->ErrorCount++;
|
|
|
|
} else {
|
|
|
|
BOOLEAN invalidatePartitionTable = FALSE;
|
|
BOOLEAN bForceBusRelations = FALSE;
|
|
|
|
//
|
|
// See if this might indicate that something on the drive has changed.
|
|
//
|
|
|
|
if ((Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) &&
|
|
(Srb->SenseInfoBufferLength >=
|
|
FIELD_OFFSET(SENSE_DATA, CommandSpecificInformation))) {
|
|
|
|
PSENSE_DATA senseBuffer = Srb->SenseInfoBuffer;
|
|
ULONG senseKey = senseBuffer->SenseKey & 0xf;
|
|
ULONG asc = senseBuffer->AdditionalSenseCode;
|
|
ULONG ascq = senseBuffer->AdditionalSenseCodeQualifier;
|
|
|
|
switch (senseKey) {
|
|
|
|
case SCSI_SENSE_ILLEGAL_REQUEST: {
|
|
|
|
switch (asc) {
|
|
|
|
case SCSI_ADSENSE_ILLEGAL_BLOCK: {
|
|
if ((cdb->CDB10.OperationCode == SCSIOP_READ) ||
|
|
(cdb->CDB10.OperationCode == SCSIOP_WRITE)
|
|
) {
|
|
|
|
// should only retry these if the request was
|
|
// truly within our expected size. however,
|
|
// this check was already done in the
|
|
// ReadWriteVerification routine.
|
|
|
|
/*
|
|
* Fujitsu IDE drives have been observed to
|
|
* return this error transiently for a legal LBA;
|
|
* manual retry in the debugger then works, so
|
|
* there is a good chance that a programmed retry
|
|
* will also work.
|
|
*/
|
|
*Retry = TRUE;
|
|
|
|
}
|
|
break;
|
|
}
|
|
|
|
case SCSI_ADSENSE_INVALID_CDB:
|
|
{
|
|
//
|
|
// Look to see if this is an Io request with the ForceUnitAccess flag set
|
|
//
|
|
if ((cdb->CDB10.OperationCode == SCSIOP_WRITE) && (cdb->CDB10.ForceUnitAccess))
|
|
{
|
|
PDISK_DATA diskData = (PDISK_DATA)fdoExtension->CommonExtension.DriverData;
|
|
|
|
if (diskData->WriteCacheOverride == DiskWriteCacheEnable)
|
|
{
|
|
//
|
|
// The user has explicitly requested that write caching be turned on
|
|
//
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Turn off write caching on this device. This is so that future
|
|
// critical requests need not be sent down with ForceUnitAccess
|
|
//
|
|
PIO_WORKITEM workItem = IoAllocateWorkItem(Fdo);
|
|
|
|
if (workItem)
|
|
{
|
|
IoQueueWorkItem(workItem, DisableWriteCache, CriticalWorkQueue, workItem);
|
|
}
|
|
}
|
|
|
|
SET_FLAG(fdoExtension->ScanForSpecialFlags, CLASS_SPECIAL_FUA_NOT_SUPPORTED);
|
|
|
|
cdb->CDB10.ForceUnitAccess = FALSE;
|
|
*Retry = TRUE;
|
|
}
|
|
|
|
break;
|
|
}
|
|
} // end switch(asc)
|
|
break;
|
|
}
|
|
|
|
case SCSI_SENSE_NOT_READY: {
|
|
|
|
switch (asc) {
|
|
case SCSI_ADSENSE_LUN_NOT_READY: {
|
|
switch (ascq) {
|
|
case SCSI_SENSEQ_BECOMING_READY:
|
|
case SCSI_SENSEQ_MANUAL_INTERVENTION_REQUIRED:
|
|
case SCSI_SENSEQ_CAUSE_NOT_REPORTABLE: {
|
|
invalidatePartitionTable = TRUE;
|
|
break;
|
|
}
|
|
} // end switch(ascq)
|
|
break;
|
|
}
|
|
|
|
case SCSI_ADSENSE_NO_MEDIA_IN_DEVICE: {
|
|
invalidatePartitionTable = TRUE;
|
|
break;
|
|
}
|
|
} // end switch(asc)
|
|
break;
|
|
}
|
|
|
|
case SCSI_SENSE_MEDIUM_ERROR: {
|
|
invalidatePartitionTable = TRUE;
|
|
break;
|
|
}
|
|
|
|
case SCSI_SENSE_HARDWARE_ERROR: {
|
|
invalidatePartitionTable = TRUE;
|
|
break;
|
|
}
|
|
|
|
case SCSI_SENSE_UNIT_ATTENTION:
|
|
{
|
|
invalidatePartitionTable = TRUE;
|
|
|
|
switch (senseBuffer->AdditionalSenseCode)
|
|
{
|
|
case SCSI_ADSENSE_MEDIUM_CHANGED:
|
|
{
|
|
bForceBusRelations = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case SCSI_SENSE_RECOVERED_ERROR: {
|
|
invalidatePartitionTable = TRUE;
|
|
break;
|
|
}
|
|
|
|
} // end switch(senseKey)
|
|
} else {
|
|
|
|
//
|
|
// On any exceptional scsi condition which might indicate that the
|
|
// device was changed we will flush out the state of the partition
|
|
// table.
|
|
//
|
|
|
|
switch (SRB_STATUS(Srb->SrbStatus)) {
|
|
case SRB_STATUS_INVALID_LUN:
|
|
case SRB_STATUS_INVALID_TARGET_ID:
|
|
case SRB_STATUS_NO_DEVICE:
|
|
case SRB_STATUS_NO_HBA:
|
|
case SRB_STATUS_INVALID_PATH_ID:
|
|
case SRB_STATUS_COMMAND_TIMEOUT:
|
|
case SRB_STATUS_TIMEOUT:
|
|
case SRB_STATUS_SELECTION_TIMEOUT:
|
|
case SRB_STATUS_REQUEST_FLUSHED:
|
|
case SRB_STATUS_UNEXPECTED_BUS_FREE:
|
|
case SRB_STATUS_PARITY_ERROR:
|
|
{
|
|
invalidatePartitionTable = TRUE;
|
|
break;
|
|
}
|
|
|
|
case SRB_STATUS_ERROR:
|
|
{
|
|
if (Srb->ScsiStatus == SCSISTAT_RESERVATION_CONFLICT)
|
|
{
|
|
invalidatePartitionTable = TRUE;
|
|
}
|
|
|
|
break;
|
|
}
|
|
} // end switch(Srb->SrbStatus)
|
|
}
|
|
|
|
if (invalidatePartitionTable)
|
|
{
|
|
if (TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA) && (fdoExtension->CommonExtension.ChildList))
|
|
{
|
|
//
|
|
// Locate the PDO associated with this removable device
|
|
//
|
|
|
|
PDEVICE_OBJECT Pdo = (fdoExtension->CommonExtension.ChildList)->CommonExtension.DeviceObject;
|
|
|
|
if ((ClassGetVpb(Pdo) != NULL) && (ClassGetVpb(Pdo)->Flags & VPB_MOUNTED))
|
|
{
|
|
//
|
|
// Set a flag to inform the filesystem
|
|
// that this volume needs verification
|
|
//
|
|
|
|
SET_FLAG(Pdo->Flags, DO_VERIFY_VOLUME);
|
|
}
|
|
}
|
|
|
|
if (DiskInvalidatePartitionTable(fdoExtension, FALSE) || bForceBusRelations)
|
|
{
|
|
IoInvalidateDeviceRelations(fdoExtension->LowerPdo, BusRelations);
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
DiskSetSpecialHacks(
|
|
IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
|
|
IN ULONG_PTR Data
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function checks to see if an SCSI logical unit requires speical
|
|
flags to be set.
|
|
|
|
Arguments:
|
|
|
|
Fdo - Supplies the device object to be tested.
|
|
|
|
InquiryData - Supplies the inquiry data returned by the device of interest.
|
|
|
|
AdapterDescriptor - Supplies the capabilities of the device object.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_OBJECT fdo = FdoExtension->DeviceObject;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugPrint((1, "Disk SetSpecialHacks, Setting Hacks %p\n", Data));
|
|
|
|
//
|
|
// Found a listed controller. Determine what must be done.
|
|
//
|
|
|
|
if (TEST_FLAG(Data, HackDisableTaggedQueuing)) {
|
|
|
|
//
|
|
// Disable tagged queuing.
|
|
//
|
|
|
|
CLEAR_FLAG(FdoExtension->SrbFlags, SRB_FLAGS_QUEUE_ACTION_ENABLE);
|
|
}
|
|
|
|
if (TEST_FLAG(Data, HackDisableSynchronousTransfers)) {
|
|
|
|
//
|
|
// Disable synchronous data transfers.
|
|
//
|
|
|
|
SET_FLAG(FdoExtension->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
|
|
|
|
}
|
|
|
|
if (TEST_FLAG(Data, HackDisableSpinDown)) {
|
|
|
|
//
|
|
// Disable spinning down of drives.
|
|
//
|
|
|
|
SET_FLAG(FdoExtension->ScanForSpecialFlags,
|
|
CLASS_SPECIAL_DISABLE_SPIN_DOWN);
|
|
|
|
}
|
|
|
|
if (TEST_FLAG(Data, HackDisableWriteCache)) {
|
|
|
|
//
|
|
// Disable the drive's write cache
|
|
//
|
|
|
|
SET_FLAG(FdoExtension->ScanForSpecialFlags,
|
|
CLASS_SPECIAL_DISABLE_WRITE_CACHE);
|
|
|
|
}
|
|
|
|
if (TEST_FLAG(Data, HackCauseNotReportableHack)) {
|
|
|
|
SET_FLAG(FdoExtension->ScanForSpecialFlags,
|
|
CLASS_SPECIAL_CAUSE_NOT_REPORTABLE_HACK);
|
|
}
|
|
|
|
if (TEST_FLAG(fdo->Characteristics, FILE_REMOVABLE_MEDIA) &&
|
|
TEST_FLAG(Data, HackRequiresStartUnitCommand)
|
|
) {
|
|
|
|
//
|
|
// this is a list of vendors who require the START_UNIT command
|
|
//
|
|
|
|
DebugPrint((1, "DiskScanForSpecial (%p) => This unit requires "
|
|
" START_UNITS\n", fdo));
|
|
SET_FLAG(FdoExtension->DeviceFlags, DEV_SAFE_START_UNIT);
|
|
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
ResetBus(
|
|
IN PDEVICE_OBJECT Fdo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This command sends a reset bus command to the SCSI port driver.
|
|
|
|
Arguments:
|
|
|
|
Fdo - The functional device object for the logical unit with hardware problem.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION irpStack;
|
|
PIRP irp;
|
|
|
|
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
|
|
PSCSI_REQUEST_BLOCK srb;
|
|
PCOMPLETION_CONTEXT context;
|
|
|
|
DebugPrint((1, "Disk ResetBus: Sending reset bus request to port driver.\n"));
|
|
|
|
//
|
|
// Allocate Srb from nonpaged pool.
|
|
//
|
|
|
|
context = ExAllocatePoolWithTag(NonPagedPool,
|
|
sizeof(COMPLETION_CONTEXT),
|
|
DISK_TAG_CCONTEXT);
|
|
|
|
if(context == NULL) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Save the device object in the context for use by the completion
|
|
// routine.
|
|
//
|
|
|
|
context->DeviceObject = Fdo;
|
|
srb = &context->Srb;
|
|
|
|
//
|
|
// Zero out srb.
|
|
//
|
|
|
|
RtlZeroMemory(srb, SCSI_REQUEST_BLOCK_SIZE);
|
|
|
|
//
|
|
// Write length to SRB.
|
|
//
|
|
|
|
srb->Length = SCSI_REQUEST_BLOCK_SIZE;
|
|
|
|
srb->Function = SRB_FUNCTION_RESET_BUS;
|
|
|
|
//
|
|
// Build the asynchronous request to be sent to the port driver.
|
|
// Since this routine is called from a DPC the IRP should always be
|
|
// available.
|
|
//
|
|
|
|
irp = IoAllocateIrp(Fdo->StackSize, FALSE);
|
|
|
|
if(irp == NULL) {
|
|
ExFreePool(context);
|
|
return;
|
|
}
|
|
|
|
ClassAcquireRemoveLock(Fdo, irp);
|
|
|
|
IoSetCompletionRoutine(irp,
|
|
(PIO_COMPLETION_ROUTINE)ClassAsynchronousCompletion,
|
|
context,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE);
|
|
|
|
irpStack = IoGetNextIrpStackLocation(irp);
|
|
|
|
irpStack->MajorFunction = IRP_MJ_SCSI;
|
|
|
|
srb->OriginalRequest = irp;
|
|
|
|
//
|
|
// Store the SRB address in next stack for port driver.
|
|
//
|
|
|
|
irpStack->Parameters.Scsi.Srb = srb;
|
|
|
|
//
|
|
// Call the port driver with the IRP.
|
|
//
|
|
|
|
IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp);
|
|
|
|
return;
|
|
|
|
} // end ResetBus()
|
|
|
|
|
|
NTSTATUS
|
|
DiskQueryPnpCapabilities(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PDEVICE_CAPABILITIES Capabilities
|
|
)
|
|
|
|
{
|
|
PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
|
|
PDISK_DATA diskData = commonExtension->DriverData;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(DeviceObject);
|
|
ASSERT(Capabilities);
|
|
|
|
if(commonExtension->IsFdo) {
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
} else {
|
|
|
|
PPHYSICAL_DEVICE_EXTENSION physicalExtension =
|
|
DeviceObject->DeviceExtension;
|
|
|
|
Capabilities->SilentInstall = 1;
|
|
Capabilities->RawDeviceOK = 1;
|
|
Capabilities->Address = commonExtension->PartitionNumber;
|
|
|
|
if(!TEST_FLAG(DeviceObject->Characteristics, FILE_REMOVABLE_MEDIA)) {
|
|
|
|
//
|
|
// Media's not removable, deviceId/DeviceInstance should be
|
|
// globally unique.
|
|
//
|
|
|
|
Capabilities->UniqueID = 1;
|
|
} else {
|
|
Capabilities->UniqueID = 0;
|
|
}
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
DiskGetCacheInformation(
|
|
IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
|
|
IN PDISK_CACHE_INFORMATION CacheInfo
|
|
)
|
|
|
|
{
|
|
PMODE_PARAMETER_HEADER modeData;
|
|
PMODE_CACHING_PAGE pageData;
|
|
|
|
ULONG length;
|
|
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
modeData = ExAllocatePoolWithTag(NonPagedPoolCacheAligned,
|
|
MODE_DATA_SIZE,
|
|
DISK_TAG_DISABLE_CACHE);
|
|
|
|
if (modeData == NULL) {
|
|
|
|
DebugPrint((1, "DiskGetSetCacheInformation: Unable to allocate mode "
|
|
"data buffer\n"));
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlZeroMemory(modeData, MODE_DATA_SIZE);
|
|
|
|
length = ClassModeSense(FdoExtension->DeviceObject,
|
|
(PUCHAR) modeData,
|
|
MODE_DATA_SIZE,
|
|
MODE_SENSE_RETURN_ALL);
|
|
|
|
if (length < sizeof(MODE_PARAMETER_HEADER)) {
|
|
|
|
//
|
|
// Retry the request in case of a check condition.
|
|
//
|
|
|
|
length = ClassModeSense(FdoExtension->DeviceObject,
|
|
(PUCHAR) modeData,
|
|
MODE_DATA_SIZE,
|
|
MODE_SENSE_RETURN_ALL);
|
|
|
|
if (length < sizeof(MODE_PARAMETER_HEADER)) {
|
|
|
|
|
|
DebugPrint((1, "Disk.DisableWriteCache: Mode Sense failed\n"));
|
|
|
|
ExFreePool(modeData);
|
|
return STATUS_IO_DEVICE_ERROR;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the length is greater than length indicated by the mode data reset
|
|
// the data to the mode data.
|
|
//
|
|
|
|
if (length > (ULONG) (modeData->ModeDataLength + 1)) {
|
|
length = modeData->ModeDataLength + 1;
|
|
}
|
|
|
|
//
|
|
// Check to see if the write cache is enabled.
|
|
//
|
|
|
|
pageData = ClassFindModePage((PUCHAR) modeData,
|
|
length,
|
|
MODE_PAGE_CACHING,
|
|
TRUE);
|
|
|
|
//
|
|
// Check if valid caching page exists.
|
|
//
|
|
|
|
if (pageData == NULL) {
|
|
ExFreePool(modeData);
|
|
return STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
//
|
|
// Copy the parameters over.
|
|
//
|
|
|
|
RtlZeroMemory(CacheInfo, sizeof(DISK_CACHE_INFORMATION));
|
|
|
|
CacheInfo->ParametersSavable = pageData->PageSavable;
|
|
|
|
CacheInfo->ReadCacheEnabled = !(pageData->ReadDisableCache);
|
|
CacheInfo->WriteCacheEnabled = pageData->WriteCacheEnable;
|
|
|
|
|
|
//
|
|
// Translate the values in the mode page into the ones defined in
|
|
// ntdddisk.h.
|
|
//
|
|
|
|
CacheInfo->ReadRetentionPriority =
|
|
TRANSLATE_RETENTION_PRIORITY(pageData->ReadRetensionPriority);
|
|
CacheInfo->WriteRetentionPriority =
|
|
TRANSLATE_RETENTION_PRIORITY(pageData->WriteRetensionPriority);
|
|
|
|
CacheInfo->DisablePrefetchTransferLength =
|
|
((pageData->DisablePrefetchTransfer[0] << 8) +
|
|
pageData->DisablePrefetchTransfer[1]);
|
|
|
|
CacheInfo->ScalarPrefetch.Minimum =
|
|
((pageData->MinimumPrefetch[0] << 8) + pageData->MinimumPrefetch[1]);
|
|
|
|
CacheInfo->ScalarPrefetch.Maximum =
|
|
((pageData->MaximumPrefetch[0] << 8) + pageData->MaximumPrefetch[1]);
|
|
|
|
if(pageData->MultiplicationFactor) {
|
|
CacheInfo->PrefetchScalar = TRUE;
|
|
CacheInfo->ScalarPrefetch.MaximumBlocks =
|
|
((pageData->MaximumPrefetchCeiling[0] << 8) +
|
|
pageData->MaximumPrefetchCeiling[1]);
|
|
}
|
|
|
|
ExFreePool(modeData);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
DiskSetCacheInformation(
|
|
IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
|
|
IN PDISK_CACHE_INFORMATION CacheInfo
|
|
)
|
|
{
|
|
PMODE_PARAMETER_HEADER modeData;
|
|
ULONG length;
|
|
PMODE_CACHING_PAGE pageData;
|
|
ULONG i;
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
modeData = ExAllocatePoolWithTag(NonPagedPoolCacheAligned,
|
|
MODE_DATA_SIZE,
|
|
DISK_TAG_DISABLE_CACHE);
|
|
|
|
if (modeData == NULL) {
|
|
|
|
DebugPrint((1, "DiskSetCacheInformation: Unable to allocate mode "
|
|
"data buffer\n"));
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlZeroMemory(modeData, MODE_DATA_SIZE);
|
|
|
|
length = ClassModeSense(FdoExtension->DeviceObject,
|
|
(PUCHAR) modeData,
|
|
MODE_DATA_SIZE,
|
|
MODE_PAGE_CACHING);
|
|
|
|
if (length < sizeof(MODE_PARAMETER_HEADER)) {
|
|
|
|
//
|
|
// Retry the request in case of a check condition.
|
|
//
|
|
|
|
length = ClassModeSense(FdoExtension->DeviceObject,
|
|
(PUCHAR) modeData,
|
|
MODE_DATA_SIZE,
|
|
MODE_PAGE_CACHING);
|
|
|
|
if (length < sizeof(MODE_PARAMETER_HEADER)) {
|
|
|
|
|
|
DebugPrint((1, "Disk.DisableWriteCache: Mode Sense failed\n"));
|
|
|
|
ExFreePool(modeData);
|
|
return STATUS_IO_DEVICE_ERROR;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the length is greater than length indicated by the mode data reset
|
|
// the data to the mode data.
|
|
//
|
|
|
|
if (length > (ULONG) (modeData->ModeDataLength + 1)) {
|
|
length = modeData->ModeDataLength + 1;
|
|
}
|
|
|
|
//
|
|
// Check to see if the write cache is enabled.
|
|
//
|
|
|
|
pageData = ClassFindModePage((PUCHAR) modeData,
|
|
length,
|
|
MODE_PAGE_CACHING,
|
|
TRUE);
|
|
|
|
//
|
|
// Check if valid caching page exists.
|
|
//
|
|
|
|
if (pageData == NULL) {
|
|
ExFreePool(modeData);
|
|
return STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
//
|
|
// Don't touch any of the normal parameters - not all drives actually
|
|
// use the correct size of caching mode page. Just change the things
|
|
// which the user could have modified.
|
|
//
|
|
|
|
pageData->PageSavable = FALSE;
|
|
|
|
pageData->ReadDisableCache = !(CacheInfo->ReadCacheEnabled);
|
|
pageData->MultiplicationFactor = CacheInfo->PrefetchScalar;
|
|
pageData->WriteCacheEnable = CacheInfo->WriteCacheEnabled;
|
|
|
|
pageData->WriteRetensionPriority = (UCHAR)
|
|
TRANSLATE_RETENTION_PRIORITY(CacheInfo->WriteRetentionPriority);
|
|
pageData->ReadRetensionPriority = (UCHAR)
|
|
TRANSLATE_RETENTION_PRIORITY(CacheInfo->ReadRetentionPriority);
|
|
|
|
pageData->DisablePrefetchTransfer[0] =
|
|
(UCHAR) (CacheInfo->DisablePrefetchTransferLength >> 8);
|
|
pageData->DisablePrefetchTransfer[1] =
|
|
(UCHAR) (CacheInfo->DisablePrefetchTransferLength & 0x00ff);
|
|
|
|
pageData->MinimumPrefetch[0] =
|
|
(UCHAR) (CacheInfo->ScalarPrefetch.Minimum >> 8);
|
|
pageData->MinimumPrefetch[1] =
|
|
(UCHAR) (CacheInfo->ScalarPrefetch.Minimum & 0x00ff);
|
|
|
|
pageData->MaximumPrefetch[0] =
|
|
(UCHAR) (CacheInfo->ScalarPrefetch.Maximum >> 8);
|
|
pageData->MaximumPrefetch[1] =
|
|
(UCHAR) (CacheInfo->ScalarPrefetch.Maximum & 0x00ff);
|
|
|
|
if(pageData->MultiplicationFactor) {
|
|
|
|
pageData->MaximumPrefetchCeiling[0] =
|
|
(UCHAR) (CacheInfo->ScalarPrefetch.MaximumBlocks >> 8);
|
|
pageData->MaximumPrefetchCeiling[1] =
|
|
(UCHAR) (CacheInfo->ScalarPrefetch.MaximumBlocks & 0x00ff);
|
|
}
|
|
|
|
//
|
|
// We will attempt (twice) to issue the mode select with the page.
|
|
//
|
|
|
|
for (i = 0; i < 2; i++) {
|
|
|
|
status = DiskModeSelect(FdoExtension->DeviceObject,
|
|
(PUCHAR) pageData,
|
|
(pageData->PageLength + 2),
|
|
CacheInfo->ParametersSavable);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
if (CacheInfo->WriteCacheEnabled)
|
|
{
|
|
SET_FLAG(FdoExtension->DeviceFlags, DEV_WRITE_CACHE);
|
|
}
|
|
else
|
|
{
|
|
CLEAR_FLAG(FdoExtension->DeviceFlags, DEV_WRITE_CACHE);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
//
|
|
// We were unable to modify the disk write cache setting
|
|
//
|
|
|
|
SET_FLAG(FdoExtension->ScanForSpecialFlags, CLASS_SPECIAL_MODIFY_CACHE_UNSUCCESSFUL);
|
|
}
|
|
|
|
ExFreePool(modeData);
|
|
return status;
|
|
}
|
|
|
|
|
|
VOID
|
|
DiskLogCacheInformation(
|
|
IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
|
|
IN PDISK_CACHE_INFORMATION CacheInfo,
|
|
IN NTSTATUS Status
|
|
)
|
|
{
|
|
PIO_ERROR_LOG_PACKET logEntry = NULL;
|
|
|
|
PAGED_CODE();
|
|
|
|
logEntry = IoAllocateErrorLogEntry(FdoExtension->DeviceObject, sizeof(IO_ERROR_LOG_PACKET) + (4 * sizeof(ULONG)));
|
|
|
|
if (logEntry != NULL)
|
|
{
|
|
PDISK_DATA diskData = FdoExtension->CommonExtension.DriverData;
|
|
BOOLEAN bIsEnabled = TEST_FLAG(FdoExtension->DeviceFlags, DEV_WRITE_CACHE);
|
|
|
|
logEntry->FinalStatus = Status;
|
|
logEntry->ErrorCode = (bIsEnabled) ? IO_WRITE_CACHE_ENABLED : IO_WRITE_CACHE_DISABLED;
|
|
logEntry->SequenceNumber = 0;
|
|
logEntry->MajorFunctionCode = IRP_MJ_SCSI;
|
|
logEntry->IoControlCode = 0;
|
|
logEntry->RetryCount = 0;
|
|
logEntry->UniqueErrorValue = 0x1;
|
|
logEntry->DumpDataSize = 4;
|
|
|
|
logEntry->DumpData[0] = diskData->ScsiAddress.PathId;
|
|
logEntry->DumpData[1] = diskData->ScsiAddress.TargetId;
|
|
logEntry->DumpData[2] = diskData->ScsiAddress.Lun;
|
|
logEntry->DumpData[3] = CacheInfo->WriteCacheEnabled;
|
|
|
|
//
|
|
// Write the error log packet.
|
|
//
|
|
|
|
IoWriteErrorLogEntry(logEntry);
|
|
}
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
DiskIoctlGetCacheSetting(
|
|
IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine services IOCTL_DISK_GET_CACHE_SETTING. It looks to
|
|
see if there are any issues with the disk cache and whether the
|
|
user had previously indicated that the cache is power-protected
|
|
|
|
Arguments:
|
|
|
|
Fdo - The functional device object processing the request
|
|
Irp - The ioctl to be processed
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS if successful, an error code otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (irpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(DISK_CACHE_SETTING))
|
|
{
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
else
|
|
{
|
|
PDISK_CACHE_SETTING cacheSetting = (PDISK_CACHE_SETTING)Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
cacheSetting->Version = sizeof(DISK_CACHE_SETTING);
|
|
cacheSetting->State = DiskCacheNormal;
|
|
|
|
//
|
|
// Determine whether it is safe to turn on the cache
|
|
//
|
|
if (TEST_FLAG(FdoExtension->ScanForSpecialFlags, CLASS_SPECIAL_FUA_NOT_SUPPORTED))
|
|
{
|
|
cacheSetting->State = DiskCacheWriteThroughNotSupported;
|
|
}
|
|
|
|
//
|
|
// Determine whether it is possible to modify the cache setting
|
|
//
|
|
if (TEST_FLAG(FdoExtension->ScanForSpecialFlags, CLASS_SPECIAL_MODIFY_CACHE_UNSUCCESSFUL))
|
|
{
|
|
cacheSetting->State = DiskCacheModifyUnsuccessful;
|
|
}
|
|
|
|
cacheSetting->IsPowerProtected = TEST_FLAG(FdoExtension->DeviceFlags, DEV_POWER_PROTECTED);
|
|
|
|
Irp->IoStatus.Information = sizeof(DISK_CACHE_SETTING);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
DiskIoctlSetCacheSetting(
|
|
IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine services IOCTL_DISK_SET_CACHE_SETTING. It allows
|
|
the user to specify whether the disk cache is power-protected
|
|
or not
|
|
|
|
Arguments:
|
|
|
|
Fdo - The functional device object processing the request
|
|
Irp - The ioctl to be processed
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS if successful, an error code otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(DISK_CACHE_SETTING))
|
|
{
|
|
status = STATUS_INFO_LENGTH_MISMATCH;
|
|
}
|
|
else
|
|
{
|
|
PDISK_CACHE_SETTING cacheSetting = (PDISK_CACHE_SETTING)Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
if (cacheSetting->Version == sizeof(DISK_CACHE_SETTING))
|
|
{
|
|
ULONG isPowerProtected;
|
|
|
|
//
|
|
// Save away the user-defined override in our extension and the registry
|
|
//
|
|
if (cacheSetting->IsPowerProtected)
|
|
{
|
|
SET_FLAG(FdoExtension->DeviceFlags, DEV_POWER_PROTECTED);
|
|
isPowerProtected = 1;
|
|
}
|
|
else
|
|
{
|
|
CLEAR_FLAG(FdoExtension->DeviceFlags, DEV_POWER_PROTECTED);
|
|
isPowerProtected = 0;
|
|
}
|
|
|
|
ClassSetDeviceParameter(FdoExtension, DiskDeviceParameterSubkey, DiskDeviceCacheIsPowerProtected, isPowerProtected);
|
|
}
|
|
else
|
|
{
|
|
status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
PPARTITION_INFORMATION_EX
|
|
DiskPdoFindPartitionEntry(
|
|
IN PPHYSICAL_DEVICE_EXTENSION Pdo,
|
|
IN PDRIVE_LAYOUT_INFORMATION_EX LayoutInfo
|
|
)
|
|
|
|
{
|
|
PCOMMON_DEVICE_EXTENSION commonExtension= &(Pdo->CommonExtension);
|
|
ULONG partitionIndex;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugPrint((1, "DiskPdoFindPartitionEntry: Searching layout for "
|
|
"matching partition.\n"));
|
|
|
|
for(partitionIndex = 0;
|
|
partitionIndex < LayoutInfo->PartitionCount;
|
|
partitionIndex++) {
|
|
|
|
PPARTITION_INFORMATION_EX partitionInfo;
|
|
|
|
//
|
|
// Get the partition entry
|
|
//
|
|
|
|
partitionInfo = &LayoutInfo->PartitionEntry[partitionIndex];
|
|
|
|
//
|
|
// See if it is the one we are looking for...
|
|
//
|
|
|
|
if( LayoutInfo->PartitionStyle == PARTITION_STYLE_MBR &&
|
|
(partitionInfo->Mbr.PartitionType == PARTITION_ENTRY_UNUSED ||
|
|
IsContainerPartition(partitionInfo->Mbr.PartitionType)) ) {
|
|
|
|
continue;
|
|
}
|
|
|
|
if( LayoutInfo->PartitionStyle == PARTITION_STYLE_GPT &&
|
|
DiskCompareGuid (&partitionInfo->Gpt.PartitionType, &GUID_NULL) == 00) {
|
|
|
|
continue;
|
|
}
|
|
|
|
if( (commonExtension->StartingOffset.QuadPart ==
|
|
partitionInfo->StartingOffset.QuadPart) &&
|
|
(commonExtension->PartitionLength.QuadPart ==
|
|
partitionInfo->PartitionLength.QuadPart)) {
|
|
|
|
//
|
|
// Found it!
|
|
//
|
|
|
|
DebugPrint((1, "DiskPdoFindPartitionEntry: Found matching "
|
|
"partition.\n"));
|
|
return partitionInfo;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
PPARTITION_INFORMATION_EX
|
|
DiskFindAdjacentPartition(
|
|
IN PDRIVE_LAYOUT_INFORMATION_EX LayoutInfo,
|
|
IN PPARTITION_INFORMATION_EX BasePartition
|
|
)
|
|
{
|
|
ULONG partitionIndex;
|
|
LONGLONG baseStoppingOffset;
|
|
LONGLONG adjacentStartingOffset;
|
|
PPARTITION_INFORMATION_EX adjacentPartition = 0;
|
|
|
|
ASSERT(LayoutInfo && BasePartition);
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugPrint((1, "DiskPdoFindAdjacentPartition: Searching layout for adjacent partition.\n"));
|
|
|
|
//
|
|
// Construct the base stopping offset for comparison
|
|
//
|
|
|
|
baseStoppingOffset = (BasePartition->StartingOffset.QuadPart +
|
|
BasePartition->PartitionLength.QuadPart -
|
|
1);
|
|
|
|
adjacentStartingOffset = MAXLONGLONG;
|
|
|
|
for(partitionIndex = 0;
|
|
partitionIndex < LayoutInfo->PartitionCount;
|
|
partitionIndex++) {
|
|
|
|
PPARTITION_INFORMATION_EX partitionInfo;
|
|
|
|
//
|
|
// Get the partition entry
|
|
//
|
|
|
|
partitionInfo = &LayoutInfo->PartitionEntry[partitionIndex];
|
|
|
|
//
|
|
// See if it is the one we are looking for...
|
|
//
|
|
|
|
if( LayoutInfo->PartitionStyle == PARTITION_STYLE_MBR &&
|
|
partitionInfo->Mbr.PartitionType == PARTITION_ENTRY_UNUSED ) {
|
|
|
|
continue;
|
|
}
|
|
|
|
if( LayoutInfo->PartitionStyle == PARTITION_STYLE_GPT &&
|
|
DiskCompareGuid (&partitionInfo->Gpt.PartitionType, &GUID_NULL) == 00 ) {
|
|
|
|
continue;
|
|
}
|
|
|
|
|
|
if((partitionInfo->StartingOffset.QuadPart > baseStoppingOffset) &&
|
|
(partitionInfo->StartingOffset.QuadPart < adjacentStartingOffset)) {
|
|
|
|
// Found a closer neighbor...update and remember.
|
|
adjacentPartition = partitionInfo;
|
|
|
|
adjacentStartingOffset = adjacentPartition->StartingOffset.QuadPart;
|
|
|
|
DebugPrint((1, "DiskPdoFindAdjacentPartition: Found adjacent "
|
|
"partition.\n"));
|
|
}
|
|
}
|
|
return adjacentPartition;
|
|
}
|
|
|
|
|
|
PPARTITION_INFORMATION_EX
|
|
DiskFindContainingPartition(
|
|
IN PDRIVE_LAYOUT_INFORMATION_EX LayoutInfo,
|
|
IN PPARTITION_INFORMATION_EX BasePartition,
|
|
IN BOOLEAN SearchTopToBottom
|
|
)
|
|
|
|
{
|
|
|
|
LONG partitionIndex;
|
|
LONG startIndex;
|
|
LONG stopIndex;
|
|
LONG stepIndex;
|
|
|
|
LONGLONG baseStoppingOffset;
|
|
LONGLONG containerStoppingOffset;
|
|
|
|
PPARTITION_INFORMATION_EX partitionInfo = 0;
|
|
PPARTITION_INFORMATION_EX containerPartition = 0;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT( LayoutInfo && BasePartition);
|
|
|
|
DebugPrint((1, "DiskFindContainingPartition: Searching for extended partition.\n"));
|
|
|
|
if( LayoutInfo->PartitionCount != 0) {
|
|
|
|
baseStoppingOffset = (BasePartition->StartingOffset.QuadPart +
|
|
BasePartition->PartitionLength.QuadPart - 1);
|
|
|
|
//
|
|
// Determine the search direction and setup the loop
|
|
//
|
|
if(SearchTopToBottom == TRUE) {
|
|
|
|
startIndex = 0;
|
|
stopIndex = LayoutInfo->PartitionCount;
|
|
stepIndex = +1;
|
|
} else {
|
|
startIndex = LayoutInfo->PartitionCount - 1;
|
|
stopIndex = -1;
|
|
stepIndex = -1;
|
|
}
|
|
|
|
//
|
|
// Using the loop parameters, walk the layout information and
|
|
// return the first containing partition.
|
|
//
|
|
|
|
for(partitionIndex = startIndex;
|
|
partitionIndex != stopIndex;
|
|
partitionIndex += stepIndex) {
|
|
|
|
//
|
|
// Get the next partition entry
|
|
//
|
|
|
|
partitionInfo = &LayoutInfo->PartitionEntry[partitionIndex];
|
|
|
|
containerStoppingOffset = (partitionInfo->StartingOffset.QuadPart +
|
|
partitionInfo->PartitionLength.QuadPart -
|
|
1);
|
|
|
|
//
|
|
// Search for a containing partition without detecting the
|
|
// same partition as a container of itself. The starting
|
|
// offset of a partition and its container should never be
|
|
// the same; however, the stopping offset can be the same.
|
|
//
|
|
|
|
//
|
|
// NOTE: Container partitions are MBR only.
|
|
//
|
|
|
|
if((LayoutInfo->PartitionStyle == PARTITION_STYLE_MBR) &&
|
|
(IsContainerPartition(partitionInfo->Mbr.PartitionType)) &&
|
|
(BasePartition->StartingOffset.QuadPart >
|
|
partitionInfo->StartingOffset.QuadPart) &&
|
|
(baseStoppingOffset <= containerStoppingOffset)) {
|
|
|
|
containerPartition = partitionInfo;
|
|
|
|
DebugPrint((1, "DiskFindContainingPartition: Found a "
|
|
"containing extended partition.\n"));
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return containerPartition;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
DiskGetInfoExceptionInformation(
|
|
IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
|
|
IN PMODE_INFO_EXCEPTIONS ReturnPageData
|
|
)
|
|
{
|
|
PMODE_PARAMETER_HEADER modeData;
|
|
PMODE_INFO_EXCEPTIONS pageData;
|
|
ULONG length;
|
|
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// ReturnPageData is allocated by the caller
|
|
//
|
|
|
|
modeData = ExAllocatePoolWithTag(NonPagedPoolCacheAligned,
|
|
MODE_DATA_SIZE,
|
|
DISK_TAG_INFO_EXCEPTION);
|
|
|
|
if (modeData == NULL) {
|
|
|
|
DebugPrint((1, "DiskGetInfoExceptionInformation: Unable to allocate mode "
|
|
"data buffer\n"));
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlZeroMemory(modeData, MODE_DATA_SIZE);
|
|
|
|
length = ClassModeSense(FdoExtension->DeviceObject,
|
|
(PUCHAR) modeData,
|
|
MODE_DATA_SIZE,
|
|
MODE_PAGE_FAULT_REPORTING);
|
|
|
|
if (length < sizeof(MODE_PARAMETER_HEADER)) {
|
|
|
|
//
|
|
// Retry the request in case of a check condition.
|
|
//
|
|
|
|
length = ClassModeSense(FdoExtension->DeviceObject,
|
|
(PUCHAR) modeData,
|
|
MODE_DATA_SIZE,
|
|
MODE_PAGE_FAULT_REPORTING);
|
|
|
|
if (length < sizeof(MODE_PARAMETER_HEADER)) {
|
|
|
|
|
|
DebugPrint((1, "Disk.DisableWriteCache: Mode Sense failed\n"));
|
|
|
|
ExFreePool(modeData);
|
|
return STATUS_IO_DEVICE_ERROR;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the length is greater than length indicated by the mode data reset
|
|
// the data to the mode data.
|
|
//
|
|
|
|
if (length > (ULONG) (modeData->ModeDataLength + 1)) {
|
|
length = modeData->ModeDataLength + 1;
|
|
}
|
|
|
|
//
|
|
// Find the mode page for info exceptions
|
|
//
|
|
|
|
pageData = ClassFindModePage((PUCHAR) modeData,
|
|
length,
|
|
MODE_PAGE_FAULT_REPORTING,
|
|
TRUE);
|
|
|
|
if (pageData != NULL) {
|
|
RtlCopyMemory(ReturnPageData, pageData, sizeof(MODE_INFO_EXCEPTIONS));
|
|
status = STATUS_SUCCESS;
|
|
} else {
|
|
status = STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
DebugPrint((3, "DiskGetInfoExceptionInformation: %s support SMART for device %x\n",
|
|
NT_SUCCESS(status) ? "does" : "does not",
|
|
FdoExtension->DeviceObject));
|
|
|
|
|
|
ExFreePool(modeData);
|
|
return(status);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
DiskSetInfoExceptionInformation(
|
|
IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
|
|
IN PMODE_INFO_EXCEPTIONS PageData
|
|
)
|
|
|
|
{
|
|
ULONG i;
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// We will attempt (twice) to issue the mode select with the page.
|
|
// Make the setting persistant so that we don't have to turn it back
|
|
// on after a bus reset.
|
|
//
|
|
|
|
for (i = 0; i < 2; i++)
|
|
{
|
|
status = DiskModeSelect(FdoExtension->DeviceObject,
|
|
(PUCHAR) PageData,
|
|
sizeof(MODE_INFO_EXCEPTIONS),
|
|
TRUE);
|
|
|
|
}
|
|
|
|
DebugPrint((3, "DiskSetInfoExceptionInformation: %s for device %p\n",
|
|
NT_SUCCESS(status) ? "succeeded" : "failed",
|
|
FdoExtension->DeviceObject));
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
DiskIoctlCreateDisk(
|
|
IN OUT PDEVICE_OBJECT DeviceObject,
|
|
IN OUT PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Handler for IOCTL_DISK_CREATE_DISK ioctl.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Device object representing a disk that will be created or
|
|
erased.
|
|
|
|
Irp - The IRP for this request.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS code.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
PCOMMON_DEVICE_EXTENSION commonExtension;
|
|
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PDISK_DATA diskData;
|
|
PCREATE_DISK createDiskInfo;
|
|
|
|
|
|
PAGED_CODE ();
|
|
|
|
ASSERT ( DeviceObject != NULL );
|
|
ASSERT ( Irp != NULL );
|
|
|
|
//
|
|
// Initialization
|
|
//
|
|
|
|
commonExtension = DeviceObject->DeviceExtension;
|
|
fdoExtension = DeviceObject->DeviceExtension;
|
|
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
diskData = (PDISK_DATA)(commonExtension->DriverData);
|
|
|
|
|
|
ASSERT (commonExtension->IsFdo);
|
|
|
|
//
|
|
// Check the input buffer size.
|
|
//
|
|
|
|
if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof (CREATE_DISK) ) {
|
|
|
|
return STATUS_INFO_LENGTH_MISMATCH;
|
|
}
|
|
|
|
//
|
|
// If we are being asked to create a GPT disk on a system that doesn't
|
|
// support GPT, fail.
|
|
//
|
|
|
|
createDiskInfo = (PCREATE_DISK)Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
if (DiskDisableGpt &&
|
|
createDiskInfo->PartitionStyle == PARTITION_STYLE_GPT) {
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Call the lower level Io routine to do the dirty work of writing a
|
|
// new partition table.
|
|
//
|
|
|
|
DiskAcquirePartitioningLock(fdoExtension);
|
|
|
|
DiskInvalidatePartitionTable(fdoExtension, TRUE);
|
|
|
|
status = IoCreateDisk (
|
|
commonExtension->PartitionZeroExtension->CommonExtension.DeviceObject,
|
|
Irp->AssociatedIrp.SystemBuffer
|
|
);
|
|
DiskReleasePartitioningLock(fdoExtension);
|
|
ClassInvalidateBusRelations(DeviceObject);
|
|
|
|
Irp->IoStatus.Status = status;
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
DiskIoctlGetDriveLayout(
|
|
IN OUT PDEVICE_OBJECT DeviceObject,
|
|
IN OUT PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Handler for IOCTL_DISK_GET_DRIVE_LAYOUT ioctl.
|
|
|
|
This ioctl has been replace by IOCTL_DISK_GET_DRIVE_LAYOUT_EX.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Device object representing a disk the layout information
|
|
will be obtained for.
|
|
|
|
Irp - The IRP for this request.
|
|
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS code.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
ULONG size;
|
|
PDRIVE_LAYOUT_INFORMATION partitionList;
|
|
PDRIVE_LAYOUT_INFORMATION_EX partitionListEx;
|
|
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
|
|
PPHYSICAL_DEVICE_EXTENSION pdoExtension;
|
|
PCOMMON_DEVICE_EXTENSION commonExtension;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PDISK_DATA diskData;
|
|
BOOLEAN bUseCache = TRUE;
|
|
|
|
PAGED_CODE ();
|
|
|
|
ASSERT ( DeviceObject );
|
|
ASSERT ( Irp );
|
|
|
|
//
|
|
// Initialization
|
|
//
|
|
|
|
partitionListEx = NULL;
|
|
partitionList = NULL;
|
|
fdoExtension = DeviceObject->DeviceExtension;
|
|
commonExtension = DeviceObject->DeviceExtension;
|
|
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
diskData = (PDISK_DATA)(commonExtension->DriverData);
|
|
|
|
//
|
|
// If our cached partition table is valid we do not need to touch the disk
|
|
//
|
|
|
|
if (diskData->CachedPartitionTableValid == FALSE)
|
|
{
|
|
//
|
|
// Issue a read capacity to update the apparent size of the disk.
|
|
//
|
|
|
|
DiskReadDriveCapacity(fdoExtension->DeviceObject);
|
|
|
|
bUseCache = FALSE;
|
|
}
|
|
|
|
DiskAcquirePartitioningLock(fdoExtension);
|
|
|
|
status = DiskReadPartitionTableEx(fdoExtension, FALSE, &partitionListEx);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
DiskReleasePartitioningLock(fdoExtension);
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// This ioctl is only supported on MBR partitioned disks. Fail the
|
|
// call otherwise.
|
|
//
|
|
|
|
if (partitionListEx->PartitionStyle != PARTITION_STYLE_MBR) {
|
|
DiskReleasePartitioningLock(fdoExtension);
|
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
|
|
//
|
|
// The disk layout has been returned in the partitionListEx
|
|
// buffer. Determine its size and, if the data will fit
|
|
// into the intermediate buffer, return it.
|
|
//
|
|
|
|
size = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION, PartitionEntry[0]);
|
|
size += partitionListEx->PartitionCount * sizeof(PARTITION_INFORMATION);
|
|
|
|
if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
|
|
size) {
|
|
|
|
Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
|
|
|
|
DiskReleasePartitioningLock(fdoExtension);
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
//
|
|
// Update the partition device objects and set valid partition numbers
|
|
//
|
|
|
|
ASSERT(diskData->UpdatePartitionRoutine != NULL);
|
|
diskData->UpdatePartitionRoutine(DeviceObject, partitionListEx);
|
|
|
|
//
|
|
// Convert the extended drive layout structure to a regular drive layout
|
|
// structure to return. DiskConvertExtendedToLayout() allocates pool
|
|
// that we must free.
|
|
//
|
|
|
|
partitionList = DiskConvertExtendedToLayout(partitionListEx);
|
|
|
|
if (partitionList == NULL) {
|
|
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
DiskReleasePartitioningLock (fdoExtension);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// We're done with the extended partition list now.
|
|
//
|
|
|
|
partitionListEx = NULL;
|
|
|
|
//
|
|
// Copy partition information to system buffer.
|
|
//
|
|
|
|
RtlMoveMemory(Irp->AssociatedIrp.SystemBuffer,
|
|
partitionList,
|
|
size);
|
|
|
|
Irp->IoStatus.Information = size;
|
|
Irp->IoStatus.Status = status;
|
|
|
|
//
|
|
// Finally, free the buffer allocated by reading the
|
|
// partition table.
|
|
//
|
|
|
|
ExFreePool(partitionList);
|
|
DiskReleasePartitioningLock(fdoExtension);
|
|
|
|
if (bUseCache == FALSE)
|
|
{
|
|
ClassInvalidateBusRelations(DeviceObject);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
DiskIoctlGetDriveLayoutEx(
|
|
IN OUT PDEVICE_OBJECT DeviceObject,
|
|
IN OUT PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Handler for IOCTL_DISK_GET_DRIVE_LAYOUT_EX ioctl.
|
|
|
|
This ioctl replaces IOCTL_DISK_GET_DRIVE_LAYOUT.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Device object representing a disk the layout information
|
|
will be obtained for.
|
|
|
|
Irp - The IRP for this request.
|
|
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS code.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
ULONG size;
|
|
PDRIVE_LAYOUT_INFORMATION_EX partitionList;
|
|
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
|
|
PPHYSICAL_DEVICE_EXTENSION pdoExtension;
|
|
PCOMMON_DEVICE_EXTENSION commonExtension;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PDISK_DATA diskData;
|
|
BOOLEAN bUseCache = TRUE;
|
|
|
|
|
|
PAGED_CODE ();
|
|
|
|
ASSERT ( DeviceObject );
|
|
ASSERT ( Irp );
|
|
|
|
//
|
|
// Initialization
|
|
//
|
|
|
|
fdoExtension = DeviceObject->DeviceExtension;
|
|
pdoExtension = DeviceObject->DeviceExtension;
|
|
commonExtension = DeviceObject->DeviceExtension;
|
|
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
diskData = (PDISK_DATA)(commonExtension->DriverData);
|
|
|
|
//
|
|
// If our cached partition table is valid we do not need to touch the disk
|
|
//
|
|
|
|
if (diskData->CachedPartitionTableValid == FALSE)
|
|
{
|
|
//
|
|
// Issue a read capacity to update the apparent size of the disk.
|
|
//
|
|
|
|
DiskReadDriveCapacity(fdoExtension->DeviceObject);
|
|
|
|
bUseCache = FALSE;
|
|
}
|
|
|
|
DiskAcquirePartitioningLock (fdoExtension);
|
|
|
|
status = DiskReadPartitionTableEx (fdoExtension, FALSE, &partitionList);
|
|
|
|
if ( !NT_SUCCESS (status) ) {
|
|
DiskReleasePartitioningLock (fdoExtension);
|
|
return status;
|
|
}
|
|
|
|
size = FIELD_OFFSET (DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry[0]) +
|
|
partitionList->PartitionCount * sizeof (PARTITION_INFORMATION_EX);
|
|
|
|
//
|
|
// If the output buffer is large enough, copy data to the output buffer,
|
|
// otherwise, fail.
|
|
//
|
|
|
|
if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
|
|
size) {
|
|
|
|
Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
|
|
|
|
DiskReleasePartitioningLock(fdoExtension);
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
//
|
|
// Update the partition device objects and set valid partition numbers
|
|
//
|
|
|
|
ASSERT(diskData->UpdatePartitionRoutine != NULL);
|
|
diskData->UpdatePartitionRoutine(DeviceObject, partitionList);
|
|
|
|
RtlCopyMemory (Irp->AssociatedIrp.SystemBuffer,
|
|
partitionList,
|
|
size
|
|
);
|
|
|
|
Irp->IoStatus.Information = size;
|
|
Irp->IoStatus.Status = status;
|
|
|
|
DiskReleasePartitioningLock(fdoExtension);
|
|
|
|
if (bUseCache == FALSE)
|
|
{
|
|
ClassInvalidateBusRelations(DeviceObject);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
DiskIoctlSetDriveLayout(
|
|
IN OUT PDEVICE_OBJECT DeviceObject,
|
|
IN OUT PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Handler for IOCTL_DISK_SET_DRIVE_LAYOUT ioctl.
|
|
|
|
This ioctl has been replaced by IOCTL_DISK_SET_DRIVE_LAYOUT_EX.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Device object for which partition table should be written.
|
|
|
|
Irp - IRP involved.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS code.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
PDRIVE_LAYOUT_INFORMATION partitionList;
|
|
PDRIVE_LAYOUT_INFORMATION_EX partitionListEx;
|
|
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
|
|
PPHYSICAL_DEVICE_EXTENSION pdoExtension;
|
|
PCOMMON_DEVICE_EXTENSION commonExtension;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PDISK_DATA diskData;
|
|
BOOLEAN invalidateBusRelations;
|
|
SIZE_T listSize;
|
|
SIZE_T inputBufferLength;
|
|
SIZE_T outputBufferLength;
|
|
|
|
PAGED_CODE ();
|
|
|
|
ASSERT ( DeviceObject );
|
|
ASSERT ( Irp );
|
|
|
|
//
|
|
// Initialization
|
|
//
|
|
|
|
partitionListEx = NULL;
|
|
partitionList = NULL;
|
|
fdoExtension = DeviceObject->DeviceExtension;
|
|
pdoExtension = DeviceObject->DeviceExtension;
|
|
commonExtension = DeviceObject->DeviceExtension;
|
|
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
diskData = (PDISK_DATA)(commonExtension->DriverData);
|
|
partitionList = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
inputBufferLength = irpStack->Parameters.DeviceIoControl.InputBufferLength;
|
|
outputBufferLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
|
|
|
|
//
|
|
// Update the partition table.
|
|
//
|
|
|
|
if (inputBufferLength < sizeof (DRIVE_LAYOUT_INFORMATION)) {
|
|
|
|
status = STATUS_INFO_LENGTH_MISMATCH;
|
|
return status;
|
|
}
|
|
|
|
DiskAcquirePartitioningLock(fdoExtension);
|
|
|
|
listSize = (partitionList->PartitionCount - 1);
|
|
listSize *= sizeof(PARTITION_INFORMATION);
|
|
listSize += sizeof(DRIVE_LAYOUT_INFORMATION);
|
|
|
|
if (inputBufferLength < listSize) {
|
|
|
|
//
|
|
// The remaning size of the input buffer not big enough to
|
|
// hold the additional partition entries
|
|
//
|
|
|
|
status = STATUS_INFO_LENGTH_MISMATCH;
|
|
DiskReleasePartitioningLock(fdoExtension);
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Convert the parititon information structure into an extended
|
|
// structure.
|
|
//
|
|
|
|
partitionListEx = DiskConvertLayoutToExtended (partitionList);
|
|
|
|
if ( partitionListEx == NULL ) {
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
Irp->IoStatus.Status = status;
|
|
DiskReleasePartitioningLock(fdoExtension);
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Redo all the partition numbers in the partition information
|
|
//
|
|
|
|
ASSERT(diskData->UpdatePartitionRoutine != NULL);
|
|
diskData->UpdatePartitionRoutine(DeviceObject, partitionListEx);
|
|
|
|
//
|
|
// Write changes to disk.
|
|
//
|
|
|
|
status = DiskWritePartitionTableEx(fdoExtension, partitionListEx);
|
|
|
|
//
|
|
// Update IRP with bytes returned. Make sure we don't claim to be
|
|
// returning more bytes than the caller is expecting to get back.
|
|
//
|
|
|
|
if (NT_SUCCESS (status)) {
|
|
if (outputBufferLength < listSize) {
|
|
Irp->IoStatus.Information = outputBufferLength;
|
|
} else {
|
|
ULONG i;
|
|
|
|
Irp->IoStatus.Information = listSize;
|
|
|
|
//
|
|
// Also update the partition numbers.
|
|
//
|
|
|
|
for (i = 0; i < partitionList->PartitionCount; i++) {
|
|
|
|
PPARTITION_INFORMATION partition;
|
|
PPARTITION_INFORMATION_EX partitionEx;
|
|
|
|
partition = &partitionList->PartitionEntry[i];
|
|
partitionEx = &partitionListEx->PartitionEntry[i];
|
|
partition->PartitionNumber = partitionEx->PartitionNumber;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
ExFreePool (partitionListEx);
|
|
DiskReleasePartitioningLock(fdoExtension);
|
|
ClassInvalidateBusRelations(DeviceObject);
|
|
|
|
Irp->IoStatus.Status = status;
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
DiskIoctlSetDriveLayoutEx(
|
|
IN OUT PDEVICE_OBJECT DeviceObject,
|
|
IN OUT PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Handler for IOCTL_DISK_SET_DRIVE_LAYOUT_EX ioctl.
|
|
|
|
This ioctl replaces IOCTL_DISK_SET_DRIVE_LAYOUT.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Device object for which partition table should be written.
|
|
|
|
Irp - IRP involved.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS code.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
NTSTATUS status;
|
|
PDRIVE_LAYOUT_INFORMATION_EX partitionListEx;
|
|
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
|
|
PCOMMON_DEVICE_EXTENSION commonExtension;
|
|
|
|
PIO_STACK_LOCATION irpStack;
|
|
PDISK_DATA diskData;
|
|
BOOLEAN invalidateBusRelations;
|
|
SIZE_T listSize;
|
|
SIZE_T inputBufferLength;
|
|
SIZE_T outputBufferLength;
|
|
|
|
PAGED_CODE ();
|
|
|
|
ASSERT ( DeviceObject );
|
|
ASSERT ( Irp );
|
|
|
|
//
|
|
// Initialization
|
|
//
|
|
|
|
partitionListEx = NULL;
|
|
fdoExtension = DeviceObject->DeviceExtension;
|
|
commonExtension = DeviceObject->DeviceExtension;
|
|
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
diskData = (PDISK_DATA)(commonExtension->DriverData);
|
|
partitionListEx = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
inputBufferLength = irpStack->Parameters.DeviceIoControl.InputBufferLength;
|
|
outputBufferLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
|
|
|
|
//
|
|
// Update the partition table.
|
|
//
|
|
|
|
if (inputBufferLength <
|
|
FIELD_OFFSET (DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry)) {
|
|
status = STATUS_INFO_LENGTH_MISMATCH;
|
|
return status;
|
|
}
|
|
|
|
DiskAcquirePartitioningLock(fdoExtension);
|
|
|
|
listSize = partitionListEx->PartitionCount;
|
|
listSize *= sizeof(PARTITION_INFORMATION_EX);
|
|
listSize += FIELD_OFFSET (DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry);
|
|
|
|
if (inputBufferLength < listSize) {
|
|
|
|
//
|
|
// The remaning size of the input buffer not big enough to
|
|
// hold the additional partition entries
|
|
//
|
|
|
|
status = STATUS_INFO_LENGTH_MISMATCH;
|
|
DiskReleasePartitioningLock(fdoExtension);
|
|
return status;
|
|
}
|
|
|
|
|
|
//
|
|
// If the partition count is zero, this is a request to clear
|
|
// the partition table.
|
|
//
|
|
|
|
if (partitionListEx->PartitionCount == 0) {
|
|
|
|
CREATE_DISK CreateDiskInfo = { 0 };
|
|
|
|
CreateDiskInfo.PartitionStyle = diskData->PartitionStyle;
|
|
|
|
if (diskData->PartitionStyle == PARTITION_STYLE_MBR) {
|
|
CreateDiskInfo.Mbr.Signature = partitionListEx->Mbr.Signature;
|
|
} else {
|
|
ASSERT (diskData->PartitionStyle == PARTITION_STYLE_GPT);
|
|
CreateDiskInfo.Gpt.DiskId = partitionListEx->Gpt.DiskId;
|
|
//
|
|
// NB: Setting MaxPartitionCount to zero will
|
|
// force the GPT partition table writing code
|
|
// to use the default minimum for this value.
|
|
//
|
|
CreateDiskInfo.Gpt.MaxPartitionCount = 0;
|
|
}
|
|
DiskInvalidatePartitionTable(fdoExtension, TRUE);
|
|
|
|
status = IoCreateDisk(DeviceObject, &CreateDiskInfo);
|
|
|
|
} else {
|
|
|
|
//
|
|
// Redo all the partition numbers in the partition information
|
|
//
|
|
|
|
ASSERT(diskData->UpdatePartitionRoutine != NULL);
|
|
diskData->UpdatePartitionRoutine(DeviceObject, partitionListEx);
|
|
|
|
//
|
|
// Write changes to disk.
|
|
//
|
|
|
|
status = DiskWritePartitionTableEx(fdoExtension, partitionListEx);
|
|
}
|
|
|
|
//
|
|
// Update IRP with bytes returned. Make sure we don't claim to be
|
|
// returning more bytes than the caller is expecting to get back.
|
|
//
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
if (outputBufferLength < listSize) {
|
|
Irp->IoStatus.Information = outputBufferLength;
|
|
} else {
|
|
Irp->IoStatus.Information = listSize;
|
|
}
|
|
}
|
|
|
|
DiskReleasePartitioningLock(fdoExtension);
|
|
ClassInvalidateBusRelations(DeviceObject);
|
|
|
|
Irp->IoStatus.Status = status;
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
DiskIoctlGetPartitionInfo(
|
|
IN OUT PDEVICE_OBJECT DeviceObject,
|
|
IN OUT PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Handle the IOCTL_DISK_GET_PARTITION_INFO ioctl. Return the information
|
|
about the partition specified by the device object. Note that no
|
|
information is ever returned about the size or partition type of the
|
|
physical disk, as this doesn't make any sense.
|
|
|
|
This ioctl has been replaced by IOCTL_DISK_GET_PARTITION_INFO_EX.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject -
|
|
|
|
Irp -
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS code.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PDISK_DATA diskData;
|
|
PPARTITION_INFORMATION partitionInfo;
|
|
PFUNCTIONAL_DEVICE_EXTENSION p0Extension;
|
|
PCOMMON_DEVICE_EXTENSION commonExtension;
|
|
PDISK_DATA partitionZeroData;
|
|
NTSTATUS oldReadyStatus;
|
|
|
|
|
|
PAGED_CODE ();
|
|
|
|
ASSERT ( DeviceObject );
|
|
ASSERT ( Irp );
|
|
|
|
|
|
//
|
|
// Initialization
|
|
//
|
|
|
|
commonExtension = DeviceObject->DeviceExtension;
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
diskData = (PDISK_DATA)(commonExtension->DriverData);
|
|
p0Extension = commonExtension->PartitionZeroExtension;
|
|
partitionZeroData = ((PDISK_DATA) p0Extension->CommonExtension.DriverData);
|
|
|
|
|
|
//
|
|
// Check that the buffer is large enough.
|
|
//
|
|
|
|
if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(PARTITION_INFORMATION)) {
|
|
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Update the geometry in case it has changed
|
|
//
|
|
|
|
status = DiskReadDriveCapacity(p0Extension->DeviceObject);
|
|
|
|
//
|
|
// Note whether the drive is ready. If the status has changed then
|
|
// notify pnp.
|
|
//
|
|
|
|
oldReadyStatus = InterlockedExchange(
|
|
&(partitionZeroData->ReadyStatus),
|
|
status);
|
|
|
|
if(partitionZeroData->ReadyStatus != oldReadyStatus) {
|
|
IoInvalidateDeviceRelations(p0Extension->LowerPdo,
|
|
BusRelations);
|
|
}
|
|
|
|
if(!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
|
|
//
|
|
// Partition zero, the partition representing the entire disk, is
|
|
// special cased. The logic below allows for sending this ioctl to
|
|
// a GPT disk only for partition zero. This allows us to obtain
|
|
// the size of a GPT disk using Win2k compatible IOCTLs.
|
|
//
|
|
|
|
if (commonExtension->PartitionNumber == 0) {
|
|
|
|
partitionInfo = (PPARTITION_INFORMATION) Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
partitionInfo->PartitionType = PARTITION_ENTRY_UNUSED;
|
|
partitionInfo->StartingOffset = commonExtension->StartingOffset;
|
|
partitionInfo->PartitionLength = commonExtension->PartitionLength;
|
|
partitionInfo->HiddenSectors = 0;
|
|
partitionInfo->PartitionNumber = commonExtension->PartitionNumber;
|
|
partitionInfo->BootIndicator = FALSE;
|
|
partitionInfo->RewritePartition = FALSE;
|
|
partitionInfo->RecognizedPartition = FALSE;
|
|
|
|
} else {
|
|
|
|
//
|
|
// We do not support this IOCTL on an EFI partitioned disk
|
|
// for any partition other than partition zero.
|
|
//
|
|
|
|
if (diskData->PartitionStyle != PARTITION_STYLE_MBR) {
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
Irp->IoStatus.Status = status;
|
|
return status;
|
|
}
|
|
|
|
DiskEnumerateDevice(p0Extension->DeviceObject);
|
|
|
|
DiskAcquirePartitioningLock(p0Extension);
|
|
|
|
|
|
partitionInfo = (PPARTITION_INFORMATION) Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
partitionInfo->PartitionType = diskData->Mbr.PartitionType;
|
|
partitionInfo->StartingOffset = commonExtension->StartingOffset;
|
|
partitionInfo->PartitionLength = commonExtension->PartitionLength;
|
|
partitionInfo->HiddenSectors = diskData->Mbr.HiddenSectors;
|
|
partitionInfo->PartitionNumber = commonExtension->PartitionNumber;
|
|
partitionInfo->BootIndicator = diskData->Mbr.BootIndicator;
|
|
partitionInfo->RewritePartition = FALSE;
|
|
partitionInfo->RecognizedPartition =
|
|
IsRecognizedPartition(diskData->Mbr.PartitionType);
|
|
|
|
DiskReleasePartitioningLock(p0Extension);
|
|
|
|
}
|
|
|
|
status = STATUS_SUCCESS;
|
|
Irp->IoStatus.Information = sizeof(PARTITION_INFORMATION);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
DiskIoctlGetPartitionInfoEx(
|
|
IN OUT PDEVICE_OBJECT DeviceObject,
|
|
IN OUT PIRP Irp
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PDISK_DATA diskData;
|
|
PPARTITION_INFORMATION_EX partitionInfo;
|
|
PFUNCTIONAL_DEVICE_EXTENSION p0Extension;
|
|
PCOMMON_DEVICE_EXTENSION commonExtension;
|
|
PDISK_DATA partitionZeroData;
|
|
NTSTATUS oldReadyStatus;
|
|
|
|
|
|
PAGED_CODE ();
|
|
|
|
ASSERT ( DeviceObject );
|
|
ASSERT ( Irp );
|
|
|
|
|
|
//
|
|
// Initialization
|
|
//
|
|
|
|
commonExtension = DeviceObject->DeviceExtension;
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
diskData = (PDISK_DATA)(commonExtension->DriverData);
|
|
p0Extension = commonExtension->PartitionZeroExtension;
|
|
partitionZeroData = ((PDISK_DATA) p0Extension->CommonExtension.DriverData);
|
|
|
|
|
|
//
|
|
// Check that the buffer is large enough.
|
|
//
|
|
|
|
if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(PARTITION_INFORMATION_EX)) {
|
|
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
Irp->IoStatus.Status = status;
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Update the geometry in case it has changed
|
|
//
|
|
|
|
status = DiskReadDriveCapacity(p0Extension->DeviceObject);
|
|
|
|
//
|
|
// Note whether the drive is ready. If the status has changed then
|
|
// notify pnp.
|
|
//
|
|
|
|
oldReadyStatus = InterlockedExchange(
|
|
&(partitionZeroData->ReadyStatus),
|
|
status);
|
|
|
|
if(partitionZeroData->ReadyStatus != oldReadyStatus) {
|
|
IoInvalidateDeviceRelations(p0Extension->LowerPdo,
|
|
BusRelations);
|
|
}
|
|
|
|
if(!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// If this is something other than partition 0 then do a
|
|
// re-enumeration to make sure we've got up-to-date information.
|
|
//
|
|
if(commonExtension->PartitionNumber != 0) {
|
|
DiskEnumerateDevice(p0Extension->DeviceObject);
|
|
DiskAcquirePartitioningLock(p0Extension);
|
|
}
|
|
|
|
partitionInfo = (PPARTITION_INFORMATION_EX) Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
partitionInfo->StartingOffset = commonExtension->StartingOffset;
|
|
partitionInfo->PartitionLength = commonExtension->PartitionLength;
|
|
partitionInfo->RewritePartition = FALSE;
|
|
partitionInfo->PartitionNumber = commonExtension->PartitionNumber;
|
|
partitionInfo->PartitionStyle = diskData->PartitionStyle;
|
|
|
|
if ( diskData->PartitionStyle == PARTITION_STYLE_MBR ) {
|
|
|
|
partitionInfo->Mbr.PartitionType = diskData->Mbr.PartitionType;
|
|
partitionInfo->Mbr.HiddenSectors = diskData->Mbr.HiddenSectors;
|
|
partitionInfo->Mbr.BootIndicator = diskData->Mbr.BootIndicator;
|
|
partitionInfo->Mbr.RecognizedPartition =
|
|
IsRecognizedPartition(diskData->Mbr.PartitionType);
|
|
|
|
} else {
|
|
|
|
//
|
|
// ISSUE - 2000/02/09 - math: Review for Partition0.
|
|
// Is this correct for Partition0?
|
|
//
|
|
|
|
partitionInfo->Gpt.PartitionType = diskData->Efi.PartitionType;
|
|
partitionInfo->Gpt.PartitionId = diskData->Efi.PartitionId;
|
|
partitionInfo->Gpt.Attributes = diskData->Efi.Attributes;
|
|
RtlCopyMemory (
|
|
partitionInfo->Gpt.Name,
|
|
diskData->Efi.PartitionName,
|
|
sizeof (partitionInfo->Gpt.Name)
|
|
);
|
|
}
|
|
|
|
status = STATUS_SUCCESS;
|
|
Irp->IoStatus.Information = sizeof(PARTITION_INFORMATION_EX);
|
|
|
|
if(commonExtension->PartitionNumber != 0) {
|
|
DiskReleasePartitioningLock(p0Extension);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
DiskIoctlGetLengthInfo(
|
|
IN OUT PDEVICE_OBJECT DeviceObject,
|
|
IN OUT PIRP Irp
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PDISK_DATA diskData;
|
|
PGET_LENGTH_INFORMATION lengthInfo;
|
|
PFUNCTIONAL_DEVICE_EXTENSION p0Extension;
|
|
PCOMMON_DEVICE_EXTENSION commonExtension;
|
|
PDISK_DATA partitionZeroData;
|
|
NTSTATUS oldReadyStatus;
|
|
|
|
|
|
PAGED_CODE ();
|
|
|
|
ASSERT ( DeviceObject );
|
|
ASSERT ( Irp );
|
|
|
|
|
|
//
|
|
// Initialization
|
|
//
|
|
|
|
commonExtension = DeviceObject->DeviceExtension;
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
diskData = (PDISK_DATA)(commonExtension->DriverData);
|
|
p0Extension = commonExtension->PartitionZeroExtension;
|
|
partitionZeroData = ((PDISK_DATA) p0Extension->CommonExtension.DriverData);
|
|
|
|
|
|
//
|
|
// Check that the buffer is large enough.
|
|
//
|
|
|
|
if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(GET_LENGTH_INFORMATION)) {
|
|
|
|
Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
//
|
|
// Update the geometry in case it has changed
|
|
//
|
|
|
|
status = DiskReadDriveCapacity(p0Extension->DeviceObject);
|
|
|
|
//
|
|
// Note whether the drive is ready. If the status has changed then
|
|
// notify pnp.
|
|
//
|
|
|
|
oldReadyStatus = InterlockedExchange(
|
|
&(partitionZeroData->ReadyStatus),
|
|
status);
|
|
|
|
if(partitionZeroData->ReadyStatus != oldReadyStatus) {
|
|
IoInvalidateDeviceRelations(p0Extension->LowerPdo,
|
|
BusRelations);
|
|
}
|
|
|
|
if(!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// If this is something other than partition 0 then do a
|
|
// re-enumeration to make sure we've got up-to-date information.
|
|
//
|
|
if(commonExtension->PartitionNumber != 0) {
|
|
DiskEnumerateDevice(p0Extension->DeviceObject);
|
|
DiskAcquirePartitioningLock(p0Extension);
|
|
}
|
|
|
|
lengthInfo = (PGET_LENGTH_INFORMATION) Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
lengthInfo->Length = commonExtension->PartitionLength;
|
|
|
|
status = STATUS_SUCCESS;
|
|
Irp->IoStatus.Information = sizeof(GET_LENGTH_INFORMATION);
|
|
|
|
if(commonExtension->PartitionNumber != 0) {
|
|
DiskReleasePartitioningLock(p0Extension);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
DiskIoctlSetPartitionInfo(
|
|
IN OUT PDEVICE_OBJECT DeviceObject,
|
|
IN OUT PIRP Irp
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
PSET_PARTITION_INFORMATION inputBuffer;
|
|
PDISK_DATA diskData;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PCOMMON_DEVICE_EXTENSION commonExtension;
|
|
|
|
|
|
PAGED_CODE ();
|
|
|
|
ASSERT ( DeviceObject != NULL );
|
|
ASSERT ( Irp != NULL );
|
|
|
|
|
|
//
|
|
// Initialization
|
|
//
|
|
|
|
commonExtension = DeviceObject->DeviceExtension;
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
diskData = (PDISK_DATA)(commonExtension->DriverData);
|
|
inputBuffer = (PSET_PARTITION_INFORMATION)Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
if(commonExtension->IsFdo) {
|
|
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
|
|
if (diskData->PartitionStyle != PARTITION_STYLE_MBR) {
|
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
|
|
//
|
|
// Validate buffer length
|
|
//
|
|
|
|
if(irpStack->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(SET_PARTITION_INFORMATION)) {
|
|
|
|
return STATUS_INFO_LENGTH_MISMATCH;
|
|
}
|
|
|
|
DiskAcquirePartitioningLock(commonExtension->PartitionZeroExtension);
|
|
|
|
//
|
|
// The HAL routines IoGet- and IoSetPartitionInformation were
|
|
// developed before support of dynamic partitioning and therefore
|
|
// don't distinguish between partition ordinal (that is the order
|
|
// of a paritition on a disk) and the partition number. (The
|
|
// partition number is assigned to a partition to identify it to
|
|
// the system.) Use partition ordinals for these legacy calls.
|
|
//
|
|
|
|
status = DiskSetPartitionInformation(
|
|
commonExtension->PartitionZeroExtension,
|
|
commonExtension->PartitionZeroExtension->DiskGeometry.BytesPerSector,
|
|
diskData->PartitionOrdinal,
|
|
inputBuffer->PartitionType);
|
|
|
|
if(NT_SUCCESS(status)) {
|
|
|
|
diskData->Mbr.PartitionType = inputBuffer->PartitionType;
|
|
}
|
|
|
|
DiskReleasePartitioningLock(commonExtension->PartitionZeroExtension);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
DiskIoctlSetPartitionInfoEx(
|
|
IN OUT PDEVICE_OBJECT DeviceObject,
|
|
IN OUT PIRP Irp
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
PSET_PARTITION_INFORMATION_EX inputBuffer;
|
|
PDISK_DATA diskData;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PCOMMON_DEVICE_EXTENSION commonExtension;
|
|
|
|
|
|
PAGED_CODE ();
|
|
|
|
ASSERT ( DeviceObject != NULL );
|
|
ASSERT ( Irp != NULL );
|
|
|
|
|
|
//
|
|
// Initialization
|
|
//
|
|
|
|
commonExtension = DeviceObject->DeviceExtension;
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
diskData = (PDISK_DATA)(commonExtension->DriverData);
|
|
inputBuffer = (PSET_PARTITION_INFORMATION_EX)Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
if(commonExtension->IsFdo) {
|
|
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
//
|
|
// Validate buffer length
|
|
//
|
|
|
|
if(irpStack->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(SET_PARTITION_INFORMATION_EX)) {
|
|
|
|
return STATUS_INFO_LENGTH_MISMATCH;
|
|
}
|
|
|
|
DiskAcquirePartitioningLock(commonExtension->PartitionZeroExtension);
|
|
|
|
//
|
|
// The HAL routines IoGet- and IoSetPartitionInformation were
|
|
// developed before support of dynamic partitioning and therefore
|
|
// don't distinguish between partition ordinal (that is the order
|
|
// of a paritition on a disk) and the partition number. (The
|
|
// partition number is assigned to a partition to identify it to
|
|
// the system.) Use partition ordinals for these legacy calls.
|
|
//
|
|
|
|
status = DiskSetPartitionInformationEx(
|
|
commonExtension->PartitionZeroExtension,
|
|
diskData->PartitionOrdinal,
|
|
inputBuffer
|
|
);
|
|
|
|
if(NT_SUCCESS(status)) {
|
|
|
|
if (diskData->PartitionStyle == PARTITION_STYLE_MBR) {
|
|
|
|
diskData->Mbr.PartitionType = inputBuffer->Mbr.PartitionType;
|
|
|
|
} else {
|
|
|
|
diskData->Efi.PartitionType = inputBuffer->Gpt.PartitionType;
|
|
diskData->Efi.PartitionId = inputBuffer->Gpt.PartitionId;
|
|
diskData->Efi.Attributes = inputBuffer->Gpt.Attributes;
|
|
|
|
RtlCopyMemory (
|
|
diskData->Efi.PartitionName,
|
|
inputBuffer->Gpt.Name,
|
|
sizeof (diskData->Efi.PartitionName)
|
|
);
|
|
}
|
|
}
|
|
|
|
DiskReleasePartitioningLock(commonExtension->PartitionZeroExtension);
|
|
|
|
return status;
|
|
}
|
|
|
|
typedef struct _DISK_GEOMETRY_EX_INTERNAL {
|
|
DISK_GEOMETRY Geometry;
|
|
LARGE_INTEGER DiskSize;
|
|
DISK_PARTITION_INFO Partition;
|
|
DISK_DETECTION_INFO Detection;
|
|
} DISK_GEOMETRY_EX_INTERNAL, *PDISK_GEOMETRY_EX_INTERNAL;
|
|
|
|
|
|
NTSTATUS
|
|
DiskIoctlGetDriveGeometryEx(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN OUT PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Obtain the extended geometry information for the drive.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - The device object to obtain the geometry for.
|
|
|
|
Irp - IRP with a return buffer large enough to receive the
|
|
extended geometry information.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS code
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PCOMMON_DEVICE_EXTENSION commonExtension;
|
|
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
|
|
PDISK_DATA diskData;
|
|
PDISK_GEOMETRY_EX_INTERNAL geometryEx;
|
|
ULONG OutputBufferLength;
|
|
|
|
//
|
|
// Verification
|
|
//
|
|
|
|
PAGED_CODE ();
|
|
|
|
ASSERT ( DeviceObject != NULL );
|
|
ASSERT ( Irp != NULL );
|
|
|
|
//
|
|
// Setup parameters
|
|
//
|
|
|
|
commonExtension = DeviceObject->DeviceExtension;
|
|
fdoExtension = DeviceObject->DeviceExtension;
|
|
diskData = (PDISK_DATA)(commonExtension->DriverData);
|
|
irpStack = IoGetCurrentIrpStackLocation ( Irp );
|
|
geometryEx = NULL;
|
|
OutputBufferLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
|
|
|
|
//
|
|
// This is only valid for the FDO.
|
|
//
|
|
|
|
ASSERT ( commonExtension->IsFdo );
|
|
|
|
//
|
|
// Check that the buffer is large enough. It must be large enough
|
|
// to hold at lest the Geometry and DiskSize fields of of the
|
|
// DISK_GEOMETRY_EX structure.
|
|
//
|
|
|
|
if ( OutputBufferLength < FIELD_OFFSET (DISK_GEOMETRY_EX, Data) ) {
|
|
|
|
//
|
|
// Buffer too small. Bail out, telling the caller the required
|
|
// size.
|
|
//
|
|
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
return status;
|
|
}
|
|
|
|
if (TEST_FLAG (DeviceObject->Characteristics, FILE_REMOVABLE_MEDIA)) {
|
|
|
|
//
|
|
// Issue a ReadCapacity to update device extension
|
|
// with information for the current media.
|
|
//
|
|
|
|
status = DiskReadDriveCapacity (
|
|
commonExtension->PartitionZeroExtension->DeviceObject);
|
|
|
|
diskData->ReadyStatus = status;
|
|
|
|
if (!NT_SUCCESS (status)) {
|
|
return status;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Copy drive geometry.
|
|
//
|
|
|
|
geometryEx = (PDISK_GEOMETRY_EX_INTERNAL)Irp->AssociatedIrp.SystemBuffer;
|
|
geometryEx->Geometry = fdoExtension->DiskGeometry;
|
|
geometryEx->DiskSize = commonExtension->PartitionZeroExtension->CommonExtension.PartitionLength;
|
|
|
|
//
|
|
// If the user buffer is large enough to hold the partition information
|
|
// then add that as well.
|
|
//
|
|
|
|
if (OutputBufferLength >= FIELD_OFFSET (DISK_GEOMETRY_EX_INTERNAL, Detection)) {
|
|
|
|
geometryEx->Partition.SizeOfPartitionInfo = sizeof (geometryEx->Partition);
|
|
geometryEx->Partition.PartitionStyle = diskData->PartitionStyle;
|
|
|
|
switch ( diskData->PartitionStyle ) {
|
|
|
|
case PARTITION_STYLE_GPT:
|
|
|
|
//
|
|
// Copy GPT signature.
|
|
//
|
|
|
|
geometryEx->Partition.Gpt.DiskId = diskData->Efi.DiskId;
|
|
break;
|
|
|
|
case PARTITION_STYLE_MBR:
|
|
|
|
//
|
|
// Copy MBR signature and checksum.
|
|
//
|
|
|
|
geometryEx->Partition.Mbr.Signature = diskData->Mbr.Signature;
|
|
geometryEx->Partition.Mbr.CheckSum = diskData->Mbr.MbrCheckSum;
|
|
break;
|
|
|
|
default:
|
|
|
|
//
|
|
// This is a raw disk. Zero out the signature area so
|
|
// nobody gets confused.
|
|
//
|
|
|
|
RtlZeroMemory (
|
|
&geometryEx->Partition,
|
|
sizeof (geometryEx->Partition));
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the buffer is large enough to hold the detection information,
|
|
// then also add that.
|
|
//
|
|
|
|
if (OutputBufferLength >= sizeof (DISK_GEOMETRY_EX_INTERNAL)) {
|
|
|
|
geometryEx->Detection.SizeOfDetectInfo =
|
|
sizeof (geometryEx->Detection);
|
|
|
|
status = DiskGetDetectInfo (
|
|
fdoExtension,
|
|
&geometryEx->Detection);
|
|
|
|
//
|
|
// Failed to obtain detection information, set to none.
|
|
//
|
|
|
|
if (!NT_SUCCESS (status)) {
|
|
geometryEx->Detection.DetectionType = DetectNone;
|
|
}
|
|
}
|
|
|
|
|
|
status = STATUS_SUCCESS;
|
|
Irp->IoStatus.Information = min (OutputBufferLength,
|
|
sizeof (DISK_GEOMETRY_EX_INTERNAL));
|
|
|
|
return status;
|
|
}
|