qemu-devel
[Top][All Lists]
Advanced

[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
> 
> 



reply via email to

[Prev in Thread] Current Thread [Next in Thread]