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