xref: /openbmc/qemu/qga/vss-win32/requester.cpp (revision 1b111dc1)
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     vss_ctx.hEventFrozen =  INVALID_HANDLE_VALUE;
54     vss_ctx.hEventThaw = INVALID_HANDLE_VALUE;
55     vss_ctx.hEventTimeout = INVALID_HANDLE_VALUE;
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 != INVALID_HANDLE_VALUE) {
98         CloseHandle(vss_ctx.hEventFrozen);
99         vss_ctx.hEventFrozen = INVALID_HANDLE_VALUE;
100     }
101     if (vss_ctx.hEventThaw != INVALID_HANDLE_VALUE) {
102         CloseHandle(vss_ctx.hEventThaw);
103         vss_ctx.hEventThaw = INVALID_HANDLE_VALUE;
104     }
105     if (vss_ctx.hEventTimeout != INVALID_HANDLE_VALUE) {
106         CloseHandle(vss_ctx.hEventTimeout);
107         vss_ctx.hEventTimeout = INVALID_HANDLE_VALUE;
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 void requester_freeze(int *num_vols, ErrorSet *errset)
240 {
241     COMPointer<IVssAsync> pAsync;
242     HANDLE volume;
243     HRESULT hr;
244     LONG ctx;
245     GUID guidSnapshotSet = GUID_NULL;
246     SECURITY_DESCRIPTOR sd;
247     SECURITY_ATTRIBUTES sa;
248     WCHAR short_volume_name[64], *display_name = short_volume_name;
249     DWORD wait_status;
250     int num_fixed_drives = 0, i;
251 
252     if (vss_ctx.pVssbc) { /* already frozen */
253         *num_vols = 0;
254         return;
255     }
256 
257     CoInitialize(NULL);
258 
259     assert(pCreateVssBackupComponents != NULL);
260     hr = pCreateVssBackupComponents(&vss_ctx.pVssbc);
261     if (FAILED(hr)) {
262         err_set(errset, hr, "failed to create VSS backup components");
263         goto out;
264     }
265 
266     hr = vss_ctx.pVssbc->InitializeForBackup();
267     if (FAILED(hr)) {
268         err_set(errset, hr, "failed to initialize for backup");
269         goto out;
270     }
271 
272     hr = vss_ctx.pVssbc->SetBackupState(true, true, VSS_BT_FULL, false);
273     if (FAILED(hr)) {
274         err_set(errset, hr, "failed to set backup state");
275         goto out;
276     }
277 
278     /*
279      * Currently writable snapshots are not supported.
280      * To prevent the final commit (which requires to write to snapshots),
281      * ATTR_NO_AUTORECOVERY and ATTR_TRANSPORTABLE are specified here.
282      */
283     ctx = VSS_CTX_APP_ROLLBACK | VSS_VOLSNAP_ATTR_TRANSPORTABLE |
284         VSS_VOLSNAP_ATTR_NO_AUTORECOVERY | VSS_VOLSNAP_ATTR_TXF_RECOVERY;
285     hr = vss_ctx.pVssbc->SetContext(ctx);
286     if (hr == (HRESULT)VSS_E_UNSUPPORTED_CONTEXT) {
287         /* Non-server version of Windows doesn't support ATTR_TRANSPORTABLE */
288         ctx &= ~VSS_VOLSNAP_ATTR_TRANSPORTABLE;
289         hr = vss_ctx.pVssbc->SetContext(ctx);
290     }
291     if (FAILED(hr)) {
292         err_set(errset, hr, "failed to set backup context");
293         goto out;
294     }
295 
296     hr = vss_ctx.pVssbc->GatherWriterMetadata(pAsync.replace());
297     if (SUCCEEDED(hr)) {
298         hr = WaitForAsync(pAsync);
299     }
300     if (FAILED(hr)) {
301         err_set(errset, hr, "failed to gather writer metadata");
302         goto out;
303     }
304 
305     AddComponents(errset);
306     if (err_is_set(errset)) {
307         goto out;
308     }
309 
310     hr = vss_ctx.pVssbc->StartSnapshotSet(&guidSnapshotSet);
311     if (FAILED(hr)) {
312         err_set(errset, hr, "failed to start snapshot set");
313         goto out;
314     }
315 
316     volume = FindFirstVolumeW(short_volume_name, sizeof(short_volume_name));
317     if (volume == INVALID_HANDLE_VALUE) {
318         err_set(errset, hr, "failed to find first volume");
319         goto out;
320     }
321     for (;;) {
322         if (GetDriveTypeW(short_volume_name) == DRIVE_FIXED) {
323             VSS_ID pid;
324             hr = vss_ctx.pVssbc->AddToSnapshotSet(short_volume_name,
325                                                   g_gProviderId, &pid);
326             if (FAILED(hr)) {
327                 WCHAR volume_path_name[PATH_MAX];
328                 if (GetVolumePathNamesForVolumeNameW(
329                         short_volume_name, volume_path_name,
330                         sizeof(volume_path_name), NULL) && *volume_path_name) {
331                     display_name = volume_path_name;
332                 }
333                 err_set(errset, hr, "failed to add %S to snapshot set",
334                                  display_name);
335                 FindVolumeClose(volume);
336                 goto out;
337             }
338             num_fixed_drives++;
339         }
340         if (!FindNextVolumeW(volume, short_volume_name,
341                              sizeof(short_volume_name))) {
342             FindVolumeClose(volume);
343             break;
344         }
345     }
346 
347     if (num_fixed_drives == 0) {
348         goto out; /* If there is no fixed drive, just exit. */
349     }
350 
351     hr = vss_ctx.pVssbc->PrepareForBackup(pAsync.replace());
352     if (SUCCEEDED(hr)) {
353         hr = WaitForAsync(pAsync);
354     }
355     if (FAILED(hr)) {
356         err_set(errset, hr, "failed to prepare for backup");
357         goto out;
358     }
359 
360     hr = vss_ctx.pVssbc->GatherWriterStatus(pAsync.replace());
361     if (SUCCEEDED(hr)) {
362         hr = WaitForAsync(pAsync);
363     }
364     if (FAILED(hr)) {
365         err_set(errset, hr, "failed to gather writer status");
366         goto out;
367     }
368 
369     /* Allow unrestricted access to events */
370     InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
371     SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE);
372     sa.nLength = sizeof(sa);
373     sa.lpSecurityDescriptor = &sd;
374     sa.bInheritHandle = FALSE;
375 
376     vss_ctx.hEventFrozen = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_FROZEN);
377     if (vss_ctx.hEventFrozen == INVALID_HANDLE_VALUE) {
378         err_set(errset, GetLastError(), "failed to create event %s",
379                 EVENT_NAME_FROZEN);
380         goto out;
381     }
382     vss_ctx.hEventThaw = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_THAW);
383     if (vss_ctx.hEventThaw == INVALID_HANDLE_VALUE) {
384         err_set(errset, GetLastError(), "failed to create event %s",
385                 EVENT_NAME_THAW);
386         goto out;
387     }
388     vss_ctx.hEventTimeout = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_TIMEOUT);
389     if (vss_ctx.hEventTimeout == INVALID_HANDLE_VALUE) {
390         err_set(errset, GetLastError(), "failed to create event %s",
391                 EVENT_NAME_TIMEOUT);
392         goto out;
393     }
394 
395     /*
396      * Start VSS quiescing operations.
397      * CQGAVssProvider::CommitSnapshots will kick vss_ctx.hEventFrozen
398      * after the applications and filesystems are frozen.
399      */
400     hr = vss_ctx.pVssbc->DoSnapshotSet(&vss_ctx.pAsyncSnapshot);
401     if (FAILED(hr)) {
402         err_set(errset, hr, "failed to do snapshot set");
403         goto out;
404     }
405 
406     /* Need to call QueryStatus several times to make VSS provider progress */
407     for (i = 0; i < VSS_TIMEOUT_FREEZE_MSEC/VSS_TIMEOUT_EVENT_MSEC; i++) {
408         HRESULT hr2 = vss_ctx.pAsyncSnapshot->QueryStatus(&hr, NULL);
409         if (FAILED(hr2)) {
410             err_set(errset, hr, "failed to do snapshot set");
411             goto out;
412         }
413         if (hr != VSS_S_ASYNC_PENDING) {
414             err_set(errset, E_FAIL,
415                     "DoSnapshotSet exited without Frozen event");
416             goto out;
417         }
418         wait_status = WaitForSingleObject(vss_ctx.hEventFrozen,
419                                           VSS_TIMEOUT_EVENT_MSEC);
420         if (wait_status != WAIT_TIMEOUT) {
421             break;
422         }
423     }
424     if (wait_status != WAIT_OBJECT_0) {
425         err_set(errset, E_FAIL,
426                 "couldn't receive Frozen event from VSS provider");
427         goto out;
428     }
429 
430     *num_vols = vss_ctx.cFrozenVols = num_fixed_drives;
431     return;
432 
433 out:
434     if (vss_ctx.pVssbc) {
435         vss_ctx.pVssbc->AbortBackup();
436     }
437     requester_cleanup();
438     CoUninitialize();
439 }
440 
441 
442 void requester_thaw(int *num_vols, ErrorSet *errset)
443 {
444     COMPointer<IVssAsync> pAsync;
445 
446     if (vss_ctx.hEventThaw == INVALID_HANDLE_VALUE) {
447         /*
448          * In this case, DoSnapshotSet is aborted or not started,
449          * and no volumes must be frozen. We return without an error.
450          */
451         *num_vols = 0;
452         return;
453     }
454 
455     /* Tell the provider that the snapshot is finished. */
456     SetEvent(vss_ctx.hEventThaw);
457 
458     assert(vss_ctx.pVssbc);
459     assert(vss_ctx.pAsyncSnapshot);
460 
461     HRESULT hr = WaitForAsync(vss_ctx.pAsyncSnapshot);
462     switch (hr) {
463     case VSS_S_ASYNC_FINISHED:
464         hr = vss_ctx.pVssbc->BackupComplete(pAsync.replace());
465         if (SUCCEEDED(hr)) {
466             hr = WaitForAsync(pAsync);
467         }
468         if (FAILED(hr)) {
469             err_set(errset, hr, "failed to complete backup");
470         }
471         break;
472 
473     case (HRESULT)VSS_E_OBJECT_NOT_FOUND:
474         /*
475          * On Windows earlier than 2008 SP2 which does not support
476          * VSS_VOLSNAP_ATTR_NO_AUTORECOVERY context, the final commit is not
477          * skipped and VSS is aborted by VSS_E_OBJECT_NOT_FOUND. However, as
478          * the system had been frozen until fsfreeze-thaw command was issued,
479          * we ignore this error.
480          */
481         vss_ctx.pVssbc->AbortBackup();
482         break;
483 
484     case VSS_E_UNEXPECTED_PROVIDER_ERROR:
485         if (WaitForSingleObject(vss_ctx.hEventTimeout, 0) != WAIT_OBJECT_0) {
486             err_set(errset, hr, "unexpected error in VSS provider");
487             break;
488         }
489         /* fall through if hEventTimeout is signaled */
490 
491     case (HRESULT)VSS_E_HOLD_WRITES_TIMEOUT:
492         err_set(errset, hr, "couldn't hold writes: "
493                 "fsfreeze is limited up to 10 seconds");
494         break;
495 
496     default:
497         err_set(errset, hr, "failed to do snapshot set");
498     }
499 
500     if (err_is_set(errset)) {
501         vss_ctx.pVssbc->AbortBackup();
502     }
503     *num_vols = vss_ctx.cFrozenVols;
504     requester_cleanup();
505 
506     CoUninitialize();
507 }
508