blob: 72d8b0e19d561fc7b738d528122921cc58e9654a [file] [log] [blame]
Tomoki Sekiyamab39297a2013-08-07 11:40:18 -04001/*
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
Michael Rothe55eb802016-02-24 18:14:52 -060013#include "qemu/osdep.h"
Tomoki Sekiyamab39297a2013-08-07 11:40:18 -040014#include "vss-common.h"
Markus Armbrustera9c94272016-06-22 19:11:19 +020015#include <inc/win2003/vscoordint.h>
16#include <inc/win2003/vsprov.h>
Tomoki Sekiyamab39297a2013-08-07 11:40:18 -040017
18#define VSS_TIMEOUT_MSEC (60*1000)
19
20static long g_nComObjsInUse;
21HINSTANCE g_hinstDll;
22
23/* VSS common GUID's */
24
25const CLSID CLSID_VSSCoordinator = { 0xE579AB5F, 0x1CC4, 0x44b4,
26 {0xBE, 0xD9, 0xDE, 0x09, 0x91, 0xFF, 0x06, 0x23} };
27const IID IID_IVssAdmin = { 0x77ED5996, 0x2F63, 0x11d3,
28 {0x8A, 0x39, 0x00, 0xC0, 0x4F, 0x72, 0xD8, 0xE3} };
29
30const IID IID_IVssHardwareSnapshotProvider = { 0x9593A157, 0x44E9, 0x4344,
31 {0xBB, 0xEB, 0x44, 0xFB, 0xF9, 0xB0, 0x6B, 0x10} };
32const IID IID_IVssSoftwareSnapshotProvider = { 0x609e123e, 0x2c5a, 0x44d3,
33 {0x8f, 0x01, 0x0b, 0x1d, 0x9a, 0x47, 0xd1, 0xff} };
34const IID IID_IVssProviderCreateSnapshotSet = { 0x5F894E5B, 0x1E39, 0x4778,
35 {0x8E, 0x23, 0x9A, 0xBA, 0xD9, 0xF0, 0xE0, 0x8C} };
36const IID IID_IVssProviderNotifications = { 0xE561901F, 0x03A5, 0x4afe,
37 {0x86, 0xD0, 0x72, 0xBA, 0xEE, 0xCE, 0x70, 0x04} };
38
39const IID IID_IVssEnumObject = { 0xAE1C7110, 0x2F60, 0x11d3,
40 {0x8A, 0x39, 0x00, 0xC0, 0x4F, 0x72, 0xD8, 0xE3} };
41
42
43void 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
54class CQGAVSSEnumObject : public IVssEnumObject
55{
56public:
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
72private:
73 long m_nRefCount;
74};
75
76CQGAVSSEnumObject::CQGAVSSEnumObject()
77{
78 m_nRefCount = 0;
79 LockModule(TRUE);
80}
81
82CQGAVSSEnumObject::~CQGAVSSEnumObject()
83{
84 LockModule(FALSE);
85}
86
87STDMETHODIMP 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
98STDMETHODIMP_(ULONG) CQGAVSSEnumObject::AddRef()
99{
100 return InterlockedIncrement(&m_nRefCount);
101}
102
103STDMETHODIMP_(ULONG) CQGAVSSEnumObject::Release()
104{
105 long nRefCount = InterlockedDecrement(&m_nRefCount);
106 if (m_nRefCount == 0) {
107 delete this;
108 }
109 return nRefCount;
110}
111
112STDMETHODIMP CQGAVSSEnumObject::Next(
113 ULONG celt, VSS_OBJECT_PROP *rgelt, ULONG *pceltFetched)
114{
115 *pceltFetched = 0;
116 return S_FALSE;
117}
118
119STDMETHODIMP CQGAVSSEnumObject::Skip(ULONG celt)
120{
121 return S_FALSE;
122}
123
124STDMETHODIMP CQGAVSSEnumObject::Reset(void)
125{
126 return S_OK;
127}
128
129STDMETHODIMP CQGAVSSEnumObject::Clone(IVssEnumObject **ppenum)
130{
131 return E_NOTIMPL;
132}
133
134
135/* QGAVssProvider */
136
137class CQGAVssProvider :
138 public IVssSoftwareSnapshotProvider,
139 public IVssProviderCreateSnapshotSet,
140 public IVssProviderNotifications
141{
142public:
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
190private:
191 long m_nRefCount;
192};
193
194CQGAVssProvider::CQGAVssProvider()
195{
196 m_nRefCount = 0;
197 LockModule(TRUE);
198}
199
200CQGAVssProvider::~CQGAVssProvider()
201{
202 LockModule(FALSE);
203}
204
205STDMETHODIMP 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
234STDMETHODIMP_(ULONG) CQGAVssProvider::AddRef()
235{
236 return InterlockedIncrement(&m_nRefCount);
237}
238
239STDMETHODIMP_(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
253STDMETHODIMP CQGAVssProvider::SetContext(LONG lContext)
254{
255 return S_OK;
256}
257
258STDMETHODIMP CQGAVssProvider::GetSnapshotProperties(
259 VSS_ID SnapshotId, VSS_SNAPSHOT_PROP *pProp)
260{
261 return VSS_E_OBJECT_NOT_FOUND;
262}
263
264STDMETHODIMP 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
277STDMETHODIMP CQGAVssProvider::DeleteSnapshots(
278 VSS_ID SourceObjectId, VSS_OBJECT_TYPE eSourceObjectType,
279 BOOL bForceDelete, LONG *plDeletedSnapshots, VSS_ID *pNondeletedSnapshotID)
280{
Tomoki Sekiyamad9e1f572014-01-13 12:25:39 -0500281 *plDeletedSnapshots = 0;
282 *pNondeletedSnapshotID = SourceObjectId;
283 return S_OK;
Tomoki Sekiyamab39297a2013-08-07 11:40:18 -0400284}
285
286STDMETHODIMP CQGAVssProvider::BeginPrepareSnapshot(
287 VSS_ID SnapshotSetId, VSS_ID SnapshotId,
288 VSS_PWSZ pwszVolumeName, LONG lNewContext)
289{
290 return S_OK;
291}
292
293STDMETHODIMP CQGAVssProvider::IsVolumeSupported(
294 VSS_PWSZ pwszVolumeName, BOOL *pbSupportedByThisProvider)
295{
Tomoki Sekiyamaff8adbc2014-01-13 12:25:29 -0500296 HANDLE hEventFrozen;
Tomoki Sekiyamab39297a2013-08-07 11:40:18 -0400297
Tomoki Sekiyamaff8adbc2014-01-13 12:25:29 -0500298 /* 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;
Tomoki Sekiyamab39297a2013-08-07 11:40:18 -0400307 return S_OK;
308}
309
310STDMETHODIMP CQGAVssProvider::IsVolumeSnapshotted(VSS_PWSZ pwszVolumeName,
311 BOOL *pbSnapshotsPresent, LONG *plSnapshotCompatibility)
312{
313 *pbSnapshotsPresent = FALSE;
314 *plSnapshotCompatibility = 0;
315 return S_OK;
316}
317
318STDMETHODIMP CQGAVssProvider::SetSnapshotProperty(VSS_ID SnapshotId,
319 VSS_SNAPSHOT_PROPERTY_ID eSnapshotPropertyId, VARIANT vProperty)
320{
321 return E_NOTIMPL;
322}
323
324STDMETHODIMP CQGAVssProvider::RevertToSnapshot(VSS_ID SnapshotId)
325{
326 return E_NOTIMPL;
327}
328
329STDMETHODIMP CQGAVssProvider::QueryRevertStatus(
330 VSS_PWSZ pwszVolume, IVssAsync **ppAsync)
331{
332 return E_NOTIMPL;
333}
334
335
336/*
337 * IVssProviderCreateSnapshotSet methods
338 */
339
340STDMETHODIMP CQGAVssProvider::EndPrepareSnapshots(VSS_ID SnapshotSetId)
341{
342 return S_OK;
343}
344
345STDMETHODIMP CQGAVssProvider::PreCommitSnapshots(VSS_ID SnapshotSetId)
346{
347 return S_OK;
348}
349
350STDMETHODIMP 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);
Tomoki Sekiyama4c1b8f12014-01-13 12:25:23 -0500356 if (!hEventFrozen) {
Tomoki Sekiyamab39297a2013-08-07 11:40:18 -0400357 return E_FAIL;
358 }
359
360 hEventThaw = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_THAW);
Tomoki Sekiyama4c1b8f12014-01-13 12:25:23 -0500361 if (!hEventThaw) {
Tomoki Sekiyamab39297a2013-08-07 11:40:18 -0400362 CloseHandle(hEventFrozen);
363 return E_FAIL;
364 }
365
366 hEventTimeout = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_TIMEOUT);
Tomoki Sekiyama4c1b8f12014-01-13 12:25:23 -0500367 if (!hEventTimeout) {
Tomoki Sekiyamab39297a2013-08-07 11:40:18 -0400368 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);
Tomoki Sekiyamab39297a2013-08-07 11:40:18 -0400380 }
381
382 CloseHandle(hEventThaw);
383 CloseHandle(hEventFrozen);
384 CloseHandle(hEventTimeout);
385 return hr;
386}
387
388STDMETHODIMP CQGAVssProvider::PostCommitSnapshots(
389 VSS_ID SnapshotSetId, LONG lSnapshotsCount)
390{
391 return S_OK;
392}
393
394STDMETHODIMP CQGAVssProvider::PreFinalCommitSnapshots(VSS_ID SnapshotSetId)
395{
396 return S_OK;
397}
398
399STDMETHODIMP CQGAVssProvider::PostFinalCommitSnapshots(VSS_ID SnapshotSetId)
400{
401 return S_OK;
402}
403
404STDMETHODIMP CQGAVssProvider::AbortSnapshots(VSS_ID SnapshotSetId)
405{
406 return S_OK;
407}
408
409/*
410 * IVssProviderNotifications methods
411 */
412
413STDMETHODIMP CQGAVssProvider::OnLoad(IUnknown *pCallback)
414{
415 return S_OK;
416}
417
418STDMETHODIMP CQGAVssProvider::OnUnload(BOOL bForceUnload)
419{
420 return S_OK;
421}
422
423
424/*
425 * CQGAVssProviderFactory class
426 */
427
428class CQGAVssProviderFactory : public IClassFactory
429{
430public:
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
441private:
442 long m_nRefCount;
443};
444
445CQGAVssProviderFactory::CQGAVssProviderFactory()
446{
447 m_nRefCount = 0;
448 LockModule(TRUE);
449}
450
451CQGAVssProviderFactory::~CQGAVssProviderFactory()
452{
453 LockModule(FALSE);
454}
455
456STDMETHODIMP 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
467STDMETHODIMP_(ULONG) CQGAVssProviderFactory::AddRef()
468{
469 return InterlockedIncrement(&m_nRefCount);
470}
471
472STDMETHODIMP_(ULONG) CQGAVssProviderFactory::Release()
473{
474 long nRefCount = InterlockedDecrement(&m_nRefCount);
475 if (m_nRefCount == 0) {
476 delete this;
477 }
478 return nRefCount;
479}
480
481STDMETHODIMP 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
506STDAPI 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
520STDAPI DllCanUnloadNow()
521{
522 return g_nComObjsInUse == 0 ? S_OK : S_FALSE;
523}
524
525EXTERN_C
526BOOL 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}