Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions client/Interop.cs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ internal enum TCP_TABLE_CLASS
}

public const int ERROR_SUCCESS = 0;
public const uint IOCTL_STORAGE_QUERY_PROPERTY = 0x2D1400u;

public enum QUERY_DEVICE_CONFIG_FLAGS : uint
{
Expand Down Expand Up @@ -430,6 +431,13 @@ public struct STORAGE_PROTOCOL_SPECIFIC_DATA
public uint ProtocolDataRequestSubValue4;
}
[StructLayout(LayoutKind.Sequential)]
public unsafe struct STORAGE_ADAPTER_SERIAL_NUMBER
{
public uint Version;
public uint Size;
public fixed ushort SerialNumber[128];
}
[StructLayout(LayoutKind.Sequential)]
public struct STORAGE_PROTOCOL_DATA_DESCRIPTOR
{
public uint Version;
Expand Down
86 changes: 76 additions & 10 deletions client/data/Methods/Hardware.cs
Original file line number Diff line number Diff line change
Expand Up @@ -996,8 +996,10 @@ private static async Task GetDiskDriveData()
LogEvent($"{e}", Region.Hardware);
}

foreach (var drive in drives.Where(x => x.SmartData == null))
foreach (var drive in drives.Where(x => x.IsNVMe))
{
if (drive.SmartData == null)
{
try
{
GetNvmeSmart(drive);
Expand All @@ -1009,6 +1011,18 @@ private static async Task GetDiskDriveData()
}
}

try
{
GetStorageAdapterSerialNumber(drive);
}
catch (Exception e)
{
LogEvent($"Exception during storage adapter serial number retrieval on drive {drive.DeviceName}", Region.Hardware, EventType.ERROR);
LogEvent($"{e}", Region.Hardware);
}

}

Disks = drives;
}

Expand Down Expand Up @@ -1060,14 +1074,6 @@ private static List<DiskDrive> GetDiskFreeSpace(List<DiskDrive> drives)

private static DiskDrive GetNvmeSmart(DiskDrive drive)
{
// Stop if drive is not an NVME drive. This happens on all external drives.
if (!drive.InterfaceType.Equals("NVMe", StringComparison.CurrentCultureIgnoreCase) && //VDS check
(drive.InterfaceType != "SCSI" || !drive.MediaType.ToLower().Contains("fixed"))) //Old method check
{
LogEvent($"Could not retrieve NVME Smart Data. Drive {drive.DeviceName} is not an NVME drive. Interface: {drive.InterfaceType}. Media type: {drive.MediaType}", Region.Hardware);
return drive;
}

// We can use DeviceId (Name in Windows) as a valid path to CreateFile (ex. \\?\PhysicalDrive0) instead of drive letter
var handle = CreateFile(drive.DeviceId, 0x40000000, 0x1 | 0x2, IntPtr.Zero, 0x3, 0, IntPtr.Zero);

Expand Down Expand Up @@ -1199,6 +1205,66 @@ private static DiskDrive GetNvmeSmart(DiskDrive drive)
return drive;
}

public static DiskDrive GetStorageAdapterSerialNumber(DiskDrive drive)
{
var handle = CreateFile(drive.DeviceId, 0x40000000, 0x1 | 0x2, IntPtr.Zero, 0x3, 0, IntPtr.Zero);

// Verify the handle.
if (handle == new IntPtr(-1))
{
LogEvent($"Storage adapter serial number could not be retrieved. Invalid Handle. {drive.DeviceId}", Region.Hardware, EventType.ERROR);
LogEvent($"Interop Error: {new Win32Exception(Marshal.GetLastWin32Error()).Message}", Region.Hardware);
return drive;
}

unsafe
{
STORAGE_PROPERTY_QUERY* query = null;
STORAGE_ADAPTER_SERIAL_NUMBER* adapterSn = null;

var bufferInLength = (uint)sizeof(STORAGE_PROPERTY_QUERY);
var bufferOutLength = (uint)sizeof(STORAGE_ADAPTER_SERIAL_NUMBER);
var bufferIn = Marshal.AllocHGlobal((int)bufferInLength);
var bufferOut = Marshal.AllocHGlobal((int)bufferOutLength);
try
{
query = (STORAGE_PROPERTY_QUERY*)bufferIn;
adapterSn = (STORAGE_ADAPTER_SERIAL_NUMBER*)bufferOut;

// Set up the Smart log query
query->PropertyId = STORAGE_PROPERTY_ID.StorageAdapterSerialNumberProperty;
query->QueryType = STORAGE_QUERY_TYPE.PropertyStandardQuery;

var result = DeviceIoControl(handle,
IOCTL_STORAGE_QUERY_PROPERTY,
bufferIn,
bufferInLength,
bufferOut,
bufferOutLength,
out _,
IntPtr.Zero
);
if (result)
{
var sn = Marshal.PtrToStringUni((IntPtr)adapterSn->SerialNumber);
drive.SerialNumber = sn.Split(' ').FirstOrDefault() ?? sn;
}
else
{
string errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;
LogEvent($"Interop failure during storage adapter serial number retrieval. {Marshal.GetLastWin32Error()} - {errorMessage} on drive {drive.DiskNumber}", Region.Hardware, EventType.ERROR);
return drive;
}
}
finally
{
Marshal.FreeHGlobal(bufferIn);
Marshal.FreeHGlobal(bufferOut);
}
}
return drive;
}

private unsafe static SmartAttribute MakeNvmeAttribute(byte* attr, byte id, string name)
{
unsafe
Expand Down Expand Up @@ -1411,4 +1477,4 @@ private static async Task GetBatteryData()
cmd.Close();
Batteries = BatteryInfo;
}
}
}
3 changes: 3 additions & 0 deletions client/data/Structs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ public class DiskDrive

[NonSerialized()] public string InstanceId; // Only used to link SmartData, do not serialize. Unless you really want to.
[NonSerialized] public string DeviceId; // Used to get device PnP Id from VDS method and get nvme SmartData

[JsonIgnore] public bool IsNVMe => InterfaceType.Equals("NVMe", StringComparison.CurrentCultureIgnoreCase) //VDS check
|| (InterfaceType == "SCSI" && MediaType.ToLower().Contains("fixed")); //Old method check
}

public class Partition
Expand Down
Loading