From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([2001:4830:134:3::10]:53184) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1V2V5M-0003m1-Le for qemu-devel@nongnu.org; Thu, 25 Jul 2013 19:36:45 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1V2V5H-0000nV-9t for qemu-devel@nongnu.org; Thu, 25 Jul 2013 19:36:40 -0400 Received: from mail-oa0-x229.google.com ([2607:f8b0:4003:c02::229]:34124) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1V2V5G-0000nK-Uq for qemu-devel@nongnu.org; Thu, 25 Jul 2013 19:36:35 -0400 Received: by mail-oa0-f41.google.com with SMTP id n10so6058030oag.28 for ; Thu, 25 Jul 2013 16:36:34 -0700 (PDT) Sender: fluxion Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable From: Michael Roth In-Reply-To: <20130723224554.26273.57633.stgit@outback> References: <20130723224525.26273.28321.stgit@outback> <20130723224554.26273.57633.stgit@outback> Message-ID: <20130725233628.16294.29022@loki> Date: Thu, 25 Jul 2013 18:36:28 -0500 Subject: Re: [Qemu-devel] [PATCH v8 07/10] qemu-ga: Add Windows VSS provider and requester as DLL List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: Tomoki Sekiyama , qemu-devel@nongnu.org Cc: libaiqing@huawei.com, ghammer@redhat.com, stefanha@gmail.com, lcapitulino@redhat.com, vrozenfe@redhat.com, pbonzini@redhat.com, seiji.aguchi@hds.com, lersek@redhat.com, areis@redhat.com 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 (.id= l), > VisualC++, MIDL and stdole2.tlb in Windows SDK are required. This patch a= lso > 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 proce= ss. > Qemu-ga.exe works without the DLL, although fsfreeze features are disable= d. > = > 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 > --- > 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 csco= pe.* *.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 +=3D 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 =3D qga/ qapi-types.o qapi-visit.o > +qga-vss-dll-obj-y =3D qga/ > = > vl.o: QEMU_CFLAGS+=3D$(GPROF_CFLAGS) > = > @@ -114,6 +115,7 @@ nested-vars +=3D \ > stub-obj-y \ > util-obj-y \ > qga-obj-y \ > + qga-vss-dll-obj-y \ > block-obj-y \ > common-obj-y > dummy :=3D $(call unnest-vars) > diff --git a/configure b/configure > index 48736b3..f1b7e4f 100755 > --- a/configure > +++ b/configure > @@ -3500,9 +3500,12 @@ if test "$softmmu" =3D yes ; then > virtfs=3Dno > fi > fi > - if [ "$linux" =3D "yes" -o "$bsd" =3D "yes" -o "$solaris" =3D "yes" ] = ; then > + if [ "$linux" =3D "yes" -o "$bsd" =3D "yes" -o "$solaris" =3D "yes" -o= "$mingw32" =3D "yes" ] ; then > if [ "$guest_agent" =3D "yes" ]; then > tools=3D"qemu-ga\$(EXESUF) $tools" > + if [ "$mingw32" =3D "yes" ]; then > + tools=3D"qga/vss-win32-provider/qga-provider.dll qga/vss-win32-p= rovider/qga-provider.tlb $tools" > + fi It looks like this hasn't been updated to reflect the change in directory a= nd tlb/dll filenames from an earlier version. Also, when CONFIG_QGA_VSS=3Dn, 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" =3D "yes" ]; then - tools=3D"qga/vss-win32-provider/qga-provider.dll qga/vss-win32-pro= vider/qga-provider.tlb $tools" + if [ "$mingw32" =3D "yes" -a "$guest_agent_with_vss" =3D "yes" ]; th= en + tools=3D"qga/vss-win32/qga-vss.dll qga/vss-win32/qga-vss.tlb $tool= s" > 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) +=3D commands-posix.o channel-pos= ix.o > qga-obj-$(CONFIG_WIN32) +=3D commands-win32.o channel-win32.o service-wi= n32.o > qga-obj-y +=3D qapi-generated/qga-qapi-types.o qapi-generated/qga-qapi-v= isit.o > qga-obj-y +=3D qapi-generated/qga-qmp-marshal.o > + > +ifeq ($(CONFIG_QGA_VSS),y) > +qga-vss-dll-obj-y +=3D vss-win32/ > +endif Small nit, but I think we generally prefer this form in qemu: qga-vss-dll-obj-$(CONFIG_QGA_VSS) +=3D 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 +=3D requester.o provider.o install.o > + > +obj-qga-vss-dll-obj-y =3D $(addprefix $(obj)/, $(qga-vss-dll-obj-y)) > +$(obj-qga-vss-dll-obj-y): QEMU_CXXFLAGS =3D $(filter-out -Wstrict-protot= ypes -Wmissing-prototypes -Wnested-externs -Wold-style-declaration -Wold-st= yle-definition -Wredundant-decls -fstack-protector-all, $(QEMU_CFLAGS)) -Wn= o-unknown-pragmas -Wno-delete-non-virtual-dtor > + > +$(obj)/qga-vss.dll: LDFLAGS =3D -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=3D$(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 $(TARGE= T_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 > + * > + * This work is licensed under the terms of the GNU GPL, version 2 or la= ter. > + * See the COPYING file in the top-level directory. > + */ > + > +#include > +#include > + > +#include "vss-common.h" > +#include "inc/win2003/vscoordint.h" > + > +#include > +#include > +#include > +#include > + > +extern HINSTANCE g_hinstDll; > + > +const GUID CLSID_COMAdminCatalog =3D { 0xF618C514, 0xDFB8, 0x11d1, > + {0xA2, 0xCF, 0x00, 0x80, 0x5F, 0xC7, 0x92, 0x35} }; > +const GUID IID_ICOMAdminCatalog =3D { 0xDD662187, 0xDFC2, 0x11d1, > + {0xA2, 0xCF, 0x00, 0x80, 0x5F, 0xC7, 0x92, 0x35} }; > +const GUID CLSID_WbemLocator =3D { 0x4590f811, 0x1d3a, 0x11d0, > + {0x89, 0x1f, 0x00, 0xaa, 0x00, 0x4b, 0x2e, 0x24} }; > +const GUID IID_IWbemLocator =3D { 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 =3D NULL, *nul =3D strchr(text, '('); > + int len =3D nul ? nul - text : -1; > + > + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | > + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INS= ERTS, > + 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 = =3D "") > +{ > + char *msg, buf[512]; > + > + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | > + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INS= ERTS, > + 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_ICON= ERROR); > + LocalFree(msg); > +} > + > +#define _chk(hr, status, msg, err_label) \ > + do { \ > + hr =3D (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 > +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 pLoc; > + COMPointer pSvc; > + COMPointer pEnum; > + COMPointer 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=3D'S-1-5-32-544' and localAccount= =3DTRUE"), > + WBEM_FLAG_RETURN_IMMEDIATELY | WBEM_FLAG_FORWARD= _ONLY, > + NULL, pEnum.replace())); > + if (!pEnum) { > + hr =3D E_FAIL; > + errmsg(hr, "Failed to query for Administrators"); > + goto out; > + } > + chk(pEnum->Next(WBEM_INFINITE, 1, pWobj.replace(), &returned)); > + if (returned =3D=3D 0) { > + hr =3D E_FAIL; > + errmsg(hr, "No Administrators found"); > + goto out; > + } > + > + chk(pWobj->Get(_bstr_t(L"Name"), 0, &var, 0, 0)); > + try { > + *name =3D var; > + } catch(...) { > + hr =3D 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 pUnknown; > + COMPointer pCatalog; > + COMPointer pColl; > + COMPointer pObj; > + _variant_t var; > + long i, n; > + > + chk(CoCreateInstance(CLSID_COMAdminCatalog, NULL, CLSCTX_INPROC_SERV= ER, > + 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 =3D n - 1; i >=3D 0; i--) { > + chk(pColl->get_Item(i, (IDispatch **)pObj.replace())); > + chk(pObj->get_Value(_bstr_t(L"Name"), &var)); > + if (var =3D=3D _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 *a= rg) > +{ > + (*(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 pUnknown; > + COMPointer pCatalog; > + COMPointer pApps, pRoles, pUsersInRole; > + COMPointer pObj; > + long n; > + _bstr_t name; > + _variant_t key; > + CHAR dllPath[MAX_PATH], tlbPath[MAX_PATH]; > + bool unregisterOnFailure =3D false; > + int count =3D 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_SERV= ER, > + 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 =3D true; > + > + chk(pObj->get_Key(&key)); > + > + if (!GetModuleFileName(g_hinstDll, dllPath, sizeof(dllPath))) { > + hr =3D HRESULT_FROM_WIN32(GetLastError()); > + errmsg(hr, "GetModuleFileName failed"); > + goto out; > + } > + n =3D strlen(dllPath); > + if (n < 3) { > + hr =3D 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 =3D 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 =3D RegCreateKeyEx(HKEY_CLASSES_ROOT, key, 0, NULL, > + REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, NULL); > + if (ret !=3D ERROR_SUCCESS) { > + goto out; > + } > + > + if (data !=3D NULL) { > + size =3D strlen(data) + 1; > + } else { > + size =3D 0; > + } > + > + ret =3D RegSetValueEx(hKey, value, 0, REG_SZ, (LPBYTE)data, size); > + RegCloseKey(hKey); > + > +out: > + if (ret !=3D 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 pVssAdmin; > + HRESULT hr =3D 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 =3D CoCreateInstance(CLSID_VSSCoordinator, NULL, CLSCTX_ALL, > + IID_IVssAdmin, (void **)pVssAdmin.replace()); > + if (FAILED(hr)) { > + errmsg_dialog(hr, "CoCreateInstance(VSSCoordinator) failed"); > + goto out; > + } > + > + hr =3D pVssAdmin->RegisterProvider(g_gProviderId, CLSID_QGAVSSProvid= er, > + const_cast(QGA_PROVIDER_LNA= ME), > + VSS_PROV_SOFTWARE, > + const_cast(QGA_PROVIDER_VER= SION), > + 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 pVssAdmin; > + > + HRESULT hr =3D CoCreateInstance(CLSID_VSSCoordinator, > + NULL, CLSCTX_ALL, IID_IVssAdmin, > + (void **)pVssAdmin.replace()); > + if (SUCCEEDED(hr)) { > + hr =3D 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 =3D strlen(ascii); > + BSTR bstr =3D SysAllocStringLen(NULL, len); > + > + if (!bstr) { > + return NULL; > + } > + > + if (mbstowcs(bstr, ascii, len) =3D=3D (size_t)-1) { > + fprintf(stderr, "Failed to convert string '%s' into BSTR", a= scii); > + bstr[0] =3D 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 > + * > + * This work is licensed under the terms of the GNU GPL, version 2 or la= ter. > + * See the COPYING file in the top-level directory. > + */ > + > +#include > +#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 =3D { 0xE579AB5F, 0x1CC4, 0x44b4, > + {0xBE, 0xD9, 0xDE, 0x09, 0x91, 0xFF, 0x06, 0x23} }; > +const IID IID_IVssAdmin =3D { 0x77ED5996, 0x2F63, 0x11d3, > + {0x8A, 0x39, 0x00, 0xC0, 0x4F, 0x72, 0xD8, 0xE3} }; > + > +const IID IID_IVssHardwareSnapshotProvider =3D { 0x9593A157, 0x44E9, 0x4= 344, > + {0xBB, 0xEB, 0x44, 0xFB, 0xF9, 0xB0, 0x6B, 0x10} }; > +const IID IID_IVssSoftwareSnapshotProvider =3D { 0x609e123e, 0x2c5a, 0x4= 4d3, > + {0x8f, 0x01, 0x0b, 0x1d, 0x9a, 0x47, 0xd1, 0xff} }; > +const IID IID_IVssProviderCreateSnapshotSet =3D { 0x5F894E5B, 0x1E39, 0x= 4778, > + {0x8E, 0x23, 0x9A, 0xBA, 0xD9, 0xF0, 0xE0, 0x8C} }; > +const IID IID_IVssProviderNotifications =3D { 0xE561901F, 0x03A5, 0x4afe, > + {0x86, 0xD0, 0x72, 0xBA, 0xEE, 0xCE, 0x70, 0x04} }; > + > +const IID IID_IVssEnumObject =3D { 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 =3D 0; > + LockModule(TRUE); > +} > + > +CQGAVSSEnumObject::~CQGAVSSEnumObject() > +{ > + LockModule(FALSE); > +} > + > +STDMETHODIMP CQGAVSSEnumObject::QueryInterface(REFIID riid, void **ppObj) > +{ > + if (riid =3D=3D IID_IUnknown || riid =3D=3D IID_IVssEnumObject) { > + *ppObj =3D static_cast(static_cast(this)= ); > + AddRef(); > + return S_OK; > + } > + *ppObj =3D NULL; > + return E_NOINTERFACE; > +} > + > +STDMETHODIMP_(ULONG) CQGAVSSEnumObject::AddRef() > +{ > + return InterlockedIncrement(&m_nRefCount); > +} > + > +STDMETHODIMP_(ULONG) CQGAVSSEnumObject::Release() > +{ > + long nRefCount =3D InterlockedDecrement(&m_nRefCount); > + if (m_nRefCount =3D=3D 0) { > + delete this; > + } > + return nRefCount; > +} > + > +STDMETHODIMP CQGAVSSEnumObject::Next( > + ULONG celt, VSS_OBJECT_PROP *rgelt, ULONG *pceltFetched) > +{ > + *pceltFetched =3D 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 **ppAs= ync); > + > + /* 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 =3D 0; > + LockModule(TRUE); > +} > + > +CQGAVssProvider::~CQGAVssProvider() > +{ > + LockModule(FALSE); > +} > + > +STDMETHODIMP CQGAVssProvider::QueryInterface(REFIID riid, void **ppObj) > +{ > + if (riid =3D=3D IID_IUnknown) { > + *ppObj =3D static_cast(this); > + AddRef(); > + return S_OK; > + } > + if (riid =3D=3D IID_IVssSoftwareSnapshotProvider) { > + *ppObj =3D static_cast( > + static_cast(this)); > + AddRef(); > + return S_OK; > + } > + if (riid =3D=3D IID_IVssProviderCreateSnapshotSet) { > + *ppObj =3D static_cast( > + static_cast(this)); > + AddRef(); > + return S_OK; > + } > + if (riid =3D=3D IID_IVssProviderNotifications) { > + *ppObj =3D static_cast( > + static_cast(this)); > + AddRef(); > + return S_OK; > + } > + *ppObj =3D NULL; > + return E_NOINTERFACE; > +} > + > +STDMETHODIMP_(ULONG) CQGAVssProvider::AddRef() > +{ > + return InterlockedIncrement(&m_nRefCount); > +} > + > +STDMETHODIMP_(ULONG) CQGAVssProvider::Release() > +{ > + long nRefCount =3D InterlockedDecrement(&m_nRefCount); > + if (m_nRefCount =3D=3D 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 =3D 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 *pNondeletedSnap= shotID) > +{ > + 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 =3D TRUE; > + > + return S_OK; > +} > + > +STDMETHODIMP CQGAVssProvider::IsVolumeSnapshotted(VSS_PWSZ pwszVolumeNam= e, > + BOOL *pbSnapshotsPresent, LONG *plSnapshotCompatibility) > +{ > + *pbSnapshotsPresent =3D FALSE; > + *plSnapshotCompatibility =3D 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 =3D S_OK; > + HANDLE hEventFrozen, hEventThaw; > + > + hEventFrozen =3D OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_FROZE= N); > + if (hEventFrozen =3D=3D INVALID_HANDLE_VALUE) { > + return E_FAIL; > + } > + > + hEventThaw =3D OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_THAW); > + if (hEventThaw =3D=3D 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) !=3D WAIT_OBJE= CT_0) { > + hr =3D E_ABORT; > + } > + > + CloseHandle(hEventThaw); > + CloseHandle(hEventFrozen); > + return hr; > +} > + > +STDMETHODIMP CQGAVssProvider::PostCommitSnapshots( > + VSS_ID SnapshotSetId, LONG lSnapshotsCount) > +{ > + return S_OK; > +} > + > +STDMETHODIMP CQGAVssProvider::PreFinalCommitSnapshots(VSS_ID SnapshotSet= Id) > +{ > + return S_OK; > +} > + > +STDMETHODIMP CQGAVssProvider::PostFinalCommitSnapshots(VSS_ID SnapshotSe= tId) > +{ > + 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 =3D 0; > + LockModule(TRUE); > +} > + > +CQGAVssProviderFactory::~CQGAVssProviderFactory() > +{ > + LockModule(FALSE); > +} > + > +STDMETHODIMP CQGAVssProviderFactory::QueryInterface(REFIID riid, void **= ppv) > +{ > + if (riid =3D=3D IID_IUnknown || riid =3D=3D IID_IClassFactory) { > + *ppv =3D static_cast(this); > + AddRef(); > + return S_OK; > + } > + *ppv =3D NULL; > + return E_NOINTERFACE; > +} > + > +STDMETHODIMP_(ULONG) CQGAVssProviderFactory::AddRef() > +{ > + return InterlockedIncrement(&m_nRefCount); > +} > + > +STDMETHODIMP_(ULONG) CQGAVssProviderFactory::Release() > +{ > + long nRefCount =3D InterlockedDecrement(&m_nRefCount); > + if (m_nRefCount =3D=3D 0) { > + delete this; > + } > + return nRefCount; > +} > + > +STDMETHODIMP CQGAVssProviderFactory::CreateInstance( > + IUnknown *pUnknownOuter, REFIID iid, void **ppv) > +{ > + CQGAVssProvider *pObj; > + > + if (pUnknownOuter) { > + return CLASS_E_NOAGGREGATION; > + } > + try { > + pObj =3D new CQGAVssProvider; > + } catch (...) { > + return E_OUTOFMEMORY; > + } > + HRESULT hr =3D 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 =3D new CQGAVssProviderFactory; > + } catch (...) { > + return E_OUTOFMEMORY; > + } > + factory->AddRef(); > + HRESULT hr =3D factory->QueryInterface(riid, ppv); > + factory->Release(); > + return hr; > +} > + > +STDAPI DllCanUnloadNow() > +{ > + return g_nComObjsInUse =3D=3D 0 ? S_OK : S_FALSE; > +} > + > +EXTERN_C > +BOOL WINAPI DllMain(HINSTANCE hinstDll, DWORD dwReason, LPVOID lpReserve= d) > +{ > + if (dwReason =3D=3D DLL_PROCESS_ATTACH) { > + g_hinstDll =3D 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..226452a1861371ffe0cad1019= cf90fdfdcd5ef49 > GIT binary patch > literal 1528 > zcmeYbb_-!*U}OLRP8Kl5;0UB3A_y8H!@$4 zKahR`)X0Ox{{ZC6An~sN`2tA%H9-9hNPHcj{0byK4>OPh6a(1_GM|T)fx!kz-UG zK;nbc0l9GX=3D=3D=3Dtt`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 z6{hYgzgH7@QE<^MU>KC!yoBjb#vz`9Lwu4+R-SJ!kIOX4xLBUUGN9-NyTyP76j}@n > z2f!qQ8-xepfXD+7rW+{SKz4&@7#qX~^1#sn3O|tFK>%ciE< zNbM*XJ|Un0jALSb15^rEE6h+Y8tCpA798vm9#E8DmYI@T zfvHyrsVqoU0O~4AEUE;iEfI8i=3DbXgi;_z?|20Ng!&PAz-C8;S2NtFt|o-RHLWvNBQ > znfZAN=3D6VJOdIqMZrV5EA3T{Q23NES13Py$shQ?OLW>&_Q3PuKoMqI)S5zj9Ngog_=3D > 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 > + * > + * This work is licensed under the terms of the GNU GPL, version 2 or la= ter. > + * See the COPYING file in the top-level directory. > + */ > + > +#include > +#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 secon= ds) */ > +#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_PRO= P*); > +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 operati= on */ > + 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 =3D vss_ctx.hEventThaw =3D INVALID_HANDLE_VALUE; > + > + COMInitializer initializer; /* to call CoInitializeSecurity */ > + HRESULT hr =3D 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 =3D LoadLibraryA("VSSAPI.DLL"); > + if (!hLib) { > + fprintf(stderr, "failed to load VSSAPI.DLL\n"); > + return HRESULT_FROM_WIN32(GetLastError()); > + } > + > + pCreateVssBackupComponents =3D (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 =3D (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 !=3D INVALID_HANDLE_VALUE) { > + CloseHandle(vss_ctx.hEventFrozen); > + vss_ctx.hEventFrozen =3D INVALID_HANDLE_VALUE; > + } > + if (vss_ctx.hEventThaw !=3D INVALID_HANDLE_VALUE) { > + CloseHandle(vss_ctx.hEventThaw); > + vss_ctx.hEventThaw =3D INVALID_HANDLE_VALUE; > + } > + if (vss_ctx.pAsyncSnapshot) { > + vss_ctx.pAsyncSnapshot->Release(); > + vss_ctx.pAsyncSnapshot =3D NULL; > + } > + if (vss_ctx.pVssbc) { > + vss_ctx.pVssbc->Release(); > + vss_ctx.pVssbc =3D NULL; > + } > + vss_ctx.cFrozenVols =3D 0; > +} > + > +STDAPI requester_deinit(void) > +{ > + requester_cleanup(); > + > + pCreateVssBackupComponents =3D NULL; > + pVssFreeSnapshotProperties =3D NULL; > + if (hLib) { > + FreeLibrary(hLib); > + hLib =3D NULL; > + } > + > + return S_OK; > +} > + > +static HRESULT WaitForAsync(IVssAsync *pAsync) > +{ > + HRESULT ret, hr; > + > + do { > + hr =3D pAsync->Wait(); > + if (FAILED(hr)) { > + ret =3D hr; > + break; > + } > + hr =3D pAsync->QueryStatus(&ret, NULL); > + if (FAILED(hr)) { > + ret =3D hr; > + break; > + } > + } while (ret =3D=3D VSS_S_ASYNC_PENDING); > + > + return ret; > +} > + > +static void AddComponents(ErrorSet *errset) > +{ > + unsigned int cWriters, i; > + VSS_ID id, idInstance, idWriter; > + BSTR bstrWriterName =3D NULL; > + VSS_USAGE_TYPE usage; > + VSS_SOURCE_TYPE source; > + unsigned int cComponents, c1, c2, j; > + COMPointer pMetadata; > + COMPointer pComponent; > + PVSSCOMPONENTINFO info; > + HRESULT hr; > + > + hr =3D vss_ctx.pVssbc->GetWriterMetadataCount(&cWriters); > + if (FAILED(hr)) { > + err_set(errset, hr, "failed to get writer metadata count"); > + goto out; > + } > + > + for (i =3D 0; i < cWriters; i++) { > + hr =3D vss_ctx.pVssbc->GetWriterMetadata(i, &id, pMetadata.repla= ce()); > + if (FAILED(hr)) { > + err_set(errset, hr, "failed to get writer metadata of %d/%d", > + i, cWriters); > + goto out; > + } > + > + hr =3D 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 =3D pMetadata->GetFileCounts(&c1, &c2, &cComponents); > + if (FAILED(hr)) { > + err_set(errset, hr, "failed to get file counts of %S", > + bstrWriterName); > + goto out; > + } > + > + for (j =3D 0; j < cComponents; j++) { > + hr =3D 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 =3D 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 =3D vss_ctx.pVssbc->AddComponent(idInstance, idWriter, > + info->type, > + info->bstrLogicalPath, > + info->bstrComponentNam= e); > + if (FAILED(hr)) { > + err_set(errset, hr, "failed to add component %S(%S)", > + info->bstrComponentName, bstrWriter= Name); > + goto out; > + } > + } > + SysFreeString(bstrWriterName); > + bstrWriterName =3D NULL; > + pComponent->FreeComponentInfo(info); > + info =3D NULL; > + } > + } > +out: > + if (bstrWriterName) { > + SysFreeString(bstrWriterName); > + } > + if (pComponent && info) { > + pComponent->FreeComponentInfo(info); > + } > +} > + > +void requester_freeze(int *num_vols, ErrorSet *errset) > +{ > + COMPointer pAsync; > + HANDLE volume; > + HRESULT hr; > + LONG ctx; > + GUID guidSnapshotSet =3D GUID_NULL; > + SECURITY_DESCRIPTOR sd; > + SECURITY_ATTRIBUTES sa; > + WCHAR short_volume_name[64], *display_name =3D short_volume_name; > + DWORD wait_status; > + int num_fixed_drives =3D 0, i; > + > + if (vss_ctx.pVssbc) { /* already frozen */ > + *num_vols =3D 0; > + return; > + } > + > + CoInitialize(NULL); > + > + assert(pCreateVssBackupComponents !=3D NULL); > + hr =3D pCreateVssBackupComponents(&vss_ctx.pVssbc); > + if (FAILED(hr)) { > + err_set(errset, hr, "failed to create VSS backup components"); > + goto out; > + } > + > + hr =3D vss_ctx.pVssbc->InitializeForBackup(); > + if (FAILED(hr)) { > + err_set(errset, hr, "failed to initialize for backup"); > + goto out; > + } > + > + hr =3D 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 =3D VSS_CTX_APP_ROLLBACK | VSS_VOLSNAP_ATTR_TRANSPORTABLE | > + VSS_VOLSNAP_ATTR_NO_AUTORECOVERY | VSS_VOLSNAP_ATTR_TXF_RECOVERY; > + hr =3D vss_ctx.pVssbc->SetContext(ctx); > + if (hr =3D=3D (HRESULT)VSS_E_UNSUPPORTED_CONTEXT) { > + /* Non-server version of Windows doesn't support ATTR_TRANSPORTA= BLE */ > + ctx &=3D ~VSS_VOLSNAP_ATTR_TRANSPORTABLE; > + hr =3D vss_ctx.pVssbc->SetContext(ctx); > + } > + if (FAILED(hr)) { > + err_set(errset, hr, "failed to set backup context"); > + goto out; > + } > + > + hr =3D vss_ctx.pVssbc->GatherWriterMetadata(pAsync.replace()); > + if (SUCCEEDED(hr)) { > + hr =3D 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 =3D vss_ctx.pVssbc->StartSnapshotSet(&guidSnapshotSet); > + if (FAILED(hr)) { > + err_set(errset, hr, "failed to start snapshot set"); > + goto out; > + } > + > + volume =3D FindFirstVolumeW(short_volume_name, sizeof(short_volume_n= ame)); > + if (volume =3D=3D INVALID_HANDLE_VALUE) { > + err_set(errset, hr, "failed to find first volume"); > + goto out; > + } > + for (;;) { > + if (GetDriveTypeW(short_volume_name) =3D=3D DRIVE_FIXED) { > + VSS_ID pid; > + hr =3D 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 =3D 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 =3D=3D 0) { > + goto out; /* If there is no fixed drive, just exit. */ > + } > + > + hr =3D vss_ctx.pVssbc->PrepareForBackup(pAsync.replace()); > + if (SUCCEEDED(hr)) { > + hr =3D WaitForAsync(pAsync); > + } > + if (FAILED(hr)) { > + err_set(errset, hr, "failed to prepare for backup"); > + goto out; > + } > + > + hr =3D vss_ctx.pVssbc->GatherWriterStatus(pAsync.replace()); > + if (SUCCEEDED(hr)) { > + hr =3D 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 =3D sizeof(sa); > + sa.lpSecurityDescriptor =3D &sd; > + sa.bInheritHandle =3D FALSE; > + > + vss_ctx.hEventFrozen =3D CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_FR= OZEN); > + if (vss_ctx.hEventFrozen =3D=3D INVALID_HANDLE_VALUE) { > + err_set(errset, GetLastError(), "failed to create event %s", > + EVENT_NAME_FROZEN); > + goto out; > + } > + vss_ctx.hEventThaw =3D CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_THAW= ); > + if (vss_ctx.hEventThaw =3D=3D 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 =3D 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 progr= ess */ > + for (i =3D 0; i < VSS_TIMEOUT_FREEZE_MSEC/VSS_TIMEOUT_EVENT_MSEC; i+= +) { > + HRESULT hr2 =3D vss_ctx.pAsyncSnapshot->QueryStatus(&hr, NULL); > + if (FAILED(hr2)) { > + err_set(errset, hr, "failed to do snapshot set"); > + goto out; > + } > + if (hr !=3D VSS_S_ASYNC_PENDING) { > + err_set(errset, E_FAIL, > + "DoSnapshotSet exited without Frozen event"); > + goto out; > + } > + wait_status =3D WaitForSingleObject(vss_ctx.hEventFrozen, > + VSS_TIMEOUT_EVENT_MSEC); > + if (wait_status !=3D WAIT_TIMEOUT) { > + break; > + } > + } > + if (wait_status !=3D WAIT_OBJECT_0) { > + err_set(errset, E_FAIL, > + "Couldn't receive Frozen event from VSS provider"); > + goto out; > + } > + > + *num_vols =3D vss_ctx.cFrozenVols =3D 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 pAsync; > + > + if (vss_ctx.hEventThaw =3D=3D 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 =3D 0; > + return; > + } > + > + /* Tell the provider that the snapshot is finished. */ > + SetEvent(vss_ctx.hEventThaw); > + > + assert(vss_ctx.pVssbc); > + assert(vss_ctx.pAsyncSnapshot); > + > + HRESULT hr =3D WaitForAsync(vss_ctx.pAsyncSnapshot); > + switch (hr) { > + case VSS_S_ASYNC_FINISHED: > + hr =3D vss_ctx.pVssbc->BackupComplete(pAsync.replace()); > + if (SUCCEEDED(hr)) { > + hr =3D 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 is= sued, > + * 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 =3D 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 > + * > + * This work is licensed under the terms of the GNU GPL, version 2 or la= ter. > + * 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 > + * > + * This work is licensed under the terms of the GNU GPL, version 2 or la= ter. > + * 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 > +#include > + > +/* 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=3D23490 > + */ > +#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 =3D { 0x3629d4ed, 0xee09, 0x4e0e, > + {0x9a, 0x5c, 0x6d, 0x8b, 0xa2, 0x87, 0x2a, 0xef} }; > +const GUID g_gProviderVersion =3D { 0x11ef8b15, 0xcac6, 0x40d6, > + {0x8d, 0x5c, 0x8f, 0xfc, 0x16, 0x3f, 0x24, 0xca} }; > + > +const CLSID CLSID_QGAVSSProvider =3D { 0x6e6a3492, 0x8d4d, 0x440c, > + {0x96, 0x19, 0x5e, 0x5d, 0x0c, 0xc3, 0x1c, 0xa8} }; > + > +const TCHAR g_szClsid[] =3D TEXT("{6E6A3492-8D4D-440C-9619-5E5D0CC31CA8}= "); > +const TCHAR g_szProgid[] =3D 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 =3D 0x00000002, > + VSS_VOLSNAP_ATTR_TXF_RECOVERY =3D 0x02000000 > +}; > + > + > +/* COM pointer utility; call ->Release() when it goes out of scope */ > +template > +class COMPointer { > + COMPointer(const COMPointer &p) { } /* no copy */ > + T *p; > +public: > + COMPointer &operator=3D(T *new_p) > + { > + /* Assignment of a new T* (or NULL) causes release of previous p= */ > + if (p && p !=3D new_p) { > + p->Release(); > + } > + p =3D new_p; > + return *this; > + } > + /* Replace by assignment to the pointer of p */ > + T **replace(void) > + { > + *this =3D 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 =3D NULL) : p(p) { } > + ~COMPointer() { *this =3D NULL; } /* Automatic release */ > +}; > + > +/* > + * COM initializer; this should declared before COMPointer to uninitiali= ze COM > + * after releasing COM objects. > + */ > +class COMInitializer { > +public: > + COMInitializer() { CoInitialize(NULL); } > + ~COMInitializer() { CoUninitialize(); } > +}; > + > +#endif