From patchwork Thu Feb 14 06:10:44 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomoki Sekiyama X-Patchwork-Id: 220368 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id E6B852C029E for ; Thu, 14 Feb 2013 17:16:27 +1100 (EST) Received: from localhost ([::1]:35846 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1U5s2Y-0007yk-9N for incoming@patchwork.ozlabs.org; Thu, 14 Feb 2013 01:11:26 -0500 Received: from eggs.gnu.org ([208.118.235.92]:55206) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1U5s25-0007Nc-Kv for qemu-devel@nongnu.org; Thu, 14 Feb 2013 01:11:02 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1U5s1w-000755-Rw for qemu-devel@nongnu.org; Thu, 14 Feb 2013 01:10:57 -0500 Received: from mail4.hitachi.co.jp ([133.145.228.5]:44012) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1U5s1v-00072c-QF for qemu-devel@nongnu.org; Thu, 14 Feb 2013 01:10:48 -0500 Received: from mlsv1.hitachi.co.jp (unknown [133.144.234.166]) by mail4.hitachi.co.jp (Postfix) with ESMTP id 1ED2333CC2; Thu, 14 Feb 2013 15:10:47 +0900 (JST) Received: from mfilter03.hitachi.co.jp by mlsv1.hitachi.co.jp (8.13.1/8.13.1) id r1E6AlpD028248; Thu, 14 Feb 2013 15:10:47 +0900 Received: from vshuts04.hitachi.co.jp (vshuts04.hitachi.co.jp [10.201.6.86]) by mfilter03.hitachi.co.jp (Switch-3.3.4/Switch-3.3.4) with ESMTP id r1E6AjCP002600; Thu, 14 Feb 2013 15:10:46 +0900 Received: from hsdlmain.sdl.hitachi.co.jp (unknown [133.144.14.194]) by vshuts04.hitachi.co.jp (Postfix) with ESMTP id 7FF8A140058; Thu, 14 Feb 2013 15:10:45 +0900 (JST) Received: from hsdlvgate2.sdl.hitachi.co.jp by hsdlmain.sdl.hitachi.co.jp (8.13.8/3.7W11021512) id r1E6Aj8j032139; Thu, 14 Feb 2013 15:10:45 +0900 X-AuditID: 85900ec0-d687bb900000152f-fc-511c7fe5ac80 Received: from sdl99w.sdl.hitachi.co.jp (sdl99w.sdl.hitachi.co.jp [133.144.14.250]) by hsdlvgate2.sdl.hitachi.co.jp (Symantec Mail Security) with ESMTP id 21519236561; Thu, 14 Feb 2013 15:10:45 +0900 (JST) Received: from melchior2.sdl.hitachi.co.jp (unknown [10.232.28.238]) by sdl99w.sdl.hitachi.co.jp (Postfix) with ESMTP id 9CC5253C1F5; Thu, 14 Feb 2013 15:12:13 +0900 (JST) To: qemu-devel@nongnu.org From: Tomoki Sekiyama Date: Thu, 14 Feb 2013 15:10:44 +0900 Message-ID: <20130214061044.15062.35466.stgit@melchior2.sdl.hitachi.co.jp> In-Reply-To: <20130214061026.15062.54597.stgit@melchior2.sdl.hitachi.co.jp> References: <20130214061026.15062.54597.stgit@melchior2.sdl.hitachi.co.jp> User-Agent: StGit/0.16 MIME-Version: 1.0 X-Brightmail-Tracker: AAAAAA== X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.4.x X-Received-From: 133.145.228.5 Cc: mdroth@linux.vnet.ibm.com, dle-discus@lists.sourceforge.jp, vrozenfe@redhat.com, lcapitulino@redhat.com Subject: [Qemu-devel] [RFC PATCH 05/10] qemu-ga: Add Windows VSS requester to quisce applications and filesystems X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org This adds VSS requester functions for to qemu-ga. This provides facility to request VSS service in Windows guest to quisce applications and filesystems. This function is only supported in Windows 2003 or later. In older guests, this function does nothing. Signed-off-by: Tomoki Sekiyama --- qga/Makefile.objs | 15 ++ qga/vss-win32-requester.cpp | 393 +++++++++++++++++++++++++++++++++++++++++++ qga/vss-win32-requester.h | 31 +++ 3 files changed, 439 insertions(+) create mode 100644 qga/vss-win32-requester.cpp create mode 100644 qga/vss-win32-requester.h diff --git a/qga/Makefile.objs b/qga/Makefile.objs index b8d7cd0..2e4a8d6 100644 --- a/qga/Makefile.objs +++ b/qga/Makefile.objs @@ -3,3 +3,18 @@ qga-obj-$(CONFIG_POSIX) += commands-posix.o channel-posix.o qga-obj-$(CONFIG_WIN32) += commands-win32.o channel-win32.o service-win32.o qga-obj-y += qapi-generated/qga-qapi-types.o qapi-generated/qga-qapi-visit.o qga-obj-y += qapi-generated/qga-qmp-marshal.o + +ifeq ($(CONFIG_QGA_VSS),y) + +qga-obj-$(CONFIG_WIN32) += vss-win32-requester.o +QEMU_CFLAGS += -DHAS_VSS_SDK +QGALIB_EXTDIR = $(obj)/vss-win32-provider + +qemu-ga$(EXESUF): $(QGALIB_EXTDIR)/qga-provider.dll +$(obj)/vss-win32-requester.o: QEMU_CXXFLAGS += -Wno-unknown-pragmas + +$(QGALIB_EXTDIR)/qga-provider.tlb: $(QGALIB_EXTDIR)/qga-provider.dll +$(QGALIB_EXTDIR)/qga-provider.dll: $(qga-lib-src) $(obj)/vss-win32.h + $(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C qga/$(QGALIB_EXTDIR) V="$(V)" all,) + +endif diff --git a/qga/vss-win32-requester.cpp b/qga/vss-win32-requester.cpp new file mode 100644 index 0000000..f84f464 --- /dev/null +++ b/qga/vss-win32-requester.cpp @@ -0,0 +1,393 @@ +/* + * QEMU Guest Agent win32 VSS Requester implementations + * + * Copyright Hitachi, Ltd. 2013 + * + * Authors: + * Tomoki Sekiyama + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include +#include +extern "C" { +#include "guest-agent-core.h" +} +#include "vss-win32-requester.h" +#include "vss-win32-provider.h" +#include "vss-win32.h" +#include "inc/win2003/vswriter.h" +#include "inc/win2003/vsbackup.h" + +/* Functions in VSSAPI.DLL */ +typedef HRESULT (STDAPICALLTYPE* t_CreateVssBackupComponents)(OUT IVssBackupComponents **); +typedef void (APIENTRY* t_VssFreeSnapshotProperties)(IN VSS_SNAPSHOT_PROP*); + +static t_CreateVssBackupComponents _CreateVssBackupComponents = NULL; +static t_VssFreeSnapshotProperties _VssFreeSnapshotProperties = NULL; +static IVssBackupComponents *pVssbc = NULL; +static IVssAsync *pAsyncSnapshot = NULL; +static HMODULE hLib = NULL; +static HANDLE hEvent = INVALID_HANDLE_VALUE, hEvent2 = INVALID_HANDLE_VALUE; +static int cFrozenVols = 0; + +GCC_FMT_ATTR(1, 2) +static void errmsg(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + char *msg = g_strdup_vprintf(fmt, ap); + va_end(ap); + MessageBox(NULL, msg, "Error in QEMU guest agent", MB_OK | MB_ICONWARNING); + g_free(msg); +} + +static void error_set_win32(Error **errp, DWORD err, + ErrorClass eclass, const char *text) +{ + char *msg = NULL, *nul = strchr(text, '('); + int len = nul ? nul - text : -1; + + /* print error message in native encoding */ + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (char *)&msg, 0, NULL); + printf("%.*s. (Error: %lx) %s\n", len, text, err, msg); + LocalFree(msg); + + /* set error message in UTF-8 encoding */ + msg = g_win32_error_message(err); + error_set(errp, eclass, "%.*s. (Error: %lx) %s", len, text, err, msg); + g_free(msg); +} +#define error_setg_win32(errp, err, text) \ + error_set_win32(errp, err, ERROR_CLASS_GENERIC_ERROR, text) + +#define _chk(status, text, errp, err_label) \ + do { \ + HRESULT __hr = (status); \ + if (FAILED(__hr)) { \ + error_setg_win32(errp, __hr, text); \ + goto err_label; \ + } \ + } while(0) + +#define chk(status) _chk(status, "Failed to " #status, err, out) + + +HRESULT WaitForAsync(IVssAsync *pAsync) +{ + HRESULT ret, hr; + + do { + hr = pAsync->Wait(); + if (FAILED(hr)) { + ret = hr; + break; + } + hr = pAsync->QueryStatus(&ret, NULL); + if (FAILED(hr)) { + ret = hr; + break; + } + } while (ret == VSS_S_ASYNC_PENDING); + + return ret; +} + +HRESULT vss_init(void) +{ + HRESULT hr; + + hr = VSSCheckOSVersion(); + if (hr == S_FALSE) { + return hr; + } + + hr = CoInitialize(NULL); + if (FAILED(hr)) { + errmsg("CoInitialize failed [%lx]", hr); + goto out; + }; + hr = CoInitializeSecurity( + NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_PKT_PRIVACY, + RPC_C_IMP_LEVEL_IDENTIFY, NULL, EOAC_NONE, NULL); + if (FAILED(hr)) { + errmsg("CoInitializeSecurity failed [%lx]", hr); + goto out; + } + + hLib = LoadLibraryA("VSSAPI.DLL"); + if (!hLib) { + errmsg("LoadLibrary VSSAPI.DLL failed"); + hr = E_FAIL; + goto out; + } + + _CreateVssBackupComponents = (t_CreateVssBackupComponents) + GetProcAddress(hLib, +#ifdef _WIN64 /* 64bit environment */ + "?CreateVssBackupComponents@@YAJPEAPEAVIVssBackupComponents@@@Z" +#else /* 32bit environment */ + "?CreateVssBackupComponents@@YGJPAPAVIVssBackupComponents@@@Z" +#endif + ); + _VssFreeSnapshotProperties = (t_VssFreeSnapshotProperties) + GetProcAddress(hLib, "VssFreeSnapshotProperties"); + if (!_CreateVssBackupComponents || !_VssFreeSnapshotProperties) { + errmsg("GetProcAddress failed"); + hr = E_FAIL; + goto out; + } + + return S_OK; +out: + vss_deinit(); + return hr; +} + +static void vss_cleanup(void) +{ + if (hEvent != INVALID_HANDLE_VALUE) { + CloseHandle(hEvent); + hEvent = INVALID_HANDLE_VALUE; + } + if (hEvent2 != INVALID_HANDLE_VALUE) { + CloseHandle(hEvent2); + hEvent2 = INVALID_HANDLE_VALUE; + } + if (pVssbc) { + pVssbc->Release(); + pVssbc = NULL; + } +} + +void vss_deinit(void) +{ + if (VSSCheckOSVersion() == S_FALSE) { + return; + } + + vss_cleanup(); + + CoUninitialize(); + + _CreateVssBackupComponents = NULL; + _VssFreeSnapshotProperties = NULL; + if (hLib) { + FreeLibrary(hLib); + hLib = NULL; + } +} + +int vss_initialized(void) +{ + return hLib != NULL; +} + +static void vss_add_components(Error **err) +{ + unsigned int cWriters, i; + VSS_ID id, idInstance, idWriter; + BSTR bstrWriterName; + VSS_USAGE_TYPE usage; + VSS_SOURCE_TYPE source; + unsigned int cComponents, c1, c2, j; + IVssExamineWriterMetadata *pMetadata; + IVssWMComponent *pComponent; + PVSSCOMPONENTINFO pInfo = NULL; + + chk( pVssbc->GetWriterMetadataCount(&cWriters) ); + + for (i = 0; i < cWriters; i++) { + chk( pVssbc->GetWriterMetadata(i, &id, &pMetadata) ); + chk( pMetadata->GetIdentity(&idInstance, &idWriter, + &bstrWriterName, &usage, &source) ); + chk( pMetadata->GetFileCounts(&c1, &c2, &cComponents) ); + + for (j = 0; j < cComponents; j++) { + chk( pMetadata->GetComponent(j, &pComponent) ); + chk( pComponent->GetComponentInfo(&pInfo) ); + if (pInfo->bSelectable) { + chk( pVssbc->AddComponent(idInstance, idWriter, pInfo->type, + pInfo->bstrLogicalPath, + pInfo->bstrComponentName) ); + } + pComponent->FreeComponentInfo(pInfo); + pInfo = NULL; + pComponent->Release(); + pComponent = NULL; + } + + pMetadata->Release(); + pMetadata = NULL; + } +out: + if (pComponent) { + if (pInfo) { + pComponent->FreeComponentInfo(pInfo); + } + pComponent->Release(); + } + if (pMetadata) { + pMetadata->Release(); + } +} + +void qga_vss_fsfreeze_freeze(int *num_vols, Error **err) +{ + IVssAsync *pAsync; + HANDLE h; + GUID guidSnapshotSet = GUID_NULL; + SECURITY_DESCRIPTOR sd; + SECURITY_ATTRIBUTES sa; + WCHAR buf[64], *b = buf; + int n = 0; + + if (pVssbc) { /* already frozen */ + *num_vols = 0; + return; + } + + assert(_CreateVssBackupComponents != NULL); + chk( _CreateVssBackupComponents(&pVssbc) ); + chk( pVssbc->InitializeForBackup() ); + chk( pVssbc->SetBackupState(true, true, VSS_BT_FULL, false) ); + /* + * Currently writable snapshots are not supported. + * To prevent the final commit (which requires to write to snapshots), + * VSS_VOLSNAP_ATTR_NO_AUTORECOVERY (for Win2008 SP2 or later) and + * VSS_VOLSNAP_ATTR_TRANSPORTABLE (for ealier versions) are specified here. + */ + chk( pVssbc->SetContext(VSS_CTX_APP_ROLLBACK | + VSS_VOLSNAP_ATTR_NO_AUTORECOVERY | + VSS_VOLSNAP_ATTR_TRANSPORTABLE | + VSS_VOLSNAP_ATTR_TXF_RECOVERY) ); + + chk( pVssbc->GatherWriterMetadata(&pAsync) ); + _chk( WaitForAsync(pAsync), "GatherWriterMetadata", err, out ); + pAsync->Release(); + + vss_add_components(err); + if (error_is_set(err)) { + goto out; + } + + chk( pVssbc->StartSnapshotSet(&guidSnapshotSet) ); + + h = FindFirstVolumeW(buf, sizeof(buf)); + while (h != INVALID_HANDLE_VALUE) { + if (GetDriveTypeW(buf) == DRIVE_FIXED) { + VSS_ID pid; + HRESULT hr = pVssbc->AddToSnapshotSet(buf, g_gProviderId, &pid); + if (FAILED(hr)) { + WCHAR name[PATH_MAX]; + char msg[PATH_MAX+32]; + if (GetVolumePathNamesForVolumeNameW( + buf, name, sizeof(name), NULL) && *name) { + b = name; + } + snprintf(msg, sizeof(msg), "add %S to snapshot set", b); + error_setg_win32(err, hr, msg); + goto out; + } + n++; + } + if (!FindNextVolumeW(h, buf, sizeof(buf))) { + FindVolumeClose(h); + break; + } + } + + chk( pVssbc->PrepareForBackup(&pAsync) ); + _chk( WaitForAsync(pAsync), "PrepareForBackup", err, out ); + pAsync->Release(); + + chk( pVssbc->GatherWriterStatus(&pAsync) ); + _chk( WaitForAsync(pAsync), "GatherWriterStatus", err, out ); + pAsync->Release(); + + /* Allow unrestricted access to events */ + InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION); + SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE); + sa.nLength = sizeof(sa); + sa.lpSecurityDescriptor = &sd; + sa.bInheritHandle = FALSE; + + hEvent = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_FROZEN); + if (hEvent == INVALID_HANDLE_VALUE) { + error_setg_win32(err, GetLastError(), "CreateEvenet"); + goto out; + } + hEvent2 = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_THAW); + if (hEvent2 == INVALID_HANDLE_VALUE) { + error_setg_win32(err, GetLastError(), "CreateEvenet"); + goto out; + } + + chk( pVssbc->DoSnapshotSet(&pAsyncSnapshot) ); + + /* Need to call QueryStatus several times to make VSS provider progress */ + for (int i = 0; i < 1000; i++) { + HRESULT hr = S_OK; + chk( pAsyncSnapshot->QueryStatus(&hr, NULL) ); + if (hr != VSS_S_ASYNC_PENDING) { + error_setg(err, "DoSnapshotSet exited without freeze event"); + goto out; + } + DWORD ret = WaitForSingleObject(hEvent, 10); + if (ret == WAIT_OBJECT_0) { + break; + } + } + + *num_vols = cFrozenVols = n; + return; + +out: + if (pVssbc) { + pVssbc->AbortBackup(); + } + vss_cleanup(); +} + + +void qga_vss_fsfreeze_thaw(int *num_vols, Error **err) +{ + IVssAsync *pAsync; + + if (hEvent2 == INVALID_HANDLE_VALUE) { + /* + * In this case, DoSnapshotSet is aborted or not started, + * and no volumes must be frozen. We return without an error. + */ + *num_vols = 0; + return; + } + SetEvent(hEvent2); + + assert(pVssbc); + assert(pAsyncSnapshot); + + _chk( WaitForAsync(pAsyncSnapshot), "DoSnapshotSet", err, out ); + pAsyncSnapshot->Release(); + pAsyncSnapshot = NULL; + + chk( pVssbc->BackupComplete(&pAsync) ); + _chk( WaitForAsync(pAsync), "BackupComplete", err, out ); + pAsync->Release(); + + *num_vols = cFrozenVols; + cFrozenVols = 0; + goto done; + +out: + if (pVssbc) { + pVssbc->AbortBackup(); + } +done: + vss_cleanup(); +} diff --git a/qga/vss-win32-requester.h b/qga/vss-win32-requester.h new file mode 100644 index 0000000..db0428e --- /dev/null +++ b/qga/vss-win32-requester.h @@ -0,0 +1,31 @@ +/* + * QEMU Guest Agent VSS Requester declarations + * + * Copyright Hitachi, Ltd. 2013 + * + * Authors: + * Tomoki Sekiyama + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef VSS_WIN32_REQUESTER_H +#define VSS_WIN32_REQUESTER_H + +#ifdef __cplusplus +extern "C" { +#endif + +HRESULT vss_init(void); +void vss_deinit(void); +int vss_initialized(void); + +void qga_vss_fsfreeze_freeze(int *nr_volume, struct Error **err); +void qga_vss_fsfreeze_thaw(int *nr_volume, struct Error **err); + +#ifdef __cplusplus +} +#endif + +#endif