]> git.proxmox.com Git - mirror_qemu.git/blob - qga/vss-win32/requester.cpp
Merge tag 'mips-20230710' of https://github.com/philmd/qemu into staging
[mirror_qemu.git] / qga / vss-win32 / requester.cpp
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 "vss-debug.h"
16 #include "requester.h"
17 #include "install.h"
18 #include <vswriter.h>
19 #include <vsbackup.h>
20
21 /* Max wait time for frozen event (VSS can only hold writes for 10 seconds) */
22 #define VSS_TIMEOUT_FREEZE_MSEC 60000
23
24 /* Call QueryStatus every 10 ms while waiting for frozen event */
25 #define VSS_TIMEOUT_EVENT_MSEC 10
26
27 #define DEFAULT_VSS_BACKUP_TYPE VSS_BT_FULL
28
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__); \
33 }
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 */
37
38 /* Handle to VSSAPI.DLL */
39 static HMODULE hLib;
40
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;
47
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;
57
58 STDAPI requester_init(void)
59 {
60 qga_debug_begin;
61
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;
69 }
70
71 hLib = LoadLibraryA("VSSAPI.DLL");
72 if (!hLib) {
73 qga_debug("failed to load VSSAPI.DLL");
74 return HRESULT_FROM_WIN32(GetLastError());
75 }
76
77 pCreateVssBackupComponents = (t_CreateVssBackupComponents)
78 GetProcAddress(hLib,
79 #ifdef _WIN64 /* 64bit environment */
80 "?CreateVssBackupComponents@@YAJPEAPEAVIVssBackupComponents@@@Z"
81 #else /* 32bit environment */
82 "?CreateVssBackupComponents@@YGJPAPAVIVssBackupComponents@@@Z"
83 #endif
84 );
85 if (!pCreateVssBackupComponents) {
86 qga_debug("failed to get proc address from VSSAPI.DLL");
87 return HRESULT_FROM_WIN32(GetLastError());
88 }
89
90 pVssFreeSnapshotProperties = (t_VssFreeSnapshotProperties)
91 GetProcAddress(hLib, "VssFreeSnapshotProperties");
92 if (!pVssFreeSnapshotProperties) {
93 qga_debug("failed to get proc address from VSSAPI.DLL");
94 return HRESULT_FROM_WIN32(GetLastError());
95 }
96
97 qga_debug_end;
98 return S_OK;
99 }
100
101 static void requester_cleanup(void)
102 {
103 qga_debug_begin;
104
105 if (vss_ctx.hEventFrozen) {
106 CloseHandle(vss_ctx.hEventFrozen);
107 vss_ctx.hEventFrozen = NULL;
108 }
109 if (vss_ctx.hEventThaw) {
110 CloseHandle(vss_ctx.hEventThaw);
111 vss_ctx.hEventThaw = NULL;
112 }
113 if (vss_ctx.hEventTimeout) {
114 CloseHandle(vss_ctx.hEventTimeout);
115 vss_ctx.hEventTimeout = NULL;
116 }
117 if (vss_ctx.pAsyncSnapshot) {
118 vss_ctx.pAsyncSnapshot->Release();
119 vss_ctx.pAsyncSnapshot = NULL;
120 }
121 if (vss_ctx.pVssbc) {
122 vss_ctx.pVssbc->Release();
123 vss_ctx.pVssbc = NULL;
124 }
125 vss_ctx.cFrozenVols = 0;
126 qga_debug_end;
127 }
128
129 STDAPI requester_deinit(void)
130 {
131 qga_debug_begin;
132
133 requester_cleanup();
134
135 pCreateVssBackupComponents = NULL;
136 pVssFreeSnapshotProperties = NULL;
137 if (hLib) {
138 FreeLibrary(hLib);
139 hLib = NULL;
140 }
141
142 qga_debug_end;
143 return S_OK;
144 }
145
146 static HRESULT WaitForAsync(IVssAsync *pAsync)
147 {
148 qga_debug_begin;
149
150 HRESULT ret, hr;
151
152 do {
153 hr = pAsync->Wait();
154 if (FAILED(hr)) {
155 ret = hr;
156 break;
157 }
158 hr = pAsync->QueryStatus(&ret, NULL);
159 if (FAILED(hr)) {
160 ret = hr;
161 break;
162 }
163 } while (ret == VSS_S_ASYNC_PENDING);
164
165 qga_debug_end;
166 return ret;
167 }
168
169 static void AddComponents(ErrorSet *errset)
170 {
171 qga_debug_begin;
172
173 unsigned int cWriters, i;
174 VSS_ID id, idInstance, idWriter;
175 BSTR bstrWriterName = NULL;
176 VSS_USAGE_TYPE usage;
177 VSS_SOURCE_TYPE source;
178 unsigned int cComponents, c1, c2, j;
179 COMPointer<IVssExamineWriterMetadata> pMetadata;
180 COMPointer<IVssWMComponent> pComponent;
181 PVSSCOMPONENTINFO info;
182 HRESULT hr;
183
184 hr = vss_ctx.pVssbc->GetWriterMetadataCount(&cWriters);
185 if (FAILED(hr)) {
186 err_set(errset, hr, "failed to get writer metadata count");
187 goto out;
188 }
189
190 for (i = 0; i < cWriters; i++) {
191 hr = vss_ctx.pVssbc->GetWriterMetadata(i, &id, pMetadata.replace());
192 if (FAILED(hr)) {
193 err_set(errset, hr, "failed to get writer metadata of %d/%d",
194 i, cWriters);
195 goto out;
196 }
197
198 hr = pMetadata->GetIdentity(&idInstance, &idWriter,
199 &bstrWriterName, &usage, &source);
200 if (FAILED(hr)) {
201 err_set(errset, hr, "failed to get identity of writer %d/%d",
202 i, cWriters);
203 goto out;
204 }
205
206 hr = pMetadata->GetFileCounts(&c1, &c2, &cComponents);
207 if (FAILED(hr)) {
208 err_set(errset, hr, "failed to get file counts of %S",
209 bstrWriterName);
210 goto out;
211 }
212
213 for (j = 0; j < cComponents; j++) {
214 hr = pMetadata->GetComponent(j, pComponent.replace());
215 if (FAILED(hr)) {
216 err_set(errset, hr,
217 "failed to get component %d/%d of %S",
218 j, cComponents, bstrWriterName);
219 goto out;
220 }
221
222 hr = pComponent->GetComponentInfo(&info);
223 if (FAILED(hr)) {
224 err_set(errset, hr,
225 "failed to get component info %d/%d of %S",
226 j, cComponents, bstrWriterName);
227 goto out;
228 }
229
230 if (info->bSelectable) {
231 hr = vss_ctx.pVssbc->AddComponent(idInstance, idWriter,
232 info->type,
233 info->bstrLogicalPath,
234 info->bstrComponentName);
235 if (FAILED(hr)) {
236 err_set(errset, hr, "failed to add component %S(%S)",
237 info->bstrComponentName, bstrWriterName);
238 goto out;
239 }
240 }
241 SysFreeString(bstrWriterName);
242 bstrWriterName = NULL;
243 pComponent->FreeComponentInfo(info);
244 info = NULL;
245 }
246 }
247 out:
248 if (bstrWriterName) {
249 SysFreeString(bstrWriterName);
250 }
251 if (pComponent && info) {
252 pComponent->FreeComponentInfo(info);
253 }
254 qga_debug_end;
255 }
256
257 DWORD get_reg_dword_value(HKEY baseKey, LPCSTR subKey, LPCSTR valueName,
258 DWORD defaultData)
259 {
260 qga_debug_begin;
261
262 DWORD regGetValueError;
263 DWORD dwordData;
264 DWORD dataSize = sizeof(DWORD);
265
266 regGetValueError = RegGetValue(baseKey, subKey, valueName, RRF_RT_DWORD,
267 NULL, &dwordData, &dataSize);
268 qga_debug_end;
269 if (regGetValueError != ERROR_SUCCESS) {
270 return defaultData;
271 }
272 return dwordData;
273 }
274
275 bool is_valid_vss_backup_type(VSS_BACKUP_TYPE vssBT)
276 {
277 return (vssBT > VSS_BT_UNDEFINED && vssBT < VSS_BT_OTHER);
278 }
279
280 VSS_BACKUP_TYPE get_vss_backup_type(
281 VSS_BACKUP_TYPE defaultVssBT = DEFAULT_VSS_BACKUP_TYPE)
282 {
283 qga_debug_begin;
284
285 VSS_BACKUP_TYPE vssBackupType;
286
287 vssBackupType = static_cast<VSS_BACKUP_TYPE>(
288 get_reg_dword_value(HKEY_LOCAL_MACHINE,
289 QGA_PROVIDER_REGISTRY_ADDRESS,
290 "VssOption",
291 defaultVssBT));
292 qga_debug_end;
293 if (!is_valid_vss_backup_type(vssBackupType)) {
294 return defaultVssBT;
295 }
296 return vssBackupType;
297 }
298
299 void requester_freeze(int *num_vols, void *mountpoints, ErrorSet *errset)
300 {
301 qga_debug_begin;
302
303 COMPointer<IVssAsync> pAsync;
304 HANDLE volume;
305 HRESULT hr;
306 LONG ctx;
307 GUID guidSnapshotSet = GUID_NULL;
308 SECURITY_DESCRIPTOR sd;
309 SECURITY_ATTRIBUTES sa;
310 WCHAR short_volume_name[64], *display_name = short_volume_name;
311 DWORD wait_status;
312 int num_fixed_drives = 0, i;
313 int num_mount_points = 0;
314 VSS_BACKUP_TYPE vss_bt = get_vss_backup_type();
315
316 if (vss_ctx.pVssbc) { /* already frozen */
317 *num_vols = 0;
318 qga_debug("finished, already frozen");
319 return;
320 }
321
322 CoInitialize(NULL);
323
324 /* Allow unrestricted access to events */
325 InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
326 SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE);
327 sa.nLength = sizeof(sa);
328 sa.lpSecurityDescriptor = &sd;
329 sa.bInheritHandle = FALSE;
330
331 vss_ctx.hEventFrozen = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_FROZEN);
332 if (!vss_ctx.hEventFrozen) {
333 err_set(errset, GetLastError(), "failed to create event %s",
334 EVENT_NAME_FROZEN);
335 goto out;
336 }
337 vss_ctx.hEventThaw = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_THAW);
338 if (!vss_ctx.hEventThaw) {
339 err_set(errset, GetLastError(), "failed to create event %s",
340 EVENT_NAME_THAW);
341 goto out;
342 }
343 vss_ctx.hEventTimeout = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_TIMEOUT);
344 if (!vss_ctx.hEventTimeout) {
345 err_set(errset, GetLastError(), "failed to create event %s",
346 EVENT_NAME_TIMEOUT);
347 goto out;
348 }
349
350 assert(pCreateVssBackupComponents != NULL);
351 hr = pCreateVssBackupComponents(&vss_ctx.pVssbc);
352 if (FAILED(hr)) {
353 err_set(errset, hr, "failed to create VSS backup components");
354 goto out;
355 }
356
357 hr = vss_ctx.pVssbc->InitializeForBackup();
358 if (FAILED(hr)) {
359 err_set(errset, hr, "failed to initialize for backup");
360 goto out;
361 }
362
363 hr = vss_ctx.pVssbc->SetBackupState(true, true, vss_bt, false);
364 if (FAILED(hr)) {
365 err_set(errset, hr, "failed to set backup state");
366 goto out;
367 }
368
369 /*
370 * Currently writable snapshots are not supported.
371 * To prevent the final commit (which requires to write to snapshots),
372 * ATTR_NO_AUTORECOVERY and ATTR_TRANSPORTABLE are specified here.
373 */
374 ctx = VSS_CTX_APP_ROLLBACK | VSS_VOLSNAP_ATTR_TRANSPORTABLE |
375 VSS_VOLSNAP_ATTR_NO_AUTORECOVERY | VSS_VOLSNAP_ATTR_TXF_RECOVERY;
376 hr = vss_ctx.pVssbc->SetContext(ctx);
377 if (hr == (HRESULT)VSS_E_UNSUPPORTED_CONTEXT) {
378 /* Non-server version of Windows doesn't support ATTR_TRANSPORTABLE */
379 ctx &= ~VSS_VOLSNAP_ATTR_TRANSPORTABLE;
380 hr = vss_ctx.pVssbc->SetContext(ctx);
381 }
382 if (FAILED(hr)) {
383 err_set(errset, hr, "failed to set backup context");
384 goto out;
385 }
386
387 hr = vss_ctx.pVssbc->GatherWriterMetadata(pAsync.replace());
388 if (SUCCEEDED(hr)) {
389 hr = WaitForAsync(pAsync);
390 }
391 if (FAILED(hr)) {
392 err_set(errset, hr, "failed to gather writer metadata");
393 goto out;
394 }
395
396 AddComponents(errset);
397 if (err_is_set(errset)) {
398 goto out;
399 }
400
401 hr = vss_ctx.pVssbc->StartSnapshotSet(&guidSnapshotSet);
402 if (FAILED(hr)) {
403 err_set(errset, hr, "failed to start snapshot set");
404 goto out;
405 }
406
407 if (mountpoints) {
408 PWCHAR volume_name_wchar;
409 for (volList *list = (volList *)mountpoints; list; list = list->next) {
410 size_t len = strlen(list->value) + 1;
411 size_t converted = 0;
412 VSS_ID pid;
413
414 volume_name_wchar = new wchar_t[len];
415 mbstowcs_s(&converted, volume_name_wchar, len,
416 list->value, _TRUNCATE);
417
418 hr = vss_ctx.pVssbc->AddToSnapshotSet(volume_name_wchar,
419 g_gProviderId, &pid);
420 if (FAILED(hr)) {
421 err_set(errset, hr, "failed to add %S to snapshot set",
422 volume_name_wchar);
423 delete[] volume_name_wchar;
424 goto out;
425 }
426 num_mount_points++;
427
428 delete[] volume_name_wchar;
429 }
430
431 if (num_mount_points == 0) {
432 /* If there is no valid mount points, just exit. */
433 goto out;
434 }
435 }
436
437 if (!mountpoints) {
438 volume = FindFirstVolumeW(short_volume_name, sizeof(short_volume_name));
439 if (volume == INVALID_HANDLE_VALUE) {
440 err_set(errset, hr, "failed to find first volume");
441 goto out;
442 }
443
444 for (;;) {
445 if (GetDriveTypeW(short_volume_name) == DRIVE_FIXED) {
446 VSS_ID pid;
447 hr = vss_ctx.pVssbc->AddToSnapshotSet(short_volume_name,
448 g_gProviderId, &pid);
449 if (FAILED(hr)) {
450 WCHAR volume_path_name[PATH_MAX];
451 if (GetVolumePathNamesForVolumeNameW(
452 short_volume_name, volume_path_name,
453 sizeof(volume_path_name), NULL) &&
454 *volume_path_name) {
455 display_name = volume_path_name;
456 }
457 err_set(errset, hr, "failed to add %S to snapshot set",
458 display_name);
459 FindVolumeClose(volume);
460 goto out;
461 }
462 num_fixed_drives++;
463 }
464 if (!FindNextVolumeW(volume, short_volume_name,
465 sizeof(short_volume_name))) {
466 FindVolumeClose(volume);
467 break;
468 }
469 }
470
471 if (num_fixed_drives == 0) {
472 goto out; /* If there is no fixed drive, just exit. */
473 }
474 }
475
476 qga_debug("preparing for backup");
477 hr = vss_ctx.pVssbc->PrepareForBackup(pAsync.replace());
478 if (SUCCEEDED(hr)) {
479 hr = WaitForAsync(pAsync);
480 }
481 if (FAILED(hr)) {
482 err_set(errset, hr, "failed to prepare for backup");
483 goto out;
484 }
485
486 hr = vss_ctx.pVssbc->GatherWriterStatus(pAsync.replace());
487 if (SUCCEEDED(hr)) {
488 hr = WaitForAsync(pAsync);
489 }
490 if (FAILED(hr)) {
491 err_set(errset, hr, "failed to gather writer status");
492 goto out;
493 }
494
495 /*
496 * Start VSS quiescing operations.
497 * CQGAVssProvider::CommitSnapshots will kick vss_ctx.hEventFrozen
498 * after the applications and filesystems are frozen.
499 */
500 qga_debug("do snapshot set");
501 hr = vss_ctx.pVssbc->DoSnapshotSet(&vss_ctx.pAsyncSnapshot);
502 if (FAILED(hr)) {
503 err_set(errset, hr, "failed to do snapshot set");
504 goto out;
505 }
506
507 /* Need to call QueryStatus several times to make VSS provider progress */
508 for (i = 0; i < VSS_TIMEOUT_FREEZE_MSEC/VSS_TIMEOUT_EVENT_MSEC; i++) {
509 HRESULT hr2 = vss_ctx.pAsyncSnapshot->QueryStatus(&hr, NULL);
510 if (FAILED(hr2)) {
511 err_set(errset, hr, "failed to do snapshot set");
512 goto out;
513 }
514 if (hr != VSS_S_ASYNC_PENDING) {
515 err_set(errset, E_FAIL,
516 "DoSnapshotSet exited without Frozen event");
517 goto out;
518 }
519 wait_status = WaitForSingleObject(vss_ctx.hEventFrozen,
520 VSS_TIMEOUT_EVENT_MSEC);
521 if (wait_status != WAIT_TIMEOUT) {
522 break;
523 }
524 }
525
526 if (wait_status == WAIT_TIMEOUT) {
527 err_set(errset, E_FAIL,
528 "timeout when try to receive Frozen event from VSS provider");
529 /* If we are here, VSS had timeout.
530 * Don't call AbortBackup, just return directly.
531 */
532 goto out1;
533 }
534
535 if (wait_status != WAIT_OBJECT_0) {
536 err_set(errset, E_FAIL,
537 "couldn't receive Frozen event from VSS provider");
538 goto out;
539 }
540
541 if (mountpoints) {
542 *num_vols = vss_ctx.cFrozenVols = num_mount_points;
543 } else {
544 *num_vols = vss_ctx.cFrozenVols = num_fixed_drives;
545 }
546
547 qga_debug("end successful");
548 return;
549
550 out:
551 if (vss_ctx.pVssbc) {
552 vss_ctx.pVssbc->AbortBackup();
553 }
554
555 out1:
556 requester_cleanup();
557 CoUninitialize();
558
559 qga_debug_end;
560 }
561
562
563 void requester_thaw(int *num_vols, void *mountpints, ErrorSet *errset)
564 {
565 qga_debug_begin;
566 COMPointer<IVssAsync> pAsync;
567
568 if (!vss_ctx.hEventThaw) {
569 /*
570 * In this case, DoSnapshotSet is aborted or not started,
571 * and no volumes must be frozen. We return without an error.
572 */
573 *num_vols = 0;
574 qga_debug("finished, no volumes were frozen");
575
576 return;
577 }
578
579 /* Tell the provider that the snapshot is finished. */
580 SetEvent(vss_ctx.hEventThaw);
581
582 assert(vss_ctx.pVssbc);
583 assert(vss_ctx.pAsyncSnapshot);
584
585 HRESULT hr = WaitForAsync(vss_ctx.pAsyncSnapshot);
586 switch (hr) {
587 case VSS_S_ASYNC_FINISHED:
588 hr = vss_ctx.pVssbc->BackupComplete(pAsync.replace());
589 if (SUCCEEDED(hr)) {
590 hr = WaitForAsync(pAsync);
591 }
592 if (FAILED(hr)) {
593 err_set(errset, hr, "failed to complete backup");
594 }
595 break;
596
597 case (HRESULT)VSS_E_OBJECT_NOT_FOUND:
598 /*
599 * On Windows earlier than 2008 SP2 which does not support
600 * VSS_VOLSNAP_ATTR_NO_AUTORECOVERY context, the final commit is not
601 * skipped and VSS is aborted by VSS_E_OBJECT_NOT_FOUND. However, as
602 * the system had been frozen until fsfreeze-thaw command was issued,
603 * we ignore this error.
604 */
605 vss_ctx.pVssbc->AbortBackup();
606 break;
607
608 case VSS_E_UNEXPECTED_PROVIDER_ERROR:
609 if (WaitForSingleObject(vss_ctx.hEventTimeout, 0) != WAIT_OBJECT_0) {
610 err_set(errset, hr, "unexpected error in VSS provider");
611 break;
612 }
613 /* fall through if hEventTimeout is signaled */
614
615 case (HRESULT)VSS_E_HOLD_WRITES_TIMEOUT:
616 err_set(errset, hr, "couldn't hold writes: "
617 "fsfreeze is limited up to 10 seconds");
618 break;
619
620 default:
621 err_set(errset, hr, "failed to do snapshot set");
622 }
623
624 if (err_is_set(errset)) {
625 vss_ctx.pVssbc->AbortBackup();
626 }
627 *num_vols = vss_ctx.cFrozenVols;
628 requester_cleanup();
629
630 CoUninitialize();
631 StopService();
632
633 qga_debug_end;
634 }