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