]> git.proxmox.com Git - mirror_qemu.git/blob - qga/vss-win32/requester.cpp
Use #include "..." for our own headers, <...> for others
[mirror_qemu.git] / qga / vss-win32 / requester.cpp
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 }