Import QEMU upstream snapshot d2e570c

Upstream: https://gitlab.com/qemu-project/qemu.git

Upstream-Commit: d2e570cc0f97b936902a5b1b86b73c0f5998b475
This commit is contained in:
2026-08-31 02:15:30 +02:00
commit cf256aa081
11315 changed files with 3598369 additions and 0 deletions
+603
View File
@@ -0,0 +1,603 @@
/*
* QEMU Guest Agent win32 VSS Provider installer
*
* Copyright Hitachi Data Systems Corp. 2013
*
* Authors:
* Tomoki Sekiyama <[email protected]>
*
* 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 "qemu/osdep.h"
#include "vss-common.h"
#include "vss-debug.h"
#ifdef HAVE_VSS_SDK
#include <vscoordint.h>
#else
#include <vsadmin.h>
#endif
#include "install.h"
#include <wbemidl.h>
#include <comdef.h>
#include <comutil.h>
#include <sddl.h>
#include <winsvc.h>
#define BUFFER_SIZE 1024
extern HINSTANCE g_hinstDll;
const GUID CLSID_COMAdminCatalog = { 0xF618C514, 0xDFB8, 0x11d1,
{0xA2, 0xCF, 0x00, 0x80, 0x5F, 0xC7, 0x92, 0x35} };
const GUID IID_ICOMAdminCatalog2 = { 0x790C6E0B, 0x9194, 0x4cc9,
{0x94, 0x26, 0xA4, 0x8A, 0x63, 0x18, 0x56, 0x96} };
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} };
static 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;
const char *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);
qga_debug("%.*s. (Error: %lx) %s", 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)
#if !defined(__MINGW64_VERSION_MAJOR) || !defined(__MINGW64_VERSION_MINOR) || \
__MINGW64_VERSION_MAJOR * 100 + __MINGW64_VERSION_MINOR < 301
void __stdcall _com_issue_error(HRESULT hr)
{
errmsg(hr, "Unexpected error in COM");
}
#endif
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)
{
qga_debug_begin;
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:
qga_debug_end;
return hr;
}
/* Acquire group or user name by SID */
static HRESULT getNameByStringSID(
const wchar_t *sid, LPWSTR buffer, LPDWORD bufferLen)
{
qga_debug_begin;
HRESULT hr = S_OK;
PSID psid = NULL;
SID_NAME_USE groupType;
DWORD domainNameLen = BUFFER_SIZE;
wchar_t domainName[BUFFER_SIZE];
if (!ConvertStringSidToSidW(sid, &psid)) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto out;
}
if (!LookupAccountSidW(NULL, psid, buffer, bufferLen,
domainName, &domainNameLen, &groupType)) {
hr = HRESULT_FROM_WIN32(GetLastError());
/* Fall through and free psid */
}
LocalFree(psid);
out:
qga_debug_end;
return hr;
}
/* Find and iterate QGA VSS provider in COM+ Application Catalog */
static HRESULT QGAProviderFind(
HRESULT (*found)(ICatalogCollection *, int, void *), void *arg)
{
qga_debug_begin;
HRESULT hr;
COMInitializer initializer;
COMPointer<IUnknown> pUnknown;
COMPointer<ICOMAdminCatalog2> 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_ICOMAdminCatalog2,
(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:
qga_debug_end;
return hr;
}
/* Count QGA VSS provider in COM+ Application Catalog */
static HRESULT QGAProviderCount(ICatalogCollection *coll, int i, void *arg)
{
qga_debug_begin;
(*(int *)arg)++;
qga_debug_end;
return S_OK;
}
/* Remove QGA VSS provider from COM+ Application Catalog Collection */
static HRESULT QGAProviderRemove(ICatalogCollection *coll, int i, void *arg)
{
qga_debug_begin;
HRESULT hr;
qga_debug("Removing COM+ Application: %s", QGA_PROVIDER_NAME);
chk(coll->Remove(i));
out:
qga_debug_end;
return hr;
}
/* Unregister this module from COM+ Applications Catalog */
STDAPI COMUnregister(void);
STDAPI COMUnregister(void)
{
qga_debug_begin;
HRESULT hr;
DllUnregisterServer();
chk(QGAProviderFind(QGAProviderRemove, NULL));
out:
qga_debug_end;
return hr;
}
/* Register this module to COM+ Applications Catalog */
STDAPI COMRegister(void);
STDAPI COMRegister(void)
{
qga_debug_begin;
HRESULT hr;
COMInitializer initializer;
COMPointer<IUnknown> pUnknown;
COMPointer<ICOMAdminCatalog2> 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;
DWORD bufferLen = BUFFER_SIZE;
wchar_t buffer[BUFFER_SIZE];
const wchar_t *administratorsGroupSID = L"S-1-5-32-544";
const wchar_t *systemUserSID = L"S-1-5-18";
if (!g_hinstDll) {
errmsg(E_FAIL, "Failed to initialize DLL");
qga_debug_end;
return E_FAIL;
}
chk(QGAProviderFind(QGAProviderCount, (void *)&count));
if (count) {
qga_debug("QGA VSS Provider is already installed. Attempting to unregister first.");
hr = COMUnregister();
if (FAILED(hr)) {
errmsg(hr, "Failed to unregister existing QGA VSS Provider. Aborting installation.");
qga_debug_end;
return E_ABORT;
}
}
chk(CoCreateInstance(CLSID_COMAdminCatalog, NULL, CLSCTX_INPROC_SERVER,
IID_IUnknown, (void **)pUnknown.replace()));
chk(pUnknown->QueryInterface(IID_ICOMAdminCatalog2,
(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");
qga_debug("Registering " QGA_PROVIDER_NAME ": %s %s",
dllPath, tlbPath);
if (!PathFileExists(tlbPath)) {
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
errmsg(hr, "Failed to lookup tlb");
goto out;
}
chk(pCatalog->CreateServiceForApplication(
_bstr_t(QGA_PROVIDER_LNAME), _bstr_t(QGA_PROVIDER_LNAME),
_bstr_t(L"SERVICE_DEMAND_START"), _bstr_t(L"SERVICE_ERROR_NORMAL"),
_bstr_t(L""), _bstr_t(L".\\localsystem"), _bstr_t(L""), FALSE));
chk(pCatalog->InstallComponent(_bstr_t(QGA_PROVIDER_LNAME),
_bstr_t(dllPath), _bstr_t(tlbPath),
_bstr_t("")));
/* Setup roles of the application */
chk(getNameByStringSID(administratorsGroupSID, buffer, &bufferLen));
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", buffer));
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));
bufferLen = BUFFER_SIZE;
chk(getNameByStringSID(systemUserSID, buffer, &bufferLen));
chk(pUsersInRole->Add((IDispatch **)pObj.replace()));
chk(put_Value(pObj, L"User", buffer));
chk(pUsersInRole->SaveChanges(&n));
out:
if (unregisterOnFailure && FAILED(hr)) {
COMUnregister();
}
qga_debug_end;
return hr;
}
STDAPI_(void) CALLBACK DLLCOMRegister(HWND, HINSTANCE, LPSTR, int);
STDAPI_(void) CALLBACK DLLCOMRegister(HWND, HINSTANCE, LPSTR, int)
{
HRESULT hr = COMRegister();
if (FAILED(hr)) {
exit(hr);
}
}
STDAPI_(void) CALLBACK DLLCOMUnregister(HWND, HINSTANCE, LPSTR, int);
STDAPI_(void) CALLBACK DLLCOMUnregister(HWND, HINSTANCE, LPSTR, int)
{
COMUnregister();
}
static BOOL CreateRegistryKey(LPCTSTR key, LPCTSTR value, LPCTSTR data)
{
qga_debug_begin;
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:
qga_debug_end;
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)
{
qga_debug_begin;
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 registry */
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 (hr == (long int) VSS_E_PROVIDER_ALREADY_REGISTERED) {
DllUnregisterServer();
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();
}
qga_debug_end;
return hr;
}
/* Unregister this VSS hardware provider from the system */
STDAPI DllUnregisterServer(void)
{
qga_debug_begin;
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);
qga_debug_end;
return S_OK; /* Uninstall should never fail */
}
/* Support function to convert ASCII string into BSTR (used in _bstr_t) */
#ifndef CONFIG_CONVERT_STRING_TO_BSTR
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) {
qga_debug("Failed to convert string '%s' into BSTR", ascii);
bstr[0] = 0;
}
return bstr;
}
}
#endif
/* Stop QGA VSS provider service using Winsvc API */
STDAPI StopService(void)
{
qga_debug_begin;
HRESULT hr = S_OK;
SC_HANDLE manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
SC_HANDLE service = NULL;
if (!manager) {
errmsg(E_FAIL, "Failed to open service manager");
hr = E_FAIL;
goto out;
}
service = OpenService(manager, QGA_PROVIDER_NAME, SC_MANAGER_ALL_ACCESS);
if (!service) {
errmsg(E_FAIL, "Failed to open service");
hr = E_FAIL;
goto out;
}
if (!(ControlService(service, SERVICE_CONTROL_STOP, NULL))) {
errmsg(E_FAIL, "Failed to stop service");
hr = E_FAIL;
}
out:
CloseServiceHandle(service);
CloseServiceHandle(manager);
qga_debug_end;
return hr;
}
+20
View File
@@ -0,0 +1,20 @@
/*
* QEMU Guest Agent VSS requester declarations
*
* Copyright Hitachi Data Systems Corp. 2013
*
* Authors:
* Tomoki Sekiyama <[email protected]>
*
* 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 INSTALL_H
#define INSTALL_H
#include <comadmin.h>
STDAPI StopService(void);
#endif
+36
View File
@@ -0,0 +1,36 @@
link_args = cc.get_supported_link_arguments([
'-fstack-protector-all',
'-fstack-protector-strong',
'-Wl,--add-stdcall-alias',
'-Wl,--enable-stdcall-fixup'
])
qga_vss = shared_module(
'qga-vss',
['requester.cpp', 'provider.cpp', 'install.cpp', 'vss-debug.cpp', genh],
name_prefix: '',
cpp_args: ['-Wno-unknown-pragmas', '-Wno-delete-non-virtual-dtor', '-Wno-non-virtual-dtor'],
link_args: link_args,
vs_module_defs: 'qga-vss.def',
dependencies: [
socket,
cc.find_library('ole32'),
cc.find_library('oleaut32'),
cc.find_library('shlwapi'),
cc.find_library('uuid')
]
)
if midl.found()
gen_tlb = custom_target('gen-tlb',
input: 'qga-vss.idl',
output: 'qga-vss.tlb',
command: [midl, '@INPUT@', '/tlb', '@OUTPUT@'])
else
gen_tlb = custom_target('gen-tlb',
input: 'qga-vss.idl',
output: 'qga-vss.tlb',
command: [widl, '-t', '@INPUT@', '-o', '@OUTPUT@'])
endif
all_qga += [ qga_vss, gen_tlb ]
+543
View File
@@ -0,0 +1,543 @@
/*
* QEMU Guest Agent win32 VSS Provider implementations
*
* Copyright Hitachi Data Systems Corp. 2013
*
* Authors:
* Tomoki Sekiyama <[email protected]>
*
* 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 "qemu/osdep.h"
#include "vss-common.h"
#include "vss-debug.h"
#ifdef HAVE_VSS_SDK
#include <vscoordint.h>
#else
#include <vsadmin.h>
#endif
#include <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} };
static 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 S_OK;
}
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)
{
*plDeletedSnapshots = 0;
*pNondeletedSnapshotID = SourceObjectId;
return S_OK;
}
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)
{
HANDLE hEventFrozen;
/* Check if a requester is qemu-ga by whether an event is created */
hEventFrozen = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_FROZEN);
if (!hEventFrozen) {
*pbSupportedByThisProvider = FALSE;
return S_OK;
}
CloseHandle(hEventFrozen);
*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) {
return E_FAIL;
}
hEventThaw = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_THAW);
if (!hEventThaw) {
CloseHandle(hEventFrozen);
return E_FAIL;
}
hEventTimeout = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_TIMEOUT);
if (!hEventTimeout) {
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);
}
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);
EXTERN_C
BOOL WINAPI DllMain(HINSTANCE hinstDll, DWORD dwReason, LPVOID lpReserved)
{
qga_debug("begin, reason = %lu", dwReason);
if (dwReason == DLL_PROCESS_ATTACH) {
g_hinstDll = hinstDll;
DisableThreadLibraryCalls(hinstDll);
}
qga_debug_end;
return TRUE;
}
+15
View File
@@ -0,0 +1,15 @@
LIBRARY "QGA-PROVIDER.DLL"
EXPORTS
DLLCOMRegister
DLLCOMUnregister
COMRegister PRIVATE
COMUnregister PRIVATE
DllCanUnloadNow PRIVATE
DllGetClassObject PRIVATE
DllRegisterServer PRIVATE
DllUnregisterServer PRIVATE
requester_init PRIVATE
requester_deinit PRIVATE
requester_freeze PRIVATE
requester_thaw PRIVATE
+20
View File
@@ -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;
};
};
+637
View File
@@ -0,0 +1,637 @@
/*
* QEMU Guest Agent win32 VSS Requester implementations
*
* Copyright Hitachi Data Systems Corp. 2013
*
* Authors:
* Tomoki Sekiyama <[email protected]>
*
* 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 "qemu/osdep.h"
#include "vss-common.h"
#include "vss-debug.h"
#include "requester.h"
#include "install.h"
#include <vswriter.h>
#include <vsbackup.h>
/* Max wait time for frozen event (VSS can only hold writes for 10 seconds) */
#define VSS_TIMEOUT_FREEZE_MSEC 60000
/* Call QueryStatus every 10 ms while waiting for frozen event */
#define VSS_TIMEOUT_EVENT_MSEC 10
#define DEFAULT_VSS_BACKUP_TYPE VSS_BT_FULL
#define err_set(e, err, fmt, ...) { \
(e)->error_setg_win32_wrapper((e)->errp, __FILE__, __LINE__, __func__, \
err, fmt ": Windows error 0x%lx", \
## __VA_ARGS__, err); \
qga_debug(fmt ": Windows error 0x%lx", ## __VA_ARGS__, err); \
}
/* Bad idea, works only when (e)->errp != NULL: */
#define err_is_set(e) ((e)->errp && *(e)->errp)
/* To lift this restriction, error_propagate(), like we do in QEMU code */
/* 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)
{
qga_debug_begin;
hLib = LoadLibraryA("VSSAPI.DLL");
if (!hLib) {
qga_debug("failed to load VSSAPI.DLL");
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) {
qga_debug("failed to get proc address from VSSAPI.DLL");
return HRESULT_FROM_WIN32(GetLastError());
}
pVssFreeSnapshotProperties = (t_VssFreeSnapshotProperties)
GetProcAddress(hLib, "VssFreeSnapshotProperties");
if (!pVssFreeSnapshotProperties) {
qga_debug("failed to get proc address from VSSAPI.DLL");
return HRESULT_FROM_WIN32(GetLastError());
}
qga_debug_end;
return S_OK;
}
static void requester_cleanup(void)
{
qga_debug_begin;
if (vss_ctx.hEventFrozen) {
CloseHandle(vss_ctx.hEventFrozen);
vss_ctx.hEventFrozen = NULL;
}
if (vss_ctx.hEventThaw) {
CloseHandle(vss_ctx.hEventThaw);
vss_ctx.hEventThaw = NULL;
}
if (vss_ctx.hEventTimeout) {
CloseHandle(vss_ctx.hEventTimeout);
vss_ctx.hEventTimeout = NULL;
}
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;
qga_debug_end;
}
STDAPI requester_deinit(void)
{
qga_debug_begin;
requester_cleanup();
pCreateVssBackupComponents = NULL;
pVssFreeSnapshotProperties = NULL;
if (hLib) {
FreeLibrary(hLib);
hLib = NULL;
}
qga_debug_end;
return S_OK;
}
static HRESULT WaitForAsync(IVssAsync *pAsync)
{
qga_debug_begin;
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);
qga_debug_end;
return ret;
}
static void AddComponents(ErrorSet *errset)
{
qga_debug_begin;
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);
}
qga_debug_end;
}
static DWORD get_reg_dword_value(HKEY baseKey, LPCSTR subKey, LPCSTR valueName,
DWORD defaultData)
{
qga_debug_begin;
DWORD regGetValueError;
DWORD dwordData;
DWORD dataSize = sizeof(DWORD);
regGetValueError = RegGetValue(baseKey, subKey, valueName, RRF_RT_DWORD,
NULL, &dwordData, &dataSize);
qga_debug_end;
if (regGetValueError != ERROR_SUCCESS) {
return defaultData;
}
return dwordData;
}
static bool is_valid_vss_backup_type(VSS_BACKUP_TYPE vssBT)
{
return (vssBT > VSS_BT_UNDEFINED && vssBT < VSS_BT_OTHER);
}
static VSS_BACKUP_TYPE get_vss_backup_type(
VSS_BACKUP_TYPE defaultVssBT = DEFAULT_VSS_BACKUP_TYPE)
{
qga_debug_begin;
VSS_BACKUP_TYPE vssBackupType;
vssBackupType = static_cast<VSS_BACKUP_TYPE>(
get_reg_dword_value(HKEY_LOCAL_MACHINE,
QGA_PROVIDER_REGISTRY_ADDRESS,
"VssOption",
defaultVssBT));
qga_debug_end;
if (!is_valid_vss_backup_type(vssBackupType)) {
return defaultVssBT;
}
return vssBackupType;
}
void requester_freeze(int *num_vols, void *mountpoints, ErrorSet *errset)
{
qga_debug_begin;
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;
int num_mount_points = 0;
VSS_BACKUP_TYPE vss_bt = get_vss_backup_type();
if (vss_ctx.pVssbc) { /* already frozen */
*num_vols = 0;
qga_debug("finished, already frozen");
return;
}
/* 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) {
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) {
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) {
err_set(errset, GetLastError(), "failed to create event %s",
EVENT_NAME_TIMEOUT);
goto out;
}
if (!pCreateVssBackupComponents) {
err_set(errset, (HRESULT)ERROR_PROC_NOT_FOUND,
"CreateVssBackupComponents proc address absent. Did you call requester_init()?");
goto out;
}
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, 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;
ctx |= 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;
}
if (mountpoints) {
PWCHAR volume_name_wchar;
for (volList *list = (volList *)mountpoints; list; list = list->next) {
size_t len = strlen(list->value) + 1;
size_t converted = 0;
VSS_ID pid;
volume_name_wchar = new wchar_t[len];
mbstowcs_s(&converted, volume_name_wchar, len,
list->value, _TRUNCATE);
hr = vss_ctx.pVssbc->AddToSnapshotSet(volume_name_wchar,
g_gProviderId, &pid);
if (FAILED(hr)) {
err_set(errset, hr, "failed to add %S to snapshot set",
volume_name_wchar);
delete[] volume_name_wchar;
goto out;
}
num_mount_points++;
delete[] volume_name_wchar;
}
if (num_mount_points == 0) {
/* If there is no valid mount points, just exit. */
goto out;
}
}
if (!mountpoints) {
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[MAX_PATH];
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. */
}
}
qga_debug("preparing for backup");
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;
}
/*
* Start VSS quiescing operations.
* CQGAVssProvider::CommitSnapshots will kick vss_ctx.hEventFrozen
* after the applications and filesystems are frozen.
*/
qga_debug("do snapshot set");
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_TIMEOUT) {
err_set(errset, E_FAIL,
"timeout when try to receive Frozen event from VSS provider");
/* If we are here, VSS had timeout.
* Don't call AbortBackup, just return directly.
*/
goto out1;
}
if (wait_status != WAIT_OBJECT_0) {
err_set(errset, E_FAIL,
"couldn't receive Frozen event from VSS provider");
goto out;
}
if (mountpoints) {
*num_vols = vss_ctx.cFrozenVols = num_mount_points;
} else {
*num_vols = vss_ctx.cFrozenVols = num_fixed_drives;
}
qga_debug("end successful");
return;
out:
if (vss_ctx.pVssbc) {
vss_ctx.pVssbc->AbortBackup();
}
out1:
requester_cleanup();
qga_debug_end;
}
void requester_thaw(int *num_vols, void *mountpints, ErrorSet *errset)
{
qga_debug_begin;
COMPointer<IVssAsync> pAsync;
if (!vss_ctx.hEventThaw) {
/*
* In this case, DoSnapshotSet is aborted or not started,
* and no volumes must be frozen. We return without an error.
*/
*num_vols = 0;
qga_debug("finished, no volumes were frozen");
return;
}
/* Tell the provider that the snapshot is finished. */
SetEvent(vss_ctx.hEventThaw);
if (!vss_ctx.pVssbc) {
err_set(errset, (HRESULT)VSS_E_BAD_STATE,
"CreateVssBackupComponents is missing. Did you freeze the volumes?");
return;
}
if (!vss_ctx.pAsyncSnapshot) {
err_set(errset, (HRESULT)VSS_E_BAD_STATE,
"AsyncSnapshot set is missing. Did you freeze the volumes?");
return;
}
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();
StopService();
qga_debug_end;
}
+52
View File
@@ -0,0 +1,52 @@
/*
* QEMU Guest Agent VSS requester declarations
*
* Copyright Hitachi Data Systems Corp. 2013
*
* Authors:
* Tomoki Sekiyama <[email protected]>
*
* 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 <basetyps.h> /* STDAPI */
#ifdef __cplusplus
extern "C" {
#endif
struct Error;
/* Callback to set Error; used to avoid linking glib to the DLL */
typedef void (*ErrorSetFunc)(struct Error **errp,
const char *src, int line, const char *func,
int win32_err, const char *fmt, ...)
G_GNUC_PRINTF(6, 7);
typedef struct ErrorSet {
ErrorSetFunc error_setg_win32_wrapper;
struct Error **errp; /* restriction: must not be null */
} ErrorSet;
STDAPI requester_init(void);
STDAPI requester_deinit(void);
typedef struct volList volList;
struct volList {
volList *next;
char *value;
};
typedef void (*QGAVSSRequesterFunc)(int *, void *, ErrorSet *);
void requester_freeze(int *num_vols, void *volList, ErrorSet *errset);
void requester_thaw(int *num_vols, void *volList, ErrorSet *errset);
#ifdef __cplusplus
}
#endif
#endif
+101
View File
@@ -0,0 +1,101 @@
/*
* QEMU Guest Agent win32 VSS common declarations
*
* Copyright Hitachi Data Systems Corp. 2013
*
* Authors:
* Tomoki Sekiyama <[email protected]>
*
* 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_COMMON_H
#define VSS_COMMON_H
#define __MIDL_user_allocate_free_DEFINED__
#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)
#include <vss.h>
#include "vss-handles.h"
/* Macros to convert char definitions to wchar */
#define _L(a) L##a
#define L(a) _L(a)
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");
#ifdef HAVE_VSS_SDK
/* 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
};
#endif
/* 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
+39
View File
@@ -0,0 +1,39 @@
/*
* QEMU Guest Agent VSS debug declarations
*
* Copyright (C) 2023 Red Hat Inc
*
* Authors:
* Konstantin Kostiuk <[email protected]>
*
* 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 "qemu/osdep.h"
#include "vss-debug.h"
#include "vss-common.h"
void qga_debug_internal(const char *funcname, const char *fmt, ...)
{
char user_string[512] = {0};
char full_string[640] = {0};
va_list args;
va_start(args, fmt);
if (vsnprintf(user_string, _countof(user_string), fmt, args) <= 0) {
va_end(args);
return;
}
va_end(args);
if (snprintf(full_string, _countof(full_string),
QGA_PROVIDER_NAME "[%lu]: %s %s\n",
GetCurrentThreadId(), funcname, user_string) <= 0) {
return;
}
OutputDebugString(full_string);
fputs(full_string, stderr);
}
+24
View File
@@ -0,0 +1,24 @@
/*
* QEMU Guest Agent VSS debug declarations
*
* Copyright (C) 2023 Red Hat Inc
*
* Authors:
* Konstantin Kostiuk <[email protected]>
*
* 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 <vss-handles.h>
#ifndef VSS_DEBUG_H
#define VSS_DEBUG_H
void qga_debug_internal(const char *funcname, const char *fmt, ...) G_GNUC_PRINTF(2, 3);
#define qga_debug(fmt, ...) qga_debug_internal(__func__, fmt, ## __VA_ARGS__)
#define qga_debug_begin qga_debug("begin")
#define qga_debug_end qga_debug("end")
#endif
+17
View File
@@ -0,0 +1,17 @@
#ifndef VSS_HANDLES_H
#define VSS_HANDLES_H
/* 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 QGA_PROVIDER_REGISTRY_ADDRESS "SYSTEM\\CurrentControlSet"\
"\\Services"\
"\\" QGA_PROVIDER_NAME
#define EVENT_NAME_FROZEN "Global\\QGAVSSEvent-frozen"
#define EVENT_NAME_THAW "Global\\QGAVSSEvent-thaw"
#define EVENT_NAME_TIMEOUT "Global\\QGAVSSEvent-timeout"
#endif