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

Enumerate all partitions and test if they are NTFS

本文关键字:是否 NTFS 测试 分区 枚举      更新时间:2023-10-16

我正在使用:

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,而不是0x07(PARTITION_IFS)。

此值为(请参阅此处的文档):

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

由于@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失败了,GetLastError1ERROR_INVALID_FUNCTION(请参阅系统错误代码 (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。如果是0x07,那就是NTFS。

    • 如果diskInfo.PartitionStyle为 1 (PARTITION_STYLE_GPT),则可以测试 diskInfo.Gpt.PartitionType,请参阅此处:https://learn.microsoft.com/en-us/windows/desktop/api/winioctl/ns-winioctl-_partition_information_gpt。即使 NTFS 维基百科页面在 GPT 案例中提到了 NTFS 的 GUIDEBD0A0A2-B9E5-4433-87C0-68B6B72699C7,此 GUID 实际上与文件系统无关(请参阅下面的评论)。

  • 使用GetVolumeInformation()可能更容易,只需比较结果是否是"NTFS"字符串,如另一个答案

  • 在我的特定情况下,我最初想在尝试使用DeviceIoControl(hVol, FSCTL_ENUM_USN_DATA, ...)进行索引之前测试卷是否为 NTFS,因为我认为此类 MFT 查询仅限于 NTFS 卷。实际上,更简单的解决方案是不测试它是否是NTFS,而只是执行FSCTL_ENUM_USN_DATA。根据文档,可能发生的最糟糕的情况是FSCTL_ENUM_USN_DATA失败并出现ERROR_INVALID_FUNCTION错误:

    "ERROR_INVALID_FUNCTION指定卷上的文件系统不支持此控制代码。">

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

PARTITION_ENTRY_UNUSED通常意味着DeviceIoControl()调用失败。这取决于用户的权限。您应该检查用户的访问权限,以查看它是否具有卷C:FILE_READ_DATA权限(包含在GENERIC_READ中)。在我的测试环境中,如果您无法访问带有GENERIC_READ的打开卷C:CreateFile()返回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缓冲区中看到类型信息。