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