From c30c0341fdae3d97ea3d1efc74bbb4fdfa004e10 Mon Sep 17 00:00:00 2001 From: Johannes Anderwald Date: Thu, 26 Jan 2012 16:55:19 +0000 Subject: [PATCH] - Import disk.sys from Windows XP DDK - Does not yet build svn path=/branches/usb-bringup-trunk/; revision=55211 --- drivers/storage/class/disk_new/CMakeLists.txt | 13 + drivers/storage/class/disk_new/data.c | 77 + drivers/storage/class/disk_new/disk.c | 6581 +++++++++++++++++ drivers/storage/class/disk_new/disk.h | 977 +++ drivers/storage/class/disk_new/disk.rc | 23 + drivers/storage/class/disk_new/diskdev.inf | 96 + drivers/storage/class/disk_new/diskwmi.c | 3424 +++++++++ drivers/storage/class/disk_new/enum.c | 1250 ++++ drivers/storage/class/disk_new/geometry.c | 1470 ++++ drivers/storage/class/disk_new/part.c | 327 + drivers/storage/class/disk_new/pnp.c | 1406 ++++ 11 files changed, 15644 insertions(+) create mode 100644 drivers/storage/class/disk_new/CMakeLists.txt create mode 100644 drivers/storage/class/disk_new/data.c create mode 100644 drivers/storage/class/disk_new/disk.c create mode 100644 drivers/storage/class/disk_new/disk.h create mode 100644 drivers/storage/class/disk_new/disk.rc create mode 100644 drivers/storage/class/disk_new/diskdev.inf create mode 100644 drivers/storage/class/disk_new/diskwmi.c create mode 100644 drivers/storage/class/disk_new/enum.c create mode 100644 drivers/storage/class/disk_new/geometry.c create mode 100644 drivers/storage/class/disk_new/part.c create mode 100644 drivers/storage/class/disk_new/pnp.c diff --git a/drivers/storage/class/disk_new/CMakeLists.txt b/drivers/storage/class/disk_new/CMakeLists.txt new file mode 100644 index 00000000000..fe922b75608 --- /dev/null +++ b/drivers/storage/class/disk_new/CMakeLists.txt @@ -0,0 +1,13 @@ + +include_directories(..) + +add_library(disk SHARED data.c disk.c diskwmi.c enum.c geometry.c part.c pnp.c disk.rc) + +set_module_type(disk kernelmodedriver) +add_importlibs(disk + class2 + scsiport + ntoskrnl + hal) + +add_cd_file(TARGET disk DESTINATION reactos/system32/drivers NO_CAB FOR all) diff --git a/drivers/storage/class/disk_new/data.c b/drivers/storage/class/disk_new/data.c new file mode 100644 index 00000000000..b7fdc727356 --- /dev/null +++ b/drivers/storage/class/disk_new/data.c @@ -0,0 +1,77 @@ +/*++ + +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" + +#ifdef ALLOC_DATA_PRAGMA +#pragma data_seg("PAGE") +#endif + +/* +#define HackDisableTaggedQueuing (0x01) +#define HackDisableSynchronousTransfers (0x02) +#define HackDisableSpinDown (0x04) +#define HackDisableWriteCache (0x08) +#define HackCauseNotReportableHack (0x10) +#define HackRequiresStartUnitCommand (0x20) +*/ + +CLASSPNP_SCAN_FOR_SPECIAL_INFO DiskBadControllers[] = { + { "COMPAQ" , "PD-1" , NULL, 0x02 }, + { "CONNER" , "CP3500" , NULL, 0x02 }, + { "FUJITSU" , "M2652S-512" , NULL, 0x01 }, + { "HP ", "C1113F " , NULL, 0x20 }, + // iomegas require START_UNIT commands so be sure to match all of them. + { "iomega" , "jaz" , NULL, 0x30 }, + { "iomega" , NULL , NULL, 0x20 }, + { "IOMEGA" , "ZIP" , NULL, 0x27 }, + { "IOMEGA" , NULL , NULL, 0x20 }, + { "MAXTOR" , "MXT-540SL" , "I1.2", 0x01 }, + { "MICROP" , "1936-21MW1002002" , NULL, 0x03 }, + { "OLIVETTI", "CP3500" , NULL, 0x02 }, + { "SEAGATE" , "ST41601N" , "0102", 0x02 }, + { "SEAGATE" , "ST3655N" , NULL, 0x08 }, + { "SEAGATE" , "ST3390N" , NULL, 0x08 }, + { "SEAGATE" , "ST12550N" , NULL, 0x08 }, + { "SEAGATE" , "ST32430N" , NULL, 0x08 }, + { "SEAGATE" , "ST31230N" , NULL, 0x08 }, + { "SEAGATE" , "ST15230N" , NULL, 0x08 }, + { "SyQuest" , "SQ5110" , "CHC", 0x03 }, + { "TOSHIBA" , "MK538FB" , "60", 0x01 }, + { NULL , NULL , NULL, 0x0 } +}; + +DISK_MEDIA_TYPES_LIST const DiskMediaTypes[] = { + { "COMPAQ" , "PD-1 LF-1094" , NULL, 1, 1, PC_5_RW , 0 , 0 , 0 }, + { "HP" , NULL , NULL, 2, 2, MO_5_WO , MO_5_RW, 0 , 0 }, + { "iomega" , "jaz" , NULL, 1, 1, IOMEGA_JAZ , 0 , 0 , 0 }, + { "IOMEGA" , "ZIP" , NULL, 1, 1, IOMEGA_ZIP , 0 , 0 , 0 }, + { "PINNACLE", "Apex 4.6GB" , NULL, 3, 2, PINNACLE_APEX_5_RW, MO_5_RW, MO_5_WO, 0 }, + { "SONY" , "SMO-F541" , NULL, 2, 2, MO_5_WO , MO_5_RW, 0 , 0 }, + { "SONY" , "SMO-F551" , NULL, 2, 2, MO_5_WO , MO_5_RW, 0 , 0 }, + { NULL , NULL , NULL, 0, 0, 0 , 0 , 0 , 0 } +}; + +#ifdef ALLOC_DATA_PRAGMA +#pragma data_seg() +#endif + diff --git a/drivers/storage/class/disk_new/disk.c b/drivers/storage/class/disk_new/disk.c new file mode 100644 index 00000000000..5ed2689e34d --- /dev/null +++ b/drivers/storage/class/disk_new/disk.c @@ -0,0 +1,6581 @@ +/*++ + +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 "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, DiskModeSelect) +#pragma alloc_text(PAGE, DisableWriteCache) +#pragma alloc_text(PAGE, DiskIoctlVerify) +#pragma alloc_text(PAGE, DiskSetSpecialHacks) +#pragma alloc_text(PAGE, DiskScanRegistryForSpecial) +#pragma alloc_text(PAGE, DiskQueryPnpCapabilities) +#pragma alloc_text(PAGE, DiskGetCacheInformation) +#pragma alloc_text(PAGE, DiskSetCacheInformation) +#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))) + + +NTSTATUS +NTAPI +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; + CLASS_QUERY_WMI_REGINFO_EX_LIST classQueryWmiRegInfoExList; + GUID guidQueryRegInfoEx = GUID_CLASSPNP_QUERY_REGINFOEX; + + NTSTATUS status; + +#if defined(_X86_) + // + // Read the information NtDetect squirreled away about the disks in this + // system. + // + + status = DiskSaveDetectInfo(DriverObject); + + if(!NT_SUCCESS(status)) { + DebugPrint((1, "Disk: couldn't save NtDetect information (%#08lx)\n", + status)); + } +#endif + + // + // Zero InitData + // + + RtlZeroMemory (&InitializationData, sizeof(CLASS_INIT_DATA)); + + 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.ClassDeviceControl = DiskDeviceControl; + + 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 + // + RtlZeroMemory(&classQueryWmiRegInfoExList, sizeof(CLASS_QUERY_WMI_REGINFO_EX_LIST)); + classQueryWmiRegInfoExList.Size = sizeof(CLASS_QUERY_WMI_REGINFO_EX_LIST); + classQueryWmiRegInfoExList.ClassFdoQueryWmiRegInfoEx = DiskFdoQueryWmiRegInfoEx; + + ClassInitializeEx(DriverObject, + &guidQueryRegInfoEx, + &classQueryWmiRegInfoExList); + + return status; + +} // end DriverEntry() + + +VOID +NTAPI +DiskUnload( + IN PDRIVER_OBJECT DriverObject + ) +{ + PAGED_CODE(); + +#if defined(_X86_) + DiskCleanupDetectInfo(DriverObject); +#endif + return; +} + + +NTSTATUS +NTAPI +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 + +--*/ + +{ + CCHAR ntNameBuffer[MAXIMUM_FILENAME_LENGTH]; + STRING ntNameString; + UNICODE_STRING ntUnicodeString; + + PUCHAR deviceName = NULL; + + OBJECT_ATTRIBUTES objectAttributes; + HANDLE handle; + + NTSTATUS status; + + PDEVICE_OBJECT lowerDevice = NULL; + PDEVICE_OBJECT deviceObject = NULL; + + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension; + STORAGE_PROPERTY_ID propertyId; + PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor; + + PAGED_CODE(); + + *DeviceCount = 0; + + // + // Set up an object directory to contain the objects for this + // device and all its partitions. + // + + do { + + WCHAR buffer[64]; + UNICODE_STRING unicodeDirectoryName; + + swprintf(buffer, L"\\Device\\Harddisk%d", *DeviceCount); + + RtlInitUnicodeString(&unicodeDirectoryName, buffer); + + InitializeObjectAttributes(&objectAttributes, + &unicodeDirectoryName, + OBJ_CASE_INSENSITIVE | OBJ_PERMANENT, + NULL, + NULL); + + status = ZwCreateDirectoryObject(&handle, + DIRECTORY_ALL_ACCESS, + &objectAttributes); + + (*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", + ntNameBuffer)); + + 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: + + // + // Release the device since an error occurred. + // + + if (deviceObject != NULL) { + IoDeleteDevice(deviceObject); + } + + // + // Delete directory and return. + // + + if (!NT_SUCCESS(status)) { + ZwMakeTemporaryObject(handle); + ZwClose(handle); + } + + ObDereferenceObject(lowerDevice); + + return(status); +} + + +NTSTATUS +NTAPI +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 currentIrpStack = IoGetCurrentIrpStackLocation(Irp); + ULONG transferByteCount = currentIrpStack->Parameters.Read.Length; + LARGE_INTEGER startingOffset; + + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = + commonExtension->PartitionZeroExtension; + + ULONG residualBytes; + NTSTATUS status; + + // + // Verify parameters of this request. + // Check that ending sector is within partition and + // that number of bytes to transfer is a multiple of + // the sector size. + // + + startingOffset.QuadPart = + (currentIrpStack->Parameters.Read.ByteOffset.QuadPart + + transferByteCount); + + residualBytes = transferByteCount & + (fdoExtension->DiskGeometry.BytesPerSector - 1); + + + if ((startingOffset.QuadPart > commonExtension->PartitionLength.QuadPart) || + (residualBytes != 0)) { + + // + // 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; + + } + + } else { + + // + // the drive is ready, so ok the read/write + // + + status = STATUS_SUCCESS; + + } + + 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.TracksPerCylinder = fdoExtension->DiskGeometry.TracksPerCylinder; + mediaInfo->DeviceSpecific.DiskInfo.SectorsPerTrack = fdoExtension->DiskGeometry.SectorsPerTrack; + mediaInfo->DeviceSpecific.DiskInfo.BytesPerSector = fdoExtension->DiskGeometry.BytesPerSector; + mediaInfo->DeviceSpecific.RemovableDiskInfo.NumberMediaSides = 1; + + mediaInfo->DeviceSpecific.DiskInfo.MediaCharacteristics = (MEDIA_CURRENTLY_MOUNTED | MEDIA_READ_WRITE); + + if (!IsWritable) { + SET_FLAG(mediaInfo->DeviceSpecific.DiskInfo.MediaCharacteristics, + MEDIA_WRITE_PROTECTED); + } + + mediaInfo->DeviceSpecific.DiskInfo.MediaType = FixedMedia; + + + } 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.Information = sizeNeeded; + 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. + // + + 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; + + // + // Set the type. + // + + mediaTypes->DeviceType = FILE_DEVICE_DISK; + mediaTypes->MediaInfoCount = 1; + + mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaType = RemovableMedia; + 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 +NTAPI +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; + + 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); + } + + // + // Write zeros to Srb. + // + + 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; + Irp->IoStatus.Information = sizeof(DISK_CACHE_INFORMATION); + 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 { + + if (!cacheInfo->WriteCacheEnabled) + { + if (TEST_FLAG(fdoExtension->ScanForSpecialFlags, + CLASS_SPECIAL_DISABLE_WRITE_CACHE_NOT_SUPPORTED)) + { + // + // This request wants to disable write cache, which is + // not supported on this device. Instead of sending it + // down only to see it fail, return the error code now + // + status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + } + else + { + if (TEST_FLAG(fdoExtension->ScanForSpecialFlags, + CLASS_SPECIAL_DISABLE_WRITE_CACHE)) + { + // + // This request wants to enable write cache, which + // has been disabled to protect data integrity. So + // fail this request with access denied + // + status = STATUS_ACCESS_DENIED; + break; + } + } + + status = DiskSetCacheInformation(fdoExtension, cacheInfo); + + if (NT_SUCCESS(status)) + { + // + // Store the user-defined override in the registry + // + ClassSetDeviceParameter(fdoExtension, + DiskDeviceParameterSubkey, + DiskDeviceUserWriteCacheSetting, + (cacheInfo->WriteCacheEnabled) ? DiskWriteCacheEnable : DiskWriteCacheDisable); + } + else if (status == STATUS_INVALID_DEVICE_REQUEST) + { + if (cacheInfo->WriteCacheEnabled == FALSE) + { + // + // This device does not allow for + // the write cache to be disabled + // + ULONG specialFlags = 0; + + ClassGetDeviceParameter(fdoExtension, + DiskDeviceParameterSubkey, + DiskDeviceSpecialFlags, + &specialFlags); + + SET_FLAG(specialFlags, HackDisableWriteCacheNotSupported); + + SET_FLAG(fdoExtension->ScanForSpecialFlags, + CLASS_SPECIAL_DISABLE_WRITE_CACHE_NOT_SUPPORTED); + + ClassSetDeviceParameter(fdoExtension, + DiskDeviceParameterSubkey, + DiskDeviceSpecialFlags, + specialFlags); + } + } + } + + break; + } + + case IOCTL_DISK_GET_WRITE_CACHE_STATE: { + + PDISK_WRITE_CACHE_STATE writeCacheState = (PDISK_WRITE_CACHE_STATE)Irp->AssociatedIrp.SystemBuffer; + + if(!commonExtension->IsFdo) { + + ClassReleaseRemoveLock(DeviceObject, Irp); + ExFreePool(srb); + SendToFdo(DeviceObject, Irp, status); + return status; + } + + // + // Validate the request. + // + + if(irpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(DISK_WRITE_CACHE_STATE)) { + + status = STATUS_BUFFER_TOO_SMALL; + Irp->IoStatus.Information = sizeof(DISK_WRITE_CACHE_STATE); + break; + } + + *writeCacheState = DiskWriteCacheNormal; + + // + // Determine whether it is possible to disable the write cache + // + + if (TEST_FLAG(fdoExtension->ScanForSpecialFlags, CLASS_SPECIAL_DISABLE_WRITE_CACHE_NOT_SUPPORTED)) + { + *writeCacheState = DiskWriteCacheDisableNotSupported; + } + + // + // Determine whether it is safe to toggle the write cache + // + + if (TEST_FLAG(fdoExtension->ScanForSpecialFlags, CLASS_SPECIAL_DISABLE_WRITE_CACHE)) + { + *writeCacheState = DiskWriteCacheForceDisable; + } + + Irp->IoStatus.Information = sizeof(DISK_WRITE_CACHE_STATE); + status = STATUS_SUCCESS; + 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; + Irp->IoStatus.Information = sizeof(GETVERSIONINPARAMS); + break; + } + + // + // Create notification event object to be used to signal the + // request completion. + // + + KeInitializeEvent(&event, NotificationEvent, FALSE); + + 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; + + // + // Copy the IOCTL parameters to the srb control buffer area. + // + + RtlMoveMemory(buffer, + Irp->AssociatedIrp.SystemBuffer, + sizeof(GETVERSIONINPARAMS)); + + 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; + Irp->IoStatus.Information = 0; + break; + + } else if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < + (sizeof(SENDCMDOUTPARAMS) + 512 - 1)) { + status = STATUS_BUFFER_TOO_SMALL; + Irp->IoStatus.Information = sizeof(SENDCMDOUTPARAMS) + 512 - 1; + 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; + default: + status = STATUS_INVALID_PARAMETER; + break; + } + } else { + + status = STATUS_INVALID_PARAMETER; + } + + 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; + Irp->IoStatus.Information = 0; + break; + + } else if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < + (sizeof(SENDCMDOUTPARAMS) - 1)) { + status = STATUS_BUFFER_TOO_SMALL; + Irp->IoStatus.Information = sizeof(SENDCMDOUTPARAMS) - 1; + 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))) { + + status = STATUS_BUFFER_TOO_SMALL; + Irp->IoStatus.Information = + sizeof(SENDCMDOUTPARAMS) - 1 + sizeof(IDEREGS); + 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) { + status = STATUS_INVALID_PARAMETER; + break; + } + + length += (sizeof(SENDCMDOUTPARAMS) > sizeof(SENDCMDINPARAMS)) ? 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; + + // + // 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); + + srbControl->ControlCode = controlCode; + + 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; + BOOLEAN writable = FALSE; + BOOLEAN mediaPresent = FALSE; + + DebugPrint((3, + "Disk.DiskDeviceControl: GetMediaTypes\n")); + + if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(GET_MEDIA_TYPES)) { + status = STATUS_BUFFER_TOO_SMALL; + Irp->IoStatus.Information = sizeof(GET_MEDIA_TYPES); + 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. + // + + blockDescriptor = (PMODE_PARAMETER_BLOCK)modeData; + (ULONG_PTR)blockDescriptor += sizeof(MODE_PARAMETER_HEADER); + + // + // Do some validation. + // + + if (modeData->BlockDescriptorLength != sizeof(MODE_PARAMETER_BLOCK)) { + + DebugPrint((1, + "DiskDeviceControl: BlockDescriptor length - " + "Expected %x, actual %x\n", + modeData->BlockDescriptorLength, + sizeof(MODE_PARAMETER_BLOCK))); + } + + DebugPrint((1, + "DiskDeviceControl: DensityCode %x, MediumType %x\n", + blockDescriptor->DensityCode, + modeData->MediumType)); + + if (TEST_FLAG(modeData->DeviceSpecificParameter, + MODE_DSP_WRITE_PROTECT)) { + writable = FALSE; + } else { + writable = TRUE; + } + + status = DiskDetermineMediaTypes(DeviceObject, + Irp, + modeData->MediumType, + blockDescriptor->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; + Irp->IoStatus.Information = sizeof(DISK_GEOMETRY); + 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); + + 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)) { + // DiskReleasePartitioningLock(fdoExtension); + 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); + // DiskReleasePartitioningLock(fdoExtension); + 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; + Irp->IoStatus.Information = sizeof(STORAGE_PREDICT_FAILURE); + 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; + IO_STATUS_BLOCK ioStatus; + 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; + } + } + + ExFreePool(readBuffer); + } + 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 + ); + 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 + // + { + TARGET_DEVICE_CUSTOM_NOTIFICATION Notification; + + 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 + // + { + TARGET_DEVICE_CUSTOM_NOTIFICATION Notification; + + 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); + 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); + break; + } + + case IOCTL_DISK_DELETE_DRIVE_LAYOUT: { + + CREATE_DISK CreateDiskInfo; + + // + // 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. + // + + RtlZeroMemory (&CreateDiskInfo, sizeof (CreateDiskInfo)); + CreateDiskInfo.PartitionStyle = PARTITION_STYLE_RAW; + + status = IoCreateDisk( + DeviceObject, + &CreateDiskInfo); + + + 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; + } + + // + // Send to FDO + // + + if(!commonExtension->IsFdo) { + + ClassReleaseRemoveLock(DeviceObject, Irp); + ExFreePool(srb); + SendToFdo(DeviceObject, Irp, status); + return status; + } + + bufferSize = sizeof(REASSIGN_BLOCKS) + + ((badBlocks->Count - 1) * sizeof(ULONG)); + + if (irpStack->Parameters.DeviceIoControl.InputBufferLength < + bufferSize) { + + status = STATUS_INFO_LENGTH_MISMATCH; + break; + } + + // + // 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); + + Irp->IoStatus.Status = status; + Irp->IoStatus.Information = 0; + ExFreePool(srb); + ClassReleaseRemoveLock(DeviceObject, Irp); + ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT); + + return(status); + } + + 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); + + if(commonExtension->IsFdo) { + + Irp->IoStatus.Information = 0; + } + } + + 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; + Irp->IoStatus.Information = sizeof(DISK_GEOMETRY); + 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; + + } + + DiskReleasePartitioningLock(fdoExtension); + + break; + } + + case IOCTL_DISK_GROW_PARTITION: { + + PDISK_GROW_PARTITION inputBuffer; + + // PDEVICE_OBJECT pdo; + PCOMMON_DEVICE_EXTENSION pdoExtension; + + 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")); + + Irp->IoStatus.Information = 0; + + if (irpStack->Parameters.DeviceIoControl.InputBufferLength < + sizeof(DISK_GROW_PARTITION)) { + + status = STATUS_INFO_LENGTH_MISMATCH; + Irp->IoStatus.Information = sizeof(DISK_GROW_PARTITION); + 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 + // + + pdoExtension = &commonExtension->ChildList->CommonExtension; + + while(pdoExtension != NULL) { + + // + // Is this the partition we are searching for? + // + + if(inputBuffer->PartitionNumber == pdoExtension->PartitionNumber) { + break; + } + + pdoExtension = &pdoExtension->ChildList->CommonExtension; + } + + // Did we find the partition? + + if(pdoExtension == NULL) { + status = STATUS_INVALID_PARAMETER; + ClassReleaseChildLock(fdoExtension); + DiskReleasePartitioningLock(fdoExtension); + break; + } + + ASSERT(pdoExtension); + + // + // Compute the new values for the partition to grow. + // + + newPartitionLength.QuadPart = + (pdoExtension->PartitionLength.QuadPart + + inputBuffer->BytesToGrow.QuadPart); + + newStoppingOffset.QuadPart = + (pdoExtension->StartingOffset.QuadPart + + newPartitionLength.QuadPart - 1); + + // + // 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 > + pdoExtension->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) pdoExtension, + 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 > + pdoExtension->PartitionLength.HighPart) { + + // + // Swap in one less than the high word. + // + + InterlockedExchange( + &(pdoExtension->PartitionLength.HighPart), + (newPartitionLength.HighPart - 1)); + } + + // + // Swap in the low part. + // + + InterlockedExchange( + &(pdoExtension->PartitionLength.LowPart), + newPartitionLength.LowPart); + + if(newPartitionLength.HighPart != + pdoExtension->PartitionLength.HighPart) { + + // + // Swap in one less than the high word. + // + + InterlockedExchange( + &(pdoExtension->PartitionLength.HighPart), + newPartitionLength.HighPart); + } + } + + // + // 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_DISK_UPDATE_PROPERTIES: { + + // + // Invalidate the partition table and re-enumerate the device. + // + + if(DiskInvalidatePartitionTable(fdoExtension, FALSE)) { + IoInvalidateDeviceRelations(fdoExtension->LowerPdo, BusRelations); + } + status = STATUS_SUCCESS; + + break; + } + + case IOCTL_DISK_MEDIA_REMOVAL: { + + // + // If the disk is not removable then don't allow this command. + // + + DebugPrint((2, "IOCTL_DISK_MEDIA_REMOVAL to device %p through irp %p\n", + DeviceObject, Irp)); + DebugPrint((2, "Device is a%s.\n", + commonExtension->IsFdo ? "n fdo" : " pdo")); + + if(!commonExtension->IsFdo) { + ClassReleaseRemoveLock(DeviceObject, Irp); + ExFreePool(srb); + SendToFdo(DeviceObject,Irp,status); + return status; + } + + if (!TEST_FLAG(DeviceObject->Characteristics, FILE_REMOVABLE_MEDIA)) { + status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + // + // Fall through and let the class driver process the request. + // + goto defaultHandler; + + } + + + +defaultHandler: + 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 +NTAPI +DiskShutdownFlush ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called for a shutdown and flush IRPs. These are sent by the + system before it actually shuts down or when the file system does a flush. + A synchronize cache command is sent to the device if it is write caching. + If the device is removable an unlock command will be sent. This routine + will sent a shutdown or flush Srb to the port driver. + +Arguments: + + DriverObject - Pointer to device object to being shutdown by system. + + Irp - IRP involved. + +Return Value: + + NT Status + +--*/ + +{ + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = commonExtension->PartitionZeroExtension; + + PIO_STACK_LOCATION irpStack; + PSCSI_REQUEST_BLOCK srb; + NTSTATUS status; + PCDB cdb; + + // + // 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; + } + + // + // Allocate SCSI 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 is removable and this is a shutdown. + // + + irpStack = IoGetCurrentIrpStackLocation(Irp); + + if (TEST_FLAG(DeviceObject->Characteristics, FILE_REMOVABLE_MEDIA) && + irpStack->MajorFunction == IRP_MJ_SHUTDOWN) { + + 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 = irpStack->MajorFunction == IRP_MJ_SHUTDOWN ? + SRB_FUNCTION_SHUTDOWN : SRB_FUNCTION_FLUSH; + + // + // 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; +} // end DiskShutdown() + + +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; + 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); + + // + // Zero SRB. + // + + RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK)); + + // + // 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 + ) + +{ + ULONG specialFlags = 0; + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension; + DISK_CACHE_INFORMATION cacheInfo; + NTSTATUS status; + + PAGED_CODE(); + + fdoExtension = Fdo->DeviceExtension; + + ASSERT(fdoExtension->CommonExtension.IsFdo); + + DebugPrint((1, "Disk.DisableWriteCache: Disabling Write Cache\n")); + + ClassGetDeviceParameter(fdoExtension, + DiskDeviceParameterSubkey, + DiskDeviceSpecialFlags, + &specialFlags); + + RtlZeroMemory(&cacheInfo, sizeof(DISK_CACHE_INFORMATION)); + + status = DiskGetCacheInformation(fdoExtension, &cacheInfo); + + if (NT_SUCCESS(status) && (cacheInfo.WriteCacheEnabled == TRUE)) { + + cacheInfo.WriteCacheEnabled = FALSE; + + status = DiskSetCacheInformation(fdoExtension, &cacheInfo); + + if (status == STATUS_INVALID_DEVICE_REQUEST) + { + // + // This device does not allow for + // the write cache to be disabled + // + SET_FLAG(specialFlags, HackDisableWriteCacheNotSupported); + + SET_FLAG(fdoExtension->ScanForSpecialFlags, CLASS_SPECIAL_DISABLE_WRITE_CACHE_NOT_SUPPORTED); + } + + // + // ISSUE ( April 5, 2001 ) : This should happen inside of DiskSetCacheInformation + // + CLEAR_FLAG(fdoExtension->DeviceFlags, DEV_WRITE_CACHE); + } + + // + // Set a flag in the registry to help + // identify this device across boots + // + SET_FLAG(specialFlags, HackDisableWriteCache); + + SET_FLAG(fdoExtension->ScanForSpecialFlags, CLASS_SPECIAL_DISABLE_WRITE_CACHE); + + ClassSetDeviceParameter(fdoExtension, + DiskDeviceParameterSubkey, + DiskDeviceSpecialFlags, + specialFlags); + + 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 +NTAPI +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; + + // + // 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_INVALID_CDB: { + + if (((cdb->CDB10.OperationCode == SCSIOP_READ) || + (cdb->CDB10.OperationCode == SCSIOP_WRITE)) && + (cdb->CDB10.ForceUnitAccess) && + TEST_FLAG(fdoExtension->DeviceFlags, DEV_WRITE_CACHE)) { + + // + // This device does not permit FUA while + // the DEV_WRITE_CACHE flag is turned on + // + + PIO_WORKITEM workItem = IoAllocateWorkItem(Fdo); + if (workItem) { + + IoQueueWorkItem(workItem, + DisableWriteCache, + CriticalWorkQueue, + workItem); + } + + 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: { + switch (senseBuffer->AdditionalSenseCode) { + case SCSI_ADSENSE_MEDIUM_CHANGED: { + invalidatePartitionTable = 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: + case SRB_STATUS_ERROR: { + invalidatePartitionTable = TRUE; + break; + } + } // end switch(Srb->SrbStatus) + } + + if(invalidatePartitionTable) { + if(DiskInvalidatePartitionTable(fdoExtension, FALSE)) { + 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 +DiskScanRegistryForSpecial( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension + ) + +/*++ + +Routine Description: + + This function checks the registry to see if the SCSI logical unit + requires special attention. + +Arguments: + + Fdo - Supplies the device object to be tested. + +Return Value: + + None. + +--*/ + +{ + ULONG specialFlags = 0; + + PAGED_CODE(); + + ClassGetDeviceParameter(FdoExtension, DiskDeviceParameterSubkey, DiskDeviceSpecialFlags, &specialFlags); + + if (TEST_FLAG(specialFlags, HackDisableWriteCache)) + { + // + // This device had previously failed to perform an FUA with the DEV_WRITE_CACHE + // flag turned on. Set a bit to inform DiskStartFdo() to disable the write cache + // + + SET_FLAG(FdoExtension->ScanForSpecialFlags, CLASS_SPECIAL_DISABLE_WRITE_CACHE); + } + + if (TEST_FLAG(specialFlags, HackDisableWriteCacheNotSupported)) + { + // + // This device does not permit disabling of the write cache + // + + SET_FLAG(FdoExtension->ScanForSpecialFlags, CLASS_SPECIAL_DISABLE_WRITE_CACHE_NOT_SUPPORTED); + } +} + + +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 +NTAPI +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; + + CacheInfo->ReadRetentionPriority = pageData->ReadRetensionPriority; + CacheInfo->WriteRetentionPriority = 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; + + ULONG errorCode; + 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) CacheInfo->WriteRetentionPriority; + pageData->ReadRetensionPriority = (UCHAR) 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. + // + + // + // First save away the current state of the disk cache so we know what to + // log if the request fails. + // + + if(TEST_FLAG(FdoExtension->DeviceFlags, DEV_WRITE_CACHE)) { + errorCode = IO_WRITE_CACHE_ENABLED; + } else { + errorCode = IO_WRITE_CACHE_DISABLED; + } + + 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); + errorCode = IO_WRITE_CACHE_ENABLED; + } else { + CLEAR_FLAG(FdoExtension->DeviceFlags, DEV_WRITE_CACHE); + errorCode = IO_WRITE_CACHE_DISABLED; + } + + break; + } + } + + { + PIO_ERROR_LOG_PACKET logEntry; + + // + // Log the appropriate informational or error entry. + // + + logEntry = IoAllocateErrorLogEntry( + FdoExtension->DeviceObject, + sizeof(IO_ERROR_LOG_PACKET) + (4 * sizeof(ULONG))); + + if (logEntry != NULL) { + + PDISK_DATA diskData = FdoExtension->CommonExtension.DriverData; + + logEntry->FinalStatus = status; + logEntry->ErrorCode = errorCode; + 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); + } + } + + ExFreePool(modeData); + 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; +} + + +#if 0 +#if defined(_X86_) + +NTSTATUS +DiskQuerySuggestedLinkName( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + The routine try to find a suggested link name from registry for Removable + using device object names of NT4 and NT3.51. + +Arguments: + + DeviceObject - Pointer to driver object created by system. + Irp - IRP involved. + +Return Value: + + NTSTATUS + +--*/ + +{ + PMOUNTDEV_SUGGESTED_LINK_NAME suggestedName; + WCHAR driveLetterNameBuffer[10]; + RTL_QUERY_REGISTRY_TABLE queryTable[2]; + PWSTR valueName; + UNICODE_STRING driveLetterName; + NTSTATUS status; + PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + PFUNCTIONAL_DEVICE_EXTENSION p0Extension = commonExtension->PartitionZeroExtension; + ULONG i, diskCount; + PCONFIGURATION_INFORMATION configurationInformation; + + PAGED_CODE(); + + DebugPrint((1, "DISK: IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME to device %#08lx" + " through irp %#08lx\n", + DeviceObject, Irp)); + + DebugPrint((1, " - DeviceNumber %d, - PartitionNumber %d\n", + p0Extension->DeviceNumber, + commonExtension->PartitionNumber)); + + if (!TEST_FLAG(DeviceObject->Characteristics, FILE_REMOVABLE_MEDIA)) { + + status = STATUS_NOT_FOUND; + return status; + } + + if (commonExtension->PartitionNumber == 0) { + + status = STATUS_NOT_FOUND; + return status; + } + + if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(MOUNTDEV_SUGGESTED_LINK_NAME)) { + + status = STATUS_INVALID_PARAMETER; + return status; + } + + valueName = ExAllocatePoolWithTag(PagedPool, + sizeof(WCHAR) * 64, + DISK_TAG_NEC_98); + + if (!valueName) { + status = STATUS_INSUFFICIENT_RESOURCES; + return status; + } + + // + // Look for a device object name of NT4. + // + swprintf(valueName, L"\\Device\\Harddisk%d\\Partition%d", + p0Extension->DeviceNumber, + commonExtension->PartitionNumber); + + driveLetterName.Buffer = driveLetterNameBuffer; + driveLetterName.MaximumLength = 20; + driveLetterName.Length = 0; + + RtlZeroMemory(queryTable, 2*sizeof(RTL_QUERY_REGISTRY_TABLE)); + queryTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED | + RTL_QUERY_REGISTRY_DIRECT; + queryTable[0].Name = valueName; + queryTable[0].EntryContext = &driveLetterName; + + status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, + L"\\Registry\\Machine\\System\\DISK", + queryTable, NULL, NULL); + + if (!NT_SUCCESS(status)) { + + // + // Look for a device object name of NT3.51. + // scsimo.sys on NT3.51 created it as \Device\OpticalDiskX. + // The number X were a serial number from zero on only Removable, + // so we look for it serially without above DeviceNumber and PartitionNumber. + // + + configurationInformation = IoGetConfigurationInformation(); + diskCount = configurationInformation->DiskCount; + + for (i = 0; i < diskCount; i++) { + swprintf(valueName, L"\\Device\\OpticalDisk%d",i); + + driveLetterName.Buffer = driveLetterNameBuffer; + driveLetterName.MaximumLength = 20; + driveLetterName.Length = 0; + + RtlZeroMemory(queryTable, 2*sizeof(RTL_QUERY_REGISTRY_TABLE)); + queryTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED | + RTL_QUERY_REGISTRY_DIRECT; + queryTable[0].Name = valueName; + queryTable[0].EntryContext = &driveLetterName; + + status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, + L"\\Registry\\Machine\\System\\DISK", + queryTable, NULL, NULL); + + if (NT_SUCCESS(status)) { + break; + } + } + + if (!NT_SUCCESS(status)) { + ExFreePool(valueName); + return status; + } + } + + if (driveLetterName.Length != 4 || + driveLetterName.Buffer[0] < 'A' || + driveLetterName.Buffer[0] > 'Z' || + driveLetterName.Buffer[1] != ':') { + + status = STATUS_NOT_FOUND; + ExFreePool(valueName); + return status; + } + + suggestedName = Irp->AssociatedIrp.SystemBuffer; + suggestedName->UseOnlyIfThereAreNoOtherLinks = TRUE; + suggestedName->NameLength = 28; + + Irp->IoStatus.Information = + FIELD_OFFSET(MOUNTDEV_SUGGESTED_LINK_NAME, Name) + 28; + + if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < + Irp->IoStatus.Information) { + + Irp->IoStatus.Information = + sizeof(MOUNTDEV_SUGGESTED_LINK_NAME); + status = STATUS_BUFFER_OVERFLOW; + ExFreePool(valueName); + return status; + } + + RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE, + L"\\Registry\\Machine\\System\\DISK", + valueName); + + ExFreePool(valueName); + + RtlCopyMemory(suggestedName->Name, L"\\DosDevices\\", 24); + suggestedName->Name[12] = driveLetterName.Buffer[0]; + suggestedName->Name[13] = ':'; + + return status; +} +#endif +#endif + + +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 invalidateBusRelations; + + + 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); + + // + // Issue a read capacity to update the apparent size of the disk. + // + + DiskReadDriveCapacity(fdoExtension->DeviceObject); + + 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; + Irp->IoStatus.Information = size; + + 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); + 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 invalidateBusRelations; + + + PAGED_CODE (); + + ASSERT ( DeviceObject ); + ASSERT ( Irp ); + + // + // Initialization + // + + fdoExtension = DeviceObject->DeviceExtension; + pdoExtension = DeviceObject->DeviceExtension; + commonExtension = DeviceObject->DeviceExtension; + + irpStack = IoGetCurrentIrpStackLocation(Irp); + diskData = (PDISK_DATA)(commonExtension->DriverData); + + // + // Issue a read capacity to update the apparent size of the disk. + // + + DiskReadDriveCapacity(fdoExtension->DeviceObject); + + // + // Get the drive layout information. + // + + DiskAcquirePartitioningLock (fdoExtension); + + status = DiskReadPartitionTableEx (fdoExtension, FALSE, &partitionList); + + if ( !NT_SUCCESS (status) ) { + DiskReleasePartitioningLock (fdoExtension); + return status; + } + + // + // Update the partition device objects and set valid partition + // numbers. + // + + ASSERT(diskData->UpdatePartitionRoutine != NULL); + diskData->UpdatePartitionRoutine(DeviceObject, partitionList); + + + 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 ) { + + RtlCopyMemory (Irp->AssociatedIrp.SystemBuffer, + partitionList, + size + ); + + Irp->IoStatus.Information = size; + Irp->IoStatus.Status = status; + invalidateBusRelations = TRUE; + + } else { + + Irp->IoStatus.Information = size; + Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL; + status = STATUS_BUFFER_TOO_SMALL; + invalidateBusRelations = FALSE; + } + + DiskReleasePartitioningLock(fdoExtension); + + if ( invalidateBusRelations ) { + 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; + Irp->IoStatus.Information = sizeof (DRIVE_LAYOUT_INFORMATION); + 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; + Irp->IoStatus.Information = listSize; + 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; + Irp->IoStatus.Information = + FIELD_OFFSET (DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry); + 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; + Irp->IoStatus.Information = listSize; + 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; + + RtlZeroMemory (&CreateDiskInfo, sizeof (CreateDiskInfo)); + 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; + Irp->IoStatus.Information = sizeof(PARTITION_INFORMATION); + 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; + Irp->IoStatus.Information = sizeof(PARTITION_INFORMATION); + 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; + Irp->IoStatus.Information = sizeof(GET_LENGTH_INFORMATION); + 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 { + + ASSERT ( diskData->PartitionStyle == PARTITION_STYLE_MBR ); + + 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; + Irp->IoStatus.Status = FIELD_OFFSET (DISK_GEOMETRY_EX, Data); + 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; +} + diff --git a/drivers/storage/class/disk_new/disk.h b/drivers/storage/class/disk_new/disk.h new file mode 100644 index 00000000000..beeb187933b --- /dev/null +++ b/drivers/storage/class/disk_new/disk.h @@ -0,0 +1,977 @@ +/*++ + +Copyright (C) Microsoft Corporation, 1991 - 1999 + +Module Name: + + disk.c + +Abstract: + + SCSI disk class driver + +Environment: + + kernel mode only + +Notes: + +Revision History: + +--*/ + +#include "ntddk.h" +#include "scsi.h" +#include +#include "classpnp.h" +#if defined(JAPAN) && defined(_X86_) +#include "machine.h" +#endif + +#include + +#if defined(_X86_) +#include "mountdev.h" +#endif + +#ifdef ExAllocatePool +#undef ExAllocatePool +#define ExAllocatePool #assert(FALSE) +#endif + +#define DISK_TAG_GENERAL ' DcS' // "ScD " - generic tag +#define DISK_TAG_SMART 'aDcS' // "ScDa" - SMART allocations +#define DISK_TAG_INFO_EXCEPTION 'ADcS' // "ScDA" - Info Exceptions +#define DISK_TAG_DISABLE_CACHE 'CDcS' // "ScDC" - disable cache paths +#define DISK_TAG_CCONTEXT 'cDcS' // "ScDc" - disk allocated completion context +#define DISK_TAG_DISK_GEOM 'GDcS' // "ScDG" - disk geometry buffer +#define DISK_TAG_UPDATE_GEOM 'gDcS' // "ScDg" - update disk geometry paths +#define DISK_TAG_SENSE_INFO 'IDcS' // "ScDI" - sense info buffers +#define DISK_TAG_PNP_ID 'iDcS' // "ScDp" - pnp ids +#define DISK_TAG_MODE_DATA 'MDcS' // "ScDM" - mode data buffer +#define DISK_CACHE_MBR_CHECK 'mDcS' // "ScDM" - mbr checksum code +#define DISK_TAG_NAME 'NDcS' // "ScDN" - disk name code +#define DISK_TAG_READ_CAP 'PDcS' // "ScDP" - read capacity buffer +#define DISK_TAG_PART_LIST 'pDcS' // "ScDp" - disk partition lists +#define DISK_TAG_SRB 'SDcS' // "ScDS" - srb allocation +#define DISK_TAG_START 'sDcS' // "ScDs" - start device paths +#define DISK_TAG_UPDATE_CAP 'UDcS' // "ScDU" - update capacity path +#define DISK_TAG_WI_CONTEXT 'WDcS' // "ScDW" - work-item context + +typedef +VOID +(*PDISK_UPDATE_PARTITIONS) ( + IN PDEVICE_OBJECT Fdo, + IN OUT PDRIVE_LAYOUT_INFORMATION_EX PartitionList + ); + +#if defined(_X86_) + +// +// Disk device data +// + +typedef enum _DISK_GEOMETRY_SOURCE { + DiskGeometryUnknown, + DiskGeometryFromBios, + DiskGeometryFromPort, + DiskGeometryFromNec98, + DiskGeometryGuessedFromBios, + DiskGeometryFromDefault +} DISK_GEOMETRY_SOURCE, *PDISK_GEOMETRY_SOURCE; +#endif + +// + +typedef struct _DISK_DATA { + + // + // This field is the ordinal of a partition as it appears on a disk. + // + + ULONG PartitionOrdinal; + + // + // How has this disk been partitioned? Either EFI or MBR. + // + + PARTITION_STYLE PartitionStyle; + + union { + + struct { + + // + // Disk signature (from MBR) + // + + ULONG Signature; + + // + // MBR checksum + // + + ULONG MbrCheckSum; + + // + // Number of hidden sectors for BPB. + // + + ULONG HiddenSectors; + + // + // Partition type of this device object + // + // This field is set by: + // + // 1. Initially set according to the partition list entry + // partition type returned by IoReadPartitionTable. + // + // 2. Subsequently set by the + // IOCTL_DISK_SET_PARTITION_INFORMATION I/O control + // function when IoSetPartitionInformation function + // successfully updates the partition type on the disk. + // + + UCHAR PartitionType; + + // + // Boot indicator - indicates whether this partition is a + // bootable (active) partition for this device + // + // This field is set according to the partition list entry boot + // indicator returned by IoReadPartitionTable. + // + + BOOLEAN BootIndicator; + + } Mbr; + + struct { + + // + // The DiskGUID field from the EFI partition header. + // + + GUID DiskId; + + // + // Partition type of this device object. + // + + GUID PartitionType; + + // + // Unique partition identifier for this partition. + // + + GUID PartitionId; + + // + // EFI partition attributes for this partition. + // + + ULONG64 Attributes; + + // + // EFI partition name of this partition. + // + + WCHAR PartitionName[36]; + + } Efi; + + }; // unnamed union + + struct { + // + // This flag is set when the well known name is created (through + // DiskCreateSymbolicLinks) and cleared when destroying it + // (by calling DiskDeleteSymbolicLinks). + // + + BOOLEAN WellKnownNameCreated : 1; + + // + // This flag is set when the PhysicalDriveN link is created (through + // DiskCreateSymbolicLinks) and is cleared when destroying it (through + // DiskDeleteSymbolicLinks) + // + + BOOLEAN PhysicalDriveLinkCreated : 1; + + } LinkStatus; + + // + // ReadyStatus - STATUS_SUCCESS indicates that the drive is ready for + // use. Any error status is to be returned as an explaination for why + // a request is failed. + // + // This was done solely for the zero-length partition case of having no + // media in a removable disk drive. When that occurs, and a read is sent + // to the zero-length non-partition-zero PDO that was created, we had to + // be able to fail the request with a reasonable value. This may not have + // been the best way to do this, but it works. + // + + NTSTATUS ReadyStatus; + + // + // Routine to be called when updating the disk partitions. This routine + // is different for removable and non-removable media and is called by + // (among other things) DiskEnumerateDevice + // + + PDISK_UPDATE_PARTITIONS UpdatePartitionRoutine; + + // + // SCSI address used for SMART operations. + // + + SCSI_ADDRESS ScsiAddress; + + // + // Event used to synchronize partitioning operations and enumerations. + // + + KEVENT PartitioningEvent; + + // + // These unicode strings hold the disk and volume interface strings. If + // the interfaces were not registered or could not be set then the string + // buffer will be NULL. + // + + UNICODE_STRING DiskInterfaceString; + UNICODE_STRING PartitionInterfaceString; + + // + // What type of failure prediction mechanism is available + // + + FAILURE_PREDICTION_METHOD FailurePredictionCapability; + BOOLEAN AllowFPPerfHit; + +#if defined(_X86_) + // + // This flag indiciates that a non-default geometry for this drive has + // already been determined by the disk driver. This field is ignored + // for removable media drives. + // + + DISK_GEOMETRY_SOURCE GeometrySource; + + // + // If GeometryDetermined is TRUE this will contain the geometry which was + // reported by the firmware or by the BIOS. For removable media drives + // this will contain the last geometry used when media was present. + // + + DISK_GEOMETRY RealGeometry; +#endif + + // + // Indicates that the cached partition table is valid when set. + // + + ULONG CachedPartitionTableValid; + + // + // The cached partition table - this is only valid if the previous + // flag is set. When invalidated the cached partition table will be + // freed and replaced the next time one of the partitioning functions is + // called. This allows the error handling routines to invalidate it by + // setting the flag and doesn't require that they obtain a lock. + // + + PDRIVE_LAYOUT_INFORMATION_EX CachedPartitionTable; + + // + // This mutex prevents more than one IOCTL_DISK_VERIFY from being + // sent down to the disk. This greatly reduces the possibility of + // a Denial-of-Service attack + // + + KMUTEX VerifyMutex; + +} DISK_DATA, *PDISK_DATA; + +// Define a general structure of identfing disk controllers with bad +// hardware. +// + +#define HackDisableTaggedQueuing (0x01) +#define HackDisableSynchronousTransfers (0x02) +#define HackDisableSpinDown (0x04) +#define HackDisableWriteCache (0x08) +#define HackCauseNotReportableHack (0x10) +#define HackRequiresStartUnitCommand (0x20) +#define HackDisableWriteCacheNotSupported (0x40) + + +#define DiskDeviceParameterSubkey L"Disk" +#define DiskDeviceSpecialFlags L"SpecialFlags" +#define DiskDeviceUserWriteCacheSetting L"UserWriteCacheSetting" + + +#define FUNCTIONAL_EXTENSION_SIZE sizeof(FUNCTIONAL_DEVICE_EXTENSION) + sizeof(DISK_DATA) +#define PHYSICAL_EXTENSION_SIZE sizeof(PHYSICAL_DEVICE_EXTENSION) + sizeof(DISK_DATA) + +#define MODE_DATA_SIZE 192 +#define VALUE_BUFFER_SIZE 2048 +#define SCSI_DISK_TIMEOUT 10 +#define PARTITION0_LIST_SIZE 4 + +#define MAX_MEDIA_TYPES 4 +typedef struct _DISK_MEDIA_TYPES_LIST { + PCHAR VendorId; + PCHAR ProductId; + PCHAR Revision; + const ULONG NumberOfTypes; + const ULONG NumberOfSides; + const STORAGE_MEDIA_TYPE MediaTypes[MAX_MEDIA_TYPES]; +} DISK_MEDIA_TYPES_LIST, *PDISK_MEDIA_TYPES_LIST; + +// +// WMI reregistration structures used for reregister work item +// +typedef struct +{ + SINGLE_LIST_ENTRY Next; + PDEVICE_OBJECT DeviceObject; + PIRP Irp; +} DISKREREGREQUEST, *PDISKREREGREQUEST; + +// +// Write cache setting as defined by the user +// +typedef enum _DISK_USER_WRITE_CACHE_SETTING +{ + DiskWriteCacheDisable = 0, + DiskWriteCacheEnable = 1, + DiskWriteCacheDefault = -1 + +} DISK_USER_WRITE_CACHE_SETTING, *PDISK_USER_WRITE_CACHE_SETTING; + +#define MAX_SECTORS_PER_VERIFY 0x200 + +// +// This is based off 100ns units +// +#define ONE_MILLI_SECOND ((ULONGLONG)10 * 1000) + +// +// Context for the work-item +// +typedef struct _DISK_VERIFY_WORKITEM_CONTEXT +{ + PIRP Irp; + PSCSI_REQUEST_BLOCK Srb; + PIO_WORKITEM WorkItem; + +} DISK_VERIFY_WORKITEM_CONTEXT, *PDISK_VERIFY_WORKITEM_CONTEXT; + +// +// Poll for Failure Prediction every hour +// +#define DISK_DEFAULT_FAILURE_POLLING_PERIOD 1 * 60 * 60 + +// +// Static global lookup tables. +// + +extern CLASSPNP_SCAN_FOR_SPECIAL_INFO DiskBadControllers[]; +extern const DISK_MEDIA_TYPES_LIST DiskMediaTypes[]; + +// +// Macros +// + +// +// Routine prototypes. +// + + +NTSTATUS +NTAPI +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ); + +VOID +NTAPI +DiskUnload( + IN PDRIVER_OBJECT DriverObject + ); + +NTSTATUS +NTAPI +DiskAddDevice( + IN PDRIVER_OBJECT DriverObject, + IN PDEVICE_OBJECT Pdo + ); + +NTSTATUS +NTAPI +DiskInitFdo( + IN PDEVICE_OBJECT Fdo + ); + +NTSTATUS +NTAPI +DiskInitPdo( + IN PDEVICE_OBJECT Pdo + ); + +NTSTATUS +NTAPI +DiskStartFdo( + IN PDEVICE_OBJECT Fdo + ); + +NTSTATUS +NTAPI +DiskStartPdo( + IN PDEVICE_OBJECT Pdo + ); + +NTSTATUS +NTAPI +DiskStopDevice( + IN PDEVICE_OBJECT DeviceObject, + IN UCHAR Type + ); + +NTSTATUS +NTAPI +DiskRemoveDevice( + IN PDEVICE_OBJECT DeviceObject, + IN UCHAR Type + ); + +NTSTATUS +NTAPI +DiskReadWriteVerification( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NTAPI +DiskDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +VOID +NTAPI +DiskFdoProcessError( + PDEVICE_OBJECT DeviceObject, + PSCSI_REQUEST_BLOCK Srb, + NTSTATUS *Status, + BOOLEAN *Retry + ); + +NTSTATUS +NTAPI +DiskShutdownFlush( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NTAPI +DiskGetCacheInformation( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN PDISK_CACHE_INFORMATION CacheInfo + ); + +NTSTATUS +NTAPI +DiskSetCacheInformation( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN PDISK_CACHE_INFORMATION CacheInfo + ); + +VOID +NTAPI +DisableWriteCache( + IN PDEVICE_OBJECT DeviceObject, + IN PIO_WORKITEM WorkItem + ); + +VOID +NTAPI +DiskIoctlVerify( + IN PDEVICE_OBJECT DeviceObject, + IN PDISK_VERIFY_WORKITEM_CONTEXT Context + ); + +NTSTATUS +NTAPI +DiskModeSelect( + IN PDEVICE_OBJECT DeviceObject, + IN PCHAR ModeSelectBuffer, + IN ULONG Length, + IN BOOLEAN SavePage + ); + +// +// We need to validate that the self test subcommand is valid and +// appropriate. Right now we allow subcommands 0, 1 and 2 which are non +// captive mode tests. Once we figure out a way to know if it is safe to +// run a captive test then we can allow captive mode tests. Also if the +// atapi 5 spec is ever updated to denote that bit 7 is the captive +// mode bit, we can allow any request that does not have bit 7 set. Until +// that is done we want to be sure +// +#define DiskIsValidSmartSelfTest(Subcommand) \ + ( ((Subcommand) == SMART_OFFLINE_ROUTINE_OFFLINE) || \ + ((Subcommand) == SMART_SHORT_SELFTEST_OFFLINE) || \ + ((Subcommand) == SMART_EXTENDED_SELFTEST_OFFLINE) ) + + +NTSTATUS +NTAPI +DiskPerformSmartCommand( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN ULONG SrbControlCode, + IN UCHAR Command, + IN UCHAR Feature, + IN UCHAR SectorCount, + IN UCHAR SectorNumber, + IN OUT PSRB_IO_CONTROL SrbControl, + OUT PULONG BufferSize + ); + +NTSTATUS +NTAPI +DiskGetInfoExceptionInformation( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + OUT PMODE_INFO_EXCEPTIONS ReturnPageData + ); + +NTSTATUS +NTAPI +DiskSetInfoExceptionInformation( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN PMODE_INFO_EXCEPTIONS PageData + ); + +NTSTATUS +NTAPI +DiskDetectFailurePrediction( + PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + PFAILURE_PREDICTION_METHOD FailurePredictCapability + ); + +BOOLEAN +NTAPI +EnumerateBusKey( + IN PFUNCTIONAL_DEVICE_EXTENSION DeviceExtension, + HANDLE BusKey, + PULONG DiskNumber + ); + +NTSTATUS +NTAPI +DiskCreateFdo( + IN PDRIVER_OBJECT DriverObject, + IN PDEVICE_OBJECT LowerDeviceObject, + IN PULONG DeviceCount, + IN BOOLEAN DasdAccessOnly + ); + +VOID +NTAPI +UpdateDeviceObjects( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +VOID +NTAPI +DiskSetSpecialHacks( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN ULONG_PTR Data + ); + +VOID +NTAPI +DiskScanRegistryForSpecial( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension + ); + +VOID +NTAPI +ResetBus( + IN PDEVICE_OBJECT DeviceObject + ); + +NTSTATUS +NTAPI +DiskEnumerateDevice( + IN PDEVICE_OBJECT Fdo + ); + +NTSTATUS +NTAPI +DiskQueryId( + IN PDEVICE_OBJECT Pdo, + IN BUS_QUERY_ID_TYPE IdType, + IN PUNICODE_STRING UnicodeIdString + ); + +NTSTATUS +NTAPI +DiskQueryPnpCapabilities( + IN PDEVICE_OBJECT DeviceObject, + IN PDEVICE_CAPABILITIES Capabilities + ); + +NTSTATUS +NTAPI +DiskGenerateDeviceName( + IN BOOLEAN IsFdo, + IN ULONG DeviceNumber, + IN OPTIONAL ULONG PartitionNumber, + IN OPTIONAL PLARGE_INTEGER StartingOffset, + IN OPTIONAL PLARGE_INTEGER PartitionLength, + OUT PUCHAR *RawName + ); + +VOID +NTAPI +DiskCreateSymbolicLinks( + IN PDEVICE_OBJECT DeviceObject + ); + +VOID +NTAPI +DiskUpdatePartitions( + IN PDEVICE_OBJECT Fdo, + IN OUT PDRIVE_LAYOUT_INFORMATION_EX PartitionList + ); + +VOID +NTAPI +DiskUpdateRemovablePartitions( + IN PDEVICE_OBJECT Fdo, + IN OUT PDRIVE_LAYOUT_INFORMATION_EX PartitionList + ); + +NTSTATUS +NTAPI +DiskCreatePdo( + IN PDEVICE_OBJECT Fdo, + IN ULONG PartitionOrdinal, + IN PPARTITION_INFORMATION_EX PartitionEntry, + IN PARTITION_STYLE PartitionStyle, + OUT PDEVICE_OBJECT *Pdo + ); + +VOID +NTAPI +DiskDeleteSymbolicLinks( + IN PDEVICE_OBJECT DeviceObject + ); + +NTSTATUS +NTAPI +DiskPdoQueryWmiRegInfo( + IN PDEVICE_OBJECT DeviceObject, + OUT ULONG *RegFlags, + OUT PUNICODE_STRING InstanceName + ); + +NTSTATUS +NTAPI +DiskPdoQueryWmiDataBlock( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN ULONG GuidIndex, + IN ULONG BufferAvail, + OUT PUCHAR Buffer + ); + +NTSTATUS +NTAPI +DiskPdoSetWmiDataBlock( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN ULONG GuidIndex, + IN ULONG BufferSize, + IN PUCHAR Buffer + ); + +NTSTATUS +NTAPI +DiskPdoSetWmiDataItem( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN ULONG GuidIndex, + IN ULONG DataItemId, + IN ULONG BufferSize, + IN PUCHAR Buffer + ); + +NTSTATUS +NTAPI +DiskPdoExecuteWmiMethod( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN ULONG GuidIndex, + IN ULONG MethodId, + IN ULONG InBufferSize, + IN ULONG OutBufferSize, + IN PUCHAR Buffer + ); + +NTSTATUS +NTAPI +DiskFdoQueryWmiRegInfo( + IN PDEVICE_OBJECT DeviceObject, + OUT ULONG *RegFlags, + OUT PUNICODE_STRING InstanceName + ); + +NTSTATUS +NTAPI +DiskFdoQueryWmiRegInfoEx( + IN PDEVICE_OBJECT DeviceObject, + OUT ULONG *RegFlags, + OUT PUNICODE_STRING InstanceName, + OUT PUNICODE_STRING MofName + ); + +NTSTATUS +NTAPI +DiskFdoQueryWmiDataBlock( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN ULONG GuidIndex, + IN ULONG BufferAvail, + OUT PUCHAR Buffer + ); + +NTSTATUS +NTAPI +DiskFdoSetWmiDataBlock( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN ULONG GuidIndex, + IN ULONG BufferSize, + IN PUCHAR Buffer + ); + +NTSTATUS +NTAPI +DiskFdoSetWmiDataItem( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN ULONG GuidIndex, + IN ULONG DataItemId, + IN ULONG BufferSize, + IN PUCHAR Buffer + ); + +NTSTATUS +NTAPI +DiskFdoExecuteWmiMethod( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN ULONG GuidIndex, + IN ULONG MethodId, + IN ULONG InBufferSize, + IN ULONG OutBufferSize, + IN PUCHAR Buffer + ); + +NTSTATUS +NTAPI +DiskWmiFunctionControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN ULONG GuidIndex, + IN CLASSENABLEDISABLEFUNCTION Function, + IN BOOLEAN Enable + ); + +NTSTATUS +NTAPI +DiskReadFailurePredictStatus( + PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + PSTORAGE_FAILURE_PREDICT_STATUS DiskSmartStatus + ); + +NTSTATUS +NTAPI +DiskReadFailurePredictData( + PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + PSTORAGE_FAILURE_PREDICT_DATA DiskSmartData + ); + +NTSTATUS +NTAPI +DiskEnableDisableFailurePrediction( + PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + BOOLEAN Enable + ); + +NTSTATUS +NTAPI +DiskEnableDisableFailurePredictPolling( + PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + BOOLEAN Enable, + ULONG PollTimeInSeconds + ); + +VOID +NTAPI +DiskAcquirePartitioningLock( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension + ); + +VOID +NTAPI +DiskReleasePartitioningLock( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension + ); + +NTSTATUS NTAPI DiskInitializeReregistration( + void + ); + +extern GUIDREGINFO DiskWmiFdoGuidList[]; +extern GUIDREGINFO DiskWmiPdoGuidList[]; + +#if defined(_X86_) +NTSTATUS +NTAPI +DiskReadDriveCapacity( + IN PDEVICE_OBJECT Fdo + ); +#else +#define DiskReadDriveCapacity(Fdo) ClassReadDriveCapacity(Fdo) +#endif + + +#if defined(_X86_) + +#if 0 +NTSTATUS +DiskQuerySuggestedLinkName( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); +#endif + +NTSTATUS +NTAPI +DiskSaveDetectInfo( + PDRIVER_OBJECT DriverObject + ); + +VOID +NTAPI +DiskCleanupDetectInfo( + IN PDRIVER_OBJECT DriverObject + ); + +VOID +NTAPI +DiskDriverReinitialization ( + IN PDRIVER_OBJECT DriverObject, + IN PVOID Nothing, + IN ULONG Count + ); + +#endif + +VOID +NTAPI +DiskConvertPartitionToExtended( + IN PPARTITION_INFORMATION Partition, + OUT PPARTITION_INFORMATION_EX PartitionEx + ); + +PDRIVE_LAYOUT_INFORMATION_EX +NTAPI +DiskConvertLayoutToExtended( + IN CONST PDRIVE_LAYOUT_INFORMATION Layout + ); + +PDRIVE_LAYOUT_INFORMATION +NTAPI +DiskConvertExtendedToLayout( + IN CONST PDRIVE_LAYOUT_INFORMATION_EX LayoutEx + ); + +NTSTATUS +NTAPI +DiskReadPartitionTableEx( + IN PFUNCTIONAL_DEVICE_EXTENSION Fdo, + IN BOOLEAN BypassCache, + OUT PDRIVE_LAYOUT_INFORMATION_EX* DriveLayout + ); + +NTSTATUS +NTAPI +DiskWritePartitionTableEx( + IN PFUNCTIONAL_DEVICE_EXTENSION Fdo, + IN PDRIVE_LAYOUT_INFORMATION_EX DriveLayout + ); + +NTSTATUS +NTAPI +DiskSetPartitionInformationEx( + IN PFUNCTIONAL_DEVICE_EXTENSION Fdo, + IN ULONG PartitionNumber, + IN struct _SET_PARTITION_INFORMATION_EX* PartitionInfo + ); + +NTSTATUS +NTAPI +DiskSetPartitionInformation( + IN PFUNCTIONAL_DEVICE_EXTENSION Fdo, + IN ULONG SectorSize, + IN ULONG PartitionNumber, + IN ULONG PartitionType + ); + +NTSTATUS +NTAPI +DiskVerifyPartitionTable( + IN PFUNCTIONAL_DEVICE_EXTENSION Fdo, + IN BOOLEAN FixErrors + ); + +BOOLEAN +NTAPI +DiskInvalidatePartitionTable( + IN PFUNCTIONAL_DEVICE_EXTENSION Fdo, + IN BOOLEAN PartitionLockHeld + ); + +#if defined (_X86_) +NTSTATUS +NTAPI +DiskGetDetectInfo( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + OUT PDISK_DETECTION_INFO DetectInfo + ); + +NTSTATUS +NTAPI +DiskReadSignature( + IN PDEVICE_OBJECT Fdo + ); + +#else +#define DiskGetDetectInfo(FdoExtension, DetectInfo) (STATUS_UNSUCCESSFUL) +#endif + + +#define DiskHashGuid(Guid) (((PULONG) &Guid)[0] ^ ((PULONG) &Guid)[0] ^ ((PULONG) &Guid)[0] ^ ((PULONG) &Guid)[0]) + diff --git a/drivers/storage/class/disk_new/disk.rc b/drivers/storage/class/disk_new/disk.rc new file mode 100644 index 00000000000..14825451b14 --- /dev/null +++ b/drivers/storage/class/disk_new/disk.rc @@ -0,0 +1,23 @@ +//+------------------------------------------------------------------------- +// +// Microsoft Windows +// +// Copyright (C) Microsoft Corporation, 1996 - 1999 +// +// File: disk.rc +// +//-------------------------------------------------------------------------- + +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "PnP Disk Driver" +#define VER_INTERNALNAME_STR "scsidisk.sys" +#define VER_ORIGINALFILENAME_STR "scsidisk.sys" +#define VER_LANGNEUTRAL + +#include "common.ver" + diff --git a/drivers/storage/class/disk_new/diskdev.inf b/drivers/storage/class/disk_new/diskdev.inf new file mode 100644 index 00000000000..d14de6688bf --- /dev/null +++ b/drivers/storage/class/disk_new/diskdev.inf @@ -0,0 +1,96 @@ +; disk.inf +; +; Installation inf for the Disk drive adapter. +; +; SAMPLE INF File for Class Driver +; FOR DDK - Driver Development Kit +; +; (c) Copyright 1999 Microsoft Corp. +; + +[Version] +Signature="$Windows NT$" +Provider=%MS% +ClassGUID={4d36e967-e325-11ce-bfc1-08002be10318} +Class=DiskDrive +; CatalogFile=disk.cat ; Supply your own catalog file + ; see DDK Doc. +DriverVer=08/27/1999 + +[DestinationDirs] +DefaultDestDir = 12 + +; +; Driver information +; + +[Manufacturer] +%MS% = MS.Mfg + +[MS.Mfg] +%MS.DeviceDesc0% = disk, GenDisk +%MS.DeviceDesc1% = disk, GenOptical + + +; +; General installation section +; + +[disk] + +; +; File sections +; + + +; +; Service Installation +; + +[disk.Services] +AddService = disk, 0x00000002 , disk_Service_Inst + +[disk_Service_Inst] +DisplayName = %disk.SvcDesc% +ServiceType = 1 ; SERVICE_KERNEL_DRIVER +StartType = 1 ; SERVICE_SYSTEM_START +ErrorControl = 0x1 ; SERVICE_ERROR_NORMAL +LoadOrderGroup = Pointer Port +ServiceBinary = %12%\disk.sys + + +; +; Source file information +; + +[SourceDisksNames.x86] +1 = %DiskId1%,,,"" + +[SourceDisksFiles] +; Files for disk Microsoft Corp. Installation Disk #1 (DiskDrive) +disk.sys = 1,, + + +[Strings] + +; +; Non-Localizable Strings +; + +REG_SZ = 0x00000000 +REG_MULTI_SZ = 0x00010000 +REG_EXPAND_SZ = 0x00020000 +REG_BINARY = 0x00000001 +REG_DWORD = 0x00010001 +SERVICEROOT = "System\CurrentControlSet\Services" + +; +; Localizable Strings +; + +MS.DeviceDesc0 = "Disk drive" +MS.DeviceDesc1 = "Optical disk drive" +DiskId1 = "Microsoft Corp. Installation Disk #1 (DiskDrive)" +MS = "Microsoft Corp." +disk.SvcDesc="Disk Drive" + diff --git a/drivers/storage/class/disk_new/diskwmi.c b/drivers/storage/class/disk_new/diskwmi.c new file mode 100644 index 00000000000..070e191823b --- /dev/null +++ b/drivers/storage/class/disk_new/diskwmi.c @@ -0,0 +1,3424 @@ +/*++ + +Copyright (C) Microsoft Corporation, 1991 - 1999 + +Module Name: + + diskwmi.c + +Abstract: + + SCSI disk class driver - WMI support routines + +Environment: + + kernel mode only + +Notes: + +Revision History: + +--*/ + +#include "disk.h" + +NTSTATUS +DiskSendFailurePredictIoctl( + PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + PSTORAGE_PREDICT_FAILURE checkFailure + ); + +NTSTATUS +DiskGetIdentifyInfo( + PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + PBOOLEAN SupportSmart + ); + +NTSTATUS +DiskDetectFailurePrediction( + PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + PFAILURE_PREDICTION_METHOD FailurePredictCapability + ); + +NTSTATUS +DiskReadFailurePredictThresholds( + PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + PSTORAGE_FAILURE_PREDICT_THRESHOLDS DiskSmartThresholds + ); + +NTSTATUS +DiskReadSmartLog( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN UCHAR SectorCount, + IN UCHAR LogAddress, + OUT PUCHAR Buffer + ); + +NTSTATUS +DiskWriteSmartLog( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN UCHAR SectorCount, + IN UCHAR LogAddress, + IN PUCHAR Buffer + ); + +void DiskReregWorker( + IN PVOID Context + ); + +// +// WMI reregistration globals +// +// Since it will take too long to do a mode sense on some drive, we +// need a good way to effect the mode sense for the info exceptions +// mode page so that we can determine if SMART is supported and enabled +// for the drive. So the strategy is to do an asynchronous mode sense +// when the device starts and then look at the info exceptions mode +// page within the completion routine. Now within the completion +// routine we cannot call IoWMIRegistrationControl since we are at DPC +// level, so we create a stack of device objects that will be processed +// by a single work item that is fired off only when the stack +// transitions from empty to non empty. +// +WORK_QUEUE_ITEM DiskReregWorkItem; +SINGLE_LIST_ENTRY DiskReregHead; +KSPIN_LOCK DiskReregSpinlock; +LONG DiskReregWorkItems; + +GUIDREGINFO DiskWmiFdoGuidList[] = +{ + { + WMI_DISK_GEOMETRY_GUID, + 1, + 0 + }, + + { + WMI_STORAGE_FAILURE_PREDICT_STATUS_GUID, + 1, + WMIREG_FLAG_EXPENSIVE + }, + + { + WMI_STORAGE_FAILURE_PREDICT_DATA_GUID, + 1, + WMIREG_FLAG_EXPENSIVE + }, + + { + WMI_STORAGE_FAILURE_PREDICT_FUNCTION_GUID, + 1, + WMIREG_FLAG_EXPENSIVE + }, + + { + WMI_STORAGE_PREDICT_FAILURE_EVENT_GUID, + 1, + WMIREG_FLAG_EVENT_ONLY_GUID + }, + + { + WMI_STORAGE_FAILURE_PREDICT_THRESHOLDS_GUID, + 1, + WMIREG_FLAG_EXPENSIVE + }, + + { + WMI_STORAGE_SCSI_INFO_EXCEPTIONS_GUID, + 1, + 0 + }, + + +}; + + +GUID DiskPredictFailureEventGuid = WMI_STORAGE_PREDICT_FAILURE_EVENT_GUID; + +#define DiskGeometryGuid 0 +#define SmartStatusGuid 1 +#define SmartDataGuid 2 +#define SmartPerformFunction 3 + #define AllowDisallowPerformanceHit 1 + #define EnableDisableHardwareFailurePrediction 2 + #define EnableDisableFailurePredictionPolling 3 + #define GetFailurePredictionCapability 4 + #define EnableOfflineDiags 5 + +#define SmartEventGuid 4 +#define SmartThresholdsGuid 5 +#define ScsiInfoExceptionsGuid 6 + +#if 0 + // + // Enable this to add WMI support for PDOs +GUIDREGINFO DiskWmiPdoGuidList[] = +{ + { + // {25007F51-57C2-11d1-A528-00A0C9062910} + { 0x25007f52, 0x57c2, 0x11d1, + { 0xa5, 0x28, 0x0, 0xa0, 0xc9, 0x6, 0x29, 0x10 } }, + 0 + }, + +}; + +ULONG DiskDummyData[4] = { 1, 2, 3, 4}; +#endif + +#ifdef ALLOC_PRAGMA + +#pragma alloc_text(PAGE, DiskWmiFunctionControl) +#pragma alloc_text(PAGE, DiskFdoQueryWmiRegInfo) +#pragma alloc_text(PAGE, DiskFdoQueryWmiDataBlock) +#pragma alloc_text(PAGE, DiskFdoSetWmiDataBlock) +#pragma alloc_text(PAGE, DiskFdoSetWmiDataItem) +#pragma alloc_text(PAGE, DiskFdoExecuteWmiMethod) + +#pragma alloc_text(PAGE, DiskDetectFailurePrediction) +#pragma alloc_text(PAGE, DiskEnableDisableFailurePrediction) +#pragma alloc_text(PAGE, DiskEnableDisableFailurePredictPolling) +#pragma alloc_text(PAGE, DiskReadFailurePredictStatus) +#pragma alloc_text(PAGE, DiskReadFailurePredictData) +#pragma alloc_text(PAGE, DiskReadFailurePredictThresholds) +#pragma alloc_text(PAGE, DiskGetIdentifyInfo) +#pragma alloc_text(PAGE, DiskReadSmartLog) +#pragma alloc_text(PAGE, DiskWriteSmartLog) + +#pragma alloc_text(PAGE, DiskPerformSmartCommand) + +#pragma alloc_text(PAGE, DiskSendFailurePredictIoctl) + +#pragma alloc_text(PAGE, DiskReregWorker) +#pragma alloc_text(PAGE, DiskInitializeReregistration) + +#endif + + +// +// SMART/IDE specific routines + +// +// Read SMART data attributes. +// SrbControl should be sizeof(SRB_IO_CONTROL) + +// (sizeof(SENDCMDINPARAMS)-1) + +// READ_ATTRIBUTE_BUFFER_SIZE +// Attribute data returned at &SendCmdOutParams->bBuffer[0] +// +#define DiskReadSmartData(FdoExtension, \ + SrbControl, \ + BufferSize) \ + DiskPerformSmartCommand(FdoExtension, \ + IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS, \ + SMART_CMD, \ + READ_ATTRIBUTES, \ + 0, \ + 0, \ + (SrbControl), \ + (BufferSize)) + + +// +// Read SMART data thresholds. +// SrbControl should be sizeof(SRB_IO_CONTROL) + +// (sizeof(SENDCMDINPARAMS)-1) + +// READ_THRESHOLD_BUFFER_SIZE +// Attribute data returned at &SendCmdOutParams->bBuffer[0] +// +#define DiskReadSmartThresholds(FdoExtension, \ + SrbControl, \ + BufferSize) \ + DiskPerformSmartCommand(FdoExtension, \ + IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS, \ + SMART_CMD, \ + READ_THRESHOLDS, \ + 0, \ + 0, \ + (SrbControl), \ + (BufferSize)) + + +// +// Read SMART status +// SrbControl should be sizeof(SRB_IO_CONTROL) + +// (sizeof(SENDCMDINPARAMS)-1) + +// sizeof(IDEREGS) +// Failure predicted if cmdOutParameters[3] == 0xf4 and [4] == 0x2c +// +#define DiskReadSmartStatus(FdoExtension, \ + SrbControl, \ + BufferSize) \ + DiskPerformSmartCommand(FdoExtension, \ + IOCTL_SCSI_MINIPORT_RETURN_STATUS, \ + SMART_CMD, \ + RETURN_SMART_STATUS, \ + 0, \ + 0, \ + (SrbControl), \ + (BufferSize)) + + +// +// Read disks IDENTIFY data +// SrbControl should be sizeof(SRB_IO_CONTROL) + +// (sizeof(SENDCMDINPARAMS)-1) + +// sizeof(IDENTIFY_BUFFER_SIZE) +// Identify data returned at &cmdOutParams.bBuffer[0] +// +#define DiskGetIdentifyData(FdoExtension, \ + SrbControl, \ + BufferSize) \ + DiskPerformSmartCommand(FdoExtension, \ + IOCTL_SCSI_MINIPORT_IDENTIFY, \ + ID_CMD, \ + 0, \ + 0, \ + 0, \ + (SrbControl), \ + (BufferSize)) + + +// +// Enable SMART +// +_inline NTSTATUS +DiskEnableSmart( + PFUNCTIONAL_DEVICE_EXTENSION FdoExtension + ) +{ + UCHAR srbControl[sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS)]; + ULONG bufferSize = sizeof(srbControl); + + return DiskPerformSmartCommand(FdoExtension, + IOCTL_SCSI_MINIPORT_ENABLE_SMART, + SMART_CMD, + ENABLE_SMART, + 0, + 0, + (PSRB_IO_CONTROL)srbControl, + &bufferSize); +} + +// +// Disable SMART +// +_inline NTSTATUS +DiskDisableSmart( + PFUNCTIONAL_DEVICE_EXTENSION FdoExtension + ) +{ + UCHAR srbControl[sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS)]; + ULONG bufferSize = sizeof(srbControl); + return DiskPerformSmartCommand(FdoExtension, + IOCTL_SCSI_MINIPORT_DISABLE_SMART, + SMART_CMD, + DISABLE_SMART, + 0, + 0, + (PSRB_IO_CONTROL)srbControl, + &bufferSize); +} + +// +// Enable Attribute Autosave +// +_inline NTSTATUS +DiskEnableSmartAttributeAutosave( + PFUNCTIONAL_DEVICE_EXTENSION FdoExtension + ) +{ + UCHAR srbControl[sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS)]; + ULONG bufferSize = sizeof(srbControl); + return DiskPerformSmartCommand(FdoExtension, + IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE, + SMART_CMD, + ENABLE_DISABLE_AUTOSAVE, + 0xf1, + 0, + (PSRB_IO_CONTROL)srbControl, + &bufferSize); +} + +// +// Disable Attribute Autosave +// +_inline NTSTATUS +DiskDisableSmartAttributeAutosave( + PFUNCTIONAL_DEVICE_EXTENSION FdoExtension + ) +{ + UCHAR srbControl[sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS)]; + ULONG bufferSize = sizeof(srbControl); + return DiskPerformSmartCommand(FdoExtension, + IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE, + SMART_CMD, + ENABLE_DISABLE_AUTOSAVE, + 0x00, + 0, + (PSRB_IO_CONTROL)srbControl, + &bufferSize); +} + +// +// Initialize execution of SMART online diagnostics +// +_inline NTSTATUS +DiskExecuteSmartDiagnostics( + PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + UCHAR Subcommand + ) +{ + UCHAR srbControl[sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS)]; + ULONG bufferSize = sizeof(srbControl); + return DiskPerformSmartCommand(FdoExtension, + IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS, + SMART_CMD, + EXECUTE_OFFLINE_DIAGS, + 0, + Subcommand, + (PSRB_IO_CONTROL)srbControl, + &bufferSize); +} + + +NTSTATUS +DiskReadSmartLog( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN UCHAR SectorCount, + IN UCHAR LogAddress, + OUT PUCHAR Buffer + ) +{ + PSRB_IO_CONTROL srbControl; + NTSTATUS status; + PSENDCMDOUTPARAMS sendCmdOutParams; + ULONG logSize, bufferSize; + + PAGED_CODE(); + + logSize = SectorCount * SMART_LOG_SECTOR_SIZE; + bufferSize = sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS) - 1 + + logSize; + + srbControl = ExAllocatePoolWithTag(NonPagedPool, + bufferSize, + DISK_TAG_SMART); + + if (srbControl != NULL) + { + status = DiskPerformSmartCommand(FdoExtension, + IOCTL_SCSI_MINIPORT_READ_SMART_LOG, + SMART_CMD, + SMART_READ_LOG, + SectorCount, + LogAddress, + srbControl, + &bufferSize); + + if (NT_SUCCESS(status)) + { + sendCmdOutParams = (PSENDCMDOUTPARAMS)((PUCHAR)srbControl + + sizeof(SRB_IO_CONTROL)); + RtlCopyMemory(Buffer, + &sendCmdOutParams->bBuffer[0], + logSize); + } + + ExFreePool(srbControl); + } else { + status = STATUS_INSUFFICIENT_RESOURCES; + } + return(status); +} + + +NTSTATUS +DiskWriteSmartLog( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN UCHAR SectorCount, + IN UCHAR LogAddress, + IN PUCHAR Buffer + ) +{ + PSRB_IO_CONTROL srbControl; + NTSTATUS status; + PSENDCMDINPARAMS sendCmdInParams; + ULONG logSize, bufferSize; + + PAGED_CODE(); + + logSize = SectorCount * SMART_LOG_SECTOR_SIZE; + bufferSize = sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS) - 1 + + logSize; + + srbControl = ExAllocatePoolWithTag(NonPagedPool, + bufferSize, + DISK_TAG_SMART); + + if (srbControl != NULL) + { + sendCmdInParams = (PSENDCMDINPARAMS)((PUCHAR)srbControl + + sizeof(SRB_IO_CONTROL)); + RtlCopyMemory(&sendCmdInParams->bBuffer[0], + Buffer, + logSize); + status = DiskPerformSmartCommand(FdoExtension, + IOCTL_SCSI_MINIPORT_WRITE_SMART_LOG, + SMART_CMD, + SMART_WRITE_LOG, + SectorCount, + LogAddress, + srbControl, + &bufferSize); + + ExFreePool(srbControl); + } else { + status = STATUS_INSUFFICIENT_RESOURCES; + } + return(status); +} + +NTSTATUS +DiskPerformSmartCommand( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN ULONG SrbControlCode, + IN UCHAR Command, + IN UCHAR Feature, + IN UCHAR SectorCount, + IN UCHAR SectorNumber, + IN OUT PSRB_IO_CONTROL SrbControl, + OUT PULONG BufferSize + ) +/*++ + +Routine Description: + + This routine will perform some SMART command + +Arguments: + + FdoExtension is the FDO device extension + + SrbControlCode is the SRB control code to use for the request + + Command is the SMART command to be executed. It may be SMART_CMD or + ID_CMD. + + Feature is the value to place in the IDE feature register. + + SectorCount is the value to place in the IDE SectorCount register + + SrbControl is the buffer used to build the SRB_IO_CONTROL and pass + any input parameters. It also returns the output parameters. + + *BufferSize on entry has total size of SrbControl and on return has + the size used in SrbControl. + + + +Return Value: + + status + +--*/ +{ + PCOMMON_DEVICE_EXTENSION commonExtension = (PCOMMON_DEVICE_EXTENSION)FdoExtension; + PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData); + PUCHAR buffer; + PSENDCMDINPARAMS cmdInParameters; + PSENDCMDOUTPARAMS cmdOutParameters; + ULONG outBufferSize; + NTSTATUS status; + ULONG availableBufferSize; + KEVENT event; + PIRP irp; + IO_STATUS_BLOCK ioStatus; + SCSI_REQUEST_BLOCK srb; + LARGE_INTEGER startingOffset; + ULONG length; + PIO_STACK_LOCATION irpStack; + + PAGED_CODE(); + + // + // Point to the 'buffer' portion of the SRB_CONTROL and compute how + // much room we have left in the srb control + // + + buffer = (PUCHAR)SrbControl; + (ULONG_PTR)buffer += sizeof(SRB_IO_CONTROL); + + cmdInParameters = (PSENDCMDINPARAMS)buffer; + cmdOutParameters = (PSENDCMDOUTPARAMS)buffer; + + availableBufferSize = *BufferSize - sizeof(SRB_IO_CONTROL); + +#if DBG + // + // Ensure control codes and buffer lengths passed are correct + // + { + ULONG controlCode; + ULONG lengthNeeded = sizeof(SENDCMDINPARAMS) - 1; + + if (Command == SMART_CMD) + { + switch (Feature) + { + + 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). + // + + lengthNeeded = sizeof(SENDCMDINPARAMS) - 1 + sizeof(IDEREGS); + + controlCode = IOCTL_SCSI_MINIPORT_RETURN_STATUS; + 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: + { + controlCode = IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS; + break; + } + + case READ_ATTRIBUTES: + { + controlCode = IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS; + lengthNeeded = READ_ATTRIBUTE_BUFFER_SIZE + sizeof(SENDCMDOUTPARAMS) - 1; + break; + } + + case READ_THRESHOLDS: + { + controlCode = IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS; + lengthNeeded = READ_THRESHOLD_BUFFER_SIZE + sizeof(SENDCMDOUTPARAMS) - 1; + break; + } + + case SMART_READ_LOG: + { + controlCode = IOCTL_SCSI_MINIPORT_READ_SMART_LOG; + lengthNeeded = (SectorCount * SMART_LOG_SECTOR_SIZE) + + sizeof(SENDCMDINPARAMS) - 1; + break; + } + + case SMART_WRITE_LOG: + { + controlCode = IOCTL_SCSI_MINIPORT_WRITE_SMART_LOG; + lengthNeeded = (SectorCount * SMART_LOG_SECTOR_SIZE) + + sizeof(SENDCMDINPARAMS) - 1; + break; + } + + } + } else if (Command == ID_CMD) { + controlCode = IOCTL_SCSI_MINIPORT_IDENTIFY; + lengthNeeded = IDENTIFY_BUFFER_SIZE + sizeof(SENDCMDOUTPARAMS) -1; + } else { + ASSERT(FALSE); + } + + ASSERT(controlCode == SrbControlCode); + ASSERT(availableBufferSize >= lengthNeeded); + } +#endif + + // + // Build SrbControl and input to SMART command + // + + SrbControl->HeaderLength = sizeof(SRB_IO_CONTROL); + RtlMoveMemory (SrbControl->Signature, "SCSIDISK", 8); + SrbControl->Timeout = FdoExtension->TimeOutValue; + SrbControl->Length = availableBufferSize; + + SrbControl->ControlCode = SrbControlCode; + + cmdInParameters->cBufferSize = sizeof(SENDCMDINPARAMS); + cmdInParameters->bDriveNumber = diskData->ScsiAddress.TargetId; + cmdInParameters->irDriveRegs.bFeaturesReg = Feature; + cmdInParameters->irDriveRegs.bSectorCountReg = SectorCount; + cmdInParameters->irDriveRegs.bSectorNumberReg = SectorNumber; + cmdInParameters->irDriveRegs.bCylLowReg = SMART_CYL_LOW; + cmdInParameters->irDriveRegs.bCylHighReg = SMART_CYL_HI; + cmdInParameters->irDriveRegs.bCommandReg = Command; + + + // + // Create and send irp + // + KeInitializeEvent(&event, NotificationEvent, FALSE); + + startingOffset.QuadPart = (LONGLONG) 1; + + length = SrbControl->HeaderLength + SrbControl->Length; + + irp = IoBuildSynchronousFsdRequest( + IRP_MJ_SCSI, + commonExtension->LowerDeviceObject, + SrbControl, + length, + &startingOffset, + &event, + &ioStatus); + + if (irp == NULL) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + irpStack = IoGetNextIrpStackLocation(irp); + + // + // Set major and minor codes. + // + + irpStack->MajorFunction = IRP_MJ_SCSI; + irpStack->MinorFunction = 1; + + // + // Fill in SRB fields. + // + + irpStack->Parameters.Others.Argument1 = &srb; + + // + // Zero out the srb. + // + + RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK)); + + srb.PathId = diskData->ScsiAddress.PathId; + srb.TargetId = diskData->ScsiAddress.TargetId; + srb.Lun = diskData->ScsiAddress.Lun; + + srb.Function = SRB_FUNCTION_IO_CONTROL; + srb.Length = sizeof(SCSI_REQUEST_BLOCK); + + srb.SrbFlags = FdoExtension->SrbFlags; + SET_FLAG(srb.SrbFlags, SRB_FLAGS_DATA_IN); + SET_FLAG(srb.SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE); + SET_FLAG(srb.SrbFlags, SRB_FLAGS_NO_KEEP_AWAKE); + + srb.QueueAction = SRB_SIMPLE_TAG_REQUEST; + srb.QueueTag = SP_UNTAGGED; + + srb.OriginalRequest = irp; + + // + // Set timeout to requested value. + // + + srb.TimeOutValue = SrbControl->Timeout; + + // + // Set the data buffer. + // + + srb.DataBuffer = SrbControl; + srb.DataTransferLength = length; + + // + // Flush the data buffer for output. This will insure that the data is + // written back to memory. Since the data-in flag is the the port driver + // will flush the data again for input which will ensure the data is not + // in the cache. + // + + KeFlushIoBuffers(irp->MdlAddress, FALSE, TRUE); + + // + // Call port driver to handle this request. + // + + status = IoCallDriver(commonExtension->LowerDeviceObject, irp); + + if (status == STATUS_PENDING) { + KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); + status = ioStatus.Status; + } + + return status; +} + + +NTSTATUS +DiskGetIdentifyInfo( + PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + PBOOLEAN SupportSmart + ) +{ + UCHAR outBuffer[sizeof(SRB_IO_CONTROL) + (sizeof(SENDCMDINPARAMS)-1) + IDENTIFY_BUFFER_SIZE]; + ULONG outBufferSize = sizeof(outBuffer); + NTSTATUS status; + + PAGED_CODE(); + + status = DiskGetIdentifyData(FdoExtension, + (PSRB_IO_CONTROL)outBuffer, + &outBufferSize); + + if (NT_SUCCESS(status)) + { + PUSHORT identifyData = (PUSHORT)&(outBuffer[sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDOUTPARAMS)-1]); + USHORT commandSetSupported = identifyData[82]; + + *SupportSmart = ((commandSetSupported != 0xffff) && + (commandSetSupported != 0) && + ((commandSetSupported & 1) == 1)); + } else { + *SupportSmart = FALSE; + } + + DebugPrint((3, "DiskGetIdentifyInfo: SMART %s supported for device %p, status %lx\n", + *SupportSmart ? "is" : "is not", + FdoExtension->DeviceObject, + status)); + + return status; +} + + +// +// FP Ioctl specific routines +// + +NTSTATUS +DiskSendFailurePredictIoctl( + PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + PSTORAGE_PREDICT_FAILURE checkFailure + ) +{ + KEVENT event; + PDEVICE_OBJECT deviceObject; + IO_STATUS_BLOCK ioStatus; + PIRP irp; + NTSTATUS status; + + PAGED_CODE(); + + KeInitializeEvent(&event, SynchronizationEvent, FALSE); + + deviceObject = IoGetAttachedDeviceReference(FdoExtension->DeviceObject); + + irp = IoBuildDeviceIoControlRequest( + IOCTL_STORAGE_PREDICT_FAILURE, + deviceObject, + NULL, + 0, + checkFailure, + sizeof(STORAGE_PREDICT_FAILURE), + FALSE, + &event, + &ioStatus); + + if (irp != NULL) + { + status = IoCallDriver(deviceObject, irp); + if (status == STATUS_PENDING) + { + KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); + status = ioStatus.Status; + } + + } else { + status = STATUS_INSUFFICIENT_RESOURCES; + } + + ObDereferenceObject(deviceObject); + + return status; +} + + +// +// FP type independent routines +// + +NTSTATUS +DiskEnableDisableFailurePrediction( + PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + BOOLEAN Enable + ) +/*++ + +Routine Description: + + Enable or disable failure prediction at the hardware level + +Arguments: + + FdoExtension + + Enable + +Return Value: + + NT Status + +--*/ +{ + NTSTATUS status; + PCOMMON_DEVICE_EXTENSION commonExtension = &(FdoExtension->CommonExtension); + PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData); + + PAGED_CODE(); + + switch(diskData->FailurePredictionCapability) + { + case FailurePredictionSmart: + { + + if (Enable) + { + status = DiskEnableSmart(FdoExtension); + } else { + status = DiskDisableSmart(FdoExtension); + } + + break; + } + + case FailurePredictionSense: + case FailurePredictionIoctl: + { + // + // We assume that the drive is already setup properly for + // failure prediction + // + status = STATUS_SUCCESS; + break; + } + + default: + { + status = STATUS_INVALID_DEVICE_REQUEST; + } + } + return status; +} + +NTSTATUS +DiskEnableDisableFailurePredictPolling( + PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + BOOLEAN Enable, + ULONG PollTimeInSeconds + ) +/*++ + +Routine Description: + + Enable or disable polling for hardware failure detection + +Arguments: + + FdoExtension + + Enable + + PollTimeInSeconds - if 0 then no change to current polling timer + +Return Value: + + NT Status + +--*/ +{ + NTSTATUS status; + PCOMMON_DEVICE_EXTENSION commonExtension = (PCOMMON_DEVICE_EXTENSION)FdoExtension; + PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData); + + PAGED_CODE(); + + if (Enable) + { + status = DiskEnableDisableFailurePrediction(FdoExtension, + Enable); + } else { + status = STATUS_SUCCESS; + } + + if (NT_SUCCESS(status)) + { + status = ClassSetFailurePredictionPoll(FdoExtension, + Enable ? diskData->FailurePredictionCapability : + FailurePredictionNone, + PollTimeInSeconds); + + // + // Even if this failed we do not want to disable FP on the + // hardware. FP is only ever disabled on the hardware by + // specific command of the user. + // + } + + return status; +} + + +NTSTATUS +DiskReadFailurePredictStatus( + PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + PSTORAGE_FAILURE_PREDICT_STATUS DiskSmartStatus + ) +/*++ + +Routine Description: + + Obtains current failure prediction status + +Arguments: + + FdoExtension + + DiskSmartStatus + +Return Value: + + NT Status + +--*/ +{ + PCOMMON_DEVICE_EXTENSION commonExtension = (PCOMMON_DEVICE_EXTENSION)FdoExtension; + PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData); + NTSTATUS status; + + PAGED_CODE(); + + DiskSmartStatus->PredictFailure = FALSE; + + switch(diskData->FailurePredictionCapability) + { + case FailurePredictionSmart: + { + UCHAR outBuffer[sizeof(SRB_IO_CONTROL) + (sizeof(SENDCMDINPARAMS) - 1 + sizeof(IDEREGS))]; + ULONG outBufferSize = sizeof(outBuffer); + PSENDCMDOUTPARAMS cmdOutParameters; + + status = DiskReadSmartStatus(FdoExtension, + (PSRB_IO_CONTROL)outBuffer, + &outBufferSize); + + if (NT_SUCCESS(status)) + { + cmdOutParameters = (PSENDCMDOUTPARAMS)(outBuffer + + sizeof(SRB_IO_CONTROL)); + + DiskSmartStatus->Reason = 0; // Unknown; + DiskSmartStatus->PredictFailure = ((cmdOutParameters->bBuffer[3] == 0xf4) && + (cmdOutParameters->bBuffer[4] == 0x2c)); + } + break; + } + + case FailurePredictionSense: + { + DiskSmartStatus->Reason = FdoExtension->FailureReason; + DiskSmartStatus->PredictFailure = FdoExtension->FailurePredicted; + status = STATUS_SUCCESS; + break; + } + + case FailurePredictionIoctl: + case FailurePredictionNone: + default: + { + status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + } + + return status; +} + +NTSTATUS +DiskReadFailurePredictData( + PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + PSTORAGE_FAILURE_PREDICT_DATA DiskSmartData + ) +/*++ + +Routine Description: + + Obtains current failure prediction data. Not available for + FAILURE_PREDICT_SENSE types. + +Arguments: + + FdoExtension + + DiskSmartData + +Return Value: + + NT Status + +--*/ +{ + PCOMMON_DEVICE_EXTENSION commonExtension = (PCOMMON_DEVICE_EXTENSION)FdoExtension; + PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData); + NTSTATUS status; + + PAGED_CODE(); + + switch(diskData->FailurePredictionCapability) + { + case FailurePredictionSmart: + { + PUCHAR outBuffer; + ULONG outBufferSize; + PSENDCMDOUTPARAMS cmdOutParameters; + + outBufferSize = sizeof(SRB_IO_CONTROL) + + (sizeof(SENDCMDOUTPARAMS)-1) + + READ_ATTRIBUTE_BUFFER_SIZE; + + outBuffer = ExAllocatePoolWithTag(NonPagedPool, + outBufferSize, + DISK_TAG_SMART); + + if (outBuffer != NULL) + { + status = DiskReadSmartData(FdoExtension, + (PSRB_IO_CONTROL)outBuffer, + &outBufferSize); + + if (NT_SUCCESS(status)) + { + cmdOutParameters = (PSENDCMDOUTPARAMS)(outBuffer + + sizeof(SRB_IO_CONTROL)); + + DiskSmartData->Length = READ_ATTRIBUTE_BUFFER_SIZE; + RtlCopyMemory(DiskSmartData->VendorSpecific, + cmdOutParameters->bBuffer, + READ_ATTRIBUTE_BUFFER_SIZE); + } + ExFreePool(outBuffer); + } else { + status = STATUS_INSUFFICIENT_RESOURCES; + } + + break; + } + + case FailurePredictionSense: + { + DiskSmartData->Length = sizeof(ULONG); + *((PULONG)DiskSmartData->VendorSpecific) = FdoExtension->FailureReason; + + status = STATUS_SUCCESS; + break; + } + + case FailurePredictionIoctl: + case FailurePredictionNone: + default: + { + status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + } + + return status; +} + +NTSTATUS +DiskReadFailurePredictThresholds( + PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + PSTORAGE_FAILURE_PREDICT_THRESHOLDS DiskSmartThresholds + ) +/*++ + +Routine Description: + + Obtains current failure prediction thresholds. Not available for + FAILURE_PREDICT_SENSE types. + +Arguments: + + FdoExtension + + DiskSmartData + +Return Value: + + NT Status + +--*/ +{ + PCOMMON_DEVICE_EXTENSION commonExtension = (PCOMMON_DEVICE_EXTENSION)FdoExtension; + PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData); + NTSTATUS status; + + PAGED_CODE(); + + switch(diskData->FailurePredictionCapability) + { + case FailurePredictionSmart: + { + PUCHAR outBuffer; + PSENDCMDOUTPARAMS cmdOutParameters; + ULONG outBufferSize; + + outBufferSize = sizeof(SRB_IO_CONTROL) + + (sizeof(SENDCMDOUTPARAMS)-1) + + READ_THRESHOLD_BUFFER_SIZE; + + outBuffer = ExAllocatePoolWithTag(NonPagedPool, + outBufferSize, + DISK_TAG_SMART); + + if (outBuffer != NULL) + { + status = DiskReadSmartThresholds(FdoExtension, + (PSRB_IO_CONTROL)outBuffer, + &outBufferSize); + + if (NT_SUCCESS(status)) + { + cmdOutParameters = (PSENDCMDOUTPARAMS)(outBuffer + + sizeof(SRB_IO_CONTROL)); + + RtlCopyMemory(DiskSmartThresholds->VendorSpecific, + cmdOutParameters->bBuffer, + READ_THRESHOLD_BUFFER_SIZE); + } + ExFreePool(outBuffer); + } else { + status = STATUS_INSUFFICIENT_RESOURCES; + } + + break; + } + + case FailurePredictionSense: + case FailurePredictionIoctl: + case FailurePredictionNone: + default: + { + status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + } + + return status; +} + +void DiskReregWorker( + IN PVOID Context + ) +{ + PDISKREREGREQUEST reregRequest; + NTSTATUS status; + PDEVICE_OBJECT deviceObject; + PIRP irp; + + PAGED_CODE(); + + do + { + reregRequest = (PDISKREREGREQUEST)ExInterlockedPopEntryList( + &DiskReregHead, + &DiskReregSpinlock); + + deviceObject = reregRequest->DeviceObject; + irp = reregRequest->Irp; + + status = IoWMIRegistrationControl(deviceObject, + WMIREG_ACTION_UPDATE_GUIDS); + + if (! NT_SUCCESS(status)) + { + DebugPrint((1, "DiskReregWorker: Reregistration failed %x\n", + status)); + } + + // + // Release remove lock and free irp, now that we are done + // processing this + // + ClassReleaseRemoveLock(deviceObject, irp); + + IoFreeMdl(irp->MdlAddress); + IoFreeIrp(irp); + + ExFreePool(reregRequest); + + } while (InterlockedDecrement(&DiskReregWorkItems)); + + +} + +NTSTATUS DiskInitializeReregistration( + void + ) +{ + PAGED_CODE(); + + // + // Initialize the global work item and spinlock used to manage the + // list of disks reregistering their guids + // + ExInitializeWorkItem( &DiskReregWorkItem, + DiskReregWorker, + NULL ); + + KeInitializeSpinLock(&DiskReregSpinlock); + + return(STATUS_SUCCESS); +} + +NTSTATUS DiskPostReregisterRequest( + PDEVICE_OBJECT DeviceObject, + PIRP Irp + ) +{ + PDISKREREGREQUEST reregRequest; + NTSTATUS status; + + reregRequest = ExAllocatePoolWithTag(NonPagedPool, + sizeof(DISKREREGREQUEST), + DISK_TAG_SMART); + + if (reregRequest != NULL) + { + // + // add the disk that needs reregistration to the stack of disks + // to reregister. If the list is transitioning from empty to + // non empty then also kick off the work item so that the + // reregistration worker can do the reregister. + // + reregRequest->DeviceObject = DeviceObject; + reregRequest->Irp = Irp; + ExInterlockedPushEntryList( + &DiskReregHead, + &reregRequest->Next, + &DiskReregSpinlock); + + if (InterlockedIncrement(&DiskReregWorkItems) == 1) + { + ExQueueWorkItem( &DiskReregWorkItem, DelayedWorkQueue ); + } + status = STATUS_SUCCESS; + } else { + DebugPrint((1, "DiskPostReregisterRequest: could not allocate reregRequest for %p\n", + DeviceObject)); + status = STATUS_INSUFFICIENT_RESOURCES; + } + + return(status); +} + +NTSTATUS DiskInfoExceptionComplete( + PDEVICE_OBJECT DeviceObject, + PIRP Irp, + PVOID Context + ) +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension; + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData); + PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); + PIO_STACK_LOCATION nextIrpStack = IoGetNextIrpStackLocation(Irp); + PSCSI_REQUEST_BLOCK srb = Context; + NTSTATUS status; + BOOLEAN retry; + ULONG retryInterval; + ULONG srbStatus; + BOOLEAN freeLockAndIrp = TRUE; + KIRQL oldIrql; + + ASSERT(fdoExtension->CommonExtension.IsFdo); + + srbStatus = SRB_STATUS(srb->SrbStatus); + + // + // Check SRB status for success of completing request. + // SRB_STATUS_DATA_OVERRUN also indicates success. + // + if ((srbStatus != SRB_STATUS_SUCCESS) && + (srbStatus != SRB_STATUS_DATA_OVERRUN)) + { + DebugPrint((2, "DiskInfoExceptionComplete: IRP %p, SRB %p\n", Irp, srb)); + + retry = ClassInterpretSenseInfo( + DeviceObject, + srb, + irpStack->MajorFunction, + 0, + MAXIMUM_RETRIES - + ((ULONG)(ULONG_PTR)irpStack->Parameters.Others.Argument4), + &status, + &retryInterval); + + // + // If the status is verified required and the this request + // should bypass verify required then retry the request. + // + + if (TEST_FLAG(irpStack->Flags, SL_OVERRIDE_VERIFY_VOLUME) && + status == STATUS_VERIFY_REQUIRED) + { + status = STATUS_IO_DEVICE_ERROR; + retry = TRUE; + } + + if (retry && ((ULONG)(ULONG_PTR)irpStack->Parameters.Others.Argument4)--) + { + + // + // Retry request. + // + + DebugPrint((1, "DiskInfoExceptionComplete: Retry request %p\n", Irp)); + + ASSERT(srb->DataBuffer == MmGetMdlVirtualAddress(Irp->MdlAddress)); + + // + // Reset byte count of transfer in SRB Extension. + // + srb->DataTransferLength = Irp->MdlAddress->ByteCount; + + // + // Zero SRB statuses. + // + + srb->SrbStatus = srb->ScsiStatus = 0; + + // + // Set the no disconnect flag, disable synchronous data transfers and + // disable tagged queuing. This fixes some errors. + // + + SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_DISCONNECT); + SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER); + CLEAR_FLAG(srb->SrbFlags, SRB_FLAGS_QUEUE_ACTION_ENABLE); + + srb->QueueAction = SRB_SIMPLE_TAG_REQUEST; + srb->QueueTag = SP_UNTAGGED; + + // + // Set up major SCSI function. + // + + nextIrpStack->MajorFunction = IRP_MJ_SCSI; + + // + // Save SRB address in next stack for port driver. + // + + nextIrpStack->Parameters.Scsi.Srb = srb; + + + IoSetCompletionRoutine(Irp, + DiskInfoExceptionComplete, + srb, + TRUE, TRUE, TRUE); + + (VOID)IoCallDriver(commonExtension->LowerDeviceObject, Irp); + + return STATUS_MORE_PROCESSING_REQUIRED; + } + + } else { + + // + // Get the results from the mode sense + // + PMODE_INFO_EXCEPTIONS pageData; + PMODE_PARAMETER_HEADER modeData; + ULONG modeDataLength; + + modeData = srb->DataBuffer; + modeDataLength = srb->DataTransferLength; + + pageData = ClassFindModePage((PUCHAR) modeData, + modeDataLength, + MODE_PAGE_FAULT_REPORTING, + TRUE); + if (pageData != NULL) + { + DebugPrint((1, "DiskInfoExceptionComplete: %p supports SMART\n", + DeviceObject)); + + if (pageData->Dexcpt == 0) + { + diskData->FailurePredictionCapability = FailurePredictionSense; + status = DiskPostReregisterRequest(DeviceObject, Irp); + + if (NT_SUCCESS(status)) + { + // + // Make sure we won't free the remove lock and the irp + // since we need to keep these until after the work + // item has completed running + // + freeLockAndIrp = FALSE; + } + } else { + DebugPrint((1, "DiskInfoExceptionComplete: %p is not enabled for SMART\n", + DeviceObject)); + + } + + } else { + DebugPrint((1, "DiskInfoExceptionComplete: %p does not supports SMART\n", + DeviceObject)); + + } + + // + // Set status for successful request + // + + status = STATUS_SUCCESS; + + } // end if (SRB_STATUS(srb->SrbStatus) == SRB_STATUS_SUCCESS) + + // + // Free the srb + // + ExFreePool(srb->SenseInfoBuffer); + ExFreePool(srb->DataBuffer); + ExFreePool(srb); + + if (freeLockAndIrp) + { + // + // Set status in completing IRP. + // + + Irp->IoStatus.Status = status; + + // + // If pending has be returned for this irp then mark the current stack as + // pending. + // + + if (Irp->PendingReturned) { + IoMarkIrpPending(Irp); + } + + ClassReleaseRemoveLock(DeviceObject, Irp); + IoFreeMdl(Irp->MdlAddress); + IoFreeIrp(Irp); + } + + return(STATUS_MORE_PROCESSING_REQUIRED); + +} + +NTSTATUS DiskInfoExceptionCheck( + PFUNCTIONAL_DEVICE_EXTENSION FdoExtension + ) +{ + PUCHAR modeData; + PSCSI_REQUEST_BLOCK srb; + PCDB cdb; + PIRP irp; + PIO_STACK_LOCATION irpStack; + PVOID senseInfoBuffer; + ULONG isRemoved; + + modeData = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, + MODE_DATA_SIZE, + DISK_TAG_INFO_EXCEPTION); + if (modeData == NULL) + { + DebugPrint((1, "DiskInfoExceptionCheck: Can't allocate mode data " + "buffer\n")); + return(STATUS_INSUFFICIENT_RESOURCES); + } + + srb = ExAllocatePoolWithTag(NonPagedPool, + SCSI_REQUEST_BLOCK_SIZE, + DISK_TAG_SRB); + if (srb == NULL) + { + ExFreePool(modeData); + DebugPrint((1, "DiskInfoExceptionCheck: Can't allocate srb " + "buffer\n")); + return(STATUS_INSUFFICIENT_RESOURCES); + } + + // + // Build the MODE SENSE CDB. + // + RtlZeroMemory(srb, SCSI_REQUEST_BLOCK_SIZE); + + cdb = (PCDB)srb->Cdb; + srb->CdbLength = 6; + cdb = (PCDB)srb->Cdb; + + // + // Set timeout value from device extension. + // + srb->TimeOutValue = FdoExtension->TimeOutValue; + + cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE; + cdb->MODE_SENSE.PageCode = MODE_PAGE_FAULT_REPORTING; + cdb->MODE_SENSE.AllocationLength = MODE_DATA_SIZE; + + // + // Write length to SRB. + // + srb->Length = SCSI_REQUEST_BLOCK_SIZE; + + // + // Set SCSI bus address. + // + + srb->Function = SRB_FUNCTION_EXECUTE_SCSI; + + // + // Enable auto request sense. + // + + srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE; + + // + // Sense buffer is in aligned nonpaged pool. + // + + senseInfoBuffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, + SENSE_BUFFER_SIZE, + '7CcS'); + + if (senseInfoBuffer == NULL) + { + ExFreePool(srb); + ExFreePool(modeData); + DebugPrint((1, "DiskInfoExceptionCheck: Can't allocate request sense " + "buffer\n")); + return(STATUS_INSUFFICIENT_RESOURCES); + } + + srb->SenseInfoBuffer = senseInfoBuffer; + srb->DataBuffer = modeData; + + srb->SrbFlags = FdoExtension->SrbFlags; + + + SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_IN); + + // + // Disable synchronous transfer for these requests. + // + SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER); + + // + // Don't freeze the queue on an error + // + SET_FLAG(srb->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE); + + srb->QueueAction = SRB_SIMPLE_TAG_REQUEST; + srb->QueueTag = SP_UNTAGGED; + + + // + // Build device I/O control request with METHOD_NEITHER data transfer. + // We'll queue a completion routine to cleanup the MDL's and such ourself. + // + + irp = IoAllocateIrp( + (CCHAR) (FdoExtension->CommonExtension.LowerDeviceObject->StackSize + 1), + FALSE); + + if (irp == NULL) + { + ExFreePool(senseInfoBuffer); + ExFreePool(srb); + ExFreePool(modeData); + DebugPrint((1, "DiskInfoExceptionCheck: Can't allocate Irp\n")); + return(STATUS_INSUFFICIENT_RESOURCES); + } + + isRemoved = ClassAcquireRemoveLock(FdoExtension->DeviceObject, irp); + + if (isRemoved) + { + ClassReleaseRemoveLock(FdoExtension->DeviceObject, irp); + IoFreeIrp(irp); + ExFreePool(senseInfoBuffer); + ExFreePool(srb); + ExFreePool(modeData); + DebugPrint((1, "DiskInfoExceptionCheck: RemoveLock says isRemoved\n")); + return(STATUS_DEVICE_DOES_NOT_EXIST); + } + + // + // Get next stack location. + // + + IoSetNextIrpStackLocation(irp); + irpStack = IoGetCurrentIrpStackLocation(irp); + irpStack->DeviceObject = FdoExtension->DeviceObject; + + // + // Save retry count in current Irp stack. + // + irpStack->Parameters.Others.Argument4 = (PVOID)MAXIMUM_RETRIES; + + + irpStack = IoGetNextIrpStackLocation(irp); + + // + // Set up SRB for execute scsi request. Save SRB address in next stack + // for the port driver. + // + + irpStack->MajorFunction = IRP_MJ_SCSI; + irpStack->Parameters.Scsi.Srb = srb; + + IoSetCompletionRoutine(irp, + DiskInfoExceptionComplete, + srb, + TRUE, + TRUE, + TRUE); + + irp->MdlAddress = IoAllocateMdl( modeData, + MODE_DATA_SIZE, + FALSE, + FALSE, + irp ); + if (irp->MdlAddress == NULL) + { + ClassReleaseRemoveLock(FdoExtension->DeviceObject, irp); + ExFreePool(srb); + ExFreePool(modeData); + ExFreePool(senseInfoBuffer); + IoFreeIrp( irp ); + DebugPrint((1, "DiskINfoExceptionCheck: Can't allocate MDL\n")); + return STATUS_INSUFFICIENT_RESOURCES; + } + + MmBuildMdlForNonPagedPool(irp->MdlAddress); + + // + // Set the transfer length. + // + srb->DataTransferLength = MODE_DATA_SIZE; + + // + // Zero out status. + // + srb->ScsiStatus = srb->SrbStatus = 0; + srb->NextSrb = 0; + + // + // Set up IRP Address. + // + srb->OriginalRequest = irp; + + // + // Call the port driver with the request and wait for it to complete. + // + + IoMarkIrpPending(irp); + IoCallDriver(FdoExtension->CommonExtension.LowerDeviceObject, + irp); + + return(STATUS_PENDING); +} + +NTSTATUS +DiskDetectFailurePrediction( + PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + PFAILURE_PREDICTION_METHOD FailurePredictCapability + ) +/*++ + +Routine Description: + + Detect if device has any failure prediction capabilities. First we + check for IDE SMART capability. This is done by sending the drive an + IDENTIFY command and checking if the SMART command set bit is set. + + Next we check if SCSI SMART (aka Information Exception Control Page, + X3T10/94-190 Rev 4). This is done by querying for the Information + Exception mode page. + + Lastly we check if the device has IOCTL failure prediction. This mechanism + a filter driver implements IOCTL_STORAGE_PREDICT_FAILURE and will respond + with the information in the IOCTL. We do this by sending the ioctl and + if the status returned is STATUS_SUCCESS we assume that it is supported. + +Arguments: + + FdoExtension + + *FailurePredictCapability + +Return Value: + + NT Status + +--*/ +{ + PCOMMON_DEVICE_EXTENSION commonExtension = (PCOMMON_DEVICE_EXTENSION)FdoExtension; + PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData); + BOOLEAN supportFP; + NTSTATUS status; + STORAGE_PREDICT_FAILURE checkFailure; + STORAGE_FAILURE_PREDICT_STATUS diskSmartStatus; + BOOLEAN logErr; + + PAGED_CODE(); + + // + // Assume no failure predict mechanisms + // + *FailurePredictCapability = FailurePredictionNone; + + // + // See if this is an IDE drive that supports SMART. If so enable SMART + // and then ensure that it suports the SMART READ STATUS command + // + status = DiskGetIdentifyInfo(FdoExtension, + &supportFP); + + if (supportFP) + { + status = DiskEnableSmart(FdoExtension); + if (NT_SUCCESS(status)) + { + *FailurePredictCapability = FailurePredictionSmart; + + status = DiskReadFailurePredictStatus(FdoExtension, + &diskSmartStatus); + + DebugPrint((1, "Disk: Device %p %s IDE SMART\n", + FdoExtension->DeviceObject, + NT_SUCCESS(status) ? "does" : "does not")); + + if (! NT_SUCCESS(status)) + { + *FailurePredictCapability = FailurePredictionNone; + } + } + return(status); + } + + // + // See if there is a a filter driver to intercept + // IOCTL_STORAGE_PREDICT_FAILURE + // + status = DiskSendFailurePredictIoctl(FdoExtension, + &checkFailure); + + DebugPrint((1, "Disk: Device %p %s IOCTL_STORAGE_FAILURE_PREDICT\n", + FdoExtension->DeviceObject, + NT_SUCCESS(status) ? "does" : "does not")); + + if (NT_SUCCESS(status)) + { + *FailurePredictCapability = FailurePredictionIoctl; + if (checkFailure.PredictFailure) + { + checkFailure.PredictFailure = 512; + ClassNotifyFailurePredicted(FdoExtension, + (PUCHAR)&checkFailure, + sizeof(checkFailure), + (BOOLEAN)(FdoExtension->FailurePredicted == FALSE), + 0x11, + diskData->ScsiAddress.PathId, + diskData->ScsiAddress.TargetId, + diskData->ScsiAddress.Lun); + + FdoExtension->FailurePredicted = TRUE; + } + return(status); + } + + // + // Finally we assume it will not be a scsi smart drive. but + // we'll also send off an asynchronous mode sense so that if + // it is SMART we'll reregister the device object + // + + DiskInfoExceptionCheck(FdoExtension); + + *FailurePredictCapability = FailurePredictionNone; + + return(STATUS_SUCCESS); +} + + +NTSTATUS +DiskWmiFunctionControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN ULONG GuidIndex, + IN CLASSENABLEDISABLEFUNCTION Function, + IN BOOLEAN Enable + ) +/*++ + +Routine Description: + + This routine is a callback into the driver to enabled or disable event + generation or data block collection. A device should only expect a + single enable when the first event or data consumer enables events or + data collection and a single disable when the last event or data + consumer disables events or data collection. Data blocks will only + receive collection enable/disable if they were registered as requiring + it. + + + When NT boots, failure prediction is not automatically enabled, although + it may have been persistantly enabled on a previous boot. Polling is also + not automatically enabled. When the first data block that accesses SMART + such as SmartStatusGuid, SmartDataGuid, SmartPerformFunction, or + SmartEventGuid is accessed then SMART is automatically enabled in the + hardware. Polling is enabled when SmartEventGuid is enabled and disabled + when it is disabled. Hardware SMART is only disabled when the DisableSmart + method is called. Polling is also disabled when this is called regardless + of the status of the other guids or events. + +Arguments: + + DeviceObject is the device whose data block is being queried + + GuidIndex is the index into the list of guids provided when the + device registered + + Function specifies which functionality is being enabled or disabled + + Enable is TRUE then the function is being enabled else disabled + +Return Value: + + status + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension; + PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData); + ULONG enableCount; + + PAGED_CODE(); + + if ((Function == DataBlockCollection) && Enable) + { + if ((GuidIndex == SmartStatusGuid) || + (GuidIndex == SmartDataGuid) || + (GuidIndex == SmartThresholdsGuid) || + (GuidIndex == SmartPerformFunction)) + { + status = DiskEnableDisableFailurePrediction(fdoExtension, + TRUE); + DebugPrint((3, "Disk: DeviceObject %p, Irp %p Enable -> %lx\n", + DeviceObject, + Irp, + status)); + + } else { + DebugPrint((3, "Disk: DeviceObject %p, Irp %p, GuidIndex %d %s for Collection\n", + DeviceObject, Irp, + GuidIndex, + Enable ? "Enabled" : "Disabled")); } + } else if (Function == EventGeneration) { + DebugPrint((3, "Disk: DeviceObject %p, Irp %p, GuidIndex %d %s for Event Generation\n", + DeviceObject, Irp, + GuidIndex, + Enable ? "Enabled" : "Disabled")); + + + if ((GuidIndex == SmartEventGuid) && Enable) + { + status = DiskEnableDisableFailurePredictPolling(fdoExtension, + Enable, + 0); + DebugPrint((3, "Disk: DeviceObject %p, Irp %p %s -> %lx\n", + DeviceObject, + Irp, + Enable ? "DiskEnableSmartPolling" : "DiskDisableSmartPolling", + status)); + } + +#if DBG + } else { + DebugPrint((3, "Disk: DeviceObject %p, Irp %p, GuidIndex %d %s for function %d\n", + DeviceObject, Irp, + GuidIndex, + Enable ? "Enabled" : "Disabled", + Function)); +#endif + } + + status = ClassWmiCompleteRequest(DeviceObject, + Irp, + status, + 0, + IO_NO_INCREMENT); + return status; +} + + + +NTSTATUS +DiskFdoQueryWmiRegInfo( + IN PDEVICE_OBJECT DeviceObject, + OUT ULONG *RegFlags, + OUT PUNICODE_STRING InstanceName + ) +/*++ + +Routine Description: + + This routine is a callback into the driver to retrieve the list of + guids or data blocks that the driver wants to register with WMI. This + routine may not pend or block. Driver should NOT call + ClassWmiCompleteRequest. + +Arguments: + + DeviceObject is the device whose data block is being queried + + *RegFlags returns with a set of flags that describe the guids being + registered for this device. If the device wants enable and disable + collection callbacks before receiving queries for the registered + guids then it should return the WMIREG_FLAG_EXPENSIVE flag. Also the + returned flags may specify WMIREG_FLAG_INSTANCE_PDO in which case + the instance name is determined from the PDO associated with the + device object. Note that the PDO must have an associated devnode. If + WMIREG_FLAG_INSTANCE_PDO is not set then Name must return a unique + name for the device. + + InstanceName returns with the instance name for the guids if + WMIREG_FLAG_INSTANCE_PDO is not set in the returned *RegFlags. The + caller will call ExFreePool with the buffer returned. + + +Return Value: + + status + +--*/ +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension; + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData); + NTSTATUS status; + + PAGED_CODE(); + + SET_FLAG(DiskWmiFdoGuidList[SmartThresholdsGuid].Flags, WMIREG_FLAG_REMOVE_GUID); + SET_FLAG(DiskWmiFdoGuidList[ScsiInfoExceptionsGuid].Flags, WMIREG_FLAG_REMOVE_GUID); + + switch (diskData->FailurePredictionCapability) + { + case FailurePredictionSmart: + { + CLEAR_FLAG(DiskWmiFdoGuidList[SmartThresholdsGuid].Flags, WMIREG_FLAG_REMOVE_GUID); + // + // Fall Through + // + } + case FailurePredictionIoctl: + { + CLEAR_FLAG(DiskWmiFdoGuidList[SmartStatusGuid].Flags, WMIREG_FLAG_REMOVE_GUID); + CLEAR_FLAG(DiskWmiFdoGuidList[SmartDataGuid].Flags, WMIREG_FLAG_REMOVE_GUID); + CLEAR_FLAG(DiskWmiFdoGuidList[SmartEventGuid].Flags, WMIREG_FLAG_REMOVE_GUID); + CLEAR_FLAG(DiskWmiFdoGuidList[SmartPerformFunction].Flags, WMIREG_FLAG_REMOVE_GUID); + + break; + } + + case FailurePredictionSense: + { + CLEAR_FLAG(DiskWmiFdoGuidList[SmartStatusGuid].Flags, WMIREG_FLAG_REMOVE_GUID); + CLEAR_FLAG(DiskWmiFdoGuidList[SmartEventGuid].Flags, WMIREG_FLAG_REMOVE_GUID); + CLEAR_FLAG(DiskWmiFdoGuidList[SmartPerformFunction].Flags, WMIREG_FLAG_REMOVE_GUID); + CLEAR_FLAG(DiskWmiFdoGuidList[ScsiInfoExceptionsGuid].Flags, WMIREG_FLAG_REMOVE_GUID); + SET_FLAG (DiskWmiFdoGuidList[SmartDataGuid].Flags, WMIREG_FLAG_REMOVE_GUID); + break; + } + + + default: + { + SET_FLAG (DiskWmiFdoGuidList[SmartStatusGuid].Flags, WMIREG_FLAG_REMOVE_GUID); + SET_FLAG (DiskWmiFdoGuidList[SmartDataGuid].Flags, WMIREG_FLAG_REMOVE_GUID); + SET_FLAG (DiskWmiFdoGuidList[SmartEventGuid].Flags, WMIREG_FLAG_REMOVE_GUID); + SET_FLAG (DiskWmiFdoGuidList[SmartPerformFunction].Flags, WMIREG_FLAG_REMOVE_GUID); + break; + } + } + + // + // Use devnode for FDOs + *RegFlags = WMIREG_FLAG_INSTANCE_PDO; + + return STATUS_SUCCESS; +} + +NTSTATUS +DiskFdoQueryWmiRegInfoEx( + IN PDEVICE_OBJECT DeviceObject, + OUT ULONG *RegFlags, + OUT PUNICODE_STRING InstanceName, + OUT PUNICODE_STRING MofName + ) +/*++ + +Routine Description: + + This routine is a callback into the driver to retrieve the list of + guids or data blocks that the driver wants to register with WMI. This + routine may not pend or block. Driver should NOT call + ClassWmiCompleteRequest. + +Arguments: + + DeviceObject is the device whose data block is being queried + + *RegFlags returns with a set of flags that describe the guids being + registered for this device. If the device wants enable and disable + collection callbacks before receiving queries for the registered + guids then it should return the WMIREG_FLAG_EXPENSIVE flag. Also the + returned flags may specify WMIREG_FLAG_INSTANCE_PDO in which case + the instance name is determined from the PDO associated with the + device object. Note that the PDO must have an associated devnode. If + WMIREG_FLAG_INSTANCE_PDO is not set then Name must return a unique + name for the device. + + InstanceName returns with the instance name for the guids if + WMIREG_FLAG_INSTANCE_PDO is not set in the returned *RegFlags. The + caller will call ExFreePool with the buffer returned. + + MofName returns initialized with the mof resource name for the + binary mof resource attached to the driver's image file. If the + driver does not have a mof resource then it should leave this + parameter untouched. + +Return Value: + + status + +--*/ +{ + NTSTATUS status; + + status = DiskFdoQueryWmiRegInfo(DeviceObject, + RegFlags, + InstanceName); + + // + // Leave MofName alone since disk doesn't have one + // + return(status); +} + + +NTSTATUS +DiskFdoQueryWmiDataBlock( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN ULONG GuidIndex, + IN ULONG BufferAvail, + OUT PUCHAR Buffer + ) +/*++ + +Routine Description: + + This routine is a callback into the driver to query for the contents of + a data block. When the driver has finished filling the data block it + must call ClassWmiCompleteRequest to complete the irp. The driver can + return STATUS_PENDING if the irp cannot be completed immediately. + +Arguments: + + DeviceObject is the device whose data block is being queried + + Irp is the Irp that makes this request + + GuidIndex is the index into the list of guids provided when the + device registered + + BufferAvail on has the maximum size available to write the data + block. + + Buffer on return is filled with the returned data block + + +Return Value: + + status + +--*/ +{ + NTSTATUS status; + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension; + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData); + ULONG sizeNeeded; + + PAGED_CODE(); + + DebugPrint((3, "Disk: DiskQueryWmiDataBlock, Device %p, Irp %p, GuiIndex %d\n" + " BufferAvail %lx Buffer %lx\n", + DeviceObject, Irp, + GuidIndex, BufferAvail, Buffer)); + + switch (GuidIndex) + { + case DiskGeometryGuid: + { + sizeNeeded = sizeof(DISK_GEOMETRY); + if (BufferAvail >= sizeNeeded) + { + if (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(Buffer, + &(fdoExtension->DiskGeometry), + sizeof(DISK_GEOMETRY)); + + status = STATUS_SUCCESS; + } else { + status = STATUS_BUFFER_TOO_SMALL; + } + break; + } + + case SmartStatusGuid: + { + PSTORAGE_FAILURE_PREDICT_STATUS diskSmartStatus; + + ASSERT(diskData->FailurePredictionCapability != FailurePredictionNone); + + + sizeNeeded = sizeof(STORAGE_FAILURE_PREDICT_STATUS); + if (BufferAvail >= sizeNeeded) + { + STORAGE_PREDICT_FAILURE checkFailure; + + diskSmartStatus = (PSTORAGE_FAILURE_PREDICT_STATUS)Buffer; + + status = DiskSendFailurePredictIoctl(fdoExtension, + &checkFailure); + + if (NT_SUCCESS(status)) + { + if (diskData->FailurePredictionCapability == + FailurePredictionSense) + { + diskSmartStatus->Reason = *((PULONG)checkFailure.VendorSpecific); + } else { + diskSmartStatus->Reason = 0; // unknown + } + + diskSmartStatus->PredictFailure = (checkFailure.PredictFailure != 0); + } + } else { + status = STATUS_BUFFER_TOO_SMALL; + } + break; + } + + case SmartDataGuid: + { + PSTORAGE_FAILURE_PREDICT_DATA diskSmartData; + + ASSERT((diskData->FailurePredictionCapability == + FailurePredictionSmart) || + (diskData->FailurePredictionCapability == + FailurePredictionIoctl)); + + sizeNeeded = sizeof(STORAGE_FAILURE_PREDICT_DATA); + if (BufferAvail >= sizeNeeded) + { + PSTORAGE_PREDICT_FAILURE checkFailure = (PSTORAGE_PREDICT_FAILURE)Buffer; + + diskSmartData = (PSTORAGE_FAILURE_PREDICT_DATA)Buffer; + + status = DiskSendFailurePredictIoctl(fdoExtension, + checkFailure); + + if (NT_SUCCESS(status)) + { + diskSmartData->Length = 512; + } + } else { + status = STATUS_BUFFER_TOO_SMALL; + } + + break; + } + + case SmartThresholdsGuid: + { + PSTORAGE_FAILURE_PREDICT_THRESHOLDS diskSmartThresholds; + + ASSERT((diskData->FailurePredictionCapability == + FailurePredictionSmart)); + + sizeNeeded = sizeof(STORAGE_FAILURE_PREDICT_THRESHOLDS); + if (BufferAvail >= sizeNeeded) + { + diskSmartThresholds = (PSTORAGE_FAILURE_PREDICT_THRESHOLDS)Buffer; + status = DiskReadFailurePredictThresholds(fdoExtension, + diskSmartThresholds); + } else { + status = STATUS_BUFFER_TOO_SMALL; + } + + break; + } + + case SmartPerformFunction: + { + sizeNeeded = 0; + status = STATUS_SUCCESS; + break; + } + + case ScsiInfoExceptionsGuid: + { + PSTORAGE_SCSI_INFO_EXCEPTIONS infoExceptions; + MODE_INFO_EXCEPTIONS modeInfo; + + ASSERT((diskData->FailurePredictionCapability == + FailurePredictionSense)); + + sizeNeeded = sizeof(STORAGE_SCSI_INFO_EXCEPTIONS); + if (BufferAvail >= sizeNeeded) + { + infoExceptions = (PSTORAGE_SCSI_INFO_EXCEPTIONS)Buffer; + status = DiskGetInfoExceptionInformation(fdoExtension, + &modeInfo); + + if (NT_SUCCESS(status)) + { + infoExceptions->PageSavable = modeInfo.PSBit; + infoExceptions->Flags = modeInfo.Flags; + infoExceptions->MRIE = modeInfo.ReportMethod; + infoExceptions->Padding = 0; + REVERSE_BYTES(&infoExceptions->IntervalTimer, + &modeInfo.IntervalTimer); + REVERSE_BYTES(&infoExceptions->ReportCount, + &modeInfo.ReportCount) + } + } else { + status = STATUS_BUFFER_TOO_SMALL; + } + + break; + } + + default: + { + sizeNeeded = 0; + status = STATUS_WMI_GUID_NOT_FOUND; + } + } + DebugPrint((3, "Disk: DiskQueryWmiDataBlock Device %p, Irp %p returns %lx\n", + DeviceObject, Irp, status)); + + status = ClassWmiCompleteRequest(DeviceObject, + Irp, + status, + sizeNeeded, + IO_NO_INCREMENT); + + return status; +} + +NTSTATUS +DiskFdoSetWmiDataBlock( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN ULONG GuidIndex, + IN ULONG BufferSize, + IN PUCHAR Buffer + ) +/*++ + +Routine Description: + + This routine is a callback into the driver to query for the contents of + a data block. When the driver has finished filling the data block it + must call ClassWmiCompleteRequest to complete the irp. The driver can + return STATUS_PENDING if the irp cannot be completed immediately. + +Arguments: + + DeviceObject is the device whose data block is being queried + + Irp is the Irp that makes this request + + GuidIndex is the index into the list of guids provided when the + device registered + + BufferSize has the size of the data block passed + + Buffer has the new values for the data block + + +Return Value: + + status + +--*/ +{ + NTSTATUS status; + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension; + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData); + + PAGED_CODE(); + + DebugPrint((3, "Disk: DiskSetWmiDataBlock, Device %p, Irp %p, GuiIndex %d\n" + " BufferSize %#x Buffer %p\n", + DeviceObject, Irp, + GuidIndex, BufferSize, Buffer)); + + if (GuidIndex == ScsiInfoExceptionsGuid) + { + PSTORAGE_SCSI_INFO_EXCEPTIONS infoExceptions; + MODE_INFO_EXCEPTIONS modeInfo; + + if (BufferSize >= sizeof(STORAGE_SCSI_INFO_EXCEPTIONS)) + { + infoExceptions = (PSTORAGE_SCSI_INFO_EXCEPTIONS)Buffer; + + modeInfo.PageCode = MODE_PAGE_FAULT_REPORTING; + modeInfo.PageLength = sizeof(MODE_INFO_EXCEPTIONS) - 2; + + modeInfo.PSBit = 0; + modeInfo.Flags = infoExceptions->Flags; + + modeInfo.ReportMethod = infoExceptions->MRIE; + + REVERSE_BYTES(&modeInfo.IntervalTimer[0], + &infoExceptions->IntervalTimer); + + REVERSE_BYTES(&modeInfo.ReportCount[0], + &infoExceptions->ReportCount); + + if (modeInfo.Perf == 1) + { + diskData->AllowFPPerfHit = FALSE; + } else { + diskData->AllowFPPerfHit = TRUE; + } + + status = DiskSetInfoExceptionInformation(fdoExtension, + &modeInfo); + } else { + status = STATUS_INVALID_PARAMETER; + } + + } else if (GuidIndex <= SmartEventGuid) + { + status = STATUS_WMI_READ_ONLY; + } else { + status = STATUS_WMI_GUID_NOT_FOUND; + } + + DebugPrint((3, "Disk: DiskSetWmiDataBlock Device %p, Irp %p returns %lx\n", + DeviceObject, Irp, status)); + + status = ClassWmiCompleteRequest(DeviceObject, + Irp, + status, + 0, + IO_NO_INCREMENT); + + return status; +} + +NTSTATUS +DiskFdoSetWmiDataItem( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN ULONG GuidIndex, + IN ULONG DataItemId, + IN ULONG BufferSize, + IN PUCHAR Buffer + ) +/*++ + +Routine Description: + + This routine is a callback into the driver to query for the contents of + a data block. When the driver has finished filling the data block it + must call ClassWmiCompleteRequest to complete the irp. The driver can + return STATUS_PENDING if the irp cannot be completed immediately. + +Arguments: + + DeviceObject is the device whose data block is being queried + + Irp is the Irp that makes this request + + GuidIndex is the index into the list of guids provided when the + device registered + + DataItemId has the id of the data item being set + + BufferSize has the size of the data item passed + + Buffer has the new values for the data item + + +Return Value: + + status + +--*/ +{ + NTSTATUS status; + + PAGED_CODE(); + + DebugPrint((3, "Disk: DiskSetWmiDataItem, Device %p, Irp %p, GuiIndex %d, DataId %d\n" + " BufferSize %#x Buffer %p\n", + DeviceObject, Irp, + GuidIndex, DataItemId, BufferSize, Buffer)); + + if (GuidIndex <= SmartEventGuid) + { + status = STATUS_WMI_READ_ONLY; + } else { + status = STATUS_WMI_GUID_NOT_FOUND; + } + + DebugPrint((3, "Disk: DiskSetWmiDataItem Device %p, Irp %p returns %lx\n", + DeviceObject, Irp, status)); + + status = ClassWmiCompleteRequest(DeviceObject, + Irp, + status, + 0, + IO_NO_INCREMENT); + + return status; +} + + +NTSTATUS +DiskFdoExecuteWmiMethod( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN ULONG GuidIndex, + IN ULONG MethodId, + IN ULONG InBufferSize, + IN ULONG OutBufferSize, + IN PUCHAR Buffer + ) +/*++ + +Routine Description: + + This routine is a callback into the driver to execute a method. When the + driver has finished filling the data block it must call + ClassWmiCompleteRequest to complete the irp. The driver can + return STATUS_PENDING if the irp cannot be completed immediately. + +Arguments: + + DeviceObject is the device whose data block is being queried + + Irp is the Irp that makes this request + + GuidIndex is the index into the list of guids provided when the + device registered + + MethodId has the id of the method being called + + InBufferSize has the size of the data block passed in as the input to + the method. + + OutBufferSize on entry has the maximum size available to write the + returned data block. + + Buffer is filled with the returned data block + + +Return Value: + + status + +--*/ +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension; + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData); + ULONG sizeNeeded; + NTSTATUS status; + + PAGED_CODE(); + + DebugPrint((3, "Disk: DiskExecuteWmiMethod, DeviceObject %p, Irp %p, Guid Id %d, MethodId %d\n" + " InBufferSize %#x, OutBufferSize %#x, Buffer %p\n", + DeviceObject, Irp, + GuidIndex, MethodId, InBufferSize, OutBufferSize, Buffer)); + + switch(GuidIndex) + { + case SmartPerformFunction: + { + + ASSERT((diskData->FailurePredictionCapability == + FailurePredictionSmart) || + (diskData->FailurePredictionCapability == + FailurePredictionIoctl) || + (diskData->FailurePredictionCapability == + FailurePredictionSense)); + + + switch(MethodId) + { + // + // void AllowPerformanceHit([in] boolean Allow) + // + case AllowDisallowPerformanceHit: + { + BOOLEAN allowPerfHit; + + sizeNeeded = 0; + if (InBufferSize >= sizeof(BOOLEAN)) + { + status = STATUS_SUCCESS; + + allowPerfHit = *((PBOOLEAN)Buffer); + if (diskData->AllowFPPerfHit != allowPerfHit) + { + diskData->AllowFPPerfHit = allowPerfHit; + if (diskData->FailurePredictionCapability == + FailurePredictionSense) + { + MODE_INFO_EXCEPTIONS modeInfo; + + status = DiskGetInfoExceptionInformation(fdoExtension, + &modeInfo); + if (NT_SUCCESS(status)) + { + modeInfo.Perf = allowPerfHit ? 0 : 1; + status = DiskSetInfoExceptionInformation(fdoExtension, + &modeInfo); + } + } + } + + DebugPrint((3, "DiskFdoWmiExecuteMethod: AllowPerformanceHit %x for device %p --> %lx\n", + allowPerfHit, + fdoExtension->DeviceObject, + status)); + } else { + status = STATUS_INVALID_PARAMETER; + } + break; + } + + // + // void EnableDisableHardwareFailurePrediction([in] boolean Enable) + // + case EnableDisableHardwareFailurePrediction: + { + BOOLEAN enable; + + sizeNeeded = 0; + if (InBufferSize >= sizeof(BOOLEAN)) + { + status = STATUS_SUCCESS; + enable = *((PBOOLEAN)Buffer); + if (! enable) + { + // + // If we are disabling we need to also disable + // polling + // + DiskEnableDisableFailurePredictPolling( + fdoExtension, + enable, + 0); + } + + status = DiskEnableDisableFailurePrediction( + fdoExtension, + enable); + + DebugPrint((3, "DiskFdoWmiExecuteMethod: EnableDisableHardwareFailurePrediction: %x for device %p --> %lx\n", + enable, + fdoExtension->DeviceObject, + status)); + } else { + status = STATUS_INVALID_PARAMETER; + } + break; + } + + // + // void EnableDisableFailurePredictionPolling( + // [in] uint32 Period, + // [in] boolean Enable) + // + case EnableDisableFailurePredictionPolling: + { + BOOLEAN enable; + ULONG period; + + sizeNeeded = 0; + if (InBufferSize >= (sizeof(ULONG) + sizeof(BOOLEAN))) + { + period = *((PULONG)Buffer); + Buffer += sizeof(ULONG); + enable = *((PBOOLEAN)Buffer); + + status = DiskEnableDisableFailurePredictPolling( + fdoExtension, + enable, + period); + + DebugPrint((3, "DiskFdoWmiExecuteMethod: EnableDisableFailurePredictionPolling: %x %x for device %p --> %lx\n", + enable, + period, + fdoExtension->DeviceObject, + status)); + } else { + status = STATUS_INVALID_PARAMETER; + } + break; + } + + // + // void GetFailurePredictionCapability([out] uint32 Capability) + // + case GetFailurePredictionCapability: + { + sizeNeeded = sizeof(ULONG); + if (OutBufferSize >= sizeNeeded) + { + status = STATUS_SUCCESS; + *((PFAILURE_PREDICTION_METHOD)Buffer) = diskData->FailurePredictionCapability; + DebugPrint((3, "DiskFdoWmiExecuteMethod: GetFailurePredictionCapability: %x for device %p --> %lx\n", + *((PFAILURE_PREDICTION_METHOD)Buffer), + fdoExtension->DeviceObject, + status)); + } else { + status = STATUS_BUFFER_TOO_SMALL; + } + break; + } + + // + // void EnableOfflineDiags([out] boolean Success); + // + case EnableOfflineDiags: + { + sizeNeeded = sizeof(BOOLEAN); + if (OutBufferSize >= sizeNeeded) + { + if (diskData->FailurePredictionCapability == + FailurePredictionSmart) + { + // + // Initiate or resume offline diagnostics. + // This may cause a loss of performance + // to the disk, but mayincrease the amount + // of disk checking. + // + status = DiskExecuteSmartDiagnostics(fdoExtension, + 0); + + } else { + status = STATUS_INVALID_DEVICE_REQUEST; + } + + *((PBOOLEAN)Buffer) = NT_SUCCESS(status); + + DebugPrint((3, "DiskFdoWmiExecuteMethod: EnableOfflineDiags for device %p --> %lx\n", + fdoExtension->DeviceObject, + status)); + } else { + status = STATUS_BUFFER_TOO_SMALL; + } + break; + } + + // + // void ReadLogSectors([in] uint8 LogAddress, + // [in] uint8 SectorCount, + // [out] uint32 Length, + // [out, WmiSizeIs("Length")] uint8 LogSectors[] + // ); + // + case ReadLogSectors: + { + if (diskData->FailurePredictionCapability == + FailurePredictionSmart) + { + if (InBufferSize >= sizeof(READ_LOG_SECTORS_IN)) + { + PREAD_LOG_SECTORS_IN inParams; + PREAD_LOG_SECTORS_OUT outParams; + ULONG readSize; + + inParams = (PREAD_LOG_SECTORS_IN)Buffer; + readSize = inParams->SectorCount * SMART_LOG_SECTOR_SIZE; + sizeNeeded = FIELD_OFFSET(READ_LOG_SECTORS_OUT, + LogSectors) + readSize; + + if (OutBufferSize >= sizeNeeded) + { + outParams = (PREAD_LOG_SECTORS_OUT)Buffer; + status = DiskReadSmartLog(fdoExtension, + inParams->SectorCount, + inParams->LogAddress, + outParams->LogSectors); + + if (NT_SUCCESS(status)) + { + outParams->Length = readSize; + } else { + // + // SMART command failure is + // indicated by successful + // execution, but no data returned + // + outParams->Length = 0; + status = STATUS_SUCCESS; + } + } else { + status = STATUS_BUFFER_TOO_SMALL; + } + + } else { + status = STATUS_INVALID_PARAMETER; + } + } else { + status = STATUS_INVALID_DEVICE_REQUEST; + } + break; + } + + // void WriteLogSectors([in] uint8 LogAddress, + // [in] uint8 SectorCount, + // [in] uint32 Length, + // [in, WmiSizeIs("Length")] uint8 LogSectors[], + // [out] boolean Success + // ); + case WriteLogSectors: + { + if (diskData->FailurePredictionCapability == + FailurePredictionSmart) + { + if (InBufferSize >= FIELD_OFFSET(WRITE_LOG_SECTORS_IN, + LogSectors)) + { + PWRITE_LOG_SECTORS_IN inParams; + PWRITE_LOG_SECTORS_OUT outParams; + ULONG writeSize; + + inParams = (PWRITE_LOG_SECTORS_IN)Buffer; + writeSize = inParams->SectorCount * SMART_LOG_SECTOR_SIZE; + if (InBufferSize >= (FIELD_OFFSET(WRITE_LOG_SECTORS_IN, + LogSectors) + + writeSize)) + { + sizeNeeded = sizeof(WRITE_LOG_SECTORS_OUT); + + if (OutBufferSize >= sizeNeeded) + { + outParams = (PWRITE_LOG_SECTORS_OUT)Buffer; + status = DiskWriteSmartLog(fdoExtension, + inParams->SectorCount, + inParams->LogAddress, + inParams->LogSectors); + + if (NT_SUCCESS(status)) + { + outParams->Success = TRUE; + } else { + outParams->Success = FALSE; + status = STATUS_SUCCESS; + } + } else { + status = STATUS_BUFFER_TOO_SMALL; + } + } else { + status = STATUS_INVALID_PARAMETER; + } + } else { + status = STATUS_INVALID_PARAMETER; + } + } else { + status = STATUS_INVALID_DEVICE_REQUEST; + } + break; + } + + // void ExecuteSelfTest([in] uint8 Subcommand, + // [out, + // Values{"0", "1", "2"}, + // ValueMap{"Successful Completion", + // "Captive Mode Required", + // "Unsuccessful Completion"} + // ] + // uint32 ReturnCode); + case ExecuteSelfTest: + { + if (diskData->FailurePredictionCapability == + FailurePredictionSmart) + { + if (InBufferSize >= sizeof(EXECUTE_SELF_TEST_IN)) + { + sizeNeeded = sizeof(EXECUTE_SELF_TEST_OUT); + if (OutBufferSize >= sizeNeeded) + { + PEXECUTE_SELF_TEST_IN inParam; + PEXECUTE_SELF_TEST_OUT outParam; + + inParam = (PEXECUTE_SELF_TEST_IN)Buffer; + outParam = (PEXECUTE_SELF_TEST_OUT)Buffer; + + if (DiskIsValidSmartSelfTest(inParam->Subcommand)) + { + status = DiskExecuteSmartDiagnostics(fdoExtension, + inParam->Subcommand); + if (NT_SUCCESS(status)) + { + // + // Return self test executed + // without a problem + // + outParam->ReturnCode = 0; + } else { + // + // Return Self test execution + // failed status + // + outParam->ReturnCode = 2; + status = STATUS_SUCCESS; + } + } else { + // + // If self test subcommand requires + // captive mode then return that + // status + // + outParam->ReturnCode = 1; + status = STATUS_SUCCESS; + } + + } else { + status = STATUS_BUFFER_TOO_SMALL; + } + + } else { + status = STATUS_INVALID_PARAMETER; + } + } else { + status = STATUS_INVALID_DEVICE_REQUEST; + } + + break; + } + + default : + { + sizeNeeded = 0; + status = STATUS_WMI_ITEMID_NOT_FOUND; + break; + } + } + + break; + } + + case DiskGeometryGuid: + case SmartStatusGuid: + case SmartDataGuid: + { + + sizeNeeded = 0; + status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + default: + { + sizeNeeded = 0; + status = STATUS_WMI_GUID_NOT_FOUND; + } + } + + DebugPrint((3, "Disk: DiskExecuteMethod Device %p, Irp %p returns %lx\n", + DeviceObject, Irp, status)); + + status = ClassWmiCompleteRequest(DeviceObject, + Irp, + status, + sizeNeeded, + IO_NO_INCREMENT); + + return status; +} + + +#if 0 +// +// Enable this to add WMI support for PDOs +NTSTATUS +DiskPdoQueryWmiRegInfo( + IN PDEVICE_OBJECT DeviceObject, + OUT ULONG *RegFlags, + OUT PUNICODE_STRING InstanceName + ) +/*++ + +Routine Description: + + This routine is a callback into the driver to retrieve the list of + guids or data blocks that the driver wants to register with WMI. This + routine may not pend or block. Driver should NOT call + ClassWmiCompleteRequest. + +Arguments: + + DeviceObject is the device whose data block is being queried + + *RegFlags returns with a set of flags that describe the guids being + registered for this device. If the device wants enable and disable + collection callbacks before receiving queries for the registered + guids then it should return the WMIREG_FLAG_EXPENSIVE flag. Also the + returned flags may specify WMIREG_FLAG_INSTANCE_PDO in which case + the instance name is determined from the PDO associated with the + device object. Note that the PDO must have an associated devnode. If + WMIREG_FLAG_INSTANCE_PDO is not set then Name must return a unique + name for the device. + + InstanceName returns with the instance name for the guids if + WMIREG_FLAG_INSTANCE_PDO is not set in the returned *RegFlags. The + caller will call ExFreePool with the buffer returned. + + +Return Value: + + status + +--*/ +{ + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + PFUNCTIONAL_DEVICE_EXTENSION parentFunctionalExtension; + ANSI_STRING ansiString; + CHAR name[256]; + NTSTATUS status; + + // + // We need to pick a name for PDOs since they do not have a devnode + parentFunctionalExtension = commonExtension->PartitionZeroExtension; + sprintf(name, + "Disk(%d)_Partition(%d)_Start(%#I64x)_Length(%#I64x)", + parentFunctionalExtension->DeviceNumber, + commonExtension->PartitionNumber, + commonExtension->StartingOffset.QuadPart, + commonExtension->PartitionLength.QuadPart); + RtlInitAnsiString(&ansiString, + name); + + status = RtlAnsiStringToUnicodeString(InstanceName, + &ansiString, + TRUE); + + return status; +} + +NTSTATUS +DiskPdoQueryWmiDataBlock( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN ULONG GuidIndex, + IN ULONG BufferAvail, + OUT PUCHAR Buffer + ) +/*++ + +Routine Description: + + This routine is a callback into the driver to query for the contents of + a data block. When the driver has finished filling the data block it + must call ClassWmiCompleteRequest to complete the irp. The driver can + return STATUS_PENDING if the irp cannot be completed immediately. + +Arguments: + + DeviceObject is the device whose data block is being queried + + Irp is the Irp that makes this request + + GuidIndex is the index into the list of guids provided when the + device registered + + BufferAvail on has the maximum size available to write the data + block. + + Buffer on return is filled with the returned data block + + +Return Value: + + status + +--*/ +{ + NTSTATUS status; + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData); + ULONG sizeNeeded; + + DebugPrint((3, "Disk: DiskQueryWmiDataBlock, Device %p, Irp %p, GuiIndex %d\n" + " BufferAvail %#x Buffer %p\n", + DeviceObject, Irp, + GuidIndex, BufferAvail, Buffer)); + + switch (GuidIndex) + { + case 0: + { + sizeNeeded = 4 * sizeof(ULONG); + if (BufferAvail >= sizeNeeded) + { + RtlCopyMemory(Buffer, DiskDummyData, sizeNeeded); + status = STATUS_SUCCESS; + } else { + status = STATUS_BUFFER_TOO_SMALL; + } + break; + } + + default: + { + status = STATUS_WMI_GUID_NOT_FOUND; + } + } + + DebugPrint((3, "Disk: DiskQueryWmiDataBlock Device %p, Irp %p returns %lx\n", + DeviceObject, Irp, status)); + + status = ClassWmiCompleteRequest(DeviceObject, + Irp, + status, + sizeNeeded, + IO_NO_INCREMENT); + + return status; +} + +NTSTATUS +DiskPdoSetWmiDataBlock( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN ULONG GuidIndex, + IN ULONG BufferSize, + IN PUCHAR Buffer + ) +/*++ + +Routine Description: + + This routine is a callback into the driver to query for the contents of + a data block. When the driver has finished filling the data block it + must call ClassWmiCompleteRequest to complete the irp. The driver can + return STATUS_PENDING if the irp cannot be completed immediately. + +Arguments: + + DeviceObject is the device whose data block is being queried + + Irp is the Irp that makes this request + + GuidIndex is the index into the list of guids provided when the + device registered + + BufferSize has the size of the data block passed + + Buffer has the new values for the data block + + +Return Value: + + status + +--*/ +{ + NTSTATUS status; + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + ULONG sizeNeeded; + + DebugPrint((3, "Disk: DiskSetWmiDataBlock, Device %p, Irp %p, GuiIndex %d\n" + " BufferSize %#x Buffer %p\n", + DeviceObject, Irp, + GuidIndex, BufferSize, Buffer)); + + switch(GuidIndex) + { + case 0: + { + sizeNeeded = 4 * sizeof(ULONG); + if (BufferSize == sizeNeeded) + { + RtlCopyMemory(DiskDummyData, Buffer, sizeNeeded); + status = STATUS_SUCCESS; + } else { + status = STATUS_INFO_LENGTH_MISMATCH; + } + break; + } + + default: + { + status = STATUS_WMI_GUID_NOT_FOUND; + } + } + + DebugPrint((3, "Disk: DiskSetWmiDataBlock Device %p, Irp %p returns %lx\n", + DeviceObject, Irp, status)); + + status = ClassWmiCompleteRequest(DeviceObject, + Irp, + status, + 0, + IO_NO_INCREMENT); + + return status; +} + +NTSTATUS +DiskPdoSetWmiDataItem( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN ULONG GuidIndex, + IN ULONG DataItemId, + IN ULONG BufferSize, + IN PUCHAR Buffer + ) +/*++ + +Routine Description: + + This routine is a callback into the driver to query for the contents of + a data block. When the driver has finished filling the data block it + must call ClassWmiCompleteRequest to complete the irp. The driver can + return STATUS_PENDING if the irp cannot be completed immediately. + +Arguments: + + DeviceObject is the device whose data block is being queried + + Irp is the Irp that makes this request + + GuidIndex is the index into the list of guids provided when the + device registered + + DataItemId has the id of the data item being set + + BufferSize has the size of the data item passed + + Buffer has the new values for the data item + + +Return Value: + + status + +--*/ +{ + NTSTATUS status; + + DebugPrint((3, "Disk: DiskSetWmiDataItem, Device %p, Irp %p, GuiIndex %d, DataId %d\n" + " BufferSize %#x Buffer %p\n", + DeviceObject, Irp, + GuidIndex, DataItemId, BufferSize, Buffer)); + + switch(GuidIndex) + { + case 0: + { + if ((BufferSize == sizeof(ULONG)) && + (DataItemId <= 3)) + { + DiskDummyData[DataItemId] = *((PULONG)Buffer); + status = STATUS_SUCCESS; + } else { + status = STATUS_INVALID_DEVICE_REQUEST; + } + break; + } + + default: + { + status = STATUS_WMI_GUID_NOT_FOUND; + } + } + + + DebugPrint((3, "Disk: DiskSetWmiDataItem Device %p, Irp %p returns %lx\n", + DeviceObject, Irp, status)); + + status = ClassWmiCompleteRequest(DeviceObject, + Irp, + status, + 0, + IO_NO_INCREMENT); + + return status; +} + + +NTSTATUS +DiskPdoExecuteWmiMethod( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN ULONG GuidIndex, + IN ULONG MethodId, + IN ULONG InBufferSize, + IN ULONG OutBufferSize, + IN PUCHAR Buffer + ) +/*++ + +Routine Description: + + This routine is a callback into the driver to execute a method. When the + driver has finished filling the data block it must call + ClassWmiCompleteRequest to complete the irp. The driver can + return STATUS_PENDING if the irp cannot be completed immediately. + +Arguments: + + DeviceObject is the device whose data block is being queried + + Irp is the Irp that makes this request + + GuidIndex is the index into the list of guids provided when the + device registered + + MethodId has the id of the method being called + + InBufferSize has the size of the data block passed in as the input to + the method. + + OutBufferSize on entry has the maximum size available to write the + returned data block. + + Buffer is filled with the returned data block + + +Return Value: + + status + +--*/ +{ + ULONG sizeNeeded = 4 * sizeof(ULONG); + NTSTATUS status; + ULONG tempData[4]; + + DebugPrint((3, "Disk: DiskExecuteWmiMethod, DeviceObject %p, Irp %p, Guid Id %d, MethodId %d\n" + " InBufferSize %#x, OutBufferSize %#x, Buffer %p\n", + DeviceObject, Irp, + GuidIndex, MethodId, InBufferSize, OutBufferSize, Buffer)); + + switch(GuidIndex) + { + case 0: + { + if (MethodId == 1) + { + if (OutBufferSize >= sizeNeeded) + { + + if (InBufferSize == sizeNeeded) + { + RtlCopyMemory(tempData, Buffer, sizeNeeded); + RtlCopyMemory(Buffer, DiskDummyData, sizeNeeded); + RtlCopyMemory(DiskDummyData, tempData, sizeNeeded); + + status = STATUS_SUCCESS; + } else { + status = STATUS_INVALID_DEVICE_REQUEST; + } + } else { + status = STATUS_BUFFER_TOO_SMALL; + } + } else { + status = STATUS_INVALID_DEVICE_REQUEST; + } + break; + } + + default: + { + status = STATUS_WMI_GUID_NOT_FOUND; + } + } + + DebugPrint((3, "Disk: DiskExecuteMethod Device %p, Irp %p returns %lx\n", + DeviceObject, Irp, status)); + + status = ClassWmiCompleteRequest(DeviceObject, + Irp, + status, + 0, + IO_NO_INCREMENT); + + return status; +} +#endif + + + diff --git a/drivers/storage/class/disk_new/enum.c b/drivers/storage/class/disk_new/enum.c new file mode 100644 index 00000000000..d62a02c7c1b --- /dev/null +++ b/drivers/storage/class/disk_new/enum.c @@ -0,0 +1,1250 @@ +/*++ + +Copyright (C) Microsoft Corporation, 1991 - 1999 + +Module Name: + + pnp.c + +Abstract: + + SCSI disk class driver + +Environment: + + kernel mode only + +Notes: + +Revision History: + +--*/ + +#include "disk.h" + +#ifdef ALLOC_PRAGMA + +#pragma alloc_text(PAGE, DiskConvertExtendedToLayout) +#pragma alloc_text(PAGE, DiskConvertPartitionToExtended) +#pragma alloc_text(PAGE, DiskConvertLayoutToExtended) +#pragma alloc_text(PAGE, DiskCreatePdo) +#pragma alloc_text(PAGE, DiskEnumerateDevice) +#pragma alloc_text(PAGE, DiskUpdateRemovablePartitions) +#pragma alloc_text(PAGE, DiskUpdatePartitions) +#pragma alloc_text(PAGE, DiskCreatePdo) + +#endif + +PDRIVE_LAYOUT_INFORMATION +DiskConvertExtendedToLayout( + IN CONST PDRIVE_LAYOUT_INFORMATION_EX LayoutEx + ) +{ + ULONG i; + ULONG LayoutSize; + PDRIVE_LAYOUT_INFORMATION Layout; + PPARTITION_INFORMATION Partition; + PPARTITION_INFORMATION_EX PartitionEx; + + PAGED_CODE (); + + ASSERT ( LayoutEx ); + + + // + // The only valid conversion is from an MBR extended layout structure to + // the old structure. + // + + if (LayoutEx->PartitionStyle != PARTITION_STYLE_MBR) { + ASSERT ( FALSE ); + return NULL; + } + + LayoutSize = FIELD_OFFSET (DRIVE_LAYOUT_INFORMATION, PartitionEntry[0]) + + LayoutEx->PartitionCount * sizeof (PARTITION_INFORMATION); + + Layout = ExAllocatePoolWithTag ( + NonPagedPool, + LayoutSize, + DISK_TAG_PART_LIST + ); + + if ( Layout == NULL ) { + return NULL; + } + + Layout->Signature = LayoutEx->Mbr.Signature; + Layout->PartitionCount = LayoutEx->PartitionCount; + + for (i = 0; i < LayoutEx->PartitionCount; i++) { + + Partition = &Layout->PartitionEntry[i]; + PartitionEx = &LayoutEx->PartitionEntry[i]; + + Partition->StartingOffset = PartitionEx->StartingOffset; + Partition->PartitionLength = PartitionEx->PartitionLength; + Partition->RewritePartition = PartitionEx->RewritePartition; + Partition->PartitionNumber = PartitionEx->PartitionNumber; + + Partition->PartitionType = PartitionEx->Mbr.PartitionType; + Partition->BootIndicator = PartitionEx->Mbr.BootIndicator; + Partition->RecognizedPartition = PartitionEx->Mbr.RecognizedPartition; + Partition->HiddenSectors = PartitionEx->Mbr.HiddenSectors; + } + + return Layout; +} + +VOID +DiskConvertPartitionToExtended( + IN PPARTITION_INFORMATION Partition, + OUT PPARTITION_INFORMATION_EX PartitionEx + ) + +/*++ + +Routine Description: + + Convert a PARTITION_INFORMATION structure to a PARTITION_INFORMATION_EX + structure. + +Arguments: + + Partition - A pointer to the PARTITION_INFORMATION structure to convert. + + PartitionEx - A pointer to a buffer where the converted + PARTITION_INFORMATION_EX structure is to be stored. + +Return Values: + + None. + +--*/ + +{ + PAGED_CODE (); + + ASSERT ( PartitionEx != NULL ); + ASSERT ( Partition != NULL ); + + PartitionEx->PartitionStyle = PARTITION_STYLE_MBR; + PartitionEx->StartingOffset = Partition->StartingOffset; + PartitionEx->PartitionLength = Partition->PartitionLength; + PartitionEx->RewritePartition = Partition->RewritePartition; + PartitionEx->PartitionNumber = Partition->PartitionNumber; + + PartitionEx->Mbr.PartitionType = Partition->PartitionType; + PartitionEx->Mbr.BootIndicator = Partition->BootIndicator; + PartitionEx->Mbr.RecognizedPartition = Partition->RecognizedPartition; + PartitionEx->Mbr.HiddenSectors = Partition->HiddenSectors; +} + + +PDRIVE_LAYOUT_INFORMATION_EX +DiskConvertLayoutToExtended( + IN CONST PDRIVE_LAYOUT_INFORMATION Layout + ) + +/*++ + +Routine Description: + + Convert a DRIVE_LAYOUT_INFORMATION structure into a + DRIVE_LAYOUT_INFORMATION_EX structure. + +Arguments: + + Layout - The source DRIVE_LAYOUT_INFORMATION structure. + +Return Values: + + The resultant DRIVE_LAYOUT_INFORMATION_EX structure. This buffer must + be freed by the callee using ExFreePool. + +--*/ + +{ + ULONG i; + ULONG size; + PDRIVE_LAYOUT_INFORMATION_EX layoutEx; + + PAGED_CODE (); + + ASSERT ( Layout != NULL ); + + + // + // Allocate enough space for a DRIVE_LAYOUT_INFORMATION_EX structure + // plus as many PARTITION_INFORMATION_EX structures as are in the + // source array. + // + + size = FIELD_OFFSET (DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry[0]) + + Layout->PartitionCount * sizeof ( PARTITION_INFORMATION_EX ); + + layoutEx = ExAllocatePoolWithTag( + NonPagedPool, + size, + DISK_TAG_PART_LIST + ); + + if ( layoutEx == NULL ) { + return NULL; + } + + // + // Convert the disk information. + // + + layoutEx->PartitionStyle = PARTITION_STYLE_MBR; + layoutEx->PartitionCount = Layout->PartitionCount; + layoutEx->Mbr.Signature = Layout->Signature; + + for (i = 0; i < Layout->PartitionCount; i++) { + + // + // Convert each entry. + // + + DiskConvertPartitionToExtended ( + &Layout->PartitionEntry[i], + &layoutEx->PartitionEntry[i] + ); + } + + return layoutEx; +} + + + +NTSTATUS +NTAPI +DiskEnumerateDevice( + IN PDEVICE_OBJECT Fdo + ) + +/*++ + +Routine Description: + + This routine is called by the class driver to update the PDO list off + of this FDO. The disk driver also calls it internally to re-create + device objects. + + This routine will read the partition table and create new PDO objects as + necessary. PDO's that no longer exist will be pulled out of the PDO list + so that pnp will destroy them. + +Arguments: + + Fdo - a pointer to the FDO being re-enumerated + +Return Value: + + status + +--*/ + +{ + PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension; + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; + + PPHYSICAL_DEVICE_EXTENSION pdoExtension = NULL; + + PDISK_DATA diskData = (PDISK_DATA) commonExtension->DriverData; + + PDEVICE_OBJECT pdo = NULL; + + ULONG numberListElements = 0; + + PDRIVE_LAYOUT_INFORMATION_EX partitionList; + + NTSTATUS status; + + ASSERT(commonExtension->IsFdo); + + PAGED_CODE(); + + // + // Update our image of the size of the drive. This may be necessary if + // the drive size is extended or we just released a reservation to + // ensure the kernel doesn't reject the partition table. + // + + DiskReadDriveCapacity(Fdo); + + // + // Lock out anyone else trying to repartition the disk. + // + + DiskAcquirePartitioningLock(fdoExtension); + + // + // Create objects for all the partitions on the device. + // + + status = DiskReadPartitionTableEx(fdoExtension, FALSE, &partitionList); + + // + // If the I/O read partition table failed and this is a removable device, + // then fix up the partition list to make it look like there is one + // zero length partition. + // + + if ((!NT_SUCCESS(status) || partitionList->PartitionCount == 0) && + Fdo->Characteristics & FILE_REMOVABLE_MEDIA) { + + SIZE_T partitionListSize; + + // + // Remember whether the drive is ready. + // + + diskData->ReadyStatus = status; + + // + // Allocate and zero a partition list. + // + + partitionListSize = + FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry[1]); + + partitionList = ExAllocatePoolWithTag(NonPagedPool, + partitionListSize, + DISK_TAG_PART_LIST); + + if (partitionList != NULL) { + + RtlZeroMemory( partitionList, partitionListSize ); + + // + // Set the partition count to one and the status to success + // so one device object will be created. Set the partition type + // to a bogus value. + // + + partitionList->PartitionStyle = PARTITION_STYLE_MBR; + partitionList->PartitionCount = 1; + + status = STATUS_SUCCESS; + } else { + status = STATUS_INSUFFICIENT_RESOURCES; + } + } + + if (NT_SUCCESS(status)) { + + diskData->UpdatePartitionRoutine(Fdo, partitionList); + + // + // Record disk signature. + // + + if (partitionList->PartitionStyle == PARTITION_STYLE_MBR) { + + diskData->PartitionStyle = PARTITION_STYLE_MBR; + diskData->Mbr.Signature = partitionList->Mbr.Signature; + + } else { + + diskData->PartitionStyle = PARTITION_STYLE_GPT; + diskData->Efi.DiskId = partitionList->Gpt.DiskId; + } + } + + DiskReleasePartitioningLock(fdoExtension); + + return(STATUS_SUCCESS); + +} // end DiskEnumerateDevice() + + +VOID +DiskUpdateRemovablePartitions( + IN PDEVICE_OBJECT Fdo, + IN OUT PDRIVE_LAYOUT_INFORMATION_EX PartitionList + ) + +/*++ + +Routine Description: + + This routine is called by the class DLL to update the PDO list off of this + FDO. The disk driver also calls it internally to re-create device objects. + + This routine will read the partition table and update the size of the + single partition device object which always exists for removable devices. + +Arguments: + + Fdo - a pointer to the FDO being reenumerated. + +Return Value: + + status + +--*/ + +{ + + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; + + PPHYSICAL_DEVICE_EXTENSION pdoExtension = NULL; + + ULONG partitionCount; + + ULONG partitionNumber; + ULONG partitionOrdinal = 0; + ULONG newPartitionNumber; + + PDISK_DATA pdoData; + NTSTATUS status; + + PPARTITION_INFORMATION_EX partitionEntry; + PARTITION_STYLE partitionStyle; + + PAGED_CODE(); + + ASSERT(Fdo->Characteristics & FILE_REMOVABLE_MEDIA); + + partitionStyle = PartitionList->PartitionStyle; + partitionCount = PartitionList->PartitionCount; + + for(partitionNumber = 0; + partitionNumber < partitionCount; + partitionNumber++) { + + partitionEntry = &(PartitionList->PartitionEntry[partitionNumber]); + + partitionEntry->PartitionNumber = 0; + } + + // + // Get exclusive access to the child list while repartitioning. + // + + ClassAcquireChildLock(fdoExtension); + + // + // Removable media should never have more than one PDO. + // + + pdoExtension = fdoExtension->CommonExtension.ChildList; + + if(pdoExtension == NULL) { + + PARTITION_INFORMATION_EX tmpPartitionEntry; + PDEVICE_OBJECT pdo; + + // + // There is no PDO currently. Create one and pre-initialize it with + // a zero length. + // + + RtlZeroMemory(&tmpPartitionEntry, sizeof(tmpPartitionEntry)); + + tmpPartitionEntry.PartitionNumber = 1; + + DebugPrint((1, "DiskUpdateRemovablePartitions: Creating RM partition\n")); + + status = DiskCreatePdo(Fdo, + 0, + &tmpPartitionEntry, + partitionStyle, + &pdo); + + if(!NT_SUCCESS(status)) { + + DebugPrint((1, "DiskUpdateRemovablePartitions: error %lx creating " + "new PDO for RM partition\n", + status)); + + ClassReleaseChildLock(fdoExtension); + return; + } + + // + // mark the new device as enumerated + // + + pdoExtension = pdo->DeviceExtension; + pdoExtension->IsMissing = FALSE; + + } + + pdoData = pdoExtension->CommonExtension.DriverData; + + // + // Search the partition list for a valid entry. We're looking for a + // primary partition since we only support the one. + // + + for(partitionNumber = 0; + partitionNumber < partitionCount; + partitionNumber++) { + + partitionEntry = &(PartitionList->PartitionEntry[partitionNumber]); + + + // + // Is this partition interesting? + // + + if (partitionStyle == PARTITION_STYLE_MBR) { + + if(partitionEntry->Mbr.PartitionType == PARTITION_ENTRY_UNUSED || + IsContainerPartition(partitionEntry->Mbr.PartitionType)) { + + continue; + } + } + + partitionOrdinal++; + + // + // We have found the first and thus only partition allowed on + // this disk. Update the information in the PDO to match the new + // partition. + // + DebugPrint((1, "DiskUpdateRemovablePartitions: Matched %wZ to #%d, " + "ordinal %d\n", + &pdoExtension->CommonExtension.DeviceName, + partitionEntry->PartitionNumber, + partitionOrdinal)); + + + partitionEntry->PartitionNumber = 1; + + pdoData->PartitionStyle = partitionStyle; + pdoData->PartitionOrdinal = partitionOrdinal; + ASSERT(partitionEntry->PartitionLength.LowPart != 0x23456789); + + pdoExtension->CommonExtension.StartingOffset = + partitionEntry->StartingOffset; + + pdoExtension->CommonExtension.PartitionLength = + partitionEntry->PartitionLength; + + + if (partitionStyle == PARTITION_STYLE_MBR) { + + pdoData->Mbr.HiddenSectors = partitionEntry->Mbr.HiddenSectors; + pdoData->Mbr.BootIndicator = partitionEntry->Mbr.BootIndicator; + + + // + // If this partition is being re-written then update the type + // information as well + // + + if (partitionEntry->RewritePartition) { + pdoData->Mbr.PartitionType = partitionEntry->Mbr.PartitionType; + } + + } else { + + pdoData->Efi.PartitionType = partitionEntry->Gpt.PartitionType; + pdoData->Efi.PartitionId = partitionEntry->Gpt.PartitionId; + pdoData->Efi.Attributes = partitionEntry->Gpt.Attributes; + + RtlCopyMemory( + pdoData->Efi.PartitionName, + partitionEntry->Gpt.Name, + sizeof (pdoData->Efi.PartitionName) + ); + } + + // + // Mark this one as found + // + + pdoExtension->IsMissing = FALSE; + ClassReleaseChildLock(fdoExtension); + return; + } + + // + // No interesting partition was found. + // + + if (partitionStyle == PARTITION_STYLE_MBR) { + + pdoData->Mbr.HiddenSectors = 0; + pdoData->Mbr.PartitionType = PARTITION_ENTRY_UNUSED; + + } else { + + RtlZeroMemory (&pdoData->Efi, + sizeof (pdoData->Efi) + ); + } + + pdoExtension->CommonExtension.StartingOffset.QuadPart = 0; + pdoExtension->CommonExtension.PartitionLength.QuadPart = 0; + + ClassReleaseChildLock(fdoExtension); + return; +} + + +VOID +DiskUpdatePartitions( + IN PDEVICE_OBJECT Fdo, + IN OUT PDRIVE_LAYOUT_INFORMATION_EX PartitionList + ) + +/*++ + +Routine Description: + + This routine will synchronize the information held in the partition list + with the device objects hanging off this Fdo. Any new partition objects + will be created, any non-existant ones will be marked as un-enumerated. + + This will be done in several stages: + + * Clear state (partition number) from every entry in the partition + list + + * Set IsMissing flag on every child of this FDO + + * For each child of the FDO: + if a matching partition exists in the partition list, + update the partition number in the table, update the + ordinal in the object and mark the object as enumerated + + * For each un-enumerated device object + zero out the partition information to invalidate the device + delete the symbolic link if any + + * For each un-matched entry in the partition list: + create a new partition object + update the partition number in the list entry + create a new symbolic link if necessary + +Arguments: + + Fdo - a pointer to the functional device object this partition list is for + + PartitionList - a pointer to the partition list being updated + +Return Value: + + none + +--*/ + +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; + + PPHYSICAL_DEVICE_EXTENSION oldChildList = NULL; + PPHYSICAL_DEVICE_EXTENSION pdoExtension = NULL; + + ULONG partitionCount; + + ULONG partitionNumber; + ULONG partitionOrdinal; + ULONG newPartitionNumber; + + PPARTITION_INFORMATION_EX partitionEntry; + PDISK_DATA pdoData; + PARTITION_STYLE partitionStyle; + + NTSTATUS status; + + PAGED_CODE(); + + // + // Get exclusive access to the child list. + // + + ClassAcquireChildLock(fdoExtension); + + partitionStyle = PartitionList->PartitionStyle; + + partitionCount = PartitionList->PartitionCount; + + // + // Pull all the child device objects off the children list. We'll + // add them back later. + // + + oldChildList = fdoExtension->CommonExtension.ChildList; + fdoExtension->CommonExtension.ChildList = NULL; + + // + // Clear the partition numbers from the list entries + // + + for(partitionNumber = 0; + partitionNumber < partitionCount; + partitionNumber++) { + + partitionEntry = &(PartitionList->PartitionEntry[partitionNumber]); + partitionEntry->PartitionNumber = 0; + } + + // + // Now match each child partition to it's entry (if any) in the partition + // list. + // + + while(oldChildList != NULL) { + + pdoExtension = oldChildList; + pdoData = pdoExtension->CommonExtension.DriverData; + + // + // Check all partition entries for a match on offset and length + // + + partitionOrdinal = 0; + + for(partitionNumber = 0; + partitionNumber < partitionCount; + partitionNumber++) { + + partitionEntry = &(PartitionList->PartitionEntry[partitionNumber]); + + // + // Is this an interesting partition entry? + // + + if (partitionStyle == PARTITION_STYLE_MBR) { + + if((partitionEntry->Mbr.PartitionType == PARTITION_ENTRY_UNUSED) || + (IsContainerPartition(partitionEntry->Mbr.PartitionType))) { + + continue; + } + } + + partitionOrdinal++; + + if(partitionEntry->PartitionNumber) { + + // + // This partition has already been found - skip it + // + + continue; + } + + // + // Let's see if the partition information matches + // + + if(partitionEntry->StartingOffset.QuadPart != + pdoExtension->CommonExtension.StartingOffset.QuadPart) { + continue; + } + + if(partitionEntry->PartitionLength.QuadPart != + pdoExtension->CommonExtension.PartitionLength.QuadPart) { + continue; + } + + // + // Yep - it matches. Update the information in the entry + // + + partitionEntry->PartitionNumber = pdoExtension->CommonExtension.PartitionNumber; + + if (partitionStyle == PARTITION_STYLE_MBR) { + + pdoData->Mbr.HiddenSectors = partitionEntry->Mbr.HiddenSectors; + + } + + break; + } + + if(partitionNumber != partitionCount) { + + DebugPrint((1, "DiskUpdatePartitions: Matched %wZ to #%d, ordinal " + "%d\n", + &pdoExtension->CommonExtension.DeviceName, + partitionEntry->PartitionNumber, + partitionOrdinal)); + + ASSERT(partitionEntry->PartitionLength.LowPart != 0x23456789); + // ASSERT(pdoExtension->CommonExtension.PartitionLength.QuadPart != 0); + + pdoData->PartitionStyle = partitionStyle; + + // + // we found a match - update the information in the device object + // extension and driverdata + // + + pdoData->PartitionOrdinal = partitionOrdinal; + + // + // If this partition is being re-written then update the type + // information as well + // + + + if (partitionStyle == PARTITION_STYLE_MBR) { + + if(partitionEntry->RewritePartition) { + pdoData->Mbr.PartitionType = partitionEntry->Mbr.PartitionType; + } + + } else { + + DebugPrint((1, "DiskUpdatePartitions: EFI Partition %ws\n", + pdoData->Efi.PartitionName + )); + + pdoData->Efi.PartitionType = partitionEntry->Gpt.PartitionType; + pdoData->Efi.PartitionId = partitionEntry->Gpt.PartitionId; + pdoData->Efi.Attributes = partitionEntry->Gpt.Attributes; + + RtlCopyMemory( + pdoData->Efi.PartitionName, + partitionEntry->Gpt.Name, + sizeof (pdoData->Efi.PartitionName) + ); + } + + // + // Mark this one as found. + // + + pdoExtension->IsMissing = FALSE; + + // + // Pull it out of the old child list and add it into the + // real one. + // + + oldChildList = pdoExtension->CommonExtension.ChildList; + + pdoExtension->CommonExtension.ChildList = + fdoExtension->CommonExtension.ChildList; + + fdoExtension->CommonExtension.ChildList = pdoExtension; + + } else { + + PDEVICE_OBJECT nextPdo; + + DebugPrint ((1, "DiskUpdatePartitions: Deleting %wZ\n", + &pdoExtension->CommonExtension.DeviceName)); + + if (partitionStyle == PARTITION_STYLE_GPT) { + + DebugPrint ((1, "DiskUpdatePartitions: EFI Partition %ws\n", + pdoData->Efi.PartitionName + )); + } + // + // no matching entry in the partition list - throw this partition + // object away + // + + pdoExtension->CommonExtension.PartitionLength.QuadPart = 0; + + // + // grab a pointer to the next child before we mark this one as + // missing since missing devices could vanish at any time. + // + + oldChildList = pdoExtension->CommonExtension.ChildList; + pdoExtension->CommonExtension.ChildList = (PVOID) -1; + + // + // Now tell the class driver that this child is "missing" - this + // will cause it to be deleted. + // + + + ClassMarkChildMissing(pdoExtension, FALSE); + } + } + + // + // At this point the old child list had best be empty. + // + + ASSERT(oldChildList == NULL); + + // + // Iterate through the partition entries and create any partition + // objects that don't already exist + // + + partitionOrdinal = 0; + newPartitionNumber = 0; + + for(partitionNumber = 0; + partitionNumber < partitionCount; + partitionNumber++) { + + PDEVICE_OBJECT pdo; + + partitionEntry = &(PartitionList->PartitionEntry[partitionNumber]); + + // + // Is this partition interesting + // + + if (partitionStyle == PARTITION_STYLE_MBR) { + + if((partitionEntry->Mbr.PartitionType == PARTITION_ENTRY_UNUSED) || + (IsContainerPartition(partitionEntry->Mbr.PartitionType))) { + + continue; + } + } + + // + // Increment the count of interesting partitions + // + + partitionOrdinal++; + newPartitionNumber++; + + // + // Has this already been matched + // + + if(partitionEntry->PartitionNumber == 0) { + + LONG i; + + // + // find the first safe partition number for this device + // + + for(i = 0; i < (LONG) partitionCount; i++) { + + + PPARTITION_INFORMATION_EX tmp = &(PartitionList->PartitionEntry[i]); + + if (partitionStyle == PARTITION_STYLE_MBR) { + if (tmp->Mbr.PartitionType == PARTITION_ENTRY_UNUSED || + IsContainerPartition(tmp->Mbr.PartitionType)) { + continue; + } + } + + if(tmp->PartitionNumber == newPartitionNumber) { + + // + // Found a matching partition number - increment the count + // and restart the scan. + // + + newPartitionNumber++; + i = -1; + continue; + } + } + + // + // Assign this partition a partition number + // + + partitionEntry->PartitionNumber = newPartitionNumber; + + DebugPrint((1, "DiskUpdatePartitions: Found new partition #%d, ord %d " + "starting at %#016I64x and running for %#016I64x\n", + partitionEntry->PartitionNumber, + partitionOrdinal, + partitionEntry->StartingOffset.QuadPart, + partitionEntry->PartitionLength.QuadPart)); + + ClassReleaseChildLock(fdoExtension); + + status = DiskCreatePdo(Fdo, + partitionOrdinal, + partitionEntry, + partitionStyle, + &pdo); + + ClassAcquireChildLock(fdoExtension); + + if(!NT_SUCCESS(status)) { + + DebugPrint((1, "DiskUpdatePartitions: error %lx creating " + "new PDO for partition ordinal %d, number %d\n", + status, + partitionOrdinal, + partitionEntry->PartitionNumber)); + + // + // don't increment the partition number - we'll try to reuse + // it for the next child. + // + + partitionEntry->PartitionNumber = 0; + newPartitionNumber--; + + continue; + } + + // + // mark the new device as enumerated + // + + pdoExtension = pdo->DeviceExtension; + pdoExtension->IsMissing = FALSE; + + // + // This number's taken already - try to scanning the partition + // table more than once for a new number. + // + + } + } + + // + // ISSUE - 2000/02/09 - math: Review. + // Is PartitionStyle the only field that needs updating? + // + + { + PCOMMON_DEVICE_EXTENSION commonExtension; + PDISK_DATA diskData; + + commonExtension = Fdo->DeviceExtension; + diskData = (PDISK_DATA)(commonExtension->DriverData); + + diskData->PartitionStyle = partitionStyle; + } + + ClassReleaseChildLock(fdoExtension); + return; +} + + +NTSTATUS +DiskCreatePdo( + IN PDEVICE_OBJECT Fdo, + IN ULONG PartitionOrdinal, + IN PPARTITION_INFORMATION_EX PartitionEntry, + IN PARTITION_STYLE PartitionStyle, + OUT PDEVICE_OBJECT *Pdo + ) + +/*++ + +Routine Description: + + This routine will create and initialize a new partition device object + (PDO) and insert it into the FDO partition list. + +Arguments: + + Fdo - a pointer to the functional device object this PDO will be a child + of + + PartitionOrdinal - the partition ordinal for this PDO + + PartitionEntry - the partition information for this device object + + PartitionStyle - what style of partition table entry PartitionEntry is; + currently either MBR or EFI + + Pdo - a location to store the pdo pointer upon successful completion + +Return Value: + + status + +--*/ + +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; + + PDEVICE_OBJECT pdo = NULL; + PPHYSICAL_DEVICE_EXTENSION pdoExtension = NULL; + + PUCHAR deviceName = NULL; + + PDISK_DATA diskData = fdoExtension->CommonExtension.DriverData; + + ULONG numberListElements; + + NTSTATUS status = STATUS_SUCCESS; + + PAGED_CODE(); + + // + // Create partition object and set up partition parameters. + // + + status = DiskGenerateDeviceName(FALSE, + fdoExtension->DeviceNumber, + PartitionEntry->PartitionNumber, + &PartitionEntry->StartingOffset, + &PartitionEntry->PartitionLength, + &deviceName); + + if(!NT_SUCCESS(status)) { + + DebugPrint((1, "DiskCreatePdo - Can't generate name %lx\n", status)); + return status; + } + + DebugPrint((2, "DiskCreatePdo: Create device object %s\n", deviceName)); + + status = ClassCreateDeviceObject(Fdo->DriverObject, + deviceName, + Fdo, + FALSE, + &pdo); + + if (!NT_SUCCESS(status)) { + + DebugPrint((1, "DiskEnumerateDevice: Can't create device object for %s\n", deviceName)); + + return status; + } + + // + // Set up device extension fields. + // + + pdoExtension = pdo->DeviceExtension; + + // + // Set up device object fields. + // + + SET_FLAG(pdo->Flags, DO_DIRECT_IO); + + pdo->StackSize = (CCHAR) + pdoExtension->CommonExtension.LowerDeviceObject->StackSize + 1; + + // + // Get pointer to new disk data. + // + + diskData = (PDISK_DATA) pdoExtension->CommonExtension.DriverData; + + // + // Set the alignment requirements for the device based on the + // host adapter requirements + // + + if (Fdo->AlignmentRequirement > pdo->AlignmentRequirement) { + pdo->AlignmentRequirement = Fdo->AlignmentRequirement; + } + + if (fdoExtension->SrbFlags & SRB_FLAGS_QUEUE_ACTION_ENABLE) { + numberListElements = 30; + } else { + numberListElements = 8; + } + + // + // Build the lookaside list for srb's for this partition based on + // whether the adapter and disk can do tagged queueing. Don't bother to + // check the status - this can't fail when called for a PDO. + // + + ClassInitializeSrbLookasideList((PCOMMON_DEVICE_EXTENSION) pdoExtension, + numberListElements); + + // + // Set the sense-data pointer in the device extension. + // + + diskData->PartitionOrdinal = PartitionOrdinal; + pdoExtension->CommonExtension.PartitionNumber = PartitionEntry->PartitionNumber; + + // + // Initialize relevant data. + // + + if (PartitionStyle == PARTITION_STYLE_MBR) { + + diskData->Mbr.PartitionType = PartitionEntry->Mbr.PartitionType; + diskData->Mbr.BootIndicator = PartitionEntry->Mbr.BootIndicator; + diskData->Mbr.HiddenSectors = PartitionEntry->Mbr.HiddenSectors; + + } else { + + diskData->Efi.PartitionType = PartitionEntry->Gpt.PartitionType; + diskData->Efi.PartitionId = PartitionEntry->Gpt.PartitionType; + diskData->Efi.Attributes = PartitionEntry->Gpt.Attributes; + RtlCopyMemory (diskData->Efi.PartitionName, + PartitionEntry->Gpt.Name, + sizeof (diskData->Efi.PartitionName) + ); + } + + DebugPrint((2, "DiskEnumerateDevice: Partition type is %x\n", + diskData->Mbr.PartitionType)); + + pdoExtension->CommonExtension.StartingOffset = + PartitionEntry->StartingOffset; + + pdoExtension->CommonExtension.PartitionLength = + PartitionEntry->PartitionLength; + + + DebugPrint((1, "DiskCreatePdo: hidden sectors value for pdo %#p set to %#x\n", + pdo, + diskData->Mbr.HiddenSectors)); + + // + // Check for removable media support. + // + + if (fdoExtension->DeviceDescriptor->RemovableMedia) { + SET_FLAG(pdo->Characteristics, FILE_REMOVABLE_MEDIA); + } + + pdoExtension->CommonExtension.DeviceObject = pdo; + + CLEAR_FLAG(pdo->Flags, DO_DEVICE_INITIALIZING); + + *Pdo = pdo; + + return status; +} + + + + + +VOID +DiskAcquirePartitioningLock( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension + ) +{ + PDISK_DATA diskData = FdoExtension->CommonExtension.DriverData; + + PAGED_CODE(); + + ASSERT_FDO(FdoExtension->DeviceObject); + + KeWaitForSingleObject(&(diskData->PartitioningEvent), + UserRequest, + UserMode, + FALSE, + NULL); + return; +} + + +VOID +DiskReleasePartitioningLock( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension + ) +{ + PDISK_DATA diskData = FdoExtension->CommonExtension.DriverData; + + PAGED_CODE(); + + ASSERT_FDO(FdoExtension->DeviceObject); + + KeSetEvent(&(diskData->PartitioningEvent), IO_NO_INCREMENT, FALSE); + return; +} + diff --git a/drivers/storage/class/disk_new/geometry.c b/drivers/storage/class/disk_new/geometry.c new file mode 100644 index 00000000000..4607acb80e5 --- /dev/null +++ b/drivers/storage/class/disk_new/geometry.c @@ -0,0 +1,1470 @@ +/*++ + +Copyright (C) Microsoft Corporation, 1991 - 1999 + +Module Name: + + geometry.c + +Abstract: + + SCSI disk class driver - this module contains all the code for generating + disk geometries. + +Environment: + + kernel mode only + +Notes: + +Revision History: + +--*/ + + +#include "disk.h" +#include "ntddstor.h" + +#if defined (_X86_) + +DISK_GEOMETRY_SOURCE +DiskUpdateGeometry( + IN PFUNCTIONAL_DEVICE_EXTENSION DeviceExtension + ); + +NTSTATUS +DiskUpdateRemovableGeometry ( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension + ); + +VOID +DiskScanBusDetectInfo( + IN PDRIVER_OBJECT DriverObject, + IN HANDLE BusKey + ); + +NTSTATUS +DiskSaveBusDetectInfo( + IN PDRIVER_OBJECT DriverObject, + IN HANDLE TargetKey, + IN ULONG DiskNumber + ); + +NTSTATUS +DiskSaveGeometryDetectInfo( + IN PDRIVER_OBJECT DriverObject, + IN HANDLE HardwareKey + ); + +NTSTATUS +DiskGetPortGeometry( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + OUT PDISK_GEOMETRY Geometry + ); + +typedef struct _DISK_DETECT_INFO { + BOOLEAN Initialized; + ULONG Style; + ULONG Signature; + ULONG MbrCheckSum; + PDEVICE_OBJECT Device; + CM_INT13_DRIVE_PARAMETER DriveParameters; +} DISK_DETECT_INFO, *PDISK_DETECT_INFO; + +// +// Information about the disk geometries collected and saved into the registry +// by NTDETECT.COM or the system firmware. +// + +PDISK_DETECT_INFO DetectInfoList = NULL; +ULONG DetectInfoCount = 0; +ULONG DetectInfoUsedCount = 0; + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, DiskSaveDetectInfo) +#pragma alloc_text(INIT, DiskScanBusDetectInfo) +#pragma alloc_text(INIT, DiskSaveBusDetectInfo) +#pragma alloc_text(INIT, DiskSaveGeometryDetectInfo) + +#pragma alloc_text(PAGE, DiskUpdateGeometry) +#pragma alloc_text(PAGE, DiskUpdateRemovableGeometry) +#pragma alloc_text(PAGE, DiskGetPortGeometry) +#pragma alloc_text(PAGE, DiskGetDetectInfo) +#pragma alloc_text(PAGE, DiskReadSignature) +#endif + + + +NTSTATUS +DiskSaveDetectInfo( + PDRIVER_OBJECT DriverObject + ) +/*++ + +Routine Description: + + This routine saves away the firmware information about the disks which has + been saved in the registry. It generates a list (DetectInfoList) which + contains the disk geometries, signatures & checksums of all drives which + were examined by NtDetect. This list is later used to assign geometries + to disks as they are initialized. + +Arguments: + + DriverObject - the driver being initialized. This is used to get to the + hardware database. + +Return Value: + + status. + +--*/ + +{ + OBJECT_ATTRIBUTES objectAttributes; + HANDLE hardwareKey; + + UNICODE_STRING unicodeString; + HANDLE busKey; + + NTSTATUS status; + + PAGED_CODE(); + + InitializeObjectAttributes( + &objectAttributes, + DriverObject->HardwareDatabase, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + + // + // Create the hardware base key. + // + + status = ZwOpenKey(&hardwareKey, KEY_READ, &objectAttributes); + + if(!NT_SUCCESS(status)) { + DebugPrint((1, "DiskSaveDetectInfo: Cannot open hardware data. " + "Name: %wZ\n", + DriverObject->HardwareDatabase)); + return status; + } + + status = DiskSaveGeometryDetectInfo(DriverObject, hardwareKey); + + if(!NT_SUCCESS(status)) { + DebugPrint((1, "DiskSaveDetectInfo: Can't query configuration data " + "(%#08lx)\n", + status)); + ZwClose(hardwareKey); + return status; + } + + // + // Open EISA bus key. + // + + RtlInitUnicodeString(&unicodeString, L"EisaAdapter"); + InitializeObjectAttributes(&objectAttributes, + &unicodeString, + OBJ_CASE_INSENSITIVE, + hardwareKey, + NULL); + + status = ZwOpenKey(&busKey, + KEY_READ, + &objectAttributes); + + if(NT_SUCCESS(status)) { + DebugPrint((1, "DiskSaveDetectInfo: Opened EisaAdapter key\n")); + DiskScanBusDetectInfo(DriverObject, busKey); + ZwClose(busKey); + } + + // + // Open MultiFunction bus key. + // + + RtlInitUnicodeString(&unicodeString, L"MultifunctionAdapter"); + InitializeObjectAttributes(&objectAttributes, + &unicodeString, + OBJ_CASE_INSENSITIVE, + hardwareKey, + NULL); + + status = ZwOpenKey(&busKey, + KEY_READ, + &objectAttributes); + + if(NT_SUCCESS(status)) { + DebugPrint((1, "DiskSaveDetectInfo: Opened MultifunctionAdapter key\n")); + DiskScanBusDetectInfo(DriverObject, busKey); + ZwClose(busKey); + } + + ZwClose(hardwareKey); + + return STATUS_SUCCESS; +} + + +VOID +DiskCleanupDetectInfo( + IN PDRIVER_OBJECT DriverObject + ) +/*++ + +Routine Description: + + This routine will cleanup the data structure built by DiskSaveDetectInfo. + +Arguments: + + DriverObject - a pointer to the kernel object for this driver. + +Return Value: + + none + +--*/ + +{ + if(DetectInfoList != NULL) { + ExFreePool(DetectInfoList); + DetectInfoList = NULL; + } + return; +} + + +NTSTATUS +DiskSaveGeometryDetectInfo( + IN PDRIVER_OBJECT DriverObject, + IN HANDLE HardwareKey + ) +{ + UNICODE_STRING unicodeString; + PKEY_VALUE_FULL_INFORMATION keyData; + ULONG length; + + PCM_FULL_RESOURCE_DESCRIPTOR fullDescriptor; + PCM_PARTIAL_RESOURCE_DESCRIPTOR partialDescriptor; + + PCM_INT13_DRIVE_PARAMETER driveParameters; + ULONG numberOfDrives; + + ULONG i; + + NTSTATUS status; + + PAGED_CODE(); + + // + // Get disk BIOS geometry information. + // + + RtlInitUnicodeString(&unicodeString, L"Configuration Data"); + + keyData = ExAllocatePoolWithTag(PagedPool, + VALUE_BUFFER_SIZE, + DISK_TAG_UPDATE_GEOM); + + if(keyData == NULL) { + DebugPrint((1, "DiskSaveGeometryDetectInfo: Can't allocate config " + "data buffer\n")); + return STATUS_INSUFFICIENT_RESOURCES; + } + + status = ZwQueryValueKey(HardwareKey, + &unicodeString, + KeyValueFullInformation, + keyData, + VALUE_BUFFER_SIZE, + &length); + + if(!NT_SUCCESS(status)) { + DebugPrint((1, "DiskSaveGeometryDetectInfo: Can't query configuration " + "data (%#08lx)\n", + status)); + ExFreePool(keyData); + return status; + } + + // + // Extract the resource list out of the key data. + // + + fullDescriptor = (PCM_FULL_RESOURCE_DESCRIPTOR) + (((PUCHAR) keyData) + keyData->DataOffset); + partialDescriptor = + fullDescriptor->PartialResourceList.PartialDescriptors; + length = partialDescriptor->u.DeviceSpecificData.DataSize; + + if((keyData->DataLength < sizeof(CM_FULL_RESOURCE_DESCRIPTOR)) || + (fullDescriptor->PartialResourceList.Count == 0) || + (partialDescriptor->Type != CmResourceTypeDeviceSpecific) || + (length < sizeof(ULONG))) { + + DebugPrint((1, "DiskSaveGeometryDetectInfo: BIOS header data too small " + "or invalid\n")); + ExFreePool(keyData); + return STATUS_INVALID_PARAMETER; + } + + // + // Point to the BIOS data. THe BIOS data is located after the first + // partial Resource list which should be device specific data. + // + + { + PUCHAR buffer = (PUCHAR) keyData; + buffer += keyData->DataOffset; + buffer += sizeof(CM_FULL_RESOURCE_DESCRIPTOR); + driveParameters = (PCM_INT13_DRIVE_PARAMETER) buffer; + } + + numberOfDrives = length / sizeof(CM_INT13_DRIVE_PARAMETER); + + // + // Allocate our detect info list now that we know how many entries there + // are going to be. No other routine allocates detect info and this is + // done out of DriverEntry so we don't need to synchronize it's creation. + // + + length = sizeof(DISK_DETECT_INFO) * numberOfDrives; + DetectInfoList = ExAllocatePoolWithTag(PagedPool, + length, + DISK_TAG_UPDATE_GEOM); + + if(DetectInfoList == NULL) { + DebugPrint((1, "DiskSaveGeometryDetectInfo: Couldn't allocate %x bytes " + "for DetectInfoList\n", + length)); + + ExFreePool(keyData); + return STATUS_INSUFFICIENT_RESOURCES; + } + + DetectInfoCount = numberOfDrives; + + RtlZeroMemory(DetectInfoList, length); + + // + // Copy the information out of the key data and into the list we've + // allocated. + // + + for(i = 0; i < numberOfDrives; i++) { + DetectInfoList[i].DriveParameters = driveParameters[i]; + } + + ExFreePool(keyData); + return STATUS_SUCCESS; +} + + +VOID +DiskScanBusDetectInfo( + IN PDRIVER_OBJECT DriverObject, + IN HANDLE BusKey + ) +/*++ + +Routine Description: + + The routine queries the registry to determine which disks are visible to + the BIOS. If a disk is visable to the BIOS then the geometry information + is updated with the disk's signature and MBR checksum. + +Arguments: + + DriverObject - the object for this driver. + BusKey - handle to the bus key to be enumerated. + +Return Value: + + status + +--*/ +{ + ULONG busNumber; + + NTSTATUS status; + + for(busNumber = 0; ; busNumber++) { + + WCHAR buffer[32]; + UNICODE_STRING unicodeString; + + OBJECT_ATTRIBUTES objectAttributes; + + HANDLE spareKey; + HANDLE adapterKey; + + ULONG adapterNumber; + + DebugPrint((1, "DiskScanBusDetectInfo: Scanning bus %d\n", busNumber)); + + // + // Open controller name key. + // + + swprintf(buffer, L"%d", busNumber); + RtlInitUnicodeString(&unicodeString, buffer); + + InitializeObjectAttributes(&objectAttributes, + &unicodeString, + OBJ_CASE_INSENSITIVE, + BusKey, + NULL); + + status = ZwOpenKey(&spareKey, KEY_READ, &objectAttributes); + + if(!NT_SUCCESS(status)) { + DebugPrint((1, "DiskScanBusDetectInfo: Error %#08lx opening bus " + "key %#x\n", + status, busNumber)); + break; + } + + // + // Open up a controller ordinal key. + // + + RtlInitUnicodeString(&unicodeString, L"DiskController"); + InitializeObjectAttributes(&objectAttributes, + &unicodeString, + OBJ_CASE_INSENSITIVE, + spareKey, + NULL); + + status = ZwOpenKey(&adapterKey, KEY_READ, &objectAttributes); + ZwClose(spareKey); + + if(!NT_SUCCESS(status)) { + DebugPrint((1, "DiskScanBusDetectInfo: Error %#08lx opening " + "DiskController key\n", + status)); + continue; + } + + for(adapterNumber = 0; ; adapterNumber++) { + + HANDLE diskKey; + ULONG diskNumber; + + // + // Open disk key. + // + + DebugPrint((1, "DiskScanBusDetectInfo: Scanning disk key " + "%d\\DiskController\\%d\\DiskPeripheral\n", + busNumber, adapterNumber)); + + swprintf(buffer, L"%d\\DiskPeripheral", adapterNumber); + RtlInitUnicodeString(&unicodeString, buffer); + + InitializeObjectAttributes(&objectAttributes, + &unicodeString, + OBJ_CASE_INSENSITIVE, + adapterKey, + NULL); + + status = ZwOpenKey(&diskKey, KEY_READ, &objectAttributes); + + if(!NT_SUCCESS(status)) { + DebugPrint((1, "DiskScanBusDetectInfo: Error %#08lx opening " + "disk key\n", + status)); + break; + } + + for(diskNumber = 0; ; diskNumber++) { + + HANDLE targetKey; + + DebugPrint((1, "DiskScanBusDetectInfo: Scanning target key " + "%d\\DiskController\\%d\\DiskPeripheral\\%d\n", + busNumber, adapterNumber, diskNumber)); + + swprintf(buffer, L"%d", diskNumber); + RtlInitUnicodeString(&unicodeString, buffer); + + InitializeObjectAttributes(&objectAttributes, + &unicodeString, + OBJ_CASE_INSENSITIVE, + diskKey, + NULL); + + status = ZwOpenKey(&targetKey, KEY_READ, &objectAttributes); + + if(!NT_SUCCESS(status)) { + DebugPrint((1, "DiskScanBusDetectInfo: Error %#08lx " + "opening target key\n", + status)); + break; + } + + status = DiskSaveBusDetectInfo(DriverObject, + targetKey, + diskNumber); + + ZwClose(targetKey); + } + + ZwClose(diskKey); + } + ZwClose(adapterKey); + } + return; +} + + +NTSTATUS +DiskSaveBusDetectInfo( + IN PDRIVER_OBJECT DriverObject, + IN HANDLE TargetKey, + IN ULONG DiskNumber + ) +/*++ + +Routine Description: + + This routine will transfer the firmware/ntdetect reported information + in the specified target key into the appropriate entry in the + DetectInfoList. + +Arguments: + + DriverObject - the object for this driver. + + TargetKey - the key for the disk being saved. + + DiskNumber - the ordinal of the entry in the DiskPeripheral tree for this + entry + +Return Value: + + status + +--*/ +{ + PDISK_DETECT_INFO diskInfo; + + UNICODE_STRING unicodeString; + + PKEY_VALUE_FULL_INFORMATION keyData; + ULONG length; + + NTSTATUS status; + + PAGED_CODE(); + + diskInfo = &(DetectInfoList[DiskNumber]); + + if(diskInfo->Initialized) { + + ASSERT(FALSE); + DebugPrint((1, "DiskSaveBusDetectInfo: disk entry %#x already has a " + "signature of %#08lx and mbr checksum of %#08lx\n", + DiskNumber, + diskInfo->Signature, + diskInfo->MbrCheckSum)); + return STATUS_UNSUCCESSFUL; + } + + RtlInitUnicodeString(&unicodeString, L"Identifier"); + + keyData = ExAllocatePoolWithTag(PagedPool, + VALUE_BUFFER_SIZE, + DISK_TAG_UPDATE_GEOM); + + if(keyData == NULL) { + DebugPrint((1, "DiskSaveBusDetectInfo: Couldn't allocate space for " + "registry data\n")); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Get disk peripheral identifier. + // + + status = ZwQueryValueKey(TargetKey, + &unicodeString, + KeyValueFullInformation, + keyData, + VALUE_BUFFER_SIZE, + &length); + + if(!NT_SUCCESS(status)) { + DebugPrint((1, "DiskSaveBusDetectInfo: Error %#08lx getting " + "Identifier\n", + status)); + ExFreePool(keyData); + return status; + + } else if (keyData->DataLength < 9*sizeof(WCHAR)) { + + // + // the data is too short to use (we subtract 9 chars in normal path) + // + DebugPrint((1, "DiskSaveBusDetectInfo: Saved data was invalid, " + "not enough data in registry!\n")); + ExFreePool(keyData); + return STATUS_UNSUCCESSFUL; + + } else { + + UNICODE_STRING identifier; + ULONG value; + + // + // Complete unicode string. + // + + identifier.Buffer = (PWSTR) ((PUCHAR)keyData + keyData->DataOffset); + identifier.Length = (USHORT) keyData->DataLength; + identifier.MaximumLength = (USHORT) keyData->DataLength; + + // + // Get the first value out of the identifier - this will be the MBR + // checksum. + // + + status = RtlUnicodeStringToInteger(&identifier, 16, &value); + + if(!NT_SUCCESS(status)) { + DebugPrint((1, "DiskSaveBusDetectInfo: Error %#08lx converting " + "identifier %wZ into MBR xsum\n", + status, + &identifier)); + ExFreePool(keyData); + return status; + } + + diskInfo->MbrCheckSum = value; + + // + // Shift the string over to get the disk signature + // + + identifier.Buffer += 9; + identifier.Length -= 9 * sizeof(WCHAR); + identifier.MaximumLength -= 9 * sizeof(WCHAR); + + status = RtlUnicodeStringToInteger(&identifier, 16, &value); + + if(!NT_SUCCESS(status)) { + DebugPrint((1, "DiskSaveBusDetectInfo: Error %#08lx converting " + "identifier %wZ into disk signature\n", + status, + &identifier)); + ExFreePool(keyData); + value = 0; + } + + diskInfo->Signature = value; + } + + // + // Here is where we would save away the extended int13 data. + // + + // + // Mark this entry as initialized so we can make sure not to do it again. + // + + diskInfo->Initialized = TRUE; + + + return STATUS_SUCCESS; +} + + +DISK_GEOMETRY_SOURCE +DiskUpdateGeometry( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension + ) +/*++ + +Routine Description: + + This routine checks the DetectInfoList saved away during disk driver init + to see if any geometry information was reported for this drive. If the + geometry data exists (determined by matching non-zero signatures or + non-zero MBR checksums) then it will be saved in the RealGeometry member + of the disk data block. + + ClassReadDriveCapacity MUST be called after calling this routine to update + the cylinder count based on the size of the disk and the presence of any + disk management software. + +Arguments: + + DeviceExtension - Supplies a pointer to the device information for disk. + +Return Value: + + Inidicates whether the "RealGeometry" in the data block is now valid. + +--*/ + +{ + PDISK_DATA diskData = FdoExtension->CommonExtension.DriverData; + + ULONG i; + PDISK_DETECT_INFO diskInfo; + + BOOLEAN found = FALSE; + + NTSTATUS status; + + PAGED_CODE(); + + + ASSERT(FdoExtension->CommonExtension.IsFdo); + ASSERT((FdoExtension->DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) == 0); + + // + // If we've already set a non-default geometry for this drive then there's + // no need to try and update again. + // + + if(diskData->GeometrySource != DiskGeometryUnknown) { + return diskData->GeometrySource; + } + + // + // Scan through the saved detect info to see if we can find a match + // for this device. + // + + for(i = 0; i < DetectInfoCount; i++) { + + ASSERT(DetectInfoList != NULL); + + diskInfo = &(DetectInfoList[i]); + + if((diskData->Mbr.Signature != 0) && + (diskData->Mbr.Signature == diskInfo->Signature)) { + DebugPrint((1, "DiskUpdateGeometry: found match for signature " + "%#08lx\n", + diskData->Mbr.Signature)); + found = TRUE; + break; + } else if((diskData->Mbr.Signature == 0) && + (diskData->Mbr.MbrCheckSum != 0) && + (diskData->Mbr.MbrCheckSum == diskInfo->MbrCheckSum)) { + DebugPrint((1, "DiskUpdateGeometry: found match for xsum %#08lx\n", + diskData->Mbr.MbrCheckSum)); + found = TRUE; + break; + } + } + + if(found) { + + ULONG cylinders; + ULONG sectorsPerTrack; + ULONG tracksPerCylinder; + + ULONG sectors; + ULONG length; + + // + // Point to the array of drive parameters. + // + + cylinders = diskInfo->DriveParameters.MaxCylinders + 1; + sectorsPerTrack = diskInfo->DriveParameters.SectorsPerTrack; + tracksPerCylinder = diskInfo->DriveParameters.MaxHeads + 1; + + // + // Since the BIOS may not report the full drive, recalculate the drive + // size based on the volume size and the BIOS values for tracks per + // cylinder and sectors per track.. + // + + length = tracksPerCylinder * sectorsPerTrack; + + if (length == 0) { + + // + // The BIOS information is bogus. + // + + DebugPrint((1, "DiskUpdateGeometry: H (%d) or S(%d) is zero\n", + tracksPerCylinder, sectorsPerTrack)); + return FALSE; + } + + // + // since we are copying the structure RealGeometry here, we should + // really initialize all the fields, especially since a zero'd + // BytesPerSector field would cause a trap in xHalReadPartitionTable() + // + + diskData->RealGeometry = FdoExtension->DiskGeometry; + + // + // Save the geometry information away in the disk data block and + // set the bit indicating that we found a valid one. + // + + diskData->RealGeometry.SectorsPerTrack = sectorsPerTrack; + diskData->RealGeometry.TracksPerCylinder = tracksPerCylinder; + diskData->RealGeometry.Cylinders.QuadPart = (LONGLONG)cylinders; + + DebugPrint((1, "DiskUpdateGeometry: BIOS spt %#x, #heads %#x, " + "#cylinders %#x\n", + sectorsPerTrack, tracksPerCylinder, cylinders)); + + diskData->GeometrySource = DiskGeometryFromBios; + diskInfo->Device = FdoExtension->DeviceObject; + + } else { + + DebugPrint((1, "DiskUpdateGeometry: no match found for signature %#08lx\n", diskData->Mbr.Signature)); + } + + if(diskData->GeometrySource == DiskGeometryUnknown) { + + // + // We couldn't find a geometry from the BIOS. Check with the port + // driver and see if it can provide one. + // + + status = DiskGetPortGeometry(FdoExtension, &(diskData->RealGeometry)); + + if(NT_SUCCESS(status)) { + + // + // Check the geometry to make sure it's valid. + // + + if((diskData->RealGeometry.TracksPerCylinder * + diskData->RealGeometry.SectorsPerTrack) != 0) { + + diskData->GeometrySource = DiskGeometryFromPort; + DebugPrint((1, "DiskUpdateGeometry: using Port geometry for disk %#p\n", FdoExtension)); + + if (diskData->RealGeometry.BytesPerSector == 0) { + + DebugPrint((0, "DiskDriverReinit: Port driver failed to " + "set BytesPerSector in the RealGeometry\n")); + diskData->RealGeometry.BytesPerSector = + FdoExtension->DiskGeometry.BytesPerSector; + if (diskData->RealGeometry.BytesPerSector == 0) { + ASSERT(!"BytesPerSector is still zero!"); + } + + } + } + } + } + + // + // If we came up with a "real" geometry for this drive then set it in the + // device extension. + // + + if(diskData->GeometrySource != DiskGeometryUnknown) { + FdoExtension->DiskGeometry = diskData->RealGeometry; + + // + // Increment the count of used geometry entries. + // + + InterlockedIncrement(&DetectInfoUsedCount); + } + + return diskData->GeometrySource; +} + + +NTSTATUS +DiskUpdateRemovableGeometry ( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension + ) + +/*++ + +Routine Description: + + This routine updates the geometry of the disk. It will query the port + driver to see if it can provide any geometry info. If not it will use + the current head & sector count. + + Based on these values & the capacity of the drive as reported by + ClassReadDriveCapacity it will determine a new cylinder count for the + device. + +Arguments: + + Fdo - Supplies the functional device object whos size needs to be updated. + +Return Value: + + Returns the status of the opertion. + +--*/ +{ + PCOMMON_DEVICE_EXTENSION commonExtension = &(FdoExtension->CommonExtension); + PDISK_DATA diskData = commonExtension->DriverData; + PDISK_GEOMETRY geometry = &(diskData->RealGeometry); + + NTSTATUS status; + + PAGED_CODE(); + + ASSERT_FDO(commonExtension->DeviceObject); + if (FdoExtension->DeviceDescriptor) { + ASSERT(FdoExtension->DeviceDescriptor->RemovableMedia); + } + ASSERT(TEST_FLAG(FdoExtension->DeviceObject->Characteristics, + FILE_REMOVABLE_MEDIA)); + + // + // Attempt to determine the disk geometry. First we'll check with the + // port driver to see what it suggests for a value. + // + + status = DiskGetPortGeometry(FdoExtension, geometry); + + if(NT_SUCCESS(status) && + ((geometry->TracksPerCylinder * geometry->SectorsPerTrack) != 0)) { + + FdoExtension->DiskGeometry = (*geometry); + } + + return status; +} + + +NTSTATUS +DiskGetPortGeometry( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + OUT PDISK_GEOMETRY Geometry + ) +/*++ + +Routine Description: + + This routine will query the port driver for disk geometry. Some port + drivers (in particular IDEPORT) may be able to provide geometry for the + device. + +Arguments: + + FdoExtension - the device object for the disk. + + Geometry - a structure to save the geometry information into (if any is + available) + +Return Value: + + STATUS_SUCCESS if geometry information can be provided or + error status indicating why it can't. + +--*/ +{ + PCOMMON_DEVICE_EXTENSION commonExtension = &(FdoExtension->CommonExtension); + PIRP irp; + PIO_STACK_LOCATION irpStack; + KEVENT event; + + NTSTATUS status; + + PAGED_CODE(); + + // + // Build an irp to send IOCTL_DISK_GET_DRIVE_GEOMETRY to the lower driver. + // + + irp = IoAllocateIrp(commonExtension->LowerDeviceObject->StackSize, FALSE); + + if(irp == NULL) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + irpStack = IoGetNextIrpStackLocation(irp); + + irpStack->MajorFunction = IRP_MJ_DEVICE_CONTROL; + + irpStack->Parameters.DeviceIoControl.IoControlCode = + IOCTL_DISK_GET_DRIVE_GEOMETRY; + irpStack->Parameters.DeviceIoControl.OutputBufferLength = + sizeof(DISK_GEOMETRY); + + irp->AssociatedIrp.SystemBuffer = Geometry; + + KeInitializeEvent(&event, SynchronizationEvent, FALSE); + + IoSetCompletionRoutine(irp, + ClassSignalCompletion, + &event, + TRUE, + TRUE, + TRUE); + + status = IoCallDriver(commonExtension->LowerDeviceObject, irp); + KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); + + ASSERT((status == STATUS_PENDING) || (status == irp->IoStatus.Status)); + status = irp->IoStatus.Status; + + IoFreeIrp(irp); + + return status; +} + + + +NTSTATUS +DiskReadDriveCapacity( + IN PDEVICE_OBJECT Fdo + ) +/*++ + +Routine Description: + + This routine is used by disk.sys as a wrapper for the classpnp API + ClassReadDriveCapacity. It will perform some additional operations to + attempt to determine drive geometry before it calls the classpnp version + of the routine. + + For fixed disks this involves calling DiskUpdateGeometry which will check + various sources (the BIOS, the port driver) for geometry information. + +Arguments: + + Fdo - a pointer to the device object to be checked. + +Return Value: + + status of ClassReadDriveCapacity. + +--*/ + +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; + PDISK_DATA diskData = fdoExtension->CommonExtension.DriverData; + DISK_GEOMETRY_SOURCE diskGeometrySource = DiskGeometryUnknown; + NTSTATUS status; + + ASSERT_FDO(Fdo); + + if (TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)) { + DiskUpdateRemovableGeometry(fdoExtension); + } else { + diskGeometrySource = DiskUpdateGeometry(fdoExtension); + } + + status = ClassReadDriveCapacity(Fdo); + + return status; +} + + + +VOID +DiskDriverReinitialization( + IN PDRIVER_OBJECT DriverObject, + IN PVOID Nothing, + IN ULONG Count + ) +/*++ + +Routine Description: + + This routine will scan through the current list of disks and attempt to + match them to any remaining geometry information. This will only be done + on the first call to the routine. + + Note: This routine assumes that the system will not be adding or removing + devices during this phase of the init process. This is very likely + a bad assumption but it greatly simplifies the code. + +Arguments: + + DriverObject - a pointer to the object for the disk driver. + + Nothing - unused + + Count - an indication of how many times this routine has been called. + +Return Value: + + none + +--*/ + +{ + PDEVICE_OBJECT deviceObject; + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension; + PDISK_DATA diskData; + + ULONG unmatchedDiskCount; + PDEVICE_OBJECT unmatchedDisk = NULL; + + ULONG i; + PDISK_DETECT_INFO diskInfo = NULL; + + if(Count != 1) { + DebugPrint((1, "DiskDriverReinitialization: ignoring call %d\n", + Count)); + return; + } + + // + // Check to see how many entries in the detect info list have been matched. + // If there's only one remaining we'll see if we can find a disk to go with + // it. + // + + if(DetectInfoCount == 0) { + DebugPrint((1, "DiskDriverReinitialization: no detect info saved\n")); + return; + } + + if((DetectInfoCount - DetectInfoUsedCount) != 1) { + DebugPrint((1, "DiskDriverReinitialization: %d of %d geometry entries " + "used - will not attempt match\n")); + return; + } + + // + // Scan through the list of disks and see if any of them are missing + // geometry information. If there is only one such disk we'll try to + // match it to the unmatched geometry. + // + + + // + // ISSUE-2000/5/24-henrygab - figure out if there's a way to keep + // removals from happening while doing this. + // + + for(deviceObject = DriverObject->DeviceObject, unmatchedDiskCount = 0; + deviceObject != NULL; + deviceObject = deviceObject->NextDevice) { + + // + // Make sure this is a disk and not a partition. + // + + fdoExtension = deviceObject->DeviceExtension; + if(fdoExtension->CommonExtension.IsFdo == FALSE) { + DebugPrint((1, "DiskDriverReinit: DO %#p is not an FDO\n", + deviceObject)); + continue; + } + + // + // If the geometry for this one is already known then skip it. + // + + diskData = fdoExtension->CommonExtension.DriverData; + if(diskData->GeometrySource != DiskGeometryUnknown) { + DebugPrint((1, "DiskDriverReinit: FDO %#p has a geometry\n", + deviceObject)); + continue; + } + + DebugPrint((1, "DiskDriverReinit: FDO %#p has no geometry\n", + deviceObject)); + + // + // Mark this one as using the default. It's past the time when disk + // might blunder across the geometry info. If we set the geometry + // from the bios we'll reset this field down below. + // + + diskData->GeometrySource = DiskGeometryFromDefault; + + // + // As long as we've only got one unmatched disk we're fine. + // + + unmatchedDiskCount++; + if(unmatchedDiskCount > 1) { + ASSERT(unmatchedDisk != NULL); + DebugPrint((1, "DiskDriverReinit: FDO %#p also has no geometry\n", + unmatchedDisk)); + unmatchedDisk = NULL; + break; + } + + unmatchedDisk = deviceObject; + } + + // + // If there's more or less than one ungeometried disk then we can't do + // anything about the geometry. + // + + if(unmatchedDiskCount != 1) { + DebugPrint((1, "DiskDriverReinit: Unable to match geometry\n")); + return; + + } + + fdoExtension = unmatchedDisk->DeviceExtension; + diskData = fdoExtension->CommonExtension.DriverData; + + DebugPrint((1, "DiskDriverReinit: Found possible match\n")); + + // + // Find the geometry which wasn't assigned. + // + + for(i = 0; i < DetectInfoCount; i++) { + if(DetectInfoList[i].Device == NULL) { + diskInfo = &(DetectInfoList[i]); + break; + } + } + + ASSERT(diskInfo != NULL); + + { + // + // Save the geometry information away in the disk data block and + // set the bit indicating that we found a valid one. + // + + ULONG cylinders; + ULONG sectorsPerTrack; + ULONG tracksPerCylinder; + + ULONG sectors; + ULONG length; + + // + // Point to the array of drive parameters. + // + + cylinders = diskInfo->DriveParameters.MaxCylinders + 1; + sectorsPerTrack = diskInfo->DriveParameters.SectorsPerTrack; + tracksPerCylinder = diskInfo->DriveParameters.MaxHeads + 1; + + // + // Since the BIOS may not report the full drive, recalculate the drive + // size based on the volume size and the BIOS values for tracks per + // cylinder and sectors per track.. + // + + length = tracksPerCylinder * sectorsPerTrack; + + if (length == 0) { + + // + // The BIOS information is bogus. + // + + DebugPrint((1, "DiskDriverReinit: H (%d) or S(%d) is zero\n", + tracksPerCylinder, sectorsPerTrack)); + return; + } + + // + // since we are copying the structure RealGeometry here, we should + // really initialize all the fields, especially since a zero'd + // BytesPerSector field would cause a trap in xHalReadPartitionTable() + // + + diskData->RealGeometry = fdoExtension->DiskGeometry; + + // + // Save the geometry information away in the disk data block and + // set the bit indicating that we found a valid one. + // + + diskData->RealGeometry.SectorsPerTrack = sectorsPerTrack; + diskData->RealGeometry.TracksPerCylinder = tracksPerCylinder; + diskData->RealGeometry.Cylinders.QuadPart = (LONGLONG)cylinders; + + DebugPrint((1, "DiskDriverReinit: BIOS spt %#x, #heads %#x, " + "#cylinders %#x\n", + sectorsPerTrack, tracksPerCylinder, cylinders)); + + diskData->GeometrySource = DiskGeometryGuessedFromBios; + diskInfo->Device = unmatchedDisk; + + // + // Now copy the geometry over to the fdo extension and call + // classpnp to redetermine the disk size and cylinder count. + // + + fdoExtension->DiskGeometry = diskData->RealGeometry; + + // + // BUGBUG - why not call DiskReadDriveCapacity()? + // + + ClassReadDriveCapacity(unmatchedDisk); + + if (diskData->RealGeometry.BytesPerSector == 0) { + + // + // if the BytesPerSector field is set to zero for a disk + // listed in the bios, then the system will bugcheck in + // xHalReadPartitionTable(). assert here since it is + // easier to determine what is happening this way. + // + + ASSERT(!"RealGeometry not set to non-zero bps\n"); + } + } + + return; +} + + +NTSTATUS +DiskGetDetectInfo( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + OUT PDISK_DETECTION_INFO DetectInfo + ) +/*++ + +Routine Description: + + Get the Int13 information from the BIOS DetectInfoList. + +Arguments: + + FdoExtension - Supplies a pointer to the FDO extension that we want to + obtain the detect information for. + + DetectInfo - A buffer where the detect information will be copied to. + +Return Value: + + NTSTATUS code. + +--*/ +{ + ULONG i; + BOOLEAN found; + PDISK_DETECT_INFO diskInfo; + PDISK_DATA diskData = FdoExtension->CommonExtension.DriverData; + + PAGED_CODE (); + + ASSERT(FdoExtension->CommonExtension.IsFdo); + + // + // Fail for non-fixed drives. + // + + if (TEST_FLAG (FdoExtension->DeviceObject->Characteristics, FILE_REMOVABLE_MEDIA)) { + return STATUS_NOT_SUPPORTED; + } + + // + // There is no GPT detection info, so fail this. + // + + if (diskData->PartitionStyle == PARTITION_STYLE_GPT) { + return STATUS_NOT_SUPPORTED; + } + + for(i = 0; i < DetectInfoCount; i++) { + + + ASSERT(DetectInfoList != NULL); + + diskInfo = &(DetectInfoList[i]); + + if((diskData->Mbr.Signature != 0) && + (diskData->Mbr.Signature == diskInfo->Signature)) { + DebugPrint((1, "DiskGetDetectInfo: found match for signature " + "%#08lx\n", + diskData->Mbr.Signature)); + found = TRUE; + break; + } else if((diskData->Mbr.Signature == 0) && + (diskData->Mbr.MbrCheckSum != 0) && + (diskData->Mbr.MbrCheckSum == diskInfo->MbrCheckSum)) { + DebugPrint((1, "DiskGetDetectInfo: found match for xsum %#08lx\n", + diskData->Mbr.MbrCheckSum)); + found = TRUE; + break; + } + } + + if ( found ) { + DetectInfo->DetectionType = DetectInt13; + DetectInfo->Int13.DriveSelect = diskInfo->DriveParameters.DriveSelect; + DetectInfo->Int13.MaxCylinders = diskInfo->DriveParameters.MaxCylinders; + DetectInfo->Int13.SectorsPerTrack = diskInfo->DriveParameters.SectorsPerTrack; + DetectInfo->Int13.MaxHeads = diskInfo->DriveParameters.MaxHeads; + DetectInfo->Int13.NumberDrives = diskInfo->DriveParameters.NumberDrives; + RtlZeroMemory (&DetectInfo->ExInt13, sizeof (DetectInfo->ExInt13)); + } + + return (found ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL); +} + + +NTSTATUS +DiskReadSignature( + IN PDEVICE_OBJECT Fdo + ) + +/*++ + +Routine Description: + + Read the disks signature from the drive. The signature can be either + a MBR signature or a GPT/EFI signature. + + The low-level signature reading is done by IoReadDiskSignature(). + +Arguments: + + Fdo - Pointer to the FDO of a disk to read the signature for. + +Return Value: + + NTSTATUS code. + +--*/ + + +{ + NTSTATUS Status; + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; + PDISK_DATA diskData = fdoExtension->CommonExtension.DriverData; + DISK_SIGNATURE Signature; + + PAGED_CODE (); + + Status = IoReadDiskSignature (Fdo, + fdoExtension->DiskGeometry.BytesPerSector, + &Signature); + + if (!NT_SUCCESS (Status)) { + return Status; + } + + if (Signature.PartitionStyle == PARTITION_STYLE_GPT) { + diskData->PartitionStyle = PARTITION_STYLE_GPT; + diskData->Efi.DiskId = Signature.Gpt.DiskId; + } else if (Signature.PartitionStyle == PARTITION_STYLE_MBR) { + diskData->PartitionStyle = PARTITION_STYLE_MBR; + diskData->Mbr.Signature = Signature.Mbr.Signature; + diskData->Mbr.MbrCheckSum = Signature.Mbr.CheckSum; + } else { + ASSERT (FALSE); + Status = STATUS_UNSUCCESSFUL; + } + + return Status; +} + +#endif // defined(_X86_) + + diff --git a/drivers/storage/class/disk_new/part.c b/drivers/storage/class/disk_new/part.c new file mode 100644 index 00000000000..66e69435493 --- /dev/null +++ b/drivers/storage/class/disk_new/part.c @@ -0,0 +1,327 @@ +/*++ + +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" + +#define PtCache ClassDebugExternal1 + +#ifdef ALLOC_PRAGMA + +#pragma alloc_text(PAGE, DiskReadPartitionTableEx) +#pragma alloc_text(PAGE, DiskWritePartitionTableEx) +#pragma alloc_text(PAGE, DiskSetPartitionInformationEx) +#endif + +ULONG DiskBreakOnPtInval = FALSE; + +// +// By default, 64-bit systems can see GPT disks and 32-bit systems +// cannot. This will likely change in the future. +// + +#if defined(_WIN64) +ULONG DiskDisableGpt = FALSE; +#else +ULONG DiskDisableGpt = TRUE; +#endif + +NTSTATUS +DiskReadPartitionTableEx( + IN PFUNCTIONAL_DEVICE_EXTENSION Fdo, + IN BOOLEAN BypassCache, + OUT PDRIVE_LAYOUT_INFORMATION_EX* DriveLayout + ) +/*++ + +Routine Description: + + This routine will return the current layout information for the disk. + If the cached information is still valid then it will be returned, + otherwise the layout will be retreived from the kernel and cached for + future use. + + This routine must be called with the partitioning lock held. The + partition list which is returned is not guaranteed to remain valid + once the lock has been released. + +Arguments: + + Fdo - a pointer to the FDO for the disk. + + DriveLayout - a location to store a pointer to the drive layout information. + +Return Value: + + STATUS_SUCCESS if successful or an error status indicating what failed. + +--*/ + +{ + PDISK_DATA diskData = Fdo->CommonExtension.DriverData; + NTSTATUS status; + PDRIVE_LAYOUT_INFORMATION_EX layoutEx; + + layoutEx = NULL; + + if(BypassCache) { + diskData->CachedPartitionTableValid = FALSE; + DebugPrint((PtCache, "DiskRPTEx: cache bypassed and invalidated for " + "FDO %#p\n", Fdo)); + } + + // + // If the cached partition table is present then return a copy of it. + // + + if(diskData->CachedPartitionTableValid == TRUE) { + + ULONG partitionNumber; + PDRIVE_LAYOUT_INFORMATION_EX layout = diskData->CachedPartitionTable; + + // + // Clear the partition numbers from the list entries + // + + for(partitionNumber = 0; + partitionNumber < layout->PartitionCount; + partitionNumber++) { + layout->PartitionEntry[partitionNumber].PartitionNumber = 0; + } + + *DriveLayout = diskData->CachedPartitionTable; + + DebugPrint((PtCache, "DiskRPTEx: cached PT returned (%#p) for " + "FDO %#p\n", + *DriveLayout, Fdo)); + + return STATUS_SUCCESS; + } + + ASSERTMSG("DiskReadPartitionTableEx is not using cached partition table", + (DiskBreakOnPtInval == FALSE)); + + // + // If there's a cached partition table still around then free it. + // + + if(diskData->CachedPartitionTable) { + DebugPrint((PtCache, "DiskRPTEx: cached PT (%#p) freed for FDO %#p\n", + diskData->CachedPartitionTable, Fdo)); + + ExFreePool(diskData->CachedPartitionTable); + diskData->CachedPartitionTable = NULL; + } + + // + // By default, X86 disables recognition of GPT disks. Instead we + // return the protective MBR partition. Use IoReadPartitionTable + // to get this. + // + + status = IoReadPartitionTableEx(Fdo->DeviceObject, &layoutEx); + + if (DiskDisableGpt) { + PDRIVE_LAYOUT_INFORMATION layout; + + if (NT_SUCCESS (status) && + layoutEx->PartitionStyle == PARTITION_STYLE_GPT) { + + // + // ISSUE - 2000/29/08 - math: Remove from final product. + // Leave this debug print in for a while until everybody + // has had a chance to convert their GPT disks to MBR. + // + + DbgPrint ("DISK: Disk %p recognized as a GPT disk on a system without GPT support.\n" + " Disk will appear as RAW.\n", + Fdo->DeviceObject); + + ExFreePool (layoutEx); + status = IoReadPartitionTable(Fdo->DeviceObject, + Fdo->DiskGeometry.BytesPerSector, + FALSE, + &layout); + if (NT_SUCCESS (status)) { + layoutEx = DiskConvertLayoutToExtended(layout); + ExFreePool (layout); + } + } + } + + diskData->CachedPartitionTable = layoutEx; + + // + // If the routine fails make sure we don't have a stale partition table + // pointer. Otherwise indicate that the table is now valid. + // + + if(!NT_SUCCESS(status)) { + diskData->CachedPartitionTable = NULL; + } else { + diskData->CachedPartitionTableValid = TRUE; + } + + *DriveLayout = diskData->CachedPartitionTable; + + DebugPrint((PtCache, "DiskRPTEx: returning PT %#p for FDO %#p with status " + "%#08lx. PT is %scached\n", + *DriveLayout, + Fdo, + status, + (diskData->CachedPartitionTableValid ? "" : "not "))); + + + return status; +} + + +NTSTATUS +DiskWritePartitionTableEx( + IN PFUNCTIONAL_DEVICE_EXTENSION Fdo, + IN PDRIVE_LAYOUT_INFORMATION_EX DriveLayout + ) +/*++ + +Routine Description: + + This routine will invalidate the cached partition table. It will then + write the new drive layout to disk. + +Arguments: + + Fdo - the FDO for the disk getting the new partition table. + + DriveLayout - the new drive layout. + +Return Value: + + status + +--*/ +{ + PDISK_DATA diskData = Fdo->CommonExtension.DriverData; + + // + // Invalidate the cached partition table. Do not free it as it may be + // the very drive layout that was passed in to us. + // + + diskData->CachedPartitionTableValid = FALSE; + + DebugPrint((PtCache, "DiskWPTEx: Invalidating PT cache for FDO %#p\n", + Fdo)); + + if (DiskDisableGpt) { + if (DriveLayout->PartitionStyle == PARTITION_STYLE_GPT) { + return STATUS_NOT_SUPPORTED; + } + } + + return IoWritePartitionTableEx(Fdo->DeviceObject, DriveLayout); +} + +NTSTATUS +DiskSetPartitionInformationEx( + IN PFUNCTIONAL_DEVICE_EXTENSION Fdo, + IN ULONG PartitionNumber, + IN struct _SET_PARTITION_INFORMATION_EX* PartitionInfo + ) +{ + PDISK_DATA diskData = Fdo->CommonExtension.DriverData; + + diskData->CachedPartitionTableValid = FALSE; + DebugPrint((PtCache, "DiskSPIEx: Invalidating PT cache for FDO %#p\n", + Fdo)); + + if (DiskDisableGpt) { + if (PartitionInfo->PartitionStyle == PARTITION_STYLE_GPT) { + return STATUS_NOT_SUPPORTED; + } + } + + return IoSetPartitionInformationEx(Fdo->DeviceObject, + PartitionNumber, + PartitionInfo); +} + +NTSTATUS +DiskSetPartitionInformation( + IN PFUNCTIONAL_DEVICE_EXTENSION Fdo, + IN ULONG SectorSize, + IN ULONG PartitionNumber, + IN ULONG PartitionType + ) +{ + PDISK_DATA diskData = Fdo->CommonExtension.DriverData; + + diskData->CachedPartitionTableValid = FALSE; + DebugPrint((PtCache, "DiskSPI: Invalidating PT cache for FDO %#p\n", + Fdo)); + + return IoSetPartitionInformation(Fdo->DeviceObject, + SectorSize, + PartitionNumber, + PartitionType); +} + +BOOLEAN +DiskInvalidatePartitionTable( + IN PFUNCTIONAL_DEVICE_EXTENSION Fdo, + IN BOOLEAN PartitionLockHeld + ) +{ + PDISK_DATA diskData = Fdo->CommonExtension.DriverData; + BOOLEAN wasValid; + + wasValid = (BOOLEAN) (diskData->CachedPartitionTableValid ? TRUE : FALSE); + diskData->CachedPartitionTableValid = FALSE; + + DebugPrint((PtCache, "DiskIPT: Invalidating PT cache for FDO %#p\n", + Fdo)); + + if((PartitionLockHeld) && (diskData->CachedPartitionTable != NULL)) { + DebugPrint((PtCache, "DiskIPT: Freeing PT cache (%#p) for FDO %#p\n", + diskData->CachedPartitionTable, Fdo)); + ExFreePool(diskData->CachedPartitionTable); + diskData->CachedPartitionTable = NULL; + } + + return wasValid; +} + +NTSTATUS +DiskVerifyPartitionTable( + IN PFUNCTIONAL_DEVICE_EXTENSION Fdo, + IN BOOLEAN FixErrors + ) +{ + PDISK_DATA diskData = Fdo->CommonExtension.DriverData; + + if(FixErrors) { + diskData->CachedPartitionTableValid = FALSE; + DebugPrint((PtCache, "DiskWPTEx: Invalidating PT cache for FDO %#p\n", + Fdo)); + } + + return IoVerifyPartitionTable(Fdo->DeviceObject, FixErrors); +} + diff --git a/drivers/storage/class/disk_new/pnp.c b/drivers/storage/class/disk_new/pnp.c new file mode 100644 index 00000000000..7fadcc9809f --- /dev/null +++ b/drivers/storage/class/disk_new/pnp.c @@ -0,0 +1,1406 @@ +/*++ + +Copyright (C) Microsoft Corporation, 1991 - 1999 + +Module Name: + + pnp.c + +Abstract: + + SCSI disk class driver + +Environment: + + kernel mode only + +Notes: + +Revision History: + +--*/ + +#include "disk.h" + +extern PULONG InitSafeBootMode; + +#ifdef ALLOC_PRAGMA + +#pragma alloc_text(PAGE, DiskAddDevice) +#pragma alloc_text(PAGE, DiskInitFdo) +#pragma alloc_text(PAGE, DiskInitPdo) +#pragma alloc_text(PAGE, DiskStartFdo) +#pragma alloc_text(PAGE, DiskStartPdo) +#pragma alloc_text(PAGE, DiskQueryId) +#pragma alloc_text(PAGE, DiskGenerateDeviceName) +#pragma alloc_text(PAGE, DiskCreateSymbolicLinks) +#pragma alloc_text(PAGE, DiskDeleteSymbolicLinks) +#pragma alloc_text(PAGE, DiskRemoveDevice) + +#endif + + +NTSTATUS +DiskAddDevice( + IN PDRIVER_OBJECT DriverObject, + IN PDEVICE_OBJECT PhysicalDeviceObject + ) + +/*++ + +Routine Description: + + This routine gets a port drivers capabilities, obtains the + inquiry data, searches the SCSI bus for the port driver and creates + the device objects for the disks found. + +Arguments: + + DriverObject - Pointer to driver object created by system. + + Pdo - Device object use to send requests to port driver. + +Return Value: + + True is returned if one disk was found and successfully created. + +--*/ + +{ + ULONG rootPartitionMountable = FALSE; + + PCONFIGURATION_INFORMATION configurationInformation; + ULONG diskCount; + + NTSTATUS status; + + PAGED_CODE(); + + // + // See if we should be allowing file systems to mount on partition zero. + // + + TRY { + HANDLE deviceKey; + + UNICODE_STRING diskKeyName; + OBJECT_ATTRIBUTES objectAttributes; + HANDLE diskKey; + + RTL_QUERY_REGISTRY_TABLE queryTable[2]; + + status = IoOpenDeviceRegistryKey(PhysicalDeviceObject, + PLUGPLAY_REGKEY_DEVICE, + KEY_READ, + &deviceKey); + + if(!NT_SUCCESS(status)) { + DebugPrint((1, "DiskAddDevice: Error %#08lx opening device key " + "for pdo %#08lx\n", + status, PhysicalDeviceObject)); + LEAVE; + } + + RtlInitUnicodeString(&diskKeyName, L"Disk"); + InitializeObjectAttributes(&objectAttributes, + &diskKeyName, + OBJ_CASE_INSENSITIVE, + deviceKey, + NULL); + + status = ZwOpenKey(&diskKey, KEY_READ, &objectAttributes); + ZwClose(deviceKey); + + if(!NT_SUCCESS(status)) { + DebugPrint((1, "DiskAddDevice: Error %#08lx opening disk key " + "for pdo %#08lx device key %#x\n", + status, PhysicalDeviceObject, deviceKey)); + LEAVE; + } + + RtlZeroMemory(queryTable, sizeof(queryTable)); + + queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT; + queryTable[0].Name = L"RootPartitionMountable"; + queryTable[0].EntryContext = &(rootPartitionMountable); + + status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE, + diskKey, + queryTable, + NULL, + NULL); + + if(!NT_SUCCESS(status)) { + DebugPrint((1, "DiskAddDevice: Error %#08lx reading value from " + "disk key %#x for pdo %#08lx\n", + status, diskKey, PhysicalDeviceObject)); + } + + ZwClose(diskKey); + + } FINALLY { + + // + // Do nothing. + // + + if(!NT_SUCCESS(status)) { + DebugPrint((1, "DiskAddDevice: Will %sallow file system to mount on " + "partition zero of disk %#08lx\n", + (rootPartitionMountable ? "" : "not "), + PhysicalDeviceObject)); + } + } + + // + // Create device objects for disk + // + + diskCount = 0; + + status = DiskCreateFdo( + DriverObject, + PhysicalDeviceObject, + &diskCount, + (BOOLEAN) !rootPartitionMountable + ); + + // + // Get the number of disks already initialized. + // + + configurationInformation = IoGetConfigurationInformation(); + + if (NT_SUCCESS(status)) { + + // + // Increment system disk device count. + // + + configurationInformation->DiskCount++; + + } + + return status; + +} // end DiskAddDevice() + + + +NTSTATUS +NTAPI +DiskInitFdo( + IN PDEVICE_OBJECT Fdo + ) + +/*++ + +Routine Description: + + This routine is called to do one-time initialization of new device objects + + +Arguments: + + Fdo - a pointer to the functional device object for this device + +Return Value: + + status + +--*/ + +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; + + PDISK_DATA diskData = (PDISK_DATA) fdoExtension->CommonExtension.DriverData; + + ULONG srbFlags = 0; + + ULONG timeOut = 0; + + ULONG bytesPerSector; + UCHAR sectorShift; + + BOOLEAN dmActive = FALSE; + PULONG dmSkew; + ULONG dmByteSkew; + + NTSTATUS status; + + PAGED_CODE(); + + // + // Build the lookaside list for srb's for the physical disk. Should only + // need a couple. If this fails then we don't have an emergency SRB so + // fail the call to initialize. + // + + ClassInitializeSrbLookasideList((PCOMMON_DEVICE_EXTENSION) fdoExtension, + PARTITION0_LIST_SIZE); + + // + // Because all requests share a common sense buffer, it is possible + // for the buffer to be overwritten if the port driver completes + // multiple failed requests that require a request sense before the + // class driver's completion routine can consume the data in the buffer. + // To prevent this, we allow the port driver to allocate a unique sense + // buffer each time it needs one. We are responsible for freeing this + // buffer. This also allows the adapter to be configured to support + // additional sense data beyond the minimum 18 bytes. + // + + fdoExtension->SrbFlags = SRB_FLAGS_PORT_DRIVER_ALLOCSENSE; + + // + // Initialize the srb flags. + // + + if (fdoExtension->DeviceDescriptor->CommandQueueing && + fdoExtension->AdapterDescriptor->CommandQueueing) { + + fdoExtension->SrbFlags = SRB_FLAGS_QUEUE_ACTION_ENABLE; + + } + + if (!TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)) { + SET_FLAG(fdoExtension->DeviceFlags, DEV_SAFE_START_UNIT); + } + + // + // Look for controllers that require special flags. + // + + ClassScanForSpecial(fdoExtension, DiskBadControllers, DiskSetSpecialHacks); + + // + // Look into the registry to see if this device + // requires special attention - [ like a hack ] + // + + DiskScanRegistryForSpecial(fdoExtension); + + srbFlags = fdoExtension->SrbFlags; + + // + // Clear buffer for drive geometry. + // + + RtlZeroMemory(&(fdoExtension->DiskGeometry), + sizeof(DISK_GEOMETRY)); + + // + // Allocate request sense buffer. + // + + fdoExtension->SenseData = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, + SENSE_BUFFER_SIZE, + DISK_TAG_START); + + if (fdoExtension->SenseData == NULL) { + + // + // The buffer can not be allocated. + // + + DebugPrint((1, "DiskInitFdo: Can not allocate request sense buffer\n")); + + status = STATUS_INSUFFICIENT_RESOURCES; + return status; + } + + // + // Physical device object will describe the entire + // device, starting at byte offset 0. + // + + fdoExtension->CommonExtension.StartingOffset.QuadPart = (LONGLONG)(0); + + // + // Set timeout value in seconds. + // + + timeOut = ClassQueryTimeOutRegistryValue(Fdo); + if (timeOut) { + fdoExtension->TimeOutValue = timeOut; + } else { + fdoExtension->TimeOutValue = SCSI_DISK_TIMEOUT; + } + + // + // If this is a removable drive, build an entry in devicemap\scsi + // indicating it's physicaldriveN name, set up the appropriate + // update partitions routine and set the flags correctly. + // note: only do this after the timeout value is set, above. + // + + if (fdoExtension->DeviceDescriptor->RemovableMedia) { + ClassUpdateInformationInRegistry( Fdo, + "PhysicalDrive", + fdoExtension->DeviceNumber, + NULL, + 0); + // + // Enable media change notification for removable disks + // + ClassInitializeMediaChangeDetection(fdoExtension, + "Disk"); + + SET_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA); + diskData->UpdatePartitionRoutine = DiskUpdateRemovablePartitions; + + } else { + + SET_FLAG(fdoExtension->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE); + diskData->UpdatePartitionRoutine = DiskUpdatePartitions; + + } + + // + // Read the drive capacity. Don't use the disk version of the routine here + // since we don't know the disk signature yet - the disk version will + // attempt to determine the BIOS reported geometry. + // + + status = ClassReadDriveCapacity(Fdo); + + // + // If the read capcity failed then just return, unless this is a + // removable disk where a device object partition needs to be created. + // + + if (!NT_SUCCESS(status) && + !(Fdo->Characteristics & FILE_REMOVABLE_MEDIA)) { + + DebugPrint((1, + "DiskInitFdo: Can't read capacity for device %p\n", + Fdo)); + + if (fdoExtension->DeviceDescriptor->RemovableMedia) { + fdoExtension->DiskGeometry.MediaType = RemovableMedia; + Fdo->Flags &= ~DO_VERIFY_VOLUME; + } else { + fdoExtension->DiskGeometry.MediaType = FixedMedia; + } + + status = STATUS_SUCCESS; + } + + // + // Set up sector size fields. + // + // Stack variables will be used to update + // the partition device extensions. + // + // The device extension field SectorShift is + // used to calculate sectors in I/O transfers. + // + // The DiskGeometry structure is used to service + // IOCTls used by the format utility. + // + + bytesPerSector = fdoExtension->DiskGeometry.BytesPerSector; + + // + // Make sure sector size is not zero. + // + + if (bytesPerSector == 0) { + + // + // Default sector size for disk is 512. + // + + bytesPerSector = fdoExtension->DiskGeometry.BytesPerSector = 512; + } + + sectorShift = fdoExtension->SectorShift; + + // + // Determine is DM Driver is loaded on an IDE drive that is + // under control of Atapi - this could be either a crashdump or + // an Atapi device is sharing the controller with an IDE disk. + // + + HalExamineMBR(fdoExtension->CommonExtension.DeviceObject, + fdoExtension->DiskGeometry.BytesPerSector, + (ULONG)0x54, + &dmSkew); + + if (dmSkew) { + + // + // Update the device extension, so that the call to IoReadPartitionTable + // will get the correct information. Any I/O to this disk will have + // to be skewed by *dmSkew sectors aka DMByteSkew. + // + + fdoExtension->DMSkew = *dmSkew; + fdoExtension->DMActive = TRUE; + fdoExtension->DMByteSkew = fdoExtension->DMSkew * bytesPerSector; + + // + // Save away the infomation that we need, since this deviceExtension will soon be + // blown away. + // + + dmActive = TRUE; + dmByteSkew = fdoExtension->DMByteSkew; + + } + +#if defined(_X86_) + // + // Try to read the signature off the disk and determine the correct drive + // geometry based on that. This requires rereading the disk size to get + // the cylinder count updated correctly. + // + + if(fdoExtension->DeviceDescriptor->RemovableMedia == FALSE) { + DiskReadSignature(Fdo); + DiskReadDriveCapacity(Fdo); + } +#endif + + // + // Register interfaces for this device + // + { + UNICODE_STRING interfaceName; + + RtlInitUnicodeString(&interfaceName, NULL); + + status = IoRegisterDeviceInterface(fdoExtension->LowerPdo, + (LPGUID) &DiskClassGuid, + NULL, + &interfaceName); + + if(NT_SUCCESS(status)) { + + diskData->DiskInterfaceString = interfaceName; + status = IoSetDeviceInterfaceState(&interfaceName, TRUE); + + } else { + interfaceName.Buffer = NULL; + } + + if(!NT_SUCCESS(status)) { + + DebugPrint((1, "DiskInitFdo: Unable to register or set disk DCA " + "for fdo %p [%lx]\n", Fdo, status)); + + RtlFreeUnicodeString(&interfaceName); + RtlInitUnicodeString(&(diskData->DiskInterfaceString), NULL); + } + } + + DiskCreateSymbolicLinks(Fdo); + + // + // Determine the type of disk and enable failure preiction in the hardware + // and enable failure prediction polling. + // + + if (*InitSafeBootMode == 0) + { + DiskDetectFailurePrediction(fdoExtension, + &diskData->FailurePredictionCapability); + + if (diskData->FailurePredictionCapability != FailurePredictionNone) + { + // + // Cool, we've got some sort of failure prediction, enable it + // at the hardware and then enable polling for it + // + + // + // By default we allow performance to be degradeded if failure + // prediction is enabled. + // + // TODO: Make a registry entry ? + // + + diskData->AllowFPPerfHit = TRUE; + + // + // Enable polling only after Atapi and SBP2 add support for the new + // SRB flag that indicates that the request should not reset the + // drive spin down idle timer. + // + + status = DiskEnableDisableFailurePredictPolling(fdoExtension, + TRUE, + DISK_DEFAULT_FAILURE_POLLING_PERIOD); + + DebugPrint((3, "DiskInitFdo: Failure Prediction Poll enabled as " + "%d for device %p\n", + diskData->FailurePredictionCapability, + Fdo)); + } + } else { + + // + // In safe boot mode we do not enable failure prediction, as perhaps + // it is the reason why normal boot does not work + // + + diskData->FailurePredictionCapability = FailurePredictionNone; + + } + + // + // Initialize the verify mutex + // + + KeInitializeMutex(&diskData->VerifyMutex, MAX_SECTORS_PER_VERIFY); + + return(STATUS_SUCCESS); + +} // end DiskInitFdo() + + +NTSTATUS +DiskInitPdo( + IN PDEVICE_OBJECT Pdo + ) + +/*++ + +Routine Description: + + This routine will create the well known names for a PDO and register + it's device interfaces. + +--*/ + +{ + PCOMMON_DEVICE_EXTENSION pdoExtension = Pdo->DeviceExtension; + PDISK_DATA diskData = pdoExtension->DriverData; + + UNICODE_STRING interfaceName; + + NTSTATUS status; + + PAGED_CODE(); + + DiskCreateSymbolicLinks(Pdo); + + // + // Register interfaces for this device + // + + RtlInitUnicodeString(&interfaceName, NULL); + + status = IoRegisterDeviceInterface(Pdo, + (LPGUID) &PartitionClassGuid, + NULL, + &interfaceName); + + if(NT_SUCCESS(status)) { + + diskData->PartitionInterfaceString = interfaceName; + status = IoSetDeviceInterfaceState(&interfaceName, TRUE); + + } else { + interfaceName.Buffer = NULL; + } + + if(!NT_SUCCESS(status)) { + DebugPrint((1, "DiskInitPdo: Unable to register partition DCA for " + "pdo %p [%lx]\n", Pdo, status)); + + RtlFreeUnicodeString(&interfaceName); + RtlInitUnicodeString(&(diskData->PartitionInterfaceString), NULL); + } + + return STATUS_SUCCESS; +} + + +NTSTATUS +DiskStartPdo( + IN PDEVICE_OBJECT Pdo + ) + +/*++ + +Routine Description: + + This routine will create the well known names for a PDO and register + it's device interfaces. + +--*/ + +{ + PAGED_CODE(); + + return STATUS_SUCCESS; +} + +NTSTATUS +DiskStopDevice( + IN PDEVICE_OBJECT DeviceObject, + IN UCHAR Type + ) + +{ + PFUNCTIONAL_DEVICE_EXTENSION fdo = DeviceObject->DeviceExtension; + + if(fdo->CommonExtension.IsFdo) { + DiskAcquirePartitioningLock(fdo); + DiskInvalidatePartitionTable(fdo, TRUE); + DiskReleasePartitioningLock(fdo); + } + + return STATUS_SUCCESS; +} + + +NTSTATUS +NTAPI +DiskQueryId( + IN PDEVICE_OBJECT Pdo, + IN BUS_QUERY_ID_TYPE IdType, + IN PUNICODE_STRING UnicodeIdString + ) + +/*++ + +Routine Description: + + This routine generates the PNP id's for the disk's "children". If the + specified ID isn't one that the routine can generate it must return + STATUS_NOT_IMPLEMENTED so classpnp will know not to do anything with the + PNP request's status. + + This routine allocates the buffer for the UnicodeIdString. It is the + caller's responsibility to free the buffer when it's done. + +Arguments: + + Pdo - a pointer to the PDO we are to generate an ID for + + IdType - the type of ID to be generated + + UnicodeIdString - a string to put the results into. + +Return Value: + + STATUS_SUCCCESS if successful + + STATUS_NOT_IMPLEMENTED if the IdType is not one supported by this routine + + error status otherwise. + +--*/ + +{ + ANSI_STRING ansiIdString; + + NTSTATUS status; + + PAGED_CODE(); + ASSERT_PDO(Pdo); + + if(IdType == BusQueryDeviceID) { + + if((Pdo->Characteristics & FILE_REMOVABLE_MEDIA) == 0) { + RtlInitAnsiString(&ansiIdString, "STORAGE\\Partition"); + return RtlAnsiStringToUnicodeString(UnicodeIdString, &ansiIdString, TRUE); + } + + RtlInitAnsiString(&ansiIdString, + "STORAGE\\RemovableMedia"); + + return RtlAnsiStringToUnicodeString(UnicodeIdString, &ansiIdString, TRUE); + } + + if(IdType == BusQueryInstanceID) { + + PPHYSICAL_DEVICE_EXTENSION pdoExtension = Pdo->DeviceExtension; + PCOMMON_DEVICE_EXTENSION commonExtension = Pdo->DeviceExtension; + PDISK_DATA diskData = commonExtension->PartitionZeroExtension->CommonExtension.DriverData; + + UCHAR string[64]; + + if((Pdo->Characteristics & FILE_REMOVABLE_MEDIA) == 0) { + + if (diskData->PartitionStyle == PARTITION_STYLE_MBR) { + sprintf(string, "S%08lx_O%I64lx_L%I64lx", + diskData->Mbr.Signature, + commonExtension->StartingOffset, + commonExtension->PartitionLength); + } else { + sprintf(string, + "S%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02xS_O%I64lx_L%I64lx", + diskData->Efi.DiskId.Data1, + diskData->Efi.DiskId.Data2, + diskData->Efi.DiskId.Data3, + diskData->Efi.DiskId.Data4[0], + diskData->Efi.DiskId.Data4[1], + diskData->Efi.DiskId.Data4[2], + diskData->Efi.DiskId.Data4[3], + diskData->Efi.DiskId.Data4[4], + diskData->Efi.DiskId.Data4[5], + diskData->Efi.DiskId.Data4[6], + diskData->Efi.DiskId.Data4[7], + commonExtension->StartingOffset, + commonExtension->PartitionLength); + } + } else { + sprintf(string, "RM"); + } + + RtlInitAnsiString(&ansiIdString, string); + + return RtlAnsiStringToUnicodeString(UnicodeIdString, &ansiIdString, TRUE); + } + + if((IdType == BusQueryHardwareIDs) || (IdType == BusQueryCompatibleIDs)) { + + RtlInitAnsiString(&ansiIdString, "STORAGE\\Volume"); + + UnicodeIdString->MaximumLength = (USHORT) RtlAnsiStringToUnicodeSize(&ansiIdString) + sizeof(UNICODE_NULL); + + UnicodeIdString->Buffer = ExAllocatePoolWithTag(PagedPool, + UnicodeIdString->MaximumLength, + DISK_TAG_PNP_ID); + + if(UnicodeIdString->Buffer == NULL) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlZeroMemory(UnicodeIdString->Buffer, UnicodeIdString->MaximumLength); + + return RtlAnsiStringToUnicodeString(UnicodeIdString, + &ansiIdString, + FALSE); + } + + return STATUS_NOT_IMPLEMENTED; +} + + +NTSTATUS +DiskGenerateDeviceName( + IN BOOLEAN IsFdo, + IN ULONG DeviceNumber, + IN OPTIONAL ULONG PartitionNumber, + IN OPTIONAL PLARGE_INTEGER StartingOffset, + IN OPTIONAL PLARGE_INTEGER PartitionLength, + OUT PUCHAR *RawName + ) + +/*++ + +Routine Description: + + This routine will allocate a unicode string buffer and then fill it in + with a generated name for the specified device object. + + It is the responsibility of the user to allocate a UNICODE_STRING structure + to pass in and to free UnicodeName->Buffer when done with it. + +Arguments: + + DeviceObject - a pointer to the device object + + UnicodeName - a unicode string to put the name buffer into + +Return Value: + + status + +--*/ + +//#define PDO_NAME_FORMAT "\\Device\\Harddisk%d\\DP(%d)%d" +#define PDO_NAME_FORMAT "\\Device\\Harddisk%d\\DP(%d)%#I64x-%#I64x+%lx" +#define FDO_NAME_FORMAT "\\Device\\Harddisk%d\\DR%d" + +//#define PDO_NAME_FORMAT (PDO_BASE_NAME "+%#I64x+%#I64x+%#lx") + +{ + UCHAR rawName[64]; + static ULONG diskDeviceSequenceNumber = 0; + + PAGED_CODE(); + + if(!IsFdo) { + + ASSERT(ARGUMENT_PRESENT((PVOID)(ULONG_PTR) PartitionNumber)); + ASSERT(ARGUMENT_PRESENT(PartitionLength)); + ASSERT(ARGUMENT_PRESENT(StartingOffset)); + + sprintf(rawName, PDO_NAME_FORMAT, DeviceNumber, PartitionNumber, + StartingOffset->QuadPart, + PartitionLength->QuadPart, + diskDeviceSequenceNumber++); + } else { + + ASSERT(!ARGUMENT_PRESENT((PVOID)(ULONG_PTR) PartitionNumber)); + ASSERT(!ARGUMENT_PRESENT(PartitionLength)); + ASSERT(!ARGUMENT_PRESENT(StartingOffset)); + + sprintf(rawName, FDO_NAME_FORMAT, DeviceNumber, + diskDeviceSequenceNumber++); + + } + + *RawName = ExAllocatePoolWithTag(PagedPool, + strlen(rawName) + 1, + DISK_TAG_NAME); + + if(*RawName == NULL) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + strcpy(*RawName, rawName); + + DebugPrint((2, "DiskGenerateDeviceName: generated \"%s\"\n", rawName)); + + return STATUS_SUCCESS; +} + + +VOID +DiskCreateSymbolicLinks( + IN PDEVICE_OBJECT DeviceObject + ) + +/*++ + +Routine Description: + + This routine will generate a symbolic link for the specified device object + using the well known form \\Device\HarddiskX\PartitionY, where X and Y are + filled in using the partition information in the device object's extension. + + This routine will not try to delete any previous symbolic link for the + same generated name - the caller must make sure the symbolic link has + been broken before calling this routine. + +Arguments: + + DeviceObject - the device object to make a well known name for + +Return Value: + + STATUS + +--*/ + +{ + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + + PDISK_DATA diskData = commonExtension->DriverData; + + WCHAR wideSourceName[64]; + UNICODE_STRING unicodeSourceName; + + NTSTATUS status; + + PAGED_CODE(); + + // + // Build the destination for the link first using the device name + // stored in the device object + // + + ASSERT(commonExtension->DeviceName.Buffer); + + if(!diskData->LinkStatus.WellKnownNameCreated) { + // + // Put together the source name using the partition and device number + // in the device extension and disk data segment + // + + swprintf(wideSourceName, L"\\Device\\Harddisk%d\\Partition%d", + commonExtension->PartitionZeroExtension->DeviceNumber, + (commonExtension->IsFdo ? + 0 : + commonExtension->PartitionNumber)); + + RtlInitUnicodeString(&unicodeSourceName, wideSourceName); + + DebugPrint((1, "DiskCreateSymbolicLink: Linking %wZ to %wZ\n", + &unicodeSourceName, + &commonExtension->DeviceName)); + + status = IoCreateSymbolicLink(&unicodeSourceName, + &commonExtension->DeviceName); + + #if DBG + + if((status == STATUS_OBJECT_NAME_EXISTS) || + (status == STATUS_OBJECT_NAME_COLLISION)) { + + DebugPrint((1, "DiskCreateSymbolicLink: name %wZ already exists\n", + &unicodeSourceName)); + } + #endif + + if(NT_SUCCESS(status)){ + diskData->LinkStatus.WellKnownNameCreated = TRUE; + } + } + + if((!diskData->LinkStatus.PhysicalDriveLinkCreated) && + (commonExtension->IsFdo)) { + + // + // Create a physical drive N link using the device number we saved + // away during AddDevice. + // + + swprintf(wideSourceName, + L"\\DosDevices\\PhysicalDrive%d", + commonExtension->PartitionZeroExtension->DeviceNumber); + + RtlInitUnicodeString(&unicodeSourceName, wideSourceName); + + DebugPrint((1, "DiskCreateSymbolicLink: Linking %wZ to %wZ\n", + &unicodeSourceName, + &(commonExtension->DeviceName))); + + status = IoCreateSymbolicLink(&unicodeSourceName, + &(commonExtension->DeviceName)); + +#if DBG + + if((status == STATUS_OBJECT_NAME_EXISTS) || + (status == STATUS_OBJECT_NAME_COLLISION)) { + + DebugPrint((1, "DiskCreateSymbolicLink: name %wZ already exists\n", + &unicodeSourceName)); + } +#endif + + if(NT_SUCCESS(status)) { + diskData->LinkStatus.PhysicalDriveLinkCreated = TRUE; + } + } else if(commonExtension->IsFdo == FALSE) { + diskData->LinkStatus.PhysicalDriveLinkCreated = FALSE; + } + + return; +} + + +VOID +DiskDeleteSymbolicLinks( + IN PDEVICE_OBJECT DeviceObject + ) + +/*++ + +Routine Description: + + This routine will delete the well known name (symlink) for the specified + device. It generates the link name using information stored in the + device extension + +Arguments: + + DeviceObject - the device object we are unlinking + +Return Value: + + status + +--*/ + +{ + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + PDISK_DATA diskData = commonExtension->DriverData; + + WCHAR wideLinkName[64]; + UNICODE_STRING unicodeLinkName; + + PAGED_CODE(); + + if(diskData->LinkStatus.WellKnownNameCreated) { + + swprintf(wideLinkName, + L"\\Device\\Harddisk%d\\Partition%d", + commonExtension->PartitionZeroExtension->DeviceNumber, + (commonExtension->IsFdo ? 0 : + commonExtension->PartitionNumber)); + + RtlInitUnicodeString(&unicodeLinkName, wideLinkName); + + IoDeleteSymbolicLink(&unicodeLinkName); + + diskData->LinkStatus.WellKnownNameCreated = FALSE; + } + + if(diskData->LinkStatus.PhysicalDriveLinkCreated) { + + ASSERT_FDO(DeviceObject); + + swprintf(wideLinkName, + L"\\DosDevices\\PhysicalDrive%d", + commonExtension->PartitionZeroExtension->DeviceNumber); + + RtlInitUnicodeString(&unicodeLinkName, wideLinkName); + + IoDeleteSymbolicLink(&unicodeLinkName); + + diskData->LinkStatus.PhysicalDriveLinkCreated = FALSE; + } + + return; +} + + +NTSTATUS +DiskRemoveDevice( + IN PDEVICE_OBJECT DeviceObject, + IN UCHAR Type + ) + +/*++ + +Routine Description: + + This routine will release any resources the device may have allocated for + this device object and return. + +Arguments: + + DeviceObject - the device object being removed + +Return Value: + + status + +--*/ + +{ + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + PDISK_DATA diskData = commonExtension->DriverData; + + PAGED_CODE(); + + // + // Handle query and cancel + // + + if((Type == IRP_MN_QUERY_REMOVE_DEVICE) || + (Type == IRP_MN_CANCEL_REMOVE_DEVICE)) { + return STATUS_SUCCESS; + } + + if(commonExtension->IsFdo) { + + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = + DeviceObject->DeviceExtension; + + // + // Purge the cached partition table (if any). + // + + DiskAcquirePartitioningLock(fdoExtension); + DiskInvalidatePartitionTable(fdoExtension, TRUE); + DiskReleasePartitioningLock(fdoExtension); + + // + // Delete our object directory. + // + + if(fdoExtension->AdapterDescriptor) { + ExFreePool(fdoExtension->AdapterDescriptor); + fdoExtension->AdapterDescriptor = NULL; + } + + if(fdoExtension->DeviceDescriptor) { + ExFreePool(fdoExtension->DeviceDescriptor); + fdoExtension->DeviceDescriptor = NULL; + } + + if(fdoExtension->SenseData) { + ExFreePool(fdoExtension->SenseData); + fdoExtension->SenseData = NULL; + } + + if(fdoExtension->DeviceDirectory != NULL) { + ZwMakeTemporaryObject(fdoExtension->DeviceDirectory); + ZwClose(fdoExtension->DeviceDirectory); + fdoExtension->DeviceDirectory = NULL; + } + + if(Type == IRP_MN_REMOVE_DEVICE) { + IoGetConfigurationInformation()->DiskCount--; + } + + } else { + + PPHYSICAL_DEVICE_EXTENSION pdoExtension = DeviceObject->DeviceExtension; + + } + + DiskDeleteSymbolicLinks(DeviceObject); + + // + // Release the mounted device interface if we've set it. + // + + if(diskData->PartitionInterfaceString.Buffer != NULL) { + IoSetDeviceInterfaceState(&(diskData->PartitionInterfaceString), FALSE); + RtlFreeUnicodeString(&(diskData->PartitionInterfaceString)); + RtlInitUnicodeString(&(diskData->PartitionInterfaceString), NULL); + } + if(diskData->DiskInterfaceString.Buffer != NULL) { + IoSetDeviceInterfaceState(&(diskData->DiskInterfaceString), FALSE); + RtlFreeUnicodeString(&(diskData->DiskInterfaceString)); + RtlInitUnicodeString(&(diskData->DiskInterfaceString), NULL); + } + + ClassDeleteSrbLookasideList(commonExtension); + return STATUS_SUCCESS; +} + + +NTSTATUS +NTAPI +DiskStartFdo( + IN PDEVICE_OBJECT Fdo + ) + +/*++ + +Routine Description: + + This routine will query the underlying device for any information necessary + to complete initialization of the device. This will include physical + disk geometry, mode sense information and such. + + This routine does not perform partition enumeration - that is left to the + re-enumeration routine + + If this routine fails it will return an error value. It does not clean up + any resources - that is left for the Stop/Remove routine. + +Arguments: + + Fdo - a pointer to the functional device object for this device + +Return Value: + + status + +--*/ + +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; + PCOMMON_DEVICE_EXTENSION commonExtension = &(fdoExtension->CommonExtension); + PDISK_DATA diskData = commonExtension->DriverData; + STORAGE_HOTPLUG_INFO hotplugInfo; + ULONG writeCacheOverride = DiskWriteCacheDefault; + DISK_CACHE_INFORMATION cacheInfo; + NTSTATUS status; + + PAGED_CODE(); + + // + // Get the hotplug information, so we can turn off write cache if needed + // + // NOTE: Capabilities info is not good enough to determine hotplugedness + // as we cannot determine device relations information and other + // dependencies. Get the hotplug info instead + // + + { + PIRP irp; + KEVENT event; + IO_STATUS_BLOCK statusBlock; + + KeInitializeEvent(&event, SynchronizationEvent, FALSE); + RtlZeroMemory(&hotplugInfo, sizeof(STORAGE_HOTPLUG_INFO)); + + irp = IoBuildDeviceIoControlRequest(IOCTL_STORAGE_GET_HOTPLUG_INFO, + Fdo, + NULL, + 0L, + &hotplugInfo, + sizeof(STORAGE_HOTPLUG_INFO), + FALSE, + &event, + &statusBlock); + + if (irp != NULL) { + + // send to self -- classpnp handles this + status = IoCallDriver(Fdo, irp); + if (status == STATUS_PENDING) { + KeWaitForSingleObject(&event, + Executive, + KernelMode, + FALSE, + NULL); + status = statusBlock.Status; + } + } + } + + // + // Clear the DEV_WRITE_CACHE flag now and set + // it below only if we read that from the disk + // + + CLEAR_FLAG(fdoExtension->DeviceFlags, DEV_WRITE_CACHE); + + if (TEST_FLAG(fdoExtension->ScanForSpecialFlags, CLASS_SPECIAL_DISABLE_WRITE_CACHE)) + { + // + // This flag overrides the user's setting, because faulty firmware + // may cause the filesystem to refuse to format media on this device + // + DebugPrint((1, + "DiskStartFdo: Shutting off write cache for %p due to %s\n", + Fdo, + "Possible Firmware Issue")); + + writeCacheOverride = DiskWriteCacheDisable; + } + else + { + // + // Look into the registry to see if the user + // has chosen to override the default setting + // + ClassGetDeviceParameter(fdoExtension, + DiskDeviceParameterSubkey, + DiskDeviceUserWriteCacheSetting, + &writeCacheOverride); + + if (writeCacheOverride == DiskWriteCacheDefault) + { + // + // The user has not overridden the default settings + // + if (hotplugInfo.DeviceHotplug && !hotplugInfo.WriteCacheEnableOverride) + { + DebugPrint((1, + "DiskStartFdo: Shutting off write cache for %p due to %s\n", + Fdo, + "Hotplug Device")); + + writeCacheOverride = DiskWriteCacheDisable; + } + else if (hotplugInfo.MediaHotplug) + { + DebugPrint((1, + "DiskStartFdo: Shutting off write cache for %p due to %s\n", + Fdo, + "Hotplug (unlockable) Media")); + + writeCacheOverride = DiskWriteCacheDisable; + } + else + { + // + // We enable write cache if this device has no specific issues + // + writeCacheOverride = DiskWriteCacheEnable; + } + } + } + + // + // Query the disk to see if write cache is enabled + // and set the DEV_WRITE_CACHE flag appropriately + // + + RtlZeroMemory(&cacheInfo, sizeof(DISK_CACHE_INFORMATION)); + + status = DiskGetCacheInformation(fdoExtension, &cacheInfo); + + if (NT_SUCCESS(status)) + { + if (cacheInfo.WriteCacheEnabled == TRUE) + { + if (writeCacheOverride == DiskWriteCacheDisable) + { + // + // Write cache is currently enabled on this + // device, but we would like to turn it off + // + cacheInfo.WriteCacheEnabled = FALSE; + + status = DiskSetCacheInformation(fdoExtension, &cacheInfo); + } + else + { + // + // The write cache setting either matches + // our needs or we don't care + // + SET_FLAG(fdoExtension->DeviceFlags, DEV_WRITE_CACHE); + } + } + else + { + if (writeCacheOverride == DiskWriteCacheEnable) + { + // + // Write cache is currently disabled on this + // device, but we would like to turn it on + // + cacheInfo.WriteCacheEnabled = TRUE; + + status = DiskSetCacheInformation(fdoExtension, &cacheInfo); + + SET_FLAG(fdoExtension->DeviceFlags, DEV_WRITE_CACHE); + } + } + } + + // + // In the event that there's a cached partition table flush it now. + // + + DiskAcquirePartitioningLock(fdoExtension); + DiskInvalidatePartitionTable(fdoExtension, TRUE); + DiskReleasePartitioningLock(fdoExtension); + + // + // Get the SCSI address if it's available for use with SMART ioctls. + // + + { + PIRP irp; + KEVENT event; + IO_STATUS_BLOCK statusBlock; + + KeInitializeEvent(&event, SynchronizationEvent, FALSE); + + irp = IoBuildDeviceIoControlRequest(IOCTL_SCSI_GET_ADDRESS, + commonExtension->LowerDeviceObject, + NULL, + 0L, + &(diskData->ScsiAddress), + sizeof(SCSI_ADDRESS), + FALSE, + &event, + &statusBlock); + + if(irp != NULL) { + + + status = IoCallDriver(commonExtension->LowerDeviceObject, irp); + + if(status == STATUS_PENDING) { + KeWaitForSingleObject(&event, + Executive, + KernelMode, + FALSE, + NULL); + status = statusBlock.Status; + } + } + } + + return STATUS_SUCCESS; + +} // end DiskStartFdo() +