summaryrefslogtreecommitdiffstats
path: root/qga/vss-win32.c
blob: a80933c98bccef35231d1d9bb18e382c3cf277a7 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
/*
 * 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 "qemu/osdep.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;
}

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);
}

/* Call VSS requester and freeze/thaw filesystems and applications */
void qga_vss_fsfreeze(int *nr_volume, bool freeze, Error **errp)
{
    const char *func_name = freeze ? "requester_freeze" : "requester_thaw";
    QGAVSSRequesterFunc func;
    ErrorSet errset = {
        .error_setg_win32_wrapper = error_setg_win32_internal,
        .errp = errp,
    };

    g_assert(errp);             /* requester.cpp requires it */
    func = (QGAVSSRequesterFunc)GetProcAddress(provider_lib, func_name);
    if (!func) {
        error_setg_win32(errp, GetLastError(), "failed to load %s from %s",
                         func_name, QGA_VSS_DLL);
        return;
    }

    func(nr_volume, &errset);
}