Get volume size on Windows

I am writing a library to extract information about physical disks, partitions, and volumes on a Windows system (XP or later).

I am trying to get the volume volume. Here are the approaches that I know of, and the reason why each of them fails:

Oddly enough, the number of clusters from the FSCTL_GET_VOLUME_BITMAP and WMI CIM_LogicalDisk.Size is consistent, and both of them are 4096 bytes less than the values ​​from IOCTL_DISK_GET_LENGTH_INFO .

What is the right way to get volumetric capacity? Since all other requests work without administrator access, I am also looking for a solution for least privileges.

+7
winapi disk-partitioning raid
source share
1 answer

What exactly do you want to receive?

  • 1) Physical disk capacity

    OR

  • 2) disk capacity

    OR

  • 3) file system capacity on a partition

There is a PDO for a physical disk, because disk.sys creates and attaches FDO ( \Device\Harddisk<I>\DR0 - name or \Device\Harddisk<I>\Partition0 - a symbolic link, where I have the disk number in 0,1,2 ..)

for each partition on the physical disk, disk.sys creates a PDO ( \Device\Harddisk<I>\Partition<J> - (J in {1,2,3 ..}) - a symbolic link to some \Device\HarddiskVolume<X> )

1) there are several ways to get the capacity of a physical disk:

  • but)

open any of the \Device\Harddisk<I>\Partition<J> devices (J in {0,1, ..} - so there is an FDO disk or any PDO partition) with (FILE_READ_ACCESS | FILE_WRITE_ACCESS) and send IOCTL_SCSI_PASS_THROUGH_DIRECT with SCSIOP_READ_CAPACITY and or SCSIOP_READ_CAPACITY16 - and we got SCSIOP_READ_CAPACITY or SCSIOP_READ_CAPACITY16 struct.

 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); } 

or

  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); } 
  • b)

open any of the \Device\Harddisk<I>\Partition<J> devices with FILE_READ_ACCESS and send IOCTL_STORAGE_READ_CAPACITY - there should be the same result as a) - this is the ClassReadDriveCapacity request ClassReadDriveCapacity in classpnp.sys that sends the SCSI ( SCSIOP_READ_CAPACITY ) request PDO drive. this method did not work on 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); } 
  • from)

open any of \Device\Harddisk<I>\Partition<J> with any access and send IOCTL_DISK_GET_DRIVE_GEOMETRY_EX and use DISK_GEOMETRY_EX.DiskSize . this is the best. no rights and work on XP are needed

 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); } 
  • d)

open \Device\Harddisk<I>\Partition0 or \Device\Harddisk<I>\DR0 with FILE_READ_ACCESS and use IOCTL_DISK_GET_LENGTH_INFO

  • 2)

to get the capacity of the partition on the disk - open \Device\Harddisk<I>\Partition<J> (where J is in {1,2 ..}) or if the X-letter is assigned to the partition - \GLOBAL??\X: and use IOCTL_DISK_GET_LENGTH_INFO . need FILE_READ_ACCESS again

 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)

to get the file system bandwidth in a section - open any file (for example, \GLOBAL??\X:\ ) and use 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); } 

or use GetDiskFreeSpaceEx - it also calls NtQueryVolumeInformationFile( FileFsSizeInformation) , but uses the FILE_DIRECTORY_FILE flag, since the input parameter allows only directories to be used

+1
source share

All Articles