From: Paul Gofman pgofman@codeweavers.com
--- dlls/kernel32/tests/volume.c | 35 ++++++++++++++++++++++++++++ dlls/mountmgr.sys/device.c | 28 ++++++++++++++++++++++ include/ntddstor.h | 45 +++++++++++++++++++++++++++++++++++- 3 files changed, 107 insertions(+), 1 deletion(-)
diff --git a/dlls/kernel32/tests/volume.c b/dlls/kernel32/tests/volume.c index baf055c08b0..24a9a2e7124 100644 --- a/dlls/kernel32/tests/volume.c +++ b/dlls/kernel32/tests/volume.c @@ -620,6 +620,7 @@ static void test_disk_query_property(void) STORAGE_PROPERTY_QUERY query = {0}; STORAGE_DESCRIPTOR_HEADER header = {0}; STORAGE_DEVICE_DESCRIPTOR descriptor = {0}; + DEVICE_SEEK_PENALTY_DESCRIPTOR seek_pen = {0}; HANDLE handle; DWORD error; DWORD size; @@ -656,6 +657,40 @@ static void test_disk_query_property(void) ok(descriptor.Version == sizeof(descriptor), "got descriptor.Version %ld\n", descriptor.Version); ok(descriptor.Size >= sizeof(descriptor), "got descriptor.Size %ld\n", descriptor.Size);
+ + query.PropertyId = StorageDeviceSeekPenaltyProperty; + query.QueryType = PropertyStandardQuery; + SetLastError(0xdeadbeef); + ret = DeviceIoControl(handle, IOCTL_STORAGE_QUERY_PROPERTY, &query, sizeof(query), &header, sizeof(header), &size, + NULL); + error = GetLastError(); + if (!ret && error == ERROR_INVALID_FUNCTION) + { + win_skip( "StorageDeviceSeekPenaltyProperty is not supported.\n" ); /* Win7 */ + } + else + { + ok(ret, "expect ret %#x, got %#x\n", TRUE, ret); + ok(error == 0xdeadbeef, "expect err %#x, got err %#lx\n", 0xdeadbeef, error); + ok(size == sizeof(header), "got size %ld\n", size); + ok(header.Version == sizeof(seek_pen), "got header.Version %ld\n", header.Version); + ok(header.Size == sizeof(seek_pen), "got header.Size %ld\n", header.Size); + + memset(&seek_pen, 0xcc, sizeof(seek_pen)); + ret = DeviceIoControl(handle, IOCTL_STORAGE_QUERY_PROPERTY, &query, sizeof(query), &seek_pen, sizeof(seek_pen), + &size, NULL); + error = GetLastError(); + ok(ret || (error == ERROR_INVALID_FUNCTION /* Win8 VMs */ || (error == ERROR_GEN_FAILURE /* VMs */)), + "got ret %d, error %#lx\n", ret, error); + if (ret) + { + ok(size == sizeof(seek_pen), "got size %ld\n", size); + ok(seek_pen.Version == sizeof(seek_pen), "got %ld\n", seek_pen.Version); + ok(seek_pen.Size == sizeof(seek_pen), "got %ld\n", seek_pen.Size); + ok(seek_pen.IncursSeekPenalty == TRUE || seek_pen.IncursSeekPenalty == FALSE, "got %d.\n", seek_pen.IncursSeekPenalty); + } + } + CloseHandle(handle); }
diff --git a/dlls/mountmgr.sys/device.c b/dlls/mountmgr.sys/device.c index 2aef41dad0f..271f2c86d77 100644 --- a/dlls/mountmgr.sys/device.c +++ b/dlls/mountmgr.sys/device.c @@ -1594,6 +1594,34 @@ static NTSTATUS query_property( struct disk_device *device, IRP *irp )
break; } + case StorageDeviceSeekPenaltyProperty: + { + DEVICE_SEEK_PENALTY_DESCRIPTOR *d = irp->AssociatedIrp.SystemBuffer; + + if (irpsp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(STORAGE_DESCRIPTOR_HEADER)) + { + status = STATUS_INVALID_PARAMETER; + } + else + { + if (irpsp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(*d)) + { + d->Version = d->Size = sizeof(*d); + irp->IoStatus.Information = sizeof(STORAGE_DESCRIPTOR_HEADER); + } + else + { + FIXME( "Faking StorageDeviceSeekPenaltyProperty data.\n" ); + + memset( d, 0, sizeof(*d) ); + d->Version = d->Size = sizeof(*d); + irp->IoStatus.Information = sizeof(*d); + } + status = STATUS_SUCCESS; + } + break; + } + default: FIXME( "Unsupported property %#x\n", query->PropertyId ); status = STATUS_NOT_SUPPORTED; diff --git a/include/ntddstor.h b/include/ntddstor.h index 4d715efd1ef..0cb6d01f9b9 100644 --- a/include/ntddstor.h +++ b/include/ntddstor.h @@ -214,7 +214,44 @@ typedef enum _STORAGE_QUERY_TYPE {
typedef enum _STORAGE_PROPERTY_ID { StorageDeviceProperty = 0, - StorageAdapterProperty + StorageAdapterProperty, + StorageDeviceIdProperty, + StorageDeviceUniqueIdProperty, + StorageDeviceWriteCacheProperty, + StorageMiniportProperty, + StorageAccessAlignmentProperty, + StorageDeviceSeekPenaltyProperty, + StorageDeviceTrimProperty, + StorageDeviceWriteAggregationProperty, + StorageDeviceDeviceTelemetryProperty, + StorageDeviceLBProvisioningProperty, + StorageDevicePowerProperty, + StorageDeviceCopyOffloadProperty, + StorageDeviceResiliencyProperty, + StorageDeviceMediumProductType, + StorageAdapterRpmbProperty, + StorageAdapterCryptoProperty, + StorageDeviceIoCapabilityProperty = 48, + StorageAdapterProtocolSpecificProperty, + StorageDeviceProtocolSpecificProperty, + StorageAdapterTemperatureProperty, + StorageDeviceTemperatureProperty, + StorageAdapterPhysicalTopologyProperty, + StorageDevicePhysicalTopologyProperty, + StorageDeviceAttributesProperty, + StorageDeviceManagementStatus, + StorageAdapterSerialNumberProperty, + StorageDeviceLocationProperty, + StorageDeviceNumaProperty, + StorageDeviceZonedDeviceProperty, + StorageDeviceUnsafeShutdownCount, + StorageDeviceEnduranceProperty, + StorageDeviceLedStateProperty, + StorageDeviceSelfEncryptionProperty = 64, + StorageFruIdProperty, + StorageStackProperty, + StorageAdapterProtocolSpecificPropertyEx, + StorageDeviceProtocolSpecificPropertyEx, } STORAGE_PROPERTY_ID, *PSTORAGE_PROPERTY_ID;
typedef struct _STORAGE_PROPERTY_QUERY { @@ -223,6 +260,12 @@ typedef struct _STORAGE_PROPERTY_QUERY { UCHAR AdditionalParameters[1]; } STORAGE_PROPERTY_QUERY, *PSTORAGE_PROPERTY_QUERY;
+typedef struct _DEVICE_SEEK_PENALTY_DESCRIPTOR { + DWORD Version; + DWORD Size; + BOOLEAN IncursSeekPenalty; +} DEVICE_SEEK_PENALTY_DESCRIPTOR, *PDEVICE_SEEK_PENALTY_DESCRIPTOR; + typedef struct _STORAGE_DESCRIPTOR_HEADER { ULONG Version; ULONG Size;
This is used by Dragon Age: The Veilguard which tries to detect SSD / NVME install disk this way and displays a warning about limiting graphics quality if it thinks it is HDD.
I looked a bit into properly relaying the option based on our udisks2 dbus interfacing. It is sanely possible for a limited subset of cases. There is RotationRate property of org.freedesktop.UDisks2.Drive (inside org.freedesktop.UDisks2.Block) which basically tells that. However, populated Drive object is absent in case the block device is on top of LVM disk. There is a special LVM2 object potentially present and giving some info about LVM2 logical and physical volume although I didn't see a link to the actual valid Drive which would allow to query that info. Also, lvm2 udisks2 module doesn't seem to be always available by default and might need a manual configuration. Also, there are other cases which would probably need a special handling, like MDRaid devices (and maybe encrypted devices, didn't check that).
Yet maybe even handling just RotationRate when available might be potentially useful, that would at least handle the simple cases and, in particular, typical cases of removable drives.