in host/common/win32/portablehelpersmajor.cpp [2663:2822]
SVERROR GetVolumeSize(ACE_HANDLE volumeHandle,
unsigned long long* volumeSize)
{
SVERROR sve = SVS_OK;
#define MAXIMUM_SECTORS_ON_VOLUME (1024i64 * 1024i64 * 1024i64 * 1024i64)
#define DISK_SECTOR_SIZE (512)
PVOLUME_DISK_EXTENTS pDiskExtents; // dynamically sized allocation based on number of extents
GET_LENGTH_INFORMATION lengthInfo;
DWORD bytesReturned;
DWORD returnBufferSize;
BOOLEAN fSuccess;
LONGLONG currentGap;
LONGLONG currentOffset;
LARGE_INTEGER largeInt;
PUCHAR sectorBuffer;
// try a simple IOCTl to get size, should work on WXP and later
fSuccess = DeviceIoControl(volumeHandle,
IOCTL_DISK_GET_LENGTH_INFO,
NULL,
0,
&lengthInfo,
sizeof(lengthInfo),
&bytesReturned,
NULL);
#if (PRINT_SIZES)
printf("Volume size via IOCTL_DISK_GET_LENGTH_INFO is hex:%I64X or decimal:%I64i\n", lengthInfo.Length.QuadPart, lengthInfo.Length.QuadPart);
#endif
#if (TEST_GET_EXTENTS || TEST_BINARY_SEARCH || TEST_COMPLETE_BINARY_SEARCH)
fSuccess = FALSE;
#endif
if (fSuccess) {
*volumeSize = lengthInfo.Length.QuadPart;
return SVS_OK;
}
// next attempt, see if the volume is only 1 extent, if yes use it
returnBufferSize = sizeof(VOLUME_DISK_EXTENTS);
pDiskExtents = (PVOLUME_DISK_EXTENTS) new UCHAR[returnBufferSize];
fSuccess = DeviceIoControl(volumeHandle,
IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
NULL,
0,
pDiskExtents,
returnBufferSize,
&bytesReturned,
NULL);
#if (PRINT_SIZES)
printf("Volume size via IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS is hex:%I64X or decimal:%I64i\n", pDiskExtents->Extents[0].ExtentLength.QuadPart, pDiskExtents->Extents[0].ExtentLength.QuadPart);
#endif
#if (TEST_BINARY_SEARCH || TEST_COMPLETE_BINARY_SEARCH)
fSuccess = FALSE;
#endif
if (fSuccess) {
// must only have 1 extent as the buffer only has space for one
assert(pDiskExtents->NumberOfDiskExtents == 1);
*volumeSize = pDiskExtents->Extents[0].ExtentLength.QuadPart;
delete pDiskExtents;
return SVS_OK;
}
// use harder ways of finding the size
sve = GetLastError();
#if (TEST_BINARY_SEARCH)
sve = ERROR_MORE_DATA;
#endif
if (ERROR_MORE_DATA != sve.error.hr) {
// some error other than buffer too small happened
delete pDiskExtents;
return sve;
}
// has multiple extents
DWORD nde;
INM_SAFE_ARITHMETIC(nde = InmSafeInt<DWORD>::Type(pDiskExtents->NumberOfDiskExtents) - 1, INMAGE_EX(pDiskExtents->NumberOfDiskExtents))
INM_SAFE_ARITHMETIC(returnBufferSize = sizeof(VOLUME_DISK_EXTENTS) + (nde * InmSafeInt<size_t>::Type(sizeof(DISK_EXTENT))), INMAGE_EX(sizeof(VOLUME_DISK_EXTENTS))(nde)(sizeof(DISK_EXTENT)))
delete pDiskExtents;
pDiskExtents = (PVOLUME_DISK_EXTENTS) new UCHAR[returnBufferSize];
fSuccess = DeviceIoControl(volumeHandle,
IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
NULL, 0,
pDiskExtents, returnBufferSize,
&bytesReturned,
NULL);
#if (TEST_COMPLETE_BINARY_SEARCH)
fSuccess = FALSE;
#endif
if (!fSuccess) {
currentOffset = MAXIMUM_SECTORS_ON_VOLUME; // if IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS failed, do a binary search with a large limit
}
else {
// must be multiple extents, so will have to binary search last valid sector
currentOffset = 0;
for (unsigned int i = 0; i < pDiskExtents->NumberOfDiskExtents; i++) {
currentOffset += pDiskExtents->Extents[i].ExtentLength.QuadPart;
}
// scale things now so we don't need to divide on every read
currentOffset += DISK_SECTOR_SIZE; // these two are needed to make the search algorithm work
currentOffset /= DISK_SECTOR_SIZE;
}
delete pDiskExtents;
// the search gap MUST be a power of two, so find an appropriate value
currentGap = 1i64;
while ((currentGap * 2i64) < currentOffset) {
currentGap *= 2i64;
}
// we are all ready to binary search for the last valid sector
sectorBuffer = (PUCHAR)VirtualAlloc(NULL, DISK_SECTOR_SIZE, MEM_COMMIT, PAGE_READWRITE);
do {
largeInt.QuadPart = currentOffset * DISK_SECTOR_SIZE;
SetFilePointerEx(volumeHandle,
largeInt,
NULL,
FILE_BEGIN);
fSuccess = ReadFile(volumeHandle,
sectorBuffer,
DISK_SECTOR_SIZE,
&bytesReturned,
NULL);
if (fSuccess && (bytesReturned == DISK_SECTOR_SIZE)) {
currentOffset += currentGap;
if (currentGap == 0) {
*volumeSize = (currentOffset * DISK_SECTOR_SIZE) + DISK_SECTOR_SIZE;
sve = SVS_OK;
break;
}
}
else {
currentOffset -= currentGap;
if (currentGap == 0) {
*volumeSize = currentOffset * DISK_SECTOR_SIZE;
sve = SVS_OK;
break;
}
}
currentGap /= 2;
} while (TRUE);
VirtualFree(sectorBuffer, DISK_SECTOR_SIZE, MEM_DECOMMIT);
#if (PRINT_SIZES)
printf("Volume size via binary search is hex:%I64X or decimal:%I64i\n", *volumeSize, *volumeSize);
#endif
return sve;
}