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