Revision b39297ae

b/Makefile
235 235
	rm -f qemu-options.def
236 236
	find . -name '*.[oda]' -type f -exec rm -f {} +
237 237
	find . -name '*.l[oa]' -type f -exec rm -f {} +
238
	rm -f $(TOOLS) $(HELPERS-y) qemu-ga TAGS cscope.* *.pod *~ */*~
238
	rm -f $(filter-out %.tlb,$(TOOLS)) $(HELPERS-y) qemu-ga TAGS cscope.* *.pod *~ */*~
239 239
	rm -Rf .libs
240 240
	rm -f qemu-img-cmds.h
241 241
	@# May not be present in GENERATED_HEADERS
b/Makefile.objs
109 109
# FIXME: a few definitions from qapi-types.o/qapi-visit.o are needed
110 110
# by libqemuutil.a.  These should be moved to a separate .json schema.
111 111
qga-obj-y = qga/ qapi-types.o qapi-visit.o
112
qga-vss-dll-obj-y = qga/
112 113

  
113 114
vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS)
114 115

  
......
120 121
	stub-obj-y \
121 122
	util-obj-y \
122 123
	qga-obj-y \
124
	qga-vss-dll-obj-y \
123 125
	block-obj-y \
124 126
	common-obj-y
125 127
dummy := $(call unnest-vars)
b/configure
3568 3568
  fi
3569 3569
fi
3570 3570
if [ "$guest_agent" != "no" ]; then
3571
  if [ "$linux" = "yes" -o "$bsd" = "yes" -o "$solaris" = "yes" ] ; then
3571
  if [ "$linux" = "yes" -o "$bsd" = "yes" -o "$solaris" = "yes" -o "$mingw32" = "yes" ] ; then
3572 3572
      tools="qemu-ga\$(EXESUF) $tools"
3573
      if [ "$mingw32" = "yes" -a "$guest_agent_with_vss" = "yes" ]; then
3574
        tools="qga/vss-win32/qga-vss.dll qga/vss-win32/qga-vss.tlb $tools"
3575
      fi
3573 3576
      guest_agent=yes
3574 3577
  elif [ "$guest_agent" != yes ]; then
3575 3578
      guest_agent=no
b/qga/Makefile.objs
3 3
qga-obj-$(CONFIG_WIN32) += commands-win32.o channel-win32.o service-win32.o
4 4
qga-obj-y += qapi-generated/qga-qapi-types.o qapi-generated/qga-qapi-visit.o
5 5
qga-obj-y += qapi-generated/qga-qmp-marshal.o
6

  
7
qga-vss-dll-obj-$(CONFIG_QGA_VSS) += vss-win32/
b/qga/vss-win32/Makefile.objs
1
# rules to build qga-vss.dll
2

  
3
qga-vss-dll-obj-y += requester.o provider.o install.o
4

  
5
obj-qga-vss-dll-obj-y = $(addprefix $(obj)/, $(qga-vss-dll-obj-y))
6
$(obj-qga-vss-dll-obj-y): QEMU_CXXFLAGS = $(filter-out -Wstrict-prototypes -Wmissing-prototypes -Wnested-externs -Wold-style-declaration -Wold-style-definition -Wredundant-decls -fstack-protector-all, $(QEMU_CFLAGS)) -Wno-unknown-pragmas -Wno-delete-non-virtual-dtor
7

  
8
$(obj)/qga-vss.dll: LDFLAGS = -shared -Wl,--add-stdcall-alias,--enable-stdcall-fixup -lole32 -loleaut32 -lshlwapi -luuid -static
9
$(obj)/qga-vss.dll: $(obj-qga-vss-dll-obj-y) $(SRC_PATH)/$(obj)/qga-vss.def
10
	$(call quiet-command,$(CXX) -o $@ $(qga-vss-dll-obj-y) $(SRC_PATH)/qga/vss-win32/qga-vss.def $(CXXFLAGS) $(LDFLAGS),"  LINK  $(TARGET_DIR)$@")
11

  
12

  
13
# rules to build qga-provider.tlb
14
# Currently, only native build is supported because building .tlb
15
# (TypeLibrary) from .idl requires WindowsSDK and MIDL (and cl.exe in VC++).
16
MIDL=$(WIN_SDK)/Bin/midl
17

  
18
$(obj)/qga-vss.tlb: $(SRC_PATH)/$(obj)/qga-vss.idl
19
ifeq ($(WIN_SDK),"")
20
	$(call quiet-command,cp $(dir $<)qga-vss.tlb $@, "  COPY  $(TARGET_DIR)$@")
21
else
22
	$(call quiet-command,$(MIDL) -tlb $@ -I $(WIN_SDK)/Include $<,"  MIDL  $(TARGET_DIR)$@")
23
endif
b/qga/vss-win32/install.cpp
1
/*
2
 * QEMU Guest Agent win32 VSS Provider installer
3
 *
4
 * Copyright Hitachi Data Systems Corp. 2013
5
 *
6
 * Authors:
7
 *  Tomoki Sekiyama   <tomoki.sekiyama@hds.com>
8
 *
9
 * This work is licensed under the terms of the GNU GPL, version 2 or later.
10
 * See the COPYING file in the top-level directory.
11
 */
12

  
13
#include <stdio.h>
14
#include <string.h>
15

  
16
#include "vss-common.h"
17
#include "inc/win2003/vscoordint.h"
18

  
19
#include <comadmin.h>
20
#include <wbemidl.h>
21
#include <comdef.h>
22
#include <comutil.h>
23

  
24
extern HINSTANCE g_hinstDll;
25

  
26
const GUID CLSID_COMAdminCatalog = { 0xF618C514, 0xDFB8, 0x11d1,
27
    {0xA2, 0xCF, 0x00, 0x80, 0x5F, 0xC7, 0x92, 0x35} };
28
const GUID IID_ICOMAdminCatalog = { 0xDD662187, 0xDFC2, 0x11d1,
29
    {0xA2, 0xCF, 0x00, 0x80, 0x5F, 0xC7, 0x92, 0x35} };
30
const GUID CLSID_WbemLocator = { 0x4590f811, 0x1d3a, 0x11d0,
31
    {0x89, 0x1f, 0x00, 0xaa, 0x00, 0x4b, 0x2e, 0x24} };
32
const GUID IID_IWbemLocator = { 0xdc12a687, 0x737f, 0x11cf,
33
    {0x88, 0x4d, 0x00, 0xaa, 0x00, 0x4b, 0x2e, 0x24} };
34

  
35
void errmsg(DWORD err, const char *text)
36
{
37
    /*
38
     * `text' contains function call statement when errmsg is called via chk().
39
     * To make error message more readable, we cut off the text after '('.
40
     * If text doesn't contains '(', negative precision is given, which is
41
     * treated as though it were missing.
42
     */
43
    char *msg = NULL, *nul = strchr(text, '(');
44
    int len = nul ? nul - text : -1;
45

  
46
    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
47
                  FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
48
                  NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
49
                  (char *)&msg, 0, NULL);
50
    fprintf(stderr, "%.*s. (Error: %lx) %s\n", len, text, err, msg);
51
    LocalFree(msg);
52
}
53

  
54
static void errmsg_dialog(DWORD err, const char *text, const char *opt = "")
55
{
56
    char *msg, buf[512];
57

  
58
    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
59
                  FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
60
                  NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
61
                  (char *)&msg, 0, NULL);
62
    snprintf(buf, sizeof(buf), "%s%s. (Error: %lx) %s", text, opt, err, msg);
63
    MessageBox(NULL, buf, "Error from " QGA_PROVIDER_NAME, MB_OK|MB_ICONERROR);
64
    LocalFree(msg);
65
}
66

  
67
#define _chk(hr, status, msg, err_label)        \
68
    do {                                        \
69
        hr = (status);                          \
70
        if (FAILED(hr)) {                       \
71
            errmsg(hr, msg);                    \
72
            goto err_label;                     \
73
        }                                       \
74
    } while (0)
75

  
76
#define chk(status) _chk(hr, status, "Failed to " #status, out)
77

  
78
void __stdcall _com_issue_error(HRESULT hr)
79
{
80
    errmsg(hr, "Unexpected error in COM");
81
}
82

  
83
template<class T>
84
HRESULT put_Value(ICatalogObject *pObj, LPCWSTR name, T val)
85
{
86
    return pObj->put_Value(_bstr_t(name), _variant_t(val));
87
}
88

  
89
/* Lookup Administrators group name from winmgmt */
90
static HRESULT GetAdminName(_bstr_t *name)
91
{
92
    HRESULT hr;
93
    COMPointer<IWbemLocator> pLoc;
94
    COMPointer<IWbemServices> pSvc;
95
    COMPointer<IEnumWbemClassObject> pEnum;
96
    COMPointer<IWbemClassObject> pWobj;
97
    ULONG returned;
98
    _variant_t var;
99

  
100
    chk(CoCreateInstance(CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER,
101
                         IID_IWbemLocator, (LPVOID *)pLoc.replace()));
102
    chk(pLoc->ConnectServer(_bstr_t(L"ROOT\\CIMV2"), NULL, NULL, NULL,
103
                            0, 0, 0, pSvc.replace()));
104
    chk(CoSetProxyBlanket(pSvc, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE,
105
                          NULL, RPC_C_AUTHN_LEVEL_CALL,
106
                          RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE));
107
    chk(pSvc->ExecQuery(_bstr_t(L"WQL"),
108
                        _bstr_t(L"select * from Win32_Account where "
109
                                "SID='S-1-5-32-544' and localAccount=TRUE"),
110
                        WBEM_FLAG_RETURN_IMMEDIATELY | WBEM_FLAG_FORWARD_ONLY,
111
                        NULL, pEnum.replace()));
112
    if (!pEnum) {
113
        hr = E_FAIL;
114
        errmsg(hr, "Failed to query for Administrators");
115
        goto out;
116
    }
117
    chk(pEnum->Next(WBEM_INFINITE, 1, pWobj.replace(), &returned));
118
    if (returned == 0) {
119
        hr = E_FAIL;
120
        errmsg(hr, "No Administrators found");
121
        goto out;
122
    }
123

  
124
    chk(pWobj->Get(_bstr_t(L"Name"), 0, &var, 0, 0));
125
    try {
126
        *name = var;
127
    } catch(...) {
128
        hr = E_FAIL;
129
        errmsg(hr, "Failed to get name of Administrators");
130
        goto out;
131
    }
132

  
133
out:
134
    return hr;
135
}
136

  
137
/* Find and iterate QGA VSS provider in COM+ Application Catalog */
138
static HRESULT QGAProviderFind(
139
    HRESULT (*found)(ICatalogCollection *, int, void *), void *arg)
140
{
141
    HRESULT hr;
142
    COMInitializer initializer;
143
    COMPointer<IUnknown> pUnknown;
144
    COMPointer<ICOMAdminCatalog> pCatalog;
145
    COMPointer<ICatalogCollection> pColl;
146
    COMPointer<ICatalogObject> pObj;
147
    _variant_t var;
148
    long i, n;
149

  
150
    chk(CoCreateInstance(CLSID_COMAdminCatalog, NULL, CLSCTX_INPROC_SERVER,
151
                         IID_IUnknown, (void **)pUnknown.replace()));
152
    chk(pUnknown->QueryInterface(IID_ICOMAdminCatalog,
153
                                 (void **)pCatalog.replace()));
154
    chk(pCatalog->GetCollection(_bstr_t(L"Applications"),
155
                                (IDispatch **)pColl.replace()));
156
    chk(pColl->Populate());
157

  
158
    chk(pColl->get_Count(&n));
159
    for (i = n - 1; i >= 0; i--) {
160
        chk(pColl->get_Item(i, (IDispatch **)pObj.replace()));
161
        chk(pObj->get_Value(_bstr_t(L"Name"), &var));
162
        if (var == _variant_t(QGA_PROVIDER_LNAME)) {
163
            if (FAILED(found(pColl, i, arg))) {
164
                goto out;
165
            }
166
        }
167
    }
168
    chk(pColl->SaveChanges(&n));
169

  
170
out:
171
    return hr;
172
}
173

  
174
/* Count QGA VSS provider in COM+ Application Catalog */
175
static HRESULT QGAProviderCount(ICatalogCollection *coll, int i, void *arg)
176
{
177
    (*(int *)arg)++;
178
    return S_OK;
179
}
180

  
181
/* Remove QGA VSS provider from COM+ Application Catalog Collection */
182
static HRESULT QGAProviderRemove(ICatalogCollection *coll, int i, void *arg)
183
{
184
    HRESULT hr;
185

  
186
    fprintf(stderr, "Removing COM+ Application: %s\n", QGA_PROVIDER_NAME);
187
    chk(coll->Remove(i));
188
out:
189
    return hr;
190
}
191

  
192
/* Unregister this module from COM+ Applications Catalog */
193
STDAPI COMUnregister(void)
194
{
195
    HRESULT hr;
196

  
197
    DllUnregisterServer();
198
    chk(QGAProviderFind(QGAProviderRemove, NULL));
199
out:
200
    return hr;
201
}
202

  
203
/* Register this module to COM+ Applications Catalog */
204
STDAPI COMRegister(void)
205
{
206
    HRESULT hr;
207
    COMInitializer initializer;
208
    COMPointer<IUnknown> pUnknown;
209
    COMPointer<ICOMAdminCatalog> pCatalog;
210
    COMPointer<ICatalogCollection> pApps, pRoles, pUsersInRole;
211
    COMPointer<ICatalogObject> pObj;
212
    long n;
213
    _bstr_t name;
214
    _variant_t key;
215
    CHAR dllPath[MAX_PATH], tlbPath[MAX_PATH];
216
    bool unregisterOnFailure = false;
217
    int count = 0;
218

  
219
    if (!g_hinstDll) {
220
        errmsg(E_FAIL, "Failed to initialize DLL");
221
        return E_FAIL;
222
    }
223

  
224
    chk(QGAProviderFind(QGAProviderCount, (void *)&count));
225
    if (count) {
226
        errmsg(E_ABORT, "QGA VSS Provider is already installed");
227
        return E_ABORT;
228
    }
229

  
230
    chk(CoCreateInstance(CLSID_COMAdminCatalog, NULL, CLSCTX_INPROC_SERVER,
231
                         IID_IUnknown, (void **)pUnknown.replace()));
232
    chk(pUnknown->QueryInterface(IID_ICOMAdminCatalog,
233
                                 (void **)pCatalog.replace()));
234

  
235
    /* Install COM+ Component */
236

  
237
    chk(pCatalog->GetCollection(_bstr_t(L"Applications"),
238
                                (IDispatch **)pApps.replace()));
239
    chk(pApps->Populate());
240
    chk(pApps->Add((IDispatch **)&pObj));
241
    chk(put_Value(pObj, L"Name",        QGA_PROVIDER_LNAME));
242
    chk(put_Value(pObj, L"Description", QGA_PROVIDER_LNAME));
243
    chk(put_Value(pObj, L"ApplicationAccessChecksEnabled", true));
244
    chk(put_Value(pObj, L"Authentication",                 short(6)));
245
    chk(put_Value(pObj, L"AuthenticationCapability",       short(2)));
246
    chk(put_Value(pObj, L"ImpersonationLevel",             short(2)));
247
    chk(pApps->SaveChanges(&n));
248

  
249
    /* The app should be deleted if something fails after SaveChanges */
250
    unregisterOnFailure = true;
251

  
252
    chk(pObj->get_Key(&key));
253

  
254
    if (!GetModuleFileName(g_hinstDll, dllPath, sizeof(dllPath))) {
255
        hr = HRESULT_FROM_WIN32(GetLastError());
256
        errmsg(hr, "GetModuleFileName failed");
257
        goto out;
258
    }
259
    n = strlen(dllPath);
260
    if (n < 3) {
261
        hr = E_FAIL;
262
        errmsg(hr, "Failed to lookup dll");
263
        goto out;
264
    }
265
    strcpy(tlbPath, dllPath);
266
    strcpy(tlbPath+n-3, "tlb");
267
    fprintf(stderr, "Registering " QGA_PROVIDER_NAME ":\n");
268
    fprintf(stderr, "  %s\n", dllPath);
269
    fprintf(stderr, "  %s\n", tlbPath);
270
    if (!PathFileExists(tlbPath)) {
271
        hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
272
        errmsg(hr, "Failed to lookup tlb");
273
        goto out;
274
    }
275

  
276
    chk(pCatalog->InstallComponent(_bstr_t(QGA_PROVIDER_LNAME),
277
                                   _bstr_t(dllPath), _bstr_t(tlbPath),
278
                                   _bstr_t("")));
279

  
280
    /* Setup roles of the applicaion */
281

  
282
    chk(pApps->GetCollection(_bstr_t(L"Roles"), key,
283
                             (IDispatch **)pRoles.replace()));
284
    chk(pRoles->Populate());
285
    chk(pRoles->Add((IDispatch **)pObj.replace()));
286
    chk(put_Value(pObj, L"Name",        L"Administrators"));
287
    chk(put_Value(pObj, L"Description", L"Administrators group"));
288
    chk(pRoles->SaveChanges(&n));
289
    chk(pObj->get_Key(&key));
290

  
291
    /* Setup users in the role */
292

  
293
    chk(pRoles->GetCollection(_bstr_t(L"UsersInRole"), key,
294
                              (IDispatch **)pUsersInRole.replace()));
295
    chk(pUsersInRole->Populate());
296

  
297
    chk(pUsersInRole->Add((IDispatch **)pObj.replace()));
298
    chk(GetAdminName(&name));
299
    chk(put_Value(pObj, L"User", _bstr_t(".\\") + name));
300

  
301
    chk(pUsersInRole->Add((IDispatch **)pObj.replace()));
302
    chk(put_Value(pObj, L"User", L"SYSTEM"));
303
    chk(pUsersInRole->SaveChanges(&n));
304

  
305
out:
306
    if (unregisterOnFailure && FAILED(hr)) {
307
        COMUnregister();
308
    }
309

  
310
    return hr;
311
}
312

  
313

  
314
static BOOL CreateRegistryKey(LPCTSTR key, LPCTSTR value, LPCTSTR data)
315
{
316
    HKEY  hKey;
317
    LONG  ret;
318
    DWORD size;
319

  
320
    ret = RegCreateKeyEx(HKEY_CLASSES_ROOT, key, 0, NULL,
321
        REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, NULL);
322
    if (ret != ERROR_SUCCESS) {
323
        goto out;
324
    }
325

  
326
    if (data != NULL) {
327
        size = strlen(data) + 1;
328
    } else {
329
        size = 0;
330
    }
331

  
332
    ret = RegSetValueEx(hKey, value, 0, REG_SZ, (LPBYTE)data, size);
333
    RegCloseKey(hKey);
334

  
335
out:
336
    if (ret != ERROR_SUCCESS) {
337
        /* As we cannot printf within DllRegisterServer(), show a dialog. */
338
        errmsg_dialog(ret, "Cannot add registry", key);
339
        return FALSE;
340
    }
341
    return TRUE;
342
}
343

  
344
/* Register this dll as a VSS provider */
345
STDAPI DllRegisterServer(void)
346
{
347
    COMInitializer initializer;
348
    COMPointer<IVssAdmin> pVssAdmin;
349
    HRESULT hr = E_FAIL;
350
    char dllPath[MAX_PATH];
351
    char key[256];
352

  
353
    if (!g_hinstDll) {
354
        errmsg_dialog(hr, "Module instance is not available");
355
        goto out;
356
    }
357

  
358
    /* Add this module to registery */
359

  
360
    sprintf(key, "CLSID\\%s", g_szClsid);
361
    if (!CreateRegistryKey(key, NULL, g_szClsid)) {
362
        goto out;
363
    }
364

  
365
    if (!GetModuleFileName(g_hinstDll, dllPath, sizeof(dllPath))) {
366
        errmsg_dialog(GetLastError(), "GetModuleFileName failed");
367
        goto out;
368
    }
369

  
370
    sprintf(key, "CLSID\\%s\\InprocServer32", g_szClsid);
371
    if (!CreateRegistryKey(key, NULL, dllPath)) {
372
        goto out;
373
    }
374

  
375
    if (!CreateRegistryKey(key, "ThreadingModel", "Apartment")) {
376
        goto out;
377
    }
378

  
379
    sprintf(key, "CLSID\\%s\\ProgID", g_szClsid);
380
    if (!CreateRegistryKey(key, NULL, g_szProgid)) {
381
        goto out;
382
    }
383

  
384
    if (!CreateRegistryKey(g_szProgid, NULL, QGA_PROVIDER_NAME)) {
385
        goto out;
386
    }
387

  
388
    sprintf(key, "%s\\CLSID", g_szProgid);
389
    if (!CreateRegistryKey(key, NULL, g_szClsid)) {
390
        goto out;
391
    }
392

  
393
    hr = CoCreateInstance(CLSID_VSSCoordinator, NULL, CLSCTX_ALL,
394
                          IID_IVssAdmin, (void **)pVssAdmin.replace());
395
    if (FAILED(hr)) {
396
        errmsg_dialog(hr, "CoCreateInstance(VSSCoordinator) failed");
397
        goto out;
398
    }
399

  
400
    hr = pVssAdmin->RegisterProvider(g_gProviderId, CLSID_QGAVSSProvider,
401
                                     const_cast<WCHAR*>(QGA_PROVIDER_LNAME),
402
                                     VSS_PROV_SOFTWARE,
403
                                     const_cast<WCHAR*>(QGA_PROVIDER_VERSION),
404
                                     g_gProviderVersion);
405
    if (FAILED(hr)) {
406
        errmsg_dialog(hr, "RegisterProvider failed");
407
    }
408

  
409
out:
410
    if (FAILED(hr)) {
411
        DllUnregisterServer();
412
    }
413

  
414
    return hr;
415
}
416

  
417
/* Unregister this VSS hardware provider from the system */
418
STDAPI DllUnregisterServer(void)
419
{
420
    TCHAR key[256];
421
    COMInitializer initializer;
422
    COMPointer<IVssAdmin> pVssAdmin;
423

  
424
    HRESULT hr = CoCreateInstance(CLSID_VSSCoordinator,
425
                                  NULL, CLSCTX_ALL, IID_IVssAdmin,
426
                                  (void **)pVssAdmin.replace());
427
    if (SUCCEEDED(hr)) {
428
        hr = pVssAdmin->UnregisterProvider(g_gProviderId);
429
    } else {
430
        errmsg(hr, "CoCreateInstance(VSSCoordinator) failed");
431
    }
432

  
433
    sprintf(key, "CLSID\\%s", g_szClsid);
434
    SHDeleteKey(HKEY_CLASSES_ROOT, key);
435
    SHDeleteKey(HKEY_CLASSES_ROOT, g_szProgid);
436

  
437
    return S_OK; /* Uninstall should never fail */
438
}
439

  
440

  
441
/* Support function to convert ASCII string into BSTR (used in _bstr_t) */
442
namespace _com_util
443
{
444
    BSTR WINAPI ConvertStringToBSTR(const char *ascii) {
445
        int len = strlen(ascii);
446
        BSTR bstr = SysAllocStringLen(NULL, len);
447

  
448
        if (!bstr) {
449
            return NULL;
450
        }
451

  
452
        if (mbstowcs(bstr, ascii, len) == (size_t)-1) {
453
            fprintf(stderr, "Failed to convert string '%s' into BSTR", ascii);
454
            bstr[0] = 0;
455
        }
456
        return bstr;
457
    }
458
}
b/qga/vss-win32/provider.cpp
1
/*
2
 * QEMU Guest Agent win32 VSS Provider implementations
3
 *
4
 * Copyright Hitachi Data Systems Corp. 2013
5
 *
6
 * Authors:
7
 *  Tomoki Sekiyama   <tomoki.sekiyama@hds.com>
8
 *
9
 * This work is licensed under the terms of the GNU GPL, version 2 or later.
10
 * See the COPYING file in the top-level directory.
11
 */
12

  
13
#include <stdio.h>
14
#include "vss-common.h"
15
#include "inc/win2003/vscoordint.h"
16
#include "inc/win2003/vsprov.h"
17

  
18
#define VSS_TIMEOUT_MSEC (60*1000)
19

  
20
static long g_nComObjsInUse;
21
HINSTANCE g_hinstDll;
22

  
23
/* VSS common GUID's */
24

  
25
const CLSID CLSID_VSSCoordinator = { 0xE579AB5F, 0x1CC4, 0x44b4,
26
    {0xBE, 0xD9, 0xDE, 0x09, 0x91, 0xFF, 0x06, 0x23} };
27
const IID IID_IVssAdmin = { 0x77ED5996, 0x2F63, 0x11d3,
28
    {0x8A, 0x39, 0x00, 0xC0, 0x4F, 0x72, 0xD8, 0xE3} };
29

  
30
const IID IID_IVssHardwareSnapshotProvider = { 0x9593A157, 0x44E9, 0x4344,
31
    {0xBB, 0xEB, 0x44, 0xFB, 0xF9, 0xB0, 0x6B, 0x10} };
32
const IID IID_IVssSoftwareSnapshotProvider = { 0x609e123e, 0x2c5a, 0x44d3,
33
    {0x8f, 0x01, 0x0b, 0x1d, 0x9a, 0x47, 0xd1, 0xff} };
34
const IID IID_IVssProviderCreateSnapshotSet = { 0x5F894E5B, 0x1E39, 0x4778,
35
    {0x8E, 0x23, 0x9A, 0xBA, 0xD9, 0xF0, 0xE0, 0x8C} };
36
const IID IID_IVssProviderNotifications = { 0xE561901F, 0x03A5, 0x4afe,
37
    {0x86, 0xD0, 0x72, 0xBA, 0xEE, 0xCE, 0x70, 0x04} };
38

  
39
const IID IID_IVssEnumObject = { 0xAE1C7110, 0x2F60, 0x11d3,
40
    {0x8A, 0x39, 0x00, 0xC0, 0x4F, 0x72, 0xD8, 0xE3} };
41

  
42

  
43
void LockModule(BOOL lock)
44
{
45
    if (lock) {
46
        InterlockedIncrement(&g_nComObjsInUse);
47
    } else {
48
        InterlockedDecrement(&g_nComObjsInUse);
49
    }
50
}
51

  
52
/* Empty enumerator for VssObject */
53

  
54
class CQGAVSSEnumObject : public IVssEnumObject
55
{
56
public:
57
    STDMETHODIMP QueryInterface(REFIID riid, void **ppObj);
58
    STDMETHODIMP_(ULONG) AddRef();
59
    STDMETHODIMP_(ULONG) Release();
60

  
61
    /* IVssEnumObject Methods */
62
    STDMETHODIMP Next(
63
        ULONG celt, VSS_OBJECT_PROP *rgelt, ULONG *pceltFetched);
64
    STDMETHODIMP Skip(ULONG celt);
65
    STDMETHODIMP Reset(void);
66
    STDMETHODIMP Clone(IVssEnumObject **ppenum);
67

  
68
    /* CQGAVSSEnumObject Methods */
69
    CQGAVSSEnumObject();
70
    ~CQGAVSSEnumObject();
71

  
72
private:
73
    long m_nRefCount;
74
};
75

  
76
CQGAVSSEnumObject::CQGAVSSEnumObject()
77
{
78
    m_nRefCount = 0;
79
    LockModule(TRUE);
80
}
81

  
82
CQGAVSSEnumObject::~CQGAVSSEnumObject()
83
{
84
    LockModule(FALSE);
85
}
86

  
87
STDMETHODIMP CQGAVSSEnumObject::QueryInterface(REFIID riid, void **ppObj)
88
{
89
    if (riid == IID_IUnknown || riid == IID_IVssEnumObject) {
90
        *ppObj = static_cast<void*>(static_cast<IVssEnumObject*>(this));
91
        AddRef();
92
        return S_OK;
93
    }
94
    *ppObj = NULL;
95
    return E_NOINTERFACE;
96
}
97

  
98
STDMETHODIMP_(ULONG) CQGAVSSEnumObject::AddRef()
99
{
100
    return InterlockedIncrement(&m_nRefCount);
101
}
102

  
103
STDMETHODIMP_(ULONG) CQGAVSSEnumObject::Release()
104
{
105
    long nRefCount = InterlockedDecrement(&m_nRefCount);
106
    if (m_nRefCount == 0) {
107
        delete this;
108
    }
109
    return nRefCount;
110
}
111

  
112
STDMETHODIMP CQGAVSSEnumObject::Next(
113
    ULONG celt, VSS_OBJECT_PROP *rgelt, ULONG *pceltFetched)
114
{
115
    *pceltFetched = 0;
116
    return S_FALSE;
117
}
118

  
119
STDMETHODIMP CQGAVSSEnumObject::Skip(ULONG celt)
120
{
121
    return S_FALSE;
122
}
123

  
124
STDMETHODIMP CQGAVSSEnumObject::Reset(void)
125
{
126
    return S_OK;
127
}
128

  
129
STDMETHODIMP CQGAVSSEnumObject::Clone(IVssEnumObject **ppenum)
130
{
131
    return E_NOTIMPL;
132
}
133

  
134

  
135
/* QGAVssProvider */
136

  
137
class CQGAVssProvider :
138
    public IVssSoftwareSnapshotProvider,
139
    public IVssProviderCreateSnapshotSet,
140
    public IVssProviderNotifications
141
{
142
public:
143
    STDMETHODIMP QueryInterface(REFIID riid, void **ppObj);
144
    STDMETHODIMP_(ULONG) AddRef();
145
    STDMETHODIMP_(ULONG) Release();
146

  
147
    /* IVssSoftwareSnapshotProvider Methods */
148
    STDMETHODIMP SetContext(LONG lContext);
149
    STDMETHODIMP GetSnapshotProperties(
150
        VSS_ID SnapshotId, VSS_SNAPSHOT_PROP *pProp);
151
    STDMETHODIMP Query(
152
        VSS_ID QueriedObjectId, VSS_OBJECT_TYPE eQueriedObjectType,
153
        VSS_OBJECT_TYPE eReturnedObjectsType, IVssEnumObject **ppEnum);
154
    STDMETHODIMP DeleteSnapshots(
155
        VSS_ID SourceObjectId, VSS_OBJECT_TYPE eSourceObjectType,
156
        BOOL bForceDelete, LONG *plDeletedSnapshots,
157
        VSS_ID *pNondeletedSnapshotID);
158
    STDMETHODIMP BeginPrepareSnapshot(
159
        VSS_ID SnapshotSetId, VSS_ID SnapshotId,
160
        VSS_PWSZ pwszVolumeName, LONG lNewContext);
161
    STDMETHODIMP IsVolumeSupported(
162
        VSS_PWSZ pwszVolumeName, BOOL *pbSupportedByThisProvider);
163
    STDMETHODIMP IsVolumeSnapshotted(
164
        VSS_PWSZ pwszVolumeName, BOOL *pbSnapshotsPresent,
165
        LONG *plSnapshotCompatibility);
166
    STDMETHODIMP SetSnapshotProperty(
167
        VSS_ID SnapshotId, VSS_SNAPSHOT_PROPERTY_ID eSnapshotPropertyId,
168
        VARIANT vProperty);
169
    STDMETHODIMP RevertToSnapshot(VSS_ID SnapshotId);
170
    STDMETHODIMP QueryRevertStatus(VSS_PWSZ pwszVolume, IVssAsync **ppAsync);
171

  
172
    /* IVssProviderCreateSnapshotSet Methods */
173
    STDMETHODIMP EndPrepareSnapshots(VSS_ID SnapshotSetId);
174
    STDMETHODIMP PreCommitSnapshots(VSS_ID SnapshotSetId);
175
    STDMETHODIMP CommitSnapshots(VSS_ID SnapshotSetId);
176
    STDMETHODIMP PostCommitSnapshots(
177
        VSS_ID SnapshotSetId, LONG lSnapshotsCount);
178
    STDMETHODIMP PreFinalCommitSnapshots(VSS_ID SnapshotSetId);
179
    STDMETHODIMP PostFinalCommitSnapshots(VSS_ID SnapshotSetId);
180
    STDMETHODIMP AbortSnapshots(VSS_ID SnapshotSetId);
181

  
182
    /* IVssProviderNotifications Methods */
183
    STDMETHODIMP OnLoad(IUnknown *pCallback);
184
    STDMETHODIMP OnUnload(BOOL bForceUnload);
185

  
186
    /* CQGAVssProvider Methods */
187
    CQGAVssProvider();
188
    ~CQGAVssProvider();
189

  
190
private:
191
    long m_nRefCount;
192
};
193

  
194
CQGAVssProvider::CQGAVssProvider()
195
{
196
    m_nRefCount = 0;
197
    LockModule(TRUE);
198
}
199

  
200
CQGAVssProvider::~CQGAVssProvider()
201
{
202
    LockModule(FALSE);
203
}
204

  
205
STDMETHODIMP CQGAVssProvider::QueryInterface(REFIID riid, void **ppObj)
206
{
207
    if (riid == IID_IUnknown) {
208
        *ppObj = static_cast<void*>(this);
209
        AddRef();
210
        return S_OK;
211
    }
212
    if (riid == IID_IVssSoftwareSnapshotProvider) {
213
        *ppObj = static_cast<void*>(
214
            static_cast<IVssSoftwareSnapshotProvider*>(this));
215
        AddRef();
216
        return S_OK;
217
    }
218
    if (riid == IID_IVssProviderCreateSnapshotSet) {
219
        *ppObj = static_cast<void*>(
220
            static_cast<IVssProviderCreateSnapshotSet*>(this));
221
        AddRef();
222
        return S_OK;
223
    }
224
    if (riid == IID_IVssProviderNotifications) {
225
        *ppObj = static_cast<void*>(
226
            static_cast<IVssProviderNotifications*>(this));
227
        AddRef();
228
        return S_OK;
229
    }
230
    *ppObj = NULL;
231
    return E_NOINTERFACE;
232
}
233

  
234
STDMETHODIMP_(ULONG) CQGAVssProvider::AddRef()
235
{
236
    return InterlockedIncrement(&m_nRefCount);
237
}
238

  
239
STDMETHODIMP_(ULONG) CQGAVssProvider::Release()
240
{
241
    long nRefCount = InterlockedDecrement(&m_nRefCount);
242
    if (m_nRefCount == 0) {
243
        delete this;
244
    }
245
    return nRefCount;
246
}
247

  
248

  
249
/*
250
 * IVssSoftwareSnapshotProvider methods
251
 */
252

  
253
STDMETHODIMP CQGAVssProvider::SetContext(LONG lContext)
254
{
255
    return S_OK;
256
}
257

  
258
STDMETHODIMP CQGAVssProvider::GetSnapshotProperties(
259
    VSS_ID SnapshotId, VSS_SNAPSHOT_PROP *pProp)
260
{
261
    return VSS_E_OBJECT_NOT_FOUND;
262
}
263

  
264
STDMETHODIMP CQGAVssProvider::Query(
265
    VSS_ID QueriedObjectId, VSS_OBJECT_TYPE eQueriedObjectType,
266
    VSS_OBJECT_TYPE eReturnedObjectsType, IVssEnumObject **ppEnum)
267
{
268
    try {
269
        *ppEnum = new CQGAVSSEnumObject;
270
    } catch (...) {
271
        return E_OUTOFMEMORY;
272
    }
273
    (*ppEnum)->AddRef();
274
    return S_OK;
275
}
276

  
277
STDMETHODIMP CQGAVssProvider::DeleteSnapshots(
278
    VSS_ID SourceObjectId, VSS_OBJECT_TYPE eSourceObjectType,
279
    BOOL bForceDelete, LONG *plDeletedSnapshots, VSS_ID *pNondeletedSnapshotID)
280
{
281
    return E_NOTIMPL;
282
}
283

  
284
STDMETHODIMP CQGAVssProvider::BeginPrepareSnapshot(
285
    VSS_ID SnapshotSetId, VSS_ID SnapshotId,
286
    VSS_PWSZ pwszVolumeName, LONG lNewContext)
287
{
288
    return S_OK;
289
}
290

  
291
STDMETHODIMP CQGAVssProvider::IsVolumeSupported(
292
    VSS_PWSZ pwszVolumeName, BOOL *pbSupportedByThisProvider)
293
{
294
    *pbSupportedByThisProvider = TRUE;
295

  
296
    return S_OK;
297
}
298

  
299
STDMETHODIMP CQGAVssProvider::IsVolumeSnapshotted(VSS_PWSZ pwszVolumeName,
300
    BOOL *pbSnapshotsPresent, LONG *plSnapshotCompatibility)
301
{
302
    *pbSnapshotsPresent = FALSE;
303
    *plSnapshotCompatibility = 0;
304
    return S_OK;
305
}
306

  
307
STDMETHODIMP CQGAVssProvider::SetSnapshotProperty(VSS_ID SnapshotId,
308
    VSS_SNAPSHOT_PROPERTY_ID eSnapshotPropertyId, VARIANT vProperty)
309
{
310
    return E_NOTIMPL;
311
}
312

  
313
STDMETHODIMP CQGAVssProvider::RevertToSnapshot(VSS_ID SnapshotId)
314
{
315
    return E_NOTIMPL;
316
}
317

  
318
STDMETHODIMP CQGAVssProvider::QueryRevertStatus(
319
    VSS_PWSZ pwszVolume, IVssAsync **ppAsync)
320
{
321
    return E_NOTIMPL;
322
}
323

  
324

  
325
/*
326
 * IVssProviderCreateSnapshotSet methods
327
 */
328

  
329
STDMETHODIMP CQGAVssProvider::EndPrepareSnapshots(VSS_ID SnapshotSetId)
330
{
331
    return S_OK;
332
}
333

  
334
STDMETHODIMP CQGAVssProvider::PreCommitSnapshots(VSS_ID SnapshotSetId)
335
{
336
    return S_OK;
337
}
338

  
339
STDMETHODIMP CQGAVssProvider::CommitSnapshots(VSS_ID SnapshotSetId)
340
{
341
    HRESULT hr = S_OK;
342
    HANDLE hEventFrozen, hEventThaw, hEventTimeout;
343

  
344
    hEventFrozen = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_FROZEN);
345
    if (hEventFrozen == INVALID_HANDLE_VALUE) {
346
        return E_FAIL;
347
    }
348

  
349
    hEventThaw = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_THAW);
350
    if (hEventThaw == INVALID_HANDLE_VALUE) {
351
        CloseHandle(hEventFrozen);
352
        return E_FAIL;
353
    }
354

  
355
    hEventTimeout = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_TIMEOUT);
356
    if (hEventTimeout == INVALID_HANDLE_VALUE) {
357
        CloseHandle(hEventFrozen);
358
        CloseHandle(hEventThaw);
359
        return E_FAIL;
360
    }
361

  
362
    /* Send event to qemu-ga to notify filesystem is frozen */
363
    SetEvent(hEventFrozen);
364

  
365
    /* Wait until the snapshot is taken by the host. */
366
    if (WaitForSingleObject(hEventThaw, VSS_TIMEOUT_MSEC) != WAIT_OBJECT_0) {
367
        /* Send event to qemu-ga to notify the provider is timed out */
368
        SetEvent(hEventTimeout);
369
        hr = E_ABORT;
370
    }
371

  
372
    CloseHandle(hEventThaw);
373
    CloseHandle(hEventFrozen);
374
    CloseHandle(hEventTimeout);
375
    return hr;
376
}
377

  
378
STDMETHODIMP CQGAVssProvider::PostCommitSnapshots(
379
    VSS_ID SnapshotSetId, LONG lSnapshotsCount)
380
{
381
    return S_OK;
382
}
383

  
384
STDMETHODIMP CQGAVssProvider::PreFinalCommitSnapshots(VSS_ID SnapshotSetId)
385
{
386
    return S_OK;
387
}
388

  
389
STDMETHODIMP CQGAVssProvider::PostFinalCommitSnapshots(VSS_ID SnapshotSetId)
390
{
391
    return S_OK;
392
}
393

  
394
STDMETHODIMP CQGAVssProvider::AbortSnapshots(VSS_ID SnapshotSetId)
395
{
396
    return S_OK;
397
}
398

  
399
/*
400
 * IVssProviderNotifications methods
401
 */
402

  
403
STDMETHODIMP CQGAVssProvider::OnLoad(IUnknown *pCallback)
404
{
405
    return S_OK;
406
}
407

  
408
STDMETHODIMP CQGAVssProvider::OnUnload(BOOL bForceUnload)
409
{
410
    return S_OK;
411
}
412

  
413

  
414
/*
415
 * CQGAVssProviderFactory class
416
 */
417

  
418
class CQGAVssProviderFactory : public IClassFactory
419
{
420
public:
421
    STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
422
    STDMETHODIMP_(ULONG) AddRef();
423
    STDMETHODIMP_(ULONG) Release();
424
    STDMETHODIMP CreateInstance(
425
        IUnknown *pUnknownOuter, REFIID iid, void **ppv);
426
    STDMETHODIMP LockServer(BOOL lock) { return E_NOTIMPL; }
427

  
428
    CQGAVssProviderFactory();
429
    ~CQGAVssProviderFactory();
430

  
431
private:
432
    long m_nRefCount;
433
};
434

  
435
CQGAVssProviderFactory::CQGAVssProviderFactory()
436
{
437
    m_nRefCount = 0;
438
    LockModule(TRUE);
439
}
440

  
441
CQGAVssProviderFactory::~CQGAVssProviderFactory()
442
{
443
    LockModule(FALSE);
444
}
445

  
446
STDMETHODIMP CQGAVssProviderFactory::QueryInterface(REFIID riid, void **ppv)
447
{
448
    if (riid == IID_IUnknown || riid == IID_IClassFactory) {
449
        *ppv = static_cast<void*>(this);
450
        AddRef();
451
        return S_OK;
452
    }
453
    *ppv = NULL;
454
    return E_NOINTERFACE;
455
}
456

  
457
STDMETHODIMP_(ULONG) CQGAVssProviderFactory::AddRef()
458
{
459
    return InterlockedIncrement(&m_nRefCount);
460
}
461

  
462
STDMETHODIMP_(ULONG) CQGAVssProviderFactory::Release()
463
{
464
    long nRefCount = InterlockedDecrement(&m_nRefCount);
465
    if (m_nRefCount == 0) {
466
        delete this;
467
    }
468
    return nRefCount;
469
}
470

  
471
STDMETHODIMP CQGAVssProviderFactory::CreateInstance(
472
    IUnknown *pUnknownOuter, REFIID iid, void **ppv)
473
{
474
    CQGAVssProvider *pObj;
475

  
476
    if (pUnknownOuter) {
477
        return CLASS_E_NOAGGREGATION;
478
    }
479
    try {
480
        pObj = new CQGAVssProvider;
481
    } catch (...) {
482
        return E_OUTOFMEMORY;
483
    }
484
    HRESULT hr = pObj->QueryInterface(iid, ppv);
485
    if (FAILED(hr)) {
486
        delete pObj;
487
    }
488
    return hr;
489
}
490

  
491

  
492
/*
493
 * DLL functions
494
 */
495

  
496
STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
497
{
498
    CQGAVssProviderFactory *factory;
499
    try {
500
        factory = new CQGAVssProviderFactory;
501
    } catch (...) {
502
        return E_OUTOFMEMORY;
503
    }
504
    factory->AddRef();
505
    HRESULT hr = factory->QueryInterface(riid, ppv);
506
    factory->Release();
507
    return hr;
508
}
509

  
510
STDAPI DllCanUnloadNow()
511
{
512
    return g_nComObjsInUse == 0 ? S_OK : S_FALSE;
513
}
514

  
515
EXTERN_C
516
BOOL WINAPI DllMain(HINSTANCE hinstDll, DWORD dwReason, LPVOID lpReserved)
517
{
518
    if (dwReason == DLL_PROCESS_ATTACH) {
519
        g_hinstDll = hinstDll;
520
        DisableThreadLibraryCalls(hinstDll);
521
    }
522
    return TRUE;
523
}
b/qga/vss-win32/qga-vss.def
1
LIBRARY      "QGA-PROVIDER.DLL"
2

  
3
EXPORTS
4
	COMRegister		PRIVATE
5
	COMUnregister		PRIVATE
6
	DllCanUnloadNow		PRIVATE
7
	DllGetClassObject	PRIVATE
8
	DllRegisterServer	PRIVATE
9
	DllUnregisterServer	PRIVATE
10
	requester_init		PRIVATE
11
	requester_deinit	PRIVATE
12
	requester_freeze	PRIVATE
13
	requester_thaw		PRIVATE
b/qga/vss-win32/qga-vss.idl
1
import "oaidl.idl";
2
import "ocidl.idl";
3

  
4
[
5
    uuid(103B8142-6CE5-48A7-BDE1-794D3192FCF1),
6
    version(1.0),
7
    helpstring("QGAVSSProvider Type Library")
8
]
9
library QGAVSSHWProviderLib
10
{
11
    importlib("stdole2.tlb");
12
    [
13
        uuid(6E6A3492-8D4D-440C-9619-5E5D0CC31CA8),
14
        helpstring("QGAVSSProvider Class")
15
    ]
16
    coclass QGAVSSHWProvider
17
    {
18
        [default] interface IUnknown;
19
    };
20
};
b/qga/vss-win32/requester.cpp
1
/*
2
 * QEMU Guest Agent win32 VSS Requester implementations
3
 *
4
 * Copyright Hitachi Data Systems Corp. 2013
5
 *
6
 * Authors:
7
 *  Tomoki Sekiyama   <tomoki.sekiyama@hds.com>
8
 *
9
 * This work is licensed under the terms of the GNU GPL, version 2 or later.
10
 * See the COPYING file in the top-level directory.
11
 */
12

  
13
#include <stdio.h>
14
#include "vss-common.h"
15
#include "requester.h"
16
#include "assert.h"
17
#include "inc/win2003/vswriter.h"
18
#include "inc/win2003/vsbackup.h"
19

  
20
/* Max wait time for frozen event (VSS can only hold writes for 10 seconds) */
21
#define VSS_TIMEOUT_FREEZE_MSEC 10000
22

  
23
/* Call QueryStatus every 10 ms while waiting for frozen event */
24
#define VSS_TIMEOUT_EVENT_MSEC 10
25

  
26
#define err_set(e, err, fmt, ...) \
27
    ((e)->error_set((e)->errp, err, (e)->err_class, fmt, ## __VA_ARGS__))
28
#define err_is_set(e) ((e)->errp && *(e)->errp)
29

  
30

  
31
/* Handle to VSSAPI.DLL */
32
static HMODULE hLib;
33

  
34
/* Functions in VSSAPI.DLL */
35
typedef HRESULT(STDAPICALLTYPE * t_CreateVssBackupComponents)(
36
    OUT IVssBackupComponents**);
37
typedef void(APIENTRY * t_VssFreeSnapshotProperties)(IN VSS_SNAPSHOT_PROP*);
38
static t_CreateVssBackupComponents pCreateVssBackupComponents;
39
static t_VssFreeSnapshotProperties pVssFreeSnapshotProperties;
40

  
41
/* Variables used while applications and filesystes are frozen by VSS */
42
static struct QGAVSSContext {
43
    IVssBackupComponents *pVssbc;  /* VSS requester interface */
44
    IVssAsync *pAsyncSnapshot;     /* async info of VSS snapshot operation */
45
    HANDLE hEventFrozen;           /* notify fs/writer freeze from provider */
46
    HANDLE hEventThaw;             /* request provider to thaw */
47
    HANDLE hEventTimeout;          /* notify timeout in provider */
48
    int cFrozenVols;               /* number of frozen volumes */
49
} vss_ctx;
50

  
51
STDAPI requester_init(void)
52
{
53
    vss_ctx.hEventFrozen =  INVALID_HANDLE_VALUE;
54
    vss_ctx.hEventThaw = INVALID_HANDLE_VALUE;
55
    vss_ctx.hEventTimeout = INVALID_HANDLE_VALUE;
56

  
57
    COMInitializer initializer; /* to call CoInitializeSecurity */
58
    HRESULT hr = CoInitializeSecurity(
59
        NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
60
        RPC_C_IMP_LEVEL_IDENTIFY, NULL, EOAC_NONE, NULL);
61
    if (FAILED(hr)) {
62
        fprintf(stderr, "failed to CoInitializeSecurity (error %lx)\n", hr);
63
        return hr;
64
    }
65

  
66
    hLib = LoadLibraryA("VSSAPI.DLL");
67
    if (!hLib) {
68
        fprintf(stderr, "failed to load VSSAPI.DLL\n");
69
        return HRESULT_FROM_WIN32(GetLastError());
70
    }
71

  
72
    pCreateVssBackupComponents = (t_CreateVssBackupComponents)
73
        GetProcAddress(hLib,
74
#ifdef _WIN64 /* 64bit environment */
75
        "?CreateVssBackupComponents@@YAJPEAPEAVIVssBackupComponents@@@Z"
76
#else /* 32bit environment */
77
        "?CreateVssBackupComponents@@YGJPAPAVIVssBackupComponents@@@Z"
78
#endif
79
        );
80
    if (!pCreateVssBackupComponents) {
81
        fprintf(stderr, "failed to get proc address from VSSAPI.DLL\n");
82
        return HRESULT_FROM_WIN32(GetLastError());
83
    }
84

  
85
    pVssFreeSnapshotProperties = (t_VssFreeSnapshotProperties)
86
        GetProcAddress(hLib, "VssFreeSnapshotProperties");
87
    if (!pVssFreeSnapshotProperties) {
88
        fprintf(stderr, "failed to get proc address from VSSAPI.DLL\n");
89
        return HRESULT_FROM_WIN32(GetLastError());
90
    }
91

  
92
    return S_OK;
93
}
94

  
95
static void requester_cleanup(void)
96
{
97
    if (vss_ctx.hEventFrozen != INVALID_HANDLE_VALUE) {
98
        CloseHandle(vss_ctx.hEventFrozen);
99
        vss_ctx.hEventFrozen = INVALID_HANDLE_VALUE;
100
    }
101
    if (vss_ctx.hEventThaw != INVALID_HANDLE_VALUE) {
102
        CloseHandle(vss_ctx.hEventThaw);
103
        vss_ctx.hEventThaw = INVALID_HANDLE_VALUE;
104
    }
105
    if (vss_ctx.hEventTimeout != INVALID_HANDLE_VALUE) {
106
        CloseHandle(vss_ctx.hEventTimeout);
107
        vss_ctx.hEventTimeout = INVALID_HANDLE_VALUE;
108
    }
109
    if (vss_ctx.pAsyncSnapshot) {
110
        vss_ctx.pAsyncSnapshot->Release();
111
        vss_ctx.pAsyncSnapshot = NULL;
112
    }
113
    if (vss_ctx.pVssbc) {
114
        vss_ctx.pVssbc->Release();
115
        vss_ctx.pVssbc = NULL;
116
    }
117
    vss_ctx.cFrozenVols = 0;
118
}
119

  
120
STDAPI requester_deinit(void)
121
{
122
    requester_cleanup();
123

  
124
    pCreateVssBackupComponents = NULL;
125
    pVssFreeSnapshotProperties = NULL;
126
    if (hLib) {
127
        FreeLibrary(hLib);
128
        hLib = NULL;
129
    }
130

  
131
    return S_OK;
132
}
133

  
134
static HRESULT WaitForAsync(IVssAsync *pAsync)
135
{
136
    HRESULT ret, hr;
137

  
138
    do {
139
        hr = pAsync->Wait();
140
        if (FAILED(hr)) {
141
            ret = hr;
142
            break;
143
        }
144
        hr = pAsync->QueryStatus(&ret, NULL);
145
        if (FAILED(hr)) {
146
            ret = hr;
147
            break;
148
        }
149
    } while (ret == VSS_S_ASYNC_PENDING);
150

  
151
    return ret;
152
}
153

  
154
static void AddComponents(ErrorSet *errset)
155
{
156
    unsigned int cWriters, i;
157
    VSS_ID id, idInstance, idWriter;
158
    BSTR bstrWriterName = NULL;
159
    VSS_USAGE_TYPE usage;
160
    VSS_SOURCE_TYPE source;
161
    unsigned int cComponents, c1, c2, j;
162
    COMPointer<IVssExamineWriterMetadata> pMetadata;
163
    COMPointer<IVssWMComponent> pComponent;
164
    PVSSCOMPONENTINFO info;
165
    HRESULT hr;
166

  
167
    hr = vss_ctx.pVssbc->GetWriterMetadataCount(&cWriters);
168
    if (FAILED(hr)) {
169
        err_set(errset, hr, "failed to get writer metadata count");
170
        goto out;
171
    }
172

  
173
    for (i = 0; i < cWriters; i++) {
174
        hr = vss_ctx.pVssbc->GetWriterMetadata(i, &id, pMetadata.replace());
175
        if (FAILED(hr)) {
176
            err_set(errset, hr, "failed to get writer metadata of %d/%d",
177
                             i, cWriters);
178
            goto out;
179
        }
180

  
181
        hr = pMetadata->GetIdentity(&idInstance, &idWriter,
182
                                    &bstrWriterName, &usage, &source);
183
        if (FAILED(hr)) {
184
            err_set(errset, hr, "failed to get identity of writer %d/%d",
185
                             i, cWriters);
186
            goto out;
187
        }
188

  
189
        hr = pMetadata->GetFileCounts(&c1, &c2, &cComponents);
190
        if (FAILED(hr)) {
191
            err_set(errset, hr, "failed to get file counts of %S",
192
                             bstrWriterName);
193
            goto out;
194
        }
195

  
196
        for (j = 0; j < cComponents; j++) {
197
            hr = pMetadata->GetComponent(j, pComponent.replace());
198
            if (FAILED(hr)) {
199
                err_set(errset, hr,
200
                                 "failed to get component %d/%d of %S",
201
                                 j, cComponents, bstrWriterName);
202
                goto out;
203
            }
204

  
205
            hr = pComponent->GetComponentInfo(&info);
206
            if (FAILED(hr)) {
207
                err_set(errset, hr,
208
                                 "failed to get component info %d/%d of %S",
209
                                 j, cComponents, bstrWriterName);
210
                goto out;
211
            }
212

  
213
            if (info->bSelectable) {
214
                hr = vss_ctx.pVssbc->AddComponent(idInstance, idWriter,
215
                                                  info->type,
216
                                                  info->bstrLogicalPath,
217
                                                  info->bstrComponentName);
218
                if (FAILED(hr)) {
219
                    err_set(errset, hr, "failed to add component %S(%S)",
220
                                     info->bstrComponentName, bstrWriterName);
221
                    goto out;
222
                }
223
            }
224
            SysFreeString(bstrWriterName);
225
            bstrWriterName = NULL;
226
            pComponent->FreeComponentInfo(info);
227
            info = NULL;
228
        }
229
    }
230
out:
231
    if (bstrWriterName) {
232
        SysFreeString(bstrWriterName);
233
    }
234
    if (pComponent && info) {
235
        pComponent->FreeComponentInfo(info);
236
    }
237
}
238

  
239
void requester_freeze(int *num_vols, ErrorSet *errset)
240
{
241
    COMPointer<IVssAsync> pAsync;
242
    HANDLE volume;
243
    HRESULT hr;
244
    LONG ctx;
245
    GUID guidSnapshotSet = GUID_NULL;
246
    SECURITY_DESCRIPTOR sd;
247
    SECURITY_ATTRIBUTES sa;
248
    WCHAR short_volume_name[64], *display_name = short_volume_name;
249
    DWORD wait_status;
250
    int num_fixed_drives = 0, i;
251

  
252
    if (vss_ctx.pVssbc) { /* already frozen */
253
        *num_vols = 0;
254
        return;
255
    }
256

  
257
    CoInitialize(NULL);
258

  
259
    assert(pCreateVssBackupComponents != NULL);
260
    hr = pCreateVssBackupComponents(&vss_ctx.pVssbc);
261
    if (FAILED(hr)) {
262
        err_set(errset, hr, "failed to create VSS backup components");
263
        goto out;
264
    }
265

  
266
    hr = vss_ctx.pVssbc->InitializeForBackup();
267
    if (FAILED(hr)) {
268
        err_set(errset, hr, "failed to initialize for backup");
269
        goto out;
270
    }
271

  
272
    hr = vss_ctx.pVssbc->SetBackupState(true, true, VSS_BT_FULL, false);
273
    if (FAILED(hr)) {
274
        err_set(errset, hr, "failed to set backup state");
275
        goto out;
276
    }
277

  
278
    /*
279
     * Currently writable snapshots are not supported.
280
     * To prevent the final commit (which requires to write to snapshots),
281
     * ATTR_NO_AUTORECOVERY and ATTR_TRANSPORTABLE are specified here.
282
     */
283
    ctx = VSS_CTX_APP_ROLLBACK | VSS_VOLSNAP_ATTR_TRANSPORTABLE |
284
        VSS_VOLSNAP_ATTR_NO_AUTORECOVERY | VSS_VOLSNAP_ATTR_TXF_RECOVERY;
285
    hr = vss_ctx.pVssbc->SetContext(ctx);
286
    if (hr == (HRESULT)VSS_E_UNSUPPORTED_CONTEXT) {
287
        /* Non-server version of Windows doesn't support ATTR_TRANSPORTABLE */
288
        ctx &= ~VSS_VOLSNAP_ATTR_TRANSPORTABLE;
289
        hr = vss_ctx.pVssbc->SetContext(ctx);
290
    }
291
    if (FAILED(hr)) {
292
        err_set(errset, hr, "failed to set backup context");
293
        goto out;
294
    }
295

  
296
    hr = vss_ctx.pVssbc->GatherWriterMetadata(pAsync.replace());
297
    if (SUCCEEDED(hr)) {
298
        hr = WaitForAsync(pAsync);
299
    }
300
    if (FAILED(hr)) {
301
        err_set(errset, hr, "failed to gather writer metadata");
302
        goto out;
303
    }
304

  
305
    AddComponents(errset);
306
    if (err_is_set(errset)) {
307
        goto out;
308
    }
309

  
310
    hr = vss_ctx.pVssbc->StartSnapshotSet(&guidSnapshotSet);
311
    if (FAILED(hr)) {
312
        err_set(errset, hr, "failed to start snapshot set");
313
        goto out;
314
    }
315

  
316
    volume = FindFirstVolumeW(short_volume_name, sizeof(short_volume_name));
317
    if (volume == INVALID_HANDLE_VALUE) {
318
        err_set(errset, hr, "failed to find first volume");
319
        goto out;
320
    }
321
    for (;;) {
322
        if (GetDriveTypeW(short_volume_name) == DRIVE_FIXED) {
323
            VSS_ID pid;
324
            hr = vss_ctx.pVssbc->AddToSnapshotSet(short_volume_name,
325
                                                  g_gProviderId, &pid);
326
            if (FAILED(hr)) {
327
                WCHAR volume_path_name[PATH_MAX];
328
                if (GetVolumePathNamesForVolumeNameW(
... This diff was truncated because it exceeds the maximum size that can be displayed.

Also available in: Unified diff