/* * QEMU Guest Agent win32 VSS Provider implementations * * Copyright Hitachi Data Systems Corp. 2013 * * Authors: * Tomoki Sekiyama * * 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 #else #include #endif #include #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} }; static 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(static_cast(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(this); AddRef(); return S_OK; } if (riid == IID_IVssSoftwareSnapshotProvider) { *ppObj = static_cast( static_cast(this)); AddRef(); return S_OK; } if (riid == IID_IVssProviderCreateSnapshotSet) { *ppObj = static_cast( static_cast(this)); AddRef(); return S_OK; } if (riid == IID_IVssProviderNotifications) { *ppObj = static_cast( static_cast(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(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); 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; }