如何以编程方式扩展卷

Pal*_*vel 5 c windows winapi ntfs

我的要求是通过程序扩展驱动器音量。当我在 DeviceIO 中使用 IOCTL_DISK_GROW_PARTITION 扩展它时,磁盘管理显示新修改的大小,而这台电脑(我的电脑)中的驱动器大小保持不变。

   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
    );
Run Code Online (Sandbox Code Playgroud)

通过一些分析,我发现在使用这个API时,磁盘的MBR被修改了,但驱动器的簇位图并没有改变。我想知道使用此 DeviceIO 扩展卷或其他一些 API 来执行相同过程的正确方法。

RbM*_*bMm 4

需要了解磁盘驱动程序和文件系统之间的区别,磁盘驱动程序维护有关磁盘布局和分区的信息(它的大小、距磁盘开始的偏移量、样式(gpt 或 mbr))和安装此分区的文件系统。

IOCTL_DISK_GROW_PARTITION- 此 ioctl 由磁盘驱动程序和扩展分区处理,但这对文件系统不起作用,文件系统不处理此 ioctl 并且根本不知道分区已扩展。所以你需要额外的 ioctl 使用FSCTL_EXTEND_VOLUME- 这个 ioctl 已经发送并处理到文件系统。

所以如果我们必须采取下一步行动

  1. IOCTL_DISK_GROW_PARTITION作为 DISK_GROW_PARTITION输入缓冲区发送
  2. IOCTL_DISK_UPDATE_DRIVE_SIZE作为DISK_GEOMETRY 输出缓冲区发送
  3. 发送IOCTL_DISK_GET_PARTITION_INFO_EX作为 PARTITION_INFORMATION_EX输出以立即获取分区的实际大小。
  4. 计算卷的新大小(以扇区为单位)

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

    (我们在步骤 2 中得到dg ,在步骤 3 中得到PartitionEntry

  5. 最后使用FSCTL_EXTEND_VOLUME

完整的代码可以像下一个一样

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();
}
Run Code Online (Sandbox Code Playgroud)