枚举所有分区并测试它们是否为NTFS

问题描述 投票:-1回答:2

我正在使用:

DWORD d = GetLogicalDrives();
for (int i = 0; i < 26; i++)
{
    if ((1 << i) & d) // drive letter 'A' + i present on computer
    {
        wstring s = std::wstring(L"\\\\.\\") + wchar_t('A' + i) + L":";

        PARTITION_INFORMATION diskInfo;
        DWORD dwResult;
        HANDLE dev = CreateFile(LPWSTR(s.c_str()), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);
        DeviceIoControl(dev, IOCTL_DISK_GET_PARTITION_INFO, NULL, 0, &diskInfo, sizeof(diskInfo), &dwResult, NULL);
        CloseHandle(dev);
        if (diskInfo.PartitionType == PARTITION_IFS) 
        {
            ...
        }
    }
}

枚举计算机的所有NTFS分区。

它适用于我的Windows 7,在Windows 8.1上我试过它,在Windows 10计算机上。

但它在另一台Windows 10计算机上失败:在这一台上,音量C:diskInfo.PartitionType值等于0x00,而不是0x07PARTITION_IFS)。

这个值是(参见doc here):

PARTITION_ENTRY_UNUSED:0x00:未使用的条目分区。

这很奇怪,因为我可以确认,分区确实是NTFS。

问题:

  • 众所周知IOCTL_DISK_GET_PARTITION_INFO获得分区类型并不是100%可靠吗?
  • 什么是更可靠的枚举所有NTFS卷的方法?

注意:我也看了使用IOCTL_DISK_GET_PARTITION_INFO_EX而不是IOCTL_DISK_GET_PARTITION_INFO,但结构PARTITION_INFORMATION_EX似乎没有给出关于PartitionType的信息,而结构PARTITION_INFORMATION确实提供了访问PartitionType

c++ windows winapi ntfs ntfs-mft
2个回答
1
投票

正如@RemyLebeau所说,您没有检查每个呼叫的返回值。

PARTITION_ENTRY_UNUSED通常意味着DeviceIoControl()呼叫失败。这取决于您的用户的权限。您应检查用户的访问权限,以查看它是否具有卷FILE_READ_DATA上的GENERIC_READ权限(包含在C:中)。在我的测试环境中,如果你无法使用C:访问开放量GENERIC_READCreateFile()将返回INVALID_HANDLE_VALUE,然后DeviceIoControl()也会失败。

编辑:

我建议使用GetVolumeInformation(),例如:

wchar_t fs[MAX_PATH + 1] = { 0 };
GetVolumeInformationW(L"C:\\", NULL, 0, NULL, NULL, NULL, fs, MAX_PATH + 1);

您将在fs缓冲区中看到Type信息。


1
投票

由于@ RemyLebeau的评论,我做了进一步的调查:

HANDLE dev = CreateFile(..., GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);

if (dev == INVALID_HANDLE_VALUE) 
{ 
    DWORD err = GetLastError();  // then MessageBox       
} 
else
{ 
    BOOL ret = DeviceIoControl(dev, IOCTL_DISK_GET_PARTITION_INFO, NULL, 0, &diskInfo, sizeof(diskInfo), &dwResult, NULL);

    if (ret == FALSE) 
    { 
        DWORD err = GetLastError();  // then MessageBox
    } 
    CloseHandle(dev); 
} 

在它失败的计算机上(使用Windows 10的计算机)。我发现CreateFile成功,但随后DeviceIoControl失败,GetLastError1,即ERROR_INVALID_FUNCTION(见System Error Codes (0-499))。

结论(我引用雷米的评论):

这意味着您传递给DeviceIoControl()的设备不支持IOCTL_DISK_GET_PARTITION_INFO。

然后我尝试使用IOCTL_DISK_GET_PARTITION_INFO_EX

PARTITION_INFORMATION_EX diskInfo;
BOOL ret = DeviceIoControl(dev, IOCTL_DISK_GET_PARTITION_INFO_EX, NULL, 0, &diskInfo, sizeof(diskInfo), &lpBytesReturned, NULL);

然后它奏效了。我可以看到diskInfo.PartitionStylePARTITION_STYLE_GPT(= 1),这就是IOCTL_DISK_GET_PARTITION_INFO失败的原因。我再次引用雷米的评论:

GPT分区驱动器不支持IOCTL_DISK_GET_PARTITION_INFO。

所以这是结论:

  • 使用IOCTL_DISK_GET_PARTITION_INFO_EX而不是IOCTL_DISK_GET_PARTITION_INFO 如果diskInfo.PartitionStyle为0(PARTITION_STYLE_MBR),则可以测试diskInfo.Mbr.PartitionType。 If it's 0x07, it's NTFS。 如果diskInfo.PartitionStyle为1(PARTITION_STYLE_GPT),则可以测试diskInfo.Gpt.PartitionType,请参见此处:https://docs.microsoft.com/en-us/windows/desktop/api/winioctl/ns-winioctl-_partition_information_gpt。即使NTFS Wikipedia page在GPT案例中提到了NTFS的GUID EBD0A0A2-B9E5-4433-87C0-68B6B72699C7,这个GUID实际上与文件系统无关(请参阅下面的评论)。
  • 它可能更容易使用GetVolumeInformation()而只是比较,如果结果是"NTFS"字符串,如在other answer
  • 在我的特定情况下,我最初想在尝试使用DeviceIoControl(hVol, FSCTL_ENUM_USN_DATA, ...)进行索引之前测试卷是否为NTFS,因为我认为这样的MFT查询将限于NTFS卷。事实上,一个更简单的解决方案是,如果它是NTFS,则不测试,只需做FSCTL_ENUM_USN_DATA。根据文档,可能发生的最糟糕的情况是FSCTL_ENUM_USN_DATAERROR_INVALID_FUNCTION错误而失败: “ERROR_INVALID_FUNCTION指定卷上的文件系统不支持此控制代码。”
© www.soinside.com 2019 - 2024. All rights reserved.