summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Makefile3
-rw-r--r--Makefile.objs2
-rwxr-xr-xQMP/qemu-ga-client4
-rwxr-xr-xconfigure96
-rw-r--r--hmp.c2
-rw-r--r--hw/mips/mips_malta.c36
-rw-r--r--hw/pci/pci.c2
-rw-r--r--include/qapi/error.h13
-rw-r--r--qga/Makefile.objs3
-rw-r--r--qga/commands-win32.c82
-rw-r--r--qga/main.c10
-rw-r--r--qga/vss-win32.c166
-rw-r--r--qga/vss-win32.h27
-rw-r--r--qga/vss-win32/Makefile.objs23
-rw-r--r--qga/vss-win32/install.cpp458
-rw-r--r--qga/vss-win32/provider.cpp523
-rw-r--r--qga/vss-win32/qga-vss.def13
-rw-r--r--qga/vss-win32/qga-vss.idl20
-rw-r--r--qga/vss-win32/qga-vss.tlbbin0 -> 1528 bytes
-rw-r--r--qga/vss-win32/requester.cpp507
-rw-r--r--qga/vss-win32/requester.h42
-rw-r--r--qga/vss-win32/vss-common.h129
-rw-r--r--rules.mak9
-rwxr-xr-xscripts/checkpatch.pl34
-rwxr-xr-xscripts/extract-vsssdk-headers35
-rw-r--r--scripts/qapi.py12
-rw-r--r--util/error.c35
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
diff --git a/Makefile b/Makefile
index 806946e339..362fe3e66e 100644
--- a/Makefile
+++ b/Makefile
@@ -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)
diff --git a/configure b/configure
index e989609e95..cf61f75823 100755
--- a/configure
+++ b/configure
@@ -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
diff --git a/hmp.c b/hmp.c
index 2bd31d1c69..b4a6422e7a 100644
--- a/hmp.c
+++ b/hmp.c
@@ -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
new file mode 100644
index 0000000000..226452a186
--- /dev/null
+++ b/qga/vss-win32/qga-vss.tlb
Binary files differdiff --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
diff --git a/rules.mak b/rules.mak
index 44997458da..abc2e846b3 100644
--- a/rules.mak
+++ b/rules.mak
@@ -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;