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