Получить размер тома в 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, так что в качестве входного параметра вы можете использовать только каталоги

Другие вопросы по тегам