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