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 ": Windows error 0x%lx", \
32 ## __VA_ARGS__, err); \
33 qga_debug(fmt ": Windows error 0x%lx", ## __VA_ARGS__, err); \
34 }
35 /* Bad idea, works only when (e)->errp != NULL: */
36 #define err_is_set(e) ((e)->errp && *(e)->errp)
37 /* To lift this restriction, error_propagate(), like we do in QEMU code */
38
39 /* Handle to VSSAPI.DLL */
40 static HMODULE hLib;
41
42 /* Functions in VSSAPI.DLL */
43 typedef HRESULT(STDAPICALLTYPE * t_CreateVssBackupComponents)(
44 OUT IVssBackupComponents**);
45 typedef void(APIENTRY * t_VssFreeSnapshotProperties)(IN VSS_SNAPSHOT_PROP*);
46 static t_CreateVssBackupComponents pCreateVssBackupComponents;
47 static t_VssFreeSnapshotProperties pVssFreeSnapshotProperties;
48
49 /* Variables used while applications and filesystes are frozen by VSS */
50 static struct QGAVSSContext {
51 IVssBackupComponents *pVssbc; /* VSS requester interface */
52 IVssAsync *pAsyncSnapshot; /* async info of VSS snapshot operation */
53 HANDLE hEventFrozen; /* notify fs/writer freeze from provider */
54 HANDLE hEventThaw; /* request provider to thaw */
55 HANDLE hEventTimeout; /* notify timeout in provider */
56 int cFrozenVols; /* number of frozen volumes */
57 } vss_ctx;
58
requester_init(void)59 STDAPI requester_init(void)
60 {
61 qga_debug_begin;
62
63 COMInitializer initializer; /* to call CoInitializeSecurity */
64 HRESULT hr = CoInitializeSecurity(
65 NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
66 RPC_C_IMP_LEVEL_IDENTIFY, NULL, EOAC_NONE, NULL);
67 if (FAILED(hr)) {
68 qga_debug("failed to CoInitializeSecurity (error %lx)", hr);
69 return hr;
70 }
71
72 hLib = LoadLibraryA("VSSAPI.DLL");
73 if (!hLib) {
74 qga_debug("failed to load VSSAPI.DLL");
75 return HRESULT_FROM_WIN32(GetLastError());
76 }
77
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
85 );
86 if (!pCreateVssBackupComponents) {
87 qga_debug("failed to get proc address from VSSAPI.DLL");
88 return HRESULT_FROM_WIN32(GetLastError());
89 }
90
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());
96 }
97
98 qga_debug_end;
99 return S_OK;
100 }
101
requester_cleanup(void)102 static void requester_cleanup(void)
103 {
104 qga_debug_begin;
105
106 if (vss_ctx.hEventFrozen) {
107 CloseHandle(vss_ctx.hEventFrozen);
108 vss_ctx.hEventFrozen = NULL;
109 }
110 if (vss_ctx.hEventThaw) {
111 CloseHandle(vss_ctx.hEventThaw);
112 vss_ctx.hEventThaw = NULL;
113 }
114 if (vss_ctx.hEventTimeout) {
115 CloseHandle(vss_ctx.hEventTimeout);
116 vss_ctx.hEventTimeout = NULL;
117 }
118 if (vss_ctx.pAsyncSnapshot) {
119 vss_ctx.pAsyncSnapshot->Release();
120 vss_ctx.pAsyncSnapshot = NULL;
121 }
122 if (vss_ctx.pVssbc) {
123 vss_ctx.pVssbc->Release();
124 vss_ctx.pVssbc = NULL;
125 }
126 vss_ctx.cFrozenVols = 0;
127 qga_debug_end;
128 }
129
requester_deinit(void)130 STDAPI requester_deinit(void)
131 {
132 qga_debug_begin;
133
134 requester_cleanup();
135
136 pCreateVssBackupComponents = NULL;
137 pVssFreeSnapshotProperties = NULL;
138 if (hLib) {
139 FreeLibrary(hLib);
140 hLib = NULL;
141 }
142
143 qga_debug_end;
144 return S_OK;
145 }
146
WaitForAsync(IVssAsync * pAsync)147 static HRESULT WaitForAsync(IVssAsync *pAsync)
148 {
149 qga_debug_begin;
150
151 HRESULT ret, hr;
152
153 do {
154 hr = pAsync->Wait();
155 if (FAILED(hr)) {
156 ret = hr;
157 break;
158 }
159 hr = pAsync->QueryStatus(&ret, NULL);
160 if (FAILED(hr)) {
161 ret = hr;
162 break;
163 }
164 } while (ret == VSS_S_ASYNC_PENDING);
165
166 qga_debug_end;
167 return ret;
168 }
169
AddComponents(ErrorSet * errset)170 static void AddComponents(ErrorSet *errset)
171 {
172 qga_debug_begin;
173
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;
184
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;
189 }
190
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;
197 }
198
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;
205 }
206
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;
212 }
213
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;
221 }
222
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;
229 }
230
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;
240 }
241 }
242 SysFreeString(bstrWriterName);
243 bstrWriterName = NULL;
244 pComponent->FreeComponentInfo(info);
245 info = NULL;
246 }
247 }
248 out:
249 if (bstrWriterName) {
250 SysFreeString(bstrWriterName);
251 }
252 if (pComponent && info) {
253 pComponent->FreeComponentInfo(info);
254 }
255 qga_debug_end;
256 }
257
get_reg_dword_value(HKEY baseKey,LPCSTR subKey,LPCSTR valueName,DWORD defaultData)258 static DWORD get_reg_dword_value(HKEY baseKey, LPCSTR subKey, LPCSTR valueName,
259 DWORD defaultData)
260 {
261 qga_debug_begin;
262
263 DWORD regGetValueError;
264 DWORD dwordData;
265 DWORD dataSize = sizeof(DWORD);
266
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;
272 }
273 return dwordData;
274 }
275
is_valid_vss_backup_type(VSS_BACKUP_TYPE vssBT)276 static bool is_valid_vss_backup_type(VSS_BACKUP_TYPE vssBT)
277 {
278 return (vssBT > VSS_BT_UNDEFINED && vssBT < VSS_BT_OTHER);
279 }
280
get_vss_backup_type(VSS_BACKUP_TYPE defaultVssBT=DEFAULT_VSS_BACKUP_TYPE)281 static VSS_BACKUP_TYPE get_vss_backup_type(
282 VSS_BACKUP_TYPE defaultVssBT = DEFAULT_VSS_BACKUP_TYPE)
283 {
284 qga_debug_begin;
285
286 VSS_BACKUP_TYPE vssBackupType;
287
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;
296 }
297 return vssBackupType;
298 }
299
requester_freeze(int * num_vols,void * mountpoints,ErrorSet * errset)300 void requester_freeze(int *num_vols, void *mountpoints, ErrorSet *errset)
301 {
302 qga_debug_begin;
303
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();
316
317 if (vss_ctx.pVssbc) { /* already frozen */
318 *num_vols = 0;
319 qga_debug("finished, already frozen");
320 return;
321 }
322
323 CoInitialize(NULL);
324
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;
331
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;
337 }
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;
343 }
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;
349 }
350
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;
356 }
357
358 hr = vss_ctx.pVssbc->InitializeForBackup();
359 if (FAILED(hr)) {
360 err_set(errset, hr, "failed to initialize for backup");
361 goto out;
362 }
363
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;
368 }
369
370 /*
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.
374 */
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);
382 }
383 if (FAILED(hr)) {
384 err_set(errset, hr, "failed to set backup context");
385 goto out;
386 }
387
388 hr = vss_ctx.pVssbc->GatherWriterMetadata(pAsync.replace());
389 if (SUCCEEDED(hr)) {
390 hr = WaitForAsync(pAsync);
391 }
392 if (FAILED(hr)) {
393 err_set(errset, hr, "failed to gather writer metadata");
394 goto out;
395 }
396
397 AddComponents(errset);
398 if (err_is_set(errset)) {
399 goto out;
400 }
401
402 hr = vss_ctx.pVssbc->StartSnapshotSet(&guidSnapshotSet);
403 if (FAILED(hr)) {
404 err_set(errset, hr, "failed to start snapshot set");
405 goto out;
406 }
407
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;
414
415 volume_name_wchar = new wchar_t[len];
416 mbstowcs_s(&converted, volume_name_wchar, len,
417 list->value, _TRUNCATE);
418
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;
426 }
427 num_mount_points++;
428
429 delete[] volume_name_wchar;
430 }
431
432 if (num_mount_points == 0) {
433 /* If there is no valid mount points, just exit. */
434 goto out;
435 }
436 }
437
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;
443 }
444
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;
457 }
458 err_set(errset, hr, "failed to add %S to snapshot set",
459 display_name);
460 FindVolumeClose(volume);
461 goto out;
462 }
463 num_fixed_drives++;
464 }
465 if (!FindNextVolumeW(volume, short_volume_name,
466 sizeof(short_volume_name))) {
467 FindVolumeClose(volume);
468 break;
469 }
470 }
471
472 if (num_fixed_drives == 0) {
473 goto out; /* If there is no fixed drive, just exit. */
474 }
475 }
476
477 qga_debug("preparing for backup");
478 hr = vss_ctx.pVssbc->PrepareForBackup(pAsync.replace());
479 if (SUCCEEDED(hr)) {
480 hr = WaitForAsync(pAsync);
481 }
482 if (FAILED(hr)) {
483 err_set(errset, hr, "failed to prepare for backup");
484 goto out;
485 }
486
487 hr = vss_ctx.pVssbc->GatherWriterStatus(pAsync.replace());
488 if (SUCCEEDED(hr)) {
489 hr = WaitForAsync(pAsync);
490 }
491 if (FAILED(hr)) {
492 err_set(errset, hr, "failed to gather writer status");
493 goto out;
494 }
495
496 /*
497 * Start VSS quiescing operations.
498 * CQGAVssProvider::CommitSnapshots will kick vss_ctx.hEventFrozen
499 * after the applications and filesystems are frozen.
500 */
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;
506 }
507
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;
514 }
515 if (hr != VSS_S_ASYNC_PENDING) {
516 err_set(errset, E_FAIL,
517 "DoSnapshotSet exited without Frozen event");
518 goto out;
519 }
520 wait_status = WaitForSingleObject(vss_ctx.hEventFrozen,
521 VSS_TIMEOUT_EVENT_MSEC);
522 if (wait_status != WAIT_TIMEOUT) {
523 break;
524 }
525 }
526
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.
532 */
533 goto out1;
534 }
535
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;
540 }
541
542 if (mountpoints) {
543 *num_vols = vss_ctx.cFrozenVols = num_mount_points;
544 } else {
545 *num_vols = vss_ctx.cFrozenVols = num_fixed_drives;
546 }
547
548 qga_debug("end successful");
549 return;
550
551 out:
552 if (vss_ctx.pVssbc) {
553 vss_ctx.pVssbc->AbortBackup();
554 }
555
556 out1:
557 requester_cleanup();
558 CoUninitialize();
559
560 qga_debug_end;
561 }
562
563
requester_thaw(int * num_vols,void * mountpints,ErrorSet * errset)564 void requester_thaw(int *num_vols, void *mountpints, ErrorSet *errset)
565 {
566 qga_debug_begin;
567 COMPointer<IVssAsync> pAsync;
568
569 if (!vss_ctx.hEventThaw) {
570 /*
571 * In this case, DoSnapshotSet is aborted or not started,
572 * and no volumes must be frozen. We return without an error.
573 */
574 *num_vols = 0;
575 qga_debug("finished, no volumes were frozen");
576
577 return;
578 }
579
580 /* Tell the provider that the snapshot is finished. */
581 SetEvent(vss_ctx.hEventThaw);
582
583 assert(vss_ctx.pVssbc);
584 assert(vss_ctx.pAsyncSnapshot);
585
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);
592 }
593 if (FAILED(hr)) {
594 err_set(errset, hr, "failed to complete backup");
595 }
596 break;
597
598 case (HRESULT)VSS_E_OBJECT_NOT_FOUND:
599 /*
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.
605 */
606 vss_ctx.pVssbc->AbortBackup();
607 break;
608
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;
613 }
614 /* fall through if hEventTimeout is signaled */
615
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;
620
621 default:
622 err_set(errset, hr, "failed to do snapshot set");
623 }
624
625 if (err_is_set(errset)) {
626 vss_ctx.pVssbc->AbortBackup();
627 }
628 *num_vols = vss_ctx.cFrozenVols;
629 requester_cleanup();
630
631 CoUninitialize();
632 StopService();
633
634 qga_debug_end;
635 }
636