关闭

基于vss的热迁移技术

标签: 虚拟化 Volume Shadow Co
238人阅读 评论(0) 收藏 举报
分类:

声明:本文需要一定的虚拟化相关知识

vssVolume Shadow Copy),是Windows下用于实现数据备份恢复的技术。本文要讲的是利用这种技术实现Windows系统的热迁移功能。

所谓热迁移,就是在操作系统处于正常运行的状态下,将系统迁移到指定IP的服务端,迁移内容除了磁盘上的数据,还包括内存当中的数据,内存数据的实时同步目前暂时不在本文中介绍。

vss能够实现在特定的时刻对操作系统上所有的磁盘分区制作卷影,也就是打一个快照。我们可以使用多数对原始分区有效的Windows系统api来访问快照,也就是说,快照同样可以被视为一个设备来进行数据读写操作,只是这个设备不会再有脏数据产生。

    使用Windows自带的vssadmin命令可以查看快照(vssadmin list shadows)以及其他信息。

下面先贴出我这边在win7x862008x86以及2003x64系统上测试通过的快照代码:

/**
 *  @brief  卷影复制demo
 *  @author mrfang
 *  @date   2015.11.18
 */

#include <Windows.h>
#include <tchar.h>
#include <shlwapi.h>
#include <vss.h>
#include <vswriter.h>
#include <vsbackup.h>
#include <VsProv.h>

#define LOG_BUFFER_SIZE  (4096 * 2)
#define LogDebug  mrlog
#define LogInfo   mrlog
#define LogWarn   mrlog
#define LogError  mrlog

#define BUFFER_SIZE     (4096)

#pragma comment (lib, "ole32.lib")
#pragma comment (lib, "VssApi.lib")
#pragma comment (lib, "Advapi32.lib")

// Helper macros to print a GUID using printf-style formatting
#define WSTR_GUID_FMT  _T("{%.8x-%.4x-%.4x-%.2x%.2x-%.2x%.2x%.2x%.2x%.2x%.2x}")

#define GUID_PRINTF_ARG( X )                                \
    (X).Data1,                                              \
    (X).Data2,                                              \
    (X).Data3,                                              \
    (X).Data4[0], (X).Data4[1], (X).Data4[2], (X).Data4[3], \
    (X).Data4[4], (X).Data4[5], (X).Data4[6], (X).Data4[7]


void mrlog(const TCHAR* format, ...)
{
    TCHAR szLogBuf[LOG_BUFFER_SIZE] = {0};
    va_list arg_ptr;
    va_start(arg_ptr, format);
    _vsntprintf_s(szLogBuf, sizeof(szLogBuf)/sizeof(szLogBuf[0]), format, arg_ptr);
    va_end(arg_ptr);
    _tprintf(_T("%s\n"), szLogBuf);
}

void ReleaseInterface(IUnknown* unkn)
{
    if (unkn)
    {
        unkn->Release();
        unkn = NULL;
    }
}

/**
 *  @brief  对指定逻辑卷创建卷影
 *  @param  [in]szVolumeName:逻辑卷名
 *  @param  [in]snapshotSetId:卷影副本集ID,此ID在AddToSnapshotSet后会修改为对应卷影副本的ID
 */
BOOL CreateSnapshot(_In_ IVssBackupComponents* pBackup, _In_ const TCHAR* szVolumeName)
{
    if (!pBackup)
    {
        LogError(_T("[CreateSnapshot]Invalid param"));
        return FALSE;
    }

    HRESULT hResult = S_OK;
    BOOL    bRetVal = TRUE;
    VSS_ID   snapShotId              = {0};
    IVssAsync* pPrepare              = NULL;
    IVssAsync* pDoShadowCopy         = NULL;
    VSS_SNAPSHOT_PROP   snapshotProp = {0};

    hResult = pBackup->AddToSnapshotSet(const_cast<TCHAR *>(szVolumeName), GUID_NULL, &snapShotId);
    if (hResult != S_OK)
    {
        LogError(_T("AddToSnapshotSet failed, code=0x%x"), hResult);
        bRetVal = FALSE;
        goto TheEnd;
    }
        
    hResult = pBackup->SetBackupState(false, false, /*VSS_BT_COPY*/VSS_BT_FULL);
    if (hResult != S_OK)
    {
        LogError(_T("SetBackupState failed, code=0x%x"), hResult);
        bRetVal = FALSE;
        goto TheEnd;
    }

    hResult = pBackup->PrepareForBackup(&pPrepare);
    if (hResult != S_OK)
    {
        LogError(_T("PrepareForBackup failed, code=0x%x"), hResult);
        bRetVal = FALSE;
        goto TheEnd;
    }

    LogInfo(_T("Preparing for backup..."));
    hResult = pPrepare->Wait();
    if (hResult != S_OK)
    {
        LogError(_T("IVssAsync Wait failed, code=0x%x"), hResult);
        bRetVal = FALSE;
        goto TheEnd;
    }
          
    hResult = pBackup->DoSnapshotSet(&pDoShadowCopy);
    if (hResult != S_OK)
    {
        LogError(_T("DoSnapshotSet failed, code=0x%x"), hResult);
        bRetVal = FALSE;
        goto TheEnd;
    }

    LogInfo(_T("Taking snapshots..."));
    hResult = pDoShadowCopy->Wait();
    if (hResult != S_OK)
    {
        LogError(_T("IVssAsync Wait failed, code=0x%x"), hResult);
        bRetVal = FALSE;
        goto TheEnd;
    }

    LogInfo(_T("Get the snapshot device object from the properties..."));
    
    hResult = pBackup->GetSnapshotProperties(snapShotId, &snapshotProp);
    if (hResult != S_OK)
    {
        LogError(_T("GetSnapshotProperties failed, code=0x%x"), hResult);
        bRetVal = FALSE;
        goto TheEnd;
    }

    LogDebug(_T(" Snapshot Id :")  WSTR_GUID_FMT, GUID_PRINTF_ARG( snapshotProp.m_SnapshotId));
    LogDebug(_T(" Snapshot Set Id ")  WSTR_GUID_FMT, GUID_PRINTF_ARG(snapshotProp.m_SnapshotSetId));
    LogDebug(_T(" Provider Id ")  WSTR_GUID_FMT, GUID_PRINTF_ARG(snapshotProp.m_ProviderId));
    LogDebug(_T(" OriginalVolumeName : %ls"), snapshotProp.m_pwszOriginalVolumeName);

    if (snapshotProp.m_pwszExposedName)
        LogDebug(_T(" ExposedName : %ls"), snapshotProp.m_pwszExposedName);
    if (snapshotProp.m_pwszExposedPath)
        LogDebug(_T(" ExposedPath : %ls"), snapshotProp.m_pwszExposedPath);
    if (snapshotProp.m_pwszSnapshotDeviceObject)
        LogDebug(_T(" DeviceObject : %ls"), snapshotProp.m_pwszSnapshotDeviceObject);

    VssFreeSnapshotProperties(&snapshotProp);
    bRetVal = TRUE;
TheEnd:
    ReleaseInterface(pPrepare);
    ReleaseInterface(pDoShadowCopy);
    return bRetVal;
}

/**
 *  @brief  创建卷影集
 *  @param  [out]创建所得卷影集ID
 *  @return TRUE:创建成功;FALSE:创建失败
 */
BOOL CreateSnapshotSet(_Out_ IVssBackupComponents** pBackup, 
                       _Out_ VSS_ID* snapshotSetId)
{
    if (!pBackup || !snapshotSetId)
    {
        LogError(_T("[CreateSnapshotSet]Invalid param"));
        return FALSE;
    }

    IVssAsync            *pAsync        = NULL;
    HRESULT              hResult        = S_OK;
    BOOL                 bRetVal        = TRUE;
    BOOL                 bFreeMetaData  = FALSE;

    hResult = CoInitialize(NULL);
    if (hResult != S_OK)
    {
        LogError(_T("CoInitialize failed, code=0x%x"), hResult);
        return FALSE;
    }

    hResult = CreateVssBackupComponents(pBackup);      //Release if no longer needed
    if (hResult != S_OK)
    {
        LogError(_T("CreateVssBackupComponents failed, code=0x%x"), hResult);
        return FALSE;
    }

    hResult = (*pBackup)->InitializeForBackup();
    if (hResult != S_OK)
    {
        LogError(_T("InitializeForBackup failed, code=0x%x"), hResult);
        bRetVal = FALSE;
        goto TheEnd;
    }

    hResult = (*pBackup)->SetContext(VSS_CTX_BACKUP);
    if (hResult != S_OK)
    {
        LogError(_T("IVssBackupComponents SetContext failed, code=0x%x"), hResult);  
        bRetVal = FALSE;
        goto TheEnd;
    }

    // Prompts each writer to send the metadata they have collected
    hResult = (*pBackup)->GatherWriterMetadata(&pAsync);
    bFreeMetaData = TRUE;
    if (hResult != S_OK)
    {
        LogError(_T("GatherWriterMetadata failed, code=0x%x"), hResult);
        bRetVal = FALSE;
        goto TheEnd;
    }

    hResult = pAsync->Wait();
    if (hResult != S_OK)
    {
        LogError(_T("IVssAsync Wait failed, code=0x%x"), hResult);
        bRetVal = FALSE;
        goto TheEnd;
    }

    hResult = (*pBackup)->StartSnapshotSet(snapshotSetId);
    if (hResult != S_OK)
    {
        LogError(_T("StartSnapshotSet failed, code=0x%x"), hResult);
        bRetVal = FALSE;
        goto TheEnd;
    }
    bRetVal = TRUE;
TheEnd:
    if (bFreeMetaData)
        (*pBackup)->FreeWriterMetadata();
    return bRetVal;
}

/**
 *  @brief  对指定的卷列表做卷影
 *  @param
 *  @return
 */
BOOL VolumeShadow(_In_ const TCHAR* szVolumeName)
{
    if (!szVolumeName)
    {
        LogError(_T("[CopyVolume]Invalid param"));
        return FALSE;
    }

    VSS_ID                  snapshotSetID   = {0};
    IVssBackupComponents*   pBackup         = NULL;
    BOOL                    bRetVal         = TRUE;
    LogInfo(_T("CreateSnapshotSet..."));
    if (!CreateSnapshotSet(&pBackup, &snapshotSetID))
    {
        return FALSE;
    }
    
    if (!CreateSnapshot(pBackup, szVolumeName))
    {
        LogError(_T("CreateSnapshot failed"));
        bRetVal = FALSE;
    }

    ReleaseInterface(pBackup);

    return bRetVal;
}

int _tmain(int argc, const TCHAR* argv[])
{    
    if (argc != 2)
    {
        LogError(_T("Usage: %s volumeName. eg. %s C\\"), __FILE__, __FILE__);
        return 1;
    }
    
    VolumeShadow(argv[1]);
    return 0;
}

    对分区做快照之后,可以获取快照属性,其中有一项snapshotProp.m_pwszSnapshotDeviceObject便是分区所对应的快照名,于是便可以将此快照作为设备打开,从0字节处开始读取快照内部的数据。

    为了加快拷贝效率,Windows提供了一个VOLUME_BITMAP_BUFFER结构,如下:

typedef struct {
  LARGE_INTEGER StartingLcn;
  LARGE_INTEGER BitmapSize;
  BYTE Buffer[1];} VOLUME_BITMAP_BUFFER, 
 *PVOLUME_BITMAP_BUFFER;

    MSDN上有该结构的详细描述,该结构将分区中的数据用一个位图表示,因为磁盘上的分区数据是按簇为单位来进行读写的,因此该结构中用位图中1bit代表1个簇来表示整个分区,bit值为1则表示对应簇写有数据,为0则表示未写入数据。因此位图的大小(按字节计)=分区大小(按字节计)/分区簇大小/8(表示1个字节8bit)。

    该结构可以通过DeviceIoControl(FSCTL_GET_VOLUME_BITMAP)获取。

    使用此位图,可以在读取分区数据的时候,仅读取bit值为1的簇,而跳过bit值为0的簇,并且幸运的是,这个操作针对快照也是有效的。

    至此,基于vss的热迁移技术,其主要技术点已介绍完毕,数据发送以及通信部分略去。

 

 

 

下面分享一下在做这个的过程中遇到的问题以及解决方法:

(未完待续)



转载请注明原文链接,作者保留追究相关责任的权利。

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:5348次
    • 积分:123
    • 等级:
    • 排名:千里之外
    • 原创:7篇
    • 转载:2篇
    • 译文:0篇
    • 评论:0条
    文章分类