2 * QEMU Guest Agent win32 VSS Requester implementations
4 * Copyright Hitachi Data Systems Corp. 2013
7 * Tomoki Sekiyama <tomoki.sekiyama@hds.com>
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.
13 #include "qemu/osdep.h"
14 #include "vss-common.h"
15 #include "vss-debug.h"
16 #include "requester.h"
21 /* Max wait time for frozen event (VSS can only hold writes for 10 seconds) */
22 #define VSS_TIMEOUT_FREEZE_MSEC 60000
24 /* Call QueryStatus every 10 ms while waiting for frozen event */
25 #define VSS_TIMEOUT_EVENT_MSEC 10
27 #define DEFAULT_VSS_BACKUP_TYPE VSS_BT_FULL
29 #define err_set(e, err, fmt, ...) { \
30 (e)->error_setg_win32_wrapper((e)->errp, __FILE__, __LINE__, __func__, \
31 err, fmt, ## __VA_ARGS__); \
32 qga_debug(fmt, ## __VA_ARGS__); \
34 /* Bad idea, works only when (e)->errp != NULL: */
35 #define err_is_set(e) ((e)->errp && *(e)->errp)
36 /* To lift this restriction, error_propagate(), like we do in QEMU code */
38 /* Handle to VSSAPI.DLL */
41 /* Functions in VSSAPI.DLL */
42 typedef HRESULT(STDAPICALLTYPE
* t_CreateVssBackupComponents
)(
43 OUT IVssBackupComponents
**);
44 typedef void(APIENTRY
* t_VssFreeSnapshotProperties
)(IN VSS_SNAPSHOT_PROP
*);
45 static t_CreateVssBackupComponents pCreateVssBackupComponents
;
46 static t_VssFreeSnapshotProperties pVssFreeSnapshotProperties
;
48 /* Variables used while applications and filesystes are frozen by VSS */
49 static struct QGAVSSContext
{
50 IVssBackupComponents
*pVssbc
; /* VSS requester interface */
51 IVssAsync
*pAsyncSnapshot
; /* async info of VSS snapshot operation */
52 HANDLE hEventFrozen
; /* notify fs/writer freeze from provider */
53 HANDLE hEventThaw
; /* request provider to thaw */
54 HANDLE hEventTimeout
; /* notify timeout in provider */
55 int cFrozenVols
; /* number of frozen volumes */
58 STDAPI
requester_init(void)
62 COMInitializer initializer
; /* to call CoInitializeSecurity */
63 HRESULT hr
= CoInitializeSecurity(
64 NULL
, -1, NULL
, NULL
, RPC_C_AUTHN_LEVEL_PKT_PRIVACY
,
65 RPC_C_IMP_LEVEL_IDENTIFY
, NULL
, EOAC_NONE
, NULL
);
67 qga_debug("failed to CoInitializeSecurity (error %lx)", hr
);
71 hLib
= LoadLibraryA("VSSAPI.DLL");
73 qga_debug("failed to load VSSAPI.DLL");
74 return HRESULT_FROM_WIN32(GetLastError());
77 #pragma GCC diagnostic ignored "-Wcast-function-type"
78 pCreateVssBackupComponents
= (t_CreateVssBackupComponents
)
80 #ifdef _WIN64 /* 64bit environment */
81 "?CreateVssBackupComponents@@YAJPEAPEAVIVssBackupComponents@@@Z"
82 #else /* 32bit environment */
83 "?CreateVssBackupComponents@@YGJPAPAVIVssBackupComponents@@@Z"
86 if (!pCreateVssBackupComponents
) {
87 qga_debug("failed to get proc address from VSSAPI.DLL");
88 return HRESULT_FROM_WIN32(GetLastError());
91 pVssFreeSnapshotProperties
= (t_VssFreeSnapshotProperties
)
92 GetProcAddress(hLib
, "VssFreeSnapshotProperties");
93 if (!pVssFreeSnapshotProperties
) {
94 qga_debug("failed to get proc address from VSSAPI.DLL");
95 return HRESULT_FROM_WIN32(GetLastError());
102 static void requester_cleanup(void)
106 if (vss_ctx
.hEventFrozen
) {
107 CloseHandle(vss_ctx
.hEventFrozen
);
108 vss_ctx
.hEventFrozen
= NULL
;
110 if (vss_ctx
.hEventThaw
) {
111 CloseHandle(vss_ctx
.hEventThaw
);
112 vss_ctx
.hEventThaw
= NULL
;
114 if (vss_ctx
.hEventTimeout
) {
115 CloseHandle(vss_ctx
.hEventTimeout
);
116 vss_ctx
.hEventTimeout
= NULL
;
118 if (vss_ctx
.pAsyncSnapshot
) {
119 vss_ctx
.pAsyncSnapshot
->Release();
120 vss_ctx
.pAsyncSnapshot
= NULL
;
122 if (vss_ctx
.pVssbc
) {
123 vss_ctx
.pVssbc
->Release();
124 vss_ctx
.pVssbc
= NULL
;
126 vss_ctx
.cFrozenVols
= 0;
130 STDAPI
requester_deinit(void)
136 pCreateVssBackupComponents
= NULL
;
137 pVssFreeSnapshotProperties
= NULL
;
147 static HRESULT
WaitForAsync(IVssAsync
*pAsync
)
159 hr
= pAsync
->QueryStatus(&ret
, NULL
);
164 } while (ret
== VSS_S_ASYNC_PENDING
);
170 static void AddComponents(ErrorSet
*errset
)
174 unsigned int cWriters
, i
;
175 VSS_ID id
, idInstance
, idWriter
;
176 BSTR bstrWriterName
= NULL
;
177 VSS_USAGE_TYPE usage
;
178 VSS_SOURCE_TYPE source
;
179 unsigned int cComponents
, c1
, c2
, j
;
180 COMPointer
<IVssExamineWriterMetadata
> pMetadata
;
181 COMPointer
<IVssWMComponent
> pComponent
;
182 PVSSCOMPONENTINFO info
;
185 hr
= vss_ctx
.pVssbc
->GetWriterMetadataCount(&cWriters
);
187 err_set(errset
, hr
, "failed to get writer metadata count");
191 for (i
= 0; i
< cWriters
; i
++) {
192 hr
= vss_ctx
.pVssbc
->GetWriterMetadata(i
, &id
, pMetadata
.replace());
194 err_set(errset
, hr
, "failed to get writer metadata of %d/%d",
199 hr
= pMetadata
->GetIdentity(&idInstance
, &idWriter
,
200 &bstrWriterName
, &usage
, &source
);
202 err_set(errset
, hr
, "failed to get identity of writer %d/%d",
207 hr
= pMetadata
->GetFileCounts(&c1
, &c2
, &cComponents
);
209 err_set(errset
, hr
, "failed to get file counts of %S",
214 for (j
= 0; j
< cComponents
; j
++) {
215 hr
= pMetadata
->GetComponent(j
, pComponent
.replace());
218 "failed to get component %d/%d of %S",
219 j
, cComponents
, bstrWriterName
);
223 hr
= pComponent
->GetComponentInfo(&info
);
226 "failed to get component info %d/%d of %S",
227 j
, cComponents
, bstrWriterName
);
231 if (info
->bSelectable
) {
232 hr
= vss_ctx
.pVssbc
->AddComponent(idInstance
, idWriter
,
234 info
->bstrLogicalPath
,
235 info
->bstrComponentName
);
237 err_set(errset
, hr
, "failed to add component %S(%S)",
238 info
->bstrComponentName
, bstrWriterName
);
242 SysFreeString(bstrWriterName
);
243 bstrWriterName
= NULL
;
244 pComponent
->FreeComponentInfo(info
);
249 if (bstrWriterName
) {
250 SysFreeString(bstrWriterName
);
252 if (pComponent
&& info
) {
253 pComponent
->FreeComponentInfo(info
);
258 DWORD
get_reg_dword_value(HKEY baseKey
, LPCSTR subKey
, LPCSTR valueName
,
263 DWORD regGetValueError
;
265 DWORD dataSize
= sizeof(DWORD
);
267 regGetValueError
= RegGetValue(baseKey
, subKey
, valueName
, RRF_RT_DWORD
,
268 NULL
, &dwordData
, &dataSize
);
270 if (regGetValueError
!= ERROR_SUCCESS
) {
276 bool is_valid_vss_backup_type(VSS_BACKUP_TYPE vssBT
)
278 return (vssBT
> VSS_BT_UNDEFINED
&& vssBT
< VSS_BT_OTHER
);
281 VSS_BACKUP_TYPE
get_vss_backup_type(
282 VSS_BACKUP_TYPE defaultVssBT
= DEFAULT_VSS_BACKUP_TYPE
)
286 VSS_BACKUP_TYPE vssBackupType
;
288 vssBackupType
= static_cast<VSS_BACKUP_TYPE
>(
289 get_reg_dword_value(HKEY_LOCAL_MACHINE
,
290 QGA_PROVIDER_REGISTRY_ADDRESS
,
294 if (!is_valid_vss_backup_type(vssBackupType
)) {
297 return vssBackupType
;
300 void requester_freeze(int *num_vols
, void *mountpoints
, ErrorSet
*errset
)
304 COMPointer
<IVssAsync
> pAsync
;
308 GUID guidSnapshotSet
= GUID_NULL
;
309 SECURITY_DESCRIPTOR sd
;
310 SECURITY_ATTRIBUTES sa
;
311 WCHAR short_volume_name
[64], *display_name
= short_volume_name
;
313 int num_fixed_drives
= 0, i
;
314 int num_mount_points
= 0;
315 VSS_BACKUP_TYPE vss_bt
= get_vss_backup_type();
317 if (vss_ctx
.pVssbc
) { /* already frozen */
319 qga_debug("finished, already frozen");
325 /* Allow unrestricted access to events */
326 InitializeSecurityDescriptor(&sd
, SECURITY_DESCRIPTOR_REVISION
);
327 SetSecurityDescriptorDacl(&sd
, TRUE
, NULL
, FALSE
);
328 sa
.nLength
= sizeof(sa
);
329 sa
.lpSecurityDescriptor
= &sd
;
330 sa
.bInheritHandle
= FALSE
;
332 vss_ctx
.hEventFrozen
= CreateEvent(&sa
, TRUE
, FALSE
, EVENT_NAME_FROZEN
);
333 if (!vss_ctx
.hEventFrozen
) {
334 err_set(errset
, GetLastError(), "failed to create event %s",
338 vss_ctx
.hEventThaw
= CreateEvent(&sa
, TRUE
, FALSE
, EVENT_NAME_THAW
);
339 if (!vss_ctx
.hEventThaw
) {
340 err_set(errset
, GetLastError(), "failed to create event %s",
344 vss_ctx
.hEventTimeout
= CreateEvent(&sa
, TRUE
, FALSE
, EVENT_NAME_TIMEOUT
);
345 if (!vss_ctx
.hEventTimeout
) {
346 err_set(errset
, GetLastError(), "failed to create event %s",
351 assert(pCreateVssBackupComponents
!= NULL
);
352 hr
= pCreateVssBackupComponents(&vss_ctx
.pVssbc
);
354 err_set(errset
, hr
, "failed to create VSS backup components");
358 hr
= vss_ctx
.pVssbc
->InitializeForBackup();
360 err_set(errset
, hr
, "failed to initialize for backup");
364 hr
= vss_ctx
.pVssbc
->SetBackupState(true, true, vss_bt
, false);
366 err_set(errset
, hr
, "failed to set backup state");
371 * Currently writable snapshots are not supported.
372 * To prevent the final commit (which requires to write to snapshots),
373 * ATTR_NO_AUTORECOVERY and ATTR_TRANSPORTABLE are specified here.
375 ctx
= VSS_CTX_APP_ROLLBACK
| VSS_VOLSNAP_ATTR_TRANSPORTABLE
|
376 VSS_VOLSNAP_ATTR_NO_AUTORECOVERY
| VSS_VOLSNAP_ATTR_TXF_RECOVERY
;
377 hr
= vss_ctx
.pVssbc
->SetContext(ctx
);
378 if (hr
== (HRESULT
)VSS_E_UNSUPPORTED_CONTEXT
) {
379 /* Non-server version of Windows doesn't support ATTR_TRANSPORTABLE */
380 ctx
&= ~VSS_VOLSNAP_ATTR_TRANSPORTABLE
;
381 hr
= vss_ctx
.pVssbc
->SetContext(ctx
);
384 err_set(errset
, hr
, "failed to set backup context");
388 hr
= vss_ctx
.pVssbc
->GatherWriterMetadata(pAsync
.replace());
390 hr
= WaitForAsync(pAsync
);
393 err_set(errset
, hr
, "failed to gather writer metadata");
397 AddComponents(errset
);
398 if (err_is_set(errset
)) {
402 hr
= vss_ctx
.pVssbc
->StartSnapshotSet(&guidSnapshotSet
);
404 err_set(errset
, hr
, "failed to start snapshot set");
409 PWCHAR volume_name_wchar
;
410 for (volList
*list
= (volList
*)mountpoints
; list
; list
= list
->next
) {
411 size_t len
= strlen(list
->value
) + 1;
412 size_t converted
= 0;
415 volume_name_wchar
= new wchar_t[len
];
416 mbstowcs_s(&converted
, volume_name_wchar
, len
,
417 list
->value
, _TRUNCATE
);
419 hr
= vss_ctx
.pVssbc
->AddToSnapshotSet(volume_name_wchar
,
420 g_gProviderId
, &pid
);
422 err_set(errset
, hr
, "failed to add %S to snapshot set",
424 delete[] volume_name_wchar
;
429 delete[] volume_name_wchar
;
432 if (num_mount_points
== 0) {
433 /* If there is no valid mount points, just exit. */
439 volume
= FindFirstVolumeW(short_volume_name
, sizeof(short_volume_name
));
440 if (volume
== INVALID_HANDLE_VALUE
) {
441 err_set(errset
, hr
, "failed to find first volume");
446 if (GetDriveTypeW(short_volume_name
) == DRIVE_FIXED
) {
448 hr
= vss_ctx
.pVssbc
->AddToSnapshotSet(short_volume_name
,
449 g_gProviderId
, &pid
);
451 WCHAR volume_path_name
[PATH_MAX
];
452 if (GetVolumePathNamesForVolumeNameW(
453 short_volume_name
, volume_path_name
,
454 sizeof(volume_path_name
), NULL
) &&
456 display_name
= volume_path_name
;
458 err_set(errset
, hr
, "failed to add %S to snapshot set",
460 FindVolumeClose(volume
);
465 if (!FindNextVolumeW(volume
, short_volume_name
,
466 sizeof(short_volume_name
))) {
467 FindVolumeClose(volume
);
472 if (num_fixed_drives
== 0) {
473 goto out
; /* If there is no fixed drive, just exit. */
477 qga_debug("preparing for backup");
478 hr
= vss_ctx
.pVssbc
->PrepareForBackup(pAsync
.replace());
480 hr
= WaitForAsync(pAsync
);
483 err_set(errset
, hr
, "failed to prepare for backup");
487 hr
= vss_ctx
.pVssbc
->GatherWriterStatus(pAsync
.replace());
489 hr
= WaitForAsync(pAsync
);
492 err_set(errset
, hr
, "failed to gather writer status");
497 * Start VSS quiescing operations.
498 * CQGAVssProvider::CommitSnapshots will kick vss_ctx.hEventFrozen
499 * after the applications and filesystems are frozen.
501 qga_debug("do snapshot set");
502 hr
= vss_ctx
.pVssbc
->DoSnapshotSet(&vss_ctx
.pAsyncSnapshot
);
504 err_set(errset
, hr
, "failed to do snapshot set");
508 /* Need to call QueryStatus several times to make VSS provider progress */
509 for (i
= 0; i
< VSS_TIMEOUT_FREEZE_MSEC
/VSS_TIMEOUT_EVENT_MSEC
; i
++) {
510 HRESULT hr2
= vss_ctx
.pAsyncSnapshot
->QueryStatus(&hr
, NULL
);
512 err_set(errset
, hr
, "failed to do snapshot set");
515 if (hr
!= VSS_S_ASYNC_PENDING
) {
516 err_set(errset
, E_FAIL
,
517 "DoSnapshotSet exited without Frozen event");
520 wait_status
= WaitForSingleObject(vss_ctx
.hEventFrozen
,
521 VSS_TIMEOUT_EVENT_MSEC
);
522 if (wait_status
!= WAIT_TIMEOUT
) {
527 if (wait_status
== WAIT_TIMEOUT
) {
528 err_set(errset
, E_FAIL
,
529 "timeout when try to receive Frozen event from VSS provider");
530 /* If we are here, VSS had timeout.
531 * Don't call AbortBackup, just return directly.
536 if (wait_status
!= WAIT_OBJECT_0
) {
537 err_set(errset
, E_FAIL
,
538 "couldn't receive Frozen event from VSS provider");
543 *num_vols
= vss_ctx
.cFrozenVols
= num_mount_points
;
545 *num_vols
= vss_ctx
.cFrozenVols
= num_fixed_drives
;
548 qga_debug("end successful");
552 if (vss_ctx
.pVssbc
) {
553 vss_ctx
.pVssbc
->AbortBackup();
564 void requester_thaw(int *num_vols
, void *mountpints
, ErrorSet
*errset
)
567 COMPointer
<IVssAsync
> pAsync
;
569 if (!vss_ctx
.hEventThaw
) {
571 * In this case, DoSnapshotSet is aborted or not started,
572 * and no volumes must be frozen. We return without an error.
575 qga_debug("finished, no volumes were frozen");
580 /* Tell the provider that the snapshot is finished. */
581 SetEvent(vss_ctx
.hEventThaw
);
583 assert(vss_ctx
.pVssbc
);
584 assert(vss_ctx
.pAsyncSnapshot
);
586 HRESULT hr
= WaitForAsync(vss_ctx
.pAsyncSnapshot
);
588 case VSS_S_ASYNC_FINISHED
:
589 hr
= vss_ctx
.pVssbc
->BackupComplete(pAsync
.replace());
591 hr
= WaitForAsync(pAsync
);
594 err_set(errset
, hr
, "failed to complete backup");
598 case (HRESULT
)VSS_E_OBJECT_NOT_FOUND
:
600 * On Windows earlier than 2008 SP2 which does not support
601 * VSS_VOLSNAP_ATTR_NO_AUTORECOVERY context, the final commit is not
602 * skipped and VSS is aborted by VSS_E_OBJECT_NOT_FOUND. However, as
603 * the system had been frozen until fsfreeze-thaw command was issued,
604 * we ignore this error.
606 vss_ctx
.pVssbc
->AbortBackup();
609 case VSS_E_UNEXPECTED_PROVIDER_ERROR
:
610 if (WaitForSingleObject(vss_ctx
.hEventTimeout
, 0) != WAIT_OBJECT_0
) {
611 err_set(errset
, hr
, "unexpected error in VSS provider");
614 /* fall through if hEventTimeout is signaled */
616 case (HRESULT
)VSS_E_HOLD_WRITES_TIMEOUT
:
617 err_set(errset
, hr
, "couldn't hold writes: "
618 "fsfreeze is limited up to 10 seconds");
622 err_set(errset
, hr
, "failed to do snapshot set");
625 if (err_is_set(errset
)) {
626 vss_ctx
.pVssbc
->AbortBackup();
628 *num_vols
= vss_ctx
.cFrozenVols
;