1 /* 2 * QEMU Guest Agent win32 VSS Provider 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 #ifdef HAVE_VSS_SDK 16 #include <vscoordint.h> 17 #else 18 #include <vsadmin.h> 19 #endif 20 #include <vsprov.h> 21 22 #define VSS_TIMEOUT_MSEC (60*1000) 23 24 static long g_nComObjsInUse; 25 HINSTANCE g_hinstDll; 26 27 /* VSS common GUID's */ 28 29 const CLSID CLSID_VSSCoordinator = { 0xE579AB5F, 0x1CC4, 0x44b4, 30 {0xBE, 0xD9, 0xDE, 0x09, 0x91, 0xFF, 0x06, 0x23} }; 31 const IID IID_IVssAdmin = { 0x77ED5996, 0x2F63, 0x11d3, 32 {0x8A, 0x39, 0x00, 0xC0, 0x4F, 0x72, 0xD8, 0xE3} }; 33 34 const IID IID_IVssHardwareSnapshotProvider = { 0x9593A157, 0x44E9, 0x4344, 35 {0xBB, 0xEB, 0x44, 0xFB, 0xF9, 0xB0, 0x6B, 0x10} }; 36 const IID IID_IVssSoftwareSnapshotProvider = { 0x609e123e, 0x2c5a, 0x44d3, 37 {0x8f, 0x01, 0x0b, 0x1d, 0x9a, 0x47, 0xd1, 0xff} }; 38 const IID IID_IVssProviderCreateSnapshotSet = { 0x5F894E5B, 0x1E39, 0x4778, 39 {0x8E, 0x23, 0x9A, 0xBA, 0xD9, 0xF0, 0xE0, 0x8C} }; 40 const IID IID_IVssProviderNotifications = { 0xE561901F, 0x03A5, 0x4afe, 41 {0x86, 0xD0, 0x72, 0xBA, 0xEE, 0xCE, 0x70, 0x04} }; 42 43 const IID IID_IVssEnumObject = { 0xAE1C7110, 0x2F60, 0x11d3, 44 {0x8A, 0x39, 0x00, 0xC0, 0x4F, 0x72, 0xD8, 0xE3} }; 45 46 47 void LockModule(BOOL lock) 48 { 49 if (lock) { 50 InterlockedIncrement(&g_nComObjsInUse); 51 } else { 52 InterlockedDecrement(&g_nComObjsInUse); 53 } 54 } 55 56 /* Empty enumerator for VssObject */ 57 58 class CQGAVSSEnumObject : public IVssEnumObject 59 { 60 public: 61 STDMETHODIMP QueryInterface(REFIID riid, void **ppObj); 62 STDMETHODIMP_(ULONG) AddRef(); 63 STDMETHODIMP_(ULONG) Release(); 64 65 /* IVssEnumObject Methods */ 66 STDMETHODIMP Next( 67 ULONG celt, VSS_OBJECT_PROP *rgelt, ULONG *pceltFetched); 68 STDMETHODIMP Skip(ULONG celt); 69 STDMETHODIMP Reset(void); 70 STDMETHODIMP Clone(IVssEnumObject **ppenum); 71 72 /* CQGAVSSEnumObject Methods */ 73 CQGAVSSEnumObject(); 74 ~CQGAVSSEnumObject(); 75 76 private: 77 long m_nRefCount; 78 }; 79 80 CQGAVSSEnumObject::CQGAVSSEnumObject() 81 { 82 m_nRefCount = 0; 83 LockModule(TRUE); 84 } 85 86 CQGAVSSEnumObject::~CQGAVSSEnumObject() 87 { 88 LockModule(FALSE); 89 } 90 91 STDMETHODIMP CQGAVSSEnumObject::QueryInterface(REFIID riid, void **ppObj) 92 { 93 if (riid == IID_IUnknown || riid == IID_IVssEnumObject) { 94 *ppObj = static_cast<void*>(static_cast<IVssEnumObject*>(this)); 95 AddRef(); 96 return S_OK; 97 } 98 *ppObj = NULL; 99 return E_NOINTERFACE; 100 } 101 102 STDMETHODIMP_(ULONG) CQGAVSSEnumObject::AddRef() 103 { 104 return InterlockedIncrement(&m_nRefCount); 105 } 106 107 STDMETHODIMP_(ULONG) CQGAVSSEnumObject::Release() 108 { 109 long nRefCount = InterlockedDecrement(&m_nRefCount); 110 if (m_nRefCount == 0) { 111 delete this; 112 } 113 return nRefCount; 114 } 115 116 STDMETHODIMP CQGAVSSEnumObject::Next( 117 ULONG celt, VSS_OBJECT_PROP *rgelt, ULONG *pceltFetched) 118 { 119 *pceltFetched = 0; 120 return S_FALSE; 121 } 122 123 STDMETHODIMP CQGAVSSEnumObject::Skip(ULONG celt) 124 { 125 return S_FALSE; 126 } 127 128 STDMETHODIMP CQGAVSSEnumObject::Reset(void) 129 { 130 return S_OK; 131 } 132 133 STDMETHODIMP CQGAVSSEnumObject::Clone(IVssEnumObject **ppenum) 134 { 135 return E_NOTIMPL; 136 } 137 138 139 /* QGAVssProvider */ 140 141 class CQGAVssProvider : 142 public IVssSoftwareSnapshotProvider, 143 public IVssProviderCreateSnapshotSet, 144 public IVssProviderNotifications 145 { 146 public: 147 STDMETHODIMP QueryInterface(REFIID riid, void **ppObj); 148 STDMETHODIMP_(ULONG) AddRef(); 149 STDMETHODIMP_(ULONG) Release(); 150 151 /* IVssSoftwareSnapshotProvider Methods */ 152 STDMETHODIMP SetContext(LONG lContext); 153 STDMETHODIMP GetSnapshotProperties( 154 VSS_ID SnapshotId, VSS_SNAPSHOT_PROP *pProp); 155 STDMETHODIMP Query( 156 VSS_ID QueriedObjectId, VSS_OBJECT_TYPE eQueriedObjectType, 157 VSS_OBJECT_TYPE eReturnedObjectsType, IVssEnumObject **ppEnum); 158 STDMETHODIMP DeleteSnapshots( 159 VSS_ID SourceObjectId, VSS_OBJECT_TYPE eSourceObjectType, 160 BOOL bForceDelete, LONG *plDeletedSnapshots, 161 VSS_ID *pNondeletedSnapshotID); 162 STDMETHODIMP BeginPrepareSnapshot( 163 VSS_ID SnapshotSetId, VSS_ID SnapshotId, 164 VSS_PWSZ pwszVolumeName, LONG lNewContext); 165 STDMETHODIMP IsVolumeSupported( 166 VSS_PWSZ pwszVolumeName, BOOL *pbSupportedByThisProvider); 167 STDMETHODIMP IsVolumeSnapshotted( 168 VSS_PWSZ pwszVolumeName, BOOL *pbSnapshotsPresent, 169 LONG *plSnapshotCompatibility); 170 STDMETHODIMP SetSnapshotProperty( 171 VSS_ID SnapshotId, VSS_SNAPSHOT_PROPERTY_ID eSnapshotPropertyId, 172 VARIANT vProperty); 173 STDMETHODIMP RevertToSnapshot(VSS_ID SnapshotId); 174 STDMETHODIMP QueryRevertStatus(VSS_PWSZ pwszVolume, IVssAsync **ppAsync); 175 176 /* IVssProviderCreateSnapshotSet Methods */ 177 STDMETHODIMP EndPrepareSnapshots(VSS_ID SnapshotSetId); 178 STDMETHODIMP PreCommitSnapshots(VSS_ID SnapshotSetId); 179 STDMETHODIMP CommitSnapshots(VSS_ID SnapshotSetId); 180 STDMETHODIMP PostCommitSnapshots( 181 VSS_ID SnapshotSetId, LONG lSnapshotsCount); 182 STDMETHODIMP PreFinalCommitSnapshots(VSS_ID SnapshotSetId); 183 STDMETHODIMP PostFinalCommitSnapshots(VSS_ID SnapshotSetId); 184 STDMETHODIMP AbortSnapshots(VSS_ID SnapshotSetId); 185 186 /* IVssProviderNotifications Methods */ 187 STDMETHODIMP OnLoad(IUnknown *pCallback); 188 STDMETHODIMP OnUnload(BOOL bForceUnload); 189 190 /* CQGAVssProvider Methods */ 191 CQGAVssProvider(); 192 ~CQGAVssProvider(); 193 194 private: 195 long m_nRefCount; 196 }; 197 198 CQGAVssProvider::CQGAVssProvider() 199 { 200 m_nRefCount = 0; 201 LockModule(TRUE); 202 } 203 204 CQGAVssProvider::~CQGAVssProvider() 205 { 206 LockModule(FALSE); 207 } 208 209 STDMETHODIMP CQGAVssProvider::QueryInterface(REFIID riid, void **ppObj) 210 { 211 if (riid == IID_IUnknown) { 212 *ppObj = static_cast<void*>(this); 213 AddRef(); 214 return S_OK; 215 } 216 if (riid == IID_IVssSoftwareSnapshotProvider) { 217 *ppObj = static_cast<void*>( 218 static_cast<IVssSoftwareSnapshotProvider*>(this)); 219 AddRef(); 220 return S_OK; 221 } 222 if (riid == IID_IVssProviderCreateSnapshotSet) { 223 *ppObj = static_cast<void*>( 224 static_cast<IVssProviderCreateSnapshotSet*>(this)); 225 AddRef(); 226 return S_OK; 227 } 228 if (riid == IID_IVssProviderNotifications) { 229 *ppObj = static_cast<void*>( 230 static_cast<IVssProviderNotifications*>(this)); 231 AddRef(); 232 return S_OK; 233 } 234 *ppObj = NULL; 235 return E_NOINTERFACE; 236 } 237 238 STDMETHODIMP_(ULONG) CQGAVssProvider::AddRef() 239 { 240 return InterlockedIncrement(&m_nRefCount); 241 } 242 243 STDMETHODIMP_(ULONG) CQGAVssProvider::Release() 244 { 245 long nRefCount = InterlockedDecrement(&m_nRefCount); 246 if (m_nRefCount == 0) { 247 delete this; 248 } 249 return nRefCount; 250 } 251 252 253 /* 254 * IVssSoftwareSnapshotProvider methods 255 */ 256 257 STDMETHODIMP CQGAVssProvider::SetContext(LONG lContext) 258 { 259 return S_OK; 260 } 261 262 STDMETHODIMP CQGAVssProvider::GetSnapshotProperties( 263 VSS_ID SnapshotId, VSS_SNAPSHOT_PROP *pProp) 264 { 265 return VSS_E_OBJECT_NOT_FOUND; 266 } 267 268 STDMETHODIMP CQGAVssProvider::Query( 269 VSS_ID QueriedObjectId, VSS_OBJECT_TYPE eQueriedObjectType, 270 VSS_OBJECT_TYPE eReturnedObjectsType, IVssEnumObject **ppEnum) 271 { 272 try { 273 *ppEnum = new CQGAVSSEnumObject; 274 } catch (...) { 275 return E_OUTOFMEMORY; 276 } 277 (*ppEnum)->AddRef(); 278 return S_OK; 279 } 280 281 STDMETHODIMP CQGAVssProvider::DeleteSnapshots( 282 VSS_ID SourceObjectId, VSS_OBJECT_TYPE eSourceObjectType, 283 BOOL bForceDelete, LONG *plDeletedSnapshots, VSS_ID *pNondeletedSnapshotID) 284 { 285 *plDeletedSnapshots = 0; 286 *pNondeletedSnapshotID = SourceObjectId; 287 return S_OK; 288 } 289 290 STDMETHODIMP CQGAVssProvider::BeginPrepareSnapshot( 291 VSS_ID SnapshotSetId, VSS_ID SnapshotId, 292 VSS_PWSZ pwszVolumeName, LONG lNewContext) 293 { 294 return S_OK; 295 } 296 297 STDMETHODIMP CQGAVssProvider::IsVolumeSupported( 298 VSS_PWSZ pwszVolumeName, BOOL *pbSupportedByThisProvider) 299 { 300 HANDLE hEventFrozen; 301 302 /* Check if a requester is qemu-ga by whether an event is created */ 303 hEventFrozen = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_FROZEN); 304 if (!hEventFrozen) { 305 *pbSupportedByThisProvider = FALSE; 306 return S_OK; 307 } 308 CloseHandle(hEventFrozen); 309 310 *pbSupportedByThisProvider = TRUE; 311 return S_OK; 312 } 313 314 STDMETHODIMP CQGAVssProvider::IsVolumeSnapshotted(VSS_PWSZ pwszVolumeName, 315 BOOL *pbSnapshotsPresent, LONG *plSnapshotCompatibility) 316 { 317 *pbSnapshotsPresent = FALSE; 318 *plSnapshotCompatibility = 0; 319 return S_OK; 320 } 321 322 STDMETHODIMP CQGAVssProvider::SetSnapshotProperty(VSS_ID SnapshotId, 323 VSS_SNAPSHOT_PROPERTY_ID eSnapshotPropertyId, VARIANT vProperty) 324 { 325 return E_NOTIMPL; 326 } 327 328 STDMETHODIMP CQGAVssProvider::RevertToSnapshot(VSS_ID SnapshotId) 329 { 330 return E_NOTIMPL; 331 } 332 333 STDMETHODIMP CQGAVssProvider::QueryRevertStatus( 334 VSS_PWSZ pwszVolume, IVssAsync **ppAsync) 335 { 336 return E_NOTIMPL; 337 } 338 339 340 /* 341 * IVssProviderCreateSnapshotSet methods 342 */ 343 344 STDMETHODIMP CQGAVssProvider::EndPrepareSnapshots(VSS_ID SnapshotSetId) 345 { 346 return S_OK; 347 } 348 349 STDMETHODIMP CQGAVssProvider::PreCommitSnapshots(VSS_ID SnapshotSetId) 350 { 351 return S_OK; 352 } 353 354 STDMETHODIMP CQGAVssProvider::CommitSnapshots(VSS_ID SnapshotSetId) 355 { 356 HRESULT hr = S_OK; 357 HANDLE hEventFrozen, hEventThaw, hEventTimeout; 358 359 hEventFrozen = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_FROZEN); 360 if (!hEventFrozen) { 361 return E_FAIL; 362 } 363 364 hEventThaw = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_THAW); 365 if (!hEventThaw) { 366 CloseHandle(hEventFrozen); 367 return E_FAIL; 368 } 369 370 hEventTimeout = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_TIMEOUT); 371 if (!hEventTimeout) { 372 CloseHandle(hEventFrozen); 373 CloseHandle(hEventThaw); 374 return E_FAIL; 375 } 376 377 /* Send event to qemu-ga to notify filesystem is frozen */ 378 SetEvent(hEventFrozen); 379 380 /* Wait until the snapshot is taken by the host. */ 381 if (WaitForSingleObject(hEventThaw, VSS_TIMEOUT_MSEC) != WAIT_OBJECT_0) { 382 /* Send event to qemu-ga to notify the provider is timed out */ 383 SetEvent(hEventTimeout); 384 } 385 386 CloseHandle(hEventThaw); 387 CloseHandle(hEventFrozen); 388 CloseHandle(hEventTimeout); 389 return hr; 390 } 391 392 STDMETHODIMP CQGAVssProvider::PostCommitSnapshots( 393 VSS_ID SnapshotSetId, LONG lSnapshotsCount) 394 { 395 return S_OK; 396 } 397 398 STDMETHODIMP CQGAVssProvider::PreFinalCommitSnapshots(VSS_ID SnapshotSetId) 399 { 400 return S_OK; 401 } 402 403 STDMETHODIMP CQGAVssProvider::PostFinalCommitSnapshots(VSS_ID SnapshotSetId) 404 { 405 return S_OK; 406 } 407 408 STDMETHODIMP CQGAVssProvider::AbortSnapshots(VSS_ID SnapshotSetId) 409 { 410 return S_OK; 411 } 412 413 /* 414 * IVssProviderNotifications methods 415 */ 416 417 STDMETHODIMP CQGAVssProvider::OnLoad(IUnknown *pCallback) 418 { 419 return S_OK; 420 } 421 422 STDMETHODIMP CQGAVssProvider::OnUnload(BOOL bForceUnload) 423 { 424 return S_OK; 425 } 426 427 428 /* 429 * CQGAVssProviderFactory class 430 */ 431 432 class CQGAVssProviderFactory : public IClassFactory 433 { 434 public: 435 STDMETHODIMP QueryInterface(REFIID riid, void **ppv); 436 STDMETHODIMP_(ULONG) AddRef(); 437 STDMETHODIMP_(ULONG) Release(); 438 STDMETHODIMP CreateInstance( 439 IUnknown *pUnknownOuter, REFIID iid, void **ppv); 440 STDMETHODIMP LockServer(BOOL lock) { return E_NOTIMPL; } 441 442 CQGAVssProviderFactory(); 443 ~CQGAVssProviderFactory(); 444 445 private: 446 long m_nRefCount; 447 }; 448 449 CQGAVssProviderFactory::CQGAVssProviderFactory() 450 { 451 m_nRefCount = 0; 452 LockModule(TRUE); 453 } 454 455 CQGAVssProviderFactory::~CQGAVssProviderFactory() 456 { 457 LockModule(FALSE); 458 } 459 460 STDMETHODIMP CQGAVssProviderFactory::QueryInterface(REFIID riid, void **ppv) 461 { 462 if (riid == IID_IUnknown || riid == IID_IClassFactory) { 463 *ppv = static_cast<void*>(this); 464 AddRef(); 465 return S_OK; 466 } 467 *ppv = NULL; 468 return E_NOINTERFACE; 469 } 470 471 STDMETHODIMP_(ULONG) CQGAVssProviderFactory::AddRef() 472 { 473 return InterlockedIncrement(&m_nRefCount); 474 } 475 476 STDMETHODIMP_(ULONG) CQGAVssProviderFactory::Release() 477 { 478 long nRefCount = InterlockedDecrement(&m_nRefCount); 479 if (m_nRefCount == 0) { 480 delete this; 481 } 482 return nRefCount; 483 } 484 485 STDMETHODIMP CQGAVssProviderFactory::CreateInstance( 486 IUnknown *pUnknownOuter, REFIID iid, void **ppv) 487 { 488 CQGAVssProvider *pObj; 489 490 if (pUnknownOuter) { 491 return CLASS_E_NOAGGREGATION; 492 } 493 try { 494 pObj = new CQGAVssProvider; 495 } catch (...) { 496 return E_OUTOFMEMORY; 497 } 498 HRESULT hr = pObj->QueryInterface(iid, ppv); 499 if (FAILED(hr)) { 500 delete pObj; 501 } 502 return hr; 503 } 504 505 506 /* 507 * DLL functions 508 */ 509 510 STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv) 511 { 512 CQGAVssProviderFactory *factory; 513 try { 514 factory = new CQGAVssProviderFactory; 515 } catch (...) { 516 return E_OUTOFMEMORY; 517 } 518 factory->AddRef(); 519 HRESULT hr = factory->QueryInterface(riid, ppv); 520 factory->Release(); 521 return hr; 522 } 523 524 STDAPI DllCanUnloadNow() 525 { 526 return g_nComObjsInUse == 0 ? S_OK : S_FALSE; 527 } 528 529 EXTERN_C 530 BOOL WINAPI DllMain(HINSTANCE hinstDll, DWORD dwReason, LPVOID lpReserved) 531 { 532 if (dwReason == DLL_PROCESS_ATTACH) { 533 g_hinstDll = hinstDll; 534 DisableThreadLibraryCalls(hinstDll); 535 } 536 return TRUE; 537 } 538