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