Merge tag 'v9.0.0-rc3'
[qemu/ar7.git] / qga / vss-win32 / requester.cpp
blob2a7fbc44fecf582b3da050dcc7f3f2696d9471a1
1 /*
2 * QEMU Guest Agent win32 VSS Requester implementations
4 * Copyright Hitachi Data Systems Corp. 2013
6 * Authors:
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"
17 #include "install.h"
18 #include <vswriter.h>
19 #include <vsbackup.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 */
39 static HMODULE hLib;
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 */
56 } vss_ctx;
58 STDAPI requester_init(void)
60 qga_debug_begin;
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);
66 if (FAILED(hr)) {
67 qga_debug("failed to CoInitializeSecurity (error %lx)", hr);
68 return hr;
71 hLib = LoadLibraryA("VSSAPI.DLL");
72 if (!hLib) {
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)
79 GetProcAddress(hLib,
80 #ifdef _WIN64 /* 64bit environment */
81 "?CreateVssBackupComponents@@YAJPEAPEAVIVssBackupComponents@@@Z"
82 #else /* 32bit environment */
83 "?CreateVssBackupComponents@@YGJPAPAVIVssBackupComponents@@@Z"
84 #endif
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());
98 qga_debug_end;
99 return S_OK;
102 static void requester_cleanup(void)
104 qga_debug_begin;
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;
127 qga_debug_end;
130 STDAPI requester_deinit(void)
132 qga_debug_begin;
134 requester_cleanup();
136 pCreateVssBackupComponents = NULL;
137 pVssFreeSnapshotProperties = NULL;
138 if (hLib) {
139 FreeLibrary(hLib);
140 hLib = NULL;
143 qga_debug_end;
144 return S_OK;
147 static HRESULT WaitForAsync(IVssAsync *pAsync)
149 qga_debug_begin;
151 HRESULT ret, hr;
153 do {
154 hr = pAsync->Wait();
155 if (FAILED(hr)) {
156 ret = hr;
157 break;
159 hr = pAsync->QueryStatus(&ret, NULL);
160 if (FAILED(hr)) {
161 ret = hr;
162 break;
164 } while (ret == VSS_S_ASYNC_PENDING);
166 qga_debug_end;
167 return ret;
170 static void AddComponents(ErrorSet *errset)
172 qga_debug_begin;
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;
183 HRESULT hr;
185 hr = vss_ctx.pVssbc->GetWriterMetadataCount(&cWriters);
186 if (FAILED(hr)) {
187 err_set(errset, hr, "failed to get writer metadata count");
188 goto out;
191 for (i = 0; i < cWriters; i++) {
192 hr = vss_ctx.pVssbc->GetWriterMetadata(i, &id, pMetadata.replace());
193 if (FAILED(hr)) {
194 err_set(errset, hr, "failed to get writer metadata of %d/%d",
195 i, cWriters);
196 goto out;
199 hr = pMetadata->GetIdentity(&idInstance, &idWriter,
200 &bstrWriterName, &usage, &source);
201 if (FAILED(hr)) {
202 err_set(errset, hr, "failed to get identity of writer %d/%d",
203 i, cWriters);
204 goto out;
207 hr = pMetadata->GetFileCounts(&c1, &c2, &cComponents);
208 if (FAILED(hr)) {
209 err_set(errset, hr, "failed to get file counts of %S",
210 bstrWriterName);
211 goto out;
214 for (j = 0; j < cComponents; j++) {
215 hr = pMetadata->GetComponent(j, pComponent.replace());
216 if (FAILED(hr)) {
217 err_set(errset, hr,
218 "failed to get component %d/%d of %S",
219 j, cComponents, bstrWriterName);
220 goto out;
223 hr = pComponent->GetComponentInfo(&info);
224 if (FAILED(hr)) {
225 err_set(errset, hr,
226 "failed to get component info %d/%d of %S",
227 j, cComponents, bstrWriterName);
228 goto out;
231 if (info->bSelectable) {
232 hr = vss_ctx.pVssbc->AddComponent(idInstance, idWriter,
233 info->type,
234 info->bstrLogicalPath,
235 info->bstrComponentName);
236 if (FAILED(hr)) {
237 err_set(errset, hr, "failed to add component %S(%S)",
238 info->bstrComponentName, bstrWriterName);
239 goto out;
242 SysFreeString(bstrWriterName);
243 bstrWriterName = NULL;
244 pComponent->FreeComponentInfo(info);
245 info = NULL;
248 out:
249 if (bstrWriterName) {
250 SysFreeString(bstrWriterName);
252 if (pComponent && info) {
253 pComponent->FreeComponentInfo(info);
255 qga_debug_end;
258 DWORD get_reg_dword_value(HKEY baseKey, LPCSTR subKey, LPCSTR valueName,
259 DWORD defaultData)
261 qga_debug_begin;
263 DWORD regGetValueError;
264 DWORD dwordData;
265 DWORD dataSize = sizeof(DWORD);
267 regGetValueError = RegGetValue(baseKey, subKey, valueName, RRF_RT_DWORD,
268 NULL, &dwordData, &dataSize);
269 qga_debug_end;
270 if (regGetValueError != ERROR_SUCCESS) {
271 return defaultData;
273 return dwordData;
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)
284 qga_debug_begin;
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,
291 "VssOption",
292 defaultVssBT));
293 qga_debug_end;
294 if (!is_valid_vss_backup_type(vssBackupType)) {
295 return defaultVssBT;
297 return vssBackupType;
300 void requester_freeze(int *num_vols, void *mountpoints, ErrorSet *errset)
302 qga_debug_begin;
304 COMPointer<IVssAsync> pAsync;
305 HANDLE volume;
306 HRESULT hr;
307 LONG ctx;
308 GUID guidSnapshotSet = GUID_NULL;
309 SECURITY_DESCRIPTOR sd;
310 SECURITY_ATTRIBUTES sa;
311 WCHAR short_volume_name[64], *display_name = short_volume_name;
312 DWORD wait_status;
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 */
318 *num_vols = 0;
319 qga_debug("finished, already frozen");
320 return;
323 CoInitialize(NULL);
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",
335 EVENT_NAME_FROZEN);
336 goto out;
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",
341 EVENT_NAME_THAW);
342 goto out;
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",
347 EVENT_NAME_TIMEOUT);
348 goto out;
351 assert(pCreateVssBackupComponents != NULL);
352 hr = pCreateVssBackupComponents(&vss_ctx.pVssbc);
353 if (FAILED(hr)) {
354 err_set(errset, hr, "failed to create VSS backup components");
355 goto out;
358 hr = vss_ctx.pVssbc->InitializeForBackup();
359 if (FAILED(hr)) {
360 err_set(errset, hr, "failed to initialize for backup");
361 goto out;
364 hr = vss_ctx.pVssbc->SetBackupState(true, true, vss_bt, false);
365 if (FAILED(hr)) {
366 err_set(errset, hr, "failed to set backup state");
367 goto out;
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);
383 if (FAILED(hr)) {
384 err_set(errset, hr, "failed to set backup context");
385 goto out;
388 hr = vss_ctx.pVssbc->GatherWriterMetadata(pAsync.replace());
389 if (SUCCEEDED(hr)) {
390 hr = WaitForAsync(pAsync);
392 if (FAILED(hr)) {
393 err_set(errset, hr, "failed to gather writer metadata");
394 goto out;
397 AddComponents(errset);
398 if (err_is_set(errset)) {
399 goto out;
402 hr = vss_ctx.pVssbc->StartSnapshotSet(&guidSnapshotSet);
403 if (FAILED(hr)) {
404 err_set(errset, hr, "failed to start snapshot set");
405 goto out;
408 if (mountpoints) {
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;
413 VSS_ID pid;
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);
421 if (FAILED(hr)) {
422 err_set(errset, hr, "failed to add %S to snapshot set",
423 volume_name_wchar);
424 delete[] volume_name_wchar;
425 goto out;
427 num_mount_points++;
429 delete[] volume_name_wchar;
432 if (num_mount_points == 0) {
433 /* If there is no valid mount points, just exit. */
434 goto out;
438 if (!mountpoints) {
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");
442 goto out;
445 for (;;) {
446 if (GetDriveTypeW(short_volume_name) == DRIVE_FIXED) {
447 VSS_ID pid;
448 hr = vss_ctx.pVssbc->AddToSnapshotSet(short_volume_name,
449 g_gProviderId, &pid);
450 if (FAILED(hr)) {
451 WCHAR volume_path_name[PATH_MAX];
452 if (GetVolumePathNamesForVolumeNameW(
453 short_volume_name, volume_path_name,
454 sizeof(volume_path_name), NULL) &&
455 *volume_path_name) {
456 display_name = volume_path_name;
458 err_set(errset, hr, "failed to add %S to snapshot set",
459 display_name);
460 FindVolumeClose(volume);
461 goto out;
463 num_fixed_drives++;
465 if (!FindNextVolumeW(volume, short_volume_name,
466 sizeof(short_volume_name))) {
467 FindVolumeClose(volume);
468 break;
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());
479 if (SUCCEEDED(hr)) {
480 hr = WaitForAsync(pAsync);
482 if (FAILED(hr)) {
483 err_set(errset, hr, "failed to prepare for backup");
484 goto out;
487 hr = vss_ctx.pVssbc->GatherWriterStatus(pAsync.replace());
488 if (SUCCEEDED(hr)) {
489 hr = WaitForAsync(pAsync);
491 if (FAILED(hr)) {
492 err_set(errset, hr, "failed to gather writer status");
493 goto out;
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);
503 if (FAILED(hr)) {
504 err_set(errset, hr, "failed to do snapshot set");
505 goto out;
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);
511 if (FAILED(hr2)) {
512 err_set(errset, hr, "failed to do snapshot set");
513 goto out;
515 if (hr != VSS_S_ASYNC_PENDING) {
516 err_set(errset, E_FAIL,
517 "DoSnapshotSet exited without Frozen event");
518 goto out;
520 wait_status = WaitForSingleObject(vss_ctx.hEventFrozen,
521 VSS_TIMEOUT_EVENT_MSEC);
522 if (wait_status != WAIT_TIMEOUT) {
523 break;
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.
533 goto out1;
536 if (wait_status != WAIT_OBJECT_0) {
537 err_set(errset, E_FAIL,
538 "couldn't receive Frozen event from VSS provider");
539 goto out;
542 if (mountpoints) {
543 *num_vols = vss_ctx.cFrozenVols = num_mount_points;
544 } else {
545 *num_vols = vss_ctx.cFrozenVols = num_fixed_drives;
548 qga_debug("end successful");
549 return;
551 out:
552 if (vss_ctx.pVssbc) {
553 vss_ctx.pVssbc->AbortBackup();
556 out1:
557 requester_cleanup();
558 CoUninitialize();
560 qga_debug_end;
564 void requester_thaw(int *num_vols, void *mountpints, ErrorSet *errset)
566 qga_debug_begin;
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.
574 *num_vols = 0;
575 qga_debug("finished, no volumes were frozen");
577 return;
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);
587 switch (hr) {
588 case VSS_S_ASYNC_FINISHED:
589 hr = vss_ctx.pVssbc->BackupComplete(pAsync.replace());
590 if (SUCCEEDED(hr)) {
591 hr = WaitForAsync(pAsync);
593 if (FAILED(hr)) {
594 err_set(errset, hr, "failed to complete backup");
596 break;
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();
607 break;
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");
612 break;
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");
619 break;
621 default:
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;
629 requester_cleanup();
631 CoUninitialize();
632 StopService();
634 qga_debug_end;