https://git.reactos.org/?p=reactos.git;a=commitdiff;h=f144b8c10d4971f91fc3b…
commit f144b8c10d4971f91fc3bcdb964d6a0e57fbbd6c
Author: Victor Perevertkin <victor.perevertkin(a)reactos.org>
AuthorDate: Sun Aug 30 03:43:14 2020 +0300
Commit: Victor Perevertkin <victor.perevertkin(a)reactos.org>
CommitDate: Sun Aug 30 03:43:14 2020 +0300
[DISK_NEW] Import Microsoft Disk class driver from GitHub
The source code is licensed under MS-PL license, taken from Windows Driver Samples
repository
(
https://github.com/microsoft/Windows-driver-samples/tree/master/storage/cla…)
Synched with commit 3428c5feaac7c1a5e2a09db0ed93392f7568f9e6
The driver is written for Windows 8+, so we compile it with ntoskrnl_vista
statically linked and with NTDDI_WIN8 defined
CORE-17129
---
drivers/storage/class/disk_new/CMakeLists.txt | 32 +
drivers/storage/class/disk_new/data.c | 88 +
drivers/storage/class/disk_new/disk.c | 6195 +++++++++++++++++++++++++
drivers/storage/class/disk_new/disk.h | 1288 +++++
drivers/storage/class/disk_new/disk.inf | 129 +
drivers/storage/class/disk_new/disk.rc | 23 +
drivers/storage/class/disk_new/disk_reg.inf | 7 +
drivers/storage/class/disk_new/diskwmi.c | 3510 ++++++++++++++
drivers/storage/class/disk_new/geometry.c | 1676 +++++++
drivers/storage/class/disk_new/license.txt | 23 +
drivers/storage/class/disk_new/pnp.c | 1108 +++++
11 files changed, 14079 insertions(+)
diff --git a/drivers/storage/class/disk_new/CMakeLists.txt
b/drivers/storage/class/disk_new/CMakeLists.txt
new file mode 100644
index 00000000000..8c11bc30a72
--- /dev/null
+++ b/drivers/storage/class/disk_new/CMakeLists.txt
@@ -0,0 +1,32 @@
+
+remove_definitions(-D_WIN32_WINNT=0x502)
+
+list(APPEND SOURCE
+ data.c
+ disk.c
+ diskwmi.c
+ geometry.c
+ pnp.c
+ disk.h)
+
+add_library(disk MODULE ${SOURCE} disk.rc)
+
+target_compile_definitions(disk PUBLIC
+ DEBUG_USE_KDPRINT
+ _WIN32_WINNT=0x602
+ NTDDI_VERSION=0x06020000) # NTDDI_WIN8
+
+if(USE_CLANG_CL OR (NOT MSVC))
+ target_compile_options(disk PRIVATE -Wno-format -Wno-pointer-sign)
+endif()
+
+if(GCC)
+ target_compile_options(disk PRIVATE -Wno-unused-but-set-variable
-Wno-pointer-to-int-cast -Wno-switch)
+endif()
+
+set_module_type(disk kernelmodedriver)
+target_link_libraries(disk ntoskrnl_vista libcntpr wdmguid)
+add_importlibs(disk classpnp ntoskrnl hal)
+add_cd_file(TARGET disk DESTINATION reactos/system32/drivers NO_CAB FOR all)
+add_registry_inf(disk_reg.inf)
+add_driver_inf(disk disk.inf)
diff --git a/drivers/storage/class/disk_new/data.c
b/drivers/storage/class/disk_new/data.c
new file mode 100644
index 00000000000..97214055bb1
--- /dev/null
+++ b/drivers/storage/class/disk_new/data.c
@@ -0,0 +1,88 @@
+/*++
+
+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 }
+};
+
+// ======== ROS DIFF ========
+// Added MediaTypes in their own brace nesting level
+// ======== ROS DIFF ========
+
+DISK_MEDIA_TYPES_LIST const DiskMediaTypesExclude[] = {
+ { "HP" , "RDX" , NULL, 0, 0, {0 ,
0 , 0 , 0 }},
+ { NULL , NULL , NULL, 0, 0, {0 , 0 , 0 , 0
}}
+};
+
+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 }},
+ { "SONY" , "SMO-F561" , NULL, 2, 2, {MO_5_WO ,
MO_5_RW, 0 , 0 }},
+ { "Maxoptix", "T5-2600" , NULL, 2, 2, {MO_5_WO ,
MO_5_RW, 0 , 0 }},
+ { "Maxoptix", "T6-5200" , 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..29259da6dba
--- /dev/null
+++ b/drivers/storage/class/disk_new/disk.c
@@ -0,0 +1,6195 @@
+/*++
+
+Copyright (C) Microsoft Corporation, 1991 - 2010
+
+Module Name:
+
+ disk.c
+
+Abstract:
+
+ SCSI disk class driver
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+Revision History:
+
+--*/
+
+#define DEBUG_MAIN_SOURCE 1
+#include "disk.h"
+
+
+//
+// Now instantiate the GUIDs
+//
+
+#include "initguid.h"
+#include "ntddstor.h"
+#include "ntddvol.h"
+#include "ioevent.h"
+
+#ifdef DEBUG_USE_WPP
+#include "disk.tmh"
+#endif
+
+#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, DiskSetSpecialHacks)
+#pragma alloc_text(PAGE, DiskGetCacheInformation)
+#pragma alloc_text(PAGE, DiskSetCacheInformation)
+#pragma alloc_text(PAGE, DiskLogCacheInformation)
+#pragma alloc_text(PAGE, DiskSetInfoExceptionInformation)
+#pragma alloc_text(PAGE, DiskGetInfoExceptionInformation)
+#pragma alloc_text(PAGE, DiskIoctlGetCacheSetting)
+#pragma alloc_text(PAGE, DiskIoctlSetCacheSetting)
+#pragma alloc_text(PAGE, DiskIoctlGetLengthInfo)
+#pragma alloc_text(PAGE, DiskIoctlGetDriveGeometry)
+#pragma alloc_text(PAGE, DiskIoctlGetDriveGeometryEx)
+#pragma alloc_text(PAGE, DiskIoctlGetCacheInformation)
+#pragma alloc_text(PAGE, DiskIoctlSetCacheInformation)
+#pragma alloc_text(PAGE, DiskIoctlGetMediaTypesEx)
+#pragma alloc_text(PAGE, DiskIoctlPredictFailure)
+#pragma alloc_text(PAGE, DiskIoctlEnableFailurePrediction)
+#pragma alloc_text(PAGE, DiskIoctlReassignBlocks)
+#pragma alloc_text(PAGE, DiskIoctlReassignBlocksEx)
+#pragma alloc_text(PAGE, DiskIoctlIsWritable)
+#pragma alloc_text(PAGE, DiskIoctlUpdateDriveSize)
+#pragma alloc_text(PAGE, DiskIoctlGetVolumeDiskExtents)
+#pragma alloc_text(PAGE, DiskIoctlSmartGetVersion)
+#pragma alloc_text(PAGE, DiskIoctlSmartReceiveDriveData)
+#pragma alloc_text(PAGE, DiskIoctlSmartSendDriveCommand)
+#pragma alloc_text(PAGE, DiskIoctlVerifyThread)
+
+#endif
+
+//
+// ETW related globals
+//
+BOOLEAN DiskETWEnabled = FALSE;
+
+BOOLEAN DiskIsPastReinit = FALSE;
+
+const GUID GUID_NULL = { 0 };
+#define DiskCompareGuid(_First,_Second) \
+ (memcmp ((_First),(_Second), sizeof (GUID)))
+
+//
+// This macro is used to work around a bug in the definition of
+// DISK_CACHE_RETENTION_PRIORITY. The value KeepReadData should be
+// assigned 0xf rather than 0x2. Since the interface was already published
+// when this was discovered the disk driver has been modified to translate
+// between the interface value and the correct scsi value.
+//
+// 0x2 is turned into 0xf
+// 0xf is turned into 0x2 - this ensures that future SCSI defintions can be
+// accomodated.
+//
+
+#define TRANSLATE_RETENTION_PRIORITY(_x)\
+ ((_x) == 0xf ? 0x2 : \
+ ((_x) == 0x2 ? 0xf : _x) \
+ )
+
+#define IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS_ADMIN CTL_CODE(IOCTL_VOLUME_BASE, 0,
METHOD_BUFFERED, FILE_READ_ACCESS)
+
+VOID
+NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
+DiskDriverReinit(
+ IN PDRIVER_OBJECT DriverObject,
+ IN PVOID Nothing,
+ IN ULONG Count
+ )
+{
+ UNREFERENCED_PARAMETER(DriverObject);
+ UNREFERENCED_PARAMETER(Nothing);
+ UNREFERENCED_PARAMETER(Count);
+
+ DiskIsPastReinit = TRUE;
+}
+
+VOID
+NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
+DiskBootDriverReinit(
+ IN PDRIVER_OBJECT DriverObject,
+ IN PVOID Nothing,
+ IN ULONG Count
+ )
+{
+ IoRegisterDriverReinitialization(DriverObject, DiskDriverReinit, NULL);
+
+#if defined(_X86_) || defined(_AMD64_)
+
+ DiskDriverReinitialization(DriverObject, Nothing, Count);
+
+#else
+
+ UNREFERENCED_PARAMETER(Nothing);
+ UNREFERENCED_PARAMETER(Count);
+
+#endif
+
+}
+
+NTSTATUS
+NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
+DriverEntry(
+ IN PDRIVER_OBJECT DriverObject,
+ IN PUNICODE_STRING RegistryPath
+ )
+
+/*++
+
+Routine Description:
+
+ This routine initializes the SCSI hard disk class driver.
+
+Arguments:
+
+ DriverObject - Pointer to driver object created by system.
+
+ RegistryPath - Pointer to the name of the services node for this driver.
+
+Return Value:
+
+ The function value is the final status from the initialization operation.
+
+--*/
+
+{
+ CLASS_INIT_DATA InitializationData = { 0 };
+ CLASS_QUERY_WMI_REGINFO_EX_LIST classQueryWmiRegInfoExList = { 0 };
+ GUID guidQueryRegInfoEx = GUID_CLASSPNP_QUERY_REGINFOEX;
+ GUID guidSrbSupport = GUID_CLASSPNP_SRB_SUPPORT;
+ ULONG srbSupport;
+
+ NTSTATUS status;
+
+ //
+ // Initializes tracing
+ //
+ WPP_INIT_TRACING(DriverObject, RegistryPath);
+
+#if defined(_X86_) || defined(_AMD64_)
+
+ //
+ // Read the information NtDetect squirreled away about the disks in this
+ // system.
+ //
+
+ DiskSaveDetectInfo(DriverObject);
+
+#endif
+
+ InitializationData.InitializationDataSize = sizeof(CLASS_INIT_DATA);
+
+ //
+ // Setup sizes and entry points for functional device objects
+ //
+
+ InitializationData.FdoData.DeviceExtensionSize = FUNCTIONAL_EXTENSION_SIZE;
+ InitializationData.FdoData.DeviceType = FILE_DEVICE_DISK;
+ InitializationData.FdoData.DeviceCharacteristics = FILE_DEVICE_SECURE_OPEN;
+
+ InitializationData.FdoData.ClassInitDevice = DiskInitFdo;
+ InitializationData.FdoData.ClassStartDevice = DiskStartFdo;
+ InitializationData.FdoData.ClassStopDevice = DiskStopDevice;
+ InitializationData.FdoData.ClassRemoveDevice = DiskRemoveDevice;
+ InitializationData.FdoData.ClassPowerDevice = ClassSpinDownPowerHandler;
+
+ InitializationData.FdoData.ClassError = DiskFdoProcessError;
+ InitializationData.FdoData.ClassReadWriteVerification = DiskReadWriteVerification;
+ InitializationData.FdoData.ClassDeviceControl = DiskDeviceControl;
+ InitializationData.FdoData.ClassShutdownFlush = DiskShutdownFlush;
+ InitializationData.FdoData.ClassCreateClose = NULL;
+
+
+ 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;
+
+ InitializationData.ClassAddDevice = DiskAddDevice;
+ InitializationData.ClassUnload = DiskUnload;
+
+ //
+ // Initialize regregistration data structures
+ //
+
+ DiskInitializeReregistration();
+
+ //
+ // Call the class init routine
+ //
+
+ status = ClassInitialize(DriverObject, RegistryPath, &InitializationData);
+
+ if (NT_SUCCESS(status)) {
+
+ IoRegisterBootDriverReinitialization(DriverObject,
+ DiskBootDriverReinit,
+ NULL);
+ }
+
+ //
+ // Call class init Ex routine to register a
+ // PCLASS_QUERY_WMI_REGINFO_EX routine
+ //
+
+ classQueryWmiRegInfoExList.Size = sizeof(CLASS_QUERY_WMI_REGINFO_EX_LIST);
+ classQueryWmiRegInfoExList.ClassFdoQueryWmiRegInfoEx = DiskFdoQueryWmiRegInfoEx;
+
+ (VOID)ClassInitializeEx(DriverObject,
+ &guidQueryRegInfoEx,
+ &classQueryWmiRegInfoExList);
+
+ //
+ // Call class init Ex routine to register SRB support
+ //
+ srbSupport = CLASS_SRB_SCSI_REQUEST_BLOCK | CLASS_SRB_STORAGE_REQUEST_BLOCK;
+ if (!NT_SUCCESS(ClassInitializeEx(DriverObject,
+ &guidSrbSupport,
+ &srbSupport))) {
+ //
+ // Should not fail
+ //
+ NT_ASSERT(FALSE);
+ }
+
+
+ return status;
+
+} // end DriverEntry()
+
+
+VOID
+NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
+DiskUnload(
+ IN PDRIVER_OBJECT DriverObject
+ )
+{
+ PAGED_CODE();
+
+#if defined(_X86_) || defined(_AMD64_)
+ DiskCleanupDetectInfo(DriverObject);
+#else
+ // NB: Need to use UNREFERENCED_PARAMETER to prevent build error
+ // DriverObject is not referenced below in WPP_CLEANUP.
+ // WPP_CLEANUP is currently an "NOOP" marco.
+ UNREFERENCED_PARAMETER(DriverObject);
+#endif
+
+
+ //
+ // Cleans up tracing
+ //
+ WPP_CLEANUP(DriverObject);
+
+ return;
+}
+
+
+NTSTATUS
+DiskCreateFdo(
+ IN PDRIVER_OBJECT DriverObject,
+ IN PDEVICE_OBJECT PhysicalDeviceObject,
+ IN PULONG DeviceCount,
+ IN BOOLEAN DasdAccessOnly
+ )
+
+/*++
+
+Routine Description:
+
+ This routine creates an object for the functional device
+
+Arguments:
+
+ DriverObject - Pointer to driver object created by system.
+
+ PhysicalDeviceObject - Lower level driver we should attach to
+
+ DeviceCount - Number of previously installed devices.
+
+ DasdAccessOnly - indicates whether or not a file system is allowed to mount
+ on this device object. Used to avoid double-mounting of
+ file systems on super-floppies (which can unfortunately be
+ fixed disks). If set the i/o system will only allow rawfs
+ to be mounted.
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+
+{
+ PCCHAR deviceName = NULL;
+ HANDLE handle = NULL;
+ PDEVICE_OBJECT lowerDevice = NULL;
+ PDEVICE_OBJECT deviceObject = NULL;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ *DeviceCount = 0;
+
+ //
+ // Set up an object directory to contain the objects for this
+ // device and all its partitions.
+ //
+
+ do {
+
+ WCHAR dirBuffer[64] = { 0 };
+ UNICODE_STRING dirName;
+ OBJECT_ATTRIBUTES objectAttribs;
+
+ status = RtlStringCchPrintfW(dirBuffer, sizeof(dirBuffer) / sizeof(dirBuffer[0])
- 1, L"\\Device\\Harddisk%d", *DeviceCount);
+ if (!NT_SUCCESS(status)) {
+ TracePrint((TRACE_LEVEL_FATAL, TRACE_FLAG_PNP, "DiskCreateFdo: Format
symbolic link failed with error: 0x%X\n", status));
+ return status;
+ }
+
+ RtlInitUnicodeString(&dirName, dirBuffer);
+
+ InitializeObjectAttributes(&objectAttribs,
+ &dirName,
+ OBJ_CASE_INSENSITIVE | OBJ_PERMANENT |
OBJ_KERNEL_HANDLE,
+ NULL,
+ NULL);
+
+ status = ZwCreateDirectoryObject(&handle,
+ DIRECTORY_ALL_ACCESS,
+ &objectAttribs);
+
+ (*DeviceCount)++;
+
+ } while((status == STATUS_OBJECT_NAME_COLLISION) ||
+ (status == STATUS_OBJECT_NAME_EXISTS));
+
+ if (!NT_SUCCESS(status)) {
+
+ TracePrint((TRACE_LEVEL_FATAL, TRACE_FLAG_PNP, "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(*DeviceCount, &deviceName);
+
+ if(!NT_SUCCESS(status)) {
+ TracePrint((TRACE_LEVEL_FATAL, TRACE_FLAG_PNP, "DiskCreateFdo - couldn't
create name %lx\n", status));
+
+ goto DiskCreateFdoExit;
+
+ }
+
+ status = ClassCreateDeviceObject(DriverObject,
+ deviceName,
+ PhysicalDeviceObject,
+ TRUE,
+ &deviceObject);
+
+ if (!NT_SUCCESS(status)) {
+ TracePrint((TRACE_LEVEL_FATAL, TRACE_FLAG_PNP, "DiskCreateFdo: Can not
create device object %s\n", deviceName));
+ goto DiskCreateFdoExit;
+ }
+
+ FREE_POOL(deviceName);
+
+ //
+ // 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.
+ //
+
+#ifdef _MSC_VER
+#pragma prefast(suppress:28175);
+#endif
+ 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;
+ }
+
+ //
+ // Clear the init flag.
+ //
+
+ CLEAR_FLAG(deviceObject->Flags, DO_DEVICE_INITIALIZING);
+
+ //
+ // Store a handle to the device object directory for this disk
+ //
+
+ fdoExtension->DeviceDirectory = handle;
+
+ ObDereferenceObject(lowerDevice);
+
+ return STATUS_SUCCESS;
+
+DiskCreateFdoExit:
+
+ if (deviceObject != NULL)
+ {
+ IoDeleteDevice(deviceObject);
+ }
+
+ FREE_POOL(deviceName);
+
+ ObDereferenceObject(lowerDevice);
+
+ ZwMakeTemporaryObject(handle);
+ ZwClose(handle);
+
+ return status;
+}
+
+
+NTSTATUS
+NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
+DiskReadWriteVerification(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ I/O System entry for read and write requests to SCSI disks.
+
+Arguments:
+
+ DeviceObject - Pointer to driver object created by system.
+ Irp - IRP involved.
+
+Return Value:
+
+ NT Status
+
+--*/
+
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
+ ULONG residualBytes;
+ ULONG residualOffset;
+ NTSTATUS status = STATUS_SUCCESS;
+
+ //
+ // Make sure that the request is within the bounds of the partition,
+ // the number of bytes to transfer and the byte offset are a
+ // multiple of the sector size.
+ //
+
+ residualBytes = irpSp->Parameters.Read.Length &
(commonExtension->PartitionZeroExtension->DiskGeometry.BytesPerSector - 1);
+ residualOffset = irpSp->Parameters.Read.ByteOffset.LowPart &
(commonExtension->PartitionZeroExtension->DiskGeometry.BytesPerSector - 1);
+
+ if ((irpSp->Parameters.Read.ByteOffset.QuadPart >
commonExtension->PartitionLength.QuadPart) ||
+ (irpSp->Parameters.Read.ByteOffset.QuadPart < 0) ||
+ (residualBytes != 0) ||
+ (residualOffset != 0))
+ {
+ NT_ASSERT(residualOffset == 0);
+ status = STATUS_INVALID_PARAMETER;
+ }
+ else
+ {
+ ULONGLONG bytesRemaining = commonExtension->PartitionLength.QuadPart -
irpSp->Parameters.Read.ByteOffset.QuadPart;
+
+ if ((ULONGLONG)irpSp->Parameters.Read.Length > bytesRemaining)
+ {
+ status = STATUS_INVALID_PARAMETER;
+ }
+ }
+
+ if (!NT_SUCCESS(status))
+ {
+ //
+ // This error may be caused by the fact that the drive is not ready.
+ //
+
+ status = ((PDISK_DATA) commonExtension->DriverData)->ReadyStatus;
+
+ if (!NT_SUCCESS(status)) {
+
+ //
+ // Flag this as a user error so that a popup is generated.
+ //
+
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_RW,
"DiskReadWriteVerification: ReadyStatus is %lx\n", status));
+
+ if (IoIsErrorUserInduced(status) && Irp->Tail.Overlay.Thread !=
NULL) {
+ IoSetHardErrorOrVerifyDevice(Irp, DeviceObject);
+ }
+
+ //
+ // status will keep the current error
+ //
+
+ } else if ((residualBytes == 0) && (residualOffset == 0)) {
+
+ //
+ // This failed because we think the physical disk is too small.
+ // Send it down to the drive and let the hardware decide for
+ // itself.
+ //
+
+ status = STATUS_SUCCESS;
+
+ } else {
+
+ //
+ // Note fastfat depends on this parameter to determine when to
+ // remount due to a sector size change.
+ //
+
+ status = STATUS_INVALID_PARAMETER;
+ }
+ }
+
+ Irp->IoStatus.Status = status;
+
+ return status;
+
+} // end DiskReadWrite()
+
+
+NTSTATUS
+DiskDetermineMediaTypes(
+ IN PDEVICE_OBJECT Fdo,
+ IN PIRP Irp,
+ IN UCHAR MediumType,
+ IN UCHAR DensityCode,
+ IN BOOLEAN MediaPresent,
+ IN BOOLEAN IsWritable
+ )
+
+/*++
+
+Routine Description:
+
+ Determines number of types based on the physical device, validates the user buffer
+ and builds the MEDIA_TYPE information.
+
+Arguments:
+
+ DeviceObject - Pointer to functional device object created by system.
+ Irp - IOCTL_STORAGE_GET_MEDIA_TYPES_EX Irp.
+ MediumType - byte returned in mode data header.
+ DensityCode - byte returned in mode data block descriptor.
+ NumberOfTypes - pointer to be updated based on actual device.
+
+Return Value:
+
+ Status is returned.
+
+--*/
+
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
+ 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
+ //
+
+ NT_ASSERT(irpStack->Parameters.DeviceIoControl.OutputBufferLength >=
+ sizeof(GET_MEDIA_TYPES));
+
+ //
+ // Determine if this device is removable or fixed.
+ //
+
+ if (!TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)) {
+
+ //
+ // Fixed disk.
+ //
+
+ mediaTypes->DeviceType = FILE_DEVICE_DISK;
+ mediaTypes->MediaInfoCount = 1;
+
+ mediaInfo->DeviceSpecific.DiskInfo.Cylinders.QuadPart =
fdoExtension->DiskGeometry.Cylinders.QuadPart;
+ mediaInfo->DeviceSpecific.DiskInfo.MediaType = FixedMedia;
+ mediaInfo->DeviceSpecific.DiskInfo.TracksPerCylinder =
fdoExtension->DiskGeometry.TracksPerCylinder;
+ mediaInfo->DeviceSpecific.DiskInfo.SectorsPerTrack =
fdoExtension->DiskGeometry.SectorsPerTrack;
+ mediaInfo->DeviceSpecific.DiskInfo.BytesPerSector =
fdoExtension->DiskGeometry.BytesPerSector;
+ mediaInfo->DeviceSpecific.DiskInfo.NumberMediaSides = 1;
+ mediaInfo->DeviceSpecific.DiskInfo.MediaCharacteristics =
(MEDIA_CURRENTLY_MOUNTED | MEDIA_READ_WRITE);
+
+ if (!IsWritable) {
+
+ SET_FLAG(mediaInfo->DeviceSpecific.DiskInfo.MediaCharacteristics,
+ MEDIA_WRITE_PROTECTED);
+ }
+
+ } else {
+
+ PCCHAR vendorId = (PCCHAR) fdoExtension->DeviceDescriptor +
fdoExtension->DeviceDescriptor->VendorIdOffset;
+ PCCHAR productId = (PCCHAR) fdoExtension->DeviceDescriptor +
fdoExtension->DeviceDescriptor->ProductIdOffset;
+ PCCHAR productRevision = (PCCHAR) fdoExtension->DeviceDescriptor +
fdoExtension->DeviceDescriptor->ProductRevisionOffset;
+ DISK_MEDIA_TYPES_LIST const *mediaListEntry;
+ ULONG currentMedia;
+ ULONG i;
+ ULONG j;
+ ULONG sizeNeeded;
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+ "DiskDetermineMediaTypes: Vendor %s, Product %s\n",
+ vendorId,
+ productId));
+
+
+ //
+ // If there's an entry with such vendorId & ProductId in the
DiskMediaTypesExclude list,
+ // this device shouldn't be looked up in the DiskMediaTypes list to determine
a medium type.
+ // The exclude table allows to narrow down the set of devices described by the
DiskMediaTypes
+ // list (e.g.: DiskMediaTypes says "all HP devices" and
DiskMediaTypesExlclude says
+ // "except for HP RDX")
+ //
+
+ for (i = 0; DiskMediaTypesExclude[i].VendorId != NULL; i++) {
+ mediaListEntry = &DiskMediaTypesExclude[i];
+
+ if
(strncmp(mediaListEntry->VendorId,vendorId,strlen(mediaListEntry->VendorId))) {
+ continue;
+ }
+
+ if ((mediaListEntry->ProductId != NULL) &&
+ strncmp(mediaListEntry->ProductId, productId,
strlen(mediaListEntry->ProductId))) {
+ continue;
+ }
+
+ goto SkipTable;
+ }
+
+ //
+ // Run through the list until we find the entry with a NULL Vendor Id.
+ //
+
+ for (i = 0; DiskMediaTypes[i].VendorId != NULL; i++) {
+
+ mediaListEntry = &DiskMediaTypes[i];
+
+ if
(strncmp(mediaListEntry->VendorId,vendorId,strlen(mediaListEntry->VendorId))) {
+ continue;
+ }
+
+ if ((mediaListEntry->ProductId != NULL) &&
+ strncmp(mediaListEntry->ProductId, productId,
strlen(mediaListEntry->ProductId))) {
+ continue;
+ }
+
+ if ((mediaListEntry->Revision != NULL) &&
+ strncmp(mediaListEntry->Revision, productRevision,
strlen(mediaListEntry->Revision))) {
+ continue;
+ }
+
+ deviceMatched = TRUE;
+
+ mediaTypes->DeviceType = FILE_DEVICE_DISK;
+ mediaTypes->MediaInfoCount = mediaListEntry->NumberOfTypes;
+
+ //
+ // Ensure that buffer is large enough.
+ //
+
+ sizeNeeded = FIELD_OFFSET(GET_MEDIA_TYPES, MediaInfo[0]) +
+ (mediaListEntry->NumberOfTypes *
+ sizeof(DEVICE_MEDIA_INFO)
+ );
+
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
+ sizeNeeded) {
+
+ //
+ // Buffer too small
+ //
+
+ Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ for (j = 0; j < mediaListEntry->NumberOfTypes; j++) {
+
+ mediaInfo->DeviceSpecific.RemovableDiskInfo.Cylinders.QuadPart =
fdoExtension->DiskGeometry.Cylinders.QuadPart;
+ mediaInfo->DeviceSpecific.RemovableDiskInfo.TracksPerCylinder =
fdoExtension->DiskGeometry.TracksPerCylinder;
+ mediaInfo->DeviceSpecific.RemovableDiskInfo.SectorsPerTrack =
fdoExtension->DiskGeometry.SectorsPerTrack;
+ mediaInfo->DeviceSpecific.RemovableDiskInfo.BytesPerSector =
fdoExtension->DiskGeometry.BytesPerSector;
+ mediaInfo->DeviceSpecific.RemovableDiskInfo.NumberMediaSides =
mediaListEntry->NumberOfSides;
+
+ //
+ // Set the type.
+ //
+
+ mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaType =
mediaListEntry->MediaTypes[j];
+
+ if (mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaType == MO_5_WO)
{
+ mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics =
MEDIA_WRITE_ONCE;
+ } else {
+ mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics =
MEDIA_READ_WRITE;
+ }
+
+ //
+ // Status will either be success, if media is present, or no media.
+ // It would be optimal to base from density code and medium type, but not
all devices
+ // have values for these fields.
+ //
+
+ if (MediaPresent) {
+
+ //
+ // The usage of MediumType and DensityCode is device specific, so
this may need
+ // to be extended to further key off of product/vendor ids.
+ // Currently, the MO units are the only devices that return this
information.
+ //
+
+ if (MediumType == 2) {
+ currentMedia = MO_5_WO;
+ } else if (MediumType == 3) {
+ currentMedia = MO_5_RW;
+
+ if (DensityCode == 0x87) {
+
+ //
+ // Indicate that the pinnacle 4.6 G media
+ // is present. Other density codes will default to normal
+ // RW MO media.
+ //
+
+ currentMedia = PINNACLE_APEX_5_RW;
+ }
+ } else {
+ currentMedia = 0;
+ }
+
+ if (currentMedia) {
+ if (mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaType ==
(STORAGE_MEDIA_TYPE)currentMedia) {
+
SET_FLAG(mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics,
MEDIA_CURRENTLY_MOUNTED);
+ }
+
+ } else {
+
SET_FLAG(mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics,
MEDIA_CURRENTLY_MOUNTED);
+ }
+ }
+
+ if (!IsWritable) {
+
SET_FLAG(mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics,
MEDIA_WRITE_PROTECTED);
+ }
+
+ //
+ // Advance to next entry.
+ //
+
+ mediaInfo++;
+ }
+ }
+
+SkipTable:
+
+ if (!deviceMatched) {
+
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
+ "DiskDetermineMediaTypes: Unknown device. Vendor: %s Product:
%s Revision: %s\n",
+ vendorId,
+ productId,
+ productRevision));
+ //
+ // Build an entry for unknown.
+ //
+
+ mediaTypes->DeviceType = FILE_DEVICE_DISK;
+ mediaTypes->MediaInfoCount = 1;
+
+ mediaInfo->DeviceSpecific.RemovableDiskInfo.Cylinders.QuadPart =
fdoExtension->DiskGeometry.Cylinders.QuadPart;
+ mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaType =
RemovableMedia;
+ mediaInfo->DeviceSpecific.RemovableDiskInfo.TracksPerCylinder =
fdoExtension->DiskGeometry.TracksPerCylinder;
+ mediaInfo->DeviceSpecific.RemovableDiskInfo.SectorsPerTrack =
fdoExtension->DiskGeometry.SectorsPerTrack;
+ mediaInfo->DeviceSpecific.RemovableDiskInfo.BytesPerSector =
fdoExtension->DiskGeometry.BytesPerSector;
+ mediaInfo->DeviceSpecific.RemovableDiskInfo.NumberMediaSides = 1;
+ mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics =
MEDIA_READ_WRITE;
+
+ if (MediaPresent) {
+
+
SET_FLAG(mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics,
MEDIA_CURRENTLY_MOUNTED);
+ }
+
+ if (!IsWritable) {
+
+
SET_FLAG(mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics,
MEDIA_WRITE_PROTECTED);
+ }
+ }
+ }
+
+ Irp->IoStatus.Information =
+ FIELD_OFFSET(GET_MEDIA_TYPES, MediaInfo[0]) +
+ (mediaTypes->MediaInfoCount * sizeof(DEVICE_MEDIA_INFO));
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
+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.
+
+--*/
+
+{
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
+ NTSTATUS status = STATUS_SUCCESS;
+ ULONG ioctlCode;
+
+ NT_ASSERT(DeviceObject != NULL);
+
+ Irp->IoStatus.Information = 0;
+ ioctlCode = irpStack->Parameters.DeviceIoControl.IoControlCode;
+
+ TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL, "DiskDeviceControl: Received
IOCTL 0x%X for device %p through IRP %p\n",
+ ioctlCode, DeviceObject, Irp));
+
+
+ switch (ioctlCode) {
+
+ case IOCTL_DISK_GET_CACHE_INFORMATION: {
+ status = DiskIoctlGetCacheInformation(DeviceObject, Irp);
+ break;
+ }
+
+ case IOCTL_DISK_SET_CACHE_INFORMATION: {
+ status = DiskIoctlSetCacheInformation(DeviceObject, Irp);
+ break;
+ }
+
+ case IOCTL_DISK_GET_CACHE_SETTING: {
+ status = DiskIoctlGetCacheSetting(DeviceObject, Irp);
+ break;
+ }
+
+ case IOCTL_DISK_SET_CACHE_SETTING: {
+ status = DiskIoctlSetCacheSetting(DeviceObject, Irp);
+ break;
+ }
+
+ case IOCTL_DISK_GET_DRIVE_GEOMETRY: {
+ status = DiskIoctlGetDriveGeometry(DeviceObject, Irp);
+ break;
+ }
+
+ case IOCTL_DISK_GET_DRIVE_GEOMETRY_EX: {
+ status = DiskIoctlGetDriveGeometryEx( DeviceObject, Irp );
+ break;
+ }
+
+ case IOCTL_DISK_VERIFY: {
+ status = DiskIoctlVerify(DeviceObject, Irp);
+ break;
+ }
+
+ case IOCTL_DISK_GET_LENGTH_INFO: {
+ status = DiskIoctlGetLengthInfo(DeviceObject, Irp);
+ break;
+ }
+
+ case IOCTL_DISK_IS_WRITABLE: {
+ status = DiskIoctlIsWritable(DeviceObject, Irp);
+ break;
+ }
+
+ case IOCTL_DISK_UPDATE_DRIVE_SIZE: {
+ status = DiskIoctlUpdateDriveSize(DeviceObject, Irp);
+ break;
+ }
+
+ case IOCTL_DISK_REASSIGN_BLOCKS: {
+ status = DiskIoctlReassignBlocks(DeviceObject, Irp);
+ break;
+ }
+
+ case IOCTL_DISK_REASSIGN_BLOCKS_EX: {
+ status = DiskIoctlReassignBlocksEx(DeviceObject, Irp);
+ break;
+ }
+
+ case IOCTL_DISK_INTERNAL_SET_VERIFY: {
+ status = DiskIoctlSetVerify(DeviceObject, Irp);
+ break;
+ }
+
+ case IOCTL_DISK_INTERNAL_CLEAR_VERIFY: {
+ status = DiskIoctlClearVerify(DeviceObject, Irp);
+ break;
+ }
+
+ case IOCTL_STORAGE_GET_MEDIA_TYPES_EX: {
+ status = DiskIoctlGetMediaTypesEx(DeviceObject, Irp);
+ break;
+ }
+
+ case IOCTL_STORAGE_PREDICT_FAILURE : {
+ status = DiskIoctlPredictFailure(DeviceObject, Irp);
+ break;
+ }
+
+ #if (NTDDI_VERSION >= NTDDI_WINBLUE)
+ case IOCTL_STORAGE_FAILURE_PREDICTION_CONFIG : {
+ status = DiskIoctlEnableFailurePrediction(DeviceObject, Irp);
+ break;
+ }
+ #endif
+
+ case SMART_GET_VERSION: {
+ status = DiskIoctlSmartGetVersion(DeviceObject, Irp);
+ break;
+ }
+
+ case SMART_RCV_DRIVE_DATA: {
+ status = DiskIoctlSmartReceiveDriveData(DeviceObject, Irp);
+ break;
+ }
+
+ case SMART_SEND_DRIVE_COMMAND: {
+ status = DiskIoctlSmartSendDriveCommand(DeviceObject, Irp);
+ break;
+ }
+
+ case IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS:
+ case IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS_ADMIN: {
+ status = DiskIoctlGetVolumeDiskExtents(DeviceObject, Irp);
+ break;
+ }
+
+ default: {
+
+ //
+ // Pass the request to the common device control routine.
+ //
+ return(ClassDeviceControl(DeviceObject, Irp));
+ break;
+ }
+ } // end switch
+
+ if (!NT_SUCCESS(status)) {
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskDeviceControl: IOCTL
0x%X to device %p failed with error 0x%X\n",
+ ioctlCode, DeviceObject, status));
+ if (IoIsErrorUserInduced(status) &&
+ (Irp->Tail.Overlay.Thread != NULL)) {
+ IoSetHardErrorOrVerifyDevice(Irp, DeviceObject);
+ }
+ }
+
+ //
+ // DiskIoctlVerify() (IOCTL_DISK_VERIFY) function returns STATUS_PENDING
+ // and completes the IRP in the work item. Do not touch or complete
+ // the IRP if STATUS_PENDING is returned.
+ //
+
+ if (status != STATUS_PENDING) {
+
+
+ Irp->IoStatus.Status = status;
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ }
+
+ return(status);
+} // end DiskDeviceControl()
+
+NTSTATUS
+NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
+DiskShutdownFlush(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is the handler for shutdown and flush requests. It sends
+ down a synch cache command to the device if its cache is enabled. If
+ the request is a shutdown and the media is removable, it sends down
+ an unlock request
+
+ Finally, an SRB_FUNCTION_SHUTDOWN or SRB_FUNCTION_FLUSH is sent down
+ the stack
+
+Arguments:
+
+ DeviceObject - The device object processing the request
+ Irp - The shutdown | flush request being serviced
+
+Return Value:
+
+ STATUS_PENDING if successful, an error code otherwise
+
+--*/
+
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension =
commonExtension->PartitionZeroExtension;
+ PDISK_DATA diskData = (PDISK_DATA) commonExtension->DriverData;
+ PIO_STACK_LOCATION irpStack;
+ NTSTATUS status = STATUS_SUCCESS;
+ ULONG srbSize;
+ PSCSI_REQUEST_BLOCK srb;
+ PSTORAGE_REQUEST_BLOCK srbEx = NULL;
+ PSTOR_ADDR_BTL8 storAddrBtl8 = NULL;
+ PSRBEX_DATA_SCSI_CDB16 srbExDataCdb16 = NULL;
+ PCDB cdb;
+ KIRQL irql;
+
+ //
+ // Flush requests are combined and need to be handled in a special manner
+ //
+
+ irpStack = IoGetCurrentIrpStackLocation(Irp);
+
+ if (irpStack->MajorFunction == IRP_MJ_FLUSH_BUFFERS) {
+
+ if (TEST_FLAG(fdoExtension->DeviceFlags, DEV_POWER_PROTECTED)) {
+
+ //
+ // We've been assured that both the disk
+ // and adapter caches are battery-backed
+ //
+
+ Irp->IoStatus.Status = STATUS_SUCCESS;
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ return STATUS_SUCCESS;
+ }
+
+ KeAcquireSpinLock(&diskData->FlushContext.Spinlock, &irql);
+
+ TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_SCSI, "DiskShutdownFlush: IRP %p
flags = 0x%x\n", Irp, irpStack->Flags));
+
+ //
+ // This request will most likely be completed asynchronously
+ //
+ IoMarkIrpPending(Irp);
+
+ //
+ // Look to see if a flush is in progress
+ //
+
+ if (diskData->FlushContext.CurrIrp != NULL) {
+
+ //
+ // There is an outstanding flush. Queue this
+ // request to the group that is next in line
+ //
+
+ if (diskData->FlushContext.NextIrp != NULL) {
+
+ #if DBG
+ diskData->FlushContext.DbgTagCount++;
+ #endif
+
+ InsertTailList(&diskData->FlushContext.NextList,
&Irp->Tail.Overlay.ListEntry);
+
+ KeReleaseSpinLock(&diskData->FlushContext.Spinlock, irql);
+
+ //
+ // This request will be completed by its representative
+ //
+
+ } else {
+
+ #if DBG
+ if (diskData->FlushContext.DbgTagCount < 64) {
+
+
diskData->FlushContext.DbgRefCount[diskData->FlushContext.DbgTagCount]++;
+ }
+
+ diskData->FlushContext.DbgSavCount +=
diskData->FlushContext.DbgTagCount;
+ diskData->FlushContext.DbgTagCount = 0;
+ #endif
+
+ diskData->FlushContext.NextIrp = Irp;
+ NT_ASSERT(IsListEmpty(&diskData->FlushContext.NextList));
+
+
+ KeReleaseSpinLock(&diskData->FlushContext.Spinlock, irql);
+
+
+ TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_SCSI,
"DiskShutdownFlush: waiting for event\n"));
+
+ //
+ // Wait for the outstanding flush to complete
+ //
+ KeWaitForSingleObject(&diskData->FlushContext.Event,
Executive, KernelMode, FALSE, NULL);
+
+ TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_SCSI,
"DiskShutdownFlush: event signal\n"));
+
+ //
+ // Make this group the outstanding one and free up the next slot
+ //
+
+ KeAcquireSpinLock(&diskData->FlushContext.Spinlock,
&irql);
+
+ NT_ASSERT(IsListEmpty(&diskData->FlushContext.CurrList));
+
+ while (!IsListEmpty(&diskData->FlushContext.NextList)) {
+
+ PLIST_ENTRY listEntry =
RemoveHeadList(&diskData->FlushContext.NextList);
+ InsertTailList(&diskData->FlushContext.CurrList,
listEntry);
+ }
+
+ NT_ASSERT(diskData->FlushContext.CurrIrp !=
diskData->FlushContext.NextIrp);
+ diskData->FlushContext.CurrIrp =
diskData->FlushContext.NextIrp;
+ diskData->FlushContext.NextIrp = NULL;
+
+ KeReleaseSpinLock(&diskData->FlushContext.Spinlock, irql);
+
+ //
+ // Send this request down to the device
+ //
+ DiskFlushDispatch(DeviceObject, &diskData->FlushContext);
+ }
+
+ } else {
+
+ diskData->FlushContext.CurrIrp = Irp;
+ NT_ASSERT(IsListEmpty(&diskData->FlushContext.CurrList));
+
+ NT_ASSERT(diskData->FlushContext.NextIrp == NULL);
+ NT_ASSERT(IsListEmpty(&diskData->FlushContext.NextList));
+
+
+ KeReleaseSpinLock(&diskData->FlushContext.Spinlock, irql);
+
+ DiskFlushDispatch(DeviceObject, &diskData->FlushContext);
+ }
+
+ } else {
+
+ //
+ // Allocate SCSI request block.
+ //
+
+ if (fdoExtension->AdapterDescriptor->SrbType ==
SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ srbSize = CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE;
+ } else {
+ srbSize = sizeof(SCSI_REQUEST_BLOCK);
+ }
+
+ srb = ExAllocatePoolWithTag(NonPagedPoolNx,
+ srbSize,
+ 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, srbSize);
+ if (fdoExtension->AdapterDescriptor->SrbType ==
SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+
+ srbEx = (PSTORAGE_REQUEST_BLOCK)srb;
+
+ //
+ // Set up STORAGE_REQUEST_BLOCK fields
+ //
+
+ srbEx->Length = FIELD_OFFSET(STORAGE_REQUEST_BLOCK, Signature);
+ srbEx->Function = SRB_FUNCTION_STORAGE_REQUEST_BLOCK;
+ srbEx->Signature = SRB_SIGNATURE;
+ srbEx->Version = STORAGE_REQUEST_BLOCK_VERSION_1;
+ srbEx->SrbLength = srbSize;
+ srbEx->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
+ srbEx->RequestPriority = IoGetIoPriorityHint(Irp);
+ srbEx->AddressOffset = sizeof(STORAGE_REQUEST_BLOCK);
+ srbEx->NumSrbExData = 1;
+
+ // Set timeout value and mark the request as not being a tagged request.
+ srbEx->TimeOutValue = fdoExtension->TimeOutValue * 4;
+ srbEx->RequestTag = SP_UNTAGGED;
+ srbEx->RequestAttribute = SRB_SIMPLE_TAG_REQUEST;
+ srbEx->SrbFlags = fdoExtension->SrbFlags;
+
+ //
+ // Set up address fields
+ //
+
+ storAddrBtl8 = (PSTOR_ADDR_BTL8) ((PUCHAR)srbEx + srbEx->AddressOffset);
+ storAddrBtl8->Type = STOR_ADDRESS_TYPE_BTL8;
+ storAddrBtl8->AddressLength = STOR_ADDR_BTL8_ADDRESS_LENGTH;
+
+ //
+ // Set up SCSI SRB extended data fields
+ //
+
+ srbEx->SrbExDataOffset[0] = sizeof(STORAGE_REQUEST_BLOCK) +
+ sizeof(STOR_ADDR_BTL8);
+ if ((srbEx->SrbExDataOffset[0] + sizeof(SRBEX_DATA_SCSI_CDB16)) <=
srbEx->SrbLength) {
+ srbExDataCdb16 = (PSRBEX_DATA_SCSI_CDB16)((PUCHAR)srbEx +
srbEx->SrbExDataOffset[0]);
+ srbExDataCdb16->Type = SrbExDataTypeScsiCdb16;
+ srbExDataCdb16->Length = SRBEX_DATA_SCSI_CDB16_LENGTH;
+
+ cdb = (PCDB)srbExDataCdb16->Cdb;
+ } else {
+ // Should not happen
+ NT_ASSERT(FALSE);
+
+ //
+ // Set the status and complete the request.
+ //
+
+ Irp->IoStatus.Status = STATUS_INTERNAL_ERROR;
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ return(STATUS_INTERNAL_ERROR);
+ }
+
+ } else {
+
+ //
+ // 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;
+ srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
+
+ cdb = (PCDB)srb->Cdb;
+ }
+
+ //
+ // If the write cache is enabled then send a synchronize cache request.
+ //
+
+ if (TEST_FLAG(fdoExtension->DeviceFlags, DEV_WRITE_CACHE)) {
+
+ if (fdoExtension->AdapterDescriptor->SrbType ==
SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ srbExDataCdb16->CdbLength = 10;
+ } else {
+ srb->CdbLength = 10;
+ }
+
+ cdb->CDB10.OperationCode = SCSIOP_SYNCHRONIZE_CACHE;
+
+ status = ClassSendSrbSynchronous(DeviceObject,
+ srb,
+ NULL,
+ 0,
+ TRUE);
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
"DiskShutdownFlush: Synchonize cache sent. Status = %lx\n", status));
+ }
+
+ //
+ // Unlock the device if it contains removable media
+ //
+
+ if (TEST_FLAG(DeviceObject->Characteristics, FILE_REMOVABLE_MEDIA))
+ {
+
+ if (fdoExtension->AdapterDescriptor->SrbType ==
SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+
+ //
+ // Reinitialize status fields to 0 in case there was a previous request
+ //
+
+ srbEx->SrbStatus = 0;
+ srbExDataCdb16->ScsiStatus = 0;
+
+ srbExDataCdb16->CdbLength = 6;
+
+ //
+ // Set timeout value
+ //
+
+ srbEx->TimeOutValue = fdoExtension->TimeOutValue;
+
+ } else {
+
+ //
+ // Reinitialize status fields to 0 in case there was a previous request
+ //
+
+ srb->SrbStatus = 0;
+ srb->ScsiStatus = 0;
+
+ srb->CdbLength = 6;
+
+ //
+ // Set timeout value.
+ //
+
+ srb->TimeOutValue = fdoExtension->TimeOutValue;
+ }
+
+ cdb->MEDIA_REMOVAL.OperationCode = SCSIOP_MEDIUM_REMOVAL;
+ cdb->MEDIA_REMOVAL.Prevent = FALSE;
+
+ status = ClassSendSrbSynchronous(DeviceObject,
+ srb,
+ NULL,
+ 0,
+ TRUE);
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
"DiskShutdownFlush: Unlock device request sent. Status = %lx\n", status));
+ }
+
+ //
+ // Set up a SHUTDOWN SRB
+ //
+
+ if (fdoExtension->AdapterDescriptor->SrbType ==
SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ srbEx->NumSrbExData = 0;
+ srbEx->SrbExDataOffset[0] = 0;
+ srbEx->SrbFunction = SRB_FUNCTION_SHUTDOWN;
+ srbEx->OriginalRequest = Irp;
+ srbEx->SrbLength = CLASS_SRBEX_NO_SRBEX_DATA_BUFFER_SIZE;
+ srbEx->SrbStatus = 0;
+ } else {
+ srb->CdbLength = 0;
+ srb->Function = SRB_FUNCTION_SHUTDOWN;
+ srb->SrbStatus = 0;
+ srb->OriginalRequest = Irp;
+ }
+
+ //
+ // 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;
+
+ //
+ // Call the port driver to process the request.
+ //
+
+ IoMarkIrpPending(Irp);
+ IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+ }
+
+ return STATUS_PENDING;
+}
+
+
+VOID
+DiskFlushDispatch(
+ IN PDEVICE_OBJECT Fdo,
+ IN PDISK_GROUP_CONTEXT FlushContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is the handler for flush requests. It sends down a synch
+ cache command to the device if its cache is enabled. This is followed
+ by an SRB_FUNCTION_FLUSH
+
+Arguments:
+
+ Fdo - The device object processing the flush request
+ FlushContext - The flush group context
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
+ PSCSI_REQUEST_BLOCK srb = &FlushContext->Srb.Srb;
+ PSTORAGE_REQUEST_BLOCK srbEx = &FlushContext->Srb.SrbEx;
+ PIO_STACK_LOCATION irpSp = NULL;
+ PSTOR_ADDR_BTL8 storAddrBtl8;
+ PSRBEX_DATA_SCSI_CDB16 srbExDataCdb16;
+ NTSTATUS SyncCacheStatus = STATUS_SUCCESS;
+
+ //
+ // Fill in the srb fields appropriately
+ //
+ if (fdoExt->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ RtlZeroMemory(srbEx, sizeof(FlushContext->Srb.SrbExBuffer));
+
+ srbEx->Length = FIELD_OFFSET(STORAGE_REQUEST_BLOCK, Signature);
+ srbEx->Function = SRB_FUNCTION_STORAGE_REQUEST_BLOCK;
+ srbEx->Signature = SRB_SIGNATURE;
+ srbEx->Version = STORAGE_REQUEST_BLOCK_VERSION_1;
+ srbEx->SrbLength = sizeof(FlushContext->Srb.SrbExBuffer);
+ srbEx->RequestPriority = IoGetIoPriorityHint(FlushContext->CurrIrp);
+ srbEx->AddressOffset = sizeof(STORAGE_REQUEST_BLOCK);
+ srbEx->TimeOutValue = fdoExt->TimeOutValue * 4;
+ srbEx->RequestTag = SP_UNTAGGED;
+ srbEx->RequestAttribute = SRB_SIMPLE_TAG_REQUEST;
+ srbEx->SrbFlags = fdoExt->SrbFlags;
+
+ //
+ // Set up address fields
+ //
+
+ storAddrBtl8 = (PSTOR_ADDR_BTL8) ((PUCHAR)srbEx + srbEx->AddressOffset);
+ storAddrBtl8->Type = STOR_ADDRESS_TYPE_BTL8;
+ storAddrBtl8->AddressLength = STOR_ADDR_BTL8_ADDRESS_LENGTH;
+
+ } else {
+ RtlZeroMemory(srb, SCSI_REQUEST_BLOCK_SIZE);
+
+ srb->Length = SCSI_REQUEST_BLOCK_SIZE;
+ srb->TimeOutValue = fdoExt->TimeOutValue * 4;
+ srb->QueueTag = SP_UNTAGGED;
+ srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
+ srb->SrbFlags = fdoExt->SrbFlags;
+ }
+
+ //
+ // If write caching is enabled then send down a synchronize cache request
+ //
+ if (TEST_FLAG(fdoExt->DeviceFlags, DEV_WRITE_CACHE))
+ {
+
+ if (fdoExt->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK)
{
+ srbEx->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
+ srbEx->NumSrbExData = 1;
+
+ //
+ // Set up SCSI SRB extended data fields
+ //
+
+ srbEx->SrbExDataOffset[0] = sizeof(STORAGE_REQUEST_BLOCK) +
+ sizeof(STOR_ADDR_BTL8);
+ if ((srbEx->SrbExDataOffset[0] + sizeof(SRBEX_DATA_SCSI_CDB16)) <=
srbEx->SrbLength) {
+ srbExDataCdb16 = (PSRBEX_DATA_SCSI_CDB16)((PUCHAR)srbEx +
srbEx->SrbExDataOffset[0]);
+ srbExDataCdb16->Type = SrbExDataTypeScsiCdb16;
+ srbExDataCdb16->Length = SRBEX_DATA_SCSI_CDB16_LENGTH;
+ srbExDataCdb16->CdbLength = 10;
+ srbExDataCdb16->Cdb[0] = SCSIOP_SYNCHRONIZE_CACHE;
+ } else {
+ // Should not happen
+ NT_ASSERT(FALSE);
+ return;
+ }
+
+ } else {
+ srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
+ srb->CdbLength = 10;
+ srb->Cdb[0] = SCSIOP_SYNCHRONIZE_CACHE;
+ }
+
+ TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_SCSI, "DiskFlushDispatch:
sending sync cache\n"));
+
+ SyncCacheStatus = ClassSendSrbSynchronous(Fdo, srb, NULL, 0, TRUE);
+ }
+
+ //
+ // Set up a FLUSH SRB
+ //
+ if (fdoExt->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ srbEx->SrbFunction = SRB_FUNCTION_FLUSH;
+ srbEx->NumSrbExData = 0;
+ srbEx->SrbExDataOffset[0] = 0;
+ srbEx->OriginalRequest = FlushContext->CurrIrp;
+ srbEx->SrbStatus = 0;
+
+ //
+ // Make sure that this srb does not get freed
+ //
+ SET_FLAG(srbEx->SrbFlags, SRB_CLASS_FLAGS_PERSISTANT);
+
+ } else {
+ srb->Function = SRB_FUNCTION_FLUSH;
+ srb->CdbLength = 0;
+ srb->OriginalRequest = FlushContext->CurrIrp;
+ srb->SrbStatus = 0;
+ srb->ScsiStatus = 0;
+
+ //
+ // Make sure that this srb does not get freed
+ //
+ SET_FLAG(srb->SrbFlags, SRB_CLASS_FLAGS_PERSISTANT);
+ }
+
+ //
+ // Make sure that this request does not get retried
+ //
+ irpSp = IoGetCurrentIrpStackLocation(FlushContext->CurrIrp);
+
+ irpSp->Parameters.Others.Argument4 = (PVOID) 0;
+
+ //
+ // Fill in the irp fields appropriately
+ //
+ irpSp = IoGetNextIrpStackLocation(FlushContext->CurrIrp);
+
+ irpSp->MajorFunction = IRP_MJ_SCSI;
+ irpSp->Parameters.Scsi.Srb = srb;
+
+ IoSetCompletionRoutine(FlushContext->CurrIrp, DiskFlushComplete,
(PVOID)SyncCacheStatus, TRUE, TRUE, TRUE);
+
+ TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_SCSI, "DiskFlushDispatch: sending
srb flush on irp %p\n", FlushContext->CurrIrp));
+
+ //
+ // Send down the flush request
+ //
+ IoCallDriver(((PCOMMON_DEVICE_EXTENSION)fdoExt)->LowerDeviceObject,
FlushContext->CurrIrp);
+}
+
+
+
+NTSTATUS
+NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
+DiskFlushComplete(
+ IN PDEVICE_OBJECT Fdo,
+ IN PIRP Irp,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This completion routine is a wrapper around ClassIoComplete. It
+ will complete all the flush requests that are tagged to it, set
+ an event to signal the next group to proceed and return
+
+Arguments:
+
+ Fdo - The device object which requested the completion routine
+ Irp - The irp that is being completed
+ Context - If disk had write cache enabled and SYNC CACHE command was sent as 1st part
of FLUSH processing
+ then context must carry the completion status of SYNC CACHE request,
+ else context must be set to STATUS_SUCCESS.
+
+Return Value:
+
+ STATUS_SUCCESS if successful, an error code otherwise
+
+--*/
+
+{
+ PDISK_GROUP_CONTEXT FlushContext;
+ NTSTATUS status;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt;
+ PDISK_DATA diskData;
+#ifdef _MSC_VER
+ #pragma warning(suppress:4311) // pointer truncation from 'PVOID' to
'NTSTATUS'
+#endif
+ NTSTATUS SyncCacheStatus = (NTSTATUS) Context;
+
+ TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_GENERAL, "DiskFlushComplete: %p
%p\n", Fdo, Irp));
+
+ //
+ // Get the flush context from the device extension
+ //
+ fdoExt = (PFUNCTIONAL_DEVICE_EXTENSION)Fdo->DeviceExtension;
+ diskData = (PDISK_DATA)fdoExt->CommonExtension.DriverData;
+ NT_ASSERT(diskData != NULL);
+ _Analysis_assume_(diskData != NULL);
+
+ FlushContext = &diskData->FlushContext;
+
+ //
+ // Make sure everything is in order
+ //
+ NT_ASSERT(Irp == FlushContext->CurrIrp);
+
+ TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_SCSI, "DiskFlushComplete: completing
irp %p\n", Irp));
+ status = ClassIoComplete(Fdo, Irp, &FlushContext->Srb.Srb);
+
+ //
+ // Make sure that ClassIoComplete did not decide to retry this request
+ //
+ NT_ASSERT(status != STATUS_MORE_PROCESSING_REQUIRED);
+
+ //
+ // If sync cache failed earlier, final status of the flush request needs to be
failure
+ // even if SRB_FUNCTION_FLUSH srb request succeeded
+ //
+ if (NT_SUCCESS(status) &&
+ (!NT_SUCCESS(SyncCacheStatus))) {
+ Irp->IoStatus.Status = status = SyncCacheStatus;
+ }
+
+ //
+ // Complete the flush requests tagged to this one
+ //
+
+ while (!IsListEmpty(&FlushContext->CurrList)) {
+
+ PLIST_ENTRY listEntry = RemoveHeadList(&FlushContext->CurrList);
+ PIRP tempIrp = CONTAINING_RECORD(listEntry, IRP, Tail.Overlay.ListEntry);
+
+ InitializeListHead(&tempIrp->Tail.Overlay.ListEntry);
+ tempIrp->IoStatus = Irp->IoStatus;
+
+ ClassReleaseRemoveLock(Fdo, tempIrp);
+ ClassCompleteRequest(Fdo, tempIrp, IO_NO_INCREMENT);
+ }
+
+
+ //
+ // Notify the next group's representative that it may go ahead now
+ //
+ KeSetEvent(&FlushContext->Event, IO_NO_INCREMENT, FALSE);
+
+
+ TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_SCSI, "DiskFlushComplete: irp %p
status = 0x%x\n", Irp, status));
+
+ return status;
+}
+
+
+
+NTSTATUS
+DiskModeSelect(
+ IN PDEVICE_OBJECT Fdo,
+ _In_reads_bytes_(Length) PCHAR ModeSelectBuffer,
+ IN ULONG Length,
+ IN BOOLEAN SavePage
+ )
+
+/*++
+
+Routine Description:
+
+ This routine sends a mode select command.
+
+Arguments:
+
+ DeviceObject - Supplies the device object associated with this request.
+
+ ModeSelectBuffer - Supplies a buffer containing the page data.
+
+ Length - Supplies the length in bytes of the mode select buffer.
+
+ SavePage - Indicates that parameters should be written to disk.
+
+Return Value:
+
+ Length of the transferred data is returned.
+
+--*/
+
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
+ PCDB cdb;
+ SCSI_REQUEST_BLOCK srb = {0};
+ ULONG retries = 1;
+ ULONG length2;
+ NTSTATUS status;
+ PULONG buffer;
+ PMODE_PARAMETER_BLOCK blockDescriptor;
+ UCHAR srbExBuffer[CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE] = {0};
+ PSTORAGE_REQUEST_BLOCK srbEx = (PSTORAGE_REQUEST_BLOCK)srbExBuffer;
+ PSTOR_ADDR_BTL8 storAddrBtl8;
+ PSRBEX_DATA_SCSI_CDB16 srbExDataCdb16;
+ PSCSI_REQUEST_BLOCK srbPtr;
+
+ PAGED_CODE();
+
+ //
+ // Check whether block length is available
+ //
+
+ if (fdoExtension->DiskGeometry.BytesPerSector == 0) {
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskModeSelect: Block
length is not available. Unable to send mode select\n"));
+ NT_ASSERT(fdoExtension->DiskGeometry.BytesPerSector != 0);
+ return STATUS_INVALID_PARAMETER;
+ }
+
+
+
+ length2 = Length + sizeof(MODE_PARAMETER_HEADER) + sizeof(MODE_PARAMETER_BLOCK);
+
+ //
+ // Allocate buffer for mode select header, block descriptor, and mode page.
+ //
+
+ buffer = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
+ 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);
+
+ blockDescriptor = (PMODE_PARAMETER_BLOCK)(buffer + 1);
+
+ //
+ // Set block length from the cached disk geometry
+ //
+
+ blockDescriptor->BlockLength[2] = (UCHAR)
(fdoExtension->DiskGeometry.BytesPerSector >> 16);
+ blockDescriptor->BlockLength[1] = (UCHAR)
(fdoExtension->DiskGeometry.BytesPerSector >> 8);
+ blockDescriptor->BlockLength[0] = (UCHAR)
(fdoExtension->DiskGeometry.BytesPerSector);
+
+ //
+ // Copy mode page to buffer.
+ //
+
+ RtlCopyMemory(buffer + 3, ModeSelectBuffer, Length);
+
+ //
+ // Build the MODE SELECT CDB.
+ //
+
+ if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK)
{
+
+ //
+ // Set up STORAGE_REQUEST_BLOCK fields
+ //
+
+ srbEx->Length = FIELD_OFFSET(STORAGE_REQUEST_BLOCK, Signature);
+ srbEx->Function = SRB_FUNCTION_STORAGE_REQUEST_BLOCK;
+ srbEx->Signature = SRB_SIGNATURE;
+ srbEx->Version = STORAGE_REQUEST_BLOCK_VERSION_1;
+ srbEx->SrbLength = sizeof(srbExBuffer);
+ srbEx->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
+ srbEx->RequestPriority = IoPriorityNormal;
+ srbEx->AddressOffset = sizeof(STORAGE_REQUEST_BLOCK);
+ srbEx->NumSrbExData = 1;
+
+ // Set timeout value from device extension.
+ srbEx->TimeOutValue = fdoExtension->TimeOutValue * 2;
+
+ //
+ // Set up address fields
+ //
+
+ storAddrBtl8 = (PSTOR_ADDR_BTL8) ((PUCHAR)srbEx + srbEx->AddressOffset);
+ storAddrBtl8->Type = STOR_ADDRESS_TYPE_BTL8;
+ storAddrBtl8->AddressLength = STOR_ADDR_BTL8_ADDRESS_LENGTH;
+
+ //
+ // Set up SCSI SRB extended data fields
+ //
+
+ srbEx->SrbExDataOffset[0] = sizeof(STORAGE_REQUEST_BLOCK) +
+ sizeof(STOR_ADDR_BTL8);
+ if ((srbEx->SrbExDataOffset[0] + sizeof(SRBEX_DATA_SCSI_CDB16)) <=
srbEx->SrbLength) {
+ srbExDataCdb16 = (PSRBEX_DATA_SCSI_CDB16)((PUCHAR)srbEx +
srbEx->SrbExDataOffset[0]);
+ srbExDataCdb16->Type = SrbExDataTypeScsiCdb16;
+ srbExDataCdb16->Length = SRBEX_DATA_SCSI_CDB16_LENGTH;
+ srbExDataCdb16->CdbLength = 6;
+
+ cdb = (PCDB)srbExDataCdb16->Cdb;
+ } else {
+ // Should not happen
+ NT_ASSERT(FALSE);
+
+ FREE_POOL(buffer);
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskModeSelect:
Insufficient extended SRB size\n"));
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ srbPtr = (PSCSI_REQUEST_BLOCK)srbEx;
+
+ } else {
+
+ srb.CdbLength = 6;
+ cdb = (PCDB)srb.Cdb;
+
+ //
+ // Set timeout value from device extension.
+ //
+
+ srb.TimeOutValue = fdoExtension->TimeOutValue * 2;
+
+ srbPtr = &srb;
+ }
+
+ 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,
+ srbPtr,
+ 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(srbPtr->SrbStatus) == SRB_STATUS_DATA_OVERRUN) {
+ status = STATUS_SUCCESS;
+ }
+
+ FREE_POOL(buffer);
+
+ return status;
+} // end DiskModeSelect()
+
+
+//
+// This routine is structured as a work-item routine
+//
+VOID
+NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
+DisableWriteCache(
+ IN PDEVICE_OBJECT Fdo,
+ IN PVOID Context
+ )
+
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension =
(PFUNCTIONAL_DEVICE_EXTENSION)Fdo->DeviceExtension;
+ DISK_CACHE_INFORMATION cacheInfo = { 0 };
+ NTSTATUS status;
+ PIO_WORKITEM WorkItem = (PIO_WORKITEM)Context;
+
+ PAGED_CODE();
+
+ NT_ASSERT(WorkItem != NULL);
+ _Analysis_assume_(WorkItem != NULL);
+
+ status = DiskGetCacheInformation(fdoExtension, &cacheInfo);
+
+ if (NT_SUCCESS(status) && (cacheInfo.WriteCacheEnabled == TRUE)) {
+
+ cacheInfo.WriteCacheEnabled = FALSE;
+
+ DiskSetCacheInformation(fdoExtension, &cacheInfo);
+ }
+
+ IoFreeWorkItem(WorkItem);
+}
+
+
+//
+// This routine is structured as a work-item routine
+//
+VOID
+NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
+DiskIoctlVerifyThread(
+ IN PDEVICE_OBJECT Fdo,
+ IN PVOID Context
+ )
+{
+ PDISK_VERIFY_WORKITEM_CONTEXT WorkContext = (PDISK_VERIFY_WORKITEM_CONTEXT)Context;
+ PIRP Irp = NULL;
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension =
(PFUNCTIONAL_DEVICE_EXTENSION)Fdo->DeviceExtension;
+ PDISK_DATA DiskData = (PDISK_DATA)FdoExtension->CommonExtension.DriverData;
+ PVERIFY_INFORMATION verifyInfo = NULL;
+ PSCSI_REQUEST_BLOCK Srb = NULL;
+ PCDB Cdb = NULL;
+ LARGE_INTEGER byteOffset;
+ LARGE_INTEGER sectorOffset;
+ ULONG sectorCount;
+ NTSTATUS status = STATUS_SUCCESS;
+ PSTORAGE_REQUEST_BLOCK srbEx = NULL;
+ PSTOR_ADDR_BTL8 storAddrBtl8 = NULL;
+ PSRBEX_DATA_SCSI_CDB16 srbExDataCdb16 = NULL;
+
+ PAGED_CODE();
+
+ NT_ASSERT(WorkContext != NULL);
+ _Analysis_assume_(WorkContext != NULL);
+
+ Srb = WorkContext->Srb;
+ Irp = WorkContext->Irp;
+ verifyInfo = (PVERIFY_INFORMATION)Irp->AssociatedIrp.SystemBuffer;
+
+ //
+ // We don't need to hold on to this memory as
+ // the following operation may take some time
+ //
+
+ IoFreeWorkItem(WorkContext->WorkItem);
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DiskIoctlVerifyThread:
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.QuadPart = byteOffset.QuadPart >> FdoExtension->SectorShift;
+
+ //
+ // Convert byte count to sector count.
+ //
+
+ sectorCount = 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);
+
+ //
+ // Initialize SCSI SRB for a verify CDB
+ //
+ if (FdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK)
{
+ RtlZeroMemory(Srb, CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE);
+ srbEx = (PSTORAGE_REQUEST_BLOCK)Srb;
+
+ //
+ // Set up STORAGE_REQUEST_BLOCK fields
+ //
+
+ srbEx->Length = FIELD_OFFSET(STORAGE_REQUEST_BLOCK, Signature);
+ srbEx->Function = SRB_FUNCTION_STORAGE_REQUEST_BLOCK;
+ srbEx->Signature = SRB_SIGNATURE;
+ srbEx->Version = STORAGE_REQUEST_BLOCK_VERSION_1;
+ srbEx->SrbLength = CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE;
+ srbEx->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
+ srbEx->RequestPriority = IoGetIoPriorityHint(Irp);
+ srbEx->AddressOffset = sizeof(STORAGE_REQUEST_BLOCK);
+ srbEx->NumSrbExData = 1;
+
+ //
+ // Set up address fields
+ //
+
+ storAddrBtl8 = (PSTOR_ADDR_BTL8) ((PUCHAR)srbEx + srbEx->AddressOffset);
+ storAddrBtl8->Type = STOR_ADDRESS_TYPE_BTL8;
+ storAddrBtl8->AddressLength = STOR_ADDR_BTL8_ADDRESS_LENGTH;
+
+ //
+ // Set up SCSI SRB extended data fields
+ //
+
+ srbEx->SrbExDataOffset[0] = sizeof(STORAGE_REQUEST_BLOCK) +
+ sizeof(STOR_ADDR_BTL8);
+ if ((srbEx->SrbExDataOffset[0] + sizeof(SRBEX_DATA_SCSI_CDB16)) <=
srbEx->SrbLength) {
+ srbExDataCdb16 = (PSRBEX_DATA_SCSI_CDB16)((PUCHAR)srbEx +
srbEx->SrbExDataOffset[0]);
+ srbExDataCdb16->Type = SrbExDataTypeScsiCdb16;
+ srbExDataCdb16->Length = SRBEX_DATA_SCSI_CDB16_LENGTH;
+
+ Cdb = (PCDB)srbExDataCdb16->Cdb;
+ if (TEST_FLAG(FdoExtension->DeviceFlags, DEV_USE_16BYTE_CDB)) {
+ srbExDataCdb16->CdbLength = 16;
+ Cdb->CDB16.OperationCode = SCSIOP_VERIFY16;
+ } else {
+ srbExDataCdb16->CdbLength = 10;
+ Cdb->CDB10.OperationCode = SCSIOP_VERIFY;
+ }
+ } else {
+ // Should not happen
+ NT_ASSERT(FALSE);
+
+ FREE_POOL(Srb);
+ FREE_POOL(WorkContext);
+ status = STATUS_INTERNAL_ERROR;
+ }
+
+ } else {
+ RtlZeroMemory(Srb, SCSI_REQUEST_BLOCK_SIZE);
+
+ Srb->Length = sizeof(SCSI_REQUEST_BLOCK);
+ Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
+
+ Cdb = (PCDB)Srb->Cdb;
+ if (TEST_FLAG(FdoExtension->DeviceFlags, DEV_USE_16BYTE_CDB)) {
+ Srb->CdbLength = 16;
+ Cdb->CDB16.OperationCode = SCSIOP_VERIFY16;
+ } else {
+ Srb->CdbLength = 10;
+ Cdb->CDB10.OperationCode = SCSIOP_VERIFY;
+ }
+
+ }
+
+ while (NT_SUCCESS(status) && (sectorCount != 0)) {
+
+ USHORT numSectors = (USHORT) min(sectorCount, MAX_SECTORS_PER_VERIFY);
+
+ if (FdoExtension->AdapterDescriptor->SrbType ==
SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+
+ //
+ // Reset status fields
+ //
+
+ srbEx->SrbStatus = 0;
+ srbExDataCdb16->ScsiStatus = 0;
+
+ //
+ // Calculate the request timeout value based
+ // on the number of sectors being verified
+ //
+
+ srbEx->TimeOutValue = ((numSectors + 0x7F) >> 7) *
FdoExtension->TimeOutValue;
+ } else {
+
+ //
+ // Reset status fields
+ //
+
+ Srb->SrbStatus = 0;
+ Srb->ScsiStatus = 0;
+
+ //
+ // Calculate the request timeout value based
+ // on the number of sectors being verified
+ //
+
+ Srb->TimeOutValue = ((numSectors + 0x7F) >> 7) *
FdoExtension->TimeOutValue;
+ }
+
+ //
+ // Update verify CDB info.
+ // NOTE - CDB opcode and length has been initialized prior to entering
+ // the while loop
+ //
+
+ if (TEST_FLAG(FdoExtension->DeviceFlags, DEV_USE_16BYTE_CDB)) {
+
+ REVERSE_BYTES_QUAD(&Cdb->CDB16.LogicalBlock, §orOffset);
+ REVERSE_BYTES_SHORT(&Cdb->CDB16.TransferLength[2], &numSectors);
+ } else {
+
+ //
+ // 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;
+ }
+
+ status = ClassSendSrbSynchronous(Fdo,
+ Srb,
+ NULL,
+ 0,
+ FALSE);
+
+ NT_ASSERT(status != STATUS_NONEXISTENT_SECTOR);
+
+ sectorCount -= numSectors;
+ sectorOffset.QuadPart += numSectors;
+ }
+
+ KeReleaseMutex(&DiskData->VerifyMutex, FALSE);
+
+ Irp->IoStatus.Status = status;
+ Irp->IoStatus.Information = 0;
+
+ ClassReleaseRemoveLock(Fdo, Irp);
+ ClassCompleteRequest(Fdo, Irp, IO_NO_INCREMENT);
+
+ FREE_POOL(Srb);
+ FREE_POOL(WorkContext);
+}
+
+
+VOID
+NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
+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;
+ PSTORAGE_REQUEST_BLOCK srbEx;
+ PCDB cdb = NULL;
+ UCHAR scsiStatus = 0;
+ UCHAR senseBufferLength = 0;
+ PVOID senseBuffer = NULL;
+ CDB noOp = {0};
+
+ //
+ // Get relevant fields from SRB
+ //
+ if (Srb->Function == SRB_FUNCTION_STORAGE_REQUEST_BLOCK) {
+
+ srbEx = (PSTORAGE_REQUEST_BLOCK)Srb;
+
+ //
+ // Look for SCSI SRB specific fields
+ //
+ if ((srbEx->SrbFunction == SRB_FUNCTION_EXECUTE_SCSI) &&
+ (srbEx->NumSrbExData > 0)) {
+ cdb = GetSrbScsiData(srbEx, NULL, NULL, &scsiStatus, &senseBuffer,
&senseBufferLength);
+
+ //
+ // cdb and sense buffer should not be NULL
+ //
+ NT_ASSERT(cdb != NULL);
+ NT_ASSERT(senseBuffer != NULL);
+
+ }
+
+ if (cdb == NULL) {
+
+ //
+ // Use a cdb that is all 0s
+ //
+ cdb = &noOp;
+ }
+
+ } else {
+
+ cdb = (PCDB)(Srb->Cdb);
+ scsiStatus = Srb->ScsiStatus;
+ senseBufferLength = Srb->SenseInfoBufferLength;
+ senseBuffer = Srb->SenseInfoBuffer;
+ }
+
+ if (*Status == STATUS_DATA_OVERRUN &&
+ (cdb != NULL) &&
+ (IS_SCSIOP_READWRITE(cdb->CDB10.OperationCode))) {
+
+ *Retry = TRUE;
+
+ //
+ // Update the error count for the device.
+ //
+
+ fdoExtension->ErrorCount++;
+
+ } else if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_ERROR &&
+ 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) &&
+ (senseBuffer != NULL) && (cdb != NULL)) {
+
+ BOOLEAN validSense = FALSE;
+ UCHAR senseKey = 0;
+ UCHAR asc = 0;
+ UCHAR ascq = 0;
+
+ validSense = ScsiGetSenseKeyAndCodes(senseBuffer,
+ senseBufferLength,
+
SCSI_SENSE_OPTIONS_FIXED_FORMAT_IF_UNKNOWN_FORMAT_INDICATED,
+ &senseKey,
+ &asc,
+ &ascq);
+
+ if (validSense) {
+
+ switch (senseKey) {
+
+ case SCSI_SENSE_ILLEGAL_REQUEST: {
+
+ switch (asc) {
+
+ case SCSI_ADSENSE_INVALID_CDB:
+ {
+ //
+ // Look to see if this is an Io request with the
ForceUnitAccess flag set
+ //
+ if (((cdb->CDB10.OperationCode == SCSIOP_WRITE) ||
+ (cdb->CDB10.OperationCode == SCSIOP_WRITE16))
&&
+ (cdb->CDB10.ForceUnitAccess))
+ {
+ PDISK_DATA diskData =
(PDISK_DATA)fdoExtension->CommonExtension.DriverData;
+
+ if (diskData->WriteCacheOverride ==
DiskWriteCacheEnable)
+ {
+ PIO_ERROR_LOG_PACKET logEntry = NULL;
+
+ //
+ // The user has explicitly requested that write
caching be turned on.
+ // Warn the user that writes with FUA enabled are
not working and that
+ // they should disable write cache.
+ //
+
+ logEntry =
IoAllocateErrorLogEntry(fdoExtension->DeviceObject,
+
sizeof(IO_ERROR_LOG_PACKET) + (4 * sizeof(ULONG)));
+
+ if (logEntry != NULL)
+ {
+ logEntry->FinalStatus = *Status;
+ logEntry->ErrorCode =
IO_WARNING_WRITE_FUA_PROBLEM;
+ logEntry->SequenceNumber = 0;
+ logEntry->MajorFunctionCode =
IRP_MJ_SCSI;
+ logEntry->IoControlCode = 0;
+ logEntry->RetryCount = 0;
+ logEntry->UniqueErrorValue = 0;
+ logEntry->DumpDataSize = 4 *
sizeof(ULONG);
+
+ logEntry->DumpData[0] =
diskData->ScsiAddress.PortNumber;
+ logEntry->DumpData[1] =
diskData->ScsiAddress.PathId;
+ logEntry->DumpData[2] =
diskData->ScsiAddress.TargetId;
+ logEntry->DumpData[3] =
diskData->ScsiAddress.Lun;
+
+ //
+ // Write the error log packet.
+ //
+
+ IoWriteErrorLogEntry(logEntry);
+ }
+ }
+ else
+ {
+ //
+ // Turn off write caching on this device. This is
so that future
+ // critical requests need not be sent down with
ForceUnitAccess
+ //
+ PIO_WORKITEM workItem = IoAllocateWorkItem(Fdo);
+
+ if (workItem)
+ {
+ IoQueueWorkItem(workItem, DisableWriteCache,
CriticalWorkQueue, workItem);
+ }
+ }
+
+ SET_FLAG(fdoExtension->ScanForSpecialFlags,
CLASS_SPECIAL_FUA_NOT_SUPPORTED);
+ ADJUST_FUA_FLAG(fdoExtension);
+
+
+ cdb->CDB10.ForceUnitAccess = FALSE;
+ *Retry = TRUE;
+
+ } else if ((cdb->CDB6FORMAT.OperationCode ==
SCSIOP_MODE_SENSE) &&
+ (cdb->MODE_SENSE.PageCode ==
MODE_SENSE_RETURN_ALL)) {
+
+ //
+ // Mode sense for all pages failed. This command
could fail with
+ // SCSI_SENSE_ILLEGAL_REQUEST /
SCSI_ADSENSE_INVALID_CDB if the data
+ // to be returned is more than 256 bytes. In which
case, try to get
+ // only MODE_PAGE_CACHING since we only need the
block descriptor.
+ //
+ // Simply change the page code and retry the request
+ //
+
+ cdb->MODE_SENSE.PageCode = MODE_PAGE_CACHING;
+ *Retry = TRUE;
+ }
+
+ break;
+ }
+ } // end switch(asc)
+ break;
+ }
+
+ case SCSI_SENSE_NOT_READY: {
+
+ switch (asc) {
+ case SCSI_ADSENSE_LUN_NOT_READY: {
+ switch (ascq) {
+ case SCSI_SENSEQ_BECOMING_READY:
+ case SCSI_SENSEQ_MANUAL_INTERVENTION_REQUIRED:
+ case SCSI_SENSEQ_CAUSE_NOT_REPORTABLE: {
+ invalidatePartitionTable = TRUE;
+ break;
+ }
+ } // end switch(ascq)
+ break;
+ }
+
+ case SCSI_ADSENSE_NO_MEDIA_IN_DEVICE: {
+ invalidatePartitionTable = TRUE;
+ break;
+ }
+ } // end switch(asc)
+ break;
+ }
+
+ case SCSI_SENSE_MEDIUM_ERROR: {
+ invalidatePartitionTable = TRUE;
+ break;
+ }
+
+ case SCSI_SENSE_HARDWARE_ERROR: {
+ invalidatePartitionTable = TRUE;
+ break;
+ }
+
+ case SCSI_SENSE_UNIT_ATTENTION:
+ {
+ invalidatePartitionTable = TRUE;
+ break;
+ }
+
+ case SCSI_SENSE_RECOVERED_ERROR: {
+ invalidatePartitionTable = TRUE;
+ break;
+ }
+
+ } // end switch(senseKey)
+ } // end if (validSense)
+ } else {
+
+ //
+ // On any exceptional scsi condition which might indicate that the
+ // device was changed we will flush out the state of the partition
+ // table.
+ //
+
+ switch (SRB_STATUS(Srb->SrbStatus)) {
+ case SRB_STATUS_INVALID_LUN:
+ case SRB_STATUS_INVALID_TARGET_ID:
+ case SRB_STATUS_NO_DEVICE:
+ case SRB_STATUS_NO_HBA:
+ case SRB_STATUS_INVALID_PATH_ID:
+ case SRB_STATUS_COMMAND_TIMEOUT:
+ case SRB_STATUS_TIMEOUT:
+ case SRB_STATUS_SELECTION_TIMEOUT:
+ case SRB_STATUS_REQUEST_FLUSHED:
+ case SRB_STATUS_UNEXPECTED_BUS_FREE:
+ case SRB_STATUS_PARITY_ERROR:
+ {
+ invalidatePartitionTable = TRUE;
+ break;
+ }
+
+ case SRB_STATUS_ERROR:
+ {
+ if (scsiStatus == SCSISTAT_RESERVATION_CONFLICT)
+ {
+ invalidatePartitionTable = TRUE;
+ }
+
+ break;
+ }
+ } // end switch(Srb->SrbStatus)
+ }
+
+ if (invalidatePartitionTable && TEST_FLAG(Fdo->Characteristics,
FILE_REMOVABLE_MEDIA)) {
+
+ //
+ // Inform the upper layers that the volume
+ // on this disk is in need of verification
+ //
+
+ SET_FLAG(Fdo->Flags, DO_VERIFY_VOLUME);
+ }
+ }
+
+ return;
+}
+
+
+VOID
+NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
+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();
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT, "Disk SetSpecialHacks,
Setting Hacks %p\n", (void*) 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
+ //
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT, "DiskScanForSpecial
(%p) => This unit requires "
+ " START_UNITS\n", fdo));
+ SET_FLAG(FdoExtension->DeviceFlags, DEV_SAFE_START_UNIT);
+
+ }
+
+ return;
+}
+
+
+VOID
+ResetBus(
+ IN PDEVICE_OBJECT Fdo
+ )
+
+/*++
+
+Routine Description:
+
+ This command sends a reset bus command to the SCSI port driver.
+
+Arguments:
+
+ Fdo - The functional device object for the logical unit with hardware problem.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PIO_STACK_LOCATION irpStack;
+ PIRP irp;
+
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
+ PSCSI_REQUEST_BLOCK srb;
+ PCOMPLETION_CONTEXT context;
+ PSTORAGE_REQUEST_BLOCK srbEx = NULL;
+ PSTOR_ADDR_BTL8 storAddrBtl8 = NULL;
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "Disk ResetBus: Sending
reset bus request to port driver.\n"));
+
+ //
+ // Allocate Srb from nonpaged pool.
+ //
+
+ context = ExAllocatePoolWithTag(NonPagedPoolNx,
+ 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.Srb;
+
+ if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK)
{
+ srbEx = &context->Srb.SrbEx;
+
+ //
+ // Zero out srb
+ //
+
+ RtlZeroMemory(srbEx, sizeof(context->Srb.SrbExBuffer));
+
+ //
+ // Set up STORAGE_REQUEST_BLOCK fields
+ //
+
+ srbEx->Length = FIELD_OFFSET(STORAGE_REQUEST_BLOCK, Signature);
+ srbEx->Function = SRB_FUNCTION_STORAGE_REQUEST_BLOCK;
+ srbEx->Signature = SRB_SIGNATURE;
+ srbEx->Version = STORAGE_REQUEST_BLOCK_VERSION_1;
+ srbEx->SrbLength = sizeof(context->Srb.SrbExBuffer);
+ srbEx->SrbFunction = SRB_FUNCTION_RESET_BUS;
+ srbEx->AddressOffset = sizeof(STORAGE_REQUEST_BLOCK);
+
+ //
+ // Set up address fields
+ //
+
+ storAddrBtl8 = (PSTOR_ADDR_BTL8) ((PUCHAR)srbEx + srbEx->AddressOffset);
+ storAddrBtl8->Type = STOR_ADDRESS_TYPE_BTL8;
+ storAddrBtl8->AddressLength = STOR_ADDR_BTL8_ADDRESS_LENGTH;
+
+ } else {
+
+ //
+ // 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) {
+ FREE_POOL(context);
+ return;
+ }
+
+ ClassAcquireRemoveLock(Fdo, irp);
+
+ IoSetCompletionRoutine(irp,
+ (PIO_COMPLETION_ROUTINE)ClassAsynchronousCompletion,
+ context,
+ TRUE,
+ TRUE,
+ TRUE);
+
+ irpStack = IoGetNextIrpStackLocation(irp);
+
+ irpStack->MajorFunction = IRP_MJ_SCSI;
+
+ if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK)
{
+ srbEx->RequestPriority = IoGetIoPriorityHint(irp);
+ srbEx->OriginalRequest = irp;
+ } else {
+ 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()
+
+
+
+VOID
+DiskLogCacheInformation(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN PDISK_CACHE_INFORMATION CacheInfo,
+ IN NTSTATUS Status
+ )
+{
+ PIO_ERROR_LOG_PACKET logEntry = NULL;
+
+ PAGED_CODE();
+
+ logEntry = IoAllocateErrorLogEntry(FdoExtension->DeviceObject,
sizeof(IO_ERROR_LOG_PACKET) + (4 * sizeof(ULONG)));
+
+ if (logEntry != NULL)
+ {
+ PDISK_DATA diskData = FdoExtension->CommonExtension.DriverData;
+ BOOLEAN bIsEnabled = TEST_FLAG(FdoExtension->DeviceFlags, DEV_WRITE_CACHE);
+
+ logEntry->FinalStatus = Status;
+ logEntry->ErrorCode = (bIsEnabled) ? IO_WRITE_CACHE_ENABLED :
IO_WRITE_CACHE_DISABLED;
+ logEntry->SequenceNumber = 0;
+ logEntry->MajorFunctionCode = IRP_MJ_SCSI;
+ logEntry->IoControlCode = 0;
+ logEntry->RetryCount = 0;
+ logEntry->UniqueErrorValue = 0x1;
+ logEntry->DumpDataSize = 4 * sizeof(ULONG);
+
+ logEntry->DumpData[0] = diskData->ScsiAddress.PathId;
+ logEntry->DumpData[1] = diskData->ScsiAddress.TargetId;
+ logEntry->DumpData[2] = diskData->ScsiAddress.Lun;
+ logEntry->DumpData[3] = CacheInfo->WriteCacheEnabled;
+
+ //
+ // Write the error log packet.
+ //
+
+ IoWriteErrorLogEntry(logEntry);
+ }
+}
+
+NTSTATUS
+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(NonPagedPoolNxCacheAligned,
+ MODE_DATA_SIZE,
+ DISK_TAG_INFO_EXCEPTION);
+
+ if (modeData == NULL) {
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_WMI,
"DiskGetInfoExceptionInformation: Unable to allocate mode "
+ "data buffer\n"));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlZeroMemory(modeData, MODE_DATA_SIZE);
+
+ length = ClassModeSense(FdoExtension->DeviceObject,
+ (PCHAR) 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,
+ (PCHAR) modeData,
+ MODE_DATA_SIZE,
+ MODE_PAGE_FAULT_REPORTING);
+
+ if (length < sizeof(MODE_PARAMETER_HEADER)) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_WMI,
"DiskGetInfoExceptionInformation: Mode Sense failed\n"));
+ FREE_POOL(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((PCHAR) 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;
+ }
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI,
"DiskGetInfoExceptionInformation: %s support SMART for device %p\n",
+ NT_SUCCESS(status) ? "does" : "does not",
+ FdoExtension->DeviceObject));
+
+ FREE_POOL(modeData);
+
+ return(status);
+}
+
+
+NTSTATUS
+DiskSetInfoExceptionInformation(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN PMODE_INFO_EXCEPTIONS PageData
+ )
+
+{
+ ULONG i;
+ NTSTATUS status = STATUS_SUCCESS;
+
+ 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,
+ (PCHAR) PageData,
+ sizeof(MODE_INFO_EXCEPTIONS),
+ TRUE);
+ }
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI,
"DiskSetInfoExceptionInformation: %s for device %p\n",
+ NT_SUCCESS(status) ? "succeeded" : "failed",
+ FdoExtension->DeviceObject));
+
+ return status;
+}
+
+NTSTATUS
+NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
+DiskGetCacheInformation(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN PDISK_CACHE_INFORMATION CacheInfo
+ )
+/*++
+
+Routine Description:
+
+ This function gets the caching mode page from the drive. This function
+ is called from DiskIoctlGetCacheInformation() in response to the IOCTL
+ IOCTL_DISK_GET_CACHE_INFORMATION. This is also called from the
+ DisableWriteCache() worker thread to disable caching when write commands fail.
+
+Arguments:
+
+ FdoExtension - The device extension for this device.
+
+ CacheInfo - Buffer to receive the Cache Information.
+
+Return Value:
+
+ NTSTATUS code
+
+--*/
+
+{
+ PMODE_PARAMETER_HEADER modeData;
+ PMODE_CACHING_PAGE pageData;
+
+ ULONG length;
+
+ PAGED_CODE();
+
+
+
+ modeData = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
+ MODE_DATA_SIZE,
+ DISK_TAG_DISABLE_CACHE);
+
+ if (modeData == NULL) {
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
"DiskGetSetCacheInformation: Unable to allocate mode "
+ "data buffer\n"));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlZeroMemory(modeData, MODE_DATA_SIZE);
+
+ length = ClassModeSense(FdoExtension->DeviceObject,
+ (PCHAR) 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,
+ (PCHAR) modeData,
+ MODE_DATA_SIZE,
+ MODE_PAGE_CACHING);
+
+ if (length < sizeof(MODE_PARAMETER_HEADER)) {
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
"DiskGetCacheInformation: Mode Sense failed\n"));
+
+ FREE_POOL(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((PCHAR) modeData,
+ length,
+ MODE_PAGE_CACHING,
+ TRUE);
+
+ //
+ // Check if valid caching page exists.
+ //
+
+ if (pageData == NULL) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskGetCacheInformation:
Unable to find caching mode page.\n"));
+ FREE_POOL(modeData);
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ //
+ // Copy the parameters over.
+ //
+
+ RtlZeroMemory(CacheInfo, sizeof(DISK_CACHE_INFORMATION));
+
+ CacheInfo->ParametersSavable = pageData->PageSavable;
+
+ CacheInfo->ReadCacheEnabled = !(pageData->ReadDisableCache);
+ CacheInfo->WriteCacheEnabled = pageData->WriteCacheEnable;
+
+
+ //
+ // Translate the values in the mode page into the ones defined in
+ // ntdddisk.h.
+ //
+
+ CacheInfo->ReadRetentionPriority =
+ TRANSLATE_RETENTION_PRIORITY(pageData->ReadRetensionPriority);
+ CacheInfo->WriteRetentionPriority =
+ TRANSLATE_RETENTION_PRIORITY(pageData->WriteRetensionPriority);
+
+ CacheInfo->DisablePrefetchTransferLength =
+ ((pageData->DisablePrefetchTransfer[0] << 8) +
+ pageData->DisablePrefetchTransfer[1]);
+
+ CacheInfo->ScalarPrefetch.Minimum =
+ ((pageData->MinimumPrefetch[0] << 8) +
pageData->MinimumPrefetch[1]);
+
+ CacheInfo->ScalarPrefetch.Maximum =
+ ((pageData->MaximumPrefetch[0] << 8) +
pageData->MaximumPrefetch[1]);
+
+ if(pageData->MultiplicationFactor) {
+ CacheInfo->PrefetchScalar = TRUE;
+ CacheInfo->ScalarPrefetch.MaximumBlocks =
+ ((pageData->MaximumPrefetchCeiling[0] << 8) +
+ pageData->MaximumPrefetchCeiling[1]);
+ }
+
+
+ FREE_POOL(modeData);
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
+DiskSetCacheInformation(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN PDISK_CACHE_INFORMATION CacheInfo
+ )
+/*++
+
+Routine Description:
+
+ This function sets the caching mode page in the drive. This function
+ is also called from the DisableWriteCache() worker thread to disable
+ caching when write commands fail.
+
+Arguments:
+
+ FdoExtension - The device extension for this device.
+
+ CacheInfo - Buffer the contains the Cache Information to be set on the drive.
+
+Return Value:
+
+ NTSTATUS code
+
+--*/
+{
+ PMODE_PARAMETER_HEADER modeData;
+ ULONG length;
+ PMODE_CACHING_PAGE pageData;
+ ULONG i;
+ NTSTATUS status = STATUS_SUCCESS;
+
+ PAGED_CODE();
+
+ modeData = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
+ MODE_DATA_SIZE,
+ DISK_TAG_DISABLE_CACHE);
+
+ if (modeData == NULL) {
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskSetCacheInformation:
Unable to allocate mode "
+ "data buffer\n"));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlZeroMemory(modeData, MODE_DATA_SIZE);
+
+ length = ClassModeSense(FdoExtension->DeviceObject,
+ (PCHAR) 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,
+ (PCHAR) modeData,
+ MODE_DATA_SIZE,
+ MODE_PAGE_CACHING);
+
+ if (length < sizeof(MODE_PARAMETER_HEADER)) {
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
"DiskSetCacheInformation: Mode Sense failed\n"));
+
+ FREE_POOL(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((PCHAR) modeData,
+ length,
+ MODE_PAGE_CACHING,
+ TRUE);
+
+ //
+ // Check if valid caching page exists.
+ //
+
+ if (pageData == NULL) {
+ FREE_POOL(modeData);
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ //
+ // Don't touch any of the normal parameters - not all drives actually
+ // use the correct size of caching mode page. Just change the things
+ // which the user could have modified.
+ //
+
+ pageData->PageSavable = FALSE;
+
+ pageData->ReadDisableCache = !(CacheInfo->ReadCacheEnabled);
+ pageData->MultiplicationFactor = CacheInfo->PrefetchScalar;
+ pageData->WriteCacheEnable = CacheInfo->WriteCacheEnabled;
+
+ pageData->WriteRetensionPriority = (UCHAR)
+ TRANSLATE_RETENTION_PRIORITY(CacheInfo->WriteRetentionPriority);
+ pageData->ReadRetensionPriority = (UCHAR)
+ TRANSLATE_RETENTION_PRIORITY(CacheInfo->ReadRetentionPriority);
+
+ pageData->DisablePrefetchTransfer[0] =
+ (UCHAR) (CacheInfo->DisablePrefetchTransferLength >> 8);
+ pageData->DisablePrefetchTransfer[1] =
+ (UCHAR) (CacheInfo->DisablePrefetchTransferLength & 0x00ff);
+
+ pageData->MinimumPrefetch[0] =
+ (UCHAR) (CacheInfo->ScalarPrefetch.Minimum >> 8);
+ pageData->MinimumPrefetch[1] =
+ (UCHAR) (CacheInfo->ScalarPrefetch.Minimum & 0x00ff);
+
+ pageData->MaximumPrefetch[0] =
+ (UCHAR) (CacheInfo->ScalarPrefetch.Maximum >> 8);
+ pageData->MaximumPrefetch[1] =
+ (UCHAR) (CacheInfo->ScalarPrefetch.Maximum & 0x00ff);
+
+ if(pageData->MultiplicationFactor) {
+
+ pageData->MaximumPrefetchCeiling[0] =
+ (UCHAR) (CacheInfo->ScalarPrefetch.MaximumBlocks >> 8);
+ pageData->MaximumPrefetchCeiling[1] =
+ (UCHAR) (CacheInfo->ScalarPrefetch.MaximumBlocks & 0x00ff);
+ }
+
+ //
+ // We will attempt (twice) to issue the mode select with the page.
+ //
+
+ for (i = 0; i < 2; i++) {
+
+ status = DiskModeSelect(FdoExtension->DeviceObject,
+ (PCHAR) pageData,
+ (pageData->PageLength + 2),
+ CacheInfo->ParametersSavable);
+
+ if (NT_SUCCESS(status)) {
+
+ if (CacheInfo->WriteCacheEnabled)
+ {
+ SET_FLAG(FdoExtension->DeviceFlags, DEV_WRITE_CACHE);
+ }
+ else
+ {
+ CLEAR_FLAG(FdoExtension->DeviceFlags, DEV_WRITE_CACHE);
+ }
+ ADJUST_FUA_FLAG(FdoExtension);
+
+ break;
+ }
+ }
+
+ if (NT_SUCCESS(status))
+ {
+ } else {
+
+ //
+ // We were unable to modify the disk write cache setting
+ //
+
+ SET_FLAG(FdoExtension->ScanForSpecialFlags,
CLASS_SPECIAL_MODIFY_CACHE_UNSUCCESSFUL);
+ }
+
+ FREE_POOL(modeData);
+ return status;
+}
+
+NTSTATUS
+DiskIoctlGetCacheSetting(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+
+/*++
+
+Routine description:
+
+ This routine services IOCTL_DISK_GET_CACHE_SETTING. It looks to
+ see if there are any issues with the disk cache and whether the
+ user had previously indicated that the cache is power-protected
+
+Arguments:
+
+ Fdo - The functional device object processing the request
+ Irp - The ioctl to be processed
+
+Return Value:
+
+ STATUS_SUCCESS if successful, an error code otherwise
+
+--*/
+
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+ PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
+ NTSTATUS status = STATUS_SUCCESS;
+
+ PAGED_CODE();
+
+ if (irpSp->Parameters.DeviceIoControl.OutputBufferLength <
sizeof(DISK_CACHE_SETTING))
+ {
+ status = STATUS_BUFFER_TOO_SMALL;
+ }
+ else
+ {
+ PDISK_CACHE_SETTING cacheSetting =
(PDISK_CACHE_SETTING)Irp->AssociatedIrp.SystemBuffer;
+
+ cacheSetting->Version = sizeof(DISK_CACHE_SETTING);
+ cacheSetting->State = DiskCacheNormal;
+
+ //
+ // Determine whether it is safe to turn on the cache
+ //
+ if (TEST_FLAG(fdoExtension->ScanForSpecialFlags,
CLASS_SPECIAL_FUA_NOT_SUPPORTED))
+ {
+ cacheSetting->State = DiskCacheWriteThroughNotSupported;
+ }
+
+ //
+ // Determine whether it is possible to modify the cache setting
+ //
+ if (TEST_FLAG(fdoExtension->ScanForSpecialFlags,
CLASS_SPECIAL_MODIFY_CACHE_UNSUCCESSFUL))
+ {
+ cacheSetting->State = DiskCacheModifyUnsuccessful;
+ }
+
+ cacheSetting->IsPowerProtected = TEST_FLAG(fdoExtension->DeviceFlags,
DEV_POWER_PROTECTED);
+
+ Irp->IoStatus.Information = sizeof(DISK_CACHE_SETTING);
+ }
+
+ return status;
+}
+
+
+NTSTATUS
+DiskIoctlSetCacheSetting(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+
+/*++
+
+Routine description:
+
+ This routine services IOCTL_DISK_SET_CACHE_SETTING. It allows
+ the user to specify whether the disk cache is power-protected
+ or not
+
+ This function must be called at IRQL < DISPATCH_LEVEL.
+
+Arguments:
+
+ Fdo - The functional device object processing the request
+ Irp - The ioctl to be processed
+
+Return Value:
+
+ STATUS_SUCCESS if successful, an error code otherwise
+
+--*/
+
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+ PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
+ NTSTATUS status = STATUS_SUCCESS;
+
+ //
+ // This function must be called at less than dispatch level.
+ // Fail if IRQL >= DISPATCH_LEVEL.
+ //
+ PAGED_CODE();
+ CHECK_IRQL();
+
+ if (irpSp->Parameters.DeviceIoControl.InputBufferLength <
sizeof(DISK_CACHE_SETTING))
+ {
+ status = STATUS_INFO_LENGTH_MISMATCH;
+ }
+ else
+ {
+ PDISK_CACHE_SETTING cacheSetting =
(PDISK_CACHE_SETTING)Irp->AssociatedIrp.SystemBuffer;
+
+ if (cacheSetting->Version == sizeof(DISK_CACHE_SETTING))
+ {
+ ULONG isPowerProtected;
+
+ //
+ // Save away the user-defined override in our extension and the registry
+ //
+ if (cacheSetting->IsPowerProtected)
+ {
+ SET_FLAG(fdoExtension->DeviceFlags, DEV_POWER_PROTECTED);
+ isPowerProtected = 1;
+ }
+ else
+ {
+ CLEAR_FLAG(fdoExtension->DeviceFlags, DEV_POWER_PROTECTED);
+ isPowerProtected = 0;
+ }
+ ADJUST_FUA_FLAG(fdoExtension);
+
+ ClassSetDeviceParameter(fdoExtension, DiskDeviceParameterSubkey,
DiskDeviceCacheIsPowerProtected, isPowerProtected);
+ }
+ else
+ {
+ status = STATUS_INVALID_PARAMETER;
+ }
+ }
+
+ return status;
+}
+
+NTSTATUS
+DiskIoctlGetLengthInfo(
+ IN OUT PDEVICE_OBJECT DeviceObject,
+ IN OUT PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine services IOCTL_DISK_GET_LENGTH_INFO. It returns
+ the disk geometry to the caller.
+
+ This function must be called at IRQL < DISPATCH_LEVEL.
+
+Arguments:
+
+ DeviceObject - Supplies the device object associated with this request.
+
+ Irp - The IRP to be processed
+
+Return Value:
+
+ NTSTATUS code
+
+--*/
+
+{
+ NTSTATUS status;
+ PIO_STACK_LOCATION irpStack;
+ PGET_LENGTH_INFORMATION lengthInfo;
+ PFUNCTIONAL_DEVICE_EXTENSION p0Extension;
+ PCOMMON_DEVICE_EXTENSION commonExtension;
+ PDISK_DATA partitionZeroData;
+ NTSTATUS oldReadyStatus;
+
+ //
+ // This function must be called at less than dispatch level.
+ // Fail if IRQL >= DISPATCH_LEVEL.
+ //
+ PAGED_CODE();
+ CHECK_IRQL();
+
+ //
+ // Initialization
+ //
+
+ commonExtension = DeviceObject->DeviceExtension;
+ irpStack = IoGetCurrentIrpStackLocation(Irp);
+ 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)) {
+ 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;
+ }
+ lengthInfo = (PGET_LENGTH_INFORMATION) Irp->AssociatedIrp.SystemBuffer;
+
+ lengthInfo->Length = commonExtension->PartitionLength;
+
+ status = STATUS_SUCCESS;
+ Irp->IoStatus.Information = sizeof(GET_LENGTH_INFORMATION);
+
+ return status;
+}
+
+NTSTATUS
+DiskIoctlGetDriveGeometry(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN OUT PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine services IOCTL_DISK_GET_DRIVE_GEOMETRY. It returns
+ the disk geometry to the caller.
+
+ This function must be called at IRQL < DISPATCH_LEVEL.
+
+Arguments:
+
+ DeviceObject - Supplies the device object associated with this request.
+
+ Irp - IRP with a return buffer large enough to receive the
+ extended geometry information.
+
+Return Value:
+
+ NTSTATUS code
+
+--*/
+
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+ PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation (Irp);
+ NTSTATUS status;
+
+ //
+ // This function must be called at less than dispatch level.
+ // Fail if IRQL >= DISPATCH_LEVEL.
+ //
+ PAGED_CODE();
+ CHECK_IRQL();
+
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
sizeof(DISK_GEOMETRY)) {
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlGetDriveGeometry:
Output buffer too small.\n"));
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ 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)) {
+ return status;
+ }
+ }
+
+ //
+ // Copy drive geometry information from device extension.
+ //
+
+ RtlMoveMemory(Irp->AssociatedIrp.SystemBuffer,
+ &(fdoExtension->DiskGeometry),
+ sizeof(DISK_GEOMETRY));
+
+ if (((PDISK_GEOMETRY)Irp->AssociatedIrp.SystemBuffer)->BytesPerSector == 0) {
+ ((PDISK_GEOMETRY)Irp->AssociatedIrp.SystemBuffer)->BytesPerSector = 512;
+ }
+ Irp->IoStatus.Information = sizeof(DISK_GEOMETRY);
+ return STATUS_SUCCESS;
+}
+
+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:
+
+ This routine services IOCTL_DISK_GET_DRIVE_GEOMETRY_EX. It returns
+ the extended disk geometry to the caller.
+
+ This function must be called at IRQL < DISPATCH_LEVEL.
+
+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;
+
+ //
+ // This function must be called at less than dispatch level.
+ // Fail if IRQL >= DISPATCH_LEVEL.
+ //
+ PAGED_CODE();
+ CHECK_IRQL();
+
+ //
+ // Setup parameters
+ //
+
+ commonExtension = DeviceObject->DeviceExtension;
+ fdoExtension = DeviceObject->DeviceExtension;
+ diskData = (PDISK_DATA)(commonExtension->DriverData);
+ irpStack = IoGetCurrentIrpStackLocation ( Irp );
+ geometryEx = NULL;
+ OutputBufferLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
+
+ //
+ // 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 ( (LONG)OutputBufferLength < FIELD_OFFSET (DISK_GEOMETRY_EX, Data) ) {
+
+ //
+ // Buffer too small. Bail out, telling the caller the required
+ // size.
+ //
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
"DiskIoctlGetDriveGeometryEx: Output buffer too small.\n"));
+ status = STATUS_BUFFER_TOO_SMALL;
+ return status;
+ }
+
+ if (TEST_FLAG (DeviceObject->Characteristics, FILE_REMOVABLE_MEDIA)) {
+
+ //
+ // Issue a ReadCapacity to update device extension
+ // with information for the current media.
+ //
+
+ status =
DiskReadDriveCapacity(commonExtension->PartitionZeroExtension->DeviceObject);
+
+ diskData->ReadyStatus = status;
+
+ if (!NT_SUCCESS (status)) {
+ return status;
+ }
+ }
+
+ //
+ // Copy drive geometry.
+ //
+
+ geometryEx = (PDISK_GEOMETRY_EX_INTERNAL)Irp->AssociatedIrp.SystemBuffer;
+ geometryEx->Geometry = fdoExtension->DiskGeometry;
+ if (geometryEx->Geometry.BytesPerSector == 0) {
+ geometryEx->Geometry.BytesPerSector = 512;
+ }
+ geometryEx->DiskSize =
commonExtension->PartitionZeroExtension->CommonExtension.PartitionLength;
+
+ //
+ // If the user buffer is large enough to hold the partition information
+ // then add that as well.
+ //
+
+ if ((LONG)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;
+}
+
+NTSTATUS
+DiskIoctlGetCacheInformation(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN OUT PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine services IOCTL_DISK_GET_CACHE_INFORMATION. It reads
+ the caching mode page from the device and returns information to
+ the caller. After validating the user parameter it calls the
+ DiskGetCacheInformation() function to get the mode page.
+
+ This function must be called at IRQL < DISPATCH_LEVEL.
+
+Arguments:
+
+ DeviceObject - Supplies the device object associated with this request.
+
+ Irp - The IRP to be processed
+
+Return Value:
+
+ NTSTATUS code
+
+--*/
+
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation (Irp);
+ PDISK_CACHE_INFORMATION cacheInfo = Irp->AssociatedIrp.SystemBuffer;
+ NTSTATUS status;
+
+ //
+ // This function must be called at less than dispatch level.
+ // Fail if IRQL >= DISPATCH_LEVEL.
+ //
+ PAGED_CODE();
+ CHECK_IRQL();
+
+ //
+ // Validate the request.
+ //
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
"DiskIoctlGetCacheInformation: DeviceObject %p Irp %p\n", DeviceObject, Irp));
+
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
sizeof(DISK_CACHE_INFORMATION)) {
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
"DiskIoctlGetCacheInformation: Output buffer too small.\n"));
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ status = DiskGetCacheInformation(fdoExtension, cacheInfo);
+
+ if (NT_SUCCESS(status)) {
+ Irp->IoStatus.Information = sizeof(DISK_CACHE_INFORMATION);
+
+ //
+ // Make sure write cache setting is reflected in device extension
+ //
+ if (cacheInfo->WriteCacheEnabled)
+ {
+ SET_FLAG(fdoExtension->DeviceFlags, DEV_WRITE_CACHE);
+ }
+ else
+ {
+ CLEAR_FLAG(fdoExtension->DeviceFlags, DEV_WRITE_CACHE);
+ }
+ ADJUST_FUA_FLAG(fdoExtension);
+
+ }
+ return status;
+}
+
+
+NTSTATUS
+DiskIoctlSetCacheInformation(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN OUT PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine services IOCTL_DISK_SET_CACHE_INFORMATION. It allows
+ the caller to set the caching mode page on the device. This function
+ validates the user parameter and calls the DiskSetCacheInformation()
+ function to set the mode page. It also stores the cache value in the
+ device extension and registry.
+
+ This function must be called at IRQL < DISPATCH_LEVEL.
+
+Arguments:
+
+ DeviceObject - Supplies the device object associated with this request.
+
+ Irp - The IRP to be processed
+
+Return Value:
+
+ NTSTATUS code
+
+--*/
+
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+ PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation (Irp);
+ PDISK_CACHE_INFORMATION cacheInfo = Irp->AssociatedIrp.SystemBuffer;
+ NTSTATUS status;
+
+ //
+ // This function must be called at less than dispatch level.
+ // Fail if IRQL is equal or above DISPATCH_LEVEL.
+ //
+
+ PAGED_CODE();
+ CHECK_IRQL();
+
+ //
+ // Validate the request.
+ //
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
"DiskIoctlSetCacheInformation: DeviceObject %p Irp %p\n", DeviceObject, Irp));
+
+ if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
sizeof(DISK_CACHE_INFORMATION)) {
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
"DiskIoctlSetCacheInformation: Input buffer length mismatch.\n"));
+ return STATUS_INFO_LENGTH_MISMATCH;
+ }
+
+ status = DiskSetCacheInformation(fdoExtension, cacheInfo);
+
+ //
+ // Save away the user-defined override in our extension and the registry
+ //
+ if (cacheInfo->WriteCacheEnabled) {
+ diskData->WriteCacheOverride = DiskWriteCacheEnable;
+ } else {
+ diskData->WriteCacheOverride = DiskWriteCacheDisable;
+ }
+
+ ClassSetDeviceParameter(fdoExtension, DiskDeviceParameterSubkey,
+ DiskDeviceUserWriteCacheSetting,
diskData->WriteCacheOverride);
+
+ DiskLogCacheInformation(fdoExtension, cacheInfo, status);
+
+ return status;
+}
+
+NTSTATUS
+DiskIoctlGetMediaTypesEx(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN OUT PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine services IOCTL_STORAGE_GET_MEDIA_TYPES_EX. It returns
+ the media type information to the caller. After validating the user
+ parameter it calls DiskDetermineMediaTypes() to get the media type.
+
+ This function must be called at IRQL < DISPATCH_LEVEL.
+
+Arguments:
+
+ DeviceObject - Supplies the device object associated with this request.
+
+ Irp - The IRP to be processed
+
+Return Value:
+
+ NTSTATUS code
+
+--*/
+
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation (Irp);
+ NTSTATUS status;
+
+ PMODE_PARAMETER_HEADER modeData;
+ PMODE_PARAMETER_BLOCK blockDescriptor;
+ PSCSI_REQUEST_BLOCK srb;
+ PCDB cdb;
+ ULONG modeLength;
+ ULONG retries = 4;
+ UCHAR densityCode = 0;
+ BOOLEAN writable = TRUE;
+ BOOLEAN mediaPresent = FALSE;
+ ULONG srbSize;
+ PSTORAGE_REQUEST_BLOCK srbEx = NULL;
+ PSTOR_ADDR_BTL8 storAddrBtl8 = NULL;
+ PSRBEX_DATA_SCSI_CDB16 srbExDataCdb16 = NULL;
+
+ //
+ // This function must be called at less than dispatch level.
+ // Fail if IRQL >= DISPATCH_LEVEL.
+ //
+ PAGED_CODE();
+ CHECK_IRQL();
+
+ //
+ // Validate the request.
+ //
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
"DiskIoctlGetMediaTypesEx: DeviceObject %p Irp %p\n", DeviceObject, Irp));
+
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
sizeof(GET_MEDIA_TYPES)) {
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlGetMediaTypesEx:
Output buffer too small.\n"));
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK)
{
+ srbSize = CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE;
+ } else {
+ srbSize = SCSI_REQUEST_BLOCK_SIZE;
+ }
+
+ srb = ExAllocatePoolWithTag(NonPagedPoolNx,
+ srbSize,
+ DISK_TAG_SRB);
+
+ if (srb == NULL) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlGetMediaTypesEx:
Unable to allocate memory.\n"));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlZeroMemory(srb, srbSize);
+
+ //
+ // Send a TUR to determine if media is present.
+ //
+
+ if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK)
{
+ srbEx = (PSTORAGE_REQUEST_BLOCK)srb;
+
+ //
+ // Set up STORAGE_REQUEST_BLOCK fields
+ //
+
+ srbEx->Length = FIELD_OFFSET(STORAGE_REQUEST_BLOCK, Signature);
+ srbEx->Function = SRB_FUNCTION_STORAGE_REQUEST_BLOCK;
+ srbEx->Signature = SRB_SIGNATURE;
+ srbEx->Version = STORAGE_REQUEST_BLOCK_VERSION_1;
+ srbEx->SrbLength = srbSize;
+ srbEx->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
+ srbEx->RequestPriority = IoGetIoPriorityHint(Irp);
+ srbEx->AddressOffset = sizeof(STORAGE_REQUEST_BLOCK);
+ srbEx->NumSrbExData = 1;
+
+ // Set timeout value.
+ srbEx->TimeOutValue = fdoExtension->TimeOutValue;
+
+ //
+ // Set up address fields
+ //
+
+ storAddrBtl8 = (PSTOR_ADDR_BTL8) ((PUCHAR)srbEx + srbEx->AddressOffset);
+ storAddrBtl8->Type = STOR_ADDRESS_TYPE_BTL8;
+ storAddrBtl8->AddressLength = STOR_ADDR_BTL8_ADDRESS_LENGTH;
+
+ //
+ // Set up SCSI SRB extended data fields
+ //
+
+ srbEx->SrbExDataOffset[0] = sizeof(STORAGE_REQUEST_BLOCK) +
+ sizeof(STOR_ADDR_BTL8);
+ if ((srbEx->SrbExDataOffset[0] + sizeof(SRBEX_DATA_SCSI_CDB16)) <=
srbEx->SrbLength) {
+ srbExDataCdb16 = (PSRBEX_DATA_SCSI_CDB16)((PUCHAR)srbEx +
srbEx->SrbExDataOffset[0]);
+ srbExDataCdb16->Type = SrbExDataTypeScsiCdb16;
+ srbExDataCdb16->Length = SRBEX_DATA_SCSI_CDB16_LENGTH;
+ srbExDataCdb16->CdbLength = 6;
+
+ cdb = (PCDB)srbExDataCdb16->Cdb;
+ } else {
+ // Should not happen
+ NT_ASSERT(FALSE);
+
+ FREE_POOL(srb);
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
"DiskIoctlGetMediaTypesEx: Insufficient extended SRB size.\n"));
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ } else {
+
+ srb->Length = SCSI_REQUEST_BLOCK_SIZE;
+ srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
+ srb->CdbLength = 6;
+ cdb = (PCDB)srb->Cdb;
+
+ //
+ // Set timeout value.
+ //
+
+ srb->TimeOutValue = fdoExtension->TimeOutValue;
+
+ }
+ cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY;
+
+ status = ClassSendSrbSynchronous(DeviceObject,
+ srb,
+ NULL,
+ 0,
+ FALSE);
+
+ if (NT_SUCCESS(status)) {
+ mediaPresent = TRUE;
+ }
+
+ modeLength = MODE_DATA_SIZE;
+ modeData = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
+ modeLength,
+ DISK_TAG_MODE_DATA);
+
+ if (modeData == NULL) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlGetMediaTypesEx:
Unable to allocate memory.\n"));
+ FREE_POOL(srb);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlZeroMemory(modeData, modeLength);
+
+ //
+ // Build the MODE SENSE CDB using previous SRB.
+ //
+
+ if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK)
{
+ srbEx->SrbStatus = 0;
+ srbExDataCdb16->ScsiStatus = 0;
+ srbExDataCdb16->CdbLength = 6;
+
+ //
+ // Set timeout value from device extension.
+ //
+
+ srbEx->TimeOutValue = fdoExtension->TimeOutValue;
+ } else {
+ srb->SrbStatus = 0;
+ srb->ScsiStatus = 0;
+ srb->CdbLength = 6;
+
+ //
+ // Set timeout value from device extension.
+ //
+
+ srb->TimeOutValue = fdoExtension->TimeOutValue;
+ }
+
+ //
+ // Page code of 0x3F will return all pages.
+ // This command could fail if the data to be returned is
+ // more than 256 bytes. In which case, we should get only
+ // the caching page since we only need the block descriptor.
+ // DiskFdoProcessError will change the page code to
+ // MODE_PAGE_CACHING if there is an error.
+ //
+
+ cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE;
+ cdb->MODE_SENSE.PageCode = MODE_SENSE_RETURN_ALL;
+ cdb->MODE_SENSE.AllocationLength = (UCHAR)modeLength;
+
+Retry:
+ status = ClassSendSrbSynchronous(DeviceObject,
+ srb,
+ modeData,
+ modeLength,
+ FALSE);
+
+ if (status == STATUS_VERIFY_REQUIRED) {
+
+ if (retries--) {
+
+ //
+ // Retry request.
+ //
+
+ goto Retry;
+ }
+ } else if (SRB_STATUS(srb->SrbStatus) == SRB_STATUS_DATA_OVERRUN) {
+ status = STATUS_SUCCESS;
+ }
+
+ if (NT_SUCCESS(status) || (status == STATUS_NO_MEDIA_IN_DEVICE)) {
+
+ //
+ // Get the block descriptor.
+ //
+
+ if (modeData->BlockDescriptorLength != 0) {
+
+ blockDescriptor = (PMODE_PARAMETER_BLOCK)((ULONG_PTR)modeData +
sizeof(MODE_PARAMETER_HEADER));
+ densityCode = blockDescriptor->DensityCode;
+ }
+
+ if (TEST_FLAG(modeData->DeviceSpecificParameter,
+ MODE_DSP_WRITE_PROTECT)) {
+
+ writable = FALSE;
+ }
+
+ status = DiskDetermineMediaTypes(DeviceObject,
+ Irp,
+ modeData->MediumType,
+ densityCode,
+ mediaPresent,
+ writable);
+ //
+ // If the buffer was too small, DetermineMediaTypes updated the status and
information and the request will fail.
+ //
+
+ } else {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlGetMediaTypesEx:
Mode sense for header/bd failed. %lx\n", status));
+ }
+
+ FREE_POOL(srb);
+ FREE_POOL(modeData);
+
+ return status;
+}
+
+NTSTATUS
+DiskIoctlPredictFailure(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN OUT PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine services IOCTL_STORAGE_PREDICT_FAILURE. If the device
+ supports SMART then it returns any available failure data.
+
+ This function must be called at IRQL < DISPATCH_LEVEL.
+
+Arguments:
+
+ DeviceObject - Supplies the device object associated with this request.
+
+ Irp - The IRP to be processed
+
+Return Value:
+
+ NTSTATUS code
+
+--*/
+
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+ PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation (Irp);
+ NTSTATUS status = STATUS_SUCCESS;
+
+ PSTORAGE_PREDICT_FAILURE checkFailure;
+ STORAGE_FAILURE_PREDICT_STATUS diskSmartStatus;
+ IO_STATUS_BLOCK ioStatus = { 0 };
+ KEVENT event;
+
+ //
+ // This function must be called at less than dispatch level.
+ // Fail if IRQL >= DISPATCH_LEVEL.
+ //
+ PAGED_CODE();
+ CHECK_IRQL();
+
+ //
+ // Validate the request.
+ //
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DiskIoctlPredictFailure:
DeviceObject %p Irp %p\n", DeviceObject, Irp));
+
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
sizeof(STORAGE_PREDICT_FAILURE)) {
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlPredictFailure:
Output buffer too small.\n"));
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ //
+ // See if the disk is predicting failure
+ //
+
+ checkFailure = (PSTORAGE_PREDICT_FAILURE)Irp->AssociatedIrp.SystemBuffer;
+
+ if (diskData->FailurePredictionCapability == FailurePredictionSense) {
+ ULONG readBufferSize;
+ PUCHAR readBuffer;
+ PIRP readIrp;
+ PDEVICE_OBJECT topOfStack;
+
+ checkFailure->PredictFailure = 0;
+
+ KeInitializeEvent(&event, SynchronizationEvent, FALSE);
+
+ topOfStack = IoGetAttachedDeviceReference(DeviceObject);
+
+ //
+ // SCSI disks need to have a read sent down to provoke any
+ // failures to be reported.
+ //
+ // Issue a normal read operation. The error-handling code in
+ // classpnp will take care of a failure prediction by logging the
+ // correct event.
+ //
+
+ readBufferSize = fdoExtension->DiskGeometry.BytesPerSector;
+ readBuffer = ExAllocatePoolWithTag(NonPagedPoolNx,
+ readBufferSize,
+ DISK_TAG_SMART);
+
+ if (readBuffer != NULL) {
+ LARGE_INTEGER offset;
+
+ offset.QuadPart = 0;
+ readIrp = IoBuildSynchronousFsdRequest(IRP_MJ_READ,
+ topOfStack,
+ readBuffer,
+ readBufferSize,
+ &offset,
+ &event,
+ &ioStatus);
+
+ if (readIrp != NULL) {
+
+ status = IoCallDriver(topOfStack, readIrp);
+ if (status == STATUS_PENDING) {
+ KeWaitForSingleObject(&event, Executive, KernelMode, FALSE,
NULL);
+ status = ioStatus.Status;
+ }
+
+
+ } else {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ FREE_POOL(readBuffer);
+ } else {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ ObDereferenceObject(topOfStack);
+ }
+
+ if (status != STATUS_INSUFFICIENT_RESOURCES)
+ {
+ 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;
+ }
+ }
+ return status;
+}
+
+#if (NTDDI_VERSION >= NTDDI_WINBLUE)
+NTSTATUS
+DiskIoctlEnableFailurePrediction(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN OUT PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine services IOCTL_STORAGE_FAILURE_PREDICTION_CONFIG. If the device
+ supports SMART then it returns any available failure data.
+
+ This function must be called at IRQL < DISPATCH_LEVEL.
+
+Arguments:
+
+ DeviceObject - Supplies the device object associated with this request.
+
+ Irp - The IRP to be processed
+
+Return Value:
+
+ NTSTATUS code
+
+--*/
+
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+ PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation (Irp);
+ NTSTATUS status = STATUS_SUCCESS;
+ PSTORAGE_FAILURE_PREDICTION_CONFIG enablePrediction;
+
+ //
+ // This function must be called at less than dispatch level.
+ // Fail if IRQL >= DISPATCH_LEVEL.
+ //
+ PAGED_CODE();
+ CHECK_IRQL();
+
+ //
+ // Validate the request.
+ //
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
"DiskIoctlEnableFailurePrediction: DeviceObject %p Irp %p\n", DeviceObject,
Irp));
+
+ if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
sizeof(STORAGE_FAILURE_PREDICTION_CONFIG) ||
+ irpStack->Parameters.DeviceIoControl.OutputBufferLength <
sizeof(STORAGE_FAILURE_PREDICTION_CONFIG)) {
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
"DiskIoctlEnableFailurePrediction: Buffer too small.\n"));
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ enablePrediction =
(PSTORAGE_FAILURE_PREDICTION_CONFIG)Irp->AssociatedIrp.SystemBuffer;
+
+ if (enablePrediction->Version != STORAGE_FAILURE_PREDICTION_CONFIG_V1 ||
+ enablePrediction->Size < sizeof(STORAGE_FAILURE_PREDICTION_CONFIG)) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
"DiskIoctlEnableFailurePrediction: Buffer version or size is incorrect.\n"));
+ status = STATUS_INVALID_PARAMETER;
+ }
+
+ if (enablePrediction->Reserved != 0) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
"DiskIoctlEnableFailurePrediction: Reserved bytes are not zero!\n"));
+ status = STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // Default to success. This might get overwritten on failure below.
+ //
+ status = STATUS_SUCCESS;
+
+ //
+ // If this is a "set" and the current state (enabled/disabled) is
+ // different from the sender's desired state,
+ //
+ if (enablePrediction->Set && enablePrediction->Enabled !=
diskData->FailurePredictionEnabled) {
+ if (diskData->FailurePredictionCapability == FailurePredictionSmart ||
+ diskData->FailurePredictionCapability == FailurePredictionIoctl) {
+ //
+ // SMART or IOCTL based failure prediction is being used so call
+ // the generic function that is normally called in the WMI path.
+ //
+ status = DiskEnableDisableFailurePrediction(fdoExtension,
enablePrediction->Enabled);
+ } else if (diskData->ScsiInfoExceptionsSupported) {
+ //
+ // If we know that the device supports the Informational Exceptions
+ // mode page, try to enable/disable failure prediction that way.
+ //
+ status = DiskEnableInfoExceptions(fdoExtension,
enablePrediction->Enabled);
+ }
+ }
+
+ //
+ // Return the current state regardless if this was a "set" or a
"get".
+ //
+ enablePrediction->Enabled = diskData->FailurePredictionEnabled;
+
+ if (NT_SUCCESS(status)) {
+ Irp->IoStatus.Information = sizeof(STORAGE_FAILURE_PREDICTION_CONFIG);
+ }
+
+ return status;
+}
+#endif //(NTDDI_VERSION >= NTDDI_WINBLUE)
+
+NTSTATUS
+DiskIoctlVerify(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN OUT PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine services IOCTL_DISK_VERIFY. After verifying
+ user input, it starts the worker thread DiskIoctlVerifyThread()
+ to verify the device.
+
+Arguments:
+
+ DeviceObject - Supplies the device object associated with this request.
+
+ Irp - The IRP to be processed
+
+Return Value:
+
+ NTSTATUS code
+
+--*/
+
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation (Irp);
+ PVERIFY_INFORMATION verifyInfo = Irp->AssociatedIrp.SystemBuffer;
+ PDISK_VERIFY_WORKITEM_CONTEXT Context = NULL;
+ PSCSI_REQUEST_BLOCK srb;
+ LARGE_INTEGER byteOffset;
+ ULONG srbSize;
+
+ //
+ // Validate the request.
+ //
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DiskIoctlVerify:
DeviceObject %p Irp %p\n", DeviceObject, Irp));
+
+ if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
sizeof(VERIFY_INFORMATION)) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlVerify: Input
buffer length mismatch.\n"));
+ return STATUS_INFO_LENGTH_MISMATCH;
+ }
+
+ if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK)
{
+ srbSize = CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE;
+ } else {
+ srbSize = SCSI_REQUEST_BLOCK_SIZE;
+ }
+ srb = ExAllocatePoolWithTag(NonPagedPoolNx,
+ srbSize,
+ DISK_TAG_SRB);
+
+ if (srb == NULL) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlVerify: Unable to
allocate memory.\n"));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlZeroMemory(srb, srbSize);
+
+ //
+ // Add disk offset to starting sector.
+ //
+
+ byteOffset.QuadPart = commonExtension->StartingOffset.QuadPart +
+ verifyInfo->StartingOffset.QuadPart;
+
+ //
+ // Perform a bounds check on the sector range
+ //
+
+ if ((verifyInfo->StartingOffset.QuadPart >
commonExtension->PartitionLength.QuadPart) ||
+ (verifyInfo->StartingOffset.QuadPart < 0)) {
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlVerify: Verify
request to invalid sector.\n"));
+ FREE_POOL(srb)
+ return STATUS_NONEXISTENT_SECTOR;
+ } else {
+
+ ULONGLONG bytesRemaining = commonExtension->PartitionLength.QuadPart -
verifyInfo->StartingOffset.QuadPart;
+
+ if ((ULONGLONG)verifyInfo->Length > bytesRemaining) {
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlVerify:
Verify request to invalid sector.\n"));
+ FREE_POOL(srb)
+ return STATUS_NONEXISTENT_SECTOR;
+ }
+ }
+
+ Context = ExAllocatePoolWithTag(NonPagedPoolNx,
+ sizeof(DISK_VERIFY_WORKITEM_CONTEXT),
+ DISK_TAG_WI_CONTEXT);
+ if (Context) {
+
+ Context->Irp = Irp;
+ Context->Srb = srb;
+ Context->WorkItem = IoAllocateWorkItem(DeviceObject);
+
+ if (Context->WorkItem) {
+
+ //
+ // Queue the work item and return.
+ //
+
+ IoMarkIrpPending(Irp);
+
+ IoQueueWorkItem(Context->WorkItem,
+ DiskIoctlVerifyThread,
+ DelayedWorkQueue,
+ Context);
+
+ return STATUS_PENDING;
+ }
+ FREE_POOL(Context);
+ }
+ FREE_POOL(srb)
+ return STATUS_INSUFFICIENT_RESOURCES;
+}
+
+NTSTATUS
+DiskIoctlReassignBlocks(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN OUT PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine services IOCTL_DISK_REASSIGN_BLOCKS. This IOCTL
+ allows the caller to remap the defective blocks to a new
+ location on the disk.
+
+ This function must be called at IRQL < DISPATCH_LEVEL.
+
+Arguments:
+
+ DeviceObject - Supplies the device object associated with this request.
+
+ Irp - The IRP to be processed
+
+Return Value:
+
+ NTSTATUS code
+
+--*/
+
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation (Irp);
+ NTSTATUS status;
+ PREASSIGN_BLOCKS badBlocks = Irp->AssociatedIrp.SystemBuffer;
+ PSCSI_REQUEST_BLOCK srb;
+ PCDB cdb;
+ ULONG bufferSize;
+ ULONG blockNumber;
+ ULONG blockCount;
+ ULONG srbSize;
+ PSTORAGE_REQUEST_BLOCK srbEx;
+ PSTOR_ADDR_BTL8 storAddrBtl8;
+ PSRBEX_DATA_SCSI_CDB16 srbExDataCdb16;
+
+ //
+ // This function must be called at less than dispatch level.
+ // Fail if IRQL >= DISPATCH_LEVEL.
+ //
+ PAGED_CODE();
+ CHECK_IRQL();
+
+ //
+ // Validate the request.
+ //
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DiskIoctlReassignBlocks:
DeviceObject %p Irp %p\n", DeviceObject, Irp));
+
+ if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
sizeof(REASSIGN_BLOCKS)) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlReassignBlocks:
Input buffer length mismatch.\n"));
+ return STATUS_INFO_LENGTH_MISMATCH;
+ }
+
+ //
+ // Make sure we have some data in the input buffer.
+ //
+
+ if (badBlocks->Count == 0) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlReassignBlocks:
Invalid block count\n"));
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ bufferSize = sizeof(REASSIGN_BLOCKS) + ((badBlocks->Count - 1) * sizeof(ULONG));
+
+ if (irpStack->Parameters.DeviceIoControl.InputBufferLength < bufferSize) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlReassignBlocks:
Input buffer length mismatch for bad blocks.\n"));
+ return STATUS_INFO_LENGTH_MISMATCH;
+ }
+
+ if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK)
{
+ srbSize = CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE;
+ } else {
+ srbSize = SCSI_REQUEST_BLOCK_SIZE;
+ }
+ srb = ExAllocatePoolWithTag(NonPagedPoolNx,
+ srbSize,
+ DISK_TAG_SRB);
+
+ if (srb == NULL) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlReassignBlocks:
Unable to allocate memory.\n"));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlZeroMemory(srb, srbSize);
+
+ //
+ // 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);
+ }
+
+ //
+ // Build a SCSI SRB containing a SCSIOP_REASSIGN_BLOCKS cdb
+ //
+
+ if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK)
{
+ srbEx = (PSTORAGE_REQUEST_BLOCK)srb;
+
+ //
+ // Set up STORAGE_REQUEST_BLOCK fields
+ //
+
+ srbEx->Length = FIELD_OFFSET(STORAGE_REQUEST_BLOCK, Signature);
+ srbEx->Function = SRB_FUNCTION_STORAGE_REQUEST_BLOCK;
+ srbEx->Signature = SRB_SIGNATURE;
+ srbEx->Version = STORAGE_REQUEST_BLOCK_VERSION_1;
+ srbEx->SrbLength = srbSize;
+ srbEx->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
+ srbEx->RequestPriority = IoGetIoPriorityHint(Irp);
+ srbEx->AddressOffset = sizeof(STORAGE_REQUEST_BLOCK);
+ srbEx->NumSrbExData = 1;
+
+ // Set timeout value.
+ srbEx->TimeOutValue = fdoExtension->TimeOutValue;
+
+ //
+ // Set up address fields
+ //
+
+ storAddrBtl8 = (PSTOR_ADDR_BTL8) ((PUCHAR)srbEx + srbEx->AddressOffset);
+ storAddrBtl8->Type = STOR_ADDRESS_TYPE_BTL8;
+ storAddrBtl8->AddressLength = STOR_ADDR_BTL8_ADDRESS_LENGTH;
+
+ //
+ // Set up SCSI SRB extended data fields
+ //
+
+ srbEx->SrbExDataOffset[0] = sizeof(STORAGE_REQUEST_BLOCK) +
+ sizeof(STOR_ADDR_BTL8);
+ if ((srbEx->SrbExDataOffset[0] + sizeof(SRBEX_DATA_SCSI_CDB16)) <=
srbEx->SrbLength) {
+ srbExDataCdb16 = (PSRBEX_DATA_SCSI_CDB16)((PUCHAR)srbEx +
srbEx->SrbExDataOffset[0]);
+ srbExDataCdb16->Type = SrbExDataTypeScsiCdb16;
+ srbExDataCdb16->Length = SRBEX_DATA_SCSI_CDB16_LENGTH;
+ srbExDataCdb16->CdbLength = 6;
+
+ cdb = (PCDB)srbExDataCdb16->Cdb;
+ } else {
+ // Should not happen
+ NT_ASSERT(FALSE);
+
+ FREE_POOL(srb);
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
"DiskIoctlReassignBlocks: Insufficient extended SRB size.\n"));
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ } else {
+ srb->Length = SCSI_REQUEST_BLOCK_SIZE;
+ srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
+ srb->CdbLength = 6;
+
+ //
+ // Set timeout value.
+ //
+
+ srb->TimeOutValue = fdoExtension->TimeOutValue;
+
... 9173 lines suppressed ...