Получить размер тома в Windows
Я пишу библиотеку для извлечения информации о физических дисках, разделах и томах в системе 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
свойство согласовано, и оба на 4096 байт меньше значения из IOCTL_DISK_GET_LENGTH_INFO
,
Как правильно получить объемную емкость? Поскольку все остальные запросы работают без прав администратора, я ищу решение с наименьшими привилегиями для этого.
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>
- (J в {1,2,3..}) - символическая ссылка на некоторые \Device\HarddiskVolume<X>
)
1) Есть несколько способов получить емкость физического диска:
- а)
открыть любой из \Device\Harddisk<I>\Partition<J>
устройства (J в {0,1,..} - то есть диск FDO или любой раздел PDO) с (FILE_READ_ACCESS | FILE_WRITE_ACCESS)
и отправьте IOCTL_SCSI_PASS_THROUGH_DIRECT с SCSIOP_READ_CAPACITY
и / или SCSIOP_READ_CAPACITY16
- и мы получили 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 - должен быть такой же результат, как а) - этот дескриптор запроса ClassReadDriveCapacity
в classpnp.sys который отправляет внутренний запрос 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
- 2)
узнать емкость раздела на диске - откройте \Device\Harddisk<I>\Partition<J>
(где J в {1,2..}) или если буква X назначена разделу - \GLOBAL??\X:
и используйте IOCTL_DISK_GET_LENGTH_INFO. снова нужно 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);
}
- 3)
чтобы узнать емкость файловой системы на разделе - откройте любой файл (\GLOBAL??\X:\
например) и использовать NtQueryVolumeInformationFile( FileFsSizeInformation)
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);
}
или используйте GetDiskFreeSpaceEx - внутренне он также вызывает NtQueryVolumeInformationFile( FileFsSizeInformation)
но использует флаг FILE_DIRECTORY_FILE
, так что в качестве входного параметра вы можете использовать только каталоги