diff mbox

[v8,07/10] qemu-ga: Add Windows VSS provider and requester as DLL

Message ID 20130723224554.26273.57633.stgit@outback
State New
Headers show

Commit Message

Tomoki Sekiyama July 23, 2013, 10:45 p.m. UTC
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>
---
 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

Comments

Michael Roth July 25, 2013, 11:36 p.m. UTC | #1
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 <tomoki.sekiyama@hds.com>
> ---
>  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   <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 <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   <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;
> +
> +    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;0UB3A_y8H!@$4<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;hd*$F@lQbP%-z+r|5P#hGW*vvKniaRx03p~wP?y>h_rLW<nKOg@=
> z6{hYgzgH7@QE<^MU>KC!yoBjb#vz`9Lwu4+R-SJ!kIOX4xLBUUGN9-NyTyP76j}@n
> z2f!qQ8-xepfXD+7rW+{SKz4&@7#qX~^1#sn3O|tFK>%ciE<<riN`6kNkzPqoQh0bc
> zNbM*XJ|Un0jALSb15^rEE6h+Y8tCpA798vm9#E8DmYI@T<dc~c4pSpwQKEn@FU<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   <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 "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   <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.
> + */
> +
> +#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   <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.
> + */
> +
> +#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
Michael Roth July 27, 2013, 1:22 a.m. UTC | #2
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 <tomoki.sekiyama@hds.com>
> ---
>  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

<snip>

Looks good

> diff --git a/Makefile.objs b/Makefile.objs
> index 5b288ba..ae541d1 100644
> --- a/Makefile.objs
> +++ b/Makefile.objs

<snip>

Looks good

> diff --git a/configure b/configure
> index 48736b3..f1b7e4f 100755
> --- a/configure
> +++ b/configure

<snip>

Other than issues mentioned earlier, looks good

> diff --git a/qga/Makefile.objs b/qga/Makefile.objs
> index b8d7cd0..4891732 100644
> --- a/qga/Makefile.objs
> +++ b/qga/Makefile.objs

<snip>

Other than small nit from earlier, looks good

> 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)$@")

Is the only way to execute this to delete qga-vss.tlb from the source
directory? Not a big deal, but seems a bit awkward for users/builders.

It looks like WIN_SDK exists only for this purpose, so perhaps just default
WIN_SDK to "no". Then if they specify --with-win-sdk with no parameters, we do
the current defaults in configure, and if they specify it explicitly we use that
path. Basically the same thing we do for --with-vss-sdk/--without-vss-sdk
currently.

Then we do something like:

  $(obj)/qga-vss.tlb: $(SRC_PATH)/$(obj)/qga-vss.idl
  ifeq ($(WIN_SDK),)
         $(call quiet-command,cp $(dir $<)qga-vss.tlb $@, "  COPY  $(TARGET_DIR)$@")
  else
         $(call quiet-command,$(MIDL) -tlb $@ -I $(WIN_SDK)/Include $<,"  MIDL  $(TARGET_DIR)$@")
  endif


> +else
> +       $(call quiet-command,cp $(dir $<)qga-vss.tlb $@, "  COPY  $(TARGET_DIR)$@")
> +endif

Looks good otherwise.

> 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

<snip>

Looks good. But just a bit of a warning going forward: I am not at all
familiar with the Windows COM interfaces and only looked up the more
interesting ones, so take this review with a grain of salt.

I did try to check the general logic/error-handling and look up the bits
related to the VSS requestor/provider implementations though.


> 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   <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;
> +
> +    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;
> +    }

I see that we approximate both the 10 second fsfreeze and 60s writer
freeze timeouts using local event timeouts, but I think there's a chance
for some races there.

We could maybe lower the timeouts a bit to be safe, but i don't really
like the sound of that...would be nice if there was some way to

a) ideally, guarantee the guest-fsfreeze-thaw will return an error if
   the fsfreeze timed out before it was called, or
b) guarantee at least that it will return an error if the vss writer
   freeze timed out.

<snip>

Looks good otherwise

> 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

<snip>

Looks good

> 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

<snip>

Looks good

> 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

<snip>

Looks good. :)


> 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   <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 "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;
> +    }

One small issue I noticed was that this error will get overwritten
with the VSS writer timeout error if we wait longer than 60s before
calling guest-fsfreeze-thaw. It might give some users false assurances
about what aspects of their snapshot may be volatile so it's
probably worth addressing.

Another small nit below but looks good otherwise.

> +
> +    *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   <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.
> + */
> +
> +#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 *);

Should be QGAVSSRequesterFunc

> +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   <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.
> + */
> +
> +#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
Tomoki Sekiyama July 29, 2013, 6:32 p.m. UTC | #3
Hi Michael,
Thank you for the review.

>>-  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"

Ah, thanks for pointing this out. I will fix into this.

>>+
>>+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

OK.

>> 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)$@")
>
>Is the only way to execute this to delete qga-vss.tlb from the source
>directory? Not a big deal, but seems a bit awkward for users/builders.
>
>It looks like WIN_SDK exists only for this purpose, so perhaps just
>default
>WIN_SDK to "no". Then if they specify --with-win-sdk with no parameters,
>we do
>the current defaults in configure, and if they specify it explicitly we
>use that
>path. Basically the same thing we do for --with-vss-sdk/--without-vss-sdk
>currently.
>
>Then we do something like:
>
>  $(obj)/qga-vss.tlb: $(SRC_PATH)/$(obj)/qga-vss.idl
>  ifeq ($(WIN_SDK),)
>         $(call quiet-command,cp $(dir $<)qga-vss.tlb $@, "  COPY
>$(TARGET_DIR)$@")
>  else
>         $(call quiet-command,$(MIDL) -tlb $@ -I $(WIN_SDK)/Include $<,"
>MIDL  $(TARGET_DIR)$@")
>  Endif

OK, I will take this way.


>> 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
<snip>
>> +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;
>> +    }
>
>I see that we approximate both the 10 second fsfreeze and 60s writer
>freeze timeouts using local event timeouts, but I think there's a chance
>for some races there.
>
>We could maybe lower the timeouts a bit to be safe, but i don't really
>like the sound of that...would be nice if there was some way to
>
>a) ideally, guarantee the guest-fsfreeze-thaw will return an error if
>   the fsfreeze timed out before it was called, or
>b) guarantee at least that it will return an error if the vss writer
>   freeze timed out.

When the writer freeze timeout is exceeded, VSS service will
automatically abort the snapshot sequence and report an error
(E_VSS_WRITES_TIMEOUT) to the requester, even if the provider
is still sleeping. This behavior guarantees the following
guest-fsfreeze-thaw return the following error:
  {"error": {"class": "GenericError", "desc": "couldn't hold writes:
  fsfreeze is limited up to 10 seconds: (error:80042314)"}}

VSS_TIMEOUT_MSEC here is just not to freeze the provider forever
if the guest-fsfreeze-thaw is not issued.

However, if the provider returns the E_ABORT by timeout, VSS reports
VSS_E_UNEXPECTED_PROVIDER_ERROR, and it results in different error
message returned to the host.
(Possibly your comment below about overwritten error intended to
 address this issue?)
To ensure we get the E_VSS_WRITES_TIMEOUT on VSS timeout, the timeout
could be longer (e.g., 120 seconds).


>> 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
<snip>
>> +    /* 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;
>> +    }
>
>One small issue I noticed was that this error will get overwritten
>with the VSS writer timeout error if we wait longer than 60s before
>calling guest-fsfreeze-thaw. It might give some users false assurances
>about what aspects of their snapshot may be volatile so it's
>probably worth addressing.

This is an error returned against guest-fsfreeze-freeze, when the
writers or filesystems take more than 60s to quiesce.
(CQGAVssProvider::CommitSnapshots that issues FrozenEvent is
called after this quiescing succeeded.) The VSS sequence is aborted
at "out:". If this happens, as the system remains thawed state, the
following guest-fsfreeze-thaw will just return 0.


<snip>
>> 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
<snip>
>> +typedef void (*QGAVSSReuqesterFunc)(int *, ErrorSet *);
>
>Should be QGAVSSRequesterFunc

Oops, thank you for catching this.
Will fix these typo in the next version.


Thanks,
Michael Roth July 30, 2013, 7:35 p.m. UTC | #4
Quoting Tomoki Sekiyama (2013-07-29 13:32:20)
> Hi Michael,
> Thank you for the review.
> 
> >>-  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"
> 
> Ah, thanks for pointing this out. I will fix into this.
> 
> >>+
> >>+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
> 
> OK.
> 
> >> 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)$@")
> >
> >Is the only way to execute this to delete qga-vss.tlb from the source
> >directory? Not a big deal, but seems a bit awkward for users/builders.
> >
> >It looks like WIN_SDK exists only for this purpose, so perhaps just
> >default
> >WIN_SDK to "no". Then if they specify --with-win-sdk with no parameters,
> >we do
> >the current defaults in configure, and if they specify it explicitly we
> >use that
> >path. Basically the same thing we do for --with-vss-sdk/--without-vss-sdk
> >currently.
> >
> >Then we do something like:
> >
> >  $(obj)/qga-vss.tlb: $(SRC_PATH)/$(obj)/qga-vss.idl
> >  ifeq ($(WIN_SDK),)
> >         $(call quiet-command,cp $(dir $<)qga-vss.tlb $@, "  COPY
> >$(TARGET_DIR)$@")
> >  else
> >         $(call quiet-command,$(MIDL) -tlb $@ -I $(WIN_SDK)/Include $<,"
> >MIDL  $(TARGET_DIR)$@")
> >  Endif
> 
> OK, I will take this way.
> 
> 
> >> 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
> <snip>
> >> +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;
> >> +    }
> >
> >I see that we approximate both the 10 second fsfreeze and 60s writer
> >freeze timeouts using local event timeouts, but I think there's a chance
> >for some races there.
> >
> >We could maybe lower the timeouts a bit to be safe, but i don't really
> >like the sound of that...would be nice if there was some way to
> >
> >a) ideally, guarantee the guest-fsfreeze-thaw will return an error if
> >   the fsfreeze timed out before it was called, or
> >b) guarantee at least that it will return an error if the vss writer
> >   freeze timed out.
> 
> When the writer freeze timeout is exceeded, VSS service will
> automatically abort the snapshot sequence and report an error
> (E_VSS_WRITES_TIMEOUT) to the requester, even if the provider
> is still sleeping. This behavior guarantees the following
> guest-fsfreeze-thaw return the following error:
>   {"error": {"class": "GenericError", "desc": "couldn't hold writes:
>   fsfreeze is limited up to 10 seconds: (error:80042314)"}}

I see, I was assuming we were using the events to track status, but
see now that we collect final status in the requester via
WaitForAsync(vss_ctx.pAsyncSnapshot).

That's great, so the VSS_E_HOLD_WRITES_TIMEOUT check allows us to
provide the same guarantees as the posix fsfreeze implementation.

Nice!

> 
> VSS_TIMEOUT_MSEC here is just not to freeze the provider forever
> if the guest-fsfreeze-thaw is not issued.
> 
> However, if the provider returns the E_ABORT by timeout, VSS reports
> VSS_E_UNEXPECTED_PROVIDER_ERROR, and it results in different error
> message returned to the host.
> (Possibly your comment below about overwritten error intended to
>  address this issue?)
> To ensure we get the E_VSS_WRITES_TIMEOUT on VSS timeout, the timeout
> could be longer (e.g., 120 seconds).
> 
> 
> >> 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
> <snip>
> >> +    /* 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;
> >> +    }
> >
> >One small issue I noticed was that this error will get overwritten
> >with the VSS writer timeout error if we wait longer than 60s before
> >calling guest-fsfreeze-thaw. It might give some users false assurances
> >about what aspects of their snapshot may be volatile so it's
> >probably worth addressing.
> 
> This is an error returned against guest-fsfreeze-freeze, when the
> writers or filesystems take more than 60s to quiesce.
> (CQGAVssProvider::CommitSnapshots that issues FrozenEvent is
> called after this quiescing succeeded.) The VSS sequence is aborted
> at "out:". If this happens, as the system remains thawed state, the
> following guest-fsfreeze-thaw will just return 0.

This is the example I'm referring to:

{'execute':'guest-fsfreeze-freeze'}
{"return": 2}
/* wait 10+ seconds */
{'execute':'guest-fsfreeze-thaw'}
{"error": {"class": "GenericError", "desc": "couldn't hold writes: fsfreeze is limited up to 10 seconds:  (error: 80042314)"}}
{'execute':'guest-fsfreeze-freeze'}
{"return": 2}
/* wait 60+ seconds */
{'execute':'guest-fsfreeze-thaw'}
{"error": {"class": "GenericError", "desc": "failed to do snapshot set:  (error: 8004230f)"}}

It this seems to be because CommitSnapshot returns in the latter instance due
to VSS_TIMEOUT_MSEC wait and it's E_ABORT error message overwrites the
VSS_E_HOLD_WRITES_TIMEOUT from earlier. Perhaps we could just have
CommitSnapshot return VSS_E_HOLD_WRITES_TIMEOUT if it doesn't get the thaw
event in time? I think that error message is much more informative for users.

> 
> 
> <snip>
> >> 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
> <snip>
> >> +typedef void (*QGAVSSReuqesterFunc)(int *, ErrorSet *);
> >
> >Should be QGAVSSRequesterFunc
> 
> Oops, thank you for catching this.
> Will fix these typo in the next version.
> 
> 
> Thanks,
> -- 
> Tomoki Sekiyama
Tomoki Sekiyama July 30, 2013, 8:54 p.m. UTC | #5
On 7/30/13 15:35 , "Michael Roth" <mdroth@linux.vnet.ibm.com> wrote:

>>>One small issue I noticed was that this error will get overwritten
>>>with the VSS writer timeout error if we wait longer than 60s before
>>>calling guest-fsfreeze-thaw. It might give some users false assurances
>>>about what aspects of their snapshot may be volatile so it's
>>>probably worth addressing.
>>This is an error returned against guest-fsfreeze-freeze, when the
>>writers or filesystems take more than 60s to quiesce.
>>(CQGAVssProvider::CommitSnapshots that issues FrozenEvent is
>>called after this quiescing succeeded.) The VSS sequence is aborted
>>at "out:". If this happens, as the system remains thawed state, the
>>following guest-fsfreeze-thaw will just return 0.
>
>This is the example I'm referring to:
>
>{'execute':'guest-fsfreeze-freeze'}
>{"return": 2}
>/* wait 10+ seconds */
>{'execute':'guest-fsfreeze-thaw'}
>{"error": {"class": "GenericError", "desc": "couldn't hold writes:
>fsfreeze is limited up to 10 seconds:  (error: 80042314)"}}
>{'execute':'guest-fsfreeze-freeze'}
>{"return": 2}
>/* wait 60+ seconds */
>{'execute':'guest-fsfreeze-thaw'}
>{"error": {"class": "GenericError", "desc": "failed to do snapshot set:
>(error: 8004230f)"}}
>
>It this seems to be because CommitSnapshot returns in the latter instance
>due
>to VSS_TIMEOUT_MSEC wait and it's E_ABORT error message overwrites the
>VSS_E_HOLD_WRITES_TIMEOUT from earlier. Perhaps we could just have
>CommitSnapshot return VSS_E_HOLD_WRITES_TIMEOUT if it doesn't get the thaw
>event in time? I think that error message is much more informative for
>users.

Agreed.

How about modifying the provider to return S_OK instead of E_ABORT
when it exceeds VSS_TIMEOUT_MSEC?

As VSS_TIMEOUT_MSEC is larger than 10 seconds fsfreeze timeout,
CommitSnapshot will return VSS_E_HOLD_WRITES_TIMEOUT on provider's timeout,
and give "fsfreeze is limited up to 10 seconds" message to users.
The writers are also automatically thawed by 60 seconds timeout anyway,

this wouldn't break anything.

The waiting code in CQGAVssProvider::CommitSnapshots would be like this:

    ...
    /* Wait until the snapshot is taken by the host. */
    wait = WaitForSingleObject(hEventThaw, VSS_TIMEOUT_MSEC);
    switch (wait) {
    case WAIT_TIMEOUT:
        /*
         * We return S_OK, but the qemu-ga will get
E_VSS_HOLD_WRITES_TIMEOUT
         * because 10 seconds fsfreeze timeout is exceeded. (If we return
some
         * error here, it results in VSS_E_UNEXPECTED_PROVIDER_ERROR that
can
         * be caused by the other reasons.)
         */
    case WAIT_OBJECT_0:
        hr = S_OK;
        break;

    default:
        hr = E_ABORT;
    }
    ...


Thanks,
Tomoki Sekiyama
Michael Roth July 30, 2013, 10:24 p.m. UTC | #6
Quoting Tomoki Sekiyama (2013-07-30 15:54:05)
> On 7/30/13 15:35 , "Michael Roth" <mdroth@linux.vnet.ibm.com> wrote:
> 
> >>>One small issue I noticed was that this error will get overwritten
> >>>with the VSS writer timeout error if we wait longer than 60s before
> >>>calling guest-fsfreeze-thaw. It might give some users false assurances
> >>>about what aspects of their snapshot may be volatile so it's
> >>>probably worth addressing.
> >>This is an error returned against guest-fsfreeze-freeze, when the
> >>writers or filesystems take more than 60s to quiesce.
> >>(CQGAVssProvider::CommitSnapshots that issues FrozenEvent is
> >>called after this quiescing succeeded.) The VSS sequence is aborted
> >>at "out:". If this happens, as the system remains thawed state, the
> >>following guest-fsfreeze-thaw will just return 0.
> >
> >This is the example I'm referring to:
> >
> >{'execute':'guest-fsfreeze-freeze'}
> >{"return": 2}
> >/* wait 10+ seconds */
> >{'execute':'guest-fsfreeze-thaw'}
> >{"error": {"class": "GenericError", "desc": "couldn't hold writes:
> >fsfreeze is limited up to 10 seconds:  (error: 80042314)"}}
> >{'execute':'guest-fsfreeze-freeze'}
> >{"return": 2}
> >/* wait 60+ seconds */
> >{'execute':'guest-fsfreeze-thaw'}
> >{"error": {"class": "GenericError", "desc": "failed to do snapshot set:
> >(error: 8004230f)"}}
> >
> >It this seems to be because CommitSnapshot returns in the latter instance
> >due
> >to VSS_TIMEOUT_MSEC wait and it's E_ABORT error message overwrites the
> >VSS_E_HOLD_WRITES_TIMEOUT from earlier. Perhaps we could just have
> >CommitSnapshot return VSS_E_HOLD_WRITES_TIMEOUT if it doesn't get the thaw
> >event in time? I think that error message is much more informative for
> >users.
> 
> Agreed.
> 
> How about modifying the provider to return S_OK instead of E_ABORT
> when it exceeds VSS_TIMEOUT_MSEC?
> 
> As VSS_TIMEOUT_MSEC is larger than 10 seconds fsfreeze timeout,
> CommitSnapshot will return VSS_E_HOLD_WRITES_TIMEOUT on provider's timeout,
> and give "fsfreeze is limited up to 10 seconds" message to users.
> The writers are also automatically thawed by 60 seconds timeout anyway,
> 
> this wouldn't break anything.

Hmm, it seems like it would work, but I'm a bit worried about returning
S_OK and hoping VSS still produces an error. Probably really unlikely, but
I could imagine a really strange timing issue where the 60 second timeout
for whatever reason gets triggered before the 10 second one (maybe because
one vcpu is being starved for whatever reason, don't know enough about
windows timekeeping mechanisms to know how plausible this is, but I'd
rather not rely on it not happening).

One way you could maybe do it would be maybe introducing a hEventTimeout
that CommitSnapshots will set if it times out. That way we can poll
it to see if the event is set when we get VSS_E_UNEXPECTED_PROVIDER_ERROR
in the requester. If it's set we know a timeout occurred on provider side,
and return the E_VSS_HOLD_WRITES_TIMEOUT error.

> 
> The waiting code in CQGAVssProvider::CommitSnapshots would be like this:
> 
>     ...
>     /* Wait until the snapshot is taken by the host. */
>     wait = WaitForSingleObject(hEventThaw, VSS_TIMEOUT_MSEC);
>     switch (wait) {
>     case WAIT_TIMEOUT:
>         /*
>          * We return S_OK, but the qemu-ga will get
> E_VSS_HOLD_WRITES_TIMEOUT
>          * because 10 seconds fsfreeze timeout is exceeded. (If we return
> some
>          * error here, it results in VSS_E_UNEXPECTED_PROVIDER_ERROR that
> can
>          * be caused by the other reasons.)
>          */
>     case WAIT_OBJECT_0:
>         hr = S_OK;
>         break;
> 
>     default:
>         hr = E_ABORT;
>     }
>     ...
> 
> 
> Thanks,
> Tomoki Sekiyama
Tomoki Sekiyama July 30, 2013, 10:28 p.m. UTC | #7
On 7/30/13 18:24 , "Michael Roth" <mdroth@linux.vnet.ibm.com> wrote:

>Quoting Tomoki Sekiyama (2013-07-30 15:54:05)
>>On 7/30/13 15:35 , "Michael Roth" <mdroth@linux.vnet.ibm.com> wrote:
>>>>>One small issue I noticed was that this error will get overwritten
>>>>>with the VSS writer timeout error if we wait longer than 60s before
>>>>>calling guest-fsfreeze-thaw. It might give some users false assurances
>>>>>about what aspects of their snapshot may be volatile so it's
>>>>>probably worth addressing.
>>>>This is an error returned against guest-fsfreeze-freeze, when the
>>>>writers or filesystems take more than 60s to quiesce.
>>>>(CQGAVssProvider::CommitSnapshots that issues FrozenEvent is
>>>>called after this quiescing succeeded.) The VSS sequence is aborted
>>>>at "out:". If this happens, as the system remains thawed state, the
>>>>following guest-fsfreeze-thaw will just return 0.
>>>
>>>This is the example I'm referring to:
>>>
>>>{'execute':'guest-fsfreeze-freeze'}
>>>{"return": 2}
>>>/* wait 10+ seconds */
>>>{'execute':'guest-fsfreeze-thaw'}
>>>{"error": {"class": "GenericError", "desc": "couldn't hold writes:
>>>fsfreeze is limited up to 10 seconds:  (error: 80042314)"}}
>>>{'execute':'guest-fsfreeze-freeze'}
>>>{"return": 2}
>>>/* wait 60+ seconds */
>>>{'execute':'guest-fsfreeze-thaw'}
>>>{"error": {"class": "GenericError", "desc": "failed to do snapshot set:
>>>(error: 8004230f)"}}
>>>
>>>It this seems to be because CommitSnapshot returns in the latter
>>>instance
>>>due
>>>to VSS_TIMEOUT_MSEC wait and it's E_ABORT error message overwrites the
>>>VSS_E_HOLD_WRITES_TIMEOUT from earlier. Perhaps we could just have
>>>CommitSnapshot return VSS_E_HOLD_WRITES_TIMEOUT if it doesn't get the
>>>thaw
>>>event in time? I think that error message is much more informative for
>>>users.
>>Agreed.
>>How about modifying the provider to return S_OK instead of E_ABORT
>>when it exceeds VSS_TIMEOUT_MSEC?
>>As VSS_TIMEOUT_MSEC is larger than 10 seconds fsfreeze timeout,
>>CommitSnapshot will return VSS_E_HOLD_WRITES_TIMEOUT on provider's
>>timeout,
>>and give "fsfreeze is limited up to 10 seconds" message to users.
>>The writers are also automatically thawed by 60 seconds timeout anyway,
>>this wouldn't break anything.
>
>Hmm, it seems like it would work, but I'm a bit worried about returning
>S_OK and hoping VSS still produces an error. Probably really unlikely, but
>I could imagine a really strange timing issue where the 60 second timeout
>for whatever reason gets triggered before the 10 second one (maybe because
>one vcpu is being starved for whatever reason, don't know enough about
>windows timekeeping mechanisms to know how plausible this is, but I'd
>rather not rely on it not happening).
>
>One way you could maybe do it would be maybe introducing a hEventTimeout
>that CommitSnapshots will set if it times out. That way we can poll
>it to see if the event is set when we get VSS_E_UNEXPECTED_PROVIDER_ERROR
>in the requester. If it's set we know a timeout occurred on provider side,
>and return the E_VSS_HOLD_WRITES_TIMEOUT error.

OK, this sounds more reliable. I will take this way.

Thanks,
Tomoki Sekiyama
diff mbox

Patch

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
     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
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   <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 <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   <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;
+
+    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;0UB3A_y8H!@$4<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;hd*$F@lQbP%-z+r|5P#hGW*vvKniaRx03p~wP?y>h_rLW<nKOg@=
z6{hYgzgH7@QE<^MU>KC!yoBjb#vz`9Lwu4+R-SJ!kIOX4xLBUUGN9-NyTyP76j}@n
z2f!qQ8-xepfXD+7rW+{SKz4&@7#qX~^1#sn3O|tFK>%ciE<<riN`6kNkzPqoQh0bc
zNbM*XJ|Un0jALSb15^rEE6h+Y8tCpA798vm9#E8DmYI@T<dc~c4pSpwQKEn@FU<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   <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 "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   <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.
+ */
+
+#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   <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.
+ */
+
+#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