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
LockModule(BOOL lock)48 static 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
CQGAVSSEnumObject()81 CQGAVSSEnumObject::CQGAVSSEnumObject()
82 {
83 m_nRefCount = 0;
84 LockModule(TRUE);
85 }
86
~CQGAVSSEnumObject()87 CQGAVSSEnumObject::~CQGAVSSEnumObject()
88 {
89 LockModule(FALSE);
90 }
91
QueryInterface(REFIID riid,void ** ppObj)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
STDMETHODIMP_(ULONG)103 STDMETHODIMP_(ULONG) CQGAVSSEnumObject::AddRef()
104 {
105 return InterlockedIncrement(&m_nRefCount);
106 }
107
STDMETHODIMP_(ULONG)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
Next(ULONG celt,VSS_OBJECT_PROP * rgelt,ULONG * pceltFetched)117 STDMETHODIMP CQGAVSSEnumObject::Next(
118 ULONG celt, VSS_OBJECT_PROP *rgelt, ULONG *pceltFetched)
119 {
120 *pceltFetched = 0;
121 return S_FALSE;
122 }
123
Skip(ULONG celt)124 STDMETHODIMP CQGAVSSEnumObject::Skip(ULONG celt)
125 {
126 return S_FALSE;
127 }
128
Reset(void)129 STDMETHODIMP CQGAVSSEnumObject::Reset(void)
130 {
131 return S_OK;
132 }
133
Clone(IVssEnumObject ** ppenum)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
CQGAVssProvider()199 CQGAVssProvider::CQGAVssProvider()
200 {
201 m_nRefCount = 0;
202 LockModule(TRUE);
203 }
204
~CQGAVssProvider()205 CQGAVssProvider::~CQGAVssProvider()
206 {
207 LockModule(FALSE);
208 }
209
QueryInterface(REFIID riid,void ** ppObj)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
STDMETHODIMP_(ULONG)239 STDMETHODIMP_(ULONG) CQGAVssProvider::AddRef()
240 {
241 return InterlockedIncrement(&m_nRefCount);
242 }
243
STDMETHODIMP_(ULONG)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
SetContext(LONG lContext)258 STDMETHODIMP CQGAVssProvider::SetContext(LONG lContext)
259 {
260 return S_OK;
261 }
262
GetSnapshotProperties(VSS_ID SnapshotId,VSS_SNAPSHOT_PROP * pProp)263 STDMETHODIMP CQGAVssProvider::GetSnapshotProperties(
264 VSS_ID SnapshotId, VSS_SNAPSHOT_PROP *pProp)
265 {
266 return VSS_E_OBJECT_NOT_FOUND;
267 }
268
Query(VSS_ID QueriedObjectId,VSS_OBJECT_TYPE eQueriedObjectType,VSS_OBJECT_TYPE eReturnedObjectsType,IVssEnumObject ** ppEnum)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
DeleteSnapshots(VSS_ID SourceObjectId,VSS_OBJECT_TYPE eSourceObjectType,BOOL bForceDelete,LONG * plDeletedSnapshots,VSS_ID * pNondeletedSnapshotID)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
BeginPrepareSnapshot(VSS_ID SnapshotSetId,VSS_ID SnapshotId,VSS_PWSZ pwszVolumeName,LONG lNewContext)291 STDMETHODIMP CQGAVssProvider::BeginPrepareSnapshot(
292 VSS_ID SnapshotSetId, VSS_ID SnapshotId,
293 VSS_PWSZ pwszVolumeName, LONG lNewContext)
294 {
295 return S_OK;
296 }
297
IsVolumeSupported(VSS_PWSZ pwszVolumeName,BOOL * pbSupportedByThisProvider)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
IsVolumeSnapshotted(VSS_PWSZ pwszVolumeName,BOOL * pbSnapshotsPresent,LONG * plSnapshotCompatibility)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
SetSnapshotProperty(VSS_ID SnapshotId,VSS_SNAPSHOT_PROPERTY_ID eSnapshotPropertyId,VARIANT vProperty)323 STDMETHODIMP CQGAVssProvider::SetSnapshotProperty(VSS_ID SnapshotId,
324 VSS_SNAPSHOT_PROPERTY_ID eSnapshotPropertyId, VARIANT vProperty)
325 {
326 return E_NOTIMPL;
327 }
328
RevertToSnapshot(VSS_ID SnapshotId)329 STDMETHODIMP CQGAVssProvider::RevertToSnapshot(VSS_ID SnapshotId)
330 {
331 return E_NOTIMPL;
332 }
333
QueryRevertStatus(VSS_PWSZ pwszVolume,IVssAsync ** ppAsync)334 STDMETHODIMP CQGAVssProvider::QueryRevertStatus(
335 VSS_PWSZ pwszVolume, IVssAsync **ppAsync)
336 {
337 return E_NOTIMPL;
338 }
339
340
341 /*
342 * IVssProviderCreateSnapshotSet methods
343 */
344
EndPrepareSnapshots(VSS_ID SnapshotSetId)345 STDMETHODIMP CQGAVssProvider::EndPrepareSnapshots(VSS_ID SnapshotSetId)
346 {
347 return S_OK;
348 }
349
PreCommitSnapshots(VSS_ID SnapshotSetId)350 STDMETHODIMP CQGAVssProvider::PreCommitSnapshots(VSS_ID SnapshotSetId)
351 {
352 return S_OK;
353 }
354
CommitSnapshots(VSS_ID SnapshotSetId)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
PostCommitSnapshots(VSS_ID SnapshotSetId,LONG lSnapshotsCount)393 STDMETHODIMP CQGAVssProvider::PostCommitSnapshots(
394 VSS_ID SnapshotSetId, LONG lSnapshotsCount)
395 {
396 return S_OK;
397 }
398
PreFinalCommitSnapshots(VSS_ID SnapshotSetId)399 STDMETHODIMP CQGAVssProvider::PreFinalCommitSnapshots(VSS_ID SnapshotSetId)
400 {
401 return S_OK;
402 }
403
PostFinalCommitSnapshots(VSS_ID SnapshotSetId)404 STDMETHODIMP CQGAVssProvider::PostFinalCommitSnapshots(VSS_ID SnapshotSetId)
405 {
406 return S_OK;
407 }
408
AbortSnapshots(VSS_ID SnapshotSetId)409 STDMETHODIMP CQGAVssProvider::AbortSnapshots(VSS_ID SnapshotSetId)
410 {
411 return S_OK;
412 }
413
414 /*
415 * IVssProviderNotifications methods
416 */
417
OnLoad(IUnknown * pCallback)418 STDMETHODIMP CQGAVssProvider::OnLoad(IUnknown *pCallback)
419 {
420 return S_OK;
421 }
422
OnUnload(BOOL bForceUnload)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);
LockServer(BOOL lock)441 STDMETHODIMP LockServer(BOOL lock) { return E_NOTIMPL; }
442
443 CQGAVssProviderFactory();
444 ~CQGAVssProviderFactory();
445
446 private:
447 long m_nRefCount;
448 };
449
CQGAVssProviderFactory()450 CQGAVssProviderFactory::CQGAVssProviderFactory()
451 {
452 m_nRefCount = 0;
453 LockModule(TRUE);
454 }
455
~CQGAVssProviderFactory()456 CQGAVssProviderFactory::~CQGAVssProviderFactory()
457 {
458 LockModule(FALSE);
459 }
460
QueryInterface(REFIID riid,void ** ppv)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
STDMETHODIMP_(ULONG)472 STDMETHODIMP_(ULONG) CQGAVssProviderFactory::AddRef()
473 {
474 return InterlockedIncrement(&m_nRefCount);
475 }
476
STDMETHODIMP_(ULONG)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
CreateInstance(IUnknown * pUnknownOuter,REFIID iid,void ** ppv)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
DllGetClassObject(REFCLSID rclsid,REFIID riid,LPVOID * ppv)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
DllCanUnloadNow()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 EXTERN_C
DllMain(HINSTANCE hinstDll,DWORD dwReason,LPVOID lpReserved)534 BOOL WINAPI DllMain(HINSTANCE hinstDll, DWORD dwReason, LPVOID lpReserved)
535 {
536 qga_debug("begin, reason = %lu", dwReason);
537 if (dwReason == DLL_PROCESS_ATTACH) {
538 g_hinstDll = hinstDll;
539 DisableThreadLibraryCalls(hinstDll);
540 }
541 qga_debug_end;
542 return TRUE;
543 }
544