diff options
-rw-r--r-- | qga/Makefile.objs | 1 | ||||
-rw-r--r-- | qga/commands-win32.c | 82 | ||||
-rw-r--r-- | qga/vss-win32.c | 141 | ||||
-rw-r--r-- | qga/vss-win32.h | 24 |
4 files changed, 240 insertions, 8 deletions
diff --git a/qga/Makefile.objs b/qga/Makefile.objs index c4bd151..1c5986c 100644 --- a/qga/Makefile.objs +++ b/qga/Makefile.objs @@ -1,6 +1,7 @@ qga-obj-y = commands.o guest-agent-command-state.o main.o 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-$(CONFIG_WIN32) += vss-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 diff --git a/qga/commands-win32.c b/qga/commands-win32.c index 24e4ad0..7a37f5c 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -15,6 +15,7 @@ #include <wtypes.h> #include <powrprof.h> #include "qga/guest-agent-core.h" +#include "qga/vss-win32.h" #include "qga-qmp-commands.h" #include "qapi/qmp/qerror.h" @@ -156,27 +157,89 @@ void qmp_guest_file_flush(int64_t handle, Error **err) */ GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err) { - error_set(err, QERR_UNSUPPORTED); - return 0; + if (!vss_initialized()) { + error_set(err, QERR_UNSUPPORTED); + return 0; + } + + if (ga_is_frozen(ga_state)) { + return GUEST_FSFREEZE_STATUS_FROZEN; + } + + return GUEST_FSFREEZE_STATUS_THAWED; } /* - * Walk list of mounted file systems in the guest, and freeze the ones which - * are real local file systems. + * Freeze local file systems using Volume Shadow-copy Service. + * The frozen state is limited for up to 10 seconds by VSS. */ int64_t qmp_guest_fsfreeze_freeze(Error **err) { - error_set(err, QERR_UNSUPPORTED); + int i; + Error *local_err = NULL; + + if (!vss_initialized()) { + error_set(err, QERR_UNSUPPORTED); + return 0; + } + + slog("guest-fsfreeze called"); + + /* cannot risk guest agent blocking itself on a write in this state */ + ga_set_frozen(ga_state); + + qga_vss_fsfreeze(&i, err, true); + if (error_is_set(err)) { + goto error; + } + + return i; + +error: + qmp_guest_fsfreeze_thaw(&local_err); + if (error_is_set(&local_err)) { + g_debug("cleanup thaw: %s", error_get_pretty(local_err)); + error_free(local_err); + } return 0; } /* - * Walk list of frozen file systems in the guest, and thaw them. + * Thaw local file systems using Volume Shadow-copy Service. */ int64_t qmp_guest_fsfreeze_thaw(Error **err) { - error_set(err, QERR_UNSUPPORTED); - return 0; + int i; + + if (!vss_initialized()) { + error_set(err, QERR_UNSUPPORTED); + return 0; + } + + qga_vss_fsfreeze(&i, err, false); + + ga_unset_frozen(ga_state); + return i; +} + +static void guest_fsfreeze_cleanup(void) +{ + Error *err = NULL; + + if (!vss_initialized()) { + return; + } + + if (ga_is_frozen(ga_state) == GUEST_FSFREEZE_STATUS_FROZEN) { + qmp_guest_fsfreeze_thaw(&err); + if (err) { + slog("failed to clean up frozen filesystems: %s", + error_get_pretty(err)); + error_free(err); + } + } + + vss_deinit(true); } /* @@ -354,4 +417,7 @@ int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp) /* register init/cleanup routines for stateful command groups */ void ga_command_state_init(GAState *s, GACommandState *cs) { + if (vss_init(true)) { + ga_command_state_add(cs, NULL, guest_fsfreeze_cleanup); + } } diff --git a/qga/vss-win32.c b/qga/vss-win32.c new file mode 100644 index 0000000..89c0f3b --- /dev/null +++ b/qga/vss-win32.c @@ -0,0 +1,141 @@ +/* + * 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; +} + +/* Call VSS requester and freeze/thaw filesystems and applications */ +void qga_vss_fsfreeze(int *nr_volume, Error **err, bool freeze) +{ + const char *func_name = freeze ? "requester_freeze" : "requester_thaw"; + QGAVSSRequesterFunc func; + ErrorSet errset = { + .error_set = (ErrorSetFunc)error_set_win32, + .errp = (void **)err, + .err_class = ERROR_CLASS_GENERIC_ERROR + }; + + func = (QGAVSSRequesterFunc)GetProcAddress(provider_lib, func_name); + if (!func) { + error_setg_win32(err, GetLastError(), "failed to load %s from %s", + func_name, QGA_VSS_DLL); + return; + } + + func(nr_volume, &errset); +} diff --git a/qga/vss-win32.h b/qga/vss-win32.h new file mode 100644 index 0000000..eac669c --- /dev/null +++ b/qga/vss-win32.h @@ -0,0 +1,24 @@ +/* + * QEMU Guest Agent VSS utility 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 + +#include "qapi/error.h" + +bool vss_init(bool init_requester); +void vss_deinit(bool deinit_requester); +bool vss_initialized(void); + +void qga_vss_fsfreeze(int *nr_volume, Error **err, bool freeze); + +#endif |