| /* |
| * QEMU Guest Agent win32 VSS Provider implementations |
| * |
| * Copyright Hitachi Data Systems Corp. 2013 |
| * |
| * Authors: |
| * Tomoki Sekiyama <tomoki.sekiyama@hds.com> |
| * |
| * This work is licensed under the terms of the GNU GPL, version 2 or later. |
| * See the COPYING file in the top-level directory. |
| */ |
| |
| #include "qemu/osdep.h" |
| #include "vss-common.h" |
| #include "vss-debug.h" |
| #ifdef HAVE_VSS_SDK |
| #include <vscoordint.h> |
| #else |
| #include <vsadmin.h> |
| #endif |
| #include <vsprov.h> |
| |
| #define VSS_TIMEOUT_MSEC (60*1000) |
| |
| static long g_nComObjsInUse; |
| HINSTANCE g_hinstDll; |
| |
| /* VSS common GUID's */ |
| |
| const CLSID CLSID_VSSCoordinator = { 0xE579AB5F, 0x1CC4, 0x44b4, |
| {0xBE, 0xD9, 0xDE, 0x09, 0x91, 0xFF, 0x06, 0x23} }; |
| const IID IID_IVssAdmin = { 0x77ED5996, 0x2F63, 0x11d3, |
| {0x8A, 0x39, 0x00, 0xC0, 0x4F, 0x72, 0xD8, 0xE3} }; |
| |
| const IID IID_IVssHardwareSnapshotProvider = { 0x9593A157, 0x44E9, 0x4344, |
| {0xBB, 0xEB, 0x44, 0xFB, 0xF9, 0xB0, 0x6B, 0x10} }; |
| const IID IID_IVssSoftwareSnapshotProvider = { 0x609e123e, 0x2c5a, 0x44d3, |
| {0x8f, 0x01, 0x0b, 0x1d, 0x9a, 0x47, 0xd1, 0xff} }; |
| const IID IID_IVssProviderCreateSnapshotSet = { 0x5F894E5B, 0x1E39, 0x4778, |
| {0x8E, 0x23, 0x9A, 0xBA, 0xD9, 0xF0, 0xE0, 0x8C} }; |
| const IID IID_IVssProviderNotifications = { 0xE561901F, 0x03A5, 0x4afe, |
| {0x86, 0xD0, 0x72, 0xBA, 0xEE, 0xCE, 0x70, 0x04} }; |
| |
| const IID IID_IVssEnumObject = { 0xAE1C7110, 0x2F60, 0x11d3, |
| {0x8A, 0x39, 0x00, 0xC0, 0x4F, 0x72, 0xD8, 0xE3} }; |
| |
| |
| void LockModule(BOOL lock) |
| { |
| if (lock) { |
| InterlockedIncrement(&g_nComObjsInUse); |
| } else { |
| InterlockedDecrement(&g_nComObjsInUse); |
| } |
| } |
| |
| /* Empty enumerator for VssObject */ |
| |
| class CQGAVSSEnumObject : public IVssEnumObject |
| { |
| public: |
| STDMETHODIMP QueryInterface(REFIID riid, void **ppObj); |
| STDMETHODIMP_(ULONG) AddRef(); |
| STDMETHODIMP_(ULONG) Release(); |
| |
| /* IVssEnumObject Methods */ |
| STDMETHODIMP Next( |
| ULONG celt, VSS_OBJECT_PROP *rgelt, ULONG *pceltFetched); |
| STDMETHODIMP Skip(ULONG celt); |
| STDMETHODIMP Reset(void); |
| STDMETHODIMP Clone(IVssEnumObject **ppenum); |
| |
| /* CQGAVSSEnumObject Methods */ |
| CQGAVSSEnumObject(); |
| ~CQGAVSSEnumObject(); |
| |
| private: |
| long m_nRefCount; |
| }; |
| |
| CQGAVSSEnumObject::CQGAVSSEnumObject() |
| { |
| m_nRefCount = 0; |
| LockModule(TRUE); |
| } |
| |
| CQGAVSSEnumObject::~CQGAVSSEnumObject() |
| { |
| LockModule(FALSE); |
| } |
| |
| STDMETHODIMP CQGAVSSEnumObject::QueryInterface(REFIID riid, void **ppObj) |
| { |
| if (riid == IID_IUnknown || riid == IID_IVssEnumObject) { |
| *ppObj = static_cast<void*>(static_cast<IVssEnumObject*>(this)); |
| AddRef(); |
| return S_OK; |
| } |
| *ppObj = NULL; |
| return E_NOINTERFACE; |
| } |
| |
| STDMETHODIMP_(ULONG) CQGAVSSEnumObject::AddRef() |
| { |
| return InterlockedIncrement(&m_nRefCount); |
| } |
| |
| STDMETHODIMP_(ULONG) CQGAVSSEnumObject::Release() |
| { |
| long nRefCount = InterlockedDecrement(&m_nRefCount); |
| if (m_nRefCount == 0) { |
| delete this; |
| } |
| return nRefCount; |
| } |
| |
| STDMETHODIMP CQGAVSSEnumObject::Next( |
| ULONG celt, VSS_OBJECT_PROP *rgelt, ULONG *pceltFetched) |
| { |
| *pceltFetched = 0; |
| return S_FALSE; |
| } |
| |
| STDMETHODIMP CQGAVSSEnumObject::Skip(ULONG celt) |
| { |
| return S_FALSE; |
| } |
| |
| STDMETHODIMP CQGAVSSEnumObject::Reset(void) |
| { |
| return S_OK; |
| } |
| |
| STDMETHODIMP CQGAVSSEnumObject::Clone(IVssEnumObject **ppenum) |
| { |
| return E_NOTIMPL; |
| } |
| |
| |
| /* QGAVssProvider */ |
| |
| class CQGAVssProvider : |
| public IVssSoftwareSnapshotProvider, |
| public IVssProviderCreateSnapshotSet, |
| public IVssProviderNotifications |
| { |
| public: |
| STDMETHODIMP QueryInterface(REFIID riid, void **ppObj); |
| STDMETHODIMP_(ULONG) AddRef(); |
| STDMETHODIMP_(ULONG) Release(); |
| |
| /* IVssSoftwareSnapshotProvider Methods */ |
| STDMETHODIMP SetContext(LONG lContext); |
| STDMETHODIMP GetSnapshotProperties( |
| VSS_ID SnapshotId, VSS_SNAPSHOT_PROP *pProp); |
| STDMETHODIMP Query( |
| VSS_ID QueriedObjectId, VSS_OBJECT_TYPE eQueriedObjectType, |
| VSS_OBJECT_TYPE eReturnedObjectsType, IVssEnumObject **ppEnum); |
| STDMETHODIMP DeleteSnapshots( |
| VSS_ID SourceObjectId, VSS_OBJECT_TYPE eSourceObjectType, |
| BOOL bForceDelete, LONG *plDeletedSnapshots, |
| VSS_ID *pNondeletedSnapshotID); |
| STDMETHODIMP BeginPrepareSnapshot( |
| VSS_ID SnapshotSetId, VSS_ID SnapshotId, |
| VSS_PWSZ pwszVolumeName, LONG lNewContext); |
| STDMETHODIMP IsVolumeSupported( |
| VSS_PWSZ pwszVolumeName, BOOL *pbSupportedByThisProvider); |
| STDMETHODIMP IsVolumeSnapshotted( |
| VSS_PWSZ pwszVolumeName, BOOL *pbSnapshotsPresent, |
| LONG *plSnapshotCompatibility); |
| STDMETHODIMP SetSnapshotProperty( |
| VSS_ID SnapshotId, VSS_SNAPSHOT_PROPERTY_ID eSnapshotPropertyId, |
| VARIANT vProperty); |
| STDMETHODIMP RevertToSnapshot(VSS_ID SnapshotId); |
| STDMETHODIMP QueryRevertStatus(VSS_PWSZ pwszVolume, IVssAsync **ppAsync); |
| |
| /* IVssProviderCreateSnapshotSet Methods */ |
| STDMETHODIMP EndPrepareSnapshots(VSS_ID SnapshotSetId); |
| STDMETHODIMP PreCommitSnapshots(VSS_ID SnapshotSetId); |
| STDMETHODIMP CommitSnapshots(VSS_ID SnapshotSetId); |
| STDMETHODIMP PostCommitSnapshots( |
| VSS_ID SnapshotSetId, LONG lSnapshotsCount); |
| STDMETHODIMP PreFinalCommitSnapshots(VSS_ID SnapshotSetId); |
| STDMETHODIMP PostFinalCommitSnapshots(VSS_ID SnapshotSetId); |
| STDMETHODIMP AbortSnapshots(VSS_ID SnapshotSetId); |
| |
| /* IVssProviderNotifications Methods */ |
| STDMETHODIMP OnLoad(IUnknown *pCallback); |
| STDMETHODIMP OnUnload(BOOL bForceUnload); |
| |
| /* CQGAVssProvider Methods */ |
| CQGAVssProvider(); |
| ~CQGAVssProvider(); |
| |
| private: |
| long m_nRefCount; |
| }; |
| |
| CQGAVssProvider::CQGAVssProvider() |
| { |
| m_nRefCount = 0; |
| LockModule(TRUE); |
| } |
| |
| CQGAVssProvider::~CQGAVssProvider() |
| { |
| LockModule(FALSE); |
| } |
| |
| STDMETHODIMP CQGAVssProvider::QueryInterface(REFIID riid, void **ppObj) |
| { |
| if (riid == IID_IUnknown) { |
| *ppObj = static_cast<void*>(this); |
| AddRef(); |
| return S_OK; |
| } |
| if (riid == IID_IVssSoftwareSnapshotProvider) { |
| *ppObj = static_cast<void*>( |
| static_cast<IVssSoftwareSnapshotProvider*>(this)); |
| AddRef(); |
| return S_OK; |
| } |
| if (riid == IID_IVssProviderCreateSnapshotSet) { |
| *ppObj = static_cast<void*>( |
| static_cast<IVssProviderCreateSnapshotSet*>(this)); |
| AddRef(); |
| return S_OK; |
| } |
| if (riid == IID_IVssProviderNotifications) { |
| *ppObj = static_cast<void*>( |
| static_cast<IVssProviderNotifications*>(this)); |
| AddRef(); |
| return S_OK; |
| } |
| *ppObj = NULL; |
| return E_NOINTERFACE; |
| } |
| |
| STDMETHODIMP_(ULONG) CQGAVssProvider::AddRef() |
| { |
| return InterlockedIncrement(&m_nRefCount); |
| } |
| |
| STDMETHODIMP_(ULONG) CQGAVssProvider::Release() |
| { |
| long nRefCount = InterlockedDecrement(&m_nRefCount); |
| if (m_nRefCount == 0) { |
| delete this; |
| } |
| return nRefCount; |
| } |
| |
| |
| /* |
| * IVssSoftwareSnapshotProvider methods |
| */ |
| |
| STDMETHODIMP CQGAVssProvider::SetContext(LONG lContext) |
| { |
| return S_OK; |
| } |
| |
| STDMETHODIMP CQGAVssProvider::GetSnapshotProperties( |
| VSS_ID SnapshotId, VSS_SNAPSHOT_PROP *pProp) |
| { |
| return VSS_E_OBJECT_NOT_FOUND; |
| } |
| |
| STDMETHODIMP CQGAVssProvider::Query( |
| VSS_ID QueriedObjectId, VSS_OBJECT_TYPE eQueriedObjectType, |
| VSS_OBJECT_TYPE eReturnedObjectsType, IVssEnumObject **ppEnum) |
| { |
| try { |
| *ppEnum = new CQGAVSSEnumObject; |
| } catch (...) { |
| return E_OUTOFMEMORY; |
| } |
| (*ppEnum)->AddRef(); |
| return S_OK; |
| } |
| |
| STDMETHODIMP CQGAVssProvider::DeleteSnapshots( |
| VSS_ID SourceObjectId, VSS_OBJECT_TYPE eSourceObjectType, |
| BOOL bForceDelete, LONG *plDeletedSnapshots, VSS_ID *pNondeletedSnapshotID) |
| { |
| *plDeletedSnapshots = 0; |
| *pNondeletedSnapshotID = SourceObjectId; |
| return S_OK; |
| } |
| |
| STDMETHODIMP CQGAVssProvider::BeginPrepareSnapshot( |
| VSS_ID SnapshotSetId, VSS_ID SnapshotId, |
| VSS_PWSZ pwszVolumeName, LONG lNewContext) |
| { |
| return S_OK; |
| } |
| |
| STDMETHODIMP CQGAVssProvider::IsVolumeSupported( |
| VSS_PWSZ pwszVolumeName, BOOL *pbSupportedByThisProvider) |
| { |
| HANDLE hEventFrozen; |
| |
| /* Check if a requester is qemu-ga by whether an event is created */ |
| hEventFrozen = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_FROZEN); |
| if (!hEventFrozen) { |
| *pbSupportedByThisProvider = FALSE; |
| return S_OK; |
| } |
| CloseHandle(hEventFrozen); |
| |
| *pbSupportedByThisProvider = TRUE; |
| return S_OK; |
| } |
| |
| STDMETHODIMP CQGAVssProvider::IsVolumeSnapshotted(VSS_PWSZ pwszVolumeName, |
| BOOL *pbSnapshotsPresent, LONG *plSnapshotCompatibility) |
| { |
| *pbSnapshotsPresent = FALSE; |
| *plSnapshotCompatibility = 0; |
| return S_OK; |
| } |
| |
| STDMETHODIMP CQGAVssProvider::SetSnapshotProperty(VSS_ID SnapshotId, |
| VSS_SNAPSHOT_PROPERTY_ID eSnapshotPropertyId, VARIANT vProperty) |
| { |
| return E_NOTIMPL; |
| } |
| |
| STDMETHODIMP CQGAVssProvider::RevertToSnapshot(VSS_ID SnapshotId) |
| { |
| return E_NOTIMPL; |
| } |
| |
| STDMETHODIMP CQGAVssProvider::QueryRevertStatus( |
| VSS_PWSZ pwszVolume, IVssAsync **ppAsync) |
| { |
| return E_NOTIMPL; |
| } |
| |
| |
| /* |
| * IVssProviderCreateSnapshotSet methods |
| */ |
| |
| STDMETHODIMP CQGAVssProvider::EndPrepareSnapshots(VSS_ID SnapshotSetId) |
| { |
| return S_OK; |
| } |
| |
| STDMETHODIMP CQGAVssProvider::PreCommitSnapshots(VSS_ID SnapshotSetId) |
| { |
| return S_OK; |
| } |
| |
| STDMETHODIMP CQGAVssProvider::CommitSnapshots(VSS_ID SnapshotSetId) |
| { |
| HRESULT hr = S_OK; |
| HANDLE hEventFrozen, hEventThaw, hEventTimeout; |
| |
| hEventFrozen = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_FROZEN); |
| if (!hEventFrozen) { |
| return E_FAIL; |
| } |
| |
| hEventThaw = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_THAW); |
| if (!hEventThaw) { |
| CloseHandle(hEventFrozen); |
| return E_FAIL; |
| } |
| |
| hEventTimeout = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_TIMEOUT); |
| if (!hEventTimeout) { |
| CloseHandle(hEventFrozen); |
| CloseHandle(hEventThaw); |
| return E_FAIL; |
| } |
| |
| /* Send event to qemu-ga to notify filesystem is frozen */ |
| SetEvent(hEventFrozen); |
| |
| /* Wait until the snapshot is taken by the host. */ |
| if (WaitForSingleObject(hEventThaw, VSS_TIMEOUT_MSEC) != WAIT_OBJECT_0) { |
| /* Send event to qemu-ga to notify the provider is timed out */ |
| SetEvent(hEventTimeout); |
| } |
| |
| CloseHandle(hEventThaw); |
| CloseHandle(hEventFrozen); |
| CloseHandle(hEventTimeout); |
| return hr; |
| } |
| |
| STDMETHODIMP CQGAVssProvider::PostCommitSnapshots( |
| VSS_ID SnapshotSetId, LONG lSnapshotsCount) |
| { |
| return S_OK; |
| } |
| |
| STDMETHODIMP CQGAVssProvider::PreFinalCommitSnapshots(VSS_ID SnapshotSetId) |
| { |
| return S_OK; |
| } |
| |
| STDMETHODIMP CQGAVssProvider::PostFinalCommitSnapshots(VSS_ID SnapshotSetId) |
| { |
| return S_OK; |
| } |
| |
| STDMETHODIMP CQGAVssProvider::AbortSnapshots(VSS_ID SnapshotSetId) |
| { |
| return S_OK; |
| } |
| |
| /* |
| * IVssProviderNotifications methods |
| */ |
| |
| STDMETHODIMP CQGAVssProvider::OnLoad(IUnknown *pCallback) |
| { |
| return S_OK; |
| } |
| |
| STDMETHODIMP CQGAVssProvider::OnUnload(BOOL bForceUnload) |
| { |
| return S_OK; |
| } |
| |
| |
| /* |
| * CQGAVssProviderFactory class |
| */ |
| |
| class CQGAVssProviderFactory : public IClassFactory |
| { |
| public: |
| STDMETHODIMP QueryInterface(REFIID riid, void **ppv); |
| STDMETHODIMP_(ULONG) AddRef(); |
| STDMETHODIMP_(ULONG) Release(); |
| STDMETHODIMP CreateInstance( |
| IUnknown *pUnknownOuter, REFIID iid, void **ppv); |
| STDMETHODIMP LockServer(BOOL lock) { return E_NOTIMPL; } |
| |
| CQGAVssProviderFactory(); |
| ~CQGAVssProviderFactory(); |
| |
| private: |
| long m_nRefCount; |
| }; |
| |
| CQGAVssProviderFactory::CQGAVssProviderFactory() |
| { |
| m_nRefCount = 0; |
| LockModule(TRUE); |
| } |
| |
| CQGAVssProviderFactory::~CQGAVssProviderFactory() |
| { |
| LockModule(FALSE); |
| } |
| |
| STDMETHODIMP CQGAVssProviderFactory::QueryInterface(REFIID riid, void **ppv) |
| { |
| if (riid == IID_IUnknown || riid == IID_IClassFactory) { |
| *ppv = static_cast<void*>(this); |
| AddRef(); |
| return S_OK; |
| } |
| *ppv = NULL; |
| return E_NOINTERFACE; |
| } |
| |
| STDMETHODIMP_(ULONG) CQGAVssProviderFactory::AddRef() |
| { |
| return InterlockedIncrement(&m_nRefCount); |
| } |
| |
| STDMETHODIMP_(ULONG) CQGAVssProviderFactory::Release() |
| { |
| long nRefCount = InterlockedDecrement(&m_nRefCount); |
| if (m_nRefCount == 0) { |
| delete this; |
| } |
| return nRefCount; |
| } |
| |
| STDMETHODIMP CQGAVssProviderFactory::CreateInstance( |
| IUnknown *pUnknownOuter, REFIID iid, void **ppv) |
| { |
| CQGAVssProvider *pObj; |
| |
| if (pUnknownOuter) { |
| return CLASS_E_NOAGGREGATION; |
| } |
| try { |
| pObj = new CQGAVssProvider; |
| } catch (...) { |
| return E_OUTOFMEMORY; |
| } |
| HRESULT hr = pObj->QueryInterface(iid, ppv); |
| if (FAILED(hr)) { |
| delete pObj; |
| } |
| return hr; |
| } |
| |
| |
| /* |
| * DLL functions |
| */ |
| |
| STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv) |
| { |
| CQGAVssProviderFactory *factory; |
| try { |
| factory = new CQGAVssProviderFactory; |
| } catch (...) { |
| return E_OUTOFMEMORY; |
| } |
| factory->AddRef(); |
| HRESULT hr = factory->QueryInterface(riid, ppv); |
| factory->Release(); |
| return hr; |
| } |
| |
| STDAPI DllCanUnloadNow() |
| { |
| return g_nComObjsInUse == 0 ? S_OK : S_FALSE; |
| } |
| |
| EXTERN_C |
| BOOL WINAPI DllMain(HINSTANCE hinstDll, DWORD dwReason, LPVOID lpReserved) |
| { |
| qga_debug("begin, reason = %lu", dwReason); |
| if (dwReason == DLL_PROCESS_ATTACH) { |
| g_hinstDll = hinstDll; |
| DisableThreadLibraryCalls(hinstDll); |
| } |
| qga_debug_end; |
| return TRUE; |
| } |