I'm writing a library to extract information about physical disks, partitions, and volumes on a Windows system (XP or later).
I'm trying to get the capacity of a volume. Here are the approaches I know about and the reason each fails:
GetDiskFreeSpaceEx
-- Affected by user quota.IOCTL_DISK_GET_DRIVE_GEOMETRY_EX
-- Gets size of entire physical disk, even when invoked using a volume handle.IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS
-- Doesn't account for RAID overhead.IOCTL_DISK_GET_LENGTH_INFO
-- Fails with access denied. (Actually, it requiresGENERIC_READ
access, unlike all other queries, andGENERIC_READ
requires administrator access.)IOCTL_STORAGE_READ_CAPACITY
-- Not available on XP, also shares the drawbacks ofIOCTL_DISK_GET_LENGTH_INFO
andIOCTL_DISK_GET_DRIVE_GEOMETRY_EX
FSCTL_GET_VOLUME_BITMAP
+GetFreeDiskSpace
for cluster size -- RequiresGENERIC_READ
(admin access) and gives the size of the data area of the filesystem, not the entire volume.IOCTL_DISK_GET_PARTITION_INFO
-- RequiresGENERIC_READ
(admin access) and also failed on a USB-attached disk (possibly using superfloppy partitioning)
Oddly, the number of clusters from FSCTL_GET_VOLUME_BITMAP
and WMI's CIM_LogicalDisk.Size
property agree, and both are 4096 bytes smaller than the value from IOCTL_DISK_GET_LENGTH_INFO
.
What is the correct way to get volume capacity? Since all the other queries work without administrator access, I'm looking for a least-privilege solution for this too.
What exactly do you want to get?
1) Physical Disk capacity
OR
2) capacity of the Partition on the Disk
OR
3) capacity of the File System on the Partition
There is PDO for Physical Disk, for it disk.sys creates and attaches FDO (
\Device\Harddisk<I>\DR0
- name or\Device\Harddisk<I>\Partition0
- symbolick link, where I disk number in 0,1,2..)for every Partition on Physical Disk disk.sys creates PDO (
\Device\Harddisk<I>\Partition<J>
- (J in {1,2,3..}) - symlink to some\Device\HarddiskVolume<X>
)1) there are several ways to get Physical Disk capacity:
open any of
\Device\Harddisk<I>\Partition<J>
devices (J in {0,1,..} - so disk FDO or any partition PDO) with(FILE_READ_ACCESS | FILE_WRITE_ACCESS)
and send IOCTL_SCSI_PASS_THROUGH_DIRECT withSCSIOP_READ_CAPACITY
and/orSCSIOP_READ_CAPACITY16
- and we gotSCSIOP_READ_CAPACITY
orSCSIOP_READ_CAPACITY16
struct.or
open any of
\Device\Harddisk<I>\Partition<J>
devices withFILE_READ_ACCESS
and send IOCTL_STORAGE_READ_CAPACITY - must be the same result as a) - this request handleClassReadDriveCapacity
in classpnp.sys wich internal send SCSI request (SCSIOP_READ_CAPACITY
) to disk PDO. this way not worked on XP.open any of
\Device\Harddisk<I>\Partition<J>
with any access and send IOCTL_DISK_GET_DRIVE_GEOMETRY_EX and useDISK_GEOMETRY_EX.DiskSize
. this think the best way. not need any rights and work on XPopen
\Device\Harddisk<I>\Partition0
or\Device\Harddisk<I>\Dr0
withFILE_READ_ACCESS
and use IOCTL_DISK_GET_LENGTH_INFOto get capacity of the Partition on the Disk - open
\Device\Harddisk<I>\Partition<J>
(where J in {1,2..} ) or if X letter assigned to partition -\GLOBAL??\X:
and use IOCTL_DISK_GET_LENGTH_INFO. again needFILE_READ_ACCESS
to get capacity of the File System on the Partition - open any file (
\GLOBAL??\X:\
for example) and use NtQueryVolumeInformationFile(FileFsSizeInformation)or use GetDiskFreeSpaceEx - internally it also calls
NtQueryVolumeInformationFile( FileFsSizeInformation)
but uses flagFILE_DIRECTORY_FILE
, so as input parameter you can use only directories