aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--Makefile.objs2
-rwxr-xr-xconfigure5
-rw-r--r--qga/Makefile.objs2
-rw-r--r--qga/vss-win32/Makefile.objs23
-rw-r--r--qga/vss-win32/install.cpp458
-rw-r--r--qga/vss-win32/provider.cpp523
-rw-r--r--qga/vss-win32/qga-vss.def13
-rw-r--r--qga/vss-win32/qga-vss.idl20
-rw-r--r--qga/vss-win32/qga-vss.tlbbin0 -> 1528 bytes
-rw-r--r--qga/vss-win32/requester.cpp507
-rw-r--r--qga/vss-win32/requester.h42
-rw-r--r--qga/vss-win32/vss-common.h129
13 files changed, 1724 insertions, 2 deletions
diff --git a/Makefile b/Makefile
index 2fb0e5a879..362fe3e66e 100644
--- a/Makefile
+++ b/Makefile
@@ -235,7 +235,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 f46a4cdd6a..2b6c1fe2a8 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -109,6 +109,7 @@ version-lobj-$(CONFIG_WIN32) += $(BUILD_DIR)/version.lo
# 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)
@@ -120,6 +121,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 abc29ab3d1..cf61f75823 100755
--- a/configure
+++ b/configure
@@ -3568,8 +3568,11 @@ if test "$softmmu" = yes ; then
fi
fi
if [ "$guest_agent" != "no" ]; then
- if [ "$linux" = "yes" -o "$bsd" = "yes" -o "$solaris" = "yes" ] ; then
+ if [ "$linux" = "yes" -o "$bsd" = "yes" -o "$solaris" = "yes" -o "$mingw32" = "yes" ] ; then
tools="qemu-ga\$(EXESUF) $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
guest_agent=yes
elif [ "$guest_agent" != yes ]; then
guest_agent=no
diff --git a/qga/Makefile.objs b/qga/Makefile.objs
index b8d7cd0a43..c4bd151845 100644
--- a/qga/Makefile.objs
+++ b/qga/Makefile.objs
@@ -3,3 +3,5 @@ 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
+
+qga-vss-dll-obj-$(CONFIG_QGA_VSS) += vss-win32/
diff --git a/qga/vss-win32/Makefile.objs b/qga/vss-win32/Makefile.objs
new file mode 100644
index 0000000000..6a69d5008d
--- /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 ($(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
diff --git a/qga/vss-win32/install.cpp b/qga/vss-win32/install.cpp
new file mode 100644
index 0000000000..37731a7271
--- /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 0000000000..bf42b5e95f
--- /dev/null
+++ b/qga/vss-win32/provider.cpp
@@ -0,0 +1,523 @@
+/*
+ * QEMU Guest Agent win32 VSS Provider implementations
+ *
+ * Copyright Hitachi Data Systems Corp. 2013
+ *
+ * Authors:
+ * Tomoki Sekiyama <tomoki.sekiyama@hds.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include <stdio.h>
+#include "vss-common.h"
+#include "inc/win2003/vscoordint.h"
+#include "inc/win2003/vsprov.h"
+
+#define VSS_TIMEOUT_MSEC (60*1000)
+
+static long g_nComObjsInUse;
+HINSTANCE g_hinstDll;
+
+/* VSS common GUID's */
+
+const CLSID CLSID_VSSCoordinator = { 0xE579AB5F, 0x1CC4, 0x44b4,
+ {0xBE, 0xD9, 0xDE, 0x09, 0x91, 0xFF, 0x06, 0x23} };
+const IID IID_IVssAdmin = { 0x77ED5996, 0x2F63, 0x11d3,
+ {0x8A, 0x39, 0x00, 0xC0, 0x4F, 0x72, 0xD8, 0xE3} };
+
+const IID IID_IVssHardwareSnapshotProvider = { 0x9593A157, 0x44E9, 0x4344,
+ {0xBB, 0xEB, 0x44, 0xFB, 0xF9, 0xB0, 0x6B, 0x10} };
+const IID IID_IVssSoftwareSnapshotProvider = { 0x609e123e, 0x2c5a, 0x44d3,
+ {0x8f, 0x01, 0x0b, 0x1d, 0x9a, 0x47, 0xd1, 0xff} };
+const IID IID_IVssProviderCreateSnapshotSet = { 0x5F894E5B, 0x1E39, 0x4778,
+ {0x8E, 0x23, 0x9A, 0xBA, 0xD9, 0xF0, 0xE0, 0x8C} };
+const IID IID_IVssProviderNotifications = { 0xE561901F, 0x03A5, 0x4afe,
+ {0x86, 0xD0, 0x72, 0xBA, 0xEE, 0xCE, 0x70, 0x04} };
+
+const IID IID_IVssEnumObject = { 0xAE1C7110, 0x2F60, 0x11d3,
+ {0x8A, 0x39, 0x00, 0xC0, 0x4F, 0x72, 0xD8, 0xE3} };
+
+
+void LockModule(BOOL lock)
+{
+ if (lock) {
+ InterlockedIncrement(&g_nComObjsInUse);
+ } else {
+ InterlockedDecrement(&g_nComObjsInUse);
+ }
+}
+
+/* Empty enumerator for VssObject */
+
+class CQGAVSSEnumObject : public IVssEnumObject
+{
+public:
+ STDMETHODIMP QueryInterface(REFIID riid, void **ppObj);
+ STDMETHODIMP_(ULONG) AddRef();
+ STDMETHODIMP_(ULONG) Release();
+
+ /* IVssEnumObject Methods */
+ STDMETHODIMP Next(
+ ULONG celt, VSS_OBJECT_PROP *rgelt, ULONG *pceltFetched);
+ STDMETHODIMP Skip(ULONG celt);
+ STDMETHODIMP Reset(void);
+ STDMETHODIMP Clone(IVssEnumObject **ppenum);
+
+ /* CQGAVSSEnumObject Methods */
+ CQGAVSSEnumObject();
+ ~CQGAVSSEnumObject();
+
+private:
+ long m_nRefCount;
+};
+
+CQGAVSSEnumObject::CQGAVSSEnumObject()
+{
+ m_nRefCount = 0;
+ LockModule(TRUE);
+}
+
+CQGAVSSEnumObject::~CQGAVSSEnumObject()
+{
+ LockModule(FALSE);
+}
+
+STDMETHODIMP CQGAVSSEnumObject::QueryInterface(REFIID riid, void **ppObj)
+{
+ if (riid == IID_IUnknown || riid == IID_IVssEnumObject) {
+ *ppObj = static_cast<void*>(static_cast<IVssEnumObject*>(this));
+ AddRef();
+ return S_OK;
+ }
+ *ppObj = NULL;
+ return E_NOINTERFACE;
+}
+
+STDMETHODIMP_(ULONG) CQGAVSSEnumObject::AddRef()
+{
+ return InterlockedIncrement(&m_nRefCount);
+}
+
+STDMETHODIMP_(ULONG) CQGAVSSEnumObject::Release()
+{
+ long nRefCount = InterlockedDecrement(&m_nRefCount);
+ if (m_nRefCount == 0) {
+ delete this;
+ }
+ return nRefCount;
+}
+
+STDMETHODIMP CQGAVSSEnumObject::Next(
+ ULONG celt, VSS_OBJECT_PROP *rgelt, ULONG *pceltFetched)
+{
+ *pceltFetched = 0;
+ return S_FALSE;
+}
+
+STDMETHODIMP CQGAVSSEnumObject::Skip(ULONG celt)
+{
+ return S_FALSE;
+}
+
+STDMETHODIMP CQGAVSSEnumObject::Reset(void)
+{
+ return S_OK;
+}
+
+STDMETHODIMP CQGAVSSEnumObject::Clone(IVssEnumObject **ppenum)
+{
+ return E_NOTIMPL;
+}
+
+
+/* QGAVssProvider */
+
+class CQGAVssProvider :
+ public IVssSoftwareSnapshotProvider,
+ public IVssProviderCreateSnapshotSet,
+ public IVssProviderNotifications
+{
+public:
+ STDMETHODIMP QueryInterface(REFIID riid, void **ppObj);
+ STDMETHODIMP_(ULONG) AddRef();
+ STDMETHODIMP_(ULONG) Release();
+
+ /* IVssSoftwareSnapshotProvider Methods */
+ STDMETHODIMP SetContext(LONG lContext);
+ STDMETHODIMP GetSnapshotProperties(
+ VSS_ID SnapshotId, VSS_SNAPSHOT_PROP *pProp);
+ STDMETHODIMP Query(
+ VSS_ID QueriedObjectId, VSS_OBJECT_TYPE eQueriedObjectType,
+ VSS_OBJECT_TYPE eReturnedObjectsType, IVssEnumObject **ppEnum);
+ STDMETHODIMP DeleteSnapshots(
+ VSS_ID SourceObjectId, VSS_OBJECT_TYPE eSourceObjectType,
+ BOOL bForceDelete, LONG *plDeletedSnapshots,
+ VSS_ID *pNondeletedSnapshotID);
+ STDMETHODIMP BeginPrepareSnapshot(
+ VSS_ID SnapshotSetId, VSS_ID SnapshotId,
+ VSS_PWSZ pwszVolumeName, LONG lNewContext);
+ STDMETHODIMP IsVolumeSupported(
+ VSS_PWSZ pwszVolumeName, BOOL *pbSupportedByThisProvider);
+ STDMETHODIMP IsVolumeSnapshotted(
+ VSS_PWSZ pwszVolumeName, BOOL *pbSnapshotsPresent,
+ LONG *plSnapshotCompatibility);
+ STDMETHODIMP SetSnapshotProperty(
+ VSS_ID SnapshotId, VSS_SNAPSHOT_PROPERTY_ID eSnapshotPropertyId,
+ VARIANT vProperty);
+ STDMETHODIMP RevertToSnapshot(VSS_ID SnapshotId);
+ STDMETHODIMP QueryRevertStatus(VSS_PWSZ pwszVolume, IVssAsync **ppAsync);
+
+ /* IVssProviderCreateSnapshotSet Methods */
+ STDMETHODIMP EndPrepareSnapshots(VSS_ID SnapshotSetId);
+ STDMETHODIMP PreCommitSnapshots(VSS_ID SnapshotSetId);
+ STDMETHODIMP CommitSnapshots(VSS_ID SnapshotSetId);
+ STDMETHODIMP PostCommitSnapshots(
+ VSS_ID SnapshotSetId, LONG lSnapshotsCount);
+ STDMETHODIMP PreFinalCommitSnapshots(VSS_ID SnapshotSetId);
+ STDMETHODIMP PostFinalCommitSnapshots(VSS_ID SnapshotSetId);
+ STDMETHODIMP AbortSnapshots(VSS_ID SnapshotSetId);
+
+ /* IVssProviderNotifications Methods */
+ STDMETHODIMP OnLoad(IUnknown *pCallback);
+ STDMETHODIMP OnUnload(BOOL bForceUnload);
+
+ /* CQGAVssProvider Methods */
+ CQGAVssProvider();
+ ~CQGAVssProvider();
+
+private:
+ long m_nRefCount;
+};
+
+CQGAVssProvider::CQGAVssProvider()
+{
+ m_nRefCount = 0;
+ LockModule(TRUE);
+}
+
+CQGAVssProvider::~CQGAVssProvider()
+{
+ LockModule(FALSE);
+}
+
+STDMETHODIMP CQGAVssProvider::QueryInterface(REFIID riid, void **ppObj)
+{
+ if (riid == IID_IUnknown) {
+ *ppObj = static_cast<void*>(this);
+ AddRef();
+ return S_OK;
+ }
+ if (riid == IID_IVssSoftwareSnapshotProvider) {
+ *ppObj = static_cast<void*>(
+ static_cast<IVssSoftwareSnapshotProvider*>(this));
+ AddRef();
+ return S_OK;
+ }
+ if (riid == IID_IVssProviderCreateSnapshotSet) {
+ *ppObj = static_cast<void*>(
+ static_cast<IVssProviderCreateSnapshotSet*>(this));
+ AddRef();
+ return S_OK;
+ }
+ if (riid == IID_IVssProviderNotifications) {
+ *ppObj = static_cast<void*>(
+ static_cast<IVssProviderNotifications*>(this));
+ AddRef();
+ return S_OK;
+ }
+ *ppObj = NULL;
+ return E_NOINTERFACE;
+}
+
+STDMETHODIMP_(ULONG) CQGAVssProvider::AddRef()
+{
+ return InterlockedIncrement(&m_nRefCount);
+}
+
+STDMETHODIMP_(ULONG) CQGAVssProvider::Release()
+{
+ long nRefCount = InterlockedDecrement(&m_nRefCount);
+ if (m_nRefCount == 0) {
+ delete this;
+ }
+ return nRefCount;
+}
+
+
+/*
+ * IVssSoftwareSnapshotProvider methods
+ */
+
+STDMETHODIMP CQGAVssProvider::SetContext(LONG lContext)
+{
+ return S_OK;
+}
+
+STDMETHODIMP CQGAVssProvider::GetSnapshotProperties(
+ VSS_ID SnapshotId, VSS_SNAPSHOT_PROP *pProp)
+{
+ return VSS_E_OBJECT_NOT_FOUND;
+}
+
+STDMETHODIMP CQGAVssProvider::Query(
+ VSS_ID QueriedObjectId, VSS_OBJECT_TYPE eQueriedObjectType,
+ VSS_OBJECT_TYPE eReturnedObjectsType, IVssEnumObject **ppEnum)
+{
+ try {
+ *ppEnum = new CQGAVSSEnumObject;
+ } catch (...) {
+ return E_OUTOFMEMORY;
+ }
+ (*ppEnum)->AddRef();
+ return S_OK;
+}
+
+STDMETHODIMP CQGAVssProvider::DeleteSnapshots(
+ VSS_ID SourceObjectId, VSS_OBJECT_TYPE eSourceObjectType,
+ BOOL bForceDelete, LONG *plDeletedSnapshots, VSS_ID *pNondeletedSnapshotID)
+{
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP CQGAVssProvider::BeginPrepareSnapshot(
+ VSS_ID SnapshotSetId, VSS_ID SnapshotId,
+ VSS_PWSZ pwszVolumeName, LONG lNewContext)
+{
+ return S_OK;
+}
+
+STDMETHODIMP CQGAVssProvider::IsVolumeSupported(
+ VSS_PWSZ pwszVolumeName, BOOL *pbSupportedByThisProvider)
+{
+ *pbSupportedByThisProvider = TRUE;
+
+ return S_OK;
+}
+
+STDMETHODIMP CQGAVssProvider::IsVolumeSnapshotted(VSS_PWSZ pwszVolumeName,
+ BOOL *pbSnapshotsPresent, LONG *plSnapshotCompatibility)
+{
+ *pbSnapshotsPresent = FALSE;
+ *plSnapshotCompatibility = 0;
+ return S_OK;
+}
+
+STDMETHODIMP CQGAVssProvider::SetSnapshotProperty(VSS_ID SnapshotId,
+ VSS_SNAPSHOT_PROPERTY_ID eSnapshotPropertyId, VARIANT vProperty)
+{
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP CQGAVssProvider::RevertToSnapshot(VSS_ID SnapshotId)
+{
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP CQGAVssProvider::QueryRevertStatus(
+ VSS_PWSZ pwszVolume, IVssAsync **ppAsync)
+{
+ return E_NOTIMPL;
+}
+
+
+/*
+ * IVssProviderCreateSnapshotSet methods
+ */
+
+STDMETHODIMP CQGAVssProvider::EndPrepareSnapshots(VSS_ID SnapshotSetId)
+{
+ return S_OK;
+}
+
+STDMETHODIMP CQGAVssProvider::PreCommitSnapshots(VSS_ID SnapshotSetId)
+{
+ return S_OK;
+}
+
+STDMETHODIMP CQGAVssProvider::CommitSnapshots(VSS_ID SnapshotSetId)
+{
+ HRESULT hr = S_OK;
+ HANDLE hEventFrozen, hEventThaw, hEventTimeout;
+
+ hEventFrozen = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_FROZEN);
+ if (hEventFrozen == INVALID_HANDLE_VALUE) {
+ return E_FAIL;
+ }
+
+ hEventThaw = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_THAW);
+ if (hEventThaw == INVALID_HANDLE_VALUE) {
+ CloseHandle(hEventFrozen);
+ return E_FAIL;
+ }
+
+ hEventTimeout = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_TIMEOUT);
+ if (hEventTimeout == INVALID_HANDLE_VALUE) {
+ CloseHandle(hEventFrozen);
+ CloseHandle(hEventThaw);
+ return E_FAIL;
+ }
+
+ /* Send event to qemu-ga to notify filesystem is frozen */
+ SetEvent(hEventFrozen);
+
+ /* Wait until the snapshot is taken by the host. */
+ if (WaitForSingleObject(hEventThaw, VSS_TIMEOUT_MSEC) != WAIT_OBJECT_0) {
+ /* Send event to qemu-ga to notify the provider is timed out */
+ SetEvent(hEventTimeout);
+ hr = E_ABORT;
+ }
+
+ CloseHandle(hEventThaw);
+ CloseHandle(hEventFrozen);
+ CloseHandle(hEventTimeout);
+ return hr;
+}
+
+STDMETHODIMP CQGAVssProvider::PostCommitSnapshots(
+ VSS_ID SnapshotSetId, LONG lSnapshotsCount)
+{
+ return S_OK;
+}
+
+STDMETHODIMP CQGAVssProvider::PreFinalCommitSnapshots(VSS_ID SnapshotSetId)
+{
+ return S_OK;
+}
+
+STDMETHODIMP CQGAVssProvider::PostFinalCommitSnapshots(VSS_ID SnapshotSetId)
+{
+ return S_OK;
+}
+
+STDMETHODIMP CQGAVssProvider::AbortSnapshots(VSS_ID SnapshotSetId)
+{
+ return S_OK;
+}
+
+/*
+ * IVssProviderNotifications methods
+ */
+
+STDMETHODIMP CQGAVssProvider::OnLoad(IUnknown *pCallback)
+{
+ return S_OK;
+}
+
+STDMETHODIMP CQGAVssProvider::OnUnload(BOOL bForceUnload)
+{
+ return S_OK;
+}
+
+
+/*
+ * CQGAVssProviderFactory class
+ */
+
+class CQGAVssProviderFactory : public IClassFactory
+{
+public:
+ STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
+ STDMETHODIMP_(ULONG) AddRef();
+ STDMETHODIMP_(ULONG) Release();
+ STDMETHODIMP CreateInstance(
+ IUnknown *pUnknownOuter, REFIID iid, void **ppv);
+ STDMETHODIMP LockServer(BOOL lock) { return E_NOTIMPL; }
+
+ CQGAVssProviderFactory();
+ ~CQGAVssProviderFactory();
+
+private:
+ long m_nRefCount;
+};
+
+CQGAVssProviderFactory::CQGAVssProviderFactory()
+{
+ m_nRefCount = 0;
+ LockModule(TRUE);
+}
+
+CQGAVssProviderFactory::~CQGAVssProviderFactory()
+{
+ LockModule(FALSE);
+}
+
+STDMETHODIMP CQGAVssProviderFactory::QueryInterface(REFIID riid, void **ppv)
+{
+ if (riid == IID_IUnknown || riid == IID_IClassFactory) {
+ *ppv = static_cast<void*>(this);
+ AddRef();
+ return S_OK;
+ }
+ *ppv = NULL;
+ return E_NOINTERFACE;
+}
+
+STDMETHODIMP_(ULONG) CQGAVssProviderFactory::AddRef()
+{
+ return InterlockedIncrement(&m_nRefCount);
+}
+
+STDMETHODIMP_(ULONG) CQGAVssProviderFactory::Release()
+{
+ long nRefCount = InterlockedDecrement(&m_nRefCount);
+ if (m_nRefCount == 0) {
+ delete this;
+ }
+ return nRefCount;
+}
+
+STDMETHODIMP CQGAVssProviderFactory::CreateInstance(
+ IUnknown *pUnknownOuter, REFIID iid, void **ppv)
+{
+ CQGAVssProvider *pObj;
+
+ if (pUnknownOuter) {
+ return CLASS_E_NOAGGREGATION;
+ }
+ try {
+ pObj = new CQGAVssProvider;
+ } catch (...) {
+ return E_OUTOFMEMORY;
+ }
+ HRESULT hr = pObj->QueryInterface(iid, ppv);
+ if (FAILED(hr)) {
+ delete pObj;
+ }
+ return hr;
+}
+
+
+/*
+ * DLL functions
+ */
+
+STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
+{
+ CQGAVssProviderFactory *factory;
+ try {
+ factory = new CQGAVssProviderFactory;
+ } catch (...) {
+ return E_OUTOFMEMORY;
+ }
+ factory->AddRef();
+ HRESULT hr = factory->QueryInterface(riid, ppv);
+ factory->Release();
+ return hr;
+}
+
+STDAPI DllCanUnloadNow()
+{
+ return g_nComObjsInUse == 0 ? S_OK : S_FALSE;
+}
+
+EXTERN_C
+BOOL WINAPI DllMain(HINSTANCE hinstDll, DWORD dwReason, LPVOID lpReserved)
+{
+ if (dwReason == DLL_PROCESS_ATTACH) {
+ g_hinstDll = hinstDll;
+ DisableThreadLibraryCalls(hinstDll);
+ }
+ return TRUE;
+}
diff --git a/qga/vss-win32/qga-vss.def b/qga/vss-win32/qga-vss.def
new file mode 100644
index 0000000000..927782c31b
--- /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 0000000000..17abca0da5
--- /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 0000000000..226452a186
--- /dev/null
+++ b/qga/vss-win32/qga-vss.tlb
Binary files differ
diff --git a/qga/vss-win32/requester.cpp b/qga/vss-win32/requester.cpp
new file mode 100644
index 0000000000..1e8dd3dfa8
--- /dev/null
+++ b/qga/vss-win32/requester.cpp
@@ -0,0 +1,507 @@
+/*
+ * 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; /* notify fs/writer freeze from provider */
+ HANDLE hEventThaw; /* request provider to thaw */
+ HANDLE hEventTimeout; /* notify timeout in provider */
+ int cFrozenVols; /* number of frozen volumes */
+} vss_ctx;
+
+STDAPI requester_init(void)
+{
+ vss_ctx.hEventFrozen = INVALID_HANDLE_VALUE;
+ vss_ctx.hEventThaw = INVALID_HANDLE_VALUE;
+ vss_ctx.hEventTimeout = 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.hEventTimeout != INVALID_HANDLE_VALUE) {
+ CloseHandle(vss_ctx.hEventTimeout);
+ vss_ctx.hEventTimeout = 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;
+ }
+ vss_ctx.hEventTimeout = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_TIMEOUT);
+ if (vss_ctx.hEventTimeout == INVALID_HANDLE_VALUE) {
+ err_set(errset, GetLastError(), "failed to create event %s",
+ EVENT_NAME_TIMEOUT);
+ 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 VSS_E_UNEXPECTED_PROVIDER_ERROR:
+ if (WaitForSingleObject(vss_ctx.hEventTimeout, 0) != WAIT_OBJECT_0) {
+ err_set(errset, hr, "unexpected error in VSS provider");
+ break;
+ }
+ /* fall through if hEventTimeout is signaled */
+
+ 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 0000000000..cffec01791
--- /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 (*QGAVSSRequesterFunc)(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 0000000000..ce14e14290
--- /dev/null
+++ b/qga/vss-win32/vss-common.h
@@ -0,0 +1,129 @@
+/*
+ * 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"
+#define EVENT_NAME_TIMEOUT "Global\\QGAVSSEvent-timeout"
+
+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