Patchwork [v8,08/10] qemu-ga: Call Windows VSS requester in fsfreeze command handler

login
register
mail settings
Submitter Tomoki Sekiyama
Date July 23, 2013, 10:45 p.m.
Message ID <20130723224558.26273.63940.stgit@outback>
Download mbox | patch
Permalink /patch/261240/
State New
Headers show

Comments

Tomoki Sekiyama - July 23, 2013, 10:45 p.m.
Support guest-fsfreeze-freeze and guest-fsfreeze-thaw commands for Windows
guests. When fsfreeze command is issued, it calls the VSS requester to
freeze filesystems and applications. On thaw command, it again tells the VSS
requester to thaw them.

This also adds calling of initialize functions for the VSS requester.

Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
---
 qga/Makefile.objs    |    1 
 qga/commands-win32.c |   82 ++++++++++++++++++++++++++---
 qga/vss-win32.c      |  141 ++++++++++++++++++++++++++++++++++++++++++++++++++
 qga/vss-win32.h      |   24 +++++++++
 4 files changed, 240 insertions(+), 8 deletions(-)
 create mode 100644 qga/vss-win32.c
 create mode 100644 qga/vss-win32.h
Michael Roth - July 30, 2013, 8:17 p.m.
Quoting Tomoki Sekiyama (2013-07-23 17:45:58)
> Support guest-fsfreeze-freeze and guest-fsfreeze-thaw commands for Windows
> guests. When fsfreeze command is issued, it calls the VSS requester to
> freeze filesystems and applications. On thaw command, it again tells the VSS
> requester to thaw them.
> 
> This also adds calling of initialize functions for the VSS requester.
> 
> Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>

Other than s/Reqeuster/Requester/ typo mentioned in 7/10, looks good:

Reviewed-by: Michael Roth <mdroth@linux.vnet.ibm.com>

> ---
>  qga/Makefile.objs    |    1 
>  qga/commands-win32.c |   82 ++++++++++++++++++++++++++---
>  qga/vss-win32.c      |  141 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  qga/vss-win32.h      |   24 +++++++++
>  4 files changed, 240 insertions(+), 8 deletions(-)
>  create mode 100644 qga/vss-win32.c
>  create mode 100644 qga/vss-win32.h
> 
> diff --git a/qga/Makefile.objs b/qga/Makefile.objs
> index 4891732..ae1c28b 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..ef5f0df
> --- /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";
> +    QGAVSSReuqesterFunc func;
> +    ErrorSet errset = {
> +        .error_set = (ErrorSetFunc)error_set_win32,
> +        .errp = (void **)err,
> +        .err_class = ERROR_CLASS_GENERIC_ERROR
> +    };
> +
> +    func = (QGAVSSReuqesterFunc)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

Patch

diff --git a/qga/Makefile.objs b/qga/Makefile.objs
index 4891732..ae1c28b 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..ef5f0df
--- /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";
+    QGAVSSReuqesterFunc func;
+    ErrorSet errset = {
+        .error_set = (ErrorSetFunc)error_set_win32,
+        .errp = (void **)err,
+        .err_class = ERROR_CLASS_GENERIC_ERROR
+    };
+
+    func = (QGAVSSReuqesterFunc)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