2013-08-07 19:40:25 +04:00
|
|
|
/*
|
|
|
|
* QEMU Guest Agent VSS utility functions
|
|
|
|
*
|
|
|
|
* 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 <windows.h>
|
|
|
|
#include "qga/guest-agent-core.h"
|
|
|
|
#include "qga/vss-win32.h"
|
|
|
|
#include "qga/vss-win32/requester.h"
|
|
|
|
|
|
|
|
#define QGA_VSS_DLL "qga-vss.dll"
|
|
|
|
|
|
|
|
static HMODULE provider_lib;
|
|
|
|
|
|
|
|
/* Call a function in qga-vss.dll with the specified name */
|
|
|
|
static HRESULT call_vss_provider_func(const char *func_name)
|
|
|
|
{
|
|
|
|
FARPROC WINAPI func;
|
|
|
|
|
|
|
|
g_assert(provider_lib);
|
|
|
|
|
|
|
|
func = GetProcAddress(provider_lib, func_name);
|
|
|
|
if (!func) {
|
|
|
|
char *msg;
|
|
|
|
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
|
|
FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
|
|
|
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
|
|
|
(char *)&msg, 0, NULL);
|
|
|
|
fprintf(stderr, "failed to load %s from %s: %s",
|
|
|
|
func_name, QGA_VSS_DLL, msg);
|
|
|
|
LocalFree(msg);
|
|
|
|
return E_FAIL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return func();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check whether this OS version supports VSS providers */
|
|
|
|
static bool vss_check_os_version(void)
|
|
|
|
{
|
|
|
|
OSVERSIONINFO OSver;
|
|
|
|
|
|
|
|
OSver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
|
|
|
|
GetVersionEx(&OSver);
|
|
|
|
if ((OSver.dwMajorVersion == 5 && OSver.dwMinorVersion >= 2) ||
|
|
|
|
OSver.dwMajorVersion > 5) {
|
|
|
|
BOOL wow64 = false;
|
|
|
|
#ifndef _WIN64
|
|
|
|
/* Provider doesn't work under WOW64 (32bit agent on 64bit OS) */
|
|
|
|
if (!IsWow64Process(GetCurrentProcess(), &wow64)) {
|
|
|
|
fprintf(stderr, "failed to IsWow64Process (Error: %lx\n)\n",
|
|
|
|
GetLastError());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (wow64) {
|
|
|
|
fprintf(stderr, "Warning: Running under WOW64\n");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return !wow64;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Load qga-vss.dll */
|
|
|
|
bool vss_init(bool init_requester)
|
|
|
|
{
|
|
|
|
if (!vss_check_os_version()) {
|
|
|
|
/* Do nothing if OS doesn't support providers. */
|
|
|
|
fprintf(stderr, "VSS provider is not supported in this OS version: "
|
|
|
|
"fsfreeze is disabled.\n");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
provider_lib = LoadLibraryA(QGA_VSS_DLL);
|
|
|
|
if (!provider_lib) {
|
|
|
|
char *msg;
|
|
|
|
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
|
|
FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
|
|
|
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
|
|
|
(char *)&msg, 0, NULL);
|
|
|
|
fprintf(stderr, "failed to load %s: %sfsfreeze is disabled\n",
|
|
|
|
QGA_VSS_DLL, msg);
|
|
|
|
LocalFree(msg);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (init_requester) {
|
|
|
|
HRESULT hr = call_vss_provider_func("requester_init");
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
fprintf(stderr, "fsfreeze is disabled.\n");
|
|
|
|
vss_deinit(false);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Unload qga-provider.dll */
|
|
|
|
void vss_deinit(bool deinit_requester)
|
|
|
|
{
|
|
|
|
if (deinit_requester) {
|
|
|
|
call_vss_provider_func("requester_deinit");
|
|
|
|
}
|
|
|
|
FreeLibrary(provider_lib);
|
|
|
|
provider_lib = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool vss_initialized(void)
|
|
|
|
{
|
|
|
|
return !!provider_lib;
|
|
|
|
}
|
|
|
|
|
2013-08-07 19:40:32 +04:00
|
|
|
int ga_install_vss_provider(void)
|
|
|
|
{
|
|
|
|
HRESULT hr;
|
|
|
|
|
|
|
|
if (!vss_init(false)) {
|
|
|
|
fprintf(stderr, "Installation of VSS provider is skipped. "
|
|
|
|
"fsfreeze will be disabled.\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
hr = call_vss_provider_func("COMRegister");
|
|
|
|
vss_deinit(false);
|
|
|
|
|
|
|
|
return SUCCEEDED(hr) ? 0 : EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ga_uninstall_vss_provider(void)
|
|
|
|
{
|
|
|
|
if (!vss_init(false)) {
|
|
|
|
fprintf(stderr, "Removal of VSS provider is skipped.\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
call_vss_provider_func("COMUnregister");
|
|
|
|
vss_deinit(false);
|
|
|
|
}
|
|
|
|
|
2013-08-07 19:40:25 +04:00
|
|
|
/* Call VSS requester and freeze/thaw filesystems and applications */
|
2014-05-02 15:26:30 +04:00
|
|
|
void qga_vss_fsfreeze(int *nr_volume, Error **errp, bool freeze)
|
2013-08-07 19:40:25 +04:00
|
|
|
{
|
|
|
|
const char *func_name = freeze ? "requester_freeze" : "requester_thaw";
|
|
|
|
QGAVSSRequesterFunc func;
|
|
|
|
ErrorSet errset = {
|
error: On abort, report where the error was created
This is particularly useful when we abort in error_propagate(),
because there the stack backtrace doesn't lead to where the error was
created. Looks like this:
Unexpected error in parse_block_error_action() at .../qemu/blockdev.c:322:
qemu-system-x86_64: -drive if=none,werror=foo: 'foo' invalid write error action
Aborted (core dumped)
Note: to get this example output, I monkey-patched drive_new() to pass
&error_abort to blockdev_init().
To keep the error handling boiler plate from growing even more, all
error_setFOO() become macros expanding into error_setFOO_internal()
with additional __FILE__, __LINE__, __func__ arguments. Not exactly
pretty, but it works.
The macro trickery breaks down when you take the address of an
error_setFOO(). Fortunately, we do that in just one place: qemu-ga's
Windows VSS provider and requester DLL wants to call
error_setg_win32() through a function pointer "to avoid linking glib
to the DLL". Use error_setg_win32_internal() there. The use of the
function pointer is already wrapped in a macro, so the churn isn't
bad.
Code size increases by some 35KiB for me (0.7%). Tolerable. Could be
less if we passed relative rather than absolute source file names to
the compiler, or forwent reporting __func__.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Acked-by: Laszlo Ersek <lersek@redhat.com>
2015-06-19 20:21:59 +03:00
|
|
|
.error_setg_win32 = error_setg_win32_internal,
|
qga: Clean up unnecessarily dirty casts
qga_vss_fsfreeze() casts error_set_win32() from
void (*)(Error **, int, ErrorClass, const char *, ...)
to
void (*)(void **, int, int, const char *, ...)
The result is later called. Since the two types are not compatible,
the call is undefined behavior. It works in practice anyway.
However, there's no real need for trickery here. Clean it up as
follows:
* Declare struct Error, and fix the first parameter.
* Switch to error_setg_win32(). This gets rid of the troublesome
ErrorClass parameter. Requires converting error_setg_win32() from
macro to function, but that's trivially easy, because this is the
only user of error_set_win32().
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
2015-06-19 21:44:54 +03:00
|
|
|
.errp = errp,
|
2013-08-07 19:40:25 +04:00
|
|
|
};
|
|
|
|
|
2015-06-20 10:33:56 +03:00
|
|
|
g_assert(errp); /* requester.cpp requires it */
|
2013-08-07 19:40:25 +04:00
|
|
|
func = (QGAVSSRequesterFunc)GetProcAddress(provider_lib, func_name);
|
|
|
|
if (!func) {
|
2014-05-02 15:26:30 +04:00
|
|
|
error_setg_win32(errp, GetLastError(), "failed to load %s from %s",
|
2013-08-07 19:40:25 +04:00
|
|
|
func_name, QGA_VSS_DLL);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
func(nr_volume, &errset);
|
|
|
|
}
|