root / qga / vss-win32 / requester.cpp @ b39297ae
History | View | Annotate | Download (15.7 kB)
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 |
} |