diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Makefile | 3 | ||||
-rw-r--r-- | Makefile.objs | 2 | ||||
-rwxr-xr-x | QMP/qemu-ga-client | 4 | ||||
-rwxr-xr-x | configure | 96 | ||||
-rw-r--r-- | hmp.c | 2 | ||||
-rw-r--r-- | hw/mips/mips_malta.c | 36 | ||||
-rw-r--r-- | hw/pci/pci.c | 2 | ||||
-rw-r--r-- | include/qapi/error.h | 13 | ||||
-rw-r--r-- | qga/Makefile.objs | 3 | ||||
-rw-r--r-- | qga/commands-win32.c | 82 | ||||
-rw-r--r-- | qga/main.c | 10 | ||||
-rw-r--r-- | qga/vss-win32.c | 166 | ||||
-rw-r--r-- | qga/vss-win32.h | 27 | ||||
-rw-r--r-- | qga/vss-win32/Makefile.objs | 23 | ||||
-rw-r--r-- | qga/vss-win32/install.cpp | 458 | ||||
-rw-r--r-- | qga/vss-win32/provider.cpp | 523 | ||||
-rw-r--r-- | qga/vss-win32/qga-vss.def | 13 | ||||
-rw-r--r-- | qga/vss-win32/qga-vss.idl | 20 | ||||
-rw-r--r-- | qga/vss-win32/qga-vss.tlb | bin | 0 -> 1528 bytes | |||
-rw-r--r-- | qga/vss-win32/requester.cpp | 507 | ||||
-rw-r--r-- | qga/vss-win32/requester.h | 42 | ||||
-rw-r--r-- | qga/vss-win32/vss-common.h | 129 | ||||
-rw-r--r-- | rules.mak | 9 | ||||
-rwxr-xr-x | scripts/checkpatch.pl | 34 | ||||
-rwxr-xr-x | scripts/extract-vsssdk-headers | 35 | ||||
-rw-r--r-- | scripts/qapi.py | 12 | ||||
-rw-r--r-- | util/error.c | 35 |
28 files changed, 2257 insertions, 30 deletions
diff --git a/.gitignore b/.gitignore index d2c5c2f6b8..8e1b73f622 100644 --- a/.gitignore +++ b/.gitignore @@ -82,6 +82,7 @@ fsdev/virtfs-proxy-helper.pod *.la *.pc .libs +.sdk *.swp *.orig .pc @@ -235,7 +235,7 @@ clean: rm -f qemu-options.def find . -name '*.[oda]' -type f -exec rm -f {} + find . -name '*.l[oa]' -type f -exec rm -f {} + - rm -f $(TOOLS) $(HELPERS-y) qemu-ga TAGS cscope.* *.pod *~ */*~ + rm -f $(filter-out %.tlb,$(TOOLS)) $(HELPERS-y) qemu-ga TAGS cscope.* *.pod *~ */*~ rm -Rf .libs rm -f qemu-img-cmds.h @# May not be present in GENERATED_HEADERS @@ -272,6 +272,7 @@ distclean: clean for d in $(TARGET_DIRS); do \ rm -rf $$d || exit 1 ; \ done + rm -Rf .sdk if test -f pixman/config.log; then make -C pixman distclean; fi if test -f dtc/version_gen.h; then make $(DTC_MAKE_ARGS) clean; fi diff --git a/Makefile.objs b/Makefile.objs index f46a4cdd6a..2b6c1fe2a8 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -109,6 +109,7 @@ version-lobj-$(CONFIG_WIN32) += $(BUILD_DIR)/version.lo # FIXME: a few definitions from qapi-types.o/qapi-visit.o are needed # by libqemuutil.a. These should be moved to a separate .json schema. qga-obj-y = qga/ qapi-types.o qapi-visit.o +qga-vss-dll-obj-y = qga/ vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS) @@ -120,6 +121,7 @@ nested-vars += \ stub-obj-y \ util-obj-y \ qga-obj-y \ + qga-vss-dll-obj-y \ block-obj-y \ common-obj-y dummy := $(call unnest-vars) diff --git a/QMP/qemu-ga-client b/QMP/qemu-ga-client index 46676c3750..b5f7e7c5ff 100755 --- a/QMP/qemu-ga-client +++ b/QMP/qemu-ga-client @@ -267,7 +267,9 @@ def main(address, cmd, args): print('Hint: qemu is not running?') sys.exit(1) - if cmd != 'ping': + if cmd == 'fsfreeze' and args[0] == 'freeze': + client.sync(60) + elif cmd != 'ping': client.sync() globals()['_cmd_' + cmd](client, args) @@ -232,6 +232,9 @@ usb_redir="" glx="" zlib="yes" guest_agent="" +guest_agent_with_vss="no" +vss_win32_sdk="" +win_sdk="no" want_tools="yes" libiscsi="" coroutine="" @@ -252,6 +255,8 @@ for opt do ;; --cc=*) CC="$optarg" ;; + --cxx=*) CXX="$optarg" + ;; --source-path=*) source_path="$optarg" ;; --cpu=*) cpu="$optarg" @@ -282,6 +287,12 @@ else cc="${CC-${cross_prefix}gcc}" fi +if test -z "${CXX}${cross_prefix}"; then + cxx="c++" +else + cxx="${CXX-${cross_prefix}g++}" +fi + ar="${AR-${cross_prefix}ar}" as="${AS-${cross_prefix}as}" cpp="${CPP-$cc -E}" @@ -626,6 +637,8 @@ for opt do ;; --host-cc=*) host_cc="$optarg" ;; + --cxx=*) + ;; --objcc=*) objcc="$optarg" ;; --make=*) make="$optarg" @@ -917,6 +930,18 @@ for opt do ;; --disable-guest-agent) guest_agent="no" ;; + --with-vss-sdk) vss_win32_sdk="" + ;; + --with-vss-sdk=*) vss_win32_sdk="$optarg" + ;; + --without-vss-sdk) vss_win32_sdk="no" + ;; + --with-win-sdk) win_sdk="" + ;; + --with-win-sdk=*) win_sdk="$optarg" + ;; + --without-win-sdk) win_sdk="no" + ;; --enable-tools) want_tools="yes" ;; --disable-tools) want_tools="no" @@ -1032,6 +1057,7 @@ echo " --cross-prefix=PREFIX use PREFIX for compile tools [$cross_prefix]" echo " --cc=CC use C compiler CC [$cc]" echo " --host-cc=CC use C compiler CC [$host_cc] for code run at" echo " build time" +echo " --cxx=CXX use C++ compiler CXX [$cxx]" echo " --objcc=OBJCC use Objective-C compiler OBJCC [$objcc]" echo " --extra-cflags=CFLAGS append extra C compiler flags QEMU_CFLAGS" echo " --extra-ldflags=LDFLAGS append extra linker flags LDFLAGS" @@ -1157,6 +1183,8 @@ echo " --disable-usb-redir disable usb network redirection support" echo " --enable-usb-redir enable usb network redirection support" echo " --disable-guest-agent disable building of the QEMU Guest Agent" echo " --enable-guest-agent enable building of the QEMU Guest Agent" +echo " --with-vss-sdk=SDK-path enable Windows VSS support in QEMU Guest Agent" +echo " --with-win-sdk=SDK-path path to Windows Platform SDK (to build VSS .tlb)" echo " --disable-seccomp disable seccomp support" echo " --enable-seccomp enables seccomp support" echo " --with-coroutine=BACKEND coroutine backend. Supported options:" @@ -3121,6 +3149,61 @@ if test "$usb_redir" != "no" ; then fi ########################################## +# check if we have VSS SDK headers for win + +if test "$mingw32" = "yes" -a "$guest_agent" != "no" -a "$vss_win32_sdk" != "no" ; then + case "$vss_win32_sdk" in + "") vss_win32_include="-I$source_path" ;; + *\ *) # The SDK is installed in "Program Files" by default, but we cannot + # handle path with spaces. So we symlink the headers into ".sdk/vss". + vss_win32_include="-I$source_path/.sdk/vss" + symlink "$vss_win32_sdk/inc" "$source_path/.sdk/vss/inc" + ;; + *) vss_win32_include="-I$vss_win32_sdk" + esac + cat > $TMPC << EOF +#define __MIDL_user_allocate_free_DEFINED__ +#include <inc/win2003/vss.h> +int main(void) { return VSS_CTX_BACKUP; } +EOF + if compile_prog "$vss_win32_include" "" ; then + guest_agent_with_vss="yes" + QEMU_CFLAGS="$QEMU_CFLAGS $vss_win32_include" + libs_qga="-lole32 -loleaut32 -lshlwapi -luuid -lstdc++ -Wl,--enable-stdcall-fixup $libs_qga" + else + if test "$vss_win32_sdk" != "" ; then + echo "ERROR: Please download and install Microsoft VSS SDK:" + echo "ERROR: http://www.microsoft.com/en-us/download/details.aspx?id=23490" + echo "ERROR: On POSIX-systems, you can extract the SDK headers by:" + echo "ERROR: scripts/extract-vsssdk-headers setup.exe" + echo "ERROR: The headers are extracted in the directory \`inc'." + feature_not_found "VSS support" + fi + guest_agent_with_vss="no" + fi +fi + +########################################## +# lookup Windows platform SDK (if not specified) +# The SDK is needed only to build .tlb (type library) file of guest agent +# VSS provider from the source. It is usually unnecessary because the +# pre-compiled .tlb file is included. + +if test "$mingw32" = "yes" -a "$guest_agent" != "no" -a "$guest_agent_with_vss" = "yes" ; then + if test -z "$win_sdk"; then + programfiles="$PROGRAMFILES" + test -n "$PROGRAMW6432" && programfiles="$PROGRAMW6432" + if test -n "$programfiles"; then + win_sdk=$(ls -d "$programfiles/Microsoft SDKs/Windows/v"* | tail -1) 2>/dev/null + else + feature_not_found "Windows SDK" + fi + elif test "$win_sdk" = "no"; then + win_sdk="" + fi +fi + +########################################## ########################################## # check if we have fdatasync @@ -3485,8 +3568,11 @@ if test "$softmmu" = yes ; then fi fi if [ "$guest_agent" != "no" ]; then - if [ "$linux" = "yes" -o "$bsd" = "yes" -o "$solaris" = "yes" ] ; then + if [ "$linux" = "yes" -o "$bsd" = "yes" -o "$solaris" = "yes" -o "$mingw32" = "yes" ] ; then tools="qemu-ga\$(EXESUF) $tools" + if [ "$mingw32" = "yes" -a "$guest_agent_with_vss" = "yes" ]; then + tools="qga/vss-win32/qga-vss.dll qga/vss-win32/qga-vss.tlb $tools" + fi guest_agent=yes elif [ "$guest_agent" != yes ]; then guest_agent=no @@ -3557,10 +3643,12 @@ echo "Manual directory `eval echo $mandir`" echo "ELF interp prefix $interp_prefix" else echo "local state directory queried at runtime" +echo "Windows SDK $win_sdk" fi echo "Source path $source_path" echo "C compiler $cc" echo "Host C compiler $host_cc" +echo "C++ compiler $cxx" echo "Objective-C compiler $objcc" echo "CFLAGS $CFLAGS" echo "QEMU_CFLAGS $QEMU_CFLAGS" @@ -3642,6 +3730,7 @@ echo "usb net redir $usb_redir" echo "GLX support $glx" echo "libiscsi support $libiscsi" echo "build guest agent $guest_agent" +echo "QGA VSS support $guest_agent_with_vss" echo "seccomp support $seccomp" echo "coroutine backend $coroutine" echo "GlusterFS support $glusterfs" @@ -3716,6 +3805,10 @@ if test "$mingw32" = "yes" ; then version_micro=0 echo "CONFIG_FILEVERSION=$version_major,$version_minor,$version_subminor,$version_micro" >> $config_host_mak echo "CONFIG_PRODUCTVERSION=$version_major,$version_minor,$version_subminor,$version_micro" >> $config_host_mak + if test "$guest_agent_with_vss" = "yes" ; then + echo "CONFIG_QGA_VSS=y" >> $config_host_mak + echo "WIN_SDK=\"$win_sdk\"" >> $config_host_mak + fi else echo "CONFIG_POSIX=y" >> $config_host_mak fi @@ -4148,6 +4241,7 @@ echo "PYTHON=$python" >> $config_host_mak echo "CC=$cc" >> $config_host_mak echo "CC_I386=$cc_i386" >> $config_host_mak echo "HOST_CC=$host_cc" >> $config_host_mak +echo "CXX=$cxx" >> $config_host_mak echo "OBJCC=$objcc" >> $config_host_mak echo "AR=$ar" >> $config_host_mak echo "AS=$as" >> $config_host_mak @@ -544,7 +544,7 @@ static void hmp_info_pci_device(Monitor *mon, const PciDeviceInfo *dev) if (dev->class_info.has_desc) { monitor_printf(mon, "%s", dev->class_info.desc); } else { - monitor_printf(mon, "Class %04" PRId64, dev->class_info.class); + monitor_printf(mon, "Class %04" PRId64, dev->class_info.q_class); } monitor_printf(mon, ": PCI device %04" PRIx64 ":%04" PRIx64 "\n", diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c index ae0921c6aa..05c8771220 100644 --- a/hw/mips/mips_malta.c +++ b/hw/mips/mips_malta.c @@ -827,7 +827,8 @@ static int64_t load_kernel (void) } prom_set(prom_buf, prom_index++, "memsize"); - prom_set(prom_buf, prom_index++, "%i", loaderparams.ram_size); + prom_set(prom_buf, prom_index++, "%i", + MIN(loaderparams.ram_size, 256 << 20)); prom_set(prom_buf, prom_index++, "modetty0"); prom_set(prom_buf, prom_index++, "38400n8r"); prom_set(prom_buf, prom_index++, NULL); @@ -884,7 +885,9 @@ void mips_malta_init(QEMUMachineInitArgs *args) char *filename; pflash_t *fl; MemoryRegion *system_memory = get_system_memory(); - MemoryRegion *ram = g_new(MemoryRegion, 1); + MemoryRegion *ram_high = g_new(MemoryRegion, 1); + MemoryRegion *ram_low_preio = g_new(MemoryRegion, 1); + MemoryRegion *ram_low_postio; MemoryRegion *bios, *bios_copy = g_new(MemoryRegion, 1); target_long bios_size = FLASH_SIZE; const size_t smbus_eeprom_size = 8 * 256; @@ -951,15 +954,32 @@ void mips_malta_init(QEMUMachineInitArgs *args) env = &cpu->env; /* allocate RAM */ - if (ram_size > (256 << 20)) { + if (ram_size > (2048u << 20)) { fprintf(stderr, - "qemu: Too much memory for this machine: %d MB, maximum 256 MB\n", + "qemu: Too much memory for this machine: %d MB, maximum 2048 MB\n", ((unsigned int)ram_size / (1 << 20))); exit(1); } - memory_region_init_ram(ram, NULL, "mips_malta.ram", ram_size); - vmstate_register_ram_global(ram); - memory_region_add_subregion(system_memory, 0, ram); + + /* register RAM at high address where it is undisturbed by IO */ + memory_region_init_ram(ram_high, NULL, "mips_malta.ram", ram_size); + vmstate_register_ram_global(ram_high); + memory_region_add_subregion(system_memory, 0x80000000, ram_high); + + /* alias for pre IO hole access */ + memory_region_init_alias(ram_low_preio, NULL, "mips_malta_low_preio.ram", + ram_high, 0, MIN(ram_size, (256 << 20))); + memory_region_add_subregion(system_memory, 0, ram_low_preio); + + /* alias for post IO hole access, if there is enough RAM */ + if (ram_size > (512 << 20)) { + ram_low_postio = g_new(MemoryRegion, 1); + memory_region_init_alias(ram_low_postio, NULL, + "mips_malta_low_postio.ram", + ram_high, 512 << 20, + ram_size - (512 << 20)); + memory_region_add_subregion(system_memory, 512 << 20, ram_low_postio); + } /* generate SPD EEPROM data */ generate_eeprom_spd(&smbus_eeprom_buf[0 * 256], ram_size); @@ -992,7 +1012,7 @@ void mips_malta_init(QEMUMachineInitArgs *args) fl_idx++; if (kernel_filename) { /* Write a small bootloader to the flash location. */ - loaderparams.ram_size = ram_size; + loaderparams.ram_size = MIN(ram_size, 256 << 20); loaderparams.kernel_filename = kernel_filename; loaderparams.kernel_cmdline = kernel_cmdline; loaderparams.initrd_filename = initrd_filename; diff --git a/hw/pci/pci.c b/hw/pci/pci.c index d00682e134..ad1c1ca91e 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -1461,7 +1461,7 @@ static PciDeviceInfo *qmp_query_pci_device(PCIDevice *dev, PCIBus *bus, info->function = PCI_FUNC(dev->devfn); class = pci_get_word(dev->config + PCI_CLASS_DEVICE); - info->class_info.class = class; + info->class_info.q_class = class; desc = get_class_desc(class); if (desc->desc) { info->class_info.has_desc = true; diff --git a/include/qapi/error.h b/include/qapi/error.h index ffd1cea477..7d4c6963d3 100644 --- a/include/qapi/error.h +++ b/include/qapi/error.h @@ -36,6 +36,15 @@ void error_set(Error **err, ErrorClass err_class, const char *fmt, ...) GCC_FMT_ */ void error_set_errno(Error **err, int os_error, ErrorClass err_class, const char *fmt, ...) GCC_FMT_ATTR(4, 5); +#ifdef _WIN32 +/** + * Set an indirect pointer to an error given a ErrorClass value and a + * printf-style human message, followed by a g_win32_error_message() string if + * @win32_err is not zero. + */ +void error_set_win32(Error **err, int win32_err, ErrorClass err_class, const char *fmt, ...) GCC_FMT_ATTR(4, 5); +#endif + /** * Same as error_set(), but sets a generic error */ @@ -43,6 +52,10 @@ void error_set_errno(Error **err, int os_error, ErrorClass err_class, const char error_set(err, ERROR_CLASS_GENERIC_ERROR, fmt, ## __VA_ARGS__) #define error_setg_errno(err, os_error, fmt, ...) \ error_set_errno(err, os_error, ERROR_CLASS_GENERIC_ERROR, fmt, ## __VA_ARGS__) +#ifdef _WIN32 +#define error_setg_win32(err, win32_err, fmt, ...) \ + error_set_win32(err, win32_err, ERROR_CLASS_GENERIC_ERROR, fmt, ## __VA_ARGS__) +#endif /** * Helper for open() errors diff --git a/qga/Makefile.objs b/qga/Makefile.objs index b8d7cd0a43..1c5986c0bb 100644 --- a/qga/Makefile.objs +++ b/qga/Makefile.objs @@ -1,5 +1,8 @@ qga-obj-y = commands.o guest-agent-command-state.o main.o 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-$(CONFIG_WIN32) += vss-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 + +qga-vss-dll-obj-$(CONFIG_QGA_VSS) += vss-win32/ diff --git a/qga/commands-win32.c b/qga/commands-win32.c index 24e4ad0319..7a37f5cadd 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -15,6 +15,7 @@ #include <wtypes.h> #include <powrprof.h> #include "qga/guest-agent-core.h" +#include "qga/vss-win32.h" #include "qga-qmp-commands.h" #include "qapi/qmp/qerror.h" @@ -156,27 +157,89 @@ void qmp_guest_file_flush(int64_t handle, Error **err) */ GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err) { - error_set(err, QERR_UNSUPPORTED); - return 0; + if (!vss_initialized()) { + error_set(err, QERR_UNSUPPORTED); + return 0; + } + + if (ga_is_frozen(ga_state)) { + return GUEST_FSFREEZE_STATUS_FROZEN; + } + + return GUEST_FSFREEZE_STATUS_THAWED; } /* - * Walk list of mounted file systems in the guest, and freeze the ones which - * are real local file systems. + * Freeze local file systems using Volume Shadow-copy Service. + * The frozen state is limited for up to 10 seconds by VSS. */ int64_t qmp_guest_fsfreeze_freeze(Error **err) { - error_set(err, QERR_UNSUPPORTED); + int i; + Error *local_err = NULL; + + if (!vss_initialized()) { + error_set(err, QERR_UNSUPPORTED); + return 0; + } + + slog("guest-fsfreeze called"); + + /* cannot risk guest agent blocking itself on a write in this state */ + ga_set_frozen(ga_state); + + qga_vss_fsfreeze(&i, err, true); + if (error_is_set(err)) { + goto error; + } + + return i; + +error: + qmp_guest_fsfreeze_thaw(&local_err); + if (error_is_set(&local_err)) { + g_debug("cleanup thaw: %s", error_get_pretty(local_err)); + error_free(local_err); + } return 0; } /* - * Walk list of frozen file systems in the guest, and thaw them. + * Thaw local file systems using Volume Shadow-copy Service. */ int64_t qmp_guest_fsfreeze_thaw(Error **err) { - error_set(err, QERR_UNSUPPORTED); - return 0; + int i; + + if (!vss_initialized()) { + error_set(err, QERR_UNSUPPORTED); + return 0; + } + + qga_vss_fsfreeze(&i, err, false); + + ga_unset_frozen(ga_state); + return i; +} + +static void guest_fsfreeze_cleanup(void) +{ + Error *err = NULL; + + if (!vss_initialized()) { + return; + } + + if (ga_is_frozen(ga_state) == GUEST_FSFREEZE_STATUS_FROZEN) { + qmp_guest_fsfreeze_thaw(&err); + if (err) { + slog("failed to clean up frozen filesystems: %s", + error_get_pretty(err)); + error_free(err); + } + } + + vss_deinit(true); } /* @@ -354,4 +417,7 @@ int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp) /* register init/cleanup routines for stateful command groups */ void ga_command_state_init(GAState *s, GACommandState *cs) { + if (vss_init(true)) { + ga_command_state_add(cs, NULL, guest_fsfreeze_cleanup); + } } diff --git a/qga/main.c b/qga/main.c index 0e04e7395c..6c746c8f3a 100644 --- a/qga/main.c +++ b/qga/main.c @@ -34,6 +34,7 @@ #include "qemu/bswap.h" #ifdef _WIN32 #include "qga/service-win32.h" +#include "qga/vss-win32.h" #include <windows.h> #endif #ifdef __linux__ @@ -1031,8 +1032,15 @@ int main(int argc, char **argv) fixed_state_dir = (state_dir == dfl_pathnames.state_dir) ? NULL : state_dir; - return ga_install_service(path, log_filepath, fixed_state_dir); + if (ga_install_vss_provider()) { + return EXIT_FAILURE; + } + if (ga_install_service(path, log_filepath, fixed_state_dir)) { + return EXIT_FAILURE; + } + return 0; } else if (strcmp(service, "uninstall") == 0) { + ga_uninstall_vss_provider(); return ga_uninstall_service(); } else { printf("Unknown service command.\n"); diff --git a/qga/vss-win32.c b/qga/vss-win32.c new file mode 100644 index 0000000000..24c428842b --- /dev/null +++ b/qga/vss-win32.c @@ -0,0 +1,166 @@ +/* + * QEMU Guest Agent VSS utility functions + * + * 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 <windows.h> +#include "qga/guest-agent-core.h" +#include "qga/vss-win32.h" +#include "qga/vss-win32/requester.h" + +#define QGA_VSS_DLL "qga-vss.dll" + +static HMODULE provider_lib; + +/* Call a function in qga-vss.dll with the specified name */ +static HRESULT call_vss_provider_func(const char *func_name) +{ + FARPROC WINAPI func; + + g_assert(provider_lib); + + func = GetProcAddress(provider_lib, func_name); + if (!func) { + char *msg; + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (char *)&msg, 0, NULL); + fprintf(stderr, "failed to load %s from %s: %s", + func_name, QGA_VSS_DLL, msg); + LocalFree(msg); + return E_FAIL; + } + + return func(); +} + +/* Check whether this OS version supports VSS providers */ +static bool vss_check_os_version(void) +{ + OSVERSIONINFO OSver; + + OSver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx(&OSver); + if ((OSver.dwMajorVersion == 5 && OSver.dwMinorVersion >= 2) || + OSver.dwMajorVersion > 5) { + BOOL wow64 = false; +#ifndef _WIN64 + /* Provider doesn't work under WOW64 (32bit agent on 64bit OS) */ + if (!IsWow64Process(GetCurrentProcess(), &wow64)) { + fprintf(stderr, "failed to IsWow64Process (Error: %lx\n)\n", + GetLastError()); + return false; + } + if (wow64) { + fprintf(stderr, "Warning: Running under WOW64\n"); + } +#endif + return !wow64; + } + return false; +} + +/* Load qga-vss.dll */ +bool vss_init(bool init_requester) +{ + if (!vss_check_os_version()) { + /* Do nothing if OS doesn't support providers. */ + fprintf(stderr, "VSS provider is not supported in this OS version: " + "fsfreeze is disabled.\n"); + return false; + } + + provider_lib = LoadLibraryA(QGA_VSS_DLL); + if (!provider_lib) { + char *msg; + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (char *)&msg, 0, NULL); + fprintf(stderr, "failed to load %s: %sfsfreeze is disabled\n", + QGA_VSS_DLL, msg); + LocalFree(msg); + return false; + } + + if (init_requester) { + HRESULT hr = call_vss_provider_func("requester_init"); + if (FAILED(hr)) { + fprintf(stderr, "fsfreeze is disabled.\n"); + vss_deinit(false); + return false; + } + } + + return true; +} + +/* Unload qga-provider.dll */ +void vss_deinit(bool deinit_requester) +{ + if (deinit_requester) { + call_vss_provider_func("requester_deinit"); + } + FreeLibrary(provider_lib); + provider_lib = NULL; +} + +bool vss_initialized(void) +{ + return !!provider_lib; +} + +int ga_install_vss_provider(void) +{ + HRESULT hr; + + if (!vss_init(false)) { + fprintf(stderr, "Installation of VSS provider is skipped. " + "fsfreeze will be disabled.\n"); + return 0; + } + hr = call_vss_provider_func("COMRegister"); + vss_deinit(false); + + return SUCCEEDED(hr) ? 0 : EXIT_FAILURE; +} + +void ga_uninstall_vss_provider(void) +{ + if (!vss_init(false)) { + fprintf(stderr, "Removal of VSS provider is skipped.\n"); + return; + } + call_vss_provider_func("COMUnregister"); + vss_deinit(false); +} + +/* Call VSS requester and freeze/thaw filesystems and applications */ +void qga_vss_fsfreeze(int *nr_volume, Error **err, bool freeze) +{ + const char *func_name = freeze ? "requester_freeze" : "requester_thaw"; + QGAVSSRequesterFunc func; + ErrorSet errset = { + .error_set = (ErrorSetFunc)error_set_win32, + .errp = (void **)err, + .err_class = ERROR_CLASS_GENERIC_ERROR + }; + + func = (QGAVSSRequesterFunc)GetProcAddress(provider_lib, func_name); + if (!func) { + error_setg_win32(err, GetLastError(), "failed to load %s from %s", + func_name, QGA_VSS_DLL); + return; + } + + func(nr_volume, &errset); +} diff --git a/qga/vss-win32.h b/qga/vss-win32.h new file mode 100644 index 0000000000..db8fbe5208 --- /dev/null +++ b/qga/vss-win32.h @@ -0,0 +1,27 @@ +/* + * QEMU Guest Agent VSS utility 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_H +#define VSS_WIN32_H + +#include "qapi/error.h" + +bool vss_init(bool init_requester); +void vss_deinit(bool deinit_requester); +bool vss_initialized(void); + +int ga_install_vss_provider(void); +void ga_uninstall_vss_provider(void); + +void qga_vss_fsfreeze(int *nr_volume, Error **err, bool freeze); + +#endif diff --git a/qga/vss-win32/Makefile.objs b/qga/vss-win32/Makefile.objs new file mode 100644 index 0000000000..6a69d5008d --- /dev/null +++ b/qga/vss-win32/Makefile.objs @@ -0,0 +1,23 @@ +# rules to build qga-vss.dll + +qga-vss-dll-obj-y += requester.o provider.o install.o + +obj-qga-vss-dll-obj-y = $(addprefix $(obj)/, $(qga-vss-dll-obj-y)) +$(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 + +$(obj)/qga-vss.dll: LDFLAGS = -shared -Wl,--add-stdcall-alias,--enable-stdcall-fixup -lole32 -loleaut32 -lshlwapi -luuid -static +$(obj)/qga-vss.dll: $(obj-qga-vss-dll-obj-y) $(SRC_PATH)/$(obj)/qga-vss.def + $(call quiet-command,$(CXX) -o $@ $(qga-vss-dll-obj-y) $(SRC_PATH)/qga/vss-win32/qga-vss.def $(CXXFLAGS) $(LDFLAGS)," LINK $(TARGET_DIR)$@") + + +# rules to build qga-provider.tlb +# Currently, only native build is supported because building .tlb +# (TypeLibrary) from .idl requires WindowsSDK and MIDL (and cl.exe in VC++). +MIDL=$(WIN_SDK)/Bin/midl + +$(obj)/qga-vss.tlb: $(SRC_PATH)/$(obj)/qga-vss.idl +ifeq ($(WIN_SDK),"") + $(call quiet-command,cp $(dir $<)qga-vss.tlb $@, " COPY $(TARGET_DIR)$@") +else + $(call quiet-command,$(MIDL) -tlb $@ -I $(WIN_SDK)/Include $<," MIDL $(TARGET_DIR)$@") +endif diff --git a/qga/vss-win32/install.cpp b/qga/vss-win32/install.cpp new file mode 100644 index 0000000000..37731a7271 --- /dev/null +++ b/qga/vss-win32/install.cpp @@ -0,0 +1,458 @@ +/* + * QEMU Guest Agent win32 VSS Provider installer + * + * 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 <string.h> + +#include "vss-common.h" +#include "inc/win2003/vscoordint.h" + +#include <comadmin.h> +#include <wbemidl.h> +#include <comdef.h> +#include <comutil.h> + +extern HINSTANCE g_hinstDll; + +const GUID CLSID_COMAdminCatalog = { 0xF618C514, 0xDFB8, 0x11d1, + {0xA2, 0xCF, 0x00, 0x80, 0x5F, 0xC7, 0x92, 0x35} }; +const GUID IID_ICOMAdminCatalog = { 0xDD662187, 0xDFC2, 0x11d1, + {0xA2, 0xCF, 0x00, 0x80, 0x5F, 0xC7, 0x92, 0x35} }; +const GUID CLSID_WbemLocator = { 0x4590f811, 0x1d3a, 0x11d0, + {0x89, 0x1f, 0x00, 0xaa, 0x00, 0x4b, 0x2e, 0x24} }; +const GUID IID_IWbemLocator = { 0xdc12a687, 0x737f, 0x11cf, + {0x88, 0x4d, 0x00, 0xaa, 0x00, 0x4b, 0x2e, 0x24} }; + +void errmsg(DWORD err, const char *text) +{ + /* + * `text' contains function call statement when errmsg is called via chk(). + * To make error message more readable, we cut off the text after '('. + * If text doesn't contains '(', negative precision is given, which is + * treated as though it were missing. + */ + char *msg = NULL, *nul = strchr(text, '('); + int len = nul ? nul - text : -1; + + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (char *)&msg, 0, NULL); + fprintf(stderr, "%.*s. (Error: %lx) %s\n", len, text, err, msg); + LocalFree(msg); +} + +static void errmsg_dialog(DWORD err, const char *text, const char *opt = "") +{ + char *msg, buf[512]; + + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (char *)&msg, 0, NULL); + snprintf(buf, sizeof(buf), "%s%s. (Error: %lx) %s", text, opt, err, msg); + MessageBox(NULL, buf, "Error from " QGA_PROVIDER_NAME, MB_OK|MB_ICONERROR); + LocalFree(msg); +} + +#define _chk(hr, status, msg, err_label) \ + do { \ + hr = (status); \ + if (FAILED(hr)) { \ + errmsg(hr, msg); \ + goto err_label; \ + } \ + } while (0) + +#define chk(status) _chk(hr, status, "Failed to " #status, out) + +void __stdcall _com_issue_error(HRESULT hr) +{ + errmsg(hr, "Unexpected error in COM"); +} + +template<class T> +HRESULT put_Value(ICatalogObject *pObj, LPCWSTR name, T val) +{ + return pObj->put_Value(_bstr_t(name), _variant_t(val)); +} + +/* Lookup Administrators group name from winmgmt */ +static HRESULT GetAdminName(_bstr_t *name) +{ + HRESULT hr; + COMPointer<IWbemLocator> pLoc; + COMPointer<IWbemServices> pSvc; + COMPointer<IEnumWbemClassObject> pEnum; + COMPointer<IWbemClassObject> pWobj; + ULONG returned; + _variant_t var; + + chk(CoCreateInstance(CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER, + IID_IWbemLocator, (LPVOID *)pLoc.replace())); + chk(pLoc->ConnectServer(_bstr_t(L"ROOT\\CIMV2"), NULL, NULL, NULL, + 0, 0, 0, pSvc.replace())); + chk(CoSetProxyBlanket(pSvc, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, + NULL, RPC_C_AUTHN_LEVEL_CALL, + RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE)); + chk(pSvc->ExecQuery(_bstr_t(L"WQL"), + _bstr_t(L"select * from Win32_Account where " + "SID='S-1-5-32-544' and localAccount=TRUE"), + WBEM_FLAG_RETURN_IMMEDIATELY | WBEM_FLAG_FORWARD_ONLY, + NULL, pEnum.replace())); + if (!pEnum) { + hr = E_FAIL; + errmsg(hr, "Failed to query for Administrators"); + goto out; + } + chk(pEnum->Next(WBEM_INFINITE, 1, pWobj.replace(), &returned)); + if (returned == 0) { + hr = E_FAIL; + errmsg(hr, "No Administrators found"); + goto out; + } + + chk(pWobj->Get(_bstr_t(L"Name"), 0, &var, 0, 0)); + try { + *name = var; + } catch(...) { + hr = E_FAIL; + errmsg(hr, "Failed to get name of Administrators"); + goto out; + } + +out: + return hr; +} + +/* Find and iterate QGA VSS provider in COM+ Application Catalog */ +static HRESULT QGAProviderFind( + HRESULT (*found)(ICatalogCollection *, int, void *), void *arg) +{ + HRESULT hr; + COMInitializer initializer; + COMPointer<IUnknown> pUnknown; + COMPointer<ICOMAdminCatalog> pCatalog; + COMPointer<ICatalogCollection> pColl; + COMPointer<ICatalogObject> pObj; + _variant_t var; + long i, n; + + chk(CoCreateInstance(CLSID_COMAdminCatalog, NULL, CLSCTX_INPROC_SERVER, + IID_IUnknown, (void **)pUnknown.replace())); + chk(pUnknown->QueryInterface(IID_ICOMAdminCatalog, + (void **)pCatalog.replace())); + chk(pCatalog->GetCollection(_bstr_t(L"Applications"), + (IDispatch **)pColl.replace())); + chk(pColl->Populate()); + + chk(pColl->get_Count(&n)); + for (i = n - 1; i >= 0; i--) { + chk(pColl->get_Item(i, (IDispatch **)pObj.replace())); + chk(pObj->get_Value(_bstr_t(L"Name"), &var)); + if (var == _variant_t(QGA_PROVIDER_LNAME)) { + if (FAILED(found(pColl, i, arg))) { + goto out; + } + } + } + chk(pColl->SaveChanges(&n)); + +out: + return hr; +} + +/* Count QGA VSS provider in COM+ Application Catalog */ +static HRESULT QGAProviderCount(ICatalogCollection *coll, int i, void *arg) +{ + (*(int *)arg)++; + return S_OK; +} + +/* Remove QGA VSS provider from COM+ Application Catalog Collection */ +static HRESULT QGAProviderRemove(ICatalogCollection *coll, int i, void *arg) +{ + HRESULT hr; + + fprintf(stderr, "Removing COM+ Application: %s\n", QGA_PROVIDER_NAME); + chk(coll->Remove(i)); +out: + return hr; +} + +/* Unregister this module from COM+ Applications Catalog */ +STDAPI COMUnregister(void) +{ + HRESULT hr; + + DllUnregisterServer(); + chk(QGAProviderFind(QGAProviderRemove, NULL)); +out: + return hr; +} + +/* Register this module to COM+ Applications Catalog */ +STDAPI COMRegister(void) +{ + HRESULT hr; + COMInitializer initializer; + COMPointer<IUnknown> pUnknown; + COMPointer<ICOMAdminCatalog> pCatalog; + COMPointer<ICatalogCollection> pApps, pRoles, pUsersInRole; + COMPointer<ICatalogObject> pObj; + long n; + _bstr_t name; + _variant_t key; + CHAR dllPath[MAX_PATH], tlbPath[MAX_PATH]; + bool unregisterOnFailure = false; + int count = 0; + + if (!g_hinstDll) { + errmsg(E_FAIL, "Failed to initialize DLL"); + return E_FAIL; + } + + chk(QGAProviderFind(QGAProviderCount, (void *)&count)); + if (count) { + errmsg(E_ABORT, "QGA VSS Provider is already installed"); + return E_ABORT; + } + + chk(CoCreateInstance(CLSID_COMAdminCatalog, NULL, CLSCTX_INPROC_SERVER, + IID_IUnknown, (void **)pUnknown.replace())); + chk(pUnknown->QueryInterface(IID_ICOMAdminCatalog, + (void **)pCatalog.replace())); + + /* Install COM+ Component */ + + chk(pCatalog->GetCollection(_bstr_t(L"Applications"), + (IDispatch **)pApps.replace())); + chk(pApps->Populate()); + chk(pApps->Add((IDispatch **)&pObj)); + chk(put_Value(pObj, L"Name", QGA_PROVIDER_LNAME)); + chk(put_Value(pObj, L"Description", QGA_PROVIDER_LNAME)); + chk(put_Value(pObj, L"ApplicationAccessChecksEnabled", true)); + chk(put_Value(pObj, L"Authentication", short(6))); + chk(put_Value(pObj, L"AuthenticationCapability", short(2))); + chk(put_Value(pObj, L"ImpersonationLevel", short(2))); + chk(pApps->SaveChanges(&n)); + + /* The app should be deleted if something fails after SaveChanges */ + unregisterOnFailure = true; + + chk(pObj->get_Key(&key)); + + if (!GetModuleFileName(g_hinstDll, dllPath, sizeof(dllPath))) { + hr = HRESULT_FROM_WIN32(GetLastError()); + errmsg(hr, "GetModuleFileName failed"); + goto out; + } + n = strlen(dllPath); + if (n < 3) { + hr = E_FAIL; + errmsg(hr, "Failed to lookup dll"); + goto out; + } + strcpy(tlbPath, dllPath); + strcpy(tlbPath+n-3, "tlb"); + fprintf(stderr, "Registering " QGA_PROVIDER_NAME ":\n"); + fprintf(stderr, " %s\n", dllPath); + fprintf(stderr, " %s\n", tlbPath); + if (!PathFileExists(tlbPath)) { + hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + errmsg(hr, "Failed to lookup tlb"); + goto out; + } + + chk(pCatalog->InstallComponent(_bstr_t(QGA_PROVIDER_LNAME), + _bstr_t(dllPath), _bstr_t(tlbPath), + _bstr_t(""))); + + /* Setup roles of the applicaion */ + + chk(pApps->GetCollection(_bstr_t(L"Roles"), key, + (IDispatch **)pRoles.replace())); + chk(pRoles->Populate()); + chk(pRoles->Add((IDispatch **)pObj.replace())); + chk(put_Value(pObj, L"Name", L"Administrators")); + chk(put_Value(pObj, L"Description", L"Administrators group")); + chk(pRoles->SaveChanges(&n)); + chk(pObj->get_Key(&key)); + + /* Setup users in the role */ + + chk(pRoles->GetCollection(_bstr_t(L"UsersInRole"), key, + (IDispatch **)pUsersInRole.replace())); + chk(pUsersInRole->Populate()); + + chk(pUsersInRole->Add((IDispatch **)pObj.replace())); + chk(GetAdminName(&name)); + chk(put_Value(pObj, L"User", _bstr_t(".\\") + name)); + + chk(pUsersInRole->Add((IDispatch **)pObj.replace())); + chk(put_Value(pObj, L"User", L"SYSTEM")); + chk(pUsersInRole->SaveChanges(&n)); + +out: + if (unregisterOnFailure && FAILED(hr)) { + COMUnregister(); + } + + return hr; +} + + +static BOOL CreateRegistryKey(LPCTSTR key, LPCTSTR value, LPCTSTR data) +{ + HKEY hKey; + LONG ret; + DWORD size; + + ret = RegCreateKeyEx(HKEY_CLASSES_ROOT, key, 0, NULL, + REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, NULL); + if (ret != ERROR_SUCCESS) { + goto out; + } + + if (data != NULL) { + size = strlen(data) + 1; + } else { + size = 0; + } + + ret = RegSetValueEx(hKey, value, 0, REG_SZ, (LPBYTE)data, size); + RegCloseKey(hKey); + +out: + if (ret != ERROR_SUCCESS) { + /* As we cannot printf within DllRegisterServer(), show a dialog. */ + errmsg_dialog(ret, "Cannot add registry", key); + return FALSE; + } + return TRUE; +} + +/* Register this dll as a VSS provider */ +STDAPI DllRegisterServer(void) +{ + COMInitializer initializer; + COMPointer<IVssAdmin> pVssAdmin; + HRESULT hr = E_FAIL; + char dllPath[MAX_PATH]; + char key[256]; + + if (!g_hinstDll) { + errmsg_dialog(hr, "Module instance is not available"); + goto out; + } + + /* Add this module to registery */ + + sprintf(key, "CLSID\\%s", g_szClsid); + if (!CreateRegistryKey(key, NULL, g_szClsid)) { + goto out; + } + + if (!GetModuleFileName(g_hinstDll, dllPath, sizeof(dllPath))) { + errmsg_dialog(GetLastError(), "GetModuleFileName failed"); + goto out; + } + + sprintf(key, "CLSID\\%s\\InprocServer32", g_szClsid); + if (!CreateRegistryKey(key, NULL, dllPath)) { + goto out; + } + + if (!CreateRegistryKey(key, "ThreadingModel", "Apartment")) { + goto out; + } + + sprintf(key, "CLSID\\%s\\ProgID", g_szClsid); + if (!CreateRegistryKey(key, NULL, g_szProgid)) { + goto out; + } + + if (!CreateRegistryKey(g_szProgid, NULL, QGA_PROVIDER_NAME)) { + goto out; + } + + sprintf(key, "%s\\CLSID", g_szProgid); + if (!CreateRegistryKey(key, NULL, g_szClsid)) { + goto out; + } + + hr = CoCreateInstance(CLSID_VSSCoordinator, NULL, CLSCTX_ALL, + IID_IVssAdmin, (void **)pVssAdmin.replace()); + if (FAILED(hr)) { + errmsg_dialog(hr, "CoCreateInstance(VSSCoordinator) failed"); + goto out; + } + + hr = pVssAdmin->RegisterProvider(g_gProviderId, CLSID_QGAVSSProvider, + const_cast<WCHAR*>(QGA_PROVIDER_LNAME), + VSS_PROV_SOFTWARE, + const_cast<WCHAR*>(QGA_PROVIDER_VERSION), + g_gProviderVersion); + if (FAILED(hr)) { + errmsg_dialog(hr, "RegisterProvider failed"); + } + +out: + if (FAILED(hr)) { + DllUnregisterServer(); + } + + return hr; +} + +/* Unregister this VSS hardware provider from the system */ +STDAPI DllUnregisterServer(void) +{ + TCHAR key[256]; + COMInitializer initializer; + COMPointer<IVssAdmin> pVssAdmin; + + HRESULT hr = CoCreateInstance(CLSID_VSSCoordinator, + NULL, CLSCTX_ALL, IID_IVssAdmin, + (void **)pVssAdmin.replace()); + if (SUCCEEDED(hr)) { + hr = pVssAdmin->UnregisterProvider(g_gProviderId); + } else { + errmsg(hr, "CoCreateInstance(VSSCoordinator) failed"); + } + + sprintf(key, "CLSID\\%s", g_szClsid); + SHDeleteKey(HKEY_CLASSES_ROOT, key); + SHDeleteKey(HKEY_CLASSES_ROOT, g_szProgid); + + return S_OK; /* Uninstall should never fail */ +} + + +/* Support function to convert ASCII string into BSTR (used in _bstr_t) */ +namespace _com_util +{ + BSTR WINAPI ConvertStringToBSTR(const char *ascii) { + int len = strlen(ascii); + BSTR bstr = SysAllocStringLen(NULL, len); + + if (!bstr) { + return NULL; + } + + if (mbstowcs(bstr, ascii, len) == (size_t)-1) { + fprintf(stderr, "Failed to convert string '%s' into BSTR", ascii); + bstr[0] = 0; + } + return bstr; + } +} diff --git a/qga/vss-win32/provider.cpp b/qga/vss-win32/provider.cpp new file mode 100644 index 0000000000..bf42b5e95f --- /dev/null +++ b/qga/vss-win32/provider.cpp @@ -0,0 +1,523 @@ +/* + * QEMU Guest Agent win32 VSS Provider 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 "vss-common.h" +#include "inc/win2003/vscoordint.h" +#include "inc/win2003/vsprov.h" + +#define VSS_TIMEOUT_MSEC (60*1000) + +static long g_nComObjsInUse; +HINSTANCE g_hinstDll; + +/* VSS common GUID's */ + +const CLSID CLSID_VSSCoordinator = { 0xE579AB5F, 0x1CC4, 0x44b4, + {0xBE, 0xD9, 0xDE, 0x09, 0x91, 0xFF, 0x06, 0x23} }; +const IID IID_IVssAdmin = { 0x77ED5996, 0x2F63, 0x11d3, + {0x8A, 0x39, 0x00, 0xC0, 0x4F, 0x72, 0xD8, 0xE3} }; + +const IID IID_IVssHardwareSnapshotProvider = { 0x9593A157, 0x44E9, 0x4344, + {0xBB, 0xEB, 0x44, 0xFB, 0xF9, 0xB0, 0x6B, 0x10} }; +const IID IID_IVssSoftwareSnapshotProvider = { 0x609e123e, 0x2c5a, 0x44d3, + {0x8f, 0x01, 0x0b, 0x1d, 0x9a, 0x47, 0xd1, 0xff} }; +const IID IID_IVssProviderCreateSnapshotSet = { 0x5F894E5B, 0x1E39, 0x4778, + {0x8E, 0x23, 0x9A, 0xBA, 0xD9, 0xF0, 0xE0, 0x8C} }; +const IID IID_IVssProviderNotifications = { 0xE561901F, 0x03A5, 0x4afe, + {0x86, 0xD0, 0x72, 0xBA, 0xEE, 0xCE, 0x70, 0x04} }; + +const IID IID_IVssEnumObject = { 0xAE1C7110, 0x2F60, 0x11d3, + {0x8A, 0x39, 0x00, 0xC0, 0x4F, 0x72, 0xD8, 0xE3} }; + + +void LockModule(BOOL lock) +{ + if (lock) { + InterlockedIncrement(&g_nComObjsInUse); + } else { + InterlockedDecrement(&g_nComObjsInUse); + } +} + +/* Empty enumerator for VssObject */ + +class CQGAVSSEnumObject : public IVssEnumObject +{ +public: + STDMETHODIMP QueryInterface(REFIID riid, void **ppObj); + STDMETHODIMP_(ULONG) AddRef(); + STDMETHODIMP_(ULONG) Release(); + + /* IVssEnumObject Methods */ + STDMETHODIMP Next( + ULONG celt, VSS_OBJECT_PROP *rgelt, ULONG *pceltFetched); + STDMETHODIMP Skip(ULONG celt); + STDMETHODIMP Reset(void); + STDMETHODIMP Clone(IVssEnumObject **ppenum); + + /* CQGAVSSEnumObject Methods */ + CQGAVSSEnumObject(); + ~CQGAVSSEnumObject(); + +private: + long m_nRefCount; +}; + +CQGAVSSEnumObject::CQGAVSSEnumObject() +{ + m_nRefCount = 0; + LockModule(TRUE); +} + +CQGAVSSEnumObject::~CQGAVSSEnumObject() +{ + LockModule(FALSE); +} + +STDMETHODIMP CQGAVSSEnumObject::QueryInterface(REFIID riid, void **ppObj) +{ + if (riid == IID_IUnknown || riid == IID_IVssEnumObject) { + *ppObj = static_cast<void*>(static_cast<IVssEnumObject*>(this)); + AddRef(); + return S_OK; + } + *ppObj = NULL; + return E_NOINTERFACE; +} + +STDMETHODIMP_(ULONG) CQGAVSSEnumObject::AddRef() +{ + return InterlockedIncrement(&m_nRefCount); +} + +STDMETHODIMP_(ULONG) CQGAVSSEnumObject::Release() +{ + long nRefCount = InterlockedDecrement(&m_nRefCount); + if (m_nRefCount == 0) { + delete this; + } + return nRefCount; +} + +STDMETHODIMP CQGAVSSEnumObject::Next( + ULONG celt, VSS_OBJECT_PROP *rgelt, ULONG *pceltFetched) +{ + *pceltFetched = 0; + return S_FALSE; +} + +STDMETHODIMP CQGAVSSEnumObject::Skip(ULONG celt) +{ + return S_FALSE; +} + +STDMETHODIMP CQGAVSSEnumObject::Reset(void) +{ + return S_OK; +} + +STDMETHODIMP CQGAVSSEnumObject::Clone(IVssEnumObject **ppenum) +{ + return E_NOTIMPL; +} + + +/* QGAVssProvider */ + +class CQGAVssProvider : + public IVssSoftwareSnapshotProvider, + public IVssProviderCreateSnapshotSet, + public IVssProviderNotifications +{ +public: + STDMETHODIMP QueryInterface(REFIID riid, void **ppObj); + STDMETHODIMP_(ULONG) AddRef(); + STDMETHODIMP_(ULONG) Release(); + + /* IVssSoftwareSnapshotProvider Methods */ + STDMETHODIMP SetContext(LONG lContext); + STDMETHODIMP GetSnapshotProperties( + VSS_ID SnapshotId, VSS_SNAPSHOT_PROP *pProp); + STDMETHODIMP Query( + VSS_ID QueriedObjectId, VSS_OBJECT_TYPE eQueriedObjectType, + VSS_OBJECT_TYPE eReturnedObjectsType, IVssEnumObject **ppEnum); + STDMETHODIMP DeleteSnapshots( + VSS_ID SourceObjectId, VSS_OBJECT_TYPE eSourceObjectType, + BOOL bForceDelete, LONG *plDeletedSnapshots, + VSS_ID *pNondeletedSnapshotID); + STDMETHODIMP BeginPrepareSnapshot( + VSS_ID SnapshotSetId, VSS_ID SnapshotId, + VSS_PWSZ pwszVolumeName, LONG lNewContext); + STDMETHODIMP IsVolumeSupported( + VSS_PWSZ pwszVolumeName, BOOL *pbSupportedByThisProvider); + STDMETHODIMP IsVolumeSnapshotted( + VSS_PWSZ pwszVolumeName, BOOL *pbSnapshotsPresent, + LONG *plSnapshotCompatibility); + STDMETHODIMP SetSnapshotProperty( + VSS_ID SnapshotId, VSS_SNAPSHOT_PROPERTY_ID eSnapshotPropertyId, + VARIANT vProperty); + STDMETHODIMP RevertToSnapshot(VSS_ID SnapshotId); + STDMETHODIMP QueryRevertStatus(VSS_PWSZ pwszVolume, IVssAsync **ppAsync); + + /* IVssProviderCreateSnapshotSet Methods */ + STDMETHODIMP EndPrepareSnapshots(VSS_ID SnapshotSetId); + STDMETHODIMP PreCommitSnapshots(VSS_ID SnapshotSetId); + STDMETHODIMP CommitSnapshots(VSS_ID SnapshotSetId); + STDMETHODIMP PostCommitSnapshots( + VSS_ID SnapshotSetId, LONG lSnapshotsCount); + STDMETHODIMP PreFinalCommitSnapshots(VSS_ID SnapshotSetId); + STDMETHODIMP PostFinalCommitSnapshots(VSS_ID SnapshotSetId); + STDMETHODIMP AbortSnapshots(VSS_ID SnapshotSetId); + + /* IVssProviderNotifications Methods */ + STDMETHODIMP OnLoad(IUnknown *pCallback); + STDMETHODIMP OnUnload(BOOL bForceUnload); + + /* CQGAVssProvider Methods */ + CQGAVssProvider(); + ~CQGAVssProvider(); + +private: + long m_nRefCount; +}; + +CQGAVssProvider::CQGAVssProvider() +{ + m_nRefCount = 0; + LockModule(TRUE); +} + +CQGAVssProvider::~CQGAVssProvider() +{ + LockModule(FALSE); +} + +STDMETHODIMP CQGAVssProvider::QueryInterface(REFIID riid, void **ppObj) +{ + if (riid == IID_IUnknown) { + *ppObj = static_cast<void*>(this); + AddRef(); + return S_OK; + } + if (riid == IID_IVssSoftwareSnapshotProvider) { + *ppObj = static_cast<void*>( + static_cast<IVssSoftwareSnapshotProvider*>(this)); + AddRef(); + return S_OK; + } + if (riid == IID_IVssProviderCreateSnapshotSet) { + *ppObj = static_cast<void*>( + static_cast<IVssProviderCreateSnapshotSet*>(this)); + AddRef(); + return S_OK; + } + if (riid == IID_IVssProviderNotifications) { + *ppObj = static_cast<void*>( + static_cast<IVssProviderNotifications*>(this)); + AddRef(); + return S_OK; + } + *ppObj = NULL; + return E_NOINTERFACE; +} + +STDMETHODIMP_(ULONG) CQGAVssProvider::AddRef() +{ + return InterlockedIncrement(&m_nRefCount); +} + +STDMETHODIMP_(ULONG) CQGAVssProvider::Release() +{ + long nRefCount = InterlockedDecrement(&m_nRefCount); + if (m_nRefCount == 0) { + delete this; + } + return nRefCount; +} + + +/* + * IVssSoftwareSnapshotProvider methods + */ + +STDMETHODIMP CQGAVssProvider::SetContext(LONG lContext) +{ + return S_OK; +} + +STDMETHODIMP CQGAVssProvider::GetSnapshotProperties( + VSS_ID SnapshotId, VSS_SNAPSHOT_PROP *pProp) +{ + return VSS_E_OBJECT_NOT_FOUND; +} + +STDMETHODIMP CQGAVssProvider::Query( + VSS_ID QueriedObjectId, VSS_OBJECT_TYPE eQueriedObjectType, + VSS_OBJECT_TYPE eReturnedObjectsType, IVssEnumObject **ppEnum) +{ + try { + *ppEnum = new CQGAVSSEnumObject; + } catch (...) { + return E_OUTOFMEMORY; + } + (*ppEnum)->AddRef(); + return S_OK; +} + +STDMETHODIMP CQGAVssProvider::DeleteSnapshots( + VSS_ID SourceObjectId, VSS_OBJECT_TYPE eSourceObjectType, + BOOL bForceDelete, LONG *plDeletedSnapshots, VSS_ID *pNondeletedSnapshotID) +{ + return E_NOTIMPL; +} + +STDMETHODIMP CQGAVssProvider::BeginPrepareSnapshot( + VSS_ID SnapshotSetId, VSS_ID SnapshotId, + VSS_PWSZ pwszVolumeName, LONG lNewContext) +{ + return S_OK; +} + +STDMETHODIMP CQGAVssProvider::IsVolumeSupported( + VSS_PWSZ pwszVolumeName, BOOL *pbSupportedByThisProvider) +{ + *pbSupportedByThisProvider = TRUE; + + return S_OK; +} + +STDMETHODIMP CQGAVssProvider::IsVolumeSnapshotted(VSS_PWSZ pwszVolumeName, + BOOL *pbSnapshotsPresent, LONG *plSnapshotCompatibility) +{ + *pbSnapshotsPresent = FALSE; + *plSnapshotCompatibility = 0; + return S_OK; +} + +STDMETHODIMP CQGAVssProvider::SetSnapshotProperty(VSS_ID SnapshotId, + VSS_SNAPSHOT_PROPERTY_ID eSnapshotPropertyId, VARIANT vProperty) +{ + return E_NOTIMPL; +} + +STDMETHODIMP CQGAVssProvider::RevertToSnapshot(VSS_ID SnapshotId) +{ + return E_NOTIMPL; +} + +STDMETHODIMP CQGAVssProvider::QueryRevertStatus( + VSS_PWSZ pwszVolume, IVssAsync **ppAsync) +{ + return E_NOTIMPL; +} + + +/* + * IVssProviderCreateSnapshotSet methods + */ + +STDMETHODIMP CQGAVssProvider::EndPrepareSnapshots(VSS_ID SnapshotSetId) +{ + return S_OK; +} + +STDMETHODIMP CQGAVssProvider::PreCommitSnapshots(VSS_ID SnapshotSetId) +{ + return S_OK; +} + +STDMETHODIMP CQGAVssProvider::CommitSnapshots(VSS_ID SnapshotSetId) +{ + HRESULT hr = S_OK; + HANDLE hEventFrozen, hEventThaw, hEventTimeout; + + hEventFrozen = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_FROZEN); + if (hEventFrozen == INVALID_HANDLE_VALUE) { + return E_FAIL; + } + + hEventThaw = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_THAW); + if (hEventThaw == INVALID_HANDLE_VALUE) { + CloseHandle(hEventFrozen); + return E_FAIL; + } + + hEventTimeout = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_TIMEOUT); + if (hEventTimeout == INVALID_HANDLE_VALUE) { + CloseHandle(hEventFrozen); + CloseHandle(hEventThaw); + return E_FAIL; + } + + /* Send event to qemu-ga to notify filesystem is frozen */ + SetEvent(hEventFrozen); + + /* Wait until the snapshot is taken by the host. */ + if (WaitForSingleObject(hEventThaw, VSS_TIMEOUT_MSEC) != WAIT_OBJECT_0) { + /* Send event to qemu-ga to notify the provider is timed out */ + SetEvent(hEventTimeout); + hr = E_ABORT; + } + + CloseHandle(hEventThaw); + CloseHandle(hEventFrozen); + CloseHandle(hEventTimeout); + return hr; +} + +STDMETHODIMP CQGAVssProvider::PostCommitSnapshots( + VSS_ID SnapshotSetId, LONG lSnapshotsCount) +{ + return S_OK; +} + +STDMETHODIMP CQGAVssProvider::PreFinalCommitSnapshots(VSS_ID SnapshotSetId) +{ + return S_OK; +} + +STDMETHODIMP CQGAVssProvider::PostFinalCommitSnapshots(VSS_ID SnapshotSetId) +{ + return S_OK; +} + +STDMETHODIMP CQGAVssProvider::AbortSnapshots(VSS_ID SnapshotSetId) +{ + return S_OK; +} + +/* + * IVssProviderNotifications methods + */ + +STDMETHODIMP CQGAVssProvider::OnLoad(IUnknown *pCallback) +{ + return S_OK; +} + +STDMETHODIMP CQGAVssProvider::OnUnload(BOOL bForceUnload) +{ + return S_OK; +} + + +/* + * CQGAVssProviderFactory class + */ + +class CQGAVssProviderFactory : public IClassFactory +{ +public: + STDMETHODIMP QueryInterface(REFIID riid, void **ppv); + STDMETHODIMP_(ULONG) AddRef(); + STDMETHODIMP_(ULONG) Release(); + STDMETHODIMP CreateInstance( + IUnknown *pUnknownOuter, REFIID iid, void **ppv); + STDMETHODIMP LockServer(BOOL lock) { return E_NOTIMPL; } + + CQGAVssProviderFactory(); + ~CQGAVssProviderFactory(); + +private: + long m_nRefCount; +}; + +CQGAVssProviderFactory::CQGAVssProviderFactory() +{ + m_nRefCount = 0; + LockModule(TRUE); +} + +CQGAVssProviderFactory::~CQGAVssProviderFactory() +{ + LockModule(FALSE); +} + +STDMETHODIMP CQGAVssProviderFactory::QueryInterface(REFIID riid, void **ppv) +{ + if (riid == IID_IUnknown || riid == IID_IClassFactory) { + *ppv = static_cast<void*>(this); + AddRef(); + return S_OK; + } + *ppv = NULL; + return E_NOINTERFACE; +} + +STDMETHODIMP_(ULONG) CQGAVssProviderFactory::AddRef() +{ + return InterlockedIncrement(&m_nRefCount); +} + +STDMETHODIMP_(ULONG) CQGAVssProviderFactory::Release() +{ + long nRefCount = InterlockedDecrement(&m_nRefCount); + if (m_nRefCount == 0) { + delete this; + } + return nRefCount; +} + +STDMETHODIMP CQGAVssProviderFactory::CreateInstance( + IUnknown *pUnknownOuter, REFIID iid, void **ppv) +{ + CQGAVssProvider *pObj; + + if (pUnknownOuter) { + return CLASS_E_NOAGGREGATION; + } + try { + pObj = new CQGAVssProvider; + } catch (...) { + return E_OUTOFMEMORY; + } + HRESULT hr = pObj->QueryInterface(iid, ppv); + if (FAILED(hr)) { + delete pObj; + } + return hr; +} + + +/* + * DLL functions + */ + +STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv) +{ + CQGAVssProviderFactory *factory; + try { + factory = new CQGAVssProviderFactory; + } catch (...) { + return E_OUTOFMEMORY; + } + factory->AddRef(); + HRESULT hr = factory->QueryInterface(riid, ppv); + factory->Release(); + return hr; +} + +STDAPI DllCanUnloadNow() +{ + return g_nComObjsInUse == 0 ? S_OK : S_FALSE; +} + +EXTERN_C +BOOL WINAPI DllMain(HINSTANCE hinstDll, DWORD dwReason, LPVOID lpReserved) +{ + if (dwReason == DLL_PROCESS_ATTACH) { + g_hinstDll = hinstDll; + DisableThreadLibraryCalls(hinstDll); + } + return TRUE; +} diff --git a/qga/vss-win32/qga-vss.def b/qga/vss-win32/qga-vss.def new file mode 100644 index 0000000000..927782c31b --- /dev/null +++ b/qga/vss-win32/qga-vss.def @@ -0,0 +1,13 @@ +LIBRARY "QGA-PROVIDER.DLL" + +EXPORTS + COMRegister PRIVATE + COMUnregister PRIVATE + DllCanUnloadNow PRIVATE + DllGetClassObject PRIVATE + DllRegisterServer PRIVATE + DllUnregisterServer PRIVATE + requester_init PRIVATE + requester_deinit PRIVATE + requester_freeze PRIVATE + requester_thaw PRIVATE diff --git a/qga/vss-win32/qga-vss.idl b/qga/vss-win32/qga-vss.idl new file mode 100644 index 0000000000..17abca0da5 --- /dev/null +++ b/qga/vss-win32/qga-vss.idl @@ -0,0 +1,20 @@ +import "oaidl.idl"; +import "ocidl.idl"; + +[ + uuid(103B8142-6CE5-48A7-BDE1-794D3192FCF1), + version(1.0), + helpstring("QGAVSSProvider Type Library") +] +library QGAVSSHWProviderLib +{ + importlib("stdole2.tlb"); + [ + uuid(6E6A3492-8D4D-440C-9619-5E5D0CC31CA8), + helpstring("QGAVSSProvider Class") + ] + coclass QGAVSSHWProvider + { + [default] interface IUnknown; + }; +}; diff --git a/qga/vss-win32/qga-vss.tlb b/qga/vss-win32/qga-vss.tlb Binary files differnew file mode 100644 index 0000000000..226452a186 --- /dev/null +++ b/qga/vss-win32/qga-vss.tlb diff --git a/qga/vss-win32/requester.cpp b/qga/vss-win32/requester.cpp new file mode 100644 index 0000000000..1e8dd3dfa8 --- /dev/null +++ b/qga/vss-win32/requester.cpp @@ -0,0 +1,507 @@ +/* + * 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 "vss-common.h" +#include "requester.h" +#include "assert.h" +#include "inc/win2003/vswriter.h" +#include "inc/win2003/vsbackup.h" + +/* Max wait time for frozen event (VSS can only hold writes for 10 seconds) */ +#define VSS_TIMEOUT_FREEZE_MSEC 10000 + +/* Call QueryStatus every 10 ms while waiting for frozen event */ +#define VSS_TIMEOUT_EVENT_MSEC 10 + +#define err_set(e, err, fmt, ...) \ + ((e)->error_set((e)->errp, err, (e)->err_class, fmt, ## __VA_ARGS__)) +#define err_is_set(e) ((e)->errp && *(e)->errp) + + +/* Handle to VSSAPI.DLL */ +static HMODULE hLib; + +/* Functions in VSSAPI.DLL */ +typedef HRESULT(STDAPICALLTYPE * t_CreateVssBackupComponents)( + OUT IVssBackupComponents**); +typedef void(APIENTRY * t_VssFreeSnapshotProperties)(IN VSS_SNAPSHOT_PROP*); +static t_CreateVssBackupComponents pCreateVssBackupComponents; +static t_VssFreeSnapshotProperties pVssFreeSnapshotProperties; + +/* Variables used while applications and filesystes are frozen by VSS */ +static struct QGAVSSContext { + IVssBackupComponents *pVssbc; /* VSS requester interface */ + IVssAsync *pAsyncSnapshot; /* async info of VSS snapshot operation */ + HANDLE hEventFrozen; /* notify fs/writer freeze from provider */ + HANDLE hEventThaw; /* request provider to thaw */ + HANDLE hEventTimeout; /* notify timeout in provider */ + int cFrozenVols; /* number of frozen volumes */ +} vss_ctx; + +STDAPI requester_init(void) +{ + vss_ctx.hEventFrozen = INVALID_HANDLE_VALUE; + vss_ctx.hEventThaw = INVALID_HANDLE_VALUE; + vss_ctx.hEventTimeout = INVALID_HANDLE_VALUE; + + COMInitializer initializer; /* to call CoInitializeSecurity */ + HRESULT hr = CoInitializeSecurity( + NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_PKT_PRIVACY, + RPC_C_IMP_LEVEL_IDENTIFY, NULL, EOAC_NONE, NULL); + if (FAILED(hr)) { + fprintf(stderr, "failed to CoInitializeSecurity (error %lx)\n", hr); + return hr; + } + + hLib = LoadLibraryA("VSSAPI.DLL"); + if (!hLib) { + fprintf(stderr, "failed to load VSSAPI.DLL\n"); + return HRESULT_FROM_WIN32(GetLastError()); + } + + pCreateVssBackupComponents = (t_CreateVssBackupComponents) + GetProcAddress(hLib, +#ifdef _WIN64 /* 64bit environment */ + "?CreateVssBackupComponents@@YAJPEAPEAVIVssBackupComponents@@@Z" +#else /* 32bit environment */ + "?CreateVssBackupComponents@@YGJPAPAVIVssBackupComponents@@@Z" +#endif + ); + if (!pCreateVssBackupComponents) { + fprintf(stderr, "failed to get proc address from VSSAPI.DLL\n"); + return HRESULT_FROM_WIN32(GetLastError()); + } + + pVssFreeSnapshotProperties = (t_VssFreeSnapshotProperties) + GetProcAddress(hLib, "VssFreeSnapshotProperties"); + if (!pVssFreeSnapshotProperties) { + fprintf(stderr, "failed to get proc address from VSSAPI.DLL\n"); + return HRESULT_FROM_WIN32(GetLastError()); + } + + return S_OK; +} + +static void requester_cleanup(void) +{ + if (vss_ctx.hEventFrozen != INVALID_HANDLE_VALUE) { + CloseHandle(vss_ctx.hEventFrozen); + vss_ctx.hEventFrozen = INVALID_HANDLE_VALUE; + } + if (vss_ctx.hEventThaw != INVALID_HANDLE_VALUE) { + CloseHandle(vss_ctx.hEventThaw); + vss_ctx.hEventThaw = INVALID_HANDLE_VALUE; + } + if (vss_ctx.hEventTimeout != INVALID_HANDLE_VALUE) { + CloseHandle(vss_ctx.hEventTimeout); + vss_ctx.hEventTimeout = INVALID_HANDLE_VALUE; + } + if (vss_ctx.pAsyncSnapshot) { + vss_ctx.pAsyncSnapshot->Release(); + vss_ctx.pAsyncSnapshot = NULL; + } + if (vss_ctx.pVssbc) { + vss_ctx.pVssbc->Release(); + vss_ctx.pVssbc = NULL; + } + vss_ctx.cFrozenVols = 0; +} + +STDAPI requester_deinit(void) +{ + requester_cleanup(); + + pCreateVssBackupComponents = NULL; + pVssFreeSnapshotProperties = NULL; + if (hLib) { + FreeLibrary(hLib); + hLib = NULL; + } + + return S_OK; +} + +static 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; +} + +static void AddComponents(ErrorSet *errset) +{ + unsigned int cWriters, i; + VSS_ID id, idInstance, idWriter; + BSTR bstrWriterName = NULL; + VSS_USAGE_TYPE usage; + VSS_SOURCE_TYPE source; + unsigned int cComponents, c1, c2, j; + COMPointer<IVssExamineWriterMetadata> pMetadata; + COMPointer<IVssWMComponent> pComponent; + PVSSCOMPONENTINFO info; + HRESULT hr; + + hr = vss_ctx.pVssbc->GetWriterMetadataCount(&cWriters); + if (FAILED(hr)) { + err_set(errset, hr, "failed to get writer metadata count"); + goto out; + } + + for (i = 0; i < cWriters; i++) { + hr = vss_ctx.pVssbc->GetWriterMetadata(i, &id, pMetadata.replace()); + if (FAILED(hr)) { + err_set(errset, hr, "failed to get writer metadata of %d/%d", + i, cWriters); + goto out; + } + + hr = pMetadata->GetIdentity(&idInstance, &idWriter, + &bstrWriterName, &usage, &source); + if (FAILED(hr)) { + err_set(errset, hr, "failed to get identity of writer %d/%d", + i, cWriters); + goto out; + } + + hr = pMetadata->GetFileCounts(&c1, &c2, &cComponents); + if (FAILED(hr)) { + err_set(errset, hr, "failed to get file counts of %S", + bstrWriterName); + goto out; + } + + for (j = 0; j < cComponents; j++) { + hr = pMetadata->GetComponent(j, pComponent.replace()); + if (FAILED(hr)) { + err_set(errset, hr, + "failed to get component %d/%d of %S", + j, cComponents, bstrWriterName); + goto out; + } + + hr = pComponent->GetComponentInfo(&info); + if (FAILED(hr)) { + err_set(errset, hr, + "failed to get component info %d/%d of %S", + j, cComponents, bstrWriterName); + goto out; + } + + if (info->bSelectable) { + hr = vss_ctx.pVssbc->AddComponent(idInstance, idWriter, + info->type, + info->bstrLogicalPath, + info->bstrComponentName); + if (FAILED(hr)) { + err_set(errset, hr, "failed to add component %S(%S)", + info->bstrComponentName, bstrWriterName); + goto out; + } + } + SysFreeString(bstrWriterName); + bstrWriterName = NULL; + pComponent->FreeComponentInfo(info); + info = NULL; + } + } +out: + if (bstrWriterName) { + SysFreeString(bstrWriterName); + } + if (pComponent && info) { + pComponent->FreeComponentInfo(info); + } +} + +void requester_freeze(int *num_vols, ErrorSet *errset) +{ + COMPointer<IVssAsync> pAsync; + HANDLE volume; + HRESULT hr; + LONG ctx; + GUID guidSnapshotSet = GUID_NULL; + SECURITY_DESCRIPTOR sd; + SECURITY_ATTRIBUTES sa; + WCHAR short_volume_name[64], *display_name = short_volume_name; + DWORD wait_status; + int num_fixed_drives = 0, i; + + if (vss_ctx.pVssbc) { /* already frozen */ + *num_vols = 0; + return; + } + + CoInitialize(NULL); + + assert(pCreateVssBackupComponents != NULL); + hr = pCreateVssBackupComponents(&vss_ctx.pVssbc); + if (FAILED(hr)) { + err_set(errset, hr, "failed to create VSS backup components"); + goto out; + } + + hr = vss_ctx.pVssbc->InitializeForBackup(); + if (FAILED(hr)) { + err_set(errset, hr, "failed to initialize for backup"); + goto out; + } + + hr = vss_ctx.pVssbc->SetBackupState(true, true, VSS_BT_FULL, false); + if (FAILED(hr)) { + err_set(errset, hr, "failed to set backup state"); + goto out; + } + + /* + * Currently writable snapshots are not supported. + * To prevent the final commit (which requires to write to snapshots), + * ATTR_NO_AUTORECOVERY and ATTR_TRANSPORTABLE are specified here. + */ + ctx = VSS_CTX_APP_ROLLBACK | VSS_VOLSNAP_ATTR_TRANSPORTABLE | + VSS_VOLSNAP_ATTR_NO_AUTORECOVERY | VSS_VOLSNAP_ATTR_TXF_RECOVERY; + hr = vss_ctx.pVssbc->SetContext(ctx); + if (hr == (HRESULT)VSS_E_UNSUPPORTED_CONTEXT) { + /* Non-server version of Windows doesn't support ATTR_TRANSPORTABLE */ + ctx &= ~VSS_VOLSNAP_ATTR_TRANSPORTABLE; + hr = vss_ctx.pVssbc->SetContext(ctx); + } + if (FAILED(hr)) { + err_set(errset, hr, "failed to set backup context"); + goto out; + } + + hr = vss_ctx.pVssbc->GatherWriterMetadata(pAsync.replace()); + if (SUCCEEDED(hr)) { + hr = WaitForAsync(pAsync); + } + if (FAILED(hr)) { + err_set(errset, hr, "failed to gather writer metadata"); + goto out; + } + + AddComponents(errset); + if (err_is_set(errset)) { + goto out; + } + + hr = vss_ctx.pVssbc->StartSnapshotSet(&guidSnapshotSet); + if (FAILED(hr)) { + err_set(errset, hr, "failed to start snapshot set"); + goto out; + } + + volume = FindFirstVolumeW(short_volume_name, sizeof(short_volume_name)); + if (volume == INVALID_HANDLE_VALUE) { + err_set(errset, hr, "failed to find first volume"); + goto out; + } + for (;;) { + if (GetDriveTypeW(short_volume_name) == DRIVE_FIXED) { + VSS_ID pid; + hr = vss_ctx.pVssbc->AddToSnapshotSet(short_volume_name, + g_gProviderId, &pid); + if (FAILED(hr)) { + WCHAR volume_path_name[PATH_MAX]; + if (GetVolumePathNamesForVolumeNameW( + short_volume_name, volume_path_name, + sizeof(volume_path_name), NULL) && *volume_path_name) { + display_name = volume_path_name; + } + err_set(errset, hr, "failed to add %S to snapshot set", + display_name); + FindVolumeClose(volume); + goto out; + } + num_fixed_drives++; + } + if (!FindNextVolumeW(volume, short_volume_name, + sizeof(short_volume_name))) { + FindVolumeClose(volume); + break; + } + } + + if (num_fixed_drives == 0) { + goto out; /* If there is no fixed drive, just exit. */ + } + + hr = vss_ctx.pVssbc->PrepareForBackup(pAsync.replace()); + if (SUCCEEDED(hr)) { + hr = WaitForAsync(pAsync); + } + if (FAILED(hr)) { + err_set(errset, hr, "failed to prepare for backup"); + goto out; + } + + hr = vss_ctx.pVssbc->GatherWriterStatus(pAsync.replace()); + if (SUCCEEDED(hr)) { + hr = WaitForAsync(pAsync); + } + if (FAILED(hr)) { + err_set(errset, hr, "failed to gather writer status"); + goto out; + } + + /* 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; + + vss_ctx.hEventFrozen = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_FROZEN); + if (vss_ctx.hEventFrozen == INVALID_HANDLE_VALUE) { + err_set(errset, GetLastError(), "failed to create event %s", + EVENT_NAME_FROZEN); + goto out; + } + vss_ctx.hEventThaw = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_THAW); + if (vss_ctx.hEventThaw == INVALID_HANDLE_VALUE) { + err_set(errset, GetLastError(), "failed to create event %s", + EVENT_NAME_THAW); + goto out; + } + vss_ctx.hEventTimeout = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_TIMEOUT); + if (vss_ctx.hEventTimeout == INVALID_HANDLE_VALUE) { + err_set(errset, GetLastError(), "failed to create event %s", + EVENT_NAME_TIMEOUT); + goto out; + } + + /* + * Start VSS quiescing operations. + * CQGAVssProvider::CommitSnapshots will kick vss_ctx.hEventFrozen + * after the applications and filesystems are frozen. + */ + hr = vss_ctx.pVssbc->DoSnapshotSet(&vss_ctx.pAsyncSnapshot); + if (FAILED(hr)) { + err_set(errset, hr, "failed to do snapshot set"); + goto out; + } + + /* Need to call QueryStatus several times to make VSS provider progress */ + for (i = 0; i < VSS_TIMEOUT_FREEZE_MSEC/VSS_TIMEOUT_EVENT_MSEC; i++) { + HRESULT hr2 = vss_ctx.pAsyncSnapshot->QueryStatus(&hr, NULL); + if (FAILED(hr2)) { + err_set(errset, hr, "failed to do snapshot set"); + goto out; + } + if (hr != VSS_S_ASYNC_PENDING) { + err_set(errset, E_FAIL, + "DoSnapshotSet exited without Frozen event"); + goto out; + } + wait_status = WaitForSingleObject(vss_ctx.hEventFrozen, + VSS_TIMEOUT_EVENT_MSEC); + if (wait_status != WAIT_TIMEOUT) { + break; + } + } + if (wait_status != WAIT_OBJECT_0) { + err_set(errset, E_FAIL, + "couldn't receive Frozen event from VSS provider"); + goto out; + } + + *num_vols = vss_ctx.cFrozenVols = num_fixed_drives; + return; + +out: + if (vss_ctx.pVssbc) { + vss_ctx.pVssbc->AbortBackup(); + } + requester_cleanup(); + CoUninitialize(); +} + + +void requester_thaw(int *num_vols, ErrorSet *errset) +{ + COMPointer<IVssAsync> pAsync; + + if (vss_ctx.hEventThaw == 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; + } + + /* Tell the provider that the snapshot is finished. */ + SetEvent(vss_ctx.hEventThaw); + + assert(vss_ctx.pVssbc); + assert(vss_ctx.pAsyncSnapshot); + + HRESULT hr = WaitForAsync(vss_ctx.pAsyncSnapshot); + switch (hr) { + case VSS_S_ASYNC_FINISHED: + hr = vss_ctx.pVssbc->BackupComplete(pAsync.replace()); + if (SUCCEEDED(hr)) { + hr = WaitForAsync(pAsync); + } + if (FAILED(hr)) { + err_set(errset, hr, "failed to complete backup"); + } + break; + + case (HRESULT)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. However, as + * the system had been frozen until fsfreeze-thaw command was issued, + * we ignore this error. + */ + vss_ctx.pVssbc->AbortBackup(); + break; + + case VSS_E_UNEXPECTED_PROVIDER_ERROR: + if (WaitForSingleObject(vss_ctx.hEventTimeout, 0) != WAIT_OBJECT_0) { + err_set(errset, hr, "unexpected error in VSS provider"); + break; + } + /* fall through if hEventTimeout is signaled */ + + case (HRESULT)VSS_E_HOLD_WRITES_TIMEOUT: + err_set(errset, hr, "couldn't hold writes: " + "fsfreeze is limited up to 10 seconds"); + break; + + default: + err_set(errset, hr, "failed to do snapshot set"); + } + + if (err_is_set(errset)) { + vss_ctx.pVssbc->AbortBackup(); + } + *num_vols = vss_ctx.cFrozenVols; + requester_cleanup(); + + CoUninitialize(); +} diff --git a/qga/vss-win32/requester.h b/qga/vss-win32/requester.h new file mode 100644 index 0000000000..cffec01791 --- /dev/null +++ b/qga/vss-win32/requester.h @@ -0,0 +1,42 @@ +/* + * 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 + +#include "qemu/compiler.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Callback to set Error; used to avoid linking glib to the DLL */ +typedef void (*ErrorSetFunc)(void **errp, int win32_err, int err_class, + const char *fmt, ...) GCC_FMT_ATTR(4, 5); +typedef struct ErrorSet { + ErrorSetFunc error_set; + void **errp; + int err_class; +} ErrorSet; + +STDAPI requester_init(void); +STDAPI requester_deinit(void); + +typedef void (*QGAVSSRequesterFunc)(int *, ErrorSet *); +void requester_freeze(int *num_vols, ErrorSet *errset); +void requester_thaw(int *num_vols, ErrorSet *errset); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/qga/vss-win32/vss-common.h b/qga/vss-win32/vss-common.h new file mode 100644 index 0000000000..ce14e14290 --- /dev/null +++ b/qga/vss-win32/vss-common.h @@ -0,0 +1,129 @@ +/* + * QEMU Guest Agent win32 VSS common 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_H +#define VSS_WIN32_H + +#define __MIDL_user_allocate_free_DEFINED__ +#include "config-host.h" +#include <windows.h> +#include <shlwapi.h> + +/* Reduce warnings to include vss.h */ + +/* Ignore annotations for MS IDE */ +#define __in IN +#define __out OUT +#define __RPC_unique_pointer +#define __RPC_string +#define __RPC__deref_inout_opt +#define __RPC__out +#ifndef __RPC__out_ecount_part +#define __RPC__out_ecount_part(x, y) +#endif +#define _declspec(x) +#undef uuid +#define uuid(x) + +/* Undef some duplicated error codes redefined in vss.h */ +#undef VSS_E_BAD_STATE +#undef VSS_E_PROVIDER_NOT_REGISTERED +#undef VSS_E_PROVIDER_VETO +#undef VSS_E_OBJECT_NOT_FOUND +#undef VSS_E_VOLUME_NOT_SUPPORTED +#undef VSS_E_VOLUME_NOT_SUPPORTED_BY_PROVIDER +#undef VSS_E_OBJECT_ALREADY_EXISTS +#undef VSS_E_UNEXPECTED_PROVIDER_ERROR +#undef VSS_E_INVALID_XML_DOCUMENT +#undef VSS_E_MAXIMUM_NUMBER_OF_VOLUMES_REACHED +#undef VSS_E_MAXIMUM_NUMBER_OF_SNAPSHOTS_REACHED + +/* + * VSS headers must be installed from Microsoft VSS SDK 7.2 available at: + * http://www.microsoft.com/en-us/download/details.aspx?id=23490 + */ +#include "inc/win2003/vss.h" + +/* Macros to convert char definitions to wchar */ +#define _L(a) L##a +#define L(a) _L(a) + +/* Constants for QGA VSS Provider */ + +#define QGA_PROVIDER_NAME "QEMU Guest Agent VSS Provider" +#define QGA_PROVIDER_LNAME L(QGA_PROVIDER_NAME) +#define QGA_PROVIDER_VERSION L(QEMU_VERSION) + +#define EVENT_NAME_FROZEN "Global\\QGAVSSEvent-frozen" +#define EVENT_NAME_THAW "Global\\QGAVSSEvent-thaw" +#define EVENT_NAME_TIMEOUT "Global\\QGAVSSEvent-timeout" + +const GUID g_gProviderId = { 0x3629d4ed, 0xee09, 0x4e0e, + {0x9a, 0x5c, 0x6d, 0x8b, 0xa2, 0x87, 0x2a, 0xef} }; +const GUID g_gProviderVersion = { 0x11ef8b15, 0xcac6, 0x40d6, + {0x8d, 0x5c, 0x8f, 0xfc, 0x16, 0x3f, 0x24, 0xca} }; + +const CLSID CLSID_QGAVSSProvider = { 0x6e6a3492, 0x8d4d, 0x440c, + {0x96, 0x19, 0x5e, 0x5d, 0x0c, 0xc3, 0x1c, 0xa8} }; + +const TCHAR g_szClsid[] = TEXT("{6E6A3492-8D4D-440C-9619-5E5D0CC31CA8}"); +const TCHAR g_szProgid[] = TEXT("QGAVSSProvider"); + +/* Enums undefined in VSS SDK 7.2 but defined in newer Windows SDK */ +enum __VSS_VOLUME_SNAPSHOT_ATTRIBUTES { + VSS_VOLSNAP_ATTR_NO_AUTORECOVERY = 0x00000002, + VSS_VOLSNAP_ATTR_TXF_RECOVERY = 0x02000000 +}; + + +/* COM pointer utility; call ->Release() when it goes out of scope */ +template <class T> +class COMPointer { + COMPointer(const COMPointer<T> &p) { } /* no copy */ + T *p; +public: + COMPointer &operator=(T *new_p) + { + /* Assignment of a new T* (or NULL) causes release of previous p */ + if (p && p != new_p) { + p->Release(); + } + p = new_p; + return *this; + } + /* Replace by assignment to the pointer of p */ + T **replace(void) + { + *this = NULL; + return &p; + } + /* Make COMPointer be used like T* */ + operator T*() { return p; } + T *operator->(void) { return p; } + T &operator*(void) { return *p; } + operator bool() { return !!p; } + + COMPointer(T *p = NULL) : p(p) { } + ~COMPointer() { *this = NULL; } /* Automatic release */ +}; + +/* + * COM initializer; this should declared before COMPointer to uninitialize COM + * after releasing COM objects. + */ +class COMInitializer { +public: + COMInitializer() { CoInitialize(NULL); } + ~COMInitializer() { CoUninitialize(); } +}; + +#endif @@ -8,9 +8,13 @@ MAKEFLAGS += -rR %.d: %.h: %.c: +%.cpp: %.m: %.mak: +# Flags for C++ compilation +QEMU_CXXFLAGS = -D__STDC_LIMIT_MACROS $(filter-out -Wstrict-prototypes -Wmissing-prototypes -Wnested-externs -Wold-style-declaration -Wold-style-definition -Wredundant-decls, $(QEMU_CFLAGS)) + # Flags for dependency generation QEMU_DGFLAGS += -MMD -MP -MT $@ -MF $(*D)/$(*F).d @@ -50,6 +54,9 @@ endif %.o: %.asm $(call quiet-command,$(AS) $(ASFLAGS) -o $@ $<," AS $(TARGET_DIR)$@") +%.o: %.cpp + $(call quiet-command,$(CXX) $(QEMU_INCLUDES) $(QEMU_CXXFLAGS) $(QEMU_DGFLAGS) $(CFLAGS) -c -o $@ $<," CXX $(TARGET_DIR)$@") + %.o: %.m $(call quiet-command,$(OBJCC) $(QEMU_INCLUDES) $(QEMU_CFLAGS) $(QEMU_DGFLAGS) $(CFLAGS) -c -o $@ $<," OBJC $(TARGET_DIR)$@") @@ -70,7 +77,7 @@ quiet-command = $(if $(V),$1,$(if $(2),@echo $2 && $1, @$1)) cc-option = $(if $(shell $(CC) $1 $2 -S -o /dev/null -xc /dev/null \ >/dev/null 2>&1 && echo OK), $2, $3) -VPATH_SUFFIXES = %.c %.h %.S %.m %.mak %.texi %.sh %.rc +VPATH_SUFFIXES = %.c %.h %.S %.cpp %.m %.mak %.texi %.sh %.rc set-vpath = $(if $1,$(foreach PATTERN,$(VPATH_SUFFIXES),$(eval vpath $(PATTERN) $1))) # find-in-path diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index ec0aa4cd93..9d46e5a104 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -1363,7 +1363,7 @@ sub process { # Check for incorrect file permissions if ($line =~ /^new (file )?mode.*[7531]\d{0,2}$/) { my $permhere = $here . "FILE: $realfile\n"; - if ($realfile =~ /(Makefile|Kconfig|\.c|\.h|\.S|\.tmpl)$/) { + if ($realfile =~ /(Makefile|Kconfig|\.c|\.cpp|\.h|\.S|\.tmpl)$/) { ERROR("do not set execute permissions for source files\n" . $permhere); } } @@ -1460,7 +1460,7 @@ sub process { } # check we are in a valid source file if not then ignore this hunk - next if ($realfile !~ /\.(h|c|s|S|pl|sh)$/); + next if ($realfile !~ /\.(h|c|cpp|s|S|pl|sh)$/); #80 column limit if ($line =~ /^\+/ && $prevrawline !~ /\/\*\*/ && @@ -1495,7 +1495,7 @@ sub process { } # check we are in a valid source file C or perl if not then ignore this hunk - next if ($realfile !~ /\.(h|c|pl)$/); + next if ($realfile !~ /\.(h|c|cpp|pl)$/); # in QEMU, no tabs are allowed if ($rawline =~ /^\+.*\t/) { @@ -1505,7 +1505,7 @@ sub process { } # check we are in a valid C source file if not then ignore this hunk - next if ($realfile !~ /\.(h|c)$/); + next if ($realfile !~ /\.(h|c|cpp)$/); # check for RCS/CVS revision markers if ($rawline =~ /^\+.*\$(Revision|Log|Id)(?:\$|)/) { @@ -1969,6 +1969,9 @@ sub process { asm|__asm__)$/x) { + # Ignore 'catch (...)' in C++ + } elsif ($name =~ /^catch$/ && $realfile =~ /(\.cpp|\.h)$/) { + # cpp #define statements have non-optional spaces, ie # if there is a space between the name and the open # parenthesis it is simply not a parameter group. @@ -1992,7 +1995,7 @@ sub process { \+=|-=|\*=|\/=|%=|\^=|\|=|&=| =>|->|<<|>>|<|>|=|!|~| &&|\|\||,|\^|\+\+|--|&|\||\+|-|\*|\/|%| - \?|: + \?|::|: }x; my @elements = split(/($ops|;)/, $opline); my $off = 0; @@ -2062,6 +2065,10 @@ sub process { # // is a comment } elsif ($op eq '//') { + # Ignore : used in class declaration in C++ + } elsif ($opv eq ':B' && $ctx =~ /Wx[WE]/ && + $line =~ /class/ && $realfile =~ /(\.cpp|\.h)$/) { + # No spaces for: # -> # : when part of a bitfield @@ -2088,7 +2095,10 @@ sub process { } elsif ($op eq '!' || $op eq '~' || $opv eq '*U' || $opv eq '-U' || $opv eq '&U' || $opv eq '&&U') { - if ($ctx !~ /[WEBC]x./ && $ca !~ /(?:\)|!|~|\*|-|\&|\||\+\+|\-\-|\{)$/) { + if ($op eq '~' && $ca =~ /::$/ && $realfile =~ /(\.cpp|\.h)$/) { + # '~' used as a name of Destructor + + } elsif ($ctx !~ /[WEBC]x./ && $ca !~ /(?:\)|!|~|\*|-|\&|\||\+\+|\-\-|\{)$/) { ERROR("space required before that '$op' $at\n" . $hereptr); } if ($op eq '*' && $cc =~/\s*$Modifier\b/) { @@ -2135,6 +2145,18 @@ sub process { } elsif ($ctx !~ /[EWC]x[CWE]/) { my $ok = 0; + if ($realfile =~ /\.cpp|\.h$/) { + # Ignore template arguments <...> in C++ + if (($op eq '<' || $op eq '>') && $line =~ /<.*>/) { + $ok = 1; + } + + # Ignore :: in C++ + if ($op eq '::') { + $ok = 1; + } + } + # Ignore email addresses <foo@bar> if (($op eq '<' && $cc =~ /^\S+\@\S+>/) || diff --git a/scripts/extract-vsssdk-headers b/scripts/extract-vsssdk-headers new file mode 100755 index 0000000000..9e38510f04 --- /dev/null +++ b/scripts/extract-vsssdk-headers @@ -0,0 +1,35 @@ +#! /bin/bash + +# extract-vsssdk-headers +# Author: Paolo Bonzini <pbonzini@redhat.com> + +set -e +if test $# != 1 || ! test -f "$1"; then + echo 'Usage: extract-vsssdk-headers /path/to/setup.exe' >&2 + exit 1 +fi + +if ! command -v msiextract > /dev/null; then + echo 'msiextract not found. Please install msitools.' >&2 + exit 1 +fi + +if test -e inc; then + echo '"inc" already exists.' >&2 + exit 1 +fi + +# Extract .MSI file in the .exe, looking for the OLE compound +# document signature. Extra data at the end does not matter. +export LC_ALL=C +MAGIC=$'\xd0\xcf\x11\xe0\xa1\xb1\x1a\xe1' +offset=$(grep -abom1 "$MAGIC" "$1" | sed -n 's/:/\n/; P') +tmpdir=$(mktemp -d) +trap 'rm -fr -- "$tmpdir" vsssdk.msi' EXIT HUP INT QUIT ALRM TERM +tail -c +$(($offset+1)) -- "$1" > vsssdk.msi + +# Now extract the files. +msiextract -C $tmpdir vsssdk.msi +mv "$tmpdir/Program Files/Microsoft/VSSSDK72/inc" inc +echo 'Extracted SDK headers into "inc" directory.' +exit 0 diff --git a/scripts/qapi.py b/scripts/qapi.py index 1069310f8d..750e9fb552 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -236,9 +236,19 @@ def c_var(name, protect=True): # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html # excluding _.* gcc_words = set(['asm', 'typeof']) + # C++ ISO/IEC 14882:2003 2.11 + cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete', + 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable', + 'namespace', 'new', 'operator', 'private', 'protected', + 'public', 'reinterpret_cast', 'static_cast', 'template', + 'this', 'throw', 'true', 'try', 'typeid', 'typename', + 'using', 'virtual', 'wchar_t', + # alternative representations + 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not', + 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq']) # namespace pollution: polluted_words = set(['unix']) - if protect and (name in c89_words | c99_words | c11_words | gcc_words | polluted_words): + if protect and (name in c89_words | c99_words | c11_words | gcc_words | cpp_words | polluted_words): return "q_" + name return name.replace('-', '_').lstrip("*") diff --git a/util/error.c b/util/error.c index 53b04354ae..ec0faa6176 100644 --- a/util/error.c +++ b/util/error.c @@ -76,6 +76,41 @@ void error_setg_file_open(Error **errp, int os_errno, const char *filename) error_setg_errno(errp, os_errno, "Could not open '%s'", filename); } +#ifdef _WIN32 + +void error_set_win32(Error **errp, int win32_err, ErrorClass err_class, + const char *fmt, ...) +{ + Error *err; + char *msg1; + va_list ap; + + if (errp == NULL) { + return; + } + assert(*errp == NULL); + + err = g_malloc0(sizeof(*err)); + + va_start(ap, fmt); + msg1 = g_strdup_vprintf(fmt, ap); + if (win32_err != 0) { + char *msg2 = g_win32_error_message(win32_err); + err->msg = g_strdup_printf("%s: %s (error: %x)", msg1, msg2, + (unsigned)win32_err); + g_free(msg2); + g_free(msg1); + } else { + err->msg = msg1; + } + va_end(ap); + err->err_class = err_class; + + *errp = err; +} + +#endif + Error *error_copy(const Error *err) { Error *err_new; |