qemu/qga/vss-win32/requester.cpp
<<
>>
Prefs
   1/*
   2 * QEMU Guest Agent win32 VSS Requester implementations
   3 *
   4 * Copyright Hitachi Data Systems Corp. 2013
   5 *
   6 * Authors:
   7 *  Tomoki Sekiyama   <tomoki.sekiyama@hds.com>
   8 *
   9 * This work is licensed under the terms of the GNU GPL, version 2 or later.
  10 * See the COPYING file in the top-level directory.
  11 */
  12
  13#include "qemu/osdep.h"
  14#include "vss-common.h"
  15#include "requester.h"
  16#include "install.h"
  17#include <inc/win2003/vswriter.h>
  18#include <inc/win2003/vsbackup.h>
  19
  20/* Max wait time for frozen event (VSS can only hold writes for 10 seconds) */
  21#define VSS_TIMEOUT_FREEZE_MSEC 60000
  22
  23/* Call QueryStatus every 10 ms while waiting for frozen event */
  24#define VSS_TIMEOUT_EVENT_MSEC 10
  25
  26#define err_set(e, err, fmt, ...)                                           \
  27    ((e)->error_setg_win32_wrapper((e)->errp, __FILE__, __LINE__, __func__, \
  28                                   err, fmt, ## __VA_ARGS__))
  29/* Bad idea, works only when (e)->errp != NULL: */
  30#define err_is_set(e) ((e)->errp && *(e)->errp)
  31/* To lift this restriction, error_propagate(), like we do in QEMU code */
  32
  33/* Handle to VSSAPI.DLL */
  34static HMODULE hLib;
  35
  36/* Functions in VSSAPI.DLL */
  37typedef HRESULT(STDAPICALLTYPE * t_CreateVssBackupComponents)(
  38    OUT IVssBackupComponents**);
  39typedef void(APIENTRY * t_VssFreeSnapshotProperties)(IN VSS_SNAPSHOT_PROP*);
  40static t_CreateVssBackupComponents pCreateVssBackupComponents;
  41static t_VssFreeSnapshotProperties pVssFreeSnapshotProperties;
  42
  43/* Variables used while applications and filesystes are frozen by VSS */
  44static struct QGAVSSContext {
  45    IVssBackupComponents *pVssbc;  /* VSS requester interface */
  46    IVssAsync *pAsyncSnapshot;     /* async info of VSS snapshot operation */
  47    HANDLE hEventFrozen;           /* notify fs/writer freeze from provider */
  48    HANDLE hEventThaw;             /* request provider to thaw */
  49    HANDLE hEventTimeout;          /* notify timeout in provider */
  50    int cFrozenVols;               /* number of frozen volumes */
  51} vss_ctx;
  52
  53STDAPI requester_init(void)
  54{
  55    COMInitializer initializer; /* to call CoInitializeSecurity */
  56    HRESULT hr = CoInitializeSecurity(
  57        NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
  58        RPC_C_IMP_LEVEL_IDENTIFY, NULL, EOAC_NONE, NULL);
  59    if (FAILED(hr)) {
  60        fprintf(stderr, "failed to CoInitializeSecurity (error %lx)\n", hr);
  61        return hr;
  62    }
  63
  64    hLib = LoadLibraryA("VSSAPI.DLL");
  65    if (!hLib) {
  66        fprintf(stderr, "failed to load VSSAPI.DLL\n");
  67        return HRESULT_FROM_WIN32(GetLastError());
  68    }
  69
  70    pCreateVssBackupComponents = (t_CreateVssBackupComponents)
  71        GetProcAddress(hLib,
  72#ifdef _WIN64 /* 64bit environment */
  73        "?CreateVssBackupComponents@@YAJPEAPEAVIVssBackupComponents@@@Z"
  74#else /* 32bit environment */
  75        "?CreateVssBackupComponents@@YGJPAPAVIVssBackupComponents@@@Z"
  76#endif
  77        );
  78    if (!pCreateVssBackupComponents) {
  79        fprintf(stderr, "failed to get proc address from VSSAPI.DLL\n");
  80        return HRESULT_FROM_WIN32(GetLastError());
  81    }
  82
  83    pVssFreeSnapshotProperties = (t_VssFreeSnapshotProperties)
  84        GetProcAddress(hLib, "VssFreeSnapshotProperties");
  85    if (!pVssFreeSnapshotProperties) {
  86        fprintf(stderr, "failed to get proc address from VSSAPI.DLL\n");
  87        return HRESULT_FROM_WIN32(GetLastError());
  88    }
  89
  90    return S_OK;
  91}
  92
  93static void requester_cleanup(void)
  94{
  95    if (vss_ctx.hEventFrozen) {
  96        CloseHandle(vss_ctx.hEventFrozen);
  97        vss_ctx.hEventFrozen = NULL;
  98    }
  99    if (vss_ctx.hEventThaw) {
 100        CloseHandle(vss_ctx.hEventThaw);
 101        vss_ctx.hEventThaw = NULL;
 102    }
 103    if (vss_ctx.hEventTimeout) {
 104        CloseHandle(vss_ctx.hEventTimeout);
 105        vss_ctx.hEventTimeout = NULL;
 106    }
 107    if (vss_ctx.pAsyncSnapshot) {
 108        vss_ctx.pAsyncSnapshot->Release();
 109        vss_ctx.pAsyncSnapshot = NULL;
 110    }
 111    if (vss_ctx.pVssbc) {
 112        vss_ctx.pVssbc->Release();
 113        vss_ctx.pVssbc = NULL;
 114    }
 115    vss_ctx.cFrozenVols = 0;
 116}
 117
 118STDAPI requester_deinit(void)
 119{
 120    requester_cleanup();
 121
 122    pCreateVssBackupComponents = NULL;
 123    pVssFreeSnapshotProperties = NULL;
 124    if (hLib) {
 125        FreeLibrary(hLib);
 126        hLib = NULL;
 127    }
 128
 129    return S_OK;
 130}
 131
 132static HRESULT WaitForAsync(IVssAsync *pAsync)
 133{
 134    HRESULT ret, hr;
 135
 136    do {
 137        hr = pAsync->Wait();
 138        if (FAILED(hr)) {
 139            ret = hr;
 140            break;
 141        }
 142        hr = pAsync->QueryStatus(&ret, NULL);
 143        if (FAILED(hr)) {
 144            ret = hr;
 145            break;
 146        }
 147    } while (ret == VSS_S_ASYNC_PENDING);
 148
 149    return ret;
 150}
 151
 152static void AddComponents(ErrorSet *errset)
 153{
 154    unsigned int cWriters, i;
 155    VSS_ID id, idInstance, idWriter;
 156    BSTR bstrWriterName = NULL;
 157    VSS_USAGE_TYPE usage;
 158    VSS_SOURCE_TYPE source;
 159    unsigned int cComponents, c1, c2, j;
 160    COMPointer<IVssExamineWriterMetadata> pMetadata;
 161    COMPointer<IVssWMComponent> pComponent;
 162    PVSSCOMPONENTINFO info;
 163    HRESULT hr;
 164
 165    hr = vss_ctx.pVssbc->GetWriterMetadataCount(&cWriters);
 166    if (FAILED(hr)) {
 167        err_set(errset, hr, "failed to get writer metadata count");
 168        goto out;
 169    }
 170
 171    for (i = 0; i < cWriters; i++) {
 172        hr = vss_ctx.pVssbc->GetWriterMetadata(i, &id, pMetadata.replace());
 173        if (FAILED(hr)) {
 174            err_set(errset, hr, "failed to get writer metadata of %d/%d",
 175                             i, cWriters);
 176            goto out;
 177        }
 178
 179        hr = pMetadata->GetIdentity(&idInstance, &idWriter,
 180                                    &bstrWriterName, &usage, &source);
 181        if (FAILED(hr)) {
 182            err_set(errset, hr, "failed to get identity of writer %d/%d",
 183                             i, cWriters);
 184            goto out;
 185        }
 186
 187        hr = pMetadata->GetFileCounts(&c1, &c2, &cComponents);
 188        if (FAILED(hr)) {
 189            err_set(errset, hr, "failed to get file counts of %S",
 190                             bstrWriterName);
 191            goto out;
 192        }
 193
 194        for (j = 0; j < cComponents; j++) {
 195            hr = pMetadata->GetComponent(j, pComponent.replace());
 196            if (FAILED(hr)) {
 197                err_set(errset, hr,
 198                                 "failed to get component %d/%d of %S",
 199                                 j, cComponents, bstrWriterName);
 200                goto out;
 201            }
 202
 203            hr = pComponent->GetComponentInfo(&info);
 204            if (FAILED(hr)) {
 205                err_set(errset, hr,
 206                                 "failed to get component info %d/%d of %S",
 207                                 j, cComponents, bstrWriterName);
 208                goto out;
 209            }
 210
 211            if (info->bSelectable) {
 212                hr = vss_ctx.pVssbc->AddComponent(idInstance, idWriter,
 213                                                  info->type,
 214                                                  info->bstrLogicalPath,
 215                                                  info->bstrComponentName);
 216                if (FAILED(hr)) {
 217                    err_set(errset, hr, "failed to add component %S(%S)",
 218                                     info->bstrComponentName, bstrWriterName);
 219                    goto out;
 220                }
 221            }
 222            SysFreeString(bstrWriterName);
 223            bstrWriterName = NULL;
 224            pComponent->FreeComponentInfo(info);
 225            info = NULL;
 226        }
 227    }
 228out:
 229    if (bstrWriterName) {
 230        SysFreeString(bstrWriterName);
 231    }
 232    if (pComponent && info) {
 233        pComponent->FreeComponentInfo(info);
 234    }
 235}
 236
 237void requester_freeze(int *num_vols, void *mountpoints, ErrorSet *errset)
 238{
 239    COMPointer<IVssAsync> pAsync;
 240    HANDLE volume;
 241    HRESULT hr;
 242    LONG ctx;
 243    GUID guidSnapshotSet = GUID_NULL;
 244    SECURITY_DESCRIPTOR sd;
 245    SECURITY_ATTRIBUTES sa;
 246    WCHAR short_volume_name[64], *display_name = short_volume_name;
 247    DWORD wait_status;
 248    int num_fixed_drives = 0, i;
 249    int num_mount_points = 0;
 250
 251    if (vss_ctx.pVssbc) { /* already frozen */
 252        *num_vols = 0;
 253        return;
 254    }
 255
 256    CoInitialize(NULL);
 257
 258    /* Allow unrestricted access to events */
 259    InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
 260    SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE);
 261    sa.nLength = sizeof(sa);
 262    sa.lpSecurityDescriptor = &sd;
 263    sa.bInheritHandle = FALSE;
 264
 265    vss_ctx.hEventFrozen = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_FROZEN);
 266    if (!vss_ctx.hEventFrozen) {
 267        err_set(errset, GetLastError(), "failed to create event %s",
 268                EVENT_NAME_FROZEN);
 269        goto out;
 270    }
 271    vss_ctx.hEventThaw = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_THAW);
 272    if (!vss_ctx.hEventThaw) {
 273        err_set(errset, GetLastError(), "failed to create event %s",
 274                EVENT_NAME_THAW);
 275        goto out;
 276    }
 277    vss_ctx.hEventTimeout = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_TIMEOUT);
 278    if (!vss_ctx.hEventTimeout) {
 279        err_set(errset, GetLastError(), "failed to create event %s",
 280                EVENT_NAME_TIMEOUT);
 281        goto out;
 282    }
 283
 284    assert(pCreateVssBackupComponents != NULL);
 285    hr = pCreateVssBackupComponents(&vss_ctx.pVssbc);
 286    if (FAILED(hr)) {
 287        err_set(errset, hr, "failed to create VSS backup components");
 288        goto out;
 289    }
 290
 291    hr = vss_ctx.pVssbc->InitializeForBackup();
 292    if (FAILED(hr)) {
 293        err_set(errset, hr, "failed to initialize for backup");
 294        goto out;
 295    }
 296
 297    hr = vss_ctx.pVssbc->SetBackupState(true, true, VSS_BT_FULL, false);
 298    if (FAILED(hr)) {
 299        err_set(errset, hr, "failed to set backup state");
 300        goto out;
 301    }
 302
 303    /*
 304     * Currently writable snapshots are not supported.
 305     * To prevent the final commit (which requires to write to snapshots),
 306     * ATTR_NO_AUTORECOVERY and ATTR_TRANSPORTABLE are specified here.
 307     */
 308    ctx = VSS_CTX_APP_ROLLBACK | VSS_VOLSNAP_ATTR_TRANSPORTABLE |
 309        VSS_VOLSNAP_ATTR_NO_AUTORECOVERY | VSS_VOLSNAP_ATTR_TXF_RECOVERY;
 310    hr = vss_ctx.pVssbc->SetContext(ctx);
 311    if (hr == (HRESULT)VSS_E_UNSUPPORTED_CONTEXT) {
 312        /* Non-server version of Windows doesn't support ATTR_TRANSPORTABLE */
 313        ctx &= ~VSS_VOLSNAP_ATTR_TRANSPORTABLE;
 314        hr = vss_ctx.pVssbc->SetContext(ctx);
 315    }
 316    if (FAILED(hr)) {
 317        err_set(errset, hr, "failed to set backup context");
 318        goto out;
 319    }
 320
 321    hr = vss_ctx.pVssbc->GatherWriterMetadata(pAsync.replace());
 322    if (SUCCEEDED(hr)) {
 323        hr = WaitForAsync(pAsync);
 324    }
 325    if (FAILED(hr)) {
 326        err_set(errset, hr, "failed to gather writer metadata");
 327        goto out;
 328    }
 329
 330    AddComponents(errset);
 331    if (err_is_set(errset)) {
 332        goto out;
 333    }
 334
 335    hr = vss_ctx.pVssbc->StartSnapshotSet(&guidSnapshotSet);
 336    if (FAILED(hr)) {
 337        err_set(errset, hr, "failed to start snapshot set");
 338        goto out;
 339    }
 340
 341    if (mountpoints) {
 342        PWCHAR volume_name_wchar;
 343        for (volList *list = (volList *)mountpoints; list; list = list->next) {
 344            size_t len = strlen(list->value) + 1;
 345            size_t converted = 0;
 346            VSS_ID pid;
 347
 348            volume_name_wchar = new wchar_t[len];
 349            mbstowcs_s(&converted, volume_name_wchar, len,
 350                       list->value, _TRUNCATE);
 351
 352            hr = vss_ctx.pVssbc->AddToSnapshotSet(volume_name_wchar,
 353                                                  g_gProviderId, &pid);
 354            if (FAILED(hr)) {
 355                err_set(errset, hr, "failed to add %S to snapshot set",
 356                        volume_name_wchar);
 357                delete volume_name_wchar;
 358                goto out;
 359            }
 360            num_mount_points++;
 361
 362            delete volume_name_wchar;
 363        }
 364
 365        if (num_mount_points == 0) {
 366            /* If there is no valid mount points, just exit. */
 367            goto out;
 368        }
 369    }
 370
 371    if (!mountpoints) {
 372        volume = FindFirstVolumeW(short_volume_name, sizeof(short_volume_name));
 373        if (volume == INVALID_HANDLE_VALUE) {
 374            err_set(errset, hr, "failed to find first volume");
 375            goto out;
 376        }
 377
 378        for (;;) {
 379            if (GetDriveTypeW(short_volume_name) == DRIVE_FIXED) {
 380                VSS_ID pid;
 381                hr = vss_ctx.pVssbc->AddToSnapshotSet(short_volume_name,
 382                                                      g_gProviderId, &pid);
 383                if (FAILED(hr)) {
 384                    WCHAR volume_path_name[PATH_MAX];
 385                    if (GetVolumePathNamesForVolumeNameW(
 386                            short_volume_name, volume_path_name,
 387                            sizeof(volume_path_name), NULL) &&
 388                            *volume_path_name) {
 389                        display_name = volume_path_name;
 390                    }
 391                    err_set(errset, hr, "failed to add %S to snapshot set",
 392                            display_name);
 393                    FindVolumeClose(volume);
 394                    goto out;
 395                }
 396                num_fixed_drives++;
 397            }
 398            if (!FindNextVolumeW(volume, short_volume_name,
 399                                 sizeof(short_volume_name))) {
 400                FindVolumeClose(volume);
 401                break;
 402            }
 403        }
 404
 405        if (num_fixed_drives == 0) {
 406            goto out; /* If there is no fixed drive, just exit. */
 407        }
 408    }
 409
 410    hr = vss_ctx.pVssbc->PrepareForBackup(pAsync.replace());
 411    if (SUCCEEDED(hr)) {
 412        hr = WaitForAsync(pAsync);
 413    }
 414    if (FAILED(hr)) {
 415        err_set(errset, hr, "failed to prepare for backup");
 416        goto out;
 417    }
 418
 419    hr = vss_ctx.pVssbc->GatherWriterStatus(pAsync.replace());
 420    if (SUCCEEDED(hr)) {
 421        hr = WaitForAsync(pAsync);
 422    }
 423    if (FAILED(hr)) {
 424        err_set(errset, hr, "failed to gather writer status");
 425        goto out;
 426    }
 427
 428    /*
 429     * Start VSS quiescing operations.
 430     * CQGAVssProvider::CommitSnapshots will kick vss_ctx.hEventFrozen
 431     * after the applications and filesystems are frozen.
 432     */
 433    hr = vss_ctx.pVssbc->DoSnapshotSet(&vss_ctx.pAsyncSnapshot);
 434    if (FAILED(hr)) {
 435        err_set(errset, hr, "failed to do snapshot set");
 436        goto out;
 437    }
 438
 439    /* Need to call QueryStatus several times to make VSS provider progress */
 440    for (i = 0; i < VSS_TIMEOUT_FREEZE_MSEC/VSS_TIMEOUT_EVENT_MSEC; i++) {
 441        HRESULT hr2 = vss_ctx.pAsyncSnapshot->QueryStatus(&hr, NULL);
 442        if (FAILED(hr2)) {
 443            err_set(errset, hr, "failed to do snapshot set");
 444            goto out;
 445        }
 446        if (hr != VSS_S_ASYNC_PENDING) {
 447            err_set(errset, E_FAIL,
 448                    "DoSnapshotSet exited without Frozen event");
 449            goto out;
 450        }
 451        wait_status = WaitForSingleObject(vss_ctx.hEventFrozen,
 452                                          VSS_TIMEOUT_EVENT_MSEC);
 453        if (wait_status != WAIT_TIMEOUT) {
 454            break;
 455        }
 456    }
 457
 458    if (wait_status == WAIT_TIMEOUT) {
 459        err_set(errset, E_FAIL,
 460                "timeout when try to receive Frozen event from VSS provider");
 461        /* If we are here, VSS had timeout.
 462         * Don't call AbortBackup, just return directly.
 463         */
 464        goto out1;
 465    }
 466
 467    if (wait_status != WAIT_OBJECT_0) {
 468        err_set(errset, E_FAIL,
 469                "couldn't receive Frozen event from VSS provider");
 470        goto out;
 471    }
 472
 473    if (mountpoints) {
 474        *num_vols = vss_ctx.cFrozenVols = num_mount_points;
 475    } else {
 476        *num_vols = vss_ctx.cFrozenVols = num_fixed_drives;
 477    }
 478
 479    return;
 480
 481out:
 482    if (vss_ctx.pVssbc) {
 483        vss_ctx.pVssbc->AbortBackup();
 484    }
 485
 486out1:
 487    requester_cleanup();
 488    CoUninitialize();
 489}
 490
 491
 492void requester_thaw(int *num_vols, void *mountpints, ErrorSet *errset)
 493{
 494    COMPointer<IVssAsync> pAsync;
 495
 496    if (!vss_ctx.hEventThaw) {
 497        /*
 498         * In this case, DoSnapshotSet is aborted or not started,
 499         * and no volumes must be frozen. We return without an error.
 500         */
 501        *num_vols = 0;
 502        return;
 503    }
 504
 505    /* Tell the provider that the snapshot is finished. */
 506    SetEvent(vss_ctx.hEventThaw);
 507
 508    assert(vss_ctx.pVssbc);
 509    assert(vss_ctx.pAsyncSnapshot);
 510
 511    HRESULT hr = WaitForAsync(vss_ctx.pAsyncSnapshot);
 512    switch (hr) {
 513    case VSS_S_ASYNC_FINISHED:
 514        hr = vss_ctx.pVssbc->BackupComplete(pAsync.replace());
 515        if (SUCCEEDED(hr)) {
 516            hr = WaitForAsync(pAsync);
 517        }
 518        if (FAILED(hr)) {
 519            err_set(errset, hr, "failed to complete backup");
 520        }
 521        break;
 522
 523    case (HRESULT)VSS_E_OBJECT_NOT_FOUND:
 524        /*
 525         * On Windows earlier than 2008 SP2 which does not support
 526         * VSS_VOLSNAP_ATTR_NO_AUTORECOVERY context, the final commit is not
 527         * skipped and VSS is aborted by VSS_E_OBJECT_NOT_FOUND. However, as
 528         * the system had been frozen until fsfreeze-thaw command was issued,
 529         * we ignore this error.
 530         */
 531        vss_ctx.pVssbc->AbortBackup();
 532        break;
 533
 534    case VSS_E_UNEXPECTED_PROVIDER_ERROR:
 535        if (WaitForSingleObject(vss_ctx.hEventTimeout, 0) != WAIT_OBJECT_0) {
 536            err_set(errset, hr, "unexpected error in VSS provider");
 537            break;
 538        }
 539        /* fall through if hEventTimeout is signaled */
 540
 541    case (HRESULT)VSS_E_HOLD_WRITES_TIMEOUT:
 542        err_set(errset, hr, "couldn't hold writes: "
 543                "fsfreeze is limited up to 10 seconds");
 544        break;
 545
 546    default:
 547        err_set(errset, hr, "failed to do snapshot set");
 548    }
 549
 550    if (err_is_set(errset)) {
 551        vss_ctx.pVssbc->AbortBackup();
 552    }
 553    *num_vols = vss_ctx.cFrozenVols;
 554    requester_cleanup();
 555
 556    CoUninitialize();
 557    StopService();
 558}
 559