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