xref: /qemu/qga/vss-win32/provider.cpp (revision 336d354b)
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