qemu/qga/vss-win32/provider.cpp
Tomoki Sekiyama b39297aedf qemu-ga: Add Windows VSS provider and requester as DLL
Adds VSS provider and requester as a qga-vss.dll, which is loaded by
Windows VSS service as well as by qemu-ga.

"provider.cpp" implements a basic stub of a software VSS provider.
Currently, this module only relays a frozen event from VSS service to the
agent, and thaw event from the agent to VSS service, to block VSS process
to keep the system frozen while snapshots are taken at the host.

To register the provider to the guest system as COM+ application, the type
library (.tlb) for qga-vss.dll is required. To build it from COM IDL (.idl),
VisualC++, MIDL and stdole2.tlb in Windows SDK are required. This patch also
adds pre-compiled .tlb file in the repository in order to enable
cross-compile qemu-ga.exe for Windows with VSS support.

"requester.cpp" provides the VSS requester to kick the VSS snapshot process.
Qemu-ga.exe works without the DLL, although fsfreeze features are disabled.

These functions are only supported in Windows 2003 or later. In older
systems, fsfreeze features are disabled.

In several versions of Windows which don't support attribute
VSS_VOLSNAP_ATTR_NO_AUTORECOVERY, DoSnapshotSet fails with error
VSS_E_OBJECT_NOT_FOUND. In this patch, we just ignore this error.
To solve this fundamentally, we need a framework to handle mount writable
snapshot on guests, which is required by VSS auto-recovery feature
(cleanup phase after a snapshot is taken).

Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2013-09-09 14:17:57 -05:00

524 lines
13 KiB
C++

/*
* 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 <stdio.h>
#include "vss-common.h"
#include "inc/win2003/vscoordint.h"
#include "inc/win2003/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)
{
return E_NOTIMPL;
}
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)
{
*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 == INVALID_HANDLE_VALUE) {
return E_FAIL;
}
hEventThaw = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_THAW);
if (hEventThaw == INVALID_HANDLE_VALUE) {
CloseHandle(hEventFrozen);
return E_FAIL;
}
hEventTimeout = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_TIMEOUT);
if (hEventTimeout == INVALID_HANDLE_VALUE) {
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);
hr = E_ABORT;
}
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)
{
if (dwReason == DLL_PROCESS_ATTACH) {
g_hinstDll = hinstDll;
DisableThreadLibraryCalls(hinstDll);
}
return TRUE;
}