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 } 381 382 CloseHandle(hEventThaw); 383 CloseHandle(hEventFrozen); 384 CloseHandle(hEventTimeout); 385 return hr; 386 } 387 388 STDMETHODIMP CQGAVssProvider::PostCommitSnapshots( 389 VSS_ID SnapshotSetId, LONG lSnapshotsCount) 390 { 391 return S_OK; 392 } 393 394 STDMETHODIMP CQGAVssProvider::PreFinalCommitSnapshots(VSS_ID SnapshotSetId) 395 { 396 return S_OK; 397 } 398 399 STDMETHODIMP CQGAVssProvider::PostFinalCommitSnapshots(VSS_ID SnapshotSetId) 400 { 401 return S_OK; 402 } 403 404 STDMETHODIMP CQGAVssProvider::AbortSnapshots(VSS_ID SnapshotSetId) 405 { 406 return S_OK; 407 } 408 409 /* 410 * IVssProviderNotifications methods 411 */ 412 413 STDMETHODIMP CQGAVssProvider::OnLoad(IUnknown *pCallback) 414 { 415 return S_OK; 416 } 417 418 STDMETHODIMP CQGAVssProvider::OnUnload(BOOL bForceUnload) 419 { 420 return S_OK; 421 } 422 423 424 /* 425 * CQGAVssProviderFactory class 426 */ 427 428 class CQGAVssProviderFactory : public IClassFactory 429 { 430 public: 431 STDMETHODIMP QueryInterface(REFIID riid, void **ppv); 432 STDMETHODIMP_(ULONG) AddRef(); 433 STDMETHODIMP_(ULONG) Release(); 434 STDMETHODIMP CreateInstance( 435 IUnknown *pUnknownOuter, REFIID iid, void **ppv); 436 STDMETHODIMP LockServer(BOOL lock) { return E_NOTIMPL; } 437 438 CQGAVssProviderFactory(); 439 ~CQGAVssProviderFactory(); 440 441 private: 442 long m_nRefCount; 443 }; 444 445 CQGAVssProviderFactory::CQGAVssProviderFactory() 446 { 447 m_nRefCount = 0; 448 LockModule(TRUE); 449 } 450 451 CQGAVssProviderFactory::~CQGAVssProviderFactory() 452 { 453 LockModule(FALSE); 454 } 455 456 STDMETHODIMP CQGAVssProviderFactory::QueryInterface(REFIID riid, void **ppv) 457 { 458 if (riid == IID_IUnknown || riid == IID_IClassFactory) { 459 *ppv = static_cast<void*>(this); 460 AddRef(); 461 return S_OK; 462 } 463 *ppv = NULL; 464 return E_NOINTERFACE; 465 } 466 467 STDMETHODIMP_(ULONG) CQGAVssProviderFactory::AddRef() 468 { 469 return InterlockedIncrement(&m_nRefCount); 470 } 471 472 STDMETHODIMP_(ULONG) CQGAVssProviderFactory::Release() 473 { 474 long nRefCount = InterlockedDecrement(&m_nRefCount); 475 if (m_nRefCount == 0) { 476 delete this; 477 } 478 return nRefCount; 479 } 480 481 STDMETHODIMP CQGAVssProviderFactory::CreateInstance( 482 IUnknown *pUnknownOuter, REFIID iid, void **ppv) 483 { 484 CQGAVssProvider *pObj; 485 486 if (pUnknownOuter) { 487 return CLASS_E_NOAGGREGATION; 488 } 489 try { 490 pObj = new CQGAVssProvider; 491 } catch (...) { 492 return E_OUTOFMEMORY; 493 } 494 HRESULT hr = pObj->QueryInterface(iid, ppv); 495 if (FAILED(hr)) { 496 delete pObj; 497 } 498 return hr; 499 } 500 501 502 /* 503 * DLL functions 504 */ 505 506 STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv) 507 { 508 CQGAVssProviderFactory *factory; 509 try { 510 factory = new CQGAVssProviderFactory; 511 } catch (...) { 512 return E_OUTOFMEMORY; 513 } 514 factory->AddRef(); 515 HRESULT hr = factory->QueryInterface(riid, ppv); 516 factory->Release(); 517 return hr; 518 } 519 520 STDAPI DllCanUnloadNow() 521 { 522 return g_nComObjsInUse == 0 ? S_OK : S_FALSE; 523 } 524 525 EXTERN_C 526 BOOL WINAPI DllMain(HINSTANCE hinstDll, DWORD dwReason, LPVOID lpReserved) 527 { 528 if (dwReason == DLL_PROCESS_ATTACH) { 529 g_hinstDll = hinstDll; 530 DisableThreadLibraryCalls(hinstDll); 531 } 532 return TRUE; 533 } 534