启用存储空间时获取物理磁盘的信息

Posted

技术标签:

【中文标题】启用存储空间时获取物理磁盘的信息【英文标题】:Get information for physical disks when Storage Spaces is enabled 【发布时间】:2019-02-08 07:44:24 【问题描述】:

启用 Windows 10 存储空间后,我需要获取系统中所有物理磁盘的以下信息。

型号 序列号 固件版本 容量 磁盘索引 磁盘的 Pnp Id(使用 CM_Get_Parent 获取 SCSI 控制器名称) 位置信息(总线号、目标 ID 和 LUN)

到目前为止我所尝试的:

    使用的 WMI 类 MSFT_PhysicalDisk 虽然这个类给了我适配器编号(所以我可以不用磁盘 PNP),但当磁盘连接到不同的 PCI 存储控制器(例如 Marvell 92xx SATA 6g 控制器)时,它给出的位置信息并不完整。

    将SetupDiGetClassDevs 与GUID_DEVINTERFACE_DISK 一起使用,将句柄传递给SetupDiGetDeviceInterface,并将SetupDiGetDeviceInterfaceDetail 用于位置信息(总线/目标ID/LUN)、PNP Id 和设备路径。我可以将设备路径传递给CreateFile 并获取其余信息(类似于this 方法)。问题在于它没有给我所有的物理磁盘。存储空间池下的磁盘被省略。

    使用与第二种方法类似的方法,但不要使用 SetupDiGetDeviceInterface 和 SetupDiGetDeviceInterfaceDetail,而是使用 SetupDiEnumDeviceInfo 和 CM_Get_DevNode_Registry_Property(使用来自 here 的磁盘驱动器指南)。尽管这为我提供了所有物理磁盘的位置和 PNP id,但我无法使用此处(我知道的)任何东西来调用 CreateFile 并获取其余详细信息。

启用存储空间后,如何获取每个物理磁盘的上述详细信息?

附带说明一下,如果有办法使用 CreateFile 和 DeviceIoControl 从磁盘索引中获取磁盘 PNP id,那对我也很有帮助。

【问题讨论】:

真的更好地使用 CM_Get_Device_Interface_ListGUID_DEVINTERFACE_DISK 和其他 CM_ api 而不是 SetupDi api。 CM_Get_Device_Interface_PropertyWDEVPKEY_Device_InstanceIdCM_Locate_DevNodeW。位置 - DEVPKEY_Device_LocationInfoCM_Get_DevNode_PropertyW 、容量、序列号 - 打开设备并发送 IOCTL。真的太多问题,或者你想要完整的程序在这里 @RbMm 样品将不胜感激 @RbMm 不幸的是,使用 CM_Get_Device_Interface_List 也会跳过存储空间池中的磁盘 不,CM_Get_Device_Interface_List 是完美的工作,没有这个跳过。您的代码中的错误 【参考方案1】:

首先我们需要通过调用CM_Get_Device_Interface_ListWCM_Get_Device_Interface_List_SizeWGUID_DEVINTERFACE_DISK 来枚举系统中的所有磁盘

#include <Shlwapi.h>
#include <cfgmgr32.h>
#undef _NTDDSTOR_H_
#include <ntddstor.h>
#include <ntdddisk.h>

static volatile UCHAR guz;

CONFIGRET EnumDisks(PCSTR prefix, PGUID InterfaceClassGuid)

    CONFIGRET err;

    PVOID stack = alloca(guz);
    ULONG BufferLen = 0, NeedLen = 256;

    union 
        PVOID buf;
        PWSTR pszDeviceInterface;
    ;

    for(;;) 
    
        if (BufferLen < NeedLen)
        
            BufferLen = RtlPointerToOffset(buf = alloca((NeedLen - BufferLen) * sizeof(WCHAR)), stack) / sizeof(WCHAR);
        

        switch (err = CM_Get_Device_Interface_ListW(InterfaceClassGuid, 
            0, pszDeviceInterface, BufferLen, CM_GET_DEVICE_INTERFACE_LIST_PRESENT))
        
        case CR_BUFFER_SMALL:
            if (err = CM_Get_Device_Interface_List_SizeW(&NeedLen, InterfaceClassGuid, 
                0, CM_GET_DEVICE_INTERFACE_LIST_PRESENT))
            
        default:
            return err;
            
            continue;

        case CR_SUCCESS:

            while (*pszDeviceInterface)
            
                DbgPrint("Interface=[%S]\n", pszDeviceInterface);

                HANDLE hFile = CreateFileW(pszDeviceInterface, FILE_GENERIC_READ, 
                    FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, 0, 0);

                if (hFile != INVALID_HANDLE_VALUE)
                
                    GetDiskPropertyByHandle(hFile);
                    CloseHandle(hFile);
                

                GetPropertyByInterface(prefix, pszDeviceInterface);

                pszDeviceInterface += 1 + wcslen(pszDeviceInterface);
            

            return CR_SUCCESS;
        
    


CONFIGRET EnumDisks()

    char prefix[256];
    memset(prefix, '\t', sizeof(prefix));
    prefix[sizeof(prefix) - 1] = 0;
    prefix[0] = 0;
    return EnumDisks(prefix + sizeof(prefix) - 1, const_cast<PGUID>(&GUID_DEVINTERFACE_DISK));

CM_Get_Device_Interface_ListW 返回多个以 NULL 结尾的 Unicode 字符串,每个字符串代表一个接口实例的符号链接名称。

从一方面这个符号链接名称可以传递给CreateFileW 用于打开磁盘设备。在此之后,我们可以将一些 ioctl 设置到磁盘 - 用于获取

磁盘索引 容量 序列号 分区信息

示例:

void GetDiskPropertyByHandle(HANDLE hDisk)

    HANDLE hPartition;
    IO_STATUS_BLOCK iosb;
    STORAGE_DEVICE_NUMBER sdn;
    GET_LENGTH_INFORMATION li;

    NTSTATUS status = NtDeviceIoControlFile(hDisk, 0, 0, 0, &iosb,
        IOCTL_STORAGE_GET_DEVICE_NUMBER, 0, 0, &sdn, sizeof(sdn));

    if (0 <= status && sdn.DeviceType == FILE_DEVICE_DISK && !sdn.PartitionNumber)
    
        DbgPrint("\\Device\\Harddisk%d\n", sdn.DeviceNumber);

        WCHAR sz[64], *c = sz + swprintf(sz, L"\\Device\\Harddisk%d\\Partition", sdn.DeviceNumber);

        WCHAR szSize[32];

        if (0 <= (status = NtDeviceIoControlFile(hDisk, 0, 0, 0, &iosb,
            IOCTL_DISK_GET_LENGTH_INFO, 0, 0, &li, sizeof(li))))
        
            DbgPrint("Length = %S (%I64x)\n", 
                StrFormatByteSizeW(li.Length.QuadPart, szSize, RTL_NUMBER_OF(szSize)), 
                li.Length.QuadPart);
        

        UNICODE_STRING ObjectName;
        OBJECT_ATTRIBUTES oa =  sizeof(oa), 0, &ObjectName, OBJ_CASE_INSENSITIVE ;

        PVOID stack = alloca(guz);

        union 
            PVOID buf;
            PDRIVE_LAYOUT_INFORMATION_EX pdli;
            PSTORAGE_DEVICE_DESCRIPTOR psdd;
            PCSTR psz;
        ;

        STORAGE_PROPERTY_QUERY spq =  StorageDeviceProperty, PropertyStandardQuery ; 

        ULONG cb = 0, rcb = sizeof(STORAGE_DEVICE_DESCRIPTOR) + 0x40, PartitionCount = 4;

        do 
        
            if (cb < rcb)
            
                cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
            

            switch (status = (NtDeviceIoControlFile(hDisk, 0, 0, 0, &iosb, 
                IOCTL_STORAGE_QUERY_PROPERTY, &spq, sizeof(spq), buf, cb)))
            
            case STATUS_SUCCESS:
            case STATUS_BUFFER_OVERFLOW:
                if (psdd->Version == sizeof(STORAGE_DEVICE_DESCRIPTOR))
                
                    if (psdd->Size > cb)
                    
                        rcb = psdd->Size;
                        status = STATUS_BUFFER_OVERFLOW;
                    
                    else
                    
                        if (psdd->SerialNumberOffset)
                        
                            DbgPrint("SerialNumber = %s\n", psz + psdd->SerialNumberOffset);
                        
                    
                
                else
                
                    status = STATUS_INVALID_PARAMETER;
                
                break;
            
         while (status == STATUS_BUFFER_OVERFLOW);

        for (;;)
        
            if (cb < (rcb = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry[PartitionCount])))
            
                cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
            

            if (0 <= (status = NtDeviceIoControlFile(hDisk, 0, 0, 0, &iosb,
                IOCTL_DISK_GET_DRIVE_LAYOUT_EX, 0, 0, buf, cb)))
            
                if (PartitionCount = pdli->PartitionCount)
                
                    PPARTITION_INFORMATION_EX PartitionEntry = pdli->PartitionEntry;

                    do 
                    
                        if (!PartitionEntry->PartitionNumber)
                        
                            continue;
                        

                        _itow(PartitionEntry->PartitionNumber, c, 10);

                        RtlInitUnicodeString(&ObjectName, sz);

                        DbgPrint("%wZ\nOffset=%S ", &ObjectName, 
                            StrFormatByteSizeW(PartitionEntry->StartingOffset.QuadPart, szSize, RTL_NUMBER_OF(szSize)));

                        DbgPrint("Length=%S\n", 
                            StrFormatByteSizeW(PartitionEntry->PartitionLength.QuadPart, szSize, RTL_NUMBER_OF(szSize)));

                        char PartitionName[256], *szPartitionName;

                        switch (PartitionEntry->PartitionStyle)
                        
                        case PARTITION_STYLE_MBR:
                            DbgPrint("MBR: type=%x boot=%x", PartitionEntry->Mbr.PartitionType, PartitionEntry->Mbr.BootIndicator);
                            break;
                        case PARTITION_STYLE_GPT:

                            if (IsEqualGUID(PartitionEntry->Gpt.PartitionType, PARTITION_ENTRY_UNUSED_GUID))
                            
                                szPartitionName = "UNUSED";
                            
                            else if (IsEqualGUID(PartitionEntry->Gpt.PartitionType, PARTITION_SYSTEM_GUID))
                            
                                szPartitionName = "SYSTEM";
                            
                            else if (IsEqualGUID(PartitionEntry->Gpt.PartitionType, PARTITION_MSFT_RESERVED_GUID))
                            
                                szPartitionName = "RESERVED";
                            
                            else if (IsEqualGUID(PartitionEntry->Gpt.PartitionType, PARTITION_BASIC_DATA_GUID))
                            
                                szPartitionName = "DATA";
                            
                            else if (IsEqualGUID(PartitionEntry->Gpt.PartitionType, PARTITION_MSFT_RECOVERY_GUID))
                            
                                szPartitionName = "RECOVERY";
                            
                            else if (IsEqualGUID(PartitionEntry->Gpt.PartitionType, PARTITION_MSFT_SNAPSHOT_GUID))
                            
                                szPartitionName = "SNAPSHOT";
                            
                            else
                            
                                sprintf(szPartitionName = PartitionName, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", 
                                    PartitionEntry->Gpt.PartitionType.Data1,
                                    PartitionEntry->Gpt.PartitionType.Data2,
                                    PartitionEntry->Gpt.PartitionType.Data3,
                                    PartitionEntry->Gpt.PartitionType.Data4[0],
                                    PartitionEntry->Gpt.PartitionType.Data4[1],
                                    PartitionEntry->Gpt.PartitionType.Data4[2],
                                    PartitionEntry->Gpt.PartitionType.Data4[3],
                                    PartitionEntry->Gpt.PartitionType.Data4[4],
                                    PartitionEntry->Gpt.PartitionType.Data4[5],
                                    PartitionEntry->Gpt.PartitionType.Data4[6],
                                    PartitionEntry->Gpt.PartitionType.Data4[7]);
                            
                            DbgPrint("[%s] %I64x \"%S\"", 
                                szPartitionName,
                                PartitionEntry->Gpt.Attributes,
                                PartitionEntry->Gpt.Name);
                            break;
                        

                        if (0 <= NtOpenFile(&hPartition, FILE_GENERIC_READ, &oa, &iosb,
                            FILE_SHARE_VALID_FLAGS, FILE_SYNCHRONOUS_IO_NONALERT))
                        
                            union 
                                BYTE bb[sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + 32*sizeof(WCHAR) ];
                                FILE_FS_ATTRIBUTE_INFORMATION ffai;
                            ;

                            switch (NtQueryVolumeInformationFile(hPartition, &iosb, &ffai, sizeof(bb), FileFsAttributeInformation))
                            
                            case STATUS_SUCCESS:
                            case STATUS_BUFFER_OVERFLOW:
                                DbgPrint(" \"%.*S\"\n", ffai.FileSystemNameLength >> 1 , ffai.FileSystemName);
                                break;
                            

                            NtClose(hPartition);
                        

                     while (PartitionEntry++, --PartitionCount);
                
                return ;
            

            switch (status)
            
            case STATUS_BUFFER_OVERFLOW:
                PartitionCount = pdli->PartitionCount;
                continue;
            case STATUS_INFO_LENGTH_MISMATCH:
            case STATUS_BUFFER_TOO_SMALL:
                PartitionCount <<= 1;
                continue;
            default:
                return ;
            
        
    

从另一个尺寸我们可以通过调用CM_Get_Device_Interface_PropertyWDEVPKEY_Device_InstanceId从接口字符串中得到Device Instance ID。之后我们调用CM_Locate_DevNodeW 来获取设备实例句柄。

CONFIGRET GetPropertyByInterface(PCSTR prefix, PCWSTR pszDeviceInterface)

    ULONG cb = 0, rcb = 256;

    PVOID stack = alloca(guz);
    DEVPROPTYPE PropertyType;

    CONFIGRET status;

    union 
        PVOID pv;
        PWSTR DeviceID;
        PBYTE pb;
    ;

    do 
    
        if (cb < rcb)
        
            rcb = cb = RtlPointerToOffset(pv = alloca(rcb - cb), stack);
        

        status = CM_Get_Device_Interface_PropertyW(pszDeviceInterface, &DEVPKEY_Device_InstanceId, &PropertyType, pb, &rcb, 0);

        if (status == CR_SUCCESS)
        
            if (PropertyType == DEVPROP_TYPE_STRING)
            
                DbgPrint("%sDeviceID = %S\n", prefix, DeviceID);

                DEVINST dnDevInst; 

                if (CR_SUCCESS == (status = CM_Locate_DevNodeW(&dnDevInst, DeviceID, CM_LOCATE_DEVNODE_NORMAL)))
                
                    GetPropertyByDeviceID(prefix, dnDevInst);
                
            
            else
            
                status = CR_WRONG_TYPE;
            

            break;
        

     while (status == CR_BUFFER_SMALL);

    return status;

使用设备实例句柄,我们可以通过CM_Get_DevNode_PropertyW 查询许多设备属性,例如: DEVPKEY_Device_LocationInfoDEVPKEY_NAMEDEVPKEY_Device_PDONameDEVPKEY_Device_FirmwareVersionDEVPKEY_Device_ModelDEVPKEY_Device_DriverVersion 和许多其他人 - 查看 devpkey.h 中的完整列表

最后我们可以调用CM_Get_Parent 并递归查询父设备的所有这些属性,直到我们没有丰富的栈顶:

#define OPEN_PDO

void GetPropertyByDeviceID(PCSTR prefix, DEVINST dnDevInst)

#ifdef OPEN_PDO
    HANDLE hFile;
    IO_STATUS_BLOCK iosb;
    UNICODE_STRING ObjectName;
    OBJECT_ATTRIBUTES oa =  sizeof(oa), 0, &ObjectName, OBJ_CASE_INSENSITIVE ;
#endif

    CONFIGRET status;

    ULONG cb = 0, rcb = 0x80;

    PVOID stack = alloca(guz);

    DEVPROPTYPE PropertyType;

    union 
        PVOID pv;
        PWSTR sz;
        PBYTE pb;
    ;

    static struct  
    
        CONST DEVPROPKEY *PropertyKey;
        PCWSTR PropertyName;
     PropertyKeys[] = 
         &DEVPKEY_Device_PDOName, L"PDOName",
         &DEVPKEY_Device_Parent, L"Parent",
         &DEVPKEY_Device_DriverVersion, L"DriverVersion",
         &DEVPKEY_Device_LocationInfo, L"LocationInfo",
         &DEVPKEY_Device_FirmwareVersion, L"FirmwareVersion",
         &DEVPKEY_Device_Model, L"Model",
         &DEVPKEY_NAME, L"NAME",
         &DEVPKEY_Device_InstanceId, L"DeviceID"
    ;

    do 
    
        int n = RTL_NUMBER_OF(PropertyKeys);

        do 
        
            CONST DEVPROPKEY *PropertyKey = PropertyKeys[--n].PropertyKey;

            do 
            
                if (cb < rcb)
                
                    rcb = cb = RtlPointerToOffset(pv = alloca(rcb - cb), stack);
                

                status = CM_Get_DevNode_PropertyW(dnDevInst, PropertyKey, &PropertyType, pb, &rcb, 0);

                if (status == CR_SUCCESS)
                
                    if (PropertyType == DEVPROP_TYPE_STRING)
                    
                        DbgPrint("%s%S=[%S]\n", prefix, PropertyKeys[n].PropertyName, sz);

#ifdef OPEN_PDO

                        if (!n)
                        
                            // DEVPKEY_Device_PDOName can use in NtOpenFile

                            RtlInitUnicodeString(&ObjectName, sz);

                            if (0 <= NtOpenFile(&hFile, FILE_READ_ATTRIBUTES|SYNCHRONIZE, &oa,
                                &iosb, FILE_SHARE_VALID_FLAGS, FILE_SYNCHRONOUS_IO_NONALERT))
                            
                                NtClose(hFile);
                            
                        
#endif
                    
                

             while (status == CR_BUFFER_SMALL);

         while (n);

        if (!*--prefix) break;

     while (CM_Get_Parent(&dnDevInst, dnDevInst, 0) == CR_SUCCESS);

还有DEVPKEY_Device_PDOName 返回的字符串,我们可以在NtOpenFile 调用打开 PDO 设备时使用。

【讨论】:

这与我之前遇到的问题相同。 CM_Get_Device_Interface_ListW 跳过属于 Microsoft 存储空间的磁盘。 +1,因为它充满了有用的指针。最终,如果在您在这里回答的另一个问题***.com/a/42439253/2631220 的帮助下弄清楚了,我在我的问题中使用了方法 3,并使用物理设备对象调用了 CreateFile(我不知道这是可能的)。 @AliZahid - 我不熟悉存储空间,但CM_Get_Device_Interface_ListW 没什么可跳过的。它准确返回实现接口的设备列表(通过 guid)。如果您没有某些设备 - 这意味着该设备未实现请求的接口。所以认为来自 SS(RAID,在软件中实现)的磁盘根本不实现GUID_DEVINTERFACE_DISK。你需要使用另一个接口来列出这个 @AliZahid - 例如存在GUID_DEVINTERFACE_VOLUME 接口但某些卷仅在GUID_DEVINTERFACE_HIDDEN_VOLUME 下列出.. 还请查看Get physical disk mapping for Win 8 Storage Spaces virtual disks.. 需要在winobj 和devicetree 中查找它的设备及其实现的接口

以上是关于启用存储空间时获取物理磁盘的信息的主要内容,如果未能解决你的问题,请参考以下文章

2017-2018-1 20155201 《信息安全系统设计基础》第十一周学习总结

操作系统王道考研 p52 文件存储空间管理

oracle -- 分区(extent)

20145207《信息安全系统设计基础》第14周学习总结

安卓获取多媒体(包括 视频音频图片)数据

物理卷相关