qom: Split out object and class caches
[qemu.git] / qga / vss-win32 / requester.cpp
blob1e8dd3dfa8faf1cd5527d85e80d785208485597f
1 /*
2 * QEMU Guest Agent win32 VSS Requester implementations
4 * Copyright Hitachi Data Systems Corp. 2013
6 * Authors:
7 * Tomoki Sekiyama <tomoki.sekiyama@hds.com>
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.
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"
20 /* Max wait time for frozen event (VSS can only hold writes for 10 seconds) */
21 #define VSS_TIMEOUT_FREEZE_MSEC 10000
23 /* Call QueryStatus every 10 ms while waiting for frozen event */
24 #define VSS_TIMEOUT_EVENT_MSEC 10
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)
31 /* Handle to VSSAPI.DLL */
32 static HMODULE hLib;
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;
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;
51 STDAPI requester_init(void)
53 vss_ctx.hEventFrozen = INVALID_HANDLE_VALUE;
54 vss_ctx.hEventThaw = INVALID_HANDLE_VALUE;
55 vss_ctx.hEventTimeout = INVALID_HANDLE_VALUE;
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;
66 hLib = LoadLibraryA("VSSAPI.DLL");
67 if (!hLib) {
68 fprintf(stderr, "failed to load VSSAPI.DLL\n");
69 return HRESULT_FROM_WIN32(GetLastError());
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
80 if (!pCreateVssBackupComponents) {
81 fprintf(stderr, "failed to get proc address from VSSAPI.DLL\n");
82 return HRESULT_FROM_WIN32(GetLastError());
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());
92 return S_OK;
95 static void requester_cleanup(void)
97 if (vss_ctx.hEventFrozen != INVALID_HANDLE_VALUE) {
98 CloseHandle(vss_ctx.hEventFrozen);
99 vss_ctx.hEventFrozen = INVALID_HANDLE_VALUE;
101 if (vss_ctx.hEventThaw != INVALID_HANDLE_VALUE) {
102 CloseHandle(vss_ctx.hEventThaw);
103 vss_ctx.hEventThaw = INVALID_HANDLE_VALUE;
105 if (vss_ctx.hEventTimeout != INVALID_HANDLE_VALUE) {
106 CloseHandle(vss_ctx.hEventTimeout);
107 vss_ctx.hEventTimeout = INVALID_HANDLE_VALUE;
109 if (vss_ctx.pAsyncSnapshot) {
110 vss_ctx.pAsyncSnapshot->Release();
111 vss_ctx.pAsyncSnapshot = NULL;
113 if (vss_ctx.pVssbc) {
114 vss_ctx.pVssbc->Release();
115 vss_ctx.pVssbc = NULL;
117 vss_ctx.cFrozenVols = 0;
120 STDAPI requester_deinit(void)
122 requester_cleanup();
124 pCreateVssBackupComponents = NULL;
125 pVssFreeSnapshotProperties = NULL;
126 if (hLib) {
127 FreeLibrary(hLib);
128 hLib = NULL;
131 return S_OK;
134 static HRESULT WaitForAsync(IVssAsync *pAsync)
136 HRESULT ret, hr;
138 do {
139 hr = pAsync->Wait();
140 if (FAILED(hr)) {
141 ret = hr;
142 break;
144 hr = pAsync->QueryStatus(&ret, NULL);
145 if (FAILED(hr)) {
146 ret = hr;
147 break;
149 } while (ret == VSS_S_ASYNC_PENDING);
151 return ret;
154 static void AddComponents(ErrorSet *errset)
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;
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;
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;
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;
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;
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;
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;
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;
224 SysFreeString(bstrWriterName);
225 bstrWriterName = NULL;
226 pComponent->FreeComponentInfo(info);
227 info = NULL;
230 out:
231 if (bstrWriterName) {
232 SysFreeString(bstrWriterName);
234 if (pComponent && info) {
235 pComponent->FreeComponentInfo(info);
239 void requester_freeze(int *num_vols, ErrorSet *errset)
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;
252 if (vss_ctx.pVssbc) { /* already frozen */
253 *num_vols = 0;
254 return;
257 CoInitialize(NULL);
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;
266 hr = vss_ctx.pVssbc->InitializeForBackup();
267 if (FAILED(hr)) {
268 err_set(errset, hr, "failed to initialize for backup");
269 goto out;
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;
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.
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);
291 if (FAILED(hr)) {
292 err_set(errset, hr, "failed to set backup context");
293 goto out;
296 hr = vss_ctx.pVssbc->GatherWriterMetadata(pAsync.replace());
297 if (SUCCEEDED(hr)) {
298 hr = WaitForAsync(pAsync);
300 if (FAILED(hr)) {
301 err_set(errset, hr, "failed to gather writer metadata");
302 goto out;
305 AddComponents(errset);
306 if (err_is_set(errset)) {
307 goto out;
310 hr = vss_ctx.pVssbc->StartSnapshotSet(&guidSnapshotSet);
311 if (FAILED(hr)) {
312 err_set(errset, hr, "failed to start snapshot set");
313 goto out;
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;
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;
333 err_set(errset, hr, "failed to add %S to snapshot set",
334 display_name);
335 FindVolumeClose(volume);
336 goto out;
338 num_fixed_drives++;
340 if (!FindNextVolumeW(volume, short_volume_name,
341 sizeof(short_volume_name))) {
342 FindVolumeClose(volume);
343 break;
347 if (num_fixed_drives == 0) {
348 goto out; /* If there is no fixed drive, just exit. */
351 hr = vss_ctx.pVssbc->PrepareForBackup(pAsync.replace());
352 if (SUCCEEDED(hr)) {
353 hr = WaitForAsync(pAsync);
355 if (FAILED(hr)) {
356 err_set(errset, hr, "failed to prepare for backup");
357 goto out;
360 hr = vss_ctx.pVssbc->GatherWriterStatus(pAsync.replace());
361 if (SUCCEEDED(hr)) {
362 hr = WaitForAsync(pAsync);
364 if (FAILED(hr)) {
365 err_set(errset, hr, "failed to gather writer status");
366 goto out;
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;
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;
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;
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;
396 * Start VSS quiescing operations.
397 * CQGAVssProvider::CommitSnapshots will kick vss_ctx.hEventFrozen
398 * after the applications and filesystems are frozen.
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;
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;
413 if (hr != VSS_S_ASYNC_PENDING) {
414 err_set(errset, E_FAIL,
415 "DoSnapshotSet exited without Frozen event");
416 goto out;
418 wait_status = WaitForSingleObject(vss_ctx.hEventFrozen,
419 VSS_TIMEOUT_EVENT_MSEC);
420 if (wait_status != WAIT_TIMEOUT) {
421 break;
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;
430 *num_vols = vss_ctx.cFrozenVols = num_fixed_drives;
431 return;
433 out:
434 if (vss_ctx.pVssbc) {
435 vss_ctx.pVssbc->AbortBackup();
437 requester_cleanup();
438 CoUninitialize();
442 void requester_thaw(int *num_vols, ErrorSet *errset)
444 COMPointer<IVssAsync> pAsync;
446 if (vss_ctx.hEventThaw == INVALID_HANDLE_VALUE) {
448 * In this case, DoSnapshotSet is aborted or not started,
449 * and no volumes must be frozen. We return without an error.
451 *num_vols = 0;
452 return;
455 /* Tell the provider that the snapshot is finished. */
456 SetEvent(vss_ctx.hEventThaw);
458 assert(vss_ctx.pVssbc);
459 assert(vss_ctx.pAsyncSnapshot);
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);
468 if (FAILED(hr)) {
469 err_set(errset, hr, "failed to complete backup");
471 break;
473 case (HRESULT)VSS_E_OBJECT_NOT_FOUND:
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.
481 vss_ctx.pVssbc->AbortBackup();
482 break;
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;
489 /* fall through if hEventTimeout is signaled */
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;
496 default:
497 err_set(errset, hr, "failed to do snapshot set");
500 if (err_is_set(errset)) {
501 vss_ctx.pVssbc->AbortBackup();
503 *num_vols = vss_ctx.cFrozenVols;
504 requester_cleanup();
506 CoUninitialize();