 e55eb806db
			
		
	
	
		e55eb806db
		
	
	
	
	
		
			
			requester.h relied on qemu/compiler.h definitions to handle GCC_FMT_ATTR() stub, but this include was removed as part of scripted clean-ups via 30456d5: all: Clean up includes under the assumption that all C files would have included it via qemu/osdep.h at that point. requester.cpp was likely missed due to C++ files requiring manual/special handling as well as VSS build options needing to be enabled to trigger build failures. Fix this by including qemu/osdep.h. That in turn pulls in a macro from qapi/error.h that conflicts with a struct field name in requester.h, so fix that as well by renaming the field. While we're at it, fix up provider.cpp/install.cpp to include osdep.h as well. Cc: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com> Reviewed-by: Eric Blake <eblake@redhat.com>
		
			
				
	
	
		
			506 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			506 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  * QEMU Guest Agent win32 VSS Requester implementations
 | |
|  *
 | |
|  * Copyright Hitachi Data Systems Corp. 2013
 | |
|  *
 | |
|  * Authors:
 | |
|  *  Tomoki Sekiyama   <tomoki.sekiyama@hds.com>
 | |
|  *
 | |
|  * This work is licensed under the terms of the GNU GPL, version 2 or later.
 | |
|  * See the COPYING file in the top-level directory.
 | |
|  */
 | |
| 
 | |
| #include "qemu/osdep.h"
 | |
| #include "vss-common.h"
 | |
| #include "requester.h"
 | |
| #include "assert.h"
 | |
| #include "inc/win2003/vswriter.h"
 | |
| #include "inc/win2003/vsbackup.h"
 | |
| 
 | |
| /* Max wait time for frozen event (VSS can only hold writes for 10 seconds) */
 | |
| #define VSS_TIMEOUT_FREEZE_MSEC 10000
 | |
| 
 | |
| /* Call QueryStatus every 10 ms while waiting for frozen event */
 | |
| #define VSS_TIMEOUT_EVENT_MSEC 10
 | |
| 
 | |
| #define err_set(e, err, fmt, ...)                                           \
 | |
|     ((e)->error_setg_win32_wrapper((e)->errp, __FILE__, __LINE__, __func__, \
 | |
|                                    err, fmt, ## __VA_ARGS__))
 | |
| /* Bad idea, works only when (e)->errp != NULL: */
 | |
| #define err_is_set(e) ((e)->errp && *(e)->errp)
 | |
| /* To lift this restriction, error_propagate(), like we do in QEMU code */
 | |
| 
 | |
| /* Handle to VSSAPI.DLL */
 | |
| static HMODULE hLib;
 | |
| 
 | |
| /* Functions in VSSAPI.DLL */
 | |
| typedef HRESULT(STDAPICALLTYPE * t_CreateVssBackupComponents)(
 | |
|     OUT IVssBackupComponents**);
 | |
| typedef void(APIENTRY * t_VssFreeSnapshotProperties)(IN VSS_SNAPSHOT_PROP*);
 | |
| static t_CreateVssBackupComponents pCreateVssBackupComponents;
 | |
| static t_VssFreeSnapshotProperties pVssFreeSnapshotProperties;
 | |
| 
 | |
| /* Variables used while applications and filesystes are frozen by VSS */
 | |
| static struct QGAVSSContext {
 | |
|     IVssBackupComponents *pVssbc;  /* VSS requester interface */
 | |
|     IVssAsync *pAsyncSnapshot;     /* async info of VSS snapshot operation */
 | |
|     HANDLE hEventFrozen;           /* notify fs/writer freeze from provider */
 | |
|     HANDLE hEventThaw;             /* request provider to thaw */
 | |
|     HANDLE hEventTimeout;          /* notify timeout in provider */
 | |
|     int cFrozenVols;               /* number of frozen volumes */
 | |
| } vss_ctx;
 | |
| 
 | |
| STDAPI requester_init(void)
 | |
| {
 | |
|     COMInitializer initializer; /* to call CoInitializeSecurity */
 | |
|     HRESULT hr = CoInitializeSecurity(
 | |
|         NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
 | |
|         RPC_C_IMP_LEVEL_IDENTIFY, NULL, EOAC_NONE, NULL);
 | |
|     if (FAILED(hr)) {
 | |
|         fprintf(stderr, "failed to CoInitializeSecurity (error %lx)\n", hr);
 | |
|         return hr;
 | |
|     }
 | |
| 
 | |
|     hLib = LoadLibraryA("VSSAPI.DLL");
 | |
|     if (!hLib) {
 | |
|         fprintf(stderr, "failed to load VSSAPI.DLL\n");
 | |
|         return HRESULT_FROM_WIN32(GetLastError());
 | |
|     }
 | |
| 
 | |
|     pCreateVssBackupComponents = (t_CreateVssBackupComponents)
 | |
|         GetProcAddress(hLib,
 | |
| #ifdef _WIN64 /* 64bit environment */
 | |
|         "?CreateVssBackupComponents@@YAJPEAPEAVIVssBackupComponents@@@Z"
 | |
| #else /* 32bit environment */
 | |
|         "?CreateVssBackupComponents@@YGJPAPAVIVssBackupComponents@@@Z"
 | |
| #endif
 | |
|         );
 | |
|     if (!pCreateVssBackupComponents) {
 | |
|         fprintf(stderr, "failed to get proc address from VSSAPI.DLL\n");
 | |
|         return HRESULT_FROM_WIN32(GetLastError());
 | |
|     }
 | |
| 
 | |
|     pVssFreeSnapshotProperties = (t_VssFreeSnapshotProperties)
 | |
|         GetProcAddress(hLib, "VssFreeSnapshotProperties");
 | |
|     if (!pVssFreeSnapshotProperties) {
 | |
|         fprintf(stderr, "failed to get proc address from VSSAPI.DLL\n");
 | |
|         return HRESULT_FROM_WIN32(GetLastError());
 | |
|     }
 | |
| 
 | |
|     return S_OK;
 | |
| }
 | |
| 
 | |
| static void requester_cleanup(void)
 | |
| {
 | |
|     if (vss_ctx.hEventFrozen) {
 | |
|         CloseHandle(vss_ctx.hEventFrozen);
 | |
|         vss_ctx.hEventFrozen = NULL;
 | |
|     }
 | |
|     if (vss_ctx.hEventThaw) {
 | |
|         CloseHandle(vss_ctx.hEventThaw);
 | |
|         vss_ctx.hEventThaw = NULL;
 | |
|     }
 | |
|     if (vss_ctx.hEventTimeout) {
 | |
|         CloseHandle(vss_ctx.hEventTimeout);
 | |
|         vss_ctx.hEventTimeout = NULL;
 | |
|     }
 | |
|     if (vss_ctx.pAsyncSnapshot) {
 | |
|         vss_ctx.pAsyncSnapshot->Release();
 | |
|         vss_ctx.pAsyncSnapshot = NULL;
 | |
|     }
 | |
|     if (vss_ctx.pVssbc) {
 | |
|         vss_ctx.pVssbc->Release();
 | |
|         vss_ctx.pVssbc = NULL;
 | |
|     }
 | |
|     vss_ctx.cFrozenVols = 0;
 | |
| }
 | |
| 
 | |
| STDAPI requester_deinit(void)
 | |
| {
 | |
|     requester_cleanup();
 | |
| 
 | |
|     pCreateVssBackupComponents = NULL;
 | |
|     pVssFreeSnapshotProperties = NULL;
 | |
|     if (hLib) {
 | |
|         FreeLibrary(hLib);
 | |
|         hLib = NULL;
 | |
|     }
 | |
| 
 | |
|     return S_OK;
 | |
| }
 | |
| 
 | |
| static HRESULT WaitForAsync(IVssAsync *pAsync)
 | |
| {
 | |
|     HRESULT ret, hr;
 | |
| 
 | |
|     do {
 | |
|         hr = pAsync->Wait();
 | |
|         if (FAILED(hr)) {
 | |
|             ret = hr;
 | |
|             break;
 | |
|         }
 | |
|         hr = pAsync->QueryStatus(&ret, NULL);
 | |
|         if (FAILED(hr)) {
 | |
|             ret = hr;
 | |
|             break;
 | |
|         }
 | |
|     } while (ret == VSS_S_ASYNC_PENDING);
 | |
| 
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| static void AddComponents(ErrorSet *errset)
 | |
| {
 | |
|     unsigned int cWriters, i;
 | |
|     VSS_ID id, idInstance, idWriter;
 | |
|     BSTR bstrWriterName = NULL;
 | |
|     VSS_USAGE_TYPE usage;
 | |
|     VSS_SOURCE_TYPE source;
 | |
|     unsigned int cComponents, c1, c2, j;
 | |
|     COMPointer<IVssExamineWriterMetadata> pMetadata;
 | |
|     COMPointer<IVssWMComponent> pComponent;
 | |
|     PVSSCOMPONENTINFO info;
 | |
|     HRESULT hr;
 | |
| 
 | |
|     hr = vss_ctx.pVssbc->GetWriterMetadataCount(&cWriters);
 | |
|     if (FAILED(hr)) {
 | |
|         err_set(errset, hr, "failed to get writer metadata count");
 | |
|         goto out;
 | |
|     }
 | |
| 
 | |
|     for (i = 0; i < cWriters; i++) {
 | |
|         hr = vss_ctx.pVssbc->GetWriterMetadata(i, &id, pMetadata.replace());
 | |
|         if (FAILED(hr)) {
 | |
|             err_set(errset, hr, "failed to get writer metadata of %d/%d",
 | |
|                              i, cWriters);
 | |
|             goto out;
 | |
|         }
 | |
| 
 | |
|         hr = pMetadata->GetIdentity(&idInstance, &idWriter,
 | |
|                                     &bstrWriterName, &usage, &source);
 | |
|         if (FAILED(hr)) {
 | |
|             err_set(errset, hr, "failed to get identity of writer %d/%d",
 | |
|                              i, cWriters);
 | |
|             goto out;
 | |
|         }
 | |
| 
 | |
|         hr = pMetadata->GetFileCounts(&c1, &c2, &cComponents);
 | |
|         if (FAILED(hr)) {
 | |
|             err_set(errset, hr, "failed to get file counts of %S",
 | |
|                              bstrWriterName);
 | |
|             goto out;
 | |
|         }
 | |
| 
 | |
|         for (j = 0; j < cComponents; j++) {
 | |
|             hr = pMetadata->GetComponent(j, pComponent.replace());
 | |
|             if (FAILED(hr)) {
 | |
|                 err_set(errset, hr,
 | |
|                                  "failed to get component %d/%d of %S",
 | |
|                                  j, cComponents, bstrWriterName);
 | |
|                 goto out;
 | |
|             }
 | |
| 
 | |
|             hr = pComponent->GetComponentInfo(&info);
 | |
|             if (FAILED(hr)) {
 | |
|                 err_set(errset, hr,
 | |
|                                  "failed to get component info %d/%d of %S",
 | |
|                                  j, cComponents, bstrWriterName);
 | |
|                 goto out;
 | |
|             }
 | |
| 
 | |
|             if (info->bSelectable) {
 | |
|                 hr = vss_ctx.pVssbc->AddComponent(idInstance, idWriter,
 | |
|                                                   info->type,
 | |
|                                                   info->bstrLogicalPath,
 | |
|                                                   info->bstrComponentName);
 | |
|                 if (FAILED(hr)) {
 | |
|                     err_set(errset, hr, "failed to add component %S(%S)",
 | |
|                                      info->bstrComponentName, bstrWriterName);
 | |
|                     goto out;
 | |
|                 }
 | |
|             }
 | |
|             SysFreeString(bstrWriterName);
 | |
|             bstrWriterName = NULL;
 | |
|             pComponent->FreeComponentInfo(info);
 | |
|             info = NULL;
 | |
|         }
 | |
|     }
 | |
| out:
 | |
|     if (bstrWriterName) {
 | |
|         SysFreeString(bstrWriterName);
 | |
|     }
 | |
|     if (pComponent && info) {
 | |
|         pComponent->FreeComponentInfo(info);
 | |
|     }
 | |
| }
 | |
| 
 | |
| void requester_freeze(int *num_vols, ErrorSet *errset)
 | |
| {
 | |
|     COMPointer<IVssAsync> pAsync;
 | |
|     HANDLE volume;
 | |
|     HRESULT hr;
 | |
|     LONG ctx;
 | |
|     GUID guidSnapshotSet = GUID_NULL;
 | |
|     SECURITY_DESCRIPTOR sd;
 | |
|     SECURITY_ATTRIBUTES sa;
 | |
|     WCHAR short_volume_name[64], *display_name = short_volume_name;
 | |
|     DWORD wait_status;
 | |
|     int num_fixed_drives = 0, i;
 | |
| 
 | |
|     if (vss_ctx.pVssbc) { /* already frozen */
 | |
|         *num_vols = 0;
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     CoInitialize(NULL);
 | |
| 
 | |
|     /* Allow unrestricted access to events */
 | |
|     InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
 | |
|     SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE);
 | |
|     sa.nLength = sizeof(sa);
 | |
|     sa.lpSecurityDescriptor = &sd;
 | |
|     sa.bInheritHandle = FALSE;
 | |
| 
 | |
|     vss_ctx.hEventFrozen = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_FROZEN);
 | |
|     if (!vss_ctx.hEventFrozen) {
 | |
|         err_set(errset, GetLastError(), "failed to create event %s",
 | |
|                 EVENT_NAME_FROZEN);
 | |
|         goto out;
 | |
|     }
 | |
|     vss_ctx.hEventThaw = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_THAW);
 | |
|     if (!vss_ctx.hEventThaw) {
 | |
|         err_set(errset, GetLastError(), "failed to create event %s",
 | |
|                 EVENT_NAME_THAW);
 | |
|         goto out;
 | |
|     }
 | |
|     vss_ctx.hEventTimeout = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_TIMEOUT);
 | |
|     if (!vss_ctx.hEventTimeout) {
 | |
|         err_set(errset, GetLastError(), "failed to create event %s",
 | |
|                 EVENT_NAME_TIMEOUT);
 | |
|         goto out;
 | |
|     }
 | |
| 
 | |
|     assert(pCreateVssBackupComponents != NULL);
 | |
|     hr = pCreateVssBackupComponents(&vss_ctx.pVssbc);
 | |
|     if (FAILED(hr)) {
 | |
|         err_set(errset, hr, "failed to create VSS backup components");
 | |
|         goto out;
 | |
|     }
 | |
| 
 | |
|     hr = vss_ctx.pVssbc->InitializeForBackup();
 | |
|     if (FAILED(hr)) {
 | |
|         err_set(errset, hr, "failed to initialize for backup");
 | |
|         goto out;
 | |
|     }
 | |
| 
 | |
|     hr = vss_ctx.pVssbc->SetBackupState(true, true, VSS_BT_FULL, false);
 | |
|     if (FAILED(hr)) {
 | |
|         err_set(errset, hr, "failed to set backup state");
 | |
|         goto out;
 | |
|     }
 | |
| 
 | |
|     /*
 | |
|      * Currently writable snapshots are not supported.
 | |
|      * To prevent the final commit (which requires to write to snapshots),
 | |
|      * ATTR_NO_AUTORECOVERY and ATTR_TRANSPORTABLE are specified here.
 | |
|      */
 | |
|     ctx = VSS_CTX_APP_ROLLBACK | VSS_VOLSNAP_ATTR_TRANSPORTABLE |
 | |
|         VSS_VOLSNAP_ATTR_NO_AUTORECOVERY | VSS_VOLSNAP_ATTR_TXF_RECOVERY;
 | |
|     hr = vss_ctx.pVssbc->SetContext(ctx);
 | |
|     if (hr == (HRESULT)VSS_E_UNSUPPORTED_CONTEXT) {
 | |
|         /* Non-server version of Windows doesn't support ATTR_TRANSPORTABLE */
 | |
|         ctx &= ~VSS_VOLSNAP_ATTR_TRANSPORTABLE;
 | |
|         hr = vss_ctx.pVssbc->SetContext(ctx);
 | |
|     }
 | |
|     if (FAILED(hr)) {
 | |
|         err_set(errset, hr, "failed to set backup context");
 | |
|         goto out;
 | |
|     }
 | |
| 
 | |
|     hr = vss_ctx.pVssbc->GatherWriterMetadata(pAsync.replace());
 | |
|     if (SUCCEEDED(hr)) {
 | |
|         hr = WaitForAsync(pAsync);
 | |
|     }
 | |
|     if (FAILED(hr)) {
 | |
|         err_set(errset, hr, "failed to gather writer metadata");
 | |
|         goto out;
 | |
|     }
 | |
| 
 | |
|     AddComponents(errset);
 | |
|     if (err_is_set(errset)) {
 | |
|         goto out;
 | |
|     }
 | |
| 
 | |
|     hr = vss_ctx.pVssbc->StartSnapshotSet(&guidSnapshotSet);
 | |
|     if (FAILED(hr)) {
 | |
|         err_set(errset, hr, "failed to start snapshot set");
 | |
|         goto out;
 | |
|     }
 | |
| 
 | |
|     volume = FindFirstVolumeW(short_volume_name, sizeof(short_volume_name));
 | |
|     if (volume == INVALID_HANDLE_VALUE) {
 | |
|         err_set(errset, hr, "failed to find first volume");
 | |
|         goto out;
 | |
|     }
 | |
|     for (;;) {
 | |
|         if (GetDriveTypeW(short_volume_name) == DRIVE_FIXED) {
 | |
|             VSS_ID pid;
 | |
|             hr = vss_ctx.pVssbc->AddToSnapshotSet(short_volume_name,
 | |
|                                                   g_gProviderId, &pid);
 | |
|             if (FAILED(hr)) {
 | |
|                 WCHAR volume_path_name[PATH_MAX];
 | |
|                 if (GetVolumePathNamesForVolumeNameW(
 | |
|                         short_volume_name, volume_path_name,
 | |
|                         sizeof(volume_path_name), NULL) && *volume_path_name) {
 | |
|                     display_name = volume_path_name;
 | |
|                 }
 | |
|                 err_set(errset, hr, "failed to add %S to snapshot set",
 | |
|                                  display_name);
 | |
|                 FindVolumeClose(volume);
 | |
|                 goto out;
 | |
|             }
 | |
|             num_fixed_drives++;
 | |
|         }
 | |
|         if (!FindNextVolumeW(volume, short_volume_name,
 | |
|                              sizeof(short_volume_name))) {
 | |
|             FindVolumeClose(volume);
 | |
|             break;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (num_fixed_drives == 0) {
 | |
|         goto out; /* If there is no fixed drive, just exit. */
 | |
|     }
 | |
| 
 | |
|     hr = vss_ctx.pVssbc->PrepareForBackup(pAsync.replace());
 | |
|     if (SUCCEEDED(hr)) {
 | |
|         hr = WaitForAsync(pAsync);
 | |
|     }
 | |
|     if (FAILED(hr)) {
 | |
|         err_set(errset, hr, "failed to prepare for backup");
 | |
|         goto out;
 | |
|     }
 | |
| 
 | |
|     hr = vss_ctx.pVssbc->GatherWriterStatus(pAsync.replace());
 | |
|     if (SUCCEEDED(hr)) {
 | |
|         hr = WaitForAsync(pAsync);
 | |
|     }
 | |
|     if (FAILED(hr)) {
 | |
|         err_set(errset, hr, "failed to gather writer status");
 | |
|         goto out;
 | |
|     }
 | |
| 
 | |
|     /*
 | |
|      * Start VSS quiescing operations.
 | |
|      * CQGAVssProvider::CommitSnapshots will kick vss_ctx.hEventFrozen
 | |
|      * after the applications and filesystems are frozen.
 | |
|      */
 | |
|     hr = vss_ctx.pVssbc->DoSnapshotSet(&vss_ctx.pAsyncSnapshot);
 | |
|     if (FAILED(hr)) {
 | |
|         err_set(errset, hr, "failed to do snapshot set");
 | |
|         goto out;
 | |
|     }
 | |
| 
 | |
|     /* Need to call QueryStatus several times to make VSS provider progress */
 | |
|     for (i = 0; i < VSS_TIMEOUT_FREEZE_MSEC/VSS_TIMEOUT_EVENT_MSEC; i++) {
 | |
|         HRESULT hr2 = vss_ctx.pAsyncSnapshot->QueryStatus(&hr, NULL);
 | |
|         if (FAILED(hr2)) {
 | |
|             err_set(errset, hr, "failed to do snapshot set");
 | |
|             goto out;
 | |
|         }
 | |
|         if (hr != VSS_S_ASYNC_PENDING) {
 | |
|             err_set(errset, E_FAIL,
 | |
|                     "DoSnapshotSet exited without Frozen event");
 | |
|             goto out;
 | |
|         }
 | |
|         wait_status = WaitForSingleObject(vss_ctx.hEventFrozen,
 | |
|                                           VSS_TIMEOUT_EVENT_MSEC);
 | |
|         if (wait_status != WAIT_TIMEOUT) {
 | |
|             break;
 | |
|         }
 | |
|     }
 | |
|     if (wait_status != WAIT_OBJECT_0) {
 | |
|         err_set(errset, E_FAIL,
 | |
|                 "couldn't receive Frozen event from VSS provider");
 | |
|         goto out;
 | |
|     }
 | |
| 
 | |
|     *num_vols = vss_ctx.cFrozenVols = num_fixed_drives;
 | |
|     return;
 | |
| 
 | |
| out:
 | |
|     if (vss_ctx.pVssbc) {
 | |
|         vss_ctx.pVssbc->AbortBackup();
 | |
|     }
 | |
|     requester_cleanup();
 | |
|     CoUninitialize();
 | |
| }
 | |
| 
 | |
| 
 | |
| void requester_thaw(int *num_vols, ErrorSet *errset)
 | |
| {
 | |
|     COMPointer<IVssAsync> pAsync;
 | |
| 
 | |
|     if (!vss_ctx.hEventThaw) {
 | |
|         /*
 | |
|          * In this case, DoSnapshotSet is aborted or not started,
 | |
|          * and no volumes must be frozen. We return without an error.
 | |
|          */
 | |
|         *num_vols = 0;
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     /* Tell the provider that the snapshot is finished. */
 | |
|     SetEvent(vss_ctx.hEventThaw);
 | |
| 
 | |
|     assert(vss_ctx.pVssbc);
 | |
|     assert(vss_ctx.pAsyncSnapshot);
 | |
| 
 | |
|     HRESULT hr = WaitForAsync(vss_ctx.pAsyncSnapshot);
 | |
|     switch (hr) {
 | |
|     case VSS_S_ASYNC_FINISHED:
 | |
|         hr = vss_ctx.pVssbc->BackupComplete(pAsync.replace());
 | |
|         if (SUCCEEDED(hr)) {
 | |
|             hr = WaitForAsync(pAsync);
 | |
|         }
 | |
|         if (FAILED(hr)) {
 | |
|             err_set(errset, hr, "failed to complete backup");
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|     case (HRESULT)VSS_E_OBJECT_NOT_FOUND:
 | |
|         /*
 | |
|          * On Windows earlier than 2008 SP2 which does not support
 | |
|          * VSS_VOLSNAP_ATTR_NO_AUTORECOVERY context, the final commit is not
 | |
|          * skipped and VSS is aborted by VSS_E_OBJECT_NOT_FOUND. However, as
 | |
|          * the system had been frozen until fsfreeze-thaw command was issued,
 | |
|          * we ignore this error.
 | |
|          */
 | |
|         vss_ctx.pVssbc->AbortBackup();
 | |
|         break;
 | |
| 
 | |
|     case VSS_E_UNEXPECTED_PROVIDER_ERROR:
 | |
|         if (WaitForSingleObject(vss_ctx.hEventTimeout, 0) != WAIT_OBJECT_0) {
 | |
|             err_set(errset, hr, "unexpected error in VSS provider");
 | |
|             break;
 | |
|         }
 | |
|         /* fall through if hEventTimeout is signaled */
 | |
| 
 | |
|     case (HRESULT)VSS_E_HOLD_WRITES_TIMEOUT:
 | |
|         err_set(errset, hr, "couldn't hold writes: "
 | |
|                 "fsfreeze is limited up to 10 seconds");
 | |
|         break;
 | |
| 
 | |
|     default:
 | |
|         err_set(errset, hr, "failed to do snapshot set");
 | |
|     }
 | |
| 
 | |
|     if (err_is_set(errset)) {
 | |
|         vss_ctx.pVssbc->AbortBackup();
 | |
|     }
 | |
|     *num_vols = vss_ctx.cFrozenVols;
 | |
|     requester_cleanup();
 | |
| 
 | |
|     CoUninitialize();
 | |
| }
 |