Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to extend a volume programmatically

Tags:

My requirement is to extend drive volume through program. When I used IOCTL_DISK_GROW_PARTITION in DeviceIO to extend it, the disk management shows the new modified size while the size of the drive in This PC (My Computer) remains unchanged.

   BOOL DeviceIoControl(
      (HANDLE) hDevice,            // handle to device
      IOCTL_DISK_GROW_PARTITION,   // dwIoControlCode
      (LPVOID) lpInBuffer,         // input buffer
      (DWORD) nInBufferSize,       // size of the input buffer
      NULL,                        // lpOutBuffer
      0,                           // nOutBufferSize 
      (LPDWORD) lpBytesReturned,   // number of bytes returned
      (LPOVERLAPPED) lpOverlapped  // OVERLAPPED structure
    );

Through some analysis I found that while using this API the MBR of the disk is modified but the cluster bitmap of drive is not changed. I want to know the correct way of using this DeviceIO to expand a volume or some other API to do the same process.

like image 504
Palanivel Avatar asked May 22 '17 09:05

Palanivel


People also ask

How do I extend an existing volume?

Select and hold (or right-click) the volume that you want to extend, and then select Extend Volume. If Extend Volume is grayed out, check the following: Disk Management or Computer Management was opened with administrator permissions.


1 Answers

need understand different between disk driver, which maintain info about disk layout and partitions (it size, offset from disk begin, style (gpt or mbr) ) and file system, which mount this partition.

IOCTL_DISK_GROW_PARTITION - this ioctl is handled by disk driver and extend partition, but this can not have effect for file system, which not handle this ioctl and have no knowledge at all that partition was extended. so you need additional ioctl use FSCTL_EXTEND_VOLUME - this ioctl already send and handle to file-system.

so if we have to do next steps

  1. send IOCTL_DISK_GROW_PARTITION with DISK_GROW_PARTITION as input buffer
  2. send IOCTL_DISK_UPDATE_DRIVE_SIZE with DISK_GEOMETRY as output buffer
  3. send IOCTL_DISK_GET_PARTITION_INFO_EX with PARTITION_INFORMATION_EX as output for get actual size of partition now.
  4. calculate new size of the volume, in sectors

    LONGLONG SectorsPerPartition = PartitionEntry->PartitionLength.QuadPart / dg.BytesPerSector;

    (dg we got at step 2 and PartitionEntry at step 3)

  5. finally use FSCTL_EXTEND_VOLUME

full code can be like next

int __cdecl SortPartitions(PPARTITION_INFORMATION_EX PartitionEntry1, PPARTITION_INFORMATION_EX PartitionEntry2)
{
    if (!PartitionEntry1->PartitionNumber) return PartitionEntry2->PartitionNumber ? -1 : 0;
    if (!PartitionEntry2->PartitionNumber) return +1;
    if (PartitionEntry1->StartingOffset.QuadPart < PartitionEntry2->StartingOffset.QuadPart) return -1;
    if (PartitionEntry1->StartingOffset.QuadPart > PartitionEntry2->StartingOffset.QuadPart) return +1;
    return 0;
}

DWORD ExtendTest(HANDLE hDisk)
{
    STORAGE_DEVICE_NUMBER sdn;

    ULONG dwBytesRet;

    if (!DeviceIoControl(hDisk, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0, &sdn, sizeof(sdn), &dwBytesRet, NULL))
    {
        return GetLastError();
    }

    if (sdn.DeviceType != FILE_DEVICE_DISK || sdn.PartitionNumber != 0)
    {
        return ERROR_GEN_FAILURE;
    }

    GET_LENGTH_INFORMATION gli;

    if (!DeviceIoControl(hDisk, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &gli, sizeof(gli), &dwBytesRet, NULL))
    {
        return GetLastError();
    }

    DbgPrint("Disk Length %I64x (%I64u)\n", gli.Length.QuadPart, gli.Length.QuadPart);

    PVOID stack = alloca(guz);

    union {
        PVOID buf;
        PDRIVE_LAYOUT_INFORMATION_EX pdli;
    };

    ULONG cb = 0, rcb, PartitionCount = 4;

    for (;;)
    {
        if (cb < (rcb = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry[PartitionCount])))
        {
            cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
        }

        if (DeviceIoControl(hDisk, IOCTL_DISK_GET_DRIVE_LAYOUT_EX, NULL, 0, buf, cb, &dwBytesRet, NULL))
        {
            if (PartitionCount = pdli->PartitionCount)
            {
                PPARTITION_INFORMATION_EX PartitionEntry = pdli->PartitionEntry;

                qsort(PartitionEntry, PartitionCount, sizeof(PARTITION_INFORMATION_EX), 
                    (int (__cdecl *)(const void *, const void *))SortPartitions );

                do 
                {
                    if (!PartitionEntry->PartitionNumber)
                    {
                        continue;
                    }

                    LARGE_INTEGER EndOffset; 
                    LARGE_INTEGER MaximumOffset = PartitionCount != 1 ? (PartitionEntry + 1)->StartingOffset : gli.Length;

                    EndOffset.QuadPart = PartitionEntry->StartingOffset.QuadPart + PartitionEntry->PartitionLength.QuadPart;

                    if (EndOffset.QuadPart > MaximumOffset.QuadPart)
                    {
                        //??
                        __debugbreak();
                    }
                    else if (EndOffset.QuadPart < MaximumOffset.QuadPart)
                    {
                        DISK_GROW_PARTITION dgp;
                        dgp.PartitionNumber = PartitionEntry->PartitionNumber;
                        dgp.BytesToGrow.QuadPart = MaximumOffset.QuadPart - EndOffset.QuadPart;

                        WCHAR sz[128];

                        swprintf(sz, L"\\\\?\\GLOBALROOT\\Device\\Harddisk%d\\Partition%u", sdn.DeviceNumber, dgp.PartitionNumber);

                        HANDLE hPartition = CreateFile(sz, FILE_READ_ACCESS|FILE_WRITE_ACCESS, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, 0, 0);

                        if (hPartition != INVALID_HANDLE_VALUE)
                        {  
                            // +++ begin extend
                            BOOL fOk = FALSE;

                            DISK_GEOMETRY dg;
                            if (DeviceIoControl(hPartition, IOCTL_DISK_GROW_PARTITION, &dgp, sizeof(dgp), 0, 0, &dwBytesRet, 0) &&
                                DeviceIoControl(hPartition, IOCTL_DISK_UPDATE_DRIVE_SIZE, 0, 0, &dg, sizeof(dg), &dwBytesRet, 0) &&
                                DeviceIoControl(hPartition, IOCTL_DISK_GET_PARTITION_INFO_EX, 0, 0, PartitionEntry, sizeof(*PartitionEntry), &dwBytesRet, 0)
                                )
                            {
                                LONGLONG SectorsPerPartition = PartitionEntry->PartitionLength.QuadPart / dg.BytesPerSector;

                                fOk = DeviceIoControl(hPartition, FSCTL_EXTEND_VOLUME, &SectorsPerPartition, 
                                    sizeof(SectorsPerPartition), 0, 0, &dwBytesRet, 0);

                            }

                            if (!fOk)
                            {
                                GetLastError();
                            }

                            //--- end extend
                            CloseHandle(hPartition);
                        }
                    }
                    // else EndOffset.QuadPart == MaximumOffset.QuadPart - partition can not be extended

                } while (PartitionEntry++, --PartitionCount);
            }

            return NOERROR;
        }

        switch (ULONG err = GetLastError())
        {
        case ERROR_MORE_DATA:
            PartitionCount = pdli->PartitionCount;
            continue;
        case ERROR_BAD_LENGTH:
        case ERROR_INSUFFICIENT_BUFFER:
            PartitionCount <<= 1;
            continue;
        default:
            return err;
        }
    }

}
DWORD ExtendTest()
{
    HANDLE hDisk = CreateFileW(L"\\\\?\\PhysicalDrive0", FILE_GENERIC_READ|FILE_GENERIC_WRITE, 
        FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, 0, 0);

    if (hDisk != INVALID_HANDLE_VALUE)
    {
        DWORD err = ExtendTest(hDisk);
        CloseHandle(hDisk);

        return err;
    }

    return GetLastError();
}
like image 113
RbMm Avatar answered Oct 11 '22 16:10

RbMm