c++ - 即使 OpenVirtualDisk 成功,QueryChangesVirtualDisk 仍返回 ERROR_INVALID_HANDLE(6)

标签 c++ wmi hyper-v vhd virtual-disk

我正在尝试 VirtualDisk API, 到目前为止,我可以打开 VHDX 文件,并使用 GetVirtualDiskInformation 获取一些属性。 但我无法获取 RCT 信息和 ChangedAreas。

第一次调用 GetVirtualDiskInformation 成功。 第二次调用 GetVirtualDiskInformation 失败,因为缓冲区不足 ERROR_INSUFFICIENT_BUFFER(122)。 对 QueryChangesVirtualDisk 的调用失败并显示 ERROR_INVALID_HANDLE(6)。 代码中硬编码的 RCT ID 是正确的,我可以使用 WMI Explorer 获取 ChangedAreas。 附上相同的屏幕截图。

如果它是无效句柄,那么 GetVirtualDiskInformation 也应该抛出相同的错误?


#include "pch.h"
#include <iostream>
#include <string>
#include <cstdlib>
#define WINVER _WIN32_WINNT_WIN10
#include <windows.h>
#include <winioctl.h>
#include <virtdisk.h>
#include <initguid.h>
#pragma comment(lib, "virtdisk.lib")

DEFINE_GUID(VIRTUAL_STORAGE_TYPE_VENDOR_MICROSOFT, 0xec984aec, 0xa0f9, 0x47e9, 0x90, 0x1f, 0x71, 0x41, 0x5a, 0x66, 0x34, 0x5b);
DEFINE_GUID(VIRTUAL_STORAGE_TYPE_VENDOR_UNKNOWN, 0x00000000, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);

int main()
{
    HANDLE vhdHandle;
    _VIRTUAL_STORAGE_TYPE storageType;
    storageType.DeviceId = VIRTUAL_STORAGE_TYPE_DEVICE_UNKNOWN;
    storageType.VendorId = VIRTUAL_STORAGE_TYPE_VENDOR_UNKNOWN;

    wchar_t path[] = L"C:\\Hyper-V\\Virtual Hard Disks\\Lacazette\\Windows2016.vhdx";
    VIRTUAL_DISK_ACCESS_MASK mask = VIRTUAL_DISK_ACCESS_GET_INFO;

    PGET_VIRTUAL_DISK_INFO diskInfo;
    ULONG diskInfoSize = sizeof(GET_VIRTUAL_DISK_INFO);
    std::wcout << "size of diskinfo structure " << diskInfoSize << std::endl;
    diskInfo = (PGET_VIRTUAL_DISK_INFO)malloc(diskInfoSize);
    if (diskInfo == NULL)
    {
        std::cout << "Failed to malloc disk info, ret=" << std::endl;
        return 0;
    }


    std::wcout << "Opening Virtual disk " << path << std::endl;
    DWORD res = OpenVirtualDisk(&storageType, path,
                                VIRTUAL_DISK_ACCESS_GET_INFO,
                                OPEN_VIRTUAL_DISK_FLAG_NO_PARENTS,
                                NULL,
                                &vhdHandle);

    if (res != ERROR_SUCCESS)
    {
        std::cout << "Failed to open disk, ret=" << res << std::endl;
        return 0;
    }

    diskInfo->Version = GET_VIRTUAL_DISK_INFO_SIZE;
    res = GetVirtualDiskInformation(vhdHandle, &diskInfoSize, diskInfo, NULL);
    if (res != ERROR_SUCCESS)
    {
        std::cout << "Failed to GET_VIRTUAL_DISK_INFO_SIZE, ret=" << res << std::endl;
    }
    long physicalSize = diskInfo->Size.PhysicalSize;
    long virtualSize = diskInfo->Size.VirtualSize;
    long sectorSize = diskInfo->Size.SectorSize;
    long blockSize = diskInfo->Size.BlockSize;
    std::wcout << "physicalSize :" << physicalSize << std::endl;
    std::wcout << "virtualSize :" << virtualSize << std::endl;
    std::wcout << "sectorSize :" << sectorSize << std::endl;
    std::wcout << "blockSize :" << blockSize << std::endl;

    diskInfo->Version = GET_VIRTUAL_DISK_INFO_CHANGE_TRACKING_STATE;

    res = GetVirtualDiskInformation(vhdHandle, &diskInfoSize, diskInfo, NULL);
    if (res != ERROR_SUCCESS)
    {
        std::cout << "Failed to GET_VIRTUAL_DISK_INFO_CHANGE_TRACKING_STATE, ret=" << res << std::endl;
    }
    std::cout << "\nrct id:" << diskInfo->ChangeTrackingState.MostRecentId << std::endl;

    std::cout << "\nQuerying for changed disk areas...\n" << std::endl;

    wchar_t rctId[] = L"rctX:c2eb01d9:ccb1:405d:acb6:f0e76d055906:00000001";
    ULONG64   byteOffset = 0L;
    ULONG64   byteLength = 19327352832;
    QUERY_CHANGES_VIRTUAL_DISK_RANGE* changedAreas = NULL;
    ULONG     rangeCount = 0L;
    ULONG64   processedLength = 0L;
    res = QueryChangesVirtualDisk(&vhdHandle, rctId, byteOffset, byteLength,
        QUERY_CHANGES_VIRTUAL_DISK_FLAG_NONE,
        changedAreas, &rangeCount, &processedLength);

    if (res != ERROR_SUCCESS)
    {
        std::cout << "Failed to get chanegd areas, ret=" << res << std::endl;
        if (vhdHandle != NULL)
        {
            CloseHandle(vhdHandle);
            std::cout << "closing handle!" <<std::endl;
        }
        return 0;
    }

    std::cout << "Total changed areas:" << rangeCount << std::endl;
    std::cout << "Total processed length:" << processedLength << std::endl;

    if (vhdHandle != NULL)
    {
        CloseHandle(vhdHandle);
    }
    return 0;
}

程序的输出:

enter image description here

wmi 资源管理器输出的屏幕截图。

enter image description here

最佳答案

这里有一些或多或少有效的东西..问题在于我传递给 QueryChangesVirtualDisk API 的 rctid 的格式。应如下面的示例所示。我将其括在大括号 {} 内,这是不正确的。下面的代码成功返回RCT更改的 block

另一个观察结果是,我们从 GetVirtualDiskInformation 获取的离线 vhdx 的 ChangeTrackingState.MostRecentId 似乎比我们最初在虚拟机启动时创建的内容多了一个。例如,我使用 rctid 创建了一个引用点:rctX: 4abdf273:7e45:4748:85af:78ec4af82ebf:00000000 现在,当 vhdx 离线时,如果我查询 ChangeTrackingState.MostRecentId,它会给我这个: rctX:4abdf273:7e45:4748:85af:78ec4af82ebf:00000001 失败QueryChangesVirtualDisk 无效rctid 错误。下面的函数期望用户从外部传递正确的 rctid。显然,Get-VmReferencePoints 仍然只列出较早的 00000 引用点。这个 (00001) 似乎仅由 GetVirtualDiskInformation API 返回

enter image description here

最后,对于实时虚拟机访问被拒绝的另一个问题,我们似乎必须将 OpenVirtualDisk 中的 VIRTUAL_DISK_ACCESS_MASK 设置为 0 以及 OPEN_VIRTUAL_DISK_PARAMETERS 的版本 3,如下所示。这样我们就可以从实时检查点虚拟机的基础磁盘查询 RCT CBT。从 https://github.com/cloudbase/rct-service/issues/1 得到澄清

#include <windows.h>
#include <virtdisk.h>
#include <iostream>

bool QueryRCTData(const std::wstring& file, const std::wstring& rctid)
{
  DWORD fRet;
  HANDLE hVHD;

  VIRTUAL_STORAGE_TYPE vst = 
  {
    VIRTUAL_STORAGE_TYPE_DEVICE_UNKNOWN,
    {0xEC984AEC, 0xA0F9, 0x47e9, { 0x90, 0x1F, 0x71, 0x41, 0x5A, 0x66, 0x34, 0x5B }}
  };

OPEN_VIRTUAL_DISK_PARAMETERS params = { 0 };
params.Version = OPEN_VIRTUAL_DISK_VERSION_3;

DWORD fRet = OpenVirtualDisk(
         &vst,
         file.c_str(),
         (VIRTUAL_DISK_ACCESS_MASK) 0, //VIRTUAL_DISK_ACCESS_ALL, //VIRTUAL_DISK_ACCESS_GET_INFO,
         OPEN_VIRTUAL_DISK_FLAG_NONE,
         &params,
         &hvhd);

  if (fRet != ERROR_SUCCESS)
  {
    std::cout << "OpenVirtualDisk failed " << fRet << "\n";
    return false;
  }
  
  //query rctid 
  WCHAR vdinfo[2048];
  ZeroMemory(vdinfo, sizeof(vdinfo));
  ((GET_VIRTUAL_DISK_INFO *)vdinfo)->Version = GET_VIRTUAL_DISK_INFO_CHANGE_TRACKING_STATE;
  ULONG vdinfoSize = sizeof(vdinfo);

  fRet = GetVirtualDiskInformation(
           hVHD,
           &vdinfoSize,
           (PGET_VIRTUAL_DISK_INFO)vdinfo,
           NULL);

  if (fRet != ERROR_SUCCESS)
  {
    std::cout << "GetVirtualDiskInformation GET_VIRTUAL_DISK_INFO_CHANGE_TRACKING_STATE failed " << fRet << "\n";
    return false;
  }

  std::wcout << "RCT ID : " << std::wstring(((GET_VIRTUAL_DISK_INFO *)vdinfo)->ChangeTrackingState.MostRecentId) << "\n";
  
  //query disk length 
  ZeroMemory(vdinfo, sizeof(vdinfo));
  ((GET_VIRTUAL_DISK_INFO *)vdinfo)->Version = GET_VIRTUAL_DISK_INFO_SIZE;
  vdinfoSize = sizeof(vdinfo);

  fRet = GetVirtualDiskInformation(
           hVHD,
           &vdinfoSize,
           (PGET_VIRTUAL_DISK_INFO)vdinfo,
           NULL);

  if (fRet != ERROR_SUCCESS)
  {
    std::cout << "GetVirtualDiskInformation GET_VIRTUAL_DISK_INFO_SIZE failed " << fRet << "\n";
    return false;
  }

  std::cout << "Disk length : " << ((GET_VIRTUAL_DISK_INFO *)vdinfo)->Size.VirtualSize << "\n";

  ULONG RangeCount = 1024;
  ULONG64 ProcessedLength = 0;
  QUERY_CHANGES_VIRTUAL_DISK_RANGE Ranges[1024] = { 0 };

  fRet = QueryChangesVirtualDisk(
            hVHD,
            rctid.c_str(), //((GET_VIRTUAL_DISK_INFO *)vdinfo)->ChangeTrackingState.MostRecentId,
            0,
            ((GET_VIRTUAL_DISK_INFO *)vdinfo)->Size.VirtualSize,
            QUERY_CHANGES_VIRTUAL_DISK_FLAG_NONE,
            Ranges,
            &RangeCount,
            &ProcessedLength);

  if (fRet != ERROR_SUCCESS)
  {
    std::cout << "QueryChangesVirtualDisk failed " << fRet << "\n";
    return false;
  }

  std::cout << "RangeCount : " << RangeCount << "\n";
  std::cout << "ProcessedLength : " << ProcessedLength << "\n";

  for (int i = 0; i < RangeCount; i++)
  {
    std::cout << "Range offset : " << Ranges[i].ByteOffset << "\n";
    std::cout << "Range length : " << Ranges[i].ByteLength << "\n";
  }

  CloseHandle(hVHD);

  return true;
}

int main(void)
{
  QueryRCTData(L"D:\\BACKUP\\INCR\\Virtual Hard Disks\\2019.vhdx", L"rctX:4abdf273:7e45:4748:85af:78ec4af82ebf:00000000");
  return 0;
}

#pragma comment(lib, "virtdisk.lib")

关于c++ - 即使 OpenVirtualDisk 成功,QueryChangesVirtualDisk 仍返回 ERROR_INVALID_HANDLE(6),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63358492/

相关文章:

copy-paste - 无法从机器复制和粘贴到虚拟机 (Hyper-V) 并返回

c++ - c 向我的编辑器添加自动建议

c++ - Doxygen - 如何在不生成图表的情况下生成文本调用关系结果

c++ - 从自定义 printf 函数返回 const char*

c# - WMI Process Watching 使用过多的 CPU!有什么更好的方法吗?

C# 访问 ManagementObjectCollection 中的管理对象

android - Hyper-V 和 HAXM

virtual-machine - 如何在没有任何第三方工具的情况下部署现有的 VHD?

c++ - Windows 的 timespec 等价物

python - 在 Windows 10 上导入 win32api 时出现 DLL 错误