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