[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-devel] [PATCH v8 07/10] qemu-ga: Add Windows VSS provider and
From: |
Michael Roth |
Subject: |
Re: [Qemu-devel] [PATCH v8 07/10] qemu-ga: Add Windows VSS provider and requester as DLL |
Date: |
Thu, 25 Jul 2013 18:36:28 -0500 |
User-agent: |
alot/0.3.4 |
Quoting Tomoki Sekiyama (2013-07-23 17:45:54)
> 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 <address@hidden>
> ---
> Makefile | 2
> Makefile.objs | 2
> configure | 5
> qga/Makefile.objs | 4
> qga/vss-win32/Makefile.objs | 23 ++
> qga/vss-win32/install.cpp | 458 ++++++++++++++++++++++++++++++++++++++
> qga/vss-win32/provider.cpp | 513
> +++++++++++++++++++++++++++++++++++++++++++
> qga/vss-win32/qga-vss.def | 13 +
> qga/vss-win32/qga-vss.idl | 20 ++
> qga/vss-win32/qga-vss.tlb | Bin
> qga/vss-win32/requester.cpp | 487 +++++++++++++++++++++++++++++++++++++++++
> qga/vss-win32/requester.h | 42 ++++
> qga/vss-win32/vss-common.h | 128 +++++++++++
> 13 files changed, 1695 insertions(+), 2 deletions(-)
> create mode 100644 qga/vss-win32/Makefile.objs
> create mode 100644 qga/vss-win32/install.cpp
> create mode 100644 qga/vss-win32/provider.cpp
> create mode 100644 qga/vss-win32/qga-vss.def
> create mode 100644 qga/vss-win32/qga-vss.idl
> create mode 100644 qga/vss-win32/qga-vss.tlb
> create mode 100644 qga/vss-win32/requester.cpp
> create mode 100644 qga/vss-win32/requester.h
> create mode 100644 qga/vss-win32/vss-common.h
>
> diff --git a/Makefile b/Makefile
> index ec35a15..6b64cfb 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -236,7 +236,7 @@ clean:
> rm -f qemu-options.def
> find . -name '*.[oda]' -type f -exec rm -f {} +
> find . -name '*.l[oa]' -type f -exec rm -f {} +
> - rm -f $(TOOLS) $(HELPERS-y) qemu-ga TAGS cscope.* *.pod *~ */*~
> + rm -f $(filter-out %.tlb,$(TOOLS)) $(HELPERS-y) qemu-ga TAGS cscope.*
> *.pod *~ */*~
> rm -Rf .libs
> rm -f qemu-img-cmds.h
> @# May not be present in GENERATED_HEADERS
> diff --git a/Makefile.objs b/Makefile.objs
> index 5b288ba..ae541d1 100644
> --- a/Makefile.objs
> +++ b/Makefile.objs
> @@ -103,6 +103,7 @@ common-obj-y += disas/
> # FIXME: a few definitions from qapi-types.o/qapi-visit.o are needed
> # by libqemuutil.a. These should be moved to a separate .json schema.
> qga-obj-y = qga/ qapi-types.o qapi-visit.o
> +qga-vss-dll-obj-y = qga/
>
> vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS)
>
> @@ -114,6 +115,7 @@ nested-vars += \
> stub-obj-y \
> util-obj-y \
> qga-obj-y \
> + qga-vss-dll-obj-y \
> block-obj-y \
> common-obj-y
> dummy := $(call unnest-vars)
> diff --git a/configure b/configure
> index 48736b3..f1b7e4f 100755
> --- a/configure
> +++ b/configure
> @@ -3500,9 +3500,12 @@ if test "$softmmu" = yes ; then
> virtfs=no
> fi
> fi
> - if [ "$linux" = "yes" -o "$bsd" = "yes" -o "$solaris" = "yes" ] ; then
> + if [ "$linux" = "yes" -o "$bsd" = "yes" -o "$solaris" = "yes" -o
> "$mingw32" = "yes" ] ; then
> if [ "$guest_agent" = "yes" ]; then
> tools="qemu-ga\$(EXESUF) $tools"
> + if [ "$mingw32" = "yes" ]; then
> + tools="qga/vss-win32-provider/qga-provider.dll
> qga/vss-win32-provider/qga-provider.tlb $tools"
> + fi
It looks like this hasn't been updated to reflect the change in directory and
tlb/dll filenames from an earlier version.
Also, when CONFIG_QGA_VSS=n, we shouldn't make the dll/tlb dependencies of
$tools, else we'll get a build error.
The following changes seem to fix things for me:
- if [ "$mingw32" = "yes" ]; then
- tools="qga/vss-win32-provider/qga-provider.dll
qga/vss-win32-provider/qga-provider.tlb $tools"
+ if [ "$mingw32" = "yes" -a "$guest_agent_with_vss" = "yes" ]; then
+ tools="qga/vss-win32/qga-vss.dll qga/vss-win32/qga-vss.tlb $tools"
> fi
> fi
> fi
> diff --git a/qga/Makefile.objs b/qga/Makefile.objs
> index b8d7cd0..4891732 100644
> --- a/qga/Makefile.objs
> +++ b/qga/Makefile.objs
> @@ -3,3 +3,7 @@ qga-obj-$(CONFIG_POSIX) += commands-posix.o channel-posix.o
> qga-obj-$(CONFIG_WIN32) += commands-win32.o channel-win32.o service-win32.o
> qga-obj-y += qapi-generated/qga-qapi-types.o qapi-generated/qga-qapi-visit.o
> qga-obj-y += qapi-generated/qga-qmp-marshal.o
> +
> +ifeq ($(CONFIG_QGA_VSS),y)
> +qga-vss-dll-obj-y += vss-win32/
> +endif
Small nit, but I think we generally prefer this form in qemu:
qga-vss-dll-obj-$(CONFIG_QGA_VSS) += vss-win32
Will review the rest file by file.
> diff --git a/qga/vss-win32/Makefile.objs b/qga/vss-win32/Makefile.objs
> new file mode 100644
> index 0000000..4dad964
> --- /dev/null
> +++ b/qga/vss-win32/Makefile.objs
> @@ -0,0 +1,23 @@
> +# rules to build qga-vss.dll
> +
> +qga-vss-dll-obj-y += requester.o provider.o install.o
> +
> +obj-qga-vss-dll-obj-y = $(addprefix $(obj)/, $(qga-vss-dll-obj-y))
> +$(obj-qga-vss-dll-obj-y): QEMU_CXXFLAGS = $(filter-out -Wstrict-prototypes
> -Wmissing-prototypes -Wnested-externs -Wold-style-declaration
> -Wold-style-definition -Wredundant-decls -fstack-protector-all,
> $(QEMU_CFLAGS)) -Wno-unknown-pragmas -Wno-delete-non-virtual-dtor
> +
> +$(obj)/qga-vss.dll: LDFLAGS = -shared
> -Wl,--add-stdcall-alias,--enable-stdcall-fixup -lole32 -loleaut32 -lshlwapi
> -luuid -static
> +$(obj)/qga-vss.dll: $(obj-qga-vss-dll-obj-y) $(SRC_PATH)/$(obj)/qga-vss.def
> + $(call quiet-command,$(CXX) -o $@ $(qga-vss-dll-obj-y)
> $(SRC_PATH)/qga/vss-win32/qga-vss.def $(CXXFLAGS) $(LDFLAGS)," LINK
> $(TARGET_DIR)$@")
> +
> +
> +# rules to build qga-provider.tlb
> +# Currently, only native build is supported because building .tlb
> +# (TypeLibrary) from .idl requires WindowsSDK and MIDL (and cl.exe in VC++).
> +MIDL=$(WIN_SDK)/Bin/midl
> +
> +$(obj)/qga-vss.tlb: $(SRC_PATH)/$(obj)/qga-vss.idl
> +ifeq ($(wildcard $(SRC_PATH)/$(obj)/qga-vss.tlb),)
> + $(call quiet-command,$(MIDL) -tlb $@ -I $(WIN_SDK)/Include $<," MIDL
> $(TARGET_DIR)$@")
> +else
> + $(call quiet-command,cp $(dir $<)qga-vss.tlb $@, " COPY
> $(TARGET_DIR)$@")
> +endif
> diff --git a/qga/vss-win32/install.cpp b/qga/vss-win32/install.cpp
> new file mode 100644
> index 0000000..37731a7
> --- /dev/null
> +++ b/qga/vss-win32/install.cpp
> @@ -0,0 +1,458 @@
> +/*
> + * QEMU Guest Agent win32 VSS Provider installer
> + *
> + * Copyright Hitachi Data Systems Corp. 2013
> + *
> + * Authors:
> + * Tomoki Sekiyama <address@hidden>
> + *
> + * 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 <string.h>
> +
> +#include "vss-common.h"
> +#include "inc/win2003/vscoordint.h"
> +
> +#include <comadmin.h>
> +#include <wbemidl.h>
> +#include <comdef.h>
> +#include <comutil.h>
> +
> +extern HINSTANCE g_hinstDll;
> +
> +const GUID CLSID_COMAdminCatalog = { 0xF618C514, 0xDFB8, 0x11d1,
> + {0xA2, 0xCF, 0x00, 0x80, 0x5F, 0xC7, 0x92, 0x35} };
> +const GUID IID_ICOMAdminCatalog = { 0xDD662187, 0xDFC2, 0x11d1,
> + {0xA2, 0xCF, 0x00, 0x80, 0x5F, 0xC7, 0x92, 0x35} };
> +const GUID CLSID_WbemLocator = { 0x4590f811, 0x1d3a, 0x11d0,
> + {0x89, 0x1f, 0x00, 0xaa, 0x00, 0x4b, 0x2e, 0x24} };
> +const GUID IID_IWbemLocator = { 0xdc12a687, 0x737f, 0x11cf,
> + {0x88, 0x4d, 0x00, 0xaa, 0x00, 0x4b, 0x2e, 0x24} };
> +
> +void errmsg(DWORD err, const char *text)
> +{
> + /*
> + * `text' contains function call statement when errmsg is called via
> chk().
> + * To make error message more readable, we cut off the text after '('.
> + * If text doesn't contains '(', negative precision is given, which is
> + * treated as though it were missing.
> + */
> + char *msg = NULL, *nul = strchr(text, '(');
> + int len = nul ? nul - text : -1;
> +
> + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
> + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
> + NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
> + (char *)&msg, 0, NULL);
> + fprintf(stderr, "%.*s. (Error: %lx) %s\n", len, text, err, msg);
> + LocalFree(msg);
> +}
> +
> +static void errmsg_dialog(DWORD err, const char *text, const char *opt = "")
> +{
> + char *msg, buf[512];
> +
> + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
> + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
> + NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
> + (char *)&msg, 0, NULL);
> + snprintf(buf, sizeof(buf), "%s%s. (Error: %lx) %s", text, opt, err, msg);
> + MessageBox(NULL, buf, "Error from " QGA_PROVIDER_NAME,
> MB_OK|MB_ICONERROR);
> + LocalFree(msg);
> +}
> +
> +#define _chk(hr, status, msg, err_label) \
> + do { \
> + hr = (status); \
> + if (FAILED(hr)) { \
> + errmsg(hr, msg); \
> + goto err_label; \
> + } \
> + } while (0)
> +
> +#define chk(status) _chk(hr, status, "Failed to " #status, out)
> +
> +void __stdcall _com_issue_error(HRESULT hr)
> +{
> + errmsg(hr, "Unexpected error in COM");
> +}
> +
> +template<class T>
> +HRESULT put_Value(ICatalogObject *pObj, LPCWSTR name, T val)
> +{
> + return pObj->put_Value(_bstr_t(name), _variant_t(val));
> +}
> +
> +/* Lookup Administrators group name from winmgmt */
> +static HRESULT GetAdminName(_bstr_t *name)
> +{
> + HRESULT hr;
> + COMPointer<IWbemLocator> pLoc;
> + COMPointer<IWbemServices> pSvc;
> + COMPointer<IEnumWbemClassObject> pEnum;
> + COMPointer<IWbemClassObject> pWobj;
> + ULONG returned;
> + _variant_t var;
> +
> + chk(CoCreateInstance(CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER,
> + IID_IWbemLocator, (LPVOID *)pLoc.replace()));
> + chk(pLoc->ConnectServer(_bstr_t(L"ROOT\\CIMV2"), NULL, NULL, NULL,
> + 0, 0, 0, pSvc.replace()));
> + chk(CoSetProxyBlanket(pSvc, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE,
> + NULL, RPC_C_AUTHN_LEVEL_CALL,
> + RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE));
> + chk(pSvc->ExecQuery(_bstr_t(L"WQL"),
> + _bstr_t(L"select * from Win32_Account where "
> + "SID='S-1-5-32-544' and localAccount=TRUE"),
> + WBEM_FLAG_RETURN_IMMEDIATELY |
> WBEM_FLAG_FORWARD_ONLY,
> + NULL, pEnum.replace()));
> + if (!pEnum) {
> + hr = E_FAIL;
> + errmsg(hr, "Failed to query for Administrators");
> + goto out;
> + }
> + chk(pEnum->Next(WBEM_INFINITE, 1, pWobj.replace(), &returned));
> + if (returned == 0) {
> + hr = E_FAIL;
> + errmsg(hr, "No Administrators found");
> + goto out;
> + }
> +
> + chk(pWobj->Get(_bstr_t(L"Name"), 0, &var, 0, 0));
> + try {
> + *name = var;
> + } catch(...) {
> + hr = E_FAIL;
> + errmsg(hr, "Failed to get name of Administrators");
> + goto out;
> + }
> +
> +out:
> + return hr;
> +}
> +
> +/* Find and iterate QGA VSS provider in COM+ Application Catalog */
> +static HRESULT QGAProviderFind(
> + HRESULT (*found)(ICatalogCollection *, int, void *), void *arg)
> +{
> + HRESULT hr;
> + COMInitializer initializer;
> + COMPointer<IUnknown> pUnknown;
> + COMPointer<ICOMAdminCatalog> pCatalog;
> + COMPointer<ICatalogCollection> pColl;
> + COMPointer<ICatalogObject> pObj;
> + _variant_t var;
> + long i, n;
> +
> + chk(CoCreateInstance(CLSID_COMAdminCatalog, NULL, CLSCTX_INPROC_SERVER,
> + IID_IUnknown, (void **)pUnknown.replace()));
> + chk(pUnknown->QueryInterface(IID_ICOMAdminCatalog,
> + (void **)pCatalog.replace()));
> + chk(pCatalog->GetCollection(_bstr_t(L"Applications"),
> + (IDispatch **)pColl.replace()));
> + chk(pColl->Populate());
> +
> + chk(pColl->get_Count(&n));
> + for (i = n - 1; i >= 0; i--) {
> + chk(pColl->get_Item(i, (IDispatch **)pObj.replace()));
> + chk(pObj->get_Value(_bstr_t(L"Name"), &var));
> + if (var == _variant_t(QGA_PROVIDER_LNAME)) {
> + if (FAILED(found(pColl, i, arg))) {
> + goto out;
> + }
> + }
> + }
> + chk(pColl->SaveChanges(&n));
> +
> +out:
> + return hr;
> +}
> +
> +/* Count QGA VSS provider in COM+ Application Catalog */
> +static HRESULT QGAProviderCount(ICatalogCollection *coll, int i, void *arg)
> +{
> + (*(int *)arg)++;
> + return S_OK;
> +}
> +
> +/* Remove QGA VSS provider from COM+ Application Catalog Collection */
> +static HRESULT QGAProviderRemove(ICatalogCollection *coll, int i, void *arg)
> +{
> + HRESULT hr;
> +
> + fprintf(stderr, "Removing COM+ Application: %s\n", QGA_PROVIDER_NAME);
> + chk(coll->Remove(i));
> +out:
> + return hr;
> +}
> +
> +/* Unregister this module from COM+ Applications Catalog */
> +STDAPI COMUnregister(void)
> +{
> + HRESULT hr;
> +
> + DllUnregisterServer();
> + chk(QGAProviderFind(QGAProviderRemove, NULL));
> +out:
> + return hr;
> +}
> +
> +/* Register this module to COM+ Applications Catalog */
> +STDAPI COMRegister(void)
> +{
> + HRESULT hr;
> + COMInitializer initializer;
> + COMPointer<IUnknown> pUnknown;
> + COMPointer<ICOMAdminCatalog> pCatalog;
> + COMPointer<ICatalogCollection> pApps, pRoles, pUsersInRole;
> + COMPointer<ICatalogObject> pObj;
> + long n;
> + _bstr_t name;
> + _variant_t key;
> + CHAR dllPath[MAX_PATH], tlbPath[MAX_PATH];
> + bool unregisterOnFailure = false;
> + int count = 0;
> +
> + if (!g_hinstDll) {
> + errmsg(E_FAIL, "Failed to initialize DLL");
> + return E_FAIL;
> + }
> +
> + chk(QGAProviderFind(QGAProviderCount, (void *)&count));
> + if (count) {
> + errmsg(E_ABORT, "QGA VSS Provider is already installed");
> + return E_ABORT;
> + }
> +
> + chk(CoCreateInstance(CLSID_COMAdminCatalog, NULL, CLSCTX_INPROC_SERVER,
> + IID_IUnknown, (void **)pUnknown.replace()));
> + chk(pUnknown->QueryInterface(IID_ICOMAdminCatalog,
> + (void **)pCatalog.replace()));
> +
> + /* Install COM+ Component */
> +
> + chk(pCatalog->GetCollection(_bstr_t(L"Applications"),
> + (IDispatch **)pApps.replace()));
> + chk(pApps->Populate());
> + chk(pApps->Add((IDispatch **)&pObj));
> + chk(put_Value(pObj, L"Name", QGA_PROVIDER_LNAME));
> + chk(put_Value(pObj, L"Description", QGA_PROVIDER_LNAME));
> + chk(put_Value(pObj, L"ApplicationAccessChecksEnabled", true));
> + chk(put_Value(pObj, L"Authentication", short(6)));
> + chk(put_Value(pObj, L"AuthenticationCapability", short(2)));
> + chk(put_Value(pObj, L"ImpersonationLevel", short(2)));
> + chk(pApps->SaveChanges(&n));
> +
> + /* The app should be deleted if something fails after SaveChanges */
> + unregisterOnFailure = true;
> +
> + chk(pObj->get_Key(&key));
> +
> + if (!GetModuleFileName(g_hinstDll, dllPath, sizeof(dllPath))) {
> + hr = HRESULT_FROM_WIN32(GetLastError());
> + errmsg(hr, "GetModuleFileName failed");
> + goto out;
> + }
> + n = strlen(dllPath);
> + if (n < 3) {
> + hr = E_FAIL;
> + errmsg(hr, "Failed to lookup dll");
> + goto out;
> + }
> + strcpy(tlbPath, dllPath);
> + strcpy(tlbPath+n-3, "tlb");
> + fprintf(stderr, "Registering " QGA_PROVIDER_NAME ":\n");
> + fprintf(stderr, " %s\n", dllPath);
> + fprintf(stderr, " %s\n", tlbPath);
> + if (!PathFileExists(tlbPath)) {
> + hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
> + errmsg(hr, "Failed to lookup tlb");
> + goto out;
> + }
> +
> + chk(pCatalog->InstallComponent(_bstr_t(QGA_PROVIDER_LNAME),
> + _bstr_t(dllPath), _bstr_t(tlbPath),
> + _bstr_t("")));
> +
> + /* Setup roles of the applicaion */
> +
> + chk(pApps->GetCollection(_bstr_t(L"Roles"), key,
> + (IDispatch **)pRoles.replace()));
> + chk(pRoles->Populate());
> + chk(pRoles->Add((IDispatch **)pObj.replace()));
> + chk(put_Value(pObj, L"Name", L"Administrators"));
> + chk(put_Value(pObj, L"Description", L"Administrators group"));
> + chk(pRoles->SaveChanges(&n));
> + chk(pObj->get_Key(&key));
> +
> + /* Setup users in the role */
> +
> + chk(pRoles->GetCollection(_bstr_t(L"UsersInRole"), key,
> + (IDispatch **)pUsersInRole.replace()));
> + chk(pUsersInRole->Populate());
> +
> + chk(pUsersInRole->Add((IDispatch **)pObj.replace()));
> + chk(GetAdminName(&name));
> + chk(put_Value(pObj, L"User", _bstr_t(".\\") + name));
> +
> + chk(pUsersInRole->Add((IDispatch **)pObj.replace()));
> + chk(put_Value(pObj, L"User", L"SYSTEM"));
> + chk(pUsersInRole->SaveChanges(&n));
> +
> +out:
> + if (unregisterOnFailure && FAILED(hr)) {
> + COMUnregister();
> + }
> +
> + return hr;
> +}
> +
> +
> +static BOOL CreateRegistryKey(LPCTSTR key, LPCTSTR value, LPCTSTR data)
> +{
> + HKEY hKey;
> + LONG ret;
> + DWORD size;
> +
> + ret = RegCreateKeyEx(HKEY_CLASSES_ROOT, key, 0, NULL,
> + REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, NULL);
> + if (ret != ERROR_SUCCESS) {
> + goto out;
> + }
> +
> + if (data != NULL) {
> + size = strlen(data) + 1;
> + } else {
> + size = 0;
> + }
> +
> + ret = RegSetValueEx(hKey, value, 0, REG_SZ, (LPBYTE)data, size);
> + RegCloseKey(hKey);
> +
> +out:
> + if (ret != ERROR_SUCCESS) {
> + /* As we cannot printf within DllRegisterServer(), show a dialog. */
> + errmsg_dialog(ret, "Cannot add registry", key);
> + return FALSE;
> + }
> + return TRUE;
> +}
> +
> +/* Register this dll as a VSS provider */
> +STDAPI DllRegisterServer(void)
> +{
> + COMInitializer initializer;
> + COMPointer<IVssAdmin> pVssAdmin;
> + HRESULT hr = E_FAIL;
> + char dllPath[MAX_PATH];
> + char key[256];
> +
> + if (!g_hinstDll) {
> + errmsg_dialog(hr, "Module instance is not available");
> + goto out;
> + }
> +
> + /* Add this module to registery */
> +
> + sprintf(key, "CLSID\\%s", g_szClsid);
> + if (!CreateRegistryKey(key, NULL, g_szClsid)) {
> + goto out;
> + }
> +
> + if (!GetModuleFileName(g_hinstDll, dllPath, sizeof(dllPath))) {
> + errmsg_dialog(GetLastError(), "GetModuleFileName failed");
> + goto out;
> + }
> +
> + sprintf(key, "CLSID\\%s\\InprocServer32", g_szClsid);
> + if (!CreateRegistryKey(key, NULL, dllPath)) {
> + goto out;
> + }
> +
> + if (!CreateRegistryKey(key, "ThreadingModel", "Apartment")) {
> + goto out;
> + }
> +
> + sprintf(key, "CLSID\\%s\\ProgID", g_szClsid);
> + if (!CreateRegistryKey(key, NULL, g_szProgid)) {
> + goto out;
> + }
> +
> + if (!CreateRegistryKey(g_szProgid, NULL, QGA_PROVIDER_NAME)) {
> + goto out;
> + }
> +
> + sprintf(key, "%s\\CLSID", g_szProgid);
> + if (!CreateRegistryKey(key, NULL, g_szClsid)) {
> + goto out;
> + }
> +
> + hr = CoCreateInstance(CLSID_VSSCoordinator, NULL, CLSCTX_ALL,
> + IID_IVssAdmin, (void **)pVssAdmin.replace());
> + if (FAILED(hr)) {
> + errmsg_dialog(hr, "CoCreateInstance(VSSCoordinator) failed");
> + goto out;
> + }
> +
> + hr = pVssAdmin->RegisterProvider(g_gProviderId, CLSID_QGAVSSProvider,
> + const_cast<WCHAR*>(QGA_PROVIDER_LNAME),
> + VSS_PROV_SOFTWARE,
> +
> const_cast<WCHAR*>(QGA_PROVIDER_VERSION),
> + g_gProviderVersion);
> + if (FAILED(hr)) {
> + errmsg_dialog(hr, "RegisterProvider failed");
> + }
> +
> +out:
> + if (FAILED(hr)) {
> + DllUnregisterServer();
> + }
> +
> + return hr;
> +}
> +
> +/* Unregister this VSS hardware provider from the system */
> +STDAPI DllUnregisterServer(void)
> +{
> + TCHAR key[256];
> + COMInitializer initializer;
> + COMPointer<IVssAdmin> pVssAdmin;
> +
> + HRESULT hr = CoCreateInstance(CLSID_VSSCoordinator,
> + NULL, CLSCTX_ALL, IID_IVssAdmin,
> + (void **)pVssAdmin.replace());
> + if (SUCCEEDED(hr)) {
> + hr = pVssAdmin->UnregisterProvider(g_gProviderId);
> + } else {
> + errmsg(hr, "CoCreateInstance(VSSCoordinator) failed");
> + }
> +
> + sprintf(key, "CLSID\\%s", g_szClsid);
> + SHDeleteKey(HKEY_CLASSES_ROOT, key);
> + SHDeleteKey(HKEY_CLASSES_ROOT, g_szProgid);
> +
> + return S_OK; /* Uninstall should never fail */
> +}
> +
> +
> +/* Support function to convert ASCII string into BSTR (used in _bstr_t) */
> +namespace _com_util
> +{
> + BSTR WINAPI ConvertStringToBSTR(const char *ascii) {
> + int len = strlen(ascii);
> + BSTR bstr = SysAllocStringLen(NULL, len);
> +
> + if (!bstr) {
> + return NULL;
> + }
> +
> + if (mbstowcs(bstr, ascii, len) == (size_t)-1) {
> + fprintf(stderr, "Failed to convert string '%s' into BSTR",
> ascii);
> + bstr[0] = 0;
> + }
> + return bstr;
> + }
> +}
> diff --git a/qga/vss-win32/provider.cpp b/qga/vss-win32/provider.cpp
> new file mode 100644
> index 0000000..e39526a
> --- /dev/null
> +++ b/qga/vss-win32/provider.cpp
> @@ -0,0 +1,513 @@
> +/*
> + * QEMU Guest Agent win32 VSS Provider implementations
> + *
> + * Copyright Hitachi Data Systems Corp. 2013
> + *
> + * Authors:
> + * Tomoki Sekiyama <address@hidden>
> + *
> + * 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;
> +
> + 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;
> + }
> +
> + /* 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) {
> + hr = E_ABORT;
> + }
> +
> + CloseHandle(hEventThaw);
> + CloseHandle(hEventFrozen);
> + 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;
> +}
> diff --git a/qga/vss-win32/qga-vss.def b/qga/vss-win32/qga-vss.def
> new file mode 100644
> index 0000000..927782c
> --- /dev/null
> +++ b/qga/vss-win32/qga-vss.def
> @@ -0,0 +1,13 @@
> +LIBRARY "QGA-PROVIDER.DLL"
> +
> +EXPORTS
> + COMRegister PRIVATE
> + COMUnregister PRIVATE
> + DllCanUnloadNow PRIVATE
> + DllGetClassObject PRIVATE
> + DllRegisterServer PRIVATE
> + DllUnregisterServer PRIVATE
> + requester_init PRIVATE
> + requester_deinit PRIVATE
> + requester_freeze PRIVATE
> + requester_thaw PRIVATE
> diff --git a/qga/vss-win32/qga-vss.idl b/qga/vss-win32/qga-vss.idl
> new file mode 100644
> index 0000000..17abca0
> --- /dev/null
> +++ b/qga/vss-win32/qga-vss.idl
> @@ -0,0 +1,20 @@
> +import "oaidl.idl";
> +import "ocidl.idl";
> +
> +[
> + uuid(103B8142-6CE5-48A7-BDE1-794D3192FCF1),
> + version(1.0),
> + helpstring("QGAVSSProvider Type Library")
> +]
> +library QGAVSSHWProviderLib
> +{
> + importlib("stdole2.tlb");
> + [
> + uuid(6E6A3492-8D4D-440C-9619-5E5D0CC31CA8),
> + helpstring("QGAVSSProvider Class")
> + ]
> + coclass QGAVSSHWProvider
> + {
> + [default] interface IUnknown;
> + };
> +};
> diff --git a/qga/vss-win32/qga-vss.tlb b/qga/vss-win32/qga-vss.tlb
> new file mode 100644
> index
> 0000000000000000000000000000000000000000..226452a1861371ffe0cad1019cf90fdfdcd5ef49
> GIT binary patch
> literal 1528
> zcmeYbb_-!*U}OLRP8Kl5;address@hidden<WGF*9|A9aP$W{R21|SCUVfs9Pj1;IC
> zKahR`)X0Ox{{ZC6An~sN`2tA%H9-9hNPHcj{0byK4>OPh6a(1_GM|T)fx!kz-UG<D
> zK;nbc0l9GX===tt`Vb`fD?q*q5+7YXI$u?Zf#C;G4-9~uhYKVCC4f!`hZ{(Z0*HVD
> zkhwswGqAt}ki;address@hidden|5P#hGW*vvKniaRx03p~wP?y>h_rLW<address@hidden
> address@hidden<^MU>address@hidden
> z2f!qQ8-xepfXD+7rW+{SKz4&@7#qX~^1#sn3O|tFK>%ciE<<riN`6kNkzPqoQh0bc
> zNbM*XJ|address@hidden<address@hidden<fE
> zfvHyrsVqoU0O~4AEUE;iEfI8i=bXgi;_z?|20Ng!&PAz-C8;S2NtFt|o-RHLWvNBQ
> znfZAN=6VJOdIqMZrV5EA3T{Q23NES13Py$shQ?OLW>&_Q3PuKoMqI)S5zj9Ngog_=
> gXfrXehlhjmFbIJB4$8MKU>*YlD1Z9^F{m5{03Vre%>V!Z
>
> literal 0
> HcmV?d00001
>
> diff --git a/qga/vss-win32/requester.cpp b/qga/vss-win32/requester.cpp
> new file mode 100644
> index 0000000..1b91ca5
> --- /dev/null
> +++ b/qga/vss-win32/requester.cpp
> @@ -0,0 +1,487 @@
> +/*
> + * QEMU Guest Agent win32 VSS Requester implementations
> + *
> + * Copyright Hitachi Data Systems Corp. 2013
> + *
> + * Authors:
> + * Tomoki Sekiyama <address@hidden>
> + *
> + * 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 "requester.h"
> +#include "assert.h"
> +#include "inc/win2003/vswriter.h"
> +#include "inc/win2003/vsbackup.h"
> +
> +/* Max wait time for frozen event (VSS can only hold writes for 10 seconds)
> */
> +#define VSS_TIMEOUT_FREEZE_MSEC 10000
> +
> +/* Call QueryStatus every 10 ms while waiting for frozen event */
> +#define VSS_TIMEOUT_EVENT_MSEC 10
> +
> +#define err_set(e, err, fmt, ...) \
> + ((e)->error_set((e)->errp, err, (e)->err_class, fmt, ## __VA_ARGS__))
> +#define err_is_set(e) ((e)->errp && *(e)->errp)
> +
> +
> +/* Handle to VSSAPI.DLL */
> +static HMODULE hLib;
> +
> +/* Functions in VSSAPI.DLL */
> +typedef HRESULT(STDAPICALLTYPE * t_CreateVssBackupComponents)(
> + OUT IVssBackupComponents**);
> +typedef void(APIENTRY * t_VssFreeSnapshotProperties)(IN VSS_SNAPSHOT_PROP*);
> +static t_CreateVssBackupComponents pCreateVssBackupComponents;
> +static t_VssFreeSnapshotProperties pVssFreeSnapshotProperties;
> +
> +/* Variables used while applications and filesystes are frozen by VSS */
> +static struct QGAVSSContext {
> + IVssBackupComponents *pVssbc; /* VSS requester interface */
> + IVssAsync *pAsyncSnapshot; /* async info of VSS snapshot operation */
> + HANDLE hEventFrozen; /* frozen event from qga-provider.dll */
> + HANDLE hEventThaw; /* thaw event to qga-provider.dll */
> + int cFrozenVols; /* number of frozen volumes */
> +} vss_ctx;
> +
> +STDAPI requester_init(void)
> +{
> + vss_ctx.hEventFrozen = vss_ctx.hEventThaw = INVALID_HANDLE_VALUE;
> +
> + COMInitializer initializer; /* to call CoInitializeSecurity */
> + HRESULT hr = CoInitializeSecurity(
> + NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
> + RPC_C_IMP_LEVEL_IDENTIFY, NULL, EOAC_NONE, NULL);
> + if (FAILED(hr)) {
> + fprintf(stderr, "failed to CoInitializeSecurity (error %lx)\n", hr);
> + return hr;
> + }
> +
> + hLib = LoadLibraryA("VSSAPI.DLL");
> + if (!hLib) {
> + fprintf(stderr, "failed to load VSSAPI.DLL\n");
> + return HRESULT_FROM_WIN32(GetLastError());
> + }
> +
> + pCreateVssBackupComponents = (t_CreateVssBackupComponents)
> + GetProcAddress(hLib,
> +#ifdef _WIN64 /* 64bit environment */
> + "?CreateVssBackupComponents@@YAJPEAPEAVIVssBackupComponents@@@Z"
> +#else /* 32bit environment */
> + "?CreateVssBackupComponents@@YGJPAPAVIVssBackupComponents@@@Z"
> +#endif
> + );
> + if (!pCreateVssBackupComponents) {
> + fprintf(stderr, "failed to get proc address from VSSAPI.DLL\n");
> + return HRESULT_FROM_WIN32(GetLastError());
> + }
> +
> + pVssFreeSnapshotProperties = (t_VssFreeSnapshotProperties)
> + GetProcAddress(hLib, "VssFreeSnapshotProperties");
> + if (!pVssFreeSnapshotProperties) {
> + fprintf(stderr, "failed to get proc address from VSSAPI.DLL\n");
> + return HRESULT_FROM_WIN32(GetLastError());
> + }
> +
> + return S_OK;
> +}
> +
> +static void requester_cleanup(void)
> +{
> + if (vss_ctx.hEventFrozen != INVALID_HANDLE_VALUE) {
> + CloseHandle(vss_ctx.hEventFrozen);
> + vss_ctx.hEventFrozen = INVALID_HANDLE_VALUE;
> + }
> + if (vss_ctx.hEventThaw != INVALID_HANDLE_VALUE) {
> + CloseHandle(vss_ctx.hEventThaw);
> + vss_ctx.hEventThaw = INVALID_HANDLE_VALUE;
> + }
> + if (vss_ctx.pAsyncSnapshot) {
> + vss_ctx.pAsyncSnapshot->Release();
> + vss_ctx.pAsyncSnapshot = NULL;
> + }
> + if (vss_ctx.pVssbc) {
> + vss_ctx.pVssbc->Release();
> + vss_ctx.pVssbc = NULL;
> + }
> + vss_ctx.cFrozenVols = 0;
> +}
> +
> +STDAPI requester_deinit(void)
> +{
> + requester_cleanup();
> +
> + pCreateVssBackupComponents = NULL;
> + pVssFreeSnapshotProperties = NULL;
> + if (hLib) {
> + FreeLibrary(hLib);
> + hLib = NULL;
> + }
> +
> + return S_OK;
> +}
> +
> +static HRESULT WaitForAsync(IVssAsync *pAsync)
> +{
> + HRESULT ret, hr;
> +
> + do {
> + hr = pAsync->Wait();
> + if (FAILED(hr)) {
> + ret = hr;
> + break;
> + }
> + hr = pAsync->QueryStatus(&ret, NULL);
> + if (FAILED(hr)) {
> + ret = hr;
> + break;
> + }
> + } while (ret == VSS_S_ASYNC_PENDING);
> +
> + return ret;
> +}
> +
> +static void AddComponents(ErrorSet *errset)
> +{
> + unsigned int cWriters, i;
> + VSS_ID id, idInstance, idWriter;
> + BSTR bstrWriterName = NULL;
> + VSS_USAGE_TYPE usage;
> + VSS_SOURCE_TYPE source;
> + unsigned int cComponents, c1, c2, j;
> + COMPointer<IVssExamineWriterMetadata> pMetadata;
> + COMPointer<IVssWMComponent> pComponent;
> + PVSSCOMPONENTINFO info;
> + HRESULT hr;
> +
> + hr = vss_ctx.pVssbc->GetWriterMetadataCount(&cWriters);
> + if (FAILED(hr)) {
> + err_set(errset, hr, "failed to get writer metadata count");
> + goto out;
> + }
> +
> + for (i = 0; i < cWriters; i++) {
> + hr = vss_ctx.pVssbc->GetWriterMetadata(i, &id, pMetadata.replace());
> + if (FAILED(hr)) {
> + err_set(errset, hr, "failed to get writer metadata of %d/%d",
> + i, cWriters);
> + goto out;
> + }
> +
> + hr = pMetadata->GetIdentity(&idInstance, &idWriter,
> + &bstrWriterName, &usage, &source);
> + if (FAILED(hr)) {
> + err_set(errset, hr, "failed to get identity of writer %d/%d",
> + i, cWriters);
> + goto out;
> + }
> +
> + hr = pMetadata->GetFileCounts(&c1, &c2, &cComponents);
> + if (FAILED(hr)) {
> + err_set(errset, hr, "failed to get file counts of %S",
> + bstrWriterName);
> + goto out;
> + }
> +
> + for (j = 0; j < cComponents; j++) {
> + hr = pMetadata->GetComponent(j, pComponent.replace());
> + if (FAILED(hr)) {
> + err_set(errset, hr,
> + "failed to get component %d/%d of %S",
> + j, cComponents, bstrWriterName);
> + goto out;
> + }
> +
> + hr = pComponent->GetComponentInfo(&info);
> + if (FAILED(hr)) {
> + err_set(errset, hr,
> + "failed to get component info %d/%d of %S",
> + j, cComponents, bstrWriterName);
> + goto out;
> + }
> +
> + if (info->bSelectable) {
> + hr = vss_ctx.pVssbc->AddComponent(idInstance, idWriter,
> + info->type,
> + info->bstrLogicalPath,
> + info->bstrComponentName);
> + if (FAILED(hr)) {
> + err_set(errset, hr, "failed to add component %S(%S)",
> + info->bstrComponentName,
> bstrWriterName);
> + goto out;
> + }
> + }
> + SysFreeString(bstrWriterName);
> + bstrWriterName = NULL;
> + pComponent->FreeComponentInfo(info);
> + info = NULL;
> + }
> + }
> +out:
> + if (bstrWriterName) {
> + SysFreeString(bstrWriterName);
> + }
> + if (pComponent && info) {
> + pComponent->FreeComponentInfo(info);
> + }
> +}
> +
> +void requester_freeze(int *num_vols, ErrorSet *errset)
> +{
> + COMPointer<IVssAsync> pAsync;
> + HANDLE volume;
> + HRESULT hr;
> + LONG ctx;
> + GUID guidSnapshotSet = GUID_NULL;
> + SECURITY_DESCRIPTOR sd;
> + SECURITY_ATTRIBUTES sa;
> + WCHAR short_volume_name[64], *display_name = short_volume_name;
> + DWORD wait_status;
> + int num_fixed_drives = 0, i;
> +
> + if (vss_ctx.pVssbc) { /* already frozen */
> + *num_vols = 0;
> + return;
> + }
> +
> + CoInitialize(NULL);
> +
> + assert(pCreateVssBackupComponents != NULL);
> + hr = pCreateVssBackupComponents(&vss_ctx.pVssbc);
> + if (FAILED(hr)) {
> + err_set(errset, hr, "failed to create VSS backup components");
> + goto out;
> + }
> +
> + hr = vss_ctx.pVssbc->InitializeForBackup();
> + if (FAILED(hr)) {
> + err_set(errset, hr, "failed to initialize for backup");
> + goto out;
> + }
> +
> + hr = vss_ctx.pVssbc->SetBackupState(true, true, VSS_BT_FULL, false);
> + if (FAILED(hr)) {
> + err_set(errset, hr, "failed to set backup state");
> + goto out;
> + }
> +
> + /*
> + * Currently writable snapshots are not supported.
> + * To prevent the final commit (which requires to write to snapshots),
> + * ATTR_NO_AUTORECOVERY and ATTR_TRANSPORTABLE are specified here.
> + */
> + ctx = VSS_CTX_APP_ROLLBACK | VSS_VOLSNAP_ATTR_TRANSPORTABLE |
> + VSS_VOLSNAP_ATTR_NO_AUTORECOVERY | VSS_VOLSNAP_ATTR_TXF_RECOVERY;
> + hr = vss_ctx.pVssbc->SetContext(ctx);
> + if (hr == (HRESULT)VSS_E_UNSUPPORTED_CONTEXT) {
> + /* Non-server version of Windows doesn't support ATTR_TRANSPORTABLE
> */
> + ctx &= ~VSS_VOLSNAP_ATTR_TRANSPORTABLE;
> + hr = vss_ctx.pVssbc->SetContext(ctx);
> + }
> + if (FAILED(hr)) {
> + err_set(errset, hr, "failed to set backup context");
> + goto out;
> + }
> +
> + hr = vss_ctx.pVssbc->GatherWriterMetadata(pAsync.replace());
> + if (SUCCEEDED(hr)) {
> + hr = WaitForAsync(pAsync);
> + }
> + if (FAILED(hr)) {
> + err_set(errset, hr, "failed to gather writer metadata");
> + goto out;
> + }
> +
> + AddComponents(errset);
> + if (err_is_set(errset)) {
> + goto out;
> + }
> +
> + hr = vss_ctx.pVssbc->StartSnapshotSet(&guidSnapshotSet);
> + if (FAILED(hr)) {
> + err_set(errset, hr, "failed to start snapshot set");
> + goto out;
> + }
> +
> + volume = FindFirstVolumeW(short_volume_name, sizeof(short_volume_name));
> + if (volume == INVALID_HANDLE_VALUE) {
> + err_set(errset, hr, "failed to find first volume");
> + goto out;
> + }
> + for (;;) {
> + if (GetDriveTypeW(short_volume_name) == DRIVE_FIXED) {
> + VSS_ID pid;
> + hr = vss_ctx.pVssbc->AddToSnapshotSet(short_volume_name,
> + g_gProviderId, &pid);
> + if (FAILED(hr)) {
> + WCHAR volume_path_name[PATH_MAX];
> + if (GetVolumePathNamesForVolumeNameW(
> + short_volume_name, volume_path_name,
> + sizeof(volume_path_name), NULL) &&
> *volume_path_name) {
> + display_name = volume_path_name;
> + }
> + err_set(errset, hr, "failed to add %S to snapshot set",
> + display_name);
> + FindVolumeClose(volume);
> + goto out;
> + }
> + num_fixed_drives++;
> + }
> + if (!FindNextVolumeW(volume, short_volume_name,
> + sizeof(short_volume_name))) {
> + FindVolumeClose(volume);
> + break;
> + }
> + }
> +
> + if (num_fixed_drives == 0) {
> + goto out; /* If there is no fixed drive, just exit. */
> + }
> +
> + hr = vss_ctx.pVssbc->PrepareForBackup(pAsync.replace());
> + if (SUCCEEDED(hr)) {
> + hr = WaitForAsync(pAsync);
> + }
> + if (FAILED(hr)) {
> + err_set(errset, hr, "failed to prepare for backup");
> + goto out;
> + }
> +
> + hr = vss_ctx.pVssbc->GatherWriterStatus(pAsync.replace());
> + if (SUCCEEDED(hr)) {
> + hr = WaitForAsync(pAsync);
> + }
> + if (FAILED(hr)) {
> + err_set(errset, hr, "failed to gather writer status");
> + goto out;
> + }
> +
> + /* Allow unrestricted access to events */
> + InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
> + SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE);
> + sa.nLength = sizeof(sa);
> + sa.lpSecurityDescriptor = &sd;
> + sa.bInheritHandle = FALSE;
> +
> + vss_ctx.hEventFrozen = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_FROZEN);
> + if (vss_ctx.hEventFrozen == INVALID_HANDLE_VALUE) {
> + err_set(errset, GetLastError(), "failed to create event %s",
> + EVENT_NAME_FROZEN);
> + goto out;
> + }
> + vss_ctx.hEventThaw = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_THAW);
> + if (vss_ctx.hEventThaw == INVALID_HANDLE_VALUE) {
> + err_set(errset, GetLastError(), "failed to create event %s",
> + EVENT_NAME_THAW);
> + goto out;
> + }
> +
> + /*
> + * Start VSS quiescing operations.
> + * CQGAVssProvider::CommitSnapshots will kick vss_ctx.hEventFrozen
> + * after the applications and filesystems are frozen.
> + */
> + hr = vss_ctx.pVssbc->DoSnapshotSet(&vss_ctx.pAsyncSnapshot);
> + if (FAILED(hr)) {
> + err_set(errset, hr, "failed to do snapshot set");
> + goto out;
> + }
> +
> + /* Need to call QueryStatus several times to make VSS provider progress
> */
> + for (i = 0; i < VSS_TIMEOUT_FREEZE_MSEC/VSS_TIMEOUT_EVENT_MSEC; i++) {
> + HRESULT hr2 = vss_ctx.pAsyncSnapshot->QueryStatus(&hr, NULL);
> + if (FAILED(hr2)) {
> + err_set(errset, hr, "failed to do snapshot set");
> + goto out;
> + }
> + if (hr != VSS_S_ASYNC_PENDING) {
> + err_set(errset, E_FAIL,
> + "DoSnapshotSet exited without Frozen event");
> + goto out;
> + }
> + wait_status = WaitForSingleObject(vss_ctx.hEventFrozen,
> + VSS_TIMEOUT_EVENT_MSEC);
> + if (wait_status != WAIT_TIMEOUT) {
> + break;
> + }
> + }
> + if (wait_status != WAIT_OBJECT_0) {
> + err_set(errset, E_FAIL,
> + "Couldn't receive Frozen event from VSS provider");
> + goto out;
> + }
> +
> + *num_vols = vss_ctx.cFrozenVols = num_fixed_drives;
> + return;
> +
> +out:
> + if (vss_ctx.pVssbc) {
> + vss_ctx.pVssbc->AbortBackup();
> + }
> + requester_cleanup();
> + CoUninitialize();
> +}
> +
> +
> +void requester_thaw(int *num_vols, ErrorSet *errset)
> +{
> + COMPointer<IVssAsync> pAsync;
> +
> + if (vss_ctx.hEventThaw == INVALID_HANDLE_VALUE) {
> + /*
> + * In this case, DoSnapshotSet is aborted or not started,
> + * and no volumes must be frozen. We return without an error.
> + */
> + *num_vols = 0;
> + return;
> + }
> +
> + /* Tell the provider that the snapshot is finished. */
> + SetEvent(vss_ctx.hEventThaw);
> +
> + assert(vss_ctx.pVssbc);
> + assert(vss_ctx.pAsyncSnapshot);
> +
> + HRESULT hr = WaitForAsync(vss_ctx.pAsyncSnapshot);
> + switch (hr) {
> + case VSS_S_ASYNC_FINISHED:
> + hr = vss_ctx.pVssbc->BackupComplete(pAsync.replace());
> + if (SUCCEEDED(hr)) {
> + hr = WaitForAsync(pAsync);
> + }
> + if (FAILED(hr)) {
> + err_set(errset, hr, "failed to complete backup");
> + }
> + break;
> +
> + case (HRESULT)VSS_E_OBJECT_NOT_FOUND:
> + /*
> + * On Windows earlier than 2008 SP2 which does not support
> + * VSS_VOLSNAP_ATTR_NO_AUTORECOVERY context, the final commit is not
> + * skipped and VSS is aborted by VSS_E_OBJECT_NOT_FOUND. However, as
> + * the system had been frozen until fsfreeze-thaw command was issued,
> + * we ignore this error.
> + */
> + vss_ctx.pVssbc->AbortBackup();
> + break;
> +
> + case (HRESULT)VSS_E_HOLD_WRITES_TIMEOUT:
> + err_set(errset, hr, "couldn't hold writes: "
> + "fsfreeze is limited up to 10 seconds");
> + break;
> +
> + default:
> + err_set(errset, hr, "failed to do snapshot set");
> + }
> +
> + if (err_is_set(errset)) {
> + vss_ctx.pVssbc->AbortBackup();
> + }
> + *num_vols = vss_ctx.cFrozenVols;
> + requester_cleanup();
> +
> + CoUninitialize();
> +}
> diff --git a/qga/vss-win32/requester.h b/qga/vss-win32/requester.h
> new file mode 100644
> index 0000000..ccd197c
> --- /dev/null
> +++ b/qga/vss-win32/requester.h
> @@ -0,0 +1,42 @@
> +/*
> + * QEMU Guest Agent VSS requester declarations
> + *
> + * Copyright Hitachi Data Systems Corp. 2013
> + *
> + * Authors:
> + * Tomoki Sekiyama <address@hidden>
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2 or later.
> + * See the COPYING file in the top-level directory.
> + */
> +
> +#ifndef VSS_WIN32_REQUESTER_H
> +#define VSS_WIN32_REQUESTER_H
> +
> +#include "qemu/compiler.h"
> +
> +#ifdef __cplusplus
> +extern "C" {
> +#endif
> +
> +/* Callback to set Error; used to avoid linking glib to the DLL */
> +typedef void (*ErrorSetFunc)(void **errp, int win32_err, int err_class,
> + const char *fmt, ...) GCC_FMT_ATTR(4, 5);
> +typedef struct ErrorSet {
> + ErrorSetFunc error_set;
> + void **errp;
> + int err_class;
> +} ErrorSet;
> +
> +STDAPI requester_init(void);
> +STDAPI requester_deinit(void);
> +
> +typedef void (*QGAVSSReuqesterFunc)(int *, ErrorSet *);
> +void requester_freeze(int *num_vols, ErrorSet *errset);
> +void requester_thaw(int *num_vols, ErrorSet *errset);
> +
> +#ifdef __cplusplus
> +}
> +#endif
> +
> +#endif
> diff --git a/qga/vss-win32/vss-common.h b/qga/vss-win32/vss-common.h
> new file mode 100644
> index 0000000..3637e8d
> --- /dev/null
> +++ b/qga/vss-win32/vss-common.h
> @@ -0,0 +1,128 @@
> +/*
> + * QEMU Guest Agent win32 VSS common declarations
> + *
> + * Copyright Hitachi Data Systems Corp. 2013
> + *
> + * Authors:
> + * Tomoki Sekiyama <address@hidden>
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2 or later.
> + * See the COPYING file in the top-level directory.
> + */
> +
> +#ifndef VSS_WIN32_H
> +#define VSS_WIN32_H
> +
> +#define __MIDL_user_allocate_free_DEFINED__
> +#include "config-host.h"
> +#include <windows.h>
> +#include <shlwapi.h>
> +
> +/* Reduce warnings to include vss.h */
> +
> +/* Ignore annotations for MS IDE */
> +#define __in IN
> +#define __out OUT
> +#define __RPC_unique_pointer
> +#define __RPC_string
> +#define __RPC__deref_inout_opt
> +#define __RPC__out
> +#ifndef __RPC__out_ecount_part
> +#define __RPC__out_ecount_part(x, y)
> +#endif
> +#define _declspec(x)
> +#undef uuid
> +#define uuid(x)
> +
> +/* Undef some duplicated error codes redefined in vss.h */
> +#undef VSS_E_BAD_STATE
> +#undef VSS_E_PROVIDER_NOT_REGISTERED
> +#undef VSS_E_PROVIDER_VETO
> +#undef VSS_E_OBJECT_NOT_FOUND
> +#undef VSS_E_VOLUME_NOT_SUPPORTED
> +#undef VSS_E_VOLUME_NOT_SUPPORTED_BY_PROVIDER
> +#undef VSS_E_OBJECT_ALREADY_EXISTS
> +#undef VSS_E_UNEXPECTED_PROVIDER_ERROR
> +#undef VSS_E_INVALID_XML_DOCUMENT
> +#undef VSS_E_MAXIMUM_NUMBER_OF_VOLUMES_REACHED
> +#undef VSS_E_MAXIMUM_NUMBER_OF_SNAPSHOTS_REACHED
> +
> +/*
> + * VSS headers must be installed from Microsoft VSS SDK 7.2 available at:
> + * http://www.microsoft.com/en-us/download/details.aspx?id=23490
> + */
> +#include "inc/win2003/vss.h"
> +
> +/* Macros to convert char definitions to wchar */
> +#define _L(a) L##a
> +#define L(a) _L(a)
> +
> +/* Constants for QGA VSS Provider */
> +
> +#define QGA_PROVIDER_NAME "QEMU Guest Agent VSS Provider"
> +#define QGA_PROVIDER_LNAME L(QGA_PROVIDER_NAME)
> +#define QGA_PROVIDER_VERSION L(QEMU_VERSION)
> +
> +#define EVENT_NAME_FROZEN "Global\\QGAVSSEvent-frozen"
> +#define EVENT_NAME_THAW "Global\\QGAVSSEvent-thaw"
> +
> +const GUID g_gProviderId = { 0x3629d4ed, 0xee09, 0x4e0e,
> + {0x9a, 0x5c, 0x6d, 0x8b, 0xa2, 0x87, 0x2a, 0xef} };
> +const GUID g_gProviderVersion = { 0x11ef8b15, 0xcac6, 0x40d6,
> + {0x8d, 0x5c, 0x8f, 0xfc, 0x16, 0x3f, 0x24, 0xca} };
> +
> +const CLSID CLSID_QGAVSSProvider = { 0x6e6a3492, 0x8d4d, 0x440c,
> + {0x96, 0x19, 0x5e, 0x5d, 0x0c, 0xc3, 0x1c, 0xa8} };
> +
> +const TCHAR g_szClsid[] = TEXT("{6E6A3492-8D4D-440C-9619-5E5D0CC31CA8}");
> +const TCHAR g_szProgid[] = TEXT("QGAVSSProvider");
> +
> +/* Enums undefined in VSS SDK 7.2 but defined in newer Windows SDK */
> +enum __VSS_VOLUME_SNAPSHOT_ATTRIBUTES {
> + VSS_VOLSNAP_ATTR_NO_AUTORECOVERY = 0x00000002,
> + VSS_VOLSNAP_ATTR_TXF_RECOVERY = 0x02000000
> +};
> +
> +
> +/* COM pointer utility; call ->Release() when it goes out of scope */
> +template <class T>
> +class COMPointer {
> + COMPointer(const COMPointer<T> &p) { } /* no copy */
> + T *p;
> +public:
> + COMPointer &operator=(T *new_p)
> + {
> + /* Assignment of a new T* (or NULL) causes release of previous p */
> + if (p && p != new_p) {
> + p->Release();
> + }
> + p = new_p;
> + return *this;
> + }
> + /* Replace by assignment to the pointer of p */
> + T **replace(void)
> + {
> + *this = NULL;
> + return &p;
> + }
> + /* Make COMPointer be used like T* */
> + operator T*() { return p; }
> + T *operator->(void) { return p; }
> + T &operator*(void) { return *p; }
> + operator bool() { return !!p; }
> +
> + COMPointer(T *p = NULL) : p(p) { }
> + ~COMPointer() { *this = NULL; } /* Automatic release */
> +};
> +
> +/*
> + * COM initializer; this should declared before COMPointer to uninitialize
> COM
> + * after releasing COM objects.
> + */
> +class COMInitializer {
> +public:
> + COMInitializer() { CoInitialize(NULL); }
> + ~COMInitializer() { CoUninitialize(); }
> +};
> +
> +#endif
- [Qemu-devel] [PATCH v8 04/10] Add a script to extract VSS SDK headers on POSIX system, (continued)
- [Qemu-devel] [PATCH v8 04/10] Add a script to extract VSS SDK headers on POSIX system, Tomoki Sekiyama, 2013/07/23
- [Qemu-devel] [PATCH v8 05/10] qemu-ga: Add configure options to specify path to Windows/VSS SDK, Tomoki Sekiyama, 2013/07/23
- [Qemu-devel] [PATCH v8 06/10] error: Add error_set_win32 and error_setg_win32, Tomoki Sekiyama, 2013/07/23
- [Qemu-devel] [PATCH v8 09/10] qemu-ga: Install Windows VSS provider on `qemu-ga -s install', Tomoki Sekiyama, 2013/07/23
- [Qemu-devel] [PATCH v8 08/10] qemu-ga: Call Windows VSS requester in fsfreeze command handler, Tomoki Sekiyama, 2013/07/23
- [Qemu-devel] [PATCH v8 10/10] QMP/qemu-ga-client: Make timeout longer for guest-fsfreeze-freeze command, Tomoki Sekiyama, 2013/07/23
- [Qemu-devel] [PATCH v8 07/10] qemu-ga: Add Windows VSS provider and requester as DLL, Tomoki Sekiyama, 2013/07/23
- Re: [Qemu-devel] [PATCH v8 07/10] qemu-ga: Add Windows VSS provider and requester as DLL,
Michael Roth <=
- Re: [Qemu-devel] [PATCH v8 07/10] qemu-ga: Add Windows VSS provider and requester as DLL, Michael Roth, 2013/07/26
- Re: [Qemu-devel] [PATCH v8 07/10] qemu-ga: Add Windows VSS provider and requester as DLL, Tomoki Sekiyama, 2013/07/29
- Re: [Qemu-devel] [PATCH v8 07/10] qemu-ga: Add Windows VSS provider and requester as DLL, Michael Roth, 2013/07/30
- Re: [Qemu-devel] [PATCH v8 07/10] qemu-ga: Add Windows VSS provider and requester as DLL, Tomoki Sekiyama, 2013/07/30
- Re: [Qemu-devel] [PATCH v8 07/10] qemu-ga: Add Windows VSS provider and requester as DLL, Michael Roth, 2013/07/30
- Re: [Qemu-devel] [PATCH v8 07/10] qemu-ga: Add Windows VSS provider and requester as DLL, Tomoki Sekiyama, 2013/07/30