简体   繁体   中英

QueryChangesVirtualDisk is returning ERROR_INVALID_HANDLE(6) even though OpenVirtualDisk is succesfull

I am trying out VirtualDisk APIs, So far I am able to Open a VHDX file, get some of the properties by using GetVirtualDiskInformation. But I am not able to get RCT information and ChangedAreas.

The first call to GetVirtualDiskInformation is successful. The second call to GetVirtualDiskInformation fails with insufficient buffer ERROR_INSUFFICIENT_BUFFER(122). Call to QueryChangesVirtualDisk fails with ERROR_INVALID_HANDLE(6). The RCT ID hardcoded in the code is proper, I am able to get the ChangedAreas using WMI Explorer. Attached screenshot of the same.

If it is invalid handle, then GetVirtualDiskInformation should also throw the same error?


#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;
}

Output of the program:

在此处输入图像描述

Screenshot of wmi explorer output.

在此处输入图像描述

Here's something that more or less works.. the issue was with the format of the rctid which I was passing to the QueryChangesVirtualDisk API. Should be as in the below sample. I was enclosing it inside the braces {} which was not correct. The below code returns RCT changed blocks successfully

Another observation is that it seems the ChangeTrackingState.MostRecentId which we get from GetVirtualDiskInformation for a offline vhdx is one more than what we initially created while the VM was up.. For example, I created a reference point with rctid: rctX:4abdf273:7e45:4748:85af:78ec4af82ebf:00000000 Now when the vhdx is offline and if I query the ChangeTrackingState.MostRecentId it is giving me this: rctX:4abdf273:7e45:4748:85af:78ec4af82ebf:00000001 which fails the QueryChangesVirtualDisk with invalid rctid error. The below function expects the user to pass the correct rctid externally. Apparently, the Get-VmReferencePoints still lists only the earlier 00000 reference point.. This one (00001) seems to be returned only by the GetVirtualDiskInformation API

在此处输入图像描述

Finally, for the other issue of getting access denied with a live VM, it seems we have to set the VIRTUAL_DISK_ACCESS_MASK in OpenVirtualDisk to 0 along with version 3 of OPEN_VIRTUAL_DISK_PARAMETERS as shown below. With this we we are able to query RCT CBT from a live checkpointed VM's base disk. Got clarified from 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")

Here is my working code for getting changed blocks from offline VHDX. (most credit goes to Neelabh Mam who posted above)

Hope it might help someone struggling to make RCT work with C++.


BOOLEAN VHDWRAP_CONVENTION CloseVhd(HANDLE hDisk)
{
    return CloseHandle(hDisk) == TRUE;
}


HANDLE VHDWRAP_CONVENTION OpenVhdx(PCWSTR path, unsigned mask)
{
    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;

    fRet = OpenVirtualDisk(
        &vst,
        path, (VIRTUAL_DISK_ACCESS_MASK)mask,
        OPEN_VIRTUAL_DISK_FLAG_NONE,
        &params,
        &hVHD);
    g_dwLastError = fRet;
    return hVHD;
}

VHDWRAP_API BOOLEAN VHDWRAP_CONVENTION EnableVhdRct(HANDLE hVHD)
{
    SET_VIRTUAL_DISK_INFO vdinfo;
    vdinfo.Version = SET_VIRTUAL_DISK_INFO_CHANGE_TRACKING_STATE;
    vdinfo.ChangeTrackingEnabled = TRUE;
    DWORD fRet = SetVirtualDiskInformation(hVHD, &vdinfo);
    g_dwLastError = fRet;
    return fRet == 0;
}

VHDWRAP_API BOOLEAN VHDWRAP_CONVENTION DisableVhdRct(HANDLE hVHD)
{
    SET_VIRTUAL_DISK_INFO vdinfo;
    vdinfo.Version = SET_VIRTUAL_DISK_INFO_CHANGE_TRACKING_STATE;
    vdinfo.ChangeTrackingEnabled = FALSE;
    DWORD fRet = SetVirtualDiskInformation(hVHD, &vdinfo);
    g_dwLastError = fRet;
    return fRet == 0;
}

VHDWRAP_API BOOLEAN VHDWRAP_CONVENTION QueryVhdRct(HANDLE hVHD, LPCWSTR szRctId)
{
    //query rctid 
    WCHAR vdinfo[1024];
    ZeroMemory(vdinfo, sizeof(vdinfo));
    ((GET_VIRTUAL_DISK_INFO*)vdinfo)->Version = GET_VIRTUAL_DISK_INFO_CHANGE_TRACKING_STATE;
    ULONG vdinfoSize = sizeof(vdinfo);

    DWORD fRet = GetVirtualDiskInformation(
        hVHD,
        &vdinfoSize,
        (PGET_VIRTUAL_DISK_INFO)vdinfo,
        NULL);
    g_dwLastError = fRet;
    if (fRet != ERROR_SUCCESS)
    {
        std::cout << "GetVirtualDiskInformation GET_VIRTUAL_DISK_INFO_CHANGE_TRACKING_STATE failed " << fRet << "\n";
        return false;
    }
    std::wcout << L"RCT status : " << ((((GET_VIRTUAL_DISK_INFO*)vdinfo)->ChangeTrackingState.Enabled) ? L"enabled" : L"disabled") << L"\n";
    std::wcout << L"RCT ID : " << std::wstring(((GET_VIRTUAL_DISK_INFO*)vdinfo)->ChangeTrackingState.MostRecentId) << "\n";
    std::wcout << L"RCT changes : " << ((((GET_VIRTUAL_DISK_INFO*)vdinfo)->ChangeTrackingState.NewerChanges) ? L"yes": L"no") << "\n";

    //query disk length 
    _GET_VIRTUAL_DISK_INFO sizeInfo;
    sizeInfo.Version = GET_VIRTUAL_DISK_INFO_SIZE;
    vdinfoSize = sizeof(sizeInfo);

    fRet = GetVirtualDiskInformation(
        hVHD,
        &vdinfoSize,
        (PGET_VIRTUAL_DISK_INFO)&sizeInfo,
        NULL);
    g_dwLastError = fRet;
    if (fRet != ERROR_SUCCESS)
    {
        std::cout << "GetVirtualDiskInformation GET_VIRTUAL_DISK_INFO_SIZE failed " << fRet << "\n";
        return FALSE;
    }

    std::cout << "Disk length : " << sizeInfo.Size.VirtualSize << "\n";

    const ULONG MaxRangeCount = 4096*1024;
    ULONG RangeCount = MaxRangeCount;
    ULONG64 ProcessedLength = 0;
    QUERY_CHANGES_VIRTUAL_DISK_RANGE* Ranges = new QUERY_CHANGES_VIRTUAL_DISK_RANGE[MaxRangeCount];

    fRet = QueryChangesVirtualDisk(
        hVHD,
        szRctId?szRctId : ((GET_VIRTUAL_DISK_INFO *)vdinfo)->ChangeTrackingState.MostRecentId,
        0,
        sizeInfo.Size.VirtualSize,
        QUERY_CHANGES_VIRTUAL_DISK_FLAG_NONE,
        Ranges,
        &RangeCount,
        &ProcessedLength);
    g_dwLastError = fRet;
    if (fRet != ERROR_SUCCESS)
    {
        delete[] Ranges;
        std::cout << "QueryChangesVirtualDisk failed " << fRet << " ranges:" << RangeCount << " len " << ProcessedLength << "\n";
        return FALSE;
    }

    if (MaxRangeCount <= RangeCount || ProcessedLength < sizeInfo.Size.VirtualSize)
    {
        delete[] Ranges;
        std::cout << "Not enough resources to capture all the changes \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";
    }

    delete[] Ranges;
    return TRUE;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM