xref: /openbmc/qemu/qga/vss-win32/requester.cpp (revision 2df1eb27)
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 }
635