diff mbox

[RFC,v3,06/11] qemu-ga: Add Windows VSS requester to quisce applications and filesystems

Message ID 20130521153357.4880.72899.stgit@hds.com
State New
Headers show

Commit Message

Tomoki Sekiyama May 21, 2013, 3:33 p.m. UTC
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 <tomoki.sekiyama@hds.com>
---
 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

Comments

Eric Blake May 21, 2013, 4:56 p.m. UTC | #1
On 05/21/2013 09:33 AM, Tomoki Sekiyama wrote:
> Add VSS requester functions for to qemu-ga.
> This provides facility to request VSS service in Windows guest to quisce

s/quisce/quiesce/

> 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).

I take it that such a framework may involve additional guest-agent
commands and management work (such as libvirt having to be aware of the
added framework).  How important is this functionality?  How frequently
does VSS even get us into that state?  That is, are Windows guests going
to frequently hit our current first-round implementation limits that
fail to let them do auto-recovery?
Tomoki Sekiyama May 21, 2013, 9:02 p.m. UTC | #2
On 5/21/13 12:56 , "Eric Blake" <eblake@redhat.com> wrote:

>On 05/21/2013 09:33 AM, Tomoki Sekiyama wrote:
>> Add VSS requester functions for to qemu-ga.
>> This provides facility to request VSS service in Windows guest to quisce
>
>s/quisce/quiesce/

Oops, thanks again.

>> 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).
>
>I take it that such a framework may involve additional guest-agent
>commands and management work (such as libvirt having to be aware of the
>added framework).  How important is this functionality?  How frequently
>does VSS even get us into that state?  That is, are Windows guests going
>to frequently hit our current first-round implementation limits that
>fail to let them do auto-recovery?

"Auto-recovery" is invoked every time after snapshots are taken by VSS
(the name is misleading, but it is normal path...) to make the snapshot
completely consistent state by rolling back incomplete transactions.
VSS requesters can explicitly disable this, but this may remain some
incomplete data in the snapshot when some applications which relying
on this feature are running. However, I believe they can usually be
recovered *after the snapshot is rolled back*.

Full-support of VSS including auto-recovery may require the other channel
between libvirt and guest-agent to conduct snapshot operation from guests.
I think such interface can also enable various VSS feature such as partial-
recovery from snapshots. It would be nice to have in the future version,
but is overkill just to enable fsfreeze command so far....


Thanks,
Tomoki Sekiyama
Jeff Cody May 28, 2013, 8:17 p.m. UTC | #3
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 <tomoki.sekiyama@hds.com>
> ---
>  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   <tomoki.sekiyama@hds.com>
> + *
> + * 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   <tomoki.sekiyama@hds.com>
> + *
> + * 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
> 
>
Tomoki Sekiyama May 31, 2013, 5:06 a.m. UTC | #4
Jeff Cody wrote:
> On Tue, May 21, 2013 at 11:33:57AM -0400, Tomoki Sekiyama wrote:
>> +    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?

0x80042308 or 0x80042308L looks interpreted as unsigned 32-bit int in
mingw gcc. And in mingw headers, these errors are defined like:

#define VSS_E_OBJECT_NOT_FOUND _HRESULT_TYPEDEF_(0x80042308)

where _HRESULT_TYPEDEF_ is a cast to HRESULT (signed 32-bit int).

Maybe I should add casts like (HRESULT)VSS_E_... to avoid the warning....

Tomoki Sekiyama
diff mbox

Patch

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   <tomoki.sekiyama@hds.com>
+ *
+ * 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) {
+        /*
+         * 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   <tomoki.sekiyama@hds.com>
+ *
+ * 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