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