[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-devel] [RFC PATCH v3 06/11] qemu-ga: Add Windows VSS requester
From: |
Jeff Cody |
Subject: |
Re: [Qemu-devel] [RFC PATCH v3 06/11] qemu-ga: Add Windows VSS requester to quisce applications and filesystems |
Date: |
Tue, 28 May 2013 16:17:02 -0400 |
User-agent: |
Mutt/1.5.21 (2010-09-15) |
On Tue, May 21, 2013 at 11:33:57AM -0400, Tomoki Sekiyama wrote:
> Add 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.
>
> In several versions of Windows which don't support attribute
> VSS_VOLSNAP_ATTR_NO_AUTORECOVERY, DoSnapshotSet fails with error
> VSS_E_OBJECT_NOT_FOUND. In this patch, we just ignore this error.
> To solve this fundamentally, we need a framework to handle mount writable
> snapshot on guests, which is required by VSS auto-recovery feature
> (a cleanup phase after snapshot is taken).
>
> Signed-off-by: Tomoki Sekiyama <address@hidden>
> ---
> qga/Makefile.objs | 3
> qga/vss-win32-requester.cpp | 404
> +++++++++++++++++++++++++++++++++++++++++++
> qga/vss-win32-requester.h | 31 +++
> 3 files changed, 437 insertions(+), 1 deletion(-)
> 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 8d93866..f17a380 100644
> --- a/qga/Makefile.objs
> +++ b/qga/Makefile.objs
> @@ -6,6 +6,7 @@ qga-obj-y += qapi-generated/qga-qmp-marshal.o
>
> ifeq ($(CONFIG_QGA_VSS),y)
> QEMU_CFLAGS += -DHAS_VSS_SDK
> -qga-obj-y += vss-win32-provider/
> +qga-obj-y += vss-win32-provider/ vss-win32-requester.o
> qga-prv-obj-y += vss-win32-provider/
> +$(obj)/vss-win32-requester.o: QEMU_CXXFLAGS += -Wno-unknown-pragmas
> endif
> diff --git a/qga/vss-win32-requester.cpp b/qga/vss-win32-requester.cpp
> new file mode 100644
> index 0000000..5a4653a
> --- /dev/null
> +++ b/qga/vss-win32-requester.cpp
> @@ -0,0 +1,404 @@
> +/*
> + * QEMU Guest Agent win32 VSS Requester implementations
> + *
> + * Copyright Hitachi Data Systems Corp. 2013
> + *
> + * Authors:
> + * Tomoki Sekiyama <address@hidden>
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2 or later.
> + * See the COPYING file in the top-level directory.
> + */
> +
> +#include <stdio.h>
> +#include <assert.h>
> +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 are specified here.
> + */
> + chk( pVssbc->SetContext(VSS_CTX_APP_ROLLBACK |
> + VSS_VOLSNAP_ATTR_NO_AUTORECOVERY |
> + 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);
> +
> + HRESULT hr = WaitForAsync(pAsyncSnapshot);
> + if (hr == VSS_E_OBJECT_NOT_FOUND) {
When I tried compiling, I received a warning that this is a signed /
unsigned comparison. I think, from the VSS_E_OBJECT_NOT_FOUND
definition found in inc/win2003/vss.h, that it is intended to be
negative (it is a 32-bit int that leads with 0x800). However, it is
interpreted as unsigned and throws the warning.
Does that sound correct?
> + /*
> + * On Windows earlier than 2008 SP2 which does not support
> + * VSS_VOLSNAP_ATTR_NO_AUTORECOVERY context, the final commit is not
> + * skipped and VSS is aborted by VSS_E_OBJECT_NOT_FOUND. Still, as
> the
> + * applications and file systems are frozen, we just ignore the
> error.
> + */
> + pAsyncSnapshot->Release();
> + pAsyncSnapshot = NULL;
> + goto final;
> + }
> + _chk( hr, "DoSnapshotSet", err, out );
> + pAsyncSnapshot->Release();
> + pAsyncSnapshot = NULL;
> +
> + chk( pVssbc->BackupComplete(&pAsync) );
> + _chk( WaitForAsync(pAsync), "BackupComplete", err, out );
> + pAsync->Release();
> +
> +final:
> + *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..f180f56
> --- /dev/null
> +++ b/qga/vss-win32-requester.h
> @@ -0,0 +1,31 @@
> +/*
> + * QEMU Guest Agent VSS Requester declarations
> + *
> + * Copyright Hitachi Data Systems Corp. 2013
> + *
> + * Authors:
> + * Tomoki Sekiyama <address@hidden>
> + *
> + * 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
>
>
- Re: [Qemu-devel] [RFC PATCH v3 03/11] Add a script to extract VSS SDK headers on POSIX system, (continued)
[Qemu-devel] [RFC PATCH v3 04/11] qemu-ga: Add an configure option to specify path to Windows VSS SDK, Tomoki Sekiyama, 2013/05/21
[Qemu-devel] [RFC PATCH v3 06/11] qemu-ga: Add Windows VSS requester to quisce applications and filesystems, Tomoki Sekiyama, 2013/05/21
[Qemu-devel] [RFC PATCH v3 05/11] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze, Tomoki Sekiyama, 2013/05/21
[Qemu-devel] [RFC PATCH v3 09/11] qemu-ga: Add VSS provider .tlb file in the repository, Tomoki Sekiyama, 2013/05/21