我正在编写一个库来提取有关Windows系统(XP或更高版本)上的物理磁盘,分区和卷的信息。
我正在努力获得音量。以下是我所知道的方法以及每种方法失败的原因:
GetDiskFreeSpaceEx
- 受用户配额影响。IOCTL_DISK_GET_DRIVE_GEOMETRY_EX
- 获取整个物理磁盘的大小,即使使用卷句柄调用也是如此。IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS
- 不考虑RAID开销。IOCTL_DISK_GET_LENGTH_INFO
- 拒绝访问失败。 (实际上,它需要GENERIC_READ
访问权限,与所有其他查询不同,GENERIC_READ
需要管理员权限。)IOCTL_STORAGE_READ_CAPACITY
- 在XP上不可用,也有IOCTL_DISK_GET_LENGTH_INFO
和IOCTL_DISK_GET_DRIVE_GEOMETRY_EX
FSCTL_GET_VOLUME_BITMAP
+ GetFreeDiskSpace
了解群集大小 - 需要GENERIC_READ
(管理员权限),并提供文件系统数据区域的大小,而不是整个卷。IOCTL_DISK_GET_PARTITION_INFO
- 需要GENERIC_READ
(管理员访问权限),并且在连接USB的磁盘上也可能失败(可能使用超级软盘分区)奇怪的是,来自FSCTL_GET_VOLUME_BITMAP
和WMI的CIM_LogicalDisk.Size
属性的群集数量一致,并且都比IOCTL_DISK_GET_LENGTH_INFO
的值小4096字节。
获得容量的正确方法是什么?由于所有其他查询在没有管理员访问权限的情况下工作,我也在寻找最少权限的解决方案。
答案 0 :(得分:1)
你到底想要得到什么?
1)物理磁盘容量
或者
2)磁盘上分区的容量
或者
3)文件系统在分区上的容量
物理磁盘有PDO,因为它disk.sys创建并附加FDO(\Device\Harddisk<I>\DR0
- 名称或\Device\Harddisk<I>\Partition0
- 符号链接,其中我的磁盘号在0,1,2 ..)
为物理磁盘上的每个分区disk.sys创建PDO(\Device\Harddisk<I>\Partition<J>
- ({1,2,3 ..}中的J) - 符号链接到某些\Device\HarddiskVolume<X>
)
1)有几种方法可以获得物理磁盘容量:
打开任何\Device\Harddisk<I>\Partition<J>
设备({0,1,..}中的J - 所以磁盘FDO或任何分区PDO)
使用(FILE_READ_ACCESS | FILE_WRITE_ACCESS)
并使用SCSIOP_READ_CAPACITY
和/或SCSIOP_READ_CAPACITY16
发送IOCTL_SCSI_PASS_THROUGH_DIRECT - 我们获得SCSIOP_READ_CAPACITY
或SCSIOP_READ_CAPACITY16
结构。
READ_CAPACITY_DATA_EX rcd;
SCSI_PASS_THROUGH_DIRECT sptd = {
sizeof(sptd), 0, 0, 0, 0, CDB12GENERIC_LENGTH, 0, SCSI_IOCTL_DATA_IN,
sizeof(rcd), 1, &rcd, 0, {SCSIOP_READ_CAPACITY16}
};
if (0 <= NtDeviceIoControlFile(hFile, 0, 0, 0, &iosb, IOCTL_SCSI_PASS_THROUGH_DIRECT,
&sptd, sizeof(sptd), &sptd, sizeof(sptd)))
{
DbgPrint("---- SCSIOP_READ_CAPACITY16 ----\n");
rcd.BytesPerBlock = _byteswap_ulong(rcd.BytesPerBlock);
rcd.LogicalBlockAddress.QuadPart = _byteswap_uint64(rcd.LogicalBlockAddress.QuadPart) + 1;
DbgPrint("%I64x %x\n", rcd.LogicalBlockAddress, rcd.BytesPerBlock);
rcd.LogicalBlockAddress.QuadPart *= rcd.BytesPerBlock;
DbgPrint("%I64x %I64u\n", rcd.LogicalBlockAddress.QuadPart, rcd.LogicalBlockAddress.QuadPart);
}
或
READ_CAPACITY_DATA rcd;
SCSI_PASS_THROUGH_DIRECT sptd = {
sizeof(sptd), 0, 0, 0, 0, CDB10GENERIC_LENGTH, 0, SCSI_IOCTL_DATA_IN,
sizeof(rcd), 1, &rcd, 0, {SCSIOP_READ_CAPACITY}
};
if (0 <= NtDeviceIoControlFile(hFile, 0, 0, 0, &iosb, IOCTL_SCSI_PASS_THROUGH_DIRECT,
&sptd, sizeof(sptd), &sptd, sizeof(sptd)))
{
DbgPrint("---- SCSIOP_READ_CAPACITY ----\n");
rcd.BytesPerBlock = _byteswap_ulong(rcd.BytesPerBlock);
rcd.LogicalBlockAddress = _byteswap_ulong(rcd.LogicalBlockAddress) + 1;
DbgPrint("%x %x\n", rcd.LogicalBlockAddress, rcd.BytesPerBlock);
ULARGE_INTEGER u = {rcd.LogicalBlockAddress};
u.QuadPart *= rcd.BytesPerBlock;
DbgPrint("%I64x %I64u\n", u.QuadPart, u.QuadPart);
}
使用\Device\Harddisk<I>\Partition<J>
打开任何FILE_READ_ACCESS
设备并发送IOCTL_STORAGE_READ_CAPACITY - 必须与a的结果相同 - 此classpnp.sys中的请求句柄ClassReadDriveCapacity
内部将SCSI请求(SCSIOP_READ_CAPACITY
)发送到磁盘PDO。这种方式在XP上无效。
STORAGE_READ_CAPACITY sc;
if (0 <= NtDeviceIoControlFile(hFile, 0, 0, 0, &iosb, IOCTL_STORAGE_READ_CAPACITY, 0, 0, &sc, sizeof(sc)))
{
DbgPrint("---- IOCTL_STORAGE_READ_CAPACITY ----\n");
DbgPrint("%I64x %I64x %x \n", sc.DiskLength.QuadPart, sc.NumberOfBlocks.QuadPart, sc.BlockLength);
sc.NumberOfBlocks.QuadPart *= sc.BlockLength;
DbgPrint("%I64x %I64u\n", sc.NumberOfBlocks.QuadPart, sc.NumberOfBlocks.QuadPart);
}
以任意访问权限打开任何\Device\Harddisk<I>\Partition<J>
并发送IOCTL_DISK_GET_DRIVE_GEOMETRY_EX并使用DISK_GEOMETRY_EX.DiskSize
。这是最好的方式。不需要任何权利并在XP上工作
DISK_GEOMETRY_EX GeometryEx;
if (0 <= NtDeviceIoControlFile(hFile, 0, 0, 0, &iosb, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, 0, 0, &GeometryEx, sizeof(GeometryEx)))
{
DbgPrint("---- IOCTL_DISK_GET_DRIVE_GEOMETRY ----\n");
ULONG BytesPerCylinder = GeometryEx.Geometry.TracksPerCylinder * GeometryEx.Geometry.SectorsPerTrack * GeometryEx.Geometry.BytesPerSector;
DbgPrint("%I64x == %I64x\n", GeometryEx.Geometry.Cylinders.QuadPart, GeometryEx.DiskSize.QuadPart / BytesPerCylinder);
DbgPrint("%I64x <= %I64x\n", GeometryEx.Geometry.Cylinders.QuadPart * BytesPerCylinder, GeometryEx.DiskSize.QuadPart);
}
使用\Device\Harddisk<I>\Partition0
打开\Device\Harddisk<I>\Dr0
或FILE_READ_ACCESS
并使用IOCTL_DISK_GET_LENGTH_INFO
获取磁盘上分区的容量 - 打开\Device\Harddisk<I>\Partition<J>
(其中J在{1,2 ..}中)或者如果X字母分配给分区 - \GLOBAL??\X:
并使用{{3} }。再次需要FILE_READ_ACCESS
GET_LENGTH_INFORMATION gli;
if (0 <= NtDeviceIoControlFile(hFile, 0, 0, 0, &iosb, IOCTL_DISK_GET_LENGTH_INFO, 0, 0, &gli, sizeof(gli)))
{
DbgPrint("---- IOCTL_DISK_GET_LENGTH_INFO ----\n");
DbgPrint("%I64x %I64u\n", gli.Length.QuadPart, gli.Length.QuadPart);
}
获取分区上文件系统的容量 - 打开任何文件(例如\GLOBAL??\X:\
)并使用IOCTL_DISK_GET_LENGTH_INFO(NtQueryVolumeInformationFile)
FILE_FS_SIZE_INFORMATION fsi;
if (0 <= NtOpenFile(&hFile, SYNCHRONIZE, &oa, &iosb, FILE_SHARE_VALID_FLAGS, FILE_OPEN_FOR_FREE_SPACE_QUERY|FILE_SYNCHRONOUS_IO_NONALERT))
{
if (0 <= NtQueryVolumeInformationFile(hFile, &iosb, &fsi, sizeof(fsi), FileFsSizeInformation))
{
DbgPrint("%I64x %x %x\n", fsi.TotalAllocationUnits.QuadPart, fsi.SectorsPerAllocationUnit, fsi.BytesPerSector);
fsi.TotalAllocationUnits.QuadPart *= fsi.SectorsPerAllocationUnit * fsi.BytesPerSector;
DbgPrint("%I64x %I64u\n", fsi.TotalAllocationUnits.QuadPart, fsi.TotalAllocationUnits.QuadPart);
}
NtClose(hFile);
}
或使用FileFsSizeInformation - 内部也调用NtQueryVolumeInformationFile( FileFsSizeInformation)
但使用标记FILE_DIRECTORY_FILE
,因此输入参数只能使用目录