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( |
Also available in: Unified diff