All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH v4 00/10] qemu-ga: fsfreeze on Windows using VSS
@ 2013-06-06 15:06 Tomoki Sekiyama
  2013-06-06 15:06 ` [Qemu-devel] [PATCH v4 01/10] configure: Support configuring c++ compiler Tomoki Sekiyama
                   ` (11 more replies)
  0 siblings, 12 replies; 70+ messages in thread
From: Tomoki Sekiyama @ 2013-06-06 15:06 UTC (permalink / raw)
  To: qemu-devel
  Cc: libaiqing, stefanha, mdroth, lcapitulino, vrozenfe, pbonzini,
	seiji.aguchi, areis

Hi,

This patch series adds fsfreeze support for Windows qemu-guest-agent.

changes from v3:
 -[01/10] Use c++ instead of g++ in configureing C++ compiler if neither
          $cross_prefix nor $CXX is specified.
 -[02/10] Add "alternative representations" as a reserved keywords in qapi.py.
 -[03/10 (newly added)] modify check-patch.pl to check C++ source code
 -[04/10] Improve compatibility (POSIX-compliant).
 -[04/10] Added some error checks.
 -[05/10] Added --with-win-sdk option to configure path to Windows SDK.
 -[06/10] Patch v3 09/11 (adding binary .tlb file) is squashed into this patch
          in order to avoid git-bisect failure.
 -[06/10 & 07/10] Fix coding style.
 -Dropped Patch v3 11/11 (encode error desc of exception with current locale)

changes from v2:
 -[06/11] Fix errors in Windows 7, reported by Li Baiqing

changes from v1: 
 - Fix out-tree build by stop using recursive Makefile
 - Added script to extract VSS SDK headers on POSIX systems using msitools
   (thanks Paolo)
 - Remove some unnecessary header files

v3: http://lists.nongnu.org/archive/html/qemu-devel/2013-05/msg02771.html


* Description
  In Windows, VSS (Volume Shadow Copy Service) provides a facility to
  quiesce filesystems and applications before disk snapshots are taken.
  This patch series implements "fsfreeze" command of qemu-ga using VSS.


* How to build & run qemu-ga with VSS support

 - Download Microsoft VSS SDK from:
   http://www.microsoft.com/en-us/download/details.aspx?id=23490

 - Setup the SDK
   scripts/extract-vsssdk-headers setup.exe (on POSIX-systems)

 - Specify installed SDK directory to configure option as:
   ./configure -with-vss-sdk="path/to/VSS SDK" --cross-prefix=i686-w64-mingw32-

 - make qemu-ga.exe

 - Install qemu-ga.exe, qga/vss-win32-provider/qga-provider.{dll,tlb}, and
   the other required mingw libraries into the same directory in guests

 - Run `qemu-ga.exe -s install' and `net start qemu-ga' in the guests

Any feedback are appreciated.

---
Tomoki Sekiyama (10):
      configure: Support configuring c++ compiler
      Add c++ keywords to QAPI helper script
      checkpatch.pl: check .cpp files
      Add a script to extract VSS SDK headers on POSIX system
      qemu-ga: Add configure options to specify path to Windows/VSS SDK
      qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze
      qemu-ga: Add Windows VSS requester to quiesce applications and filesystems
      qemu-ga: call Windows VSS requester in fsfreeze command handler
      qemu-ga: install Windows VSS provider on `qemu-ga -s install'
      QMP/qemu-ga-client: make timeout longer for guest-fsfreeze-freeze command


 .gitignore                              |    1 
 Makefile                                |    3 
 Makefile.objs                           |    2 
 QMP/qemu-ga-client                      |    4 
 configure                               |   87 +++++
 hmp.c                                   |    2 
 hw/pci/pci.c                            |    2 
 qga/Makefile.objs                       |    7 
 qga/commands-win32.c                    |   74 ++++-
 qga/main.c                              |   41 +++
 qga/vss-win32-provider.h                |   26 ++
 qga/vss-win32-provider/Makefile.objs    |   24 ++
 qga/vss-win32-provider/install.cpp      |  493 +++++++++++++++++++++++++++++++
 qga/vss-win32-provider/provider.cpp     |  479 ++++++++++++++++++++++++++++++
 qga/vss-win32-provider/qga-provider.def |   10 +
 qga/vss-win32-provider/qga-provider.idl |   20 +
 qga/vss-win32-provider/qga-provider.tlb |  Bin
 qga/vss-win32-requester.cpp             |  419 ++++++++++++++++++++++++++
 qga/vss-win32-requester.h               |   31 ++
 qga/vss-win32.h                         |   86 +++++
 rules.mak                               |    9 +
 scripts/checkpatch.pl                   |   34 ++
 scripts/extract-vsssdk-headers          |   36 ++
 scripts/qapi.py                         |   12 +
 24 files changed, 1880 insertions(+), 22 deletions(-)
 create mode 100644 qga/vss-win32-provider.h
 create mode 100644 qga/vss-win32-provider/Makefile.objs
 create mode 100644 qga/vss-win32-provider/install.cpp
 create mode 100644 qga/vss-win32-provider/provider.cpp
 create mode 100644 qga/vss-win32-provider/qga-provider.def
 create mode 100644 qga/vss-win32-provider/qga-provider.idl
 create mode 100644 qga/vss-win32-provider/qga-provider.tlb
 create mode 100644 qga/vss-win32-requester.cpp
 create mode 100644 qga/vss-win32-requester.h
 create mode 100644 qga/vss-win32.h
 create mode 100755 scripts/extract-vsssdk-headers

^ permalink raw reply	[flat|nested] 70+ messages in thread

* [Qemu-devel] [PATCH v4 01/10] configure: Support configuring c++ compiler
  2013-06-06 15:06 [Qemu-devel] [PATCH v4 00/10] qemu-ga: fsfreeze on Windows using VSS Tomoki Sekiyama
@ 2013-06-06 15:06 ` Tomoki Sekiyama
  2013-06-18 10:17   ` Paolo Bonzini
  2013-06-25  8:15   ` Laszlo Ersek
  2013-06-06 15:06 ` [Qemu-devel] [PATCH v4 02/10] Add c++ keywords to QAPI helper script Tomoki Sekiyama
                   ` (10 subsequent siblings)
  11 siblings, 2 replies; 70+ messages in thread
From: Tomoki Sekiyama @ 2013-06-06 15:06 UTC (permalink / raw)
  To: qemu-devel
  Cc: libaiqing, stefanha, mdroth, lcapitulino, vrozenfe, pbonzini,
	seiji.aguchi, areis

Add configuration for c++ compiler (${cross_prefix}g++ as default) in
configure and Makefiles.

Currently, usage of c++ language is only for access to Windows VSS
using COM+ services in qemu-guest-agent for Windows.

Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
---
 configure |   13 +++++++++++++
 rules.mak |    9 ++++++++-
 2 files changed, 21 insertions(+), 1 deletion(-)

diff --git a/configure b/configure
index 1654413..a2fc3f3 100755
--- a/configure
+++ b/configure
@@ -251,6 +251,8 @@ for opt do
   ;;
   --cc=*) CC="$optarg"
   ;;
+  --cxx=*) CXX="$optarg"
+  ;;
   --source-path=*) source_path="$optarg"
   ;;
   --cpu=*) cpu="$optarg"
@@ -281,6 +283,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}"
@@ -614,6 +622,8 @@ for opt do
   ;;
   --host-cc=*) host_cc="$optarg"
   ;;
+  --cxx=*) cxx="$optarg"
+  ;;
   --objcc=*) objcc="$optarg"
   ;;
   --make=*) make="$optarg"
@@ -1015,6 +1025,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"
@@ -3459,6 +3470,7 @@ 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"
@@ -4036,6 +4048,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/rules.mak b/rules.mak
index 4499745..abc2e84 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

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [Qemu-devel] [PATCH v4 02/10] Add c++ keywords to QAPI helper script
  2013-06-06 15:06 [Qemu-devel] [PATCH v4 00/10] qemu-ga: fsfreeze on Windows using VSS Tomoki Sekiyama
  2013-06-06 15:06 ` [Qemu-devel] [PATCH v4 01/10] configure: Support configuring c++ compiler Tomoki Sekiyama
@ 2013-06-06 15:06 ` Tomoki Sekiyama
  2013-06-25  8:16   ` Laszlo Ersek
  2013-06-06 15:06 ` [Qemu-devel] [PATCH v4 03/10] checkpatch.pl: check .cpp files Tomoki Sekiyama
                   ` (9 subsequent siblings)
  11 siblings, 1 reply; 70+ messages in thread
From: Tomoki Sekiyama @ 2013-06-06 15:06 UTC (permalink / raw)
  To: qemu-devel
  Cc: libaiqing, stefanha, mdroth, lcapitulino, vrozenfe, pbonzini,
	seiji.aguchi, areis

Add c++ keywords to avoid errors in compiling with c++ compiler.
This also renames class member of PciDeviceInfo to q_class.

Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
---
 hmp.c           |    2 +-
 hw/pci/pci.c    |    2 +-
 scripts/qapi.py |   12 +++++++++++-
 3 files changed, 13 insertions(+), 3 deletions(-)

diff --git a/hmp.c b/hmp.c
index 4fb76ec..cb0ed3b 100644
--- a/hmp.c
+++ b/hmp.c
@@ -484,7 +484,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/pci/pci.c b/hw/pci/pci.c
index bb3879b..f4bef02 100644
--- a/hw/pci/pci.c
+++ b/hw/pci/pci.c
@@ -1460,7 +1460,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/scripts/qapi.py b/scripts/qapi.py
index 02ad668..dea6ada 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -162,9 +162,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("*")
 

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [Qemu-devel] [PATCH v4 03/10] checkpatch.pl: check .cpp files
  2013-06-06 15:06 [Qemu-devel] [PATCH v4 00/10] qemu-ga: fsfreeze on Windows using VSS Tomoki Sekiyama
  2013-06-06 15:06 ` [Qemu-devel] [PATCH v4 01/10] configure: Support configuring c++ compiler Tomoki Sekiyama
  2013-06-06 15:06 ` [Qemu-devel] [PATCH v4 02/10] Add c++ keywords to QAPI helper script Tomoki Sekiyama
@ 2013-06-06 15:06 ` Tomoki Sekiyama
  2013-06-25  8:17   ` Laszlo Ersek
  2013-06-06 15:06 ` [Qemu-devel] [PATCH v4 04/10] Add a script to extract VSS SDK headers on POSIX system Tomoki Sekiyama
                   ` (8 subsequent siblings)
  11 siblings, 1 reply; 70+ messages in thread
From: Tomoki Sekiyama @ 2013-06-06 15:06 UTC (permalink / raw)
  To: qemu-devel
  Cc: libaiqing, stefanha, mdroth, lcapitulino, vrozenfe, pbonzini,
	seiji.aguchi, areis

Enable checkpatch.pl to apply the same checks as C source files for
C++ files with .cpp extensions. It also adds some exceptions for C++
sources to suppress errors for:
  - <> used in C++ template arguments (e.g. template <class T>)
  - :: used to represent namespaces   (e.g. SomeClass::method())
  - : used in class declaration       (e.g. class T : public Super)
  - ~ used in destructor method name  (e.g. T::~T())

Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
---
 scripts/checkpatch.pl |   34 ++++++++++++++++++++++++++--------
 1 file changed, 26 insertions(+), 8 deletions(-)

diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
index ec0aa4c..53a12a8 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)(?:\$|)/) {
@@ -1992,7 +1992,7 @@ sub process {
 				\+=|-=|\*=|\/=|%=|\^=|\|=|&=|
 				=>|->|<<|>>|<|>|=|!|~|
 				&&|\|\||,|\^|\+\+|--|&|\||\+|-|\*|\/|%|
-				\?|:
+				\?|::|:
 			}x;
 			my @elements = split(/($ops|;)/, $opline);
 			my $off = 0;
@@ -2066,7 +2066,8 @@ sub process {
 				#   ->
 				#   :   when part of a bitfield
 				} elsif ($op eq '->' || $opv eq ':B') {
-					if ($ctx =~ /Wx.|.xW/) {
+					if ($ctx =~ /Wx.|.xW/ &&
+						!($opv eq ':B' && $line =~ /class/)) {
 						ERROR("spaces prohibited around that '$op' $at\n" . $hereptr);
 					}
 
@@ -2088,7 +2089,11 @@ 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/) {
@@ -2126,8 +2131,9 @@ sub process {
 
 				# A colon needs no spaces before when it is
 				# terminating a case value or a label.
+				# Ignored if it is used in class declaration in C++.
 				} elsif ($opv eq ':C' || $opv eq ':L') {
-					if ($ctx =~ /Wx./) {
+					if ($ctx =~ /Wx./ && $line !~ /class/) {
 						ERROR("space prohibited before that '$op' $at\n" . $hereptr);
 					}
 
@@ -2135,6 +2141,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+>/) ||

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [Qemu-devel] [PATCH v4 04/10] Add a script to extract VSS SDK headers on POSIX system
  2013-06-06 15:06 [Qemu-devel] [PATCH v4 00/10] qemu-ga: fsfreeze on Windows using VSS Tomoki Sekiyama
                   ` (2 preceding siblings ...)
  2013-06-06 15:06 ` [Qemu-devel] [PATCH v4 03/10] checkpatch.pl: check .cpp files Tomoki Sekiyama
@ 2013-06-06 15:06 ` Tomoki Sekiyama
  2013-06-25  8:30   ` Laszlo Ersek
  2013-06-25 15:01   ` Laszlo Ersek
  2013-06-06 15:06 ` [Qemu-devel] [PATCH v4 05/10] qemu-ga: Add configure options to specify path to Windows/VSS SDK Tomoki Sekiyama
                   ` (7 subsequent siblings)
  11 siblings, 2 replies; 70+ messages in thread
From: Tomoki Sekiyama @ 2013-06-06 15:06 UTC (permalink / raw)
  To: qemu-devel
  Cc: libaiqing, stefanha, mdroth, lcapitulino, vrozenfe,
	Paolo Bonzini, seiji.aguchi, areis

VSS SDK(*) setup.exe is only runnable on Windows. This adds a script
to extract VSS SDK headers on POSIX-systems using msitools.

  * http://www.microsoft.com/en-us/download/details.aspx?id=23490

From: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
---
 scripts/extract-vsssdk-headers |   36 ++++++++++++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)
 create mode 100755 scripts/extract-vsssdk-headers

diff --git a/scripts/extract-vsssdk-headers b/scripts/extract-vsssdk-headers
new file mode 100755
index 0000000..95c0b38
--- /dev/null
+++ b/scripts/extract-vsssdk-headers
@@ -0,0 +1,36 @@
+#! /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 1" 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
+rm -rf $tmpdir vsssdk.msi
+echo 'Extracted SDK headers into "inc" directory.'
+exit 0

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [Qemu-devel] [PATCH v4 05/10] qemu-ga: Add configure options to specify path to Windows/VSS SDK
  2013-06-06 15:06 [Qemu-devel] [PATCH v4 00/10] qemu-ga: fsfreeze on Windows using VSS Tomoki Sekiyama
                   ` (3 preceding siblings ...)
  2013-06-06 15:06 ` [Qemu-devel] [PATCH v4 04/10] Add a script to extract VSS SDK headers on POSIX system Tomoki Sekiyama
@ 2013-06-06 15:06 ` Tomoki Sekiyama
  2013-06-25 11:16   ` Laszlo Ersek
  2013-06-06 15:06 ` [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze Tomoki Sekiyama
                   ` (6 subsequent siblings)
  11 siblings, 1 reply; 70+ messages in thread
From: Tomoki Sekiyama @ 2013-06-06 15:06 UTC (permalink / raw)
  To: qemu-devel
  Cc: libaiqing, stefanha, mdroth, lcapitulino, vrozenfe, pbonzini,
	seiji.aguchi, areis

To enable VSS support in qemu-ga for Windows, header files included in
VSS SDK are required.
The VSS support is enabled by the configure option like below:
  ./configure --with-vss-sdk="/path/to/VSS SDK"

If the path is omitted, it tries to search the headers from default paths
and VSS support is enabled only if the SDK is found.
VSS support is disabled if --without-vss-sdk or --with-vss-sdk=no is
specified.

VSS SDK is available from:
  http://www.microsoft.com/en-us/download/details.aspx?id=23490

To cross-compile using mingw, you need to setup the SDK on Windows
environments to extract headers. You can also extract the SDK headers on
POSIX environments using scripts/extract-vss-headers and msitools.

In addition, --with-win-sdk="/path/to/Windows SDK" option is also added to
specify path to Windows SDK, which may be used for native-compile of .tlb
file of qemu-ga VSS provider. However, this is usually unnecessary because
pre-compiled .tlb file is included.

Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
---
 .gitignore |    1 +
 Makefile   |    1 +
 configure  |   69 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 71 insertions(+)

diff --git a/.gitignore b/.gitignore
index 64e9466..67facd3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -79,6 +79,7 @@ fsdev/virtfs-proxy-helper.pod
 *.la
 *.pc
 .libs
+.sdk
 *.swp
 *.orig
 .pc
diff --git a/Makefile b/Makefile
index a96736b..4851ba0 100644
--- a/Makefile
+++ b/Makefile
@@ -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/configure b/configure
index a2fc3f3..cafe830 100755
--- a/configure
+++ b/configure
@@ -232,6 +232,8 @@ usb_redir=""
 glx=""
 zlib="yes"
 guest_agent="yes"
+guest_agent_with_vss="no"
+vss_win32_sdk=""
 want_tools="yes"
 libiscsi=""
 coroutine=""
@@ -919,6 +921,14 @@ 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="$optarg"
+  ;;
   --enable-tools) want_tools="yes"
   ;;
   --disable-tools) want_tools="no"
@@ -1151,6 +1161,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:"
@@ -3066,6 +3078,57 @@ if test "$usb_redir" != "no" ; then
 fi
 
 ##########################################
+# check if we have VSS SDK headers for win
+
+if test "$mingw32" = "yes" -a "$guest_agent" = "yes" -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" = "yes" -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
+    fi
+  fi
+fi
+
+##########################################
 
 ##########################################
 # check if we have fdatasync
@@ -3466,6 +3529,7 @@ 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"
@@ -3552,6 +3616,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"
@@ -3627,6 +3692,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

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze
  2013-06-06 15:06 [Qemu-devel] [PATCH v4 00/10] qemu-ga: fsfreeze on Windows using VSS Tomoki Sekiyama
                   ` (4 preceding siblings ...)
  2013-06-06 15:06 ` [Qemu-devel] [PATCH v4 05/10] qemu-ga: Add configure options to specify path to Windows/VSS SDK Tomoki Sekiyama
@ 2013-06-06 15:06 ` Tomoki Sekiyama
  2013-06-25 16:03   ` Laszlo Ersek
  2013-06-25 21:15   ` Paolo Bonzini
  2013-06-06 15:06 ` [Qemu-devel] [PATCH v4 07/10] qemu-ga: Add Windows VSS requester to quiesce applications and filesystems Tomoki Sekiyama
                   ` (5 subsequent siblings)
  11 siblings, 2 replies; 70+ messages in thread
From: Tomoki Sekiyama @ 2013-06-06 15:06 UTC (permalink / raw)
  To: qemu-devel
  Cc: libaiqing, stefanha, mdroth, lcapitulino, vrozenfe, pbonzini,
	seiji.aguchi, areis

Implements a basic stub of software VSS provider. Currently, this module
only provides a relay function of events between qemu-guest-agent and
Windows VSS when VSS finished filesystem freeze and when qemu snapshot
is done.

In the future, this module could be extended to support the other VSS
functions, such as query for snapshot volumes and recovery.

To build type library (.tlb) for qga-provider.dll from COM IDL (.idl),
VisualC++, MIDL and stdole2.tlb in Windows SDK are required.

This patch also adds pre-compiled .tlb file in the repository in order to
enable cross-compile qemu-ga.exe for Windows with VSS support.

Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
---
 Makefile                                |    2 
 Makefile.objs                           |    2 
 configure                               |    5 
 qga/Makefile.objs                       |    6 
 qga/vss-win32-provider.h                |   26 ++
 qga/vss-win32-provider/Makefile.objs    |   24 ++
 qga/vss-win32-provider/install.cpp      |  493 +++++++++++++++++++++++++++++++
 qga/vss-win32-provider/provider.cpp     |  479 ++++++++++++++++++++++++++++++
 qga/vss-win32-provider/qga-provider.def |   10 +
 qga/vss-win32-provider/qga-provider.idl |   20 +
 qga/vss-win32-provider/qga-provider.tlb |  Bin
 qga/vss-win32.h                         |   86 +++++
 12 files changed, 1151 insertions(+), 2 deletions(-)
 create mode 100644 qga/vss-win32-provider.h
 create mode 100644 qga/vss-win32-provider/Makefile.objs
 create mode 100644 qga/vss-win32-provider/install.cpp
 create mode 100644 qga/vss-win32-provider/provider.cpp
 create mode 100644 qga/vss-win32-provider/qga-provider.def
 create mode 100644 qga/vss-win32-provider/qga-provider.idl
 create mode 100644 qga/vss-win32-provider/qga-provider.tlb
 create mode 100644 qga/vss-win32.h

diff --git a/Makefile b/Makefile
index 4851ba0..636358d 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
diff --git a/Makefile.objs b/Makefile.objs
index 286ce06..b6c1505 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -102,6 +102,7 @@ common-obj-y += disas/
 # 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-prv-obj-y = qga/
 
 vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS)
 
@@ -113,6 +114,7 @@ nested-vars += \
 	stub-obj-y \
 	util-obj-y \
 	qga-obj-y \
+	qga-prv-obj-y \
 	block-obj-y \
 	common-obj-y
 dummy := $(call unnest-vars)
diff --git a/configure b/configure
index cafe830..8e45fad 100755
--- a/configure
+++ b/configure
@@ -3490,9 +3490,12 @@ if test "$softmmu" = yes ; then
       virtfs=no
     fi
   fi
-  if [ "$linux" = "yes" -o "$bsd" = "yes" -o "$solaris" = "yes" ] ; then
+  if [ "$linux" = "yes" -o "$bsd" = "yes" -o "$solaris" = "yes" -o "$mingw32" = "yes" ] ; then
     if [ "$guest_agent" = "yes" ]; then
       tools="qemu-ga\$(EXESUF) $tools"
+      if [ "$mingw32" = "yes" ]; then
+        tools="qga/vss-win32-provider/qga-provider.dll qga/vss-win32-provider/qga-provider.tlb $tools"
+      fi
     fi
   fi
 fi
diff --git a/qga/Makefile.objs b/qga/Makefile.objs
index b8d7cd0..8d93866 100644
--- a/qga/Makefile.objs
+++ b/qga/Makefile.objs
@@ -3,3 +3,9 @@ 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-y += qapi-generated/qga-qapi-types.o qapi-generated/qga-qapi-visit.o
 qga-obj-y += qapi-generated/qga-qmp-marshal.o
+
+ifeq ($(CONFIG_QGA_VSS),y)
+QEMU_CFLAGS += -DHAS_VSS_SDK
+qga-obj-y += vss-win32-provider/
+qga-prv-obj-y += vss-win32-provider/
+endif
diff --git a/qga/vss-win32-provider.h b/qga/vss-win32-provider.h
new file mode 100644
index 0000000..a437e71
--- /dev/null
+++ b/qga/vss-win32-provider.h
@@ -0,0 +1,26 @@
+/*
+ * QEMU Guest Agent win32 VSS provider 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_PROVIDER_H
+#define VSS_WIN32_PROVIDER_H
+
+#include <windows.h>
+
+STDAPI VSSCheckOSVersion(void);
+
+STDAPI COMRegister(void);
+STDAPI COMUnregister(void);
+
+STDAPI DllRegisterServer(void);
+STDAPI DllUnregisterServer(void);
+
+#endif
diff --git a/qga/vss-win32-provider/Makefile.objs b/qga/vss-win32-provider/Makefile.objs
new file mode 100644
index 0000000..1fe1f8f
--- /dev/null
+++ b/qga/vss-win32-provider/Makefile.objs
@@ -0,0 +1,24 @@
+# rules to build qga-provider.dll
+
+qga-obj-y += qga-provider.dll
+qga-prv-obj-y += provider.o install.o
+
+obj-qga-prv-obj-y = $(addprefix $(obj)/, $(qga-prv-obj-y))
+$(obj-qga-prv-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-provider.dll: LDFLAGS = -shared -Wl,--add-stdcall-alias,--enable-stdcall-fixup -lole32 -loleaut32 -lshlwapi -luuid -static
+$(obj)/qga-provider.dll: $(obj-qga-prv-obj-y) $(SRC_PATH)/$(obj)/qga-provider.def $(obj)/qga-provider.tlb
+	$(call quiet-command,$(CXX) -o $@ $(qga-prv-obj-y) $(SRC_PATH)/qga/vss-win32-provider/qga-provider.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-provider.tlb: $(SRC_PATH)/$(obj)/qga-provider.idl
+ifeq ($(wildcard $(SRC_PATH)/$(obj)/qga-provider.tlb),)
+	$(call quiet-command,$(MIDL) -tlb $@ -I $(WIN_SDK)/Include $<,"  MIDL  $(TARGET_DIR)$@")
+else
+	$(call quiet-command,cp $(dir $<)qga-provider.tlb $@, "  COPY  $(TARGET_DIR)$@")
+endif
diff --git a/qga/vss-win32-provider/install.cpp b/qga/vss-win32-provider/install.cpp
new file mode 100644
index 0000000..d6f1d55
--- /dev/null
+++ b/qga/vss-win32-provider/install.cpp
@@ -0,0 +1,493 @@
+/*
+ * 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-win32.h"
+#include "inc/win2003/vscoordint.h"
+#include "../vss-win32-provider.h"
+
+#include <comadmin.h>
+#include <wbemidl.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} };
+
+static void errmsg(DWORD err, const char *text)
+{
+    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);
+    printf("%.*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\n", 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)
+
+template<class T>
+HRESULT put_Value(ICatalogObject *pObj, LPCWSTR name, T val)
+{
+    return pObj->put_Value(_bstr_t(name), _variant_t(val));
+}
+
+/* Check whether this OS version supports VSS providers */
+STDAPI VSSCheckOSVersion(void)
+{
+    OSVERSIONINFO OSver;
+
+    OSver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+    GetVersionEx(&OSver);
+    if ((OSver.dwMajorVersion == 5 && OSver.dwMinorVersion >= 2) ||
+       OSver.dwMajorVersion > 5) {
+        return S_OK;
+    }
+    return S_FALSE;
+}
+
+/* Lookup Administrators group name from winmgmt */
+static HRESULT GetAdminName(_bstr_t &name)
+{
+    HRESULT hr;
+    IWbemLocator *pLoc = NULL;
+    IWbemServices *pSvc = NULL;
+    IEnumWbemClassObject *pEnum = NULL;
+    IWbemClassObject *pWobj = NULL;
+    ULONG returned;
+    _variant_t var;
+
+    chk(CoCreateInstance(CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER,
+                         IID_IWbemLocator, (LPVOID *)&pLoc));
+    chk(pLoc->ConnectServer(_bstr_t(L"ROOT\\CIMV2"), NULL, NULL, NULL,
+                            0, 0, 0, &pSvc));
+    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));
+    if (!pEnum) {
+        errmsg(E_FAIL, "Failed to query for Administrators");
+        goto out;
+    }
+    chk(pEnum->Next(WBEM_INFINITE, 1, &pWobj, &returned));
+    if (returned == 0) {
+        errmsg(E_FAIL, "Failed to query for Administrators");
+        goto out;
+    }
+
+    chk(pWobj->Get(_bstr_t(L"Name"), 0, &var, 0, 0));
+    name = var;
+out:
+    if (pLoc) {
+        pLoc->Release();
+    }
+    if (pSvc) {
+        pSvc->Release();
+    }
+    if (pEnum) {
+        pEnum->Release();
+    }
+    if (pWobj) {
+        pWobj->Release();
+    }
+    return hr;
+}
+
+/* Register this module to COM+ Applications Catalog */
+STDAPI COMRegister(void)
+{
+    HRESULT hr = E_FAIL;
+    IUnknown *pUnknown = NULL;
+    ICOMAdminCatalog *pCatalog = NULL;
+    ICatalogCollection *pApps = NULL, *pRoles = NULL, *pUsersInRole = NULL;
+    ICatalogObject *pObj = NULL;
+    long n;
+    _bstr_t name;
+    _variant_t key;
+    CHAR dllPath[MAX_PATH], tlbPath[MAX_PATH];
+
+    if (!g_hinstDll) {
+        errmsg(E_FAIL, "Failed to initialize DLL");
+        goto out;
+    }
+
+    if (VSSCheckOSVersion() == S_FALSE) {
+        printf("VSS provider is not supported in this OS version.\n");
+        return S_FALSE; /* VSS feature is disabled */
+    }
+
+    COMUnregister();
+
+    chk(CoInitialize(NULL));
+    chk(CoCreateInstance(CLSID_COMAdminCatalog, NULL, CLSCTX_INPROC_SERVER,
+                         IID_IUnknown, (void **)&pUnknown));
+    chk(pUnknown->QueryInterface(IID_ICOMAdminCatalog, (void **)&pCatalog));
+
+    /* Install COM+ Component */
+
+    chk(pCatalog->GetCollection(_bstr_t(L"Applications"),
+                                (IDispatch **)&pApps));
+    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));
+    chk(pObj->get_Key(&key));
+
+    pObj->Release();
+    pObj = NULL;
+
+    if (!GetModuleFileName(g_hinstDll, dllPath, sizeof(dllPath))) {
+        errmsg(GetLastError(), "GetModuleFileName failed");
+        goto out;
+    }
+    n = strlen(dllPath);
+    if (n < 3) {
+        errmsg(E_FAIL, "Failed to lookup dll");
+    }
+    strcpy(tlbPath, dllPath);
+    strcpy(tlbPath+n-3, "TLB");
+    printf("Registering " QGA_PROVIDER_NAME ":\n");
+    printf("  %s\n", dllPath);
+    printf("  %s\n", tlbPath);
+    if (!PathFileExists(tlbPath)) {
+        errmsg(ERROR_FILE_NOT_FOUND, "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));
+    chk(pRoles->Populate());
+    chk(pRoles->Add((IDispatch **)&pObj));
+    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));
+
+    pObj->Release();
+    pObj = NULL;
+
+    /* Setup users in the role */
+
+    chk(pRoles->GetCollection(_bstr_t(L"UsersInRole"), key,
+                              (IDispatch **)&pUsersInRole));
+    chk(pUsersInRole->Populate());
+
+    chk(pUsersInRole->Add((IDispatch **)&pObj));
+    chk(GetAdminName(name));
+    chk(put_Value(pObj, L"User", _bstr_t(".\\") + name));
+
+    pObj->Release();
+    pObj = NULL;
+
+    chk(pUsersInRole->Add((IDispatch **)&pObj));
+    chk(put_Value(pObj, L"User", L"SYSTEM"));
+    chk(pUsersInRole->SaveChanges(&n));
+
+out:
+    if (pUnknown) {
+        pUnknown->Release();
+    }
+    if (pCatalog) {
+        pCatalog->Release();
+    }
+    if (pApps) {
+        pApps->Release();
+    }
+    if (pRoles) {
+        pRoles->Release();
+    }
+    if (pUsersInRole) {
+        pUsersInRole->Release();
+    }
+    if (pObj) {
+        pObj->Release();
+    }
+
+    if (FAILED(hr)) {
+        COMUnregister();
+    }
+    CoUninitialize();
+
+    return hr;
+}
+
+/* Unregister this module from COM+ Applications Catalog */
+STDAPI COMUnregister(void)
+{
+    HRESULT hr;
+    IUnknown *pUnknown = NULL;
+    ICOMAdminCatalog *pCatalog = NULL;
+    ICatalogCollection *pColl = NULL;
+    ICatalogObject *pObj = NULL;
+    _variant_t var;
+    long i, n;
+
+    if (VSSCheckOSVersion() == S_FALSE) {
+        printf("VSS provider is not supported in this OS version.\n");
+        return S_FALSE; /* VSS feature is disabled */
+    }
+
+    chk(DllUnregisterServer());
+
+    chk(CoInitialize(NULL));
+    chk(CoCreateInstance(CLSID_COMAdminCatalog, NULL, CLSCTX_INPROC_SERVER,
+                         IID_IUnknown, (void **)&pUnknown));
+    chk(pUnknown->QueryInterface(IID_ICOMAdminCatalog, (void **)&pCatalog));
+
+    chk(pCatalog->GetCollection(_bstr_t(L"Applications"),
+                                (IDispatch **)&pColl));
+    chk(pColl->Populate());
+
+    chk(pColl->get_Count(&n));
+    for (i = n - 1; i >= 0; i--) {
+        chk(pColl->get_Item(i, (IDispatch **)&pObj));
+        chk(pObj->get_Value(_bstr_t(L"Name"), &var));
+        if (var == _variant_t(QGA_PROVIDER_LNAME)) {
+            printf("Removing COM+ Application: %S\n", (wchar_t *)_bstr_t(var));
+            chk(pColl->Remove(i));
+        }
+    }
+    chk(pColl->SaveChanges(&n));
+
+out:
+    if (pUnknown) {
+        pUnknown->Release();
+    }
+    if (pCatalog) {
+        pCatalog->Release();
+    }
+    if (pColl) {
+        pColl->Release();
+    }
+    if (pObj) {
+        pObj->Release();
+    }
+    CoUninitialize();
+
+    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 = (lstrlen(data) + 1) * sizeof(TCHAR);
+    } else {
+        size = 0;
+    }
+
+    ret = RegSetValueEx(hKey, value, 0, REG_SZ, (LPBYTE)data, size);
+    RegCloseKey(hKey);
+
+out:
+    if (ret != ERROR_SUCCESS) {
+        /* We cannot printf here, and need MessageBox to report an error. */
+        errmsg_dialog(ret, "Cannot add registry ", key);
+        return FALSE;
+    }
+    return TRUE;
+}
+
+/* Register this dll as a VSS provider */
+STDAPI DllRegisterServer(void)
+{
+    IVssAdmin *pVssAdmin = NULL;
+    HRESULT hr = E_FAIL;
+    char dllPath[MAX_PATH];
+    char key[256];
+
+    CoInitialize(NULL);
+
+    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;
+    }
+
+    sprintf(key, "CLSID\\%s\\InprocServer32", g_szClsid);
+    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);
+    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");
+        goto out;
+    }
+
+out:
+    if (pVssAdmin) {
+        pVssAdmin->Release();
+    }
+    CoUninitialize();
+
+    if (FAILED(hr)) {
+        DllUnregisterServer();
+    }
+
+    return hr;
+}
+
+/* Unregister this VSS hardware provider from the system */
+STDAPI DllUnregisterServer(void)
+{
+    TCHAR key[256];
+    IVssAdmin *pVssAdmin = NULL;
+
+    CoInitialize(NULL);
+
+    HRESULT hr = CoCreateInstance(CLSID_VSSCoordinator,
+         NULL, CLSCTX_ALL, IID_IVssAdmin, (void **)&pVssAdmin);
+    if (SUCCEEDED(hr)) {
+        hr = pVssAdmin->UnregisterProvider(g_gProviderId);
+        pVssAdmin->Release();
+    } else {
+        errmsg_dialog(hr, "CoCreateInstance(VSSCoordinator) failed");
+    }
+
+    sprintf(key, "CLSID\\%s", g_szClsid);
+    SHDeleteKey(HKEY_CLASSES_ROOT, key);
+    SHDeleteKey(HKEY_CLASSES_ROOT, g_szProgid);
+
+    CoUninitialize();
+
+    return S_OK; /* Uninstall should never fail */
+}
+
+
+/* Support functions for _bstr_t in MinGW: Originally written by: Diaa Sami */
+
+void __stdcall _com_issue_error(HRESULT hr)
+{
+    printf("_com_issue_error() called with parameter HRESULT = %lu", hr);
+}
+
+namespace _com_util
+{
+    char * __stdcall ConvertBSTRToString(BSTR bstr)
+    {
+        const unsigned int stringLength = lstrlenW(bstr);
+        char *const ascii = new char [stringLength + 1];
+
+        wcstombs(ascii, bstr, stringLength + 1);
+
+        return ascii;
+    }
+
+    BSTR __stdcall ConvertStringToBSTR(const char *const ascii)
+    {
+        const unsigned int stringLength = lstrlenA(ascii);
+        BSTR bstr = SysAllocStringLen(NULL, stringLength);
+
+        mbstowcs(bstr, ascii, stringLength + 1);
+
+        return bstr;
+    }
+}
diff --git a/qga/vss-win32-provider/provider.cpp b/qga/vss-win32-provider/provider.cpp
new file mode 100644
index 0000000..90a3d25
--- /dev/null
+++ b/qga/vss-win32-provider/provider.cpp
@@ -0,0 +1,479 @@
+/*
+ * 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-win32.h"
+#include "inc/win2003/vscoordint.h"
+#include "inc/win2003/vsprov.h"
+
+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 block)
+{
+    if (block) {
+        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;
+    } else if (riid == IID_IVssSoftwareSnapshotProvider) {
+        *ppObj = static_cast<void*>(
+            static_cast<IVssSoftwareSnapshotProvider*>(this));
+        AddRef();
+        return S_OK;
+    } else if (riid == IID_IVssProviderCreateSnapshotSet) {
+        *ppObj = static_cast<void*>(
+            static_cast<IVssProviderCreateSnapshotSet*>(this));
+        AddRef();
+        return S_OK;
+    } else 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)
+{
+    *ppEnum = new CQGAVSSEnumObject;
+    (*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)
+{
+    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 S_OK;
+}
+
+
+/*
+ * 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 hEvent, hEvent2;
+
+    hEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_FROZEN);
+    if (hEvent == INVALID_HANDLE_VALUE) {
+        hr = E_FAIL;
+        goto out;
+    }
+
+    hEvent2 = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_THAW);
+    if (hEvent == INVALID_HANDLE_VALUE) {
+        CloseHandle(hEvent);
+        hr = E_FAIL;
+        goto out;
+    }
+
+    SetEvent(hEvent);
+    if (WaitForSingleObject(hEvent2, 60*1000) != WAIT_OBJECT_0) {
+        hr = E_ABORT;
+    }
+
+    CloseHandle(hEvent2);
+    CloseHandle(hEvent);
+out:
+    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 bLock) { return E_NOTIMPL; }
+private:
+    long m_nRefCount;
+};
+
+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()
+{
+    LockModule(TRUE);
+    return 2;
+}
+
+STDMETHODIMP_(ULONG) CQGAVssProviderFactory::Release()
+{
+    LockModule(FALSE);
+    return 1;
+}
+
+STDMETHODIMP CQGAVssProviderFactory::CreateInstance(
+    IUnknown *pUnknownOuter, REFIID iid, void **ppv)
+{
+    if (pUnknownOuter) {
+        return CLASS_E_NOAGGREGATION;
+    }
+    CQGAVssProvider *pObj = new CQGAVssProvider;
+    if (!pObj) {
+        return E_OUTOFMEMORY;
+    }
+    return pObj->QueryInterface(iid, ppv);
+}
+
+
+/*
+ * DLL functions
+ */
+
+STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
+{
+    static CQGAVssProviderFactory factory;
+
+    *ppv = NULL;
+    if (IsEqualCLSID(rclsid, CLSID_QGAVSSProvider)) {
+        return factory.QueryInterface(riid, ppv);
+    }
+    return CLASS_E_CLASSNOTAVAILABLE;
+}
+
+STDAPI DllCanUnloadNow()
+{
+    return g_nComObjsInUse == 0 ? S_OK : S_FALSE;
+}
+
+EXTERN_C
+BOOL WINAPI DllMain(HINSTANCE hinstDll, DWORD dwReason, LPVOID lpReserved)
+{
+    switch (dwReason) {
+
+    case DLL_PROCESS_ATTACH:
+        g_hinstDll = hinstDll;
+        DisableThreadLibraryCalls(hinstDll);
+        break;
+    }
+
+    return TRUE;
+}
diff --git a/qga/vss-win32-provider/qga-provider.def b/qga/vss-win32-provider/qga-provider.def
new file mode 100644
index 0000000..9f3afc8
--- /dev/null
+++ b/qga/vss-win32-provider/qga-provider.def
@@ -0,0 +1,10 @@
+LIBRARY      "QGA-PROVIDER.DLL"
+
+EXPORTS
+	VSSCheckOSVersion	PRIVATE
+	COMRegister		PRIVATE
+	COMUnregister		PRIVATE
+	DllCanUnloadNow		PRIVATE
+	DllGetClassObject	PRIVATE
+	DllRegisterServer	PRIVATE
+	DllUnregisterServer	PRIVATE
diff --git a/qga/vss-win32-provider/qga-provider.idl b/qga/vss-win32-provider/qga-provider.idl
new file mode 100644
index 0000000..17abca0
--- /dev/null
+++ b/qga/vss-win32-provider/qga-provider.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-provider/qga-provider.tlb b/qga/vss-win32-provider/qga-provider.tlb
new file mode 100644
index 0000000000000000000000000000000000000000..226452a1861371ffe0cad1019cf90fdfdcd5ef49
GIT binary patch
literal 1528
zcmeYbb_-!*U}OLRP8Kl5;0UB3A_y8H!@$4<WGF*9|A9aP$W{R21|SCUVfs9Pj1;IC
zKahR`)X0Ox{{ZC6An~sN`2tA%H9-9hNPHcj{0byK4>OPh6a(1_GM|T)fx!kz-UG<D
zK;nbc0l9GX===tt`Vb`fD?q*q5+7YXI$u?Zf#C;G4-9~uhYKVCC4f!`hZ{(Z0*HVD
zkhwswGqAt}ki;hd*$F@lQbP%-z+r|5P#hGW*vvKniaRx03p~wP?y>h_rLW<nKOg@=
z6{hYgzgH7@QE<^MU>KC!yoBjb#vz`9Lwu4+R-SJ!kIOX4xLBUUGN9-NyTyP76j}@n
z2f!qQ8-xepfXD+7rW+{SKz4&@7#qX~^1#sn3O|tFK>%ciE<<riN`6kNkzPqoQh0bc
zNbM*XJ|Un0jALSb15^rEE6h+Y8tCpA798vm9#E8DmYI@T<dc~c4pSpwQKEn@FU<fE
zfvHyrsVqoU0O~4AEUE;iEfI8i=bXgi;_z?|20Ng!&PAz-C8;S2NtFt|o-RHLWvNBQ
znfZAN=6VJOdIqMZrV5EA3T{Q23NES13Py$shQ?OLW>&_Q3PuKoMqI)S5zj9Ngog_=
gXfrXehlhjmFbIJB4$8MKU>*YlD1Z9^F{m5{03Vre%>V!Z

literal 0
HcmV?d00001

diff --git a/qga/vss-win32.h b/qga/vss-win32.h
new file mode 100644
index 0000000..21655cf
--- /dev/null
+++ b/qga/vss-win32.h
@@ -0,0 +1,86 @@
+/*
+ * 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"
+
+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
+};
+
+#endif

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [Qemu-devel] [PATCH v4 07/10] qemu-ga: Add Windows VSS requester to quiesce applications and filesystems
  2013-06-06 15:06 [Qemu-devel] [PATCH v4 00/10] qemu-ga: fsfreeze on Windows using VSS Tomoki Sekiyama
                   ` (5 preceding siblings ...)
  2013-06-06 15:06 ` [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze Tomoki Sekiyama
@ 2013-06-06 15:06 ` Tomoki Sekiyama
  2013-06-28 18:01   ` Laszlo Ersek
  2013-06-06 15:06 ` [Qemu-devel] [PATCH v4 08/10] qemu-ga: call Windows VSS requester in fsfreeze command handler Tomoki Sekiyama
                   ` (4 subsequent siblings)
  11 siblings, 1 reply; 70+ messages in thread
From: Tomoki Sekiyama @ 2013-06-06 15:06 UTC (permalink / raw)
  To: qemu-devel
  Cc: libaiqing, stefanha, mdroth, lcapitulino, vrozenfe, pbonzini,
	seiji.aguchi, areis

Add VSS requester functions for to qemu-ga.
This provides facility to request VSS service in Windows guest to quiesce
applications and filesystems. This function is only supported in Windows
2003 or later. In older guests, this function does nothing.

In several versions of Windows which don't support attribute
VSS_VOLSNAP_ATTR_NO_AUTORECOVERY, DoSnapshotSet fails with error
VSS_E_OBJECT_NOT_FOUND. In this patch, we just ignore this error.
To solve this fundamentally, we need a framework to handle mount writable
snapshot on guests, which is required by VSS auto-recovery feature
(a cleanup phase after snapshot is taken).

Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
---
 qga/Makefile.objs           |    3 
 qga/vss-win32-requester.cpp |  419 +++++++++++++++++++++++++++++++++++++++++++
 qga/vss-win32-requester.h   |   31 +++
 3 files changed, 452 insertions(+), 1 deletion(-)
 create mode 100644 qga/vss-win32-requester.cpp
 create mode 100644 qga/vss-win32-requester.h

diff --git a/qga/Makefile.objs b/qga/Makefile.objs
index 8d93866..f17a380 100644
--- a/qga/Makefile.objs
+++ b/qga/Makefile.objs
@@ -6,6 +6,7 @@ qga-obj-y += qapi-generated/qga-qmp-marshal.o
 
 ifeq ($(CONFIG_QGA_VSS),y)
 QEMU_CFLAGS += -DHAS_VSS_SDK
-qga-obj-y += vss-win32-provider/
+qga-obj-y += vss-win32-provider/ vss-win32-requester.o
 qga-prv-obj-y += vss-win32-provider/
+$(obj)/vss-win32-requester.o: QEMU_CXXFLAGS += -Wno-unknown-pragmas
 endif
diff --git a/qga/vss-win32-requester.cpp b/qga/vss-win32-requester.cpp
new file mode 100644
index 0000000..7784926
--- /dev/null
+++ b/qga/vss-win32-requester.cpp
@@ -0,0 +1,419 @@
+/*
+ * QEMU Guest Agent win32 VSS Requester implementations
+ *
+ * Copyright Hitachi Data Systems Corp. 2013
+ *
+ * Authors:
+ *  Tomoki Sekiyama   <tomoki.sekiyama@hds.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include <stdio.h>
+#include <assert.h>
+extern "C" {
+#include "guest-agent-core.h"
+}
+#include "vss-win32-requester.h"
+#include "vss-win32-provider.h"
+#include "vss-win32.h"
+#include "inc/win2003/vswriter.h"
+#include "inc/win2003/vsbackup.h"
+
+/* Functions in VSSAPI.DLL */
+typedef HRESULT(STDAPICALLTYPE * t_CreateVssBackupComponents)(
+    OUT IVssBackupComponents**);
+typedef void(APIENTRY * t_VssFreeSnapshotProperties)(IN VSS_SNAPSHOT_PROP*);
+
+static t_CreateVssBackupComponents _CreateVssBackupComponents;
+static t_VssFreeSnapshotProperties _VssFreeSnapshotProperties;
+static IVssBackupComponents *pVssbc;
+static IVssAsync *pAsyncSnapshot;
+static HMODULE hLib;
+static HANDLE hEvent = INVALID_HANDLE_VALUE, hEvent2 = INVALID_HANDLE_VALUE;
+static int cFrozenVols;
+
+GCC_FMT_ATTR(1, 2)
+static void errmsg(const char *fmt, ...)
+{
+    va_list ap;
+    va_start(ap, fmt);
+    char *msg = g_strdup_vprintf(fmt, ap);
+    va_end(ap);
+    MessageBox(NULL, msg, "Error in QEMU guest agent", MB_OK | MB_ICONWARNING);
+    g_free(msg);
+}
+
+static void error_set_win32(Error **errp, DWORD err,
+                            ErrorClass eclass, const char *text)
+{
+    char *msg = NULL, *nul = strchr(text, '(');
+    int len = nul ? nul - text : -1;
+
+    /* print error message in native encoding */
+    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+                  FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+                  NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+                  (char *)&msg, 0, NULL);
+    printf("%.*s. (Error: %lx) %s\n", len, text, err, msg);
+    LocalFree(msg);
+
+    /* set error message in UTF-8 encoding */
+    msg = g_win32_error_message(err);
+    error_set(errp, eclass, "%.*s. (Error: %lx) %s", len, text, err, msg);
+    g_free(msg);
+}
+#define error_setg_win32(errp, err, text) \
+    error_set_win32(errp, err, ERROR_CLASS_GENERIC_ERROR, text)
+
+#define __chk(status, text, errp, err_label)    \
+    do {                                        \
+        HRESULT __hr = (status);                \
+        if (FAILED(__hr)) {                     \
+            error_setg_win32(errp, __hr, text); \
+            goto err_label;                     \
+        }                                       \
+    } while (0)
+
+#define _chk(status, msg) __chk(status, "Failed to " msg, err, out)
+#define chk(status)  __chk(status, "Failed to " #status, err, out)
+
+
+HRESULT WaitForAsync(IVssAsync *pAsync)
+{
+    HRESULT ret, hr;
+
+    do {
+        hr = pAsync->Wait();
+        if (FAILED(hr)) {
+            ret = hr;
+            break;
+        }
+        hr = pAsync->QueryStatus(&ret, NULL);
+        if (FAILED(hr)) {
+            ret = hr;
+            break;
+        }
+    } while (ret == VSS_S_ASYNC_PENDING);
+
+    return ret;
+}
+
+HRESULT vss_init(void)
+{
+    HRESULT hr;
+
+    hr = VSSCheckOSVersion();
+    if (hr == S_FALSE) {
+        return hr;
+    }
+
+    hr = CoInitialize(NULL);
+    if (FAILED(hr)) {
+        errmsg("CoInitialize failed [%lx]", hr);
+        goto out;
+    };
+    hr = CoInitializeSecurity(
+        NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
+        RPC_C_IMP_LEVEL_IDENTIFY, NULL, EOAC_NONE, NULL);
+    if (FAILED(hr)) {
+        errmsg("CoInitializeSecurity failed [%lx]", hr);
+        goto out;
+    }
+
+    hLib = LoadLibraryA("VSSAPI.DLL");
+    if (!hLib) {
+        errmsg("LoadLibrary VSSAPI.DLL failed");
+        hr = E_FAIL;
+        goto out;
+    }
+
+    _CreateVssBackupComponents = (t_CreateVssBackupComponents)
+        GetProcAddress(hLib,
+#ifdef _WIN64 /* 64bit environment */
+        "?CreateVssBackupComponents@@YAJPEAPEAVIVssBackupComponents@@@Z"
+#else /* 32bit environment */
+        "?CreateVssBackupComponents@@YGJPAPAVIVssBackupComponents@@@Z"
+#endif
+        );
+    _VssFreeSnapshotProperties = (t_VssFreeSnapshotProperties)
+        GetProcAddress(hLib, "VssFreeSnapshotProperties");
+    if (!_CreateVssBackupComponents || !_VssFreeSnapshotProperties) {
+        errmsg("GetProcAddress failed");
+        hr = E_FAIL;
+        goto out;
+    }
+
+    return S_OK;
+out:
+    vss_deinit();
+    return hr;
+}
+
+static void vss_cleanup(void)
+{
+    if (hEvent != INVALID_HANDLE_VALUE) {
+        CloseHandle(hEvent);
+        hEvent = INVALID_HANDLE_VALUE;
+    }
+    if (hEvent2 != INVALID_HANDLE_VALUE) {
+        CloseHandle(hEvent2);
+        hEvent2 = INVALID_HANDLE_VALUE;
+    }
+    if (pVssbc) {
+        pVssbc->Release();
+        pVssbc = NULL;
+    }
+}
+
+void vss_deinit(void)
+{
+    if (VSSCheckOSVersion() == S_FALSE) {
+        return;
+    }
+
+    vss_cleanup();
+
+    CoUninitialize();
+
+    _CreateVssBackupComponents = NULL;
+    _VssFreeSnapshotProperties = NULL;
+    if (hLib) {
+        FreeLibrary(hLib);
+        hLib = NULL;
+    }
+}
+
+int vss_initialized(void)
+{
+    return hLib != NULL;
+}
+
+static void vss_add_components(Error **err)
+{
+    unsigned int cWriters, i;
+    VSS_ID id, idInstance, idWriter;
+    BSTR bstrWriterName;
+    VSS_USAGE_TYPE usage;
+    VSS_SOURCE_TYPE source;
+    unsigned int cComponents, c1, c2, j;
+    IVssExamineWriterMetadata *pMetadata;
+    IVssWMComponent *pComponent;
+    PVSSCOMPONENTINFO pInfo = NULL;
+
+    chk(pVssbc->GetWriterMetadataCount(&cWriters));
+
+    for (i = 0; i < cWriters; i++) {
+        chk(pVssbc->GetWriterMetadata(i, &id, &pMetadata));
+        chk(pMetadata->GetIdentity(&idInstance, &idWriter,
+                                    &bstrWriterName, &usage, &source));
+        chk(pMetadata->GetFileCounts(&c1, &c2, &cComponents));
+
+        for (j = 0; j < cComponents; j++) {
+            chk(pMetadata->GetComponent(j, &pComponent));
+            chk(pComponent->GetComponentInfo(&pInfo));
+            if (pInfo->bSelectable) {
+                chk(pVssbc->AddComponent(idInstance, idWriter, pInfo->type,
+                                         pInfo->bstrLogicalPath,
+                                         pInfo->bstrComponentName));
+            }
+            pComponent->FreeComponentInfo(pInfo);
+            pInfo = NULL;
+            pComponent->Release();
+            pComponent = NULL;
+        }
+
+        pMetadata->Release();
+        pMetadata = NULL;
+    }
+out:
+    if (pComponent) {
+        if (pInfo) {
+            pComponent->FreeComponentInfo(pInfo);
+        }
+        pComponent->Release();
+    }
+    if (pMetadata) {
+        pMetadata->Release();
+    }
+}
+
+void qga_vss_fsfreeze_freeze(int *num_vols, Error **err)
+{
+    IVssAsync *pAsync;
+    HANDLE h;
+    HRESULT hr;
+    LONG ctx;
+    GUID guidSnapshotSet = GUID_NULL;
+    SECURITY_DESCRIPTOR sd;
+    SECURITY_ATTRIBUTES sa;
+    WCHAR buf[64], *b = buf;
+    int n = 0;
+
+    if (pVssbc) { /* already frozen */
+        *num_vols = 0;
+        return;
+    }
+
+    assert(_CreateVssBackupComponents != NULL);
+    chk(_CreateVssBackupComponents(&pVssbc));
+    chk(pVssbc->InitializeForBackup());
+    chk(pVssbc->SetBackupState(true, true, VSS_BT_FULL, false));
+    /*
+     * Currently writable snapshots are not supported.
+     * To prevent the final commit (which requires to write to snapshots),
+     * 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 = 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;
+        chk(pVssbc->SetContext(ctx));
+    } else {
+        _chk(hr, "SetContext");
+    }
+
+    chk(pVssbc->GatherWriterMetadata(&pAsync));
+    _chk(WaitForAsync(pAsync), "GatherWriterMetadata");
+    pAsync->Release();
+
+    vss_add_components(err);
+    if (error_is_set(err)) {
+        goto out;
+    }
+
+    chk(pVssbc->StartSnapshotSet(&guidSnapshotSet));
+
+    h = FindFirstVolumeW(buf, sizeof(buf));
+    while (h != INVALID_HANDLE_VALUE) {
+        if (GetDriveTypeW(buf) == DRIVE_FIXED) {
+            VSS_ID pid;
+            hr = pVssbc->AddToSnapshotSet(buf, g_gProviderId, &pid);
+            if (FAILED(hr)) {
+                WCHAR name[PATH_MAX];
+                char msg[PATH_MAX+32];
+                if (GetVolumePathNamesForVolumeNameW(
+                        buf, name, sizeof(name), NULL) && *name) {
+                    b = name;
+                }
+                snprintf(msg, sizeof(msg), "add %S to snapshot set", b);
+                error_setg_win32(err, hr, msg);
+                goto out;
+            }
+            n++;
+        }
+        if (!FindNextVolumeW(h, buf, sizeof(buf))) {
+            FindVolumeClose(h);
+            break;
+        }
+    }
+
+    chk(pVssbc->PrepareForBackup(&pAsync));
+    _chk(WaitForAsync(pAsync), "PrepareForBackup");
+    pAsync->Release();
+
+    chk(pVssbc->GatherWriterStatus(&pAsync));
+    _chk(WaitForAsync(pAsync), "GatherWriterStatus");
+    pAsync->Release();
+
+    /* Allow unrestricted access to events */
+    InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
+    SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE);
+    sa.nLength = sizeof(sa);
+    sa.lpSecurityDescriptor = &sd;
+    sa.bInheritHandle = FALSE;
+
+    hEvent = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_FROZEN);
+    if (hEvent == INVALID_HANDLE_VALUE) {
+        error_setg_win32(err, GetLastError(), "CreateEvenet");
+        goto out;
+    }
+    hEvent2 = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_THAW);
+    if (hEvent2 == INVALID_HANDLE_VALUE) {
+        error_setg_win32(err, GetLastError(), "CreateEvenet");
+        goto out;
+    }
+
+    chk(pVssbc->DoSnapshotSet(&pAsyncSnapshot));
+
+    /* Need to call QueryStatus several times to make VSS provider progress */
+    for (int i = 0; i < 1000; i++) {
+        HRESULT hr = S_OK;
+        chk(pAsyncSnapshot->QueryStatus(&hr, NULL));
+        if (hr != VSS_S_ASYNC_PENDING) {
+            error_setg(err, "DoSnapshotSet exited without freeze event");
+            goto out;
+        }
+        DWORD ret = WaitForSingleObject(hEvent, 10);
+        if (ret == WAIT_OBJECT_0) {
+            break;
+        }
+    }
+
+    *num_vols = cFrozenVols = n;
+    return;
+
+out:
+    if (pVssbc) {
+        pVssbc->AbortBackup();
+    }
+    vss_cleanup();
+}
+
+
+void qga_vss_fsfreeze_thaw(int *num_vols, Error **err)
+{
+    IVssAsync *pAsync;
+
+    if (hEvent2 == INVALID_HANDLE_VALUE) {
+        /*
+         * In this case, DoSnapshotSet is aborted or not started,
+         * and no volumes must be frozen. We return without an error.
+         */
+        *num_vols = 0;
+        return;
+    }
+    SetEvent(hEvent2);
+
+    assert(pVssbc);
+    assert(pAsyncSnapshot);
+
+    HRESULT hr = WaitForAsync(pAsyncSnapshot);
+    if (hr == (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. Still, as the
+         * applications and file systems are frozen, we just ignore the error.
+         */
+        pAsyncSnapshot->Release();
+        pAsyncSnapshot = NULL;
+        goto final;
+    }
+    if (hr == (HRESULT)VSS_E_HOLD_WRITES_TIMEOUT) {
+        _chk(hr, "DoSnapshotSet: Couldn't hold writes. "
+             "Fsfreeze is limited up to 10 seconds");
+    }
+    _chk(hr, "DoSnapshotSet");
+    pAsyncSnapshot->Release();
+    pAsyncSnapshot = NULL;
+
+    chk(pVssbc->BackupComplete(&pAsync));
+    _chk(WaitForAsync(pAsync), "BackupComplete");
+    pAsync->Release();
+
+final:
+    *num_vols = cFrozenVols;
+    cFrozenVols = 0;
+    goto done;
+
+out:
+    if (pVssbc) {
+        pVssbc->AbortBackup();
+    }
+done:
+    vss_cleanup();
+}
diff --git a/qga/vss-win32-requester.h b/qga/vss-win32-requester.h
new file mode 100644
index 0000000..f180f56
--- /dev/null
+++ b/qga/vss-win32-requester.h
@@ -0,0 +1,31 @@
+/*
+ * QEMU Guest Agent VSS Requester declarations
+ *
+ * Copyright Hitachi Data Systems Corp. 2013
+ *
+ * Authors:
+ *  Tomoki Sekiyama   <tomoki.sekiyama@hds.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef VSS_WIN32_REQUESTER_H
+#define VSS_WIN32_REQUESTER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+HRESULT vss_init(void);
+void vss_deinit(void);
+int vss_initialized(void);
+
+void qga_vss_fsfreeze_freeze(int *nr_volume, struct Error **err);
+void qga_vss_fsfreeze_thaw(int *nr_volume, struct Error **err);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [Qemu-devel] [PATCH v4 08/10] qemu-ga: call Windows VSS requester in fsfreeze command handler
  2013-06-06 15:06 [Qemu-devel] [PATCH v4 00/10] qemu-ga: fsfreeze on Windows using VSS Tomoki Sekiyama
                   ` (6 preceding siblings ...)
  2013-06-06 15:06 ` [Qemu-devel] [PATCH v4 07/10] qemu-ga: Add Windows VSS requester to quiesce applications and filesystems Tomoki Sekiyama
@ 2013-06-06 15:06 ` Tomoki Sekiyama
  2013-07-01 13:29   ` Laszlo Ersek
  2013-06-06 15:06 ` [Qemu-devel] [PATCH v4 09/10] qemu-ga: install Windows VSS provider on `qemu-ga -s install' Tomoki Sekiyama
                   ` (3 subsequent siblings)
  11 siblings, 1 reply; 70+ messages in thread
From: Tomoki Sekiyama @ 2013-06-06 15:06 UTC (permalink / raw)
  To: qemu-devel
  Cc: libaiqing, stefanha, mdroth, lcapitulino, vrozenfe, pbonzini,
	seiji.aguchi, areis

Support guest-fsfreeze-freeze and guest-fsfreeze-thaw commands for Windows
guests. When fsfreeze command is issued, it calls the VSS requester to
freeze filesystems and applications. On thaw command, it again tells the VSS
requester to thaw them.

This also adds calling of initialize functions for the VSS requester.

Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
---
 qga/commands-win32.c |   74 ++++++++++++++++++++++++++++++++++++++++++++++----
 qga/main.c           |   33 ++++++++++++++++++++++
 2 files changed, 100 insertions(+), 7 deletions(-)

diff --git a/qga/commands-win32.c b/qga/commands-win32.c
index 24e4ad0..67dca60 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-requester.h"
 #include "qga-qmp-commands.h"
 #include "qapi/qmp/qerror.h"
 
@@ -151,34 +152,95 @@ void qmp_guest_file_flush(int64_t handle, Error **err)
     error_set(err, QERR_UNSUPPORTED);
 }
 
+#ifdef HAS_VSS_SDK
+
 /*
  * Return status of freeze/thaw
  */
 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;
+
+    slog("guest-fsfreeze called");
+
+    if (!vss_initialized()) {
+        error_set(err, QERR_UNSUPPORTED);
+        return 0;
+    }
+
+    /* cannot risk guest agent blocking itself on a write in this state */
+    ga_set_frozen(ga_state);
+
+    qga_vss_fsfreeze_freeze(&i, err);
+    if (error_is_set(err)) {
+        goto error;
+    }
+
+    return i;
+
+error:
+    qmp_guest_fsfreeze_thaw(NULL);
     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)
 {
+    int i;
+
+    if (!vss_initialized()) {
+        error_set(err, QERR_UNSUPPORTED);
+        return 0;
+    }
+
+    qga_vss_fsfreeze_thaw(&i, err);
+
+    ga_unset_frozen(ga_state);
+    return i;
+}
+
+#else
+
+GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err)
+{
+    error_set(err, QERR_UNSUPPORTED);
+    return 0;
+}
+
+int64_t qmp_guest_fsfreeze_freeze(Error **err)
+{
+    error_set(err, QERR_UNSUPPORTED);
+    return 0;
+}
+
+int64_t qmp_guest_fsfreeze_thaw(Error **err)
+{
     error_set(err, QERR_UNSUPPORTED);
     return 0;
 }
 
+#endif
+
 /*
  * Walk list of mounted file systems in the guest, and discard unused
  * areas.
diff --git a/qga/main.c b/qga/main.c
index 0e04e73..8bcedaf 100644
--- a/qga/main.c
+++ b/qga/main.c
@@ -34,6 +34,10 @@
 #include "qemu/bswap.h"
 #ifdef _WIN32
 #include "qga/service-win32.h"
+#ifdef HAS_VSS_SDK
+#include "qga/vss-win32-provider.h"
+#include "qga/vss-win32-requester.h"
+#endif
 #include <windows.h>
 #endif
 #ifdef __linux__
@@ -701,6 +705,25 @@ static gboolean channel_init(GAState *s, const gchar *method, const gchar *path)
 }
 
 #ifdef _WIN32
+
+static gboolean vss_win32_init(void)
+{
+#ifdef HAS_VSS_SDK
+    if (FAILED(vss_init())) {
+        g_critical("failed to initialize VSS");
+        return false;
+    }
+#endif
+    return true;
+}
+
+static void vss_win32_deinit(void)
+{
+#ifdef HAS_VSS_SDK
+    vss_deinit();
+#endif
+}
+
 DWORD WINAPI service_ctrl_handler(DWORD ctrl, DWORD type, LPVOID data,
                                   LPVOID ctx)
 {
@@ -743,8 +766,12 @@ VOID WINAPI service_main(DWORD argc, TCHAR *argv[])
     service->status.dwWaitHint = 0;
     SetServiceStatus(service->status_handle, &service->status);
 
+    if (!vss_win32_init()) {
+        goto out_bad;
+    }
     g_main_loop_run(ga_state->main_loop);
-
+    vss_win32_deinit();
+out_bad:
     service->status.dwCurrentState = SERVICE_STOPPED;
     SetServiceStatus(service->status_handle, &service->status);
 }
@@ -1175,7 +1202,11 @@ int main(int argc, char **argv)
             { (char *)QGA_SERVICE_NAME, service_main }, { NULL, NULL } };
         StartServiceCtrlDispatcher(service_table);
     } else {
+        if (!vss_win32_init()) {
+            goto out_bad;
+        }
         g_main_loop_run(ga_state->main_loop);
+        vss_win32_deinit();
     }
 #endif
 

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [Qemu-devel] [PATCH v4 09/10] qemu-ga: install Windows VSS provider on `qemu-ga -s install'
  2013-06-06 15:06 [Qemu-devel] [PATCH v4 00/10] qemu-ga: fsfreeze on Windows using VSS Tomoki Sekiyama
                   ` (7 preceding siblings ...)
  2013-06-06 15:06 ` [Qemu-devel] [PATCH v4 08/10] qemu-ga: call Windows VSS requester in fsfreeze command handler Tomoki Sekiyama
@ 2013-06-06 15:06 ` Tomoki Sekiyama
  2013-07-01 14:50   ` Laszlo Ersek
  2013-06-06 15:07 ` [Qemu-devel] [PATCH v4 10/10] QMP/qemu-ga-client: make timeout longer for guest-fsfreeze-freeze command Tomoki Sekiyama
                   ` (2 subsequent siblings)
  11 siblings, 1 reply; 70+ messages in thread
From: Tomoki Sekiyama @ 2013-06-06 15:06 UTC (permalink / raw)
  To: qemu-devel
  Cc: libaiqing, stefanha, mdroth, lcapitulino, vrozenfe, pbonzini,
	seiji.aguchi, areis

Register QGA VSS provider library into Windows when qemu-ga is installed as
Windows service ('-s install' option). It is deregistered when the service
is uninstalled ('-s uninstall' option).

Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
---
 qga/main.c |    8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/qga/main.c b/qga/main.c
index 8bcedaf..739b958 100644
--- a/qga/main.c
+++ b/qga/main.c
@@ -1058,8 +1058,16 @@ int main(int argc, char **argv)
                 fixed_state_dir = (state_dir == dfl_pathnames.state_dir) ?
                                   NULL :
                                   state_dir;
+#ifdef HAS_VSS_SDK
+                if (FAILED(COMRegister())) {
+                    return EXIT_FAILURE;
+                }
+#endif
                 return ga_install_service(path, log_filepath, fixed_state_dir);
             } else if (strcmp(service, "uninstall") == 0) {
+#ifdef HAS_VSS_SDK
+                COMUnregister();
+#endif
                 return ga_uninstall_service();
             } else {
                 printf("Unknown service command.\n");

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [Qemu-devel] [PATCH v4 10/10] QMP/qemu-ga-client: make timeout longer for guest-fsfreeze-freeze command
  2013-06-06 15:06 [Qemu-devel] [PATCH v4 00/10] qemu-ga: fsfreeze on Windows using VSS Tomoki Sekiyama
                   ` (8 preceding siblings ...)
  2013-06-06 15:06 ` [Qemu-devel] [PATCH v4 09/10] qemu-ga: install Windows VSS provider on `qemu-ga -s install' Tomoki Sekiyama
@ 2013-06-06 15:07 ` Tomoki Sekiyama
  2013-06-18 10:17   ` Paolo Bonzini
  2013-07-01 15:02   ` Laszlo Ersek
  2013-06-10  9:26 ` [Qemu-devel] [PATCH v4 00/10] qemu-ga: fsfreeze on Windows using VSS Stefan Hajnoczi
  2013-06-24 19:30 ` Tomoki Sekiyama
  11 siblings, 2 replies; 70+ messages in thread
From: Tomoki Sekiyama @ 2013-06-06 15:07 UTC (permalink / raw)
  To: qemu-devel
  Cc: libaiqing, stefanha, mdroth, lcapitulino, vrozenfe, pbonzini,
	seiji.aguchi, areis

guest-fsfreeze-freeze command can take longer than 3 seconds when heavy
disk I/O is running. To avoid unexpected timeout, this changes the timeout
to 60 seconds (timeout of pre-commit phase of VSS).

Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
---
 QMP/qemu-ga-client |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/QMP/qemu-ga-client b/QMP/qemu-ga-client
index 46676c3..b5f7e7c 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)

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 00/10] qemu-ga: fsfreeze on Windows using VSS
  2013-06-06 15:06 [Qemu-devel] [PATCH v4 00/10] qemu-ga: fsfreeze on Windows using VSS Tomoki Sekiyama
                   ` (9 preceding siblings ...)
  2013-06-06 15:07 ` [Qemu-devel] [PATCH v4 10/10] QMP/qemu-ga-client: make timeout longer for guest-fsfreeze-freeze command Tomoki Sekiyama
@ 2013-06-10  9:26 ` Stefan Hajnoczi
  2013-06-24 19:30 ` Tomoki Sekiyama
  11 siblings, 0 replies; 70+ messages in thread
From: Stefan Hajnoczi @ 2013-06-10  9:26 UTC (permalink / raw)
  To: Tomoki Sekiyama
  Cc: libaiqing, mdroth, qemu-devel, lcapitulino, vrozenfe, pbonzini,
	seiji.aguchi, areis

On Thu, Jun 06, 2013 at 11:06:19AM -0400, Tomoki Sekiyama wrote:
> changes from v3:
>  -[01/10] Use c++ instead of g++ in configureing C++ compiler if neither
>           $cross_prefix nor $CXX is specified.
>  -[02/10] Add "alternative representations" as a reserved keywords in qapi.py.
>  -[03/10 (newly added)] modify check-patch.pl to check C++ source code
>  -[04/10] Improve compatibility (POSIX-compliant).
>  -[04/10] Added some error checks.
>  -[05/10] Added --with-win-sdk option to configure path to Windows SDK.
>  -[06/10] Patch v3 09/11 (adding binary .tlb file) is squashed into this patch
>           in order to avoid git-bisect failure.
>  -[06/10 & 07/10] Fix coding style.
>  -Dropped Patch v3 11/11 (encode error desc of exception with current locale)
> 
> changes from v2:
>  -[06/11] Fix errors in Windows 7, reported by Li Baiqing
> 
> changes from v1: 
>  - Fix out-tree build by stop using recursive Makefile
>  - Added script to extract VSS SDK headers on POSIX systems using msitools
>    (thanks Paolo)
>  - Remove some unnecessary header files

Cool, thanks for the checkpatch.pl and ./configure changes!

Stefan

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 01/10] configure: Support configuring c++ compiler
  2013-06-06 15:06 ` [Qemu-devel] [PATCH v4 01/10] configure: Support configuring c++ compiler Tomoki Sekiyama
@ 2013-06-18 10:17   ` Paolo Bonzini
  2013-06-25  8:15   ` Laszlo Ersek
  1 sibling, 0 replies; 70+ messages in thread
From: Paolo Bonzini @ 2013-06-18 10:17 UTC (permalink / raw)
  To: Tomoki Sekiyama
  Cc: libaiqing, mdroth, stefanha, qemu-devel, lcapitulino, vrozenfe,
	seiji.aguchi, areis

Il 06/06/2013 17:06, Tomoki Sekiyama ha scritto:
> Add configuration for c++ compiler (${cross_prefix}g++ as default) in
> configure and Makefiles.
> 
> Currently, usage of c++ language is only for access to Windows VSS
> using COM+ services in qemu-guest-agent for Windows.
> 
> Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
> ---
>  configure |   13 +++++++++++++
>  rules.mak |    9 ++++++++-
>  2 files changed, 21 insertions(+), 1 deletion(-)
> 
> diff --git a/configure b/configure
> index 1654413..a2fc3f3 100755
> --- a/configure
> +++ b/configure
> @@ -251,6 +251,8 @@ for opt do
>    ;;
>    --cc=*) CC="$optarg"
>    ;;
> +  --cxx=*) CXX="$optarg"
> +  ;;
>    --source-path=*) source_path="$optarg"
>    ;;
>    --cpu=*) cpu="$optarg"
> @@ -281,6 +283,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}"
> @@ -614,6 +622,8 @@ for opt do
>    ;;
>    --host-cc=*) host_cc="$optarg"
>    ;;
> +  --cxx=*) cxx="$optarg"
> +  ;;
>    --objcc=*) objcc="$optarg"
>    ;;
>    --make=*) make="$optarg"
> @@ -1015,6 +1025,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"
> @@ -3459,6 +3470,7 @@ 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"
> @@ -4036,6 +4048,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/rules.mak b/rules.mak
> index 4499745..abc2e84 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
> 

Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 10/10] QMP/qemu-ga-client: make timeout longer for guest-fsfreeze-freeze command
  2013-06-06 15:07 ` [Qemu-devel] [PATCH v4 10/10] QMP/qemu-ga-client: make timeout longer for guest-fsfreeze-freeze command Tomoki Sekiyama
@ 2013-06-18 10:17   ` Paolo Bonzini
  2013-07-01 15:02   ` Laszlo Ersek
  1 sibling, 0 replies; 70+ messages in thread
From: Paolo Bonzini @ 2013-06-18 10:17 UTC (permalink / raw)
  To: Tomoki Sekiyama
  Cc: libaiqing, mdroth, stefanha, qemu-devel, lcapitulino, vrozenfe,
	seiji.aguchi, areis

Il 06/06/2013 17:07, Tomoki Sekiyama ha scritto:
> guest-fsfreeze-freeze command can take longer than 3 seconds when heavy
> disk I/O is running. To avoid unexpected timeout, this changes the timeout
> to 60 seconds (timeout of pre-commit phase of VSS).
> 
> Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
> ---
>  QMP/qemu-ga-client |    4 +++-
>  1 file changed, 3 insertions(+), 1 deletion(-)
> 
> diff --git a/QMP/qemu-ga-client b/QMP/qemu-ga-client
> index 46676c3..b5f7e7c 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)
> 

Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 00/10] qemu-ga: fsfreeze on Windows using VSS
  2013-06-06 15:06 [Qemu-devel] [PATCH v4 00/10] qemu-ga: fsfreeze on Windows using VSS Tomoki Sekiyama
                   ` (10 preceding siblings ...)
  2013-06-10  9:26 ` [Qemu-devel] [PATCH v4 00/10] qemu-ga: fsfreeze on Windows using VSS Stefan Hajnoczi
@ 2013-06-24 19:30 ` Tomoki Sekiyama
  2013-06-24 21:13   ` Laszlo Ersek
  11 siblings, 1 reply; 70+ messages in thread
From: Tomoki Sekiyama @ 2013-06-24 19:30 UTC (permalink / raw)
  To: qemu-devel
  Cc: libaiqing, stefanha, mdroth, lcapitulino, vrozenfe, pbonzini,
	Seiji Aguchi, areis

Any other comments for this series?


On 6/6/13 11:06 , "Tomoki Sekiyama" <tomoki.sekiyama@hds.com> wrote:
>Hi,
>
>This patch series adds fsfreeze support for Windows qemu-guest-agent.
>
>changes from v3:
> -[01/10] Use c++ instead of g++ in configureing C++ compiler if neither
>          $cross_prefix nor $CXX is specified.
> -[02/10] Add "alternative representations" as a reserved keywords in
>qapi.py.
> -[03/10 (newly added)] modify check-patch.pl to check C++ source code
> -[04/10] Improve compatibility (POSIX-compliant).
> -[04/10] Added some error checks.
> -[05/10] Added --with-win-sdk option to configure path to Windows SDK.
> -[06/10] Patch v3 09/11 (adding binary .tlb file) is squashed into this
>patch
>          in order to avoid git-bisect failure.
> -[06/10 & 07/10] Fix coding style.
> -Dropped Patch v3 11/11 (encode error desc of exception with current
>locale)
>
>changes from v2:
> -[06/11] Fix errors in Windows 7, reported by Li Baiqing
>
>changes from v1: 
> - Fix out-tree build by stop using recursive Makefile
> - Added script to extract VSS SDK headers on POSIX systems using msitools
>   (thanks Paolo)
> - Remove some unnecessary header files
>
>v3: http://lists.nongnu.org/archive/html/qemu-devel/2013-05/msg02771.html
>
>
>* Description
>  In Windows, VSS (Volume Shadow Copy Service) provides a facility to
>  quiesce filesystems and applications before disk snapshots are taken.
>  This patch series implements "fsfreeze" command of qemu-ga using VSS.
>
>
>* How to build & run qemu-ga with VSS support
>
> - Download Microsoft VSS SDK from:
>   http://www.microsoft.com/en-us/download/details.aspx?id=23490
>
> - Setup the SDK
>   scripts/extract-vsssdk-headers setup.exe (on POSIX-systems)
>
> - Specify installed SDK directory to configure option as:
>   ./configure -with-vss-sdk="path/to/VSS SDK"
>--cross-prefix=i686-w64-mingw32-
>
> - make qemu-ga.exe
>
> - Install qemu-ga.exe, qga/vss-win32-provider/qga-provider.{dll,tlb}, and
>   the other required mingw libraries into the same directory in guests
>
> - Run `qemu-ga.exe -s install' and `net start qemu-ga' in the guests
>
>Any feedback are appreciated.
>
>---
>Tomoki Sekiyama (10):
>      configure: Support configuring c++ compiler
>      Add c++ keywords to QAPI helper script
>      checkpatch.pl: check .cpp files
>      Add a script to extract VSS SDK headers on POSIX system
>      qemu-ga: Add configure options to specify path to Windows/VSS SDK
>      qemu-ga: Add Windows VSS provider to quiesce applications on
>fsfreeze
>      qemu-ga: Add Windows VSS requester to quiesce applications and
>filesystems
>      qemu-ga: call Windows VSS requester in fsfreeze command handler
>      qemu-ga: install Windows VSS provider on `qemu-ga -s install'
>      QMP/qemu-ga-client: make timeout longer for guest-fsfreeze-freeze
>command
>
>
> .gitignore                              |    1
> Makefile                                |    3
> Makefile.objs                           |    2
> QMP/qemu-ga-client                      |    4
> configure                               |   87 +++++
> hmp.c                                   |    2
> hw/pci/pci.c                            |    2
> qga/Makefile.objs                       |    7
> qga/commands-win32.c                    |   74 ++++-
> qga/main.c                              |   41 +++
> qga/vss-win32-provider.h                |   26 ++
> qga/vss-win32-provider/Makefile.objs    |   24 ++
> qga/vss-win32-provider/install.cpp      |  493
>+++++++++++++++++++++++++++++++
> qga/vss-win32-provider/provider.cpp     |  479
>++++++++++++++++++++++++++++++
> qga/vss-win32-provider/qga-provider.def |   10 +
> qga/vss-win32-provider/qga-provider.idl |   20 +
> qga/vss-win32-provider/qga-provider.tlb |  Bin
> qga/vss-win32-requester.cpp             |  419 ++++++++++++++++++++++++++
> qga/vss-win32-requester.h               |   31 ++
> qga/vss-win32.h                         |   86 +++++
> rules.mak                               |    9 +
> scripts/checkpatch.pl                   |   34 ++
> scripts/extract-vsssdk-headers          |   36 ++
> scripts/qapi.py                         |   12 +
> 24 files changed, 1880 insertions(+), 22 deletions(-)
> create mode 100644 qga/vss-win32-provider.h
> create mode 100644 qga/vss-win32-provider/Makefile.objs
> create mode 100644 qga/vss-win32-provider/install.cpp
> create mode 100644 qga/vss-win32-provider/provider.cpp
> create mode 100644 qga/vss-win32-provider/qga-provider.def
> create mode 100644 qga/vss-win32-provider/qga-provider.idl
> create mode 100644 qga/vss-win32-provider/qga-provider.tlb
> create mode 100644 qga/vss-win32-requester.cpp
> create mode 100644 qga/vss-win32-requester.h
> create mode 100644 qga/vss-win32.h
> create mode 100755 scripts/extract-vsssdk-headers
>

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 00/10] qemu-ga: fsfreeze on Windows using VSS
  2013-06-24 19:30 ` Tomoki Sekiyama
@ 2013-06-24 21:13   ` Laszlo Ersek
  0 siblings, 0 replies; 70+ messages in thread
From: Laszlo Ersek @ 2013-06-24 21:13 UTC (permalink / raw)
  To: Tomoki Sekiyama
  Cc: libaiqing, mdroth, stefanha, qemu-devel, lcapitulino, vrozenfe,
	pbonzini, Seiji Aguchi, areis

On 06/24/13 21:30, Tomoki Sekiyama wrote:
> Any other comments for this series?

It's been on my todo list since you posted it :(

I was pleased by the design / summary you wrote up for my v3 questions,
but I didn't trust myself to give an R-b based on "just" that.

I'll try to do something about it this week. I don't know anything about
windows programming, so even if I manage to produce some review notes,
they'll have very limited value.

Laszlo

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 01/10] configure: Support configuring c++ compiler
  2013-06-06 15:06 ` [Qemu-devel] [PATCH v4 01/10] configure: Support configuring c++ compiler Tomoki Sekiyama
  2013-06-18 10:17   ` Paolo Bonzini
@ 2013-06-25  8:15   ` Laszlo Ersek
  1 sibling, 0 replies; 70+ messages in thread
From: Laszlo Ersek @ 2013-06-25  8:15 UTC (permalink / raw)
  To: Tomoki Sekiyama
  Cc: libaiqing, mdroth, stefanha, qemu-devel, lcapitulino, vrozenfe,
	pbonzini, seiji.aguchi, areis

On 06/06/13 17:06, Tomoki Sekiyama wrote:
> Add configuration for c++ compiler (${cross_prefix}g++ as default) in
> configure and Makefiles.
> 
> Currently, usage of c++ language is only for access to Windows VSS
> using COM+ services in qemu-guest-agent for Windows.
> 
> Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
> ---
>  configure |   13 +++++++++++++
>  rules.mak |    9 ++++++++-
>  2 files changed, 21 insertions(+), 1 deletion(-)

Reviewed-by: Laszlo Ersek <lersek@redhat.com>

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 02/10] Add c++ keywords to QAPI helper script
  2013-06-06 15:06 ` [Qemu-devel] [PATCH v4 02/10] Add c++ keywords to QAPI helper script Tomoki Sekiyama
@ 2013-06-25  8:16   ` Laszlo Ersek
  0 siblings, 0 replies; 70+ messages in thread
From: Laszlo Ersek @ 2013-06-25  8:16 UTC (permalink / raw)
  To: Tomoki Sekiyama
  Cc: libaiqing, mdroth, stefanha, qemu-devel, lcapitulino, vrozenfe,
	pbonzini, seiji.aguchi, areis

On 06/06/13 17:06, Tomoki Sekiyama wrote:
> Add c++ keywords to avoid errors in compiling with c++ compiler.
> This also renames class member of PciDeviceInfo to q_class.
> 
> Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
> ---
>  hmp.c           |    2 +-
>  hw/pci/pci.c    |    2 +-
>  scripts/qapi.py |   12 +++++++++++-
>  3 files changed, 13 insertions(+), 3 deletions(-)

Reviewed-by: Laszlo Ersek <lersek@redhat.com>

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 03/10] checkpatch.pl: check .cpp files
  2013-06-06 15:06 ` [Qemu-devel] [PATCH v4 03/10] checkpatch.pl: check .cpp files Tomoki Sekiyama
@ 2013-06-25  8:17   ` Laszlo Ersek
  0 siblings, 0 replies; 70+ messages in thread
From: Laszlo Ersek @ 2013-06-25  8:17 UTC (permalink / raw)
  To: Tomoki Sekiyama
  Cc: libaiqing, mdroth, stefanha, qemu-devel, lcapitulino, vrozenfe,
	pbonzini, seiji.aguchi, areis

On 06/06/13 17:06, Tomoki Sekiyama wrote:
> Enable checkpatch.pl to apply the same checks as C source files for
> C++ files with .cpp extensions. It also adds some exceptions for C++
> sources to suppress errors for:
>   - <> used in C++ template arguments (e.g. template <class T>)
>   - :: used to represent namespaces   (e.g. SomeClass::method())
>   - : used in class declaration       (e.g. class T : public Super)
>   - ~ used in destructor method name  (e.g. T::~T())
> 
> Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
> ---
>  scripts/checkpatch.pl |   34 ++++++++++++++++++++++++++--------
>  1 file changed, 26 insertions(+), 8 deletions(-)

It's Perl, enough said.

Reviewed-by: Laszlo Ersek <lersek@redhat.com>

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 04/10] Add a script to extract VSS SDK headers on POSIX system
  2013-06-06 15:06 ` [Qemu-devel] [PATCH v4 04/10] Add a script to extract VSS SDK headers on POSIX system Tomoki Sekiyama
@ 2013-06-25  8:30   ` Laszlo Ersek
  2013-06-25 15:01   ` Laszlo Ersek
  1 sibling, 0 replies; 70+ messages in thread
From: Laszlo Ersek @ 2013-06-25  8:30 UTC (permalink / raw)
  To: Tomoki Sekiyama
  Cc: libaiqing, mdroth, stefanha, qemu-devel, lcapitulino, vrozenfe,
	Paolo Bonzini, seiji.aguchi, areis

On 06/06/13 17:06, Tomoki Sekiyama wrote:

> +tmpdir=$(mktemp -d)
> +trap "rm -fr $tmpdir vsssdk.msi; exit 1" HUP INT QUIT ALRM TERM

"mktemp" keys off TMPDIR, so I would prefer something like (note the
single quotes -- $tmpdir is expanded at trap execution time):

  trap 'rm -fr -- "$tmpdir"'

but whoever starts TMPDIR with a dash, or puts whitespace in it,
deserves what's coming to him.

Also sigpsec in "trap" could have been EXIT -- then you could have
omitted the final cleanup on the success path. The "exit 1" is not
necessary; AFAIR the trap handler doesn't change the exit status that
the shell has decided for otherwise (IOW exit status of the trap handler
should be suppressed anyway).

Placing an "exit 1" in the trap handler could even be detrimental
somewhat, since it might mess with WIFSIGNALED() / WTERMSIG() that would
otherwise tell a parent shell to interrupt the parent script.

http://www.cons.org/cracauer/sigint.html

I've said nothing substantial. I wish I could go into such detail when
reviewing the real meat of the series...

Reviewed-by: Laszlo Ersek <lersek@redhat.com>

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 05/10] qemu-ga: Add configure options to specify path to Windows/VSS SDK
  2013-06-06 15:06 ` [Qemu-devel] [PATCH v4 05/10] qemu-ga: Add configure options to specify path to Windows/VSS SDK Tomoki Sekiyama
@ 2013-06-25 11:16   ` Laszlo Ersek
  2013-06-28 10:43     ` Daniel P. Berrange
  0 siblings, 1 reply; 70+ messages in thread
From: Laszlo Ersek @ 2013-06-25 11:16 UTC (permalink / raw)
  To: Tomoki Sekiyama
  Cc: libaiqing, mdroth, stefanha, qemu-devel, lcapitulino, vrozenfe,
	pbonzini, seiji.aguchi, areis

On 06/06/13 17:06, Tomoki Sekiyama wrote:
> To enable VSS support in qemu-ga for Windows, header files included in
> VSS SDK are required.
> The VSS support is enabled by the configure option like below:
>   ./configure --with-vss-sdk="/path/to/VSS SDK"
> 
> If the path is omitted, it tries to search the headers from default paths
> and VSS support is enabled only if the SDK is found.
> VSS support is disabled if --without-vss-sdk or --with-vss-sdk=no is
> specified.
> 
> VSS SDK is available from:
>   http://www.microsoft.com/en-us/download/details.aspx?id=23490
> 
> To cross-compile using mingw, you need to setup the SDK on Windows
> environments to extract headers. You can also extract the SDK headers on
> POSIX environments using scripts/extract-vss-headers and msitools.
> 
> In addition, --with-win-sdk="/path/to/Windows SDK" option is also added to
> specify path to Windows SDK, which may be used for native-compile of .tlb
> file of qemu-ga VSS provider. However, this is usually unnecessary because
> pre-compiled .tlb file is included.
> 
> Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
> ---
>  .gitignore |    1 +
>  Makefile   |    1 +
>  configure  |   69 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 71 insertions(+)

Reviewed-by: Laszlo Ersek <lersek@redhat.com>

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 04/10] Add a script to extract VSS SDK headers on POSIX system
  2013-06-25 15:01   ` Laszlo Ersek
@ 2013-06-25 15:01     ` Paolo Bonzini
  2013-06-26 10:52       ` Laszlo Ersek
  0 siblings, 1 reply; 70+ messages in thread
From: Paolo Bonzini @ 2013-06-25 15:01 UTC (permalink / raw)
  To: Laszlo Ersek
  Cc: libaiqing, mdroth, stefanha, qemu-devel, lcapitulino, vrozenfe,
	Tomoki Sekiyama, seiji.aguchi, areis

[-- Attachment #1: Type: text/plain, Size: 907 bytes --]

Il 25/06/2013 17:01, Laszlo Ersek ha scritto:
> On 06/06/13 17:06, Tomoki Sekiyama wrote:
> 
>> +if ! command -v msiextract > /dev/null; then
>> +  echo 'msiextract not found. Please install msitools.' >&2
>> +  exit 1
>> +fi
> 
> (This is not a review comment -- I'm trying to test it:)
> 
> What msiextract version (and dependencies, like libgcab, libgsf etc) are
> you using? I'm unable to extract "vsssdk.msi"; msiextract spews a bunch
> of assertion failures.
> 
> 2e39646b7850a12673bc66ade85fece3  setup.exe
> 433eb024ed0c669dd1563d952ca41091  vsssdk.msi
> 
> My versions (RHEL-6.4.z distro):
> - msitools-0.92 (built from source)
> - gcab-0.4 (built from source)
> - libgsf-1.14.15-5.el6 (patch in [1] doesn't seem to help, it only
> changes the kinds of asserts that fail)
> - glib2-2.22.5-7.el6
> - libuuid-2.17.2-12.9.el6_4.3

The attached patch may help building a newer libgsf on RHEL6.

Paolo


[-- Attachment #2: 0001-remove-need-for-glib-2.26.patch --]
[-- Type: text/x-patch, Size: 1794 bytes --]

>From 0eee72d26d06d7087d5d83efbc37564f67489019 Mon Sep 17 00:00:00 2001
From: Paolo Bonzini <pbonzini@redhat.com>
Date: Wed, 16 Jan 2013 19:27:20 +0100
Subject: [PATCH] remove need for glib 2.26

---
 configure           |    2 +-
 configure.in        |    2 +-
 gsf/gsf-timestamp.c |   16 +++++++++-------
 3 files changed, 11 insertions(+), 9 deletions(-)

diff --git a/configure b/configure
index 30aa84d..029f5f9 100755
--- a/configure
+++ b/configure
@@ -12740,7 +12740,7 @@ _ACEOF
 
 libgsf_reqs="
     gobject-2.0 >= 2.16.0
-    glib-2.0 >= 2.26.0
+    glib-2.0 >= 2.22.0
     libxml-2.0 >= 2.4.16
 "
 
diff --git a/configure.in b/configure.in
index a830360..c6c7254 100644
--- a/configure.in
+++ b/configure.in
@@ -71,7 +71,7 @@ ifelse([
 dnl Modules required for libgsf
 libgsf_reqs="
     gobject-2.0 >= 2.16.0
-    glib-2.0 >= 2.26.0
+    glib-2.0 >= 2.22.0
     libxml-2.0 >= 2.4.16
 "
 
diff --git a/gsf/gsf-timestamp.c b/gsf/gsf-timestamp.c
index 9d45f9a..a8e0a44 100644
--- a/gsf/gsf-timestamp.c
+++ b/gsf/gsf-timestamp.c
@@ -136,20 +136,22 @@ int
 gsf_timestamp_load_from_string (GsfTimestamp *stamp, char const *spec)
 {
 	int year, month, day, hour, minute, second;
-	GDateTime *dt;
+	time_t t;
 
 	/* 'YYYY-MM-DDThh:mm:ss' */
 	if (6 != sscanf (spec, "%d-%d-%dT%d:%d:%d",
 			 &year, &month, &day, &hour, &minute, &second))
 		return FALSE;
 
-	dt = g_date_time_new_utc (year, month, day, hour, minute, second);
-	if (!dt)
-		return FALSE;
-
-	stamp->timet = g_date_time_to_unix (dt);
+	if (month < 3) {
+		month += 12;
+		year--;
+	}
+	t = 86400ULL * (day + (153 * month - 457) / 5 + 365 * year + year / 4 - year / 100 + 
+		     year / 400 - 719469);
+	t += 3600 * hour + 60 * minute + second;
 
-	g_date_time_unref (dt);
+	stamp->timet = t;
 	return TRUE;
 }
 
-- 
1.7.1


^ permalink raw reply related	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 04/10] Add a script to extract VSS SDK headers on POSIX system
  2013-06-06 15:06 ` [Qemu-devel] [PATCH v4 04/10] Add a script to extract VSS SDK headers on POSIX system Tomoki Sekiyama
  2013-06-25  8:30   ` Laszlo Ersek
@ 2013-06-25 15:01   ` Laszlo Ersek
  2013-06-25 15:01     ` Paolo Bonzini
  1 sibling, 1 reply; 70+ messages in thread
From: Laszlo Ersek @ 2013-06-25 15:01 UTC (permalink / raw)
  To: Tomoki Sekiyama
  Cc: libaiqing, mdroth, stefanha, qemu-devel, lcapitulino, vrozenfe,
	Paolo Bonzini, seiji.aguchi, areis

On 06/06/13 17:06, Tomoki Sekiyama wrote:

> +if ! command -v msiextract > /dev/null; then
> +  echo 'msiextract not found. Please install msitools.' >&2
> +  exit 1
> +fi

(This is not a review comment -- I'm trying to test it:)

What msiextract version (and dependencies, like libgcab, libgsf etc) are
you using? I'm unable to extract "vsssdk.msi"; msiextract spews a bunch
of assertion failures.

2e39646b7850a12673bc66ade85fece3  setup.exe
433eb024ed0c669dd1563d952ca41091  vsssdk.msi

My versions (RHEL-6.4.z distro):
- msitools-0.92 (built from source)
- gcab-0.4 (built from source)
- libgsf-1.14.15-5.el6 (patch in [1] doesn't seem to help, it only
changes the kinds of asserts that fail)
- glib2-2.22.5-7.el6
- libuuid-2.17.2-12.9.el6_4.3

[1] http://bugzilla.gnome.org/show_bug.cgi?id=689706

Thanks!
Laszlo

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze
  2013-06-06 15:06 ` [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze Tomoki Sekiyama
@ 2013-06-25 16:03   ` Laszlo Ersek
  2013-06-25 16:19     ` Paolo Bonzini
                       ` (2 more replies)
  2013-06-25 21:15   ` Paolo Bonzini
  1 sibling, 3 replies; 70+ messages in thread
From: Laszlo Ersek @ 2013-06-25 16:03 UTC (permalink / raw)
  To: Tomoki Sekiyama
  Cc: libaiqing, mdroth, stefanha, qemu-devel, lcapitulino, vrozenfe,
	pbonzini, seiji.aguchi, areis

I'm trying to wrap my head around the build changes first.

On 06/06/13 17:06, Tomoki Sekiyama wrote:

> Implements a basic stub of software VSS provider. Currently, this
> module only provides a relay function of events between
> qemu-guest-agent and Windows VSS when VSS finished filesystem freeze
> and when qemu snapshot is done.
>
> In the future, this module could be extended to support the other VSS
> functions, such as query for snapshot volumes and recovery.
>
> To build type library (.tlb) for qga-provider.dll from COM IDL (.idl),
> VisualC++, MIDL and stdole2.tlb in Windows SDK are required.
>
> This patch also adds pre-compiled .tlb file in the repository in order
> to enable cross-compile qemu-ga.exe for Windows with VSS support.
>
> Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
> ---
>  configure                               |    5
>  Makefile                                |    2
>  Makefile.objs                           |    2
>  qga/Makefile.objs                       |    6
>  qga/vss-win32-provider/Makefile.objs    |   24 ++
>  create mode 100644 qga/vss-win32-provider/Makefile.objs

> diff --git a/configure b/configure
> index cafe830..8e45fad 100755
> --- a/configure
> +++ b/configure
> @@ -3490,9 +3490,12 @@ if test "$softmmu" = yes ; then
>        virtfs=no
>      fi
>    fi
> -  if [ "$linux" = "yes" -o "$bsd" = "yes" -o "$solaris" = "yes" ] ; then
> +  if [ "$linux" = "yes" -o "$bsd" = "yes" -o "$solaris" = "yes" -o "$mingw32" = "yes" ] ; then
>      if [ "$guest_agent" = "yes" ]; then
>        tools="qemu-ga\$(EXESUF) $tools"
> +      if [ "$mingw32" = "yes" ]; then
> +        tools="qga/vss-win32-provider/qga-provider.dll qga/vss-win32-provider/qga-provider.tlb $tools"
> +      fi
>      fi
>    fi
>  fi

This adds three targets to "tools" on mingw32 -- "qemu-ga" hasn't been
there until now. Is this actually a small, unrelated bugfix?

I'm asking because I build upstream qemu-ga.exe on my RHEL-6 laptop as
follows -- I don't have pixman installed, and configure forces me to
specify --disable-tools as well:

$ ./configure --without-pixman --disable-system --disable-tools \
      --cross-prefix=i686-pc-mingw32-
$ make qemu-ga.exe

Plain "make" after the above ./configure doesn't produce anything
useful. Will I have to specify the DLL and TLB targets too on the
command line? (Apologies, I can't try it out now, msiextract doesn't
work for me.)


> diff --git a/Makefile b/Makefile
> index 4851ba0..636358d 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

OK I guess.


> diff --git a/Makefile.objs b/Makefile.objs
> index 286ce06..b6c1505 100644
> --- a/Makefile.objs
> +++ b/Makefile.objs
> @@ -102,6 +102,7 @@ common-obj-y += disas/
>  # 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-prv-obj-y = qga/
>
>  vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS)
>
> @@ -113,6 +114,7 @@ nested-vars += \
>  	stub-obj-y \
>  	util-obj-y \
>  	qga-obj-y \
> +	qga-prv-obj-y \
>  	block-obj-y \
>  	common-obj-y
>  dummy := $(call unnest-vars)

What does this do? Does "qga-prv-obj-y" stand for "all objects under
'qga'"?

Or does it mean "look into qga/Makefile.objs for further objects"?


> diff --git a/qga/Makefile.objs b/qga/Makefile.objs
> index b8d7cd0..8d93866 100644
> --- a/qga/Makefile.objs
> +++ b/qga/Makefile.objs
> @@ -3,3 +3,9 @@ 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-y += qapi-generated/qga-qapi-types.o qapi-generated/qga-qapi-visit.o
>  qga-obj-y += qapi-generated/qga-qmp-marshal.o
> +
> +ifeq ($(CONFIG_QGA_VSS),y)
> +QEMU_CFLAGS += -DHAS_VSS_SDK

Isn't this superfluous? First, I can find no other reference to
HAS_VSS_SDK in the series, second, CONFIG_QGA_VSS would be available
directly as a macro to source files.


> +qga-obj-y += vss-win32-provider/
> +qga-prv-obj-y += vss-win32-provider/
> +endif

So we're probably just saying "look into
vss-win32-provider/Makefile.objs for further object files", for both
variables.


> diff --git a/qga/vss-win32-provider/Makefile.objs b/qga/vss-win32-provider/Makefile.objs
> new file mode 100644
> index 0000000..1fe1f8f
> --- /dev/null
> +++ b/qga/vss-win32-provider/Makefile.objs
> @@ -0,0 +1,24 @@
> +# rules to build qga-provider.dll
> +
> +qga-obj-y += qga-provider.dll
> +qga-prv-obj-y += provider.o install.o
> +
> +obj-qga-prv-obj-y = $(addprefix $(obj)/, $(qga-prv-obj-y))
> +$(obj-qga-prv-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-provider.dll: LDFLAGS = -shared -Wl,--add-stdcall-alias,--enable-stdcall-fixup -lole32 -loleaut32 -lshlwapi -luuid -static
> +$(obj)/qga-provider.dll: $(obj-qga-prv-obj-y) $(SRC_PATH)/$(obj)/qga-provider.def $(obj)/qga-provider.tlb
> +	$(call quiet-command,$(CXX) -o $@ $(qga-prv-obj-y) $(SRC_PATH)/qga/vss-win32-provider/qga-provider.def $(CXXFLAGS) $(LDFLAGS),"  LINK  $(TARGET_DIR)$@")

I'm having a hard time following this.

Looks like "qga-prv-obj-y" consists of nothing more than "provider.o"
and "install.o", and that "qga-provider.dll" depends on them. In theory,
would it be possible *not* to introduce "qga-prv-obj-y" in the
higher-level Makefile.obj files, only here?

Why does "qga-provider.dll" depend on ""qga-provider.tlb"? It is not
used in the link command.

Also, why is it necessary to collect the files "provider.o", "install.o"
and "qga-provider.def" into a DLL, and link qemu-ga.exe against that DLL
(via qga-obj-y)? Is this a VSS requirement? Can't we simply link these
objects into qemu-ga.exe statically, like the rest of "qga-obj-y"?

Will some VSS service load the provider DLL independently of
qemu-ga.exe? If so, (a) how will they communicate, (b) shouldn't
"qga-obj-y" be independent of "qga-provider.dll" (and that would explain
the high-level introduction of "qga-prv-obj-y")?


> +
> +
> +# 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-provider.tlb: $(SRC_PATH)/$(obj)/qga-provider.idl
> +ifeq ($(wildcard $(SRC_PATH)/$(obj)/qga-provider.tlb),)
> +	$(call quiet-command,$(MIDL) -tlb $@ -I $(WIN_SDK)/Include $<,"  MIDL  $(TARGET_DIR)$@")
> +else
> +	$(call quiet-command,cp $(dir $<)qga-provider.tlb $@, "  COPY  $(TARGET_DIR)$@")
> +endif

Could you please draw a dependency graph between these files? Like

  idl --> tlb
  def --> dll <-- (provider.o, install.o)

Does one of "tlb" and "dll" depend on the other, or are they needed "in
parallel"?

... I'll try to continue here. I've peaked forward a little bit, and
CQGAVssProvider::CommitSnapshots() seems to be the central method. It
kicks the "frozen" event, then blocks until the "thaw" event is kicked
back. I wonder how those will translate to QMP communication with
libvirt.

Thanks!
Laszlo


>  qga/vss-win32-provider.h                |   26 ++
>  qga/vss-win32-provider/install.cpp      |  493 +++++++++++++++++++++++++++++++
>  qga/vss-win32-provider/provider.cpp     |  479 ++++++++++++++++++++++++++++++
>  qga/vss-win32-provider/qga-provider.def |   10 +
>  qga/vss-win32-provider/qga-provider.idl |   20 +
>  qga/vss-win32-provider/qga-provider.tlb |  Bin
>  qga/vss-win32.h                         |   86 +++++
>  12 files changed, 1151 insertions(+), 2 deletions(-)
>  create mode 100644 qga/vss-win32-provider.h
>  create mode 100644 qga/vss-win32-provider/install.cpp
>  create mode 100644 qga/vss-win32-provider/provider.cpp
>  create mode 100644 qga/vss-win32-provider/qga-provider.def
>  create mode 100644 qga/vss-win32-provider/qga-provider.idl
>  create mode 100644 qga/vss-win32-provider/qga-provider.tlb
>  create mode 100644 qga/vss-win32.h
>

> diff --git a/qga/vss-win32-provider.h b/qga/vss-win32-provider.h
> new file mode 100644
> index 0000000..a437e71
> --- /dev/null
> +++ b/qga/vss-win32-provider.h
> @@ -0,0 +1,26 @@
> +/*
> + * QEMU Guest Agent win32 VSS provider 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_PROVIDER_H
> +#define VSS_WIN32_PROVIDER_H
> +
> +#include <windows.h>
> +
> +STDAPI VSSCheckOSVersion(void);
> +
> +STDAPI COMRegister(void);
> +STDAPI COMUnregister(void);
> +
> +STDAPI DllRegisterServer(void);
> +STDAPI DllUnregisterServer(void);
> +
> +#endif

> diff --git a/qga/vss-win32-provider/install.cpp b/qga/vss-win32-provider/install.cpp
> new file mode 100644
> index 0000000..d6f1d55
> --- /dev/null
> +++ b/qga/vss-win32-provider/install.cpp
> @@ -0,0 +1,493 @@
> +/*
> + * 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-win32.h"
> +#include "inc/win2003/vscoordint.h"
> +#include "../vss-win32-provider.h"
> +
> +#include <comadmin.h>
> +#include <wbemidl.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} };
> +
> +static void errmsg(DWORD err, const char *text)
> +{
> +    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);
> +    printf("%.*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\n", 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)
> +
> +template<class T>
> +HRESULT put_Value(ICatalogObject *pObj, LPCWSTR name, T val)
> +{
> +    return pObj->put_Value(_bstr_t(name), _variant_t(val));
> +}
> +
> +/* Check whether this OS version supports VSS providers */
> +STDAPI VSSCheckOSVersion(void)
> +{
> +    OSVERSIONINFO OSver;
> +
> +    OSver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
> +    GetVersionEx(&OSver);
> +    if ((OSver.dwMajorVersion == 5 && OSver.dwMinorVersion >= 2) ||
> +       OSver.dwMajorVersion > 5) {
> +        return S_OK;
> +    }
> +    return S_FALSE;
> +}
> +
> +/* Lookup Administrators group name from winmgmt */
> +static HRESULT GetAdminName(_bstr_t &name)
> +{
> +    HRESULT hr;
> +    IWbemLocator *pLoc = NULL;
> +    IWbemServices *pSvc = NULL;
> +    IEnumWbemClassObject *pEnum = NULL;
> +    IWbemClassObject *pWobj = NULL;
> +    ULONG returned;
> +    _variant_t var;
> +
> +    chk(CoCreateInstance(CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER,
> +                         IID_IWbemLocator, (LPVOID *)&pLoc));
> +    chk(pLoc->ConnectServer(_bstr_t(L"ROOT\\CIMV2"), NULL, NULL, NULL,
> +                            0, 0, 0, &pSvc));
> +    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));
> +    if (!pEnum) {
> +        errmsg(E_FAIL, "Failed to query for Administrators");
> +        goto out;
> +    }
> +    chk(pEnum->Next(WBEM_INFINITE, 1, &pWobj, &returned));
> +    if (returned == 0) {
> +        errmsg(E_FAIL, "Failed to query for Administrators");
> +        goto out;
> +    }
> +
> +    chk(pWobj->Get(_bstr_t(L"Name"), 0, &var, 0, 0));
> +    name = var;
> +out:
> +    if (pLoc) {
> +        pLoc->Release();
> +    }
> +    if (pSvc) {
> +        pSvc->Release();
> +    }
> +    if (pEnum) {
> +        pEnum->Release();
> +    }
> +    if (pWobj) {
> +        pWobj->Release();
> +    }
> +    return hr;
> +}
> +
> +/* Register this module to COM+ Applications Catalog */
> +STDAPI COMRegister(void)
> +{
> +    HRESULT hr = E_FAIL;
> +    IUnknown *pUnknown = NULL;
> +    ICOMAdminCatalog *pCatalog = NULL;
> +    ICatalogCollection *pApps = NULL, *pRoles = NULL, *pUsersInRole = NULL;
> +    ICatalogObject *pObj = NULL;
> +    long n;
> +    _bstr_t name;
> +    _variant_t key;
> +    CHAR dllPath[MAX_PATH], tlbPath[MAX_PATH];
> +
> +    if (!g_hinstDll) {
> +        errmsg(E_FAIL, "Failed to initialize DLL");
> +        goto out;
> +    }
> +
> +    if (VSSCheckOSVersion() == S_FALSE) {
> +        printf("VSS provider is not supported in this OS version.\n");
> +        return S_FALSE; /* VSS feature is disabled */
> +    }
> +
> +    COMUnregister();
> +
> +    chk(CoInitialize(NULL));
> +    chk(CoCreateInstance(CLSID_COMAdminCatalog, NULL, CLSCTX_INPROC_SERVER,
> +                         IID_IUnknown, (void **)&pUnknown));
> +    chk(pUnknown->QueryInterface(IID_ICOMAdminCatalog, (void **)&pCatalog));
> +
> +    /* Install COM+ Component */
> +
> +    chk(pCatalog->GetCollection(_bstr_t(L"Applications"),
> +                                (IDispatch **)&pApps));
> +    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));
> +    chk(pObj->get_Key(&key));
> +
> +    pObj->Release();
> +    pObj = NULL;
> +
> +    if (!GetModuleFileName(g_hinstDll, dllPath, sizeof(dllPath))) {
> +        errmsg(GetLastError(), "GetModuleFileName failed");
> +        goto out;
> +    }
> +    n = strlen(dllPath);
> +    if (n < 3) {
> +        errmsg(E_FAIL, "Failed to lookup dll");
> +    }
> +    strcpy(tlbPath, dllPath);
> +    strcpy(tlbPath+n-3, "TLB");
> +    printf("Registering " QGA_PROVIDER_NAME ":\n");
> +    printf("  %s\n", dllPath);
> +    printf("  %s\n", tlbPath);
> +    if (!PathFileExists(tlbPath)) {
> +        errmsg(ERROR_FILE_NOT_FOUND, "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));
> +    chk(pRoles->Populate());
> +    chk(pRoles->Add((IDispatch **)&pObj));
> +    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));
> +
> +    pObj->Release();
> +    pObj = NULL;
> +
> +    /* Setup users in the role */
> +
> +    chk(pRoles->GetCollection(_bstr_t(L"UsersInRole"), key,
> +                              (IDispatch **)&pUsersInRole));
> +    chk(pUsersInRole->Populate());
> +
> +    chk(pUsersInRole->Add((IDispatch **)&pObj));
> +    chk(GetAdminName(name));
> +    chk(put_Value(pObj, L"User", _bstr_t(".\\") + name));
> +
> +    pObj->Release();
> +    pObj = NULL;
> +
> +    chk(pUsersInRole->Add((IDispatch **)&pObj));
> +    chk(put_Value(pObj, L"User", L"SYSTEM"));
> +    chk(pUsersInRole->SaveChanges(&n));
> +
> +out:
> +    if (pUnknown) {
> +        pUnknown->Release();
> +    }
> +    if (pCatalog) {
> +        pCatalog->Release();
> +    }
> +    if (pApps) {
> +        pApps->Release();
> +    }
> +    if (pRoles) {
> +        pRoles->Release();
> +    }
> +    if (pUsersInRole) {
> +        pUsersInRole->Release();
> +    }
> +    if (pObj) {
> +        pObj->Release();
> +    }
> +
> +    if (FAILED(hr)) {
> +        COMUnregister();
> +    }
> +    CoUninitialize();
> +
> +    return hr;
> +}
> +
> +/* Unregister this module from COM+ Applications Catalog */
> +STDAPI COMUnregister(void)
> +{
> +    HRESULT hr;
> +    IUnknown *pUnknown = NULL;
> +    ICOMAdminCatalog *pCatalog = NULL;
> +    ICatalogCollection *pColl = NULL;
> +    ICatalogObject *pObj = NULL;
> +    _variant_t var;
> +    long i, n;
> +
> +    if (VSSCheckOSVersion() == S_FALSE) {
> +        printf("VSS provider is not supported in this OS version.\n");
> +        return S_FALSE; /* VSS feature is disabled */
> +    }
> +
> +    chk(DllUnregisterServer());
> +
> +    chk(CoInitialize(NULL));
> +    chk(CoCreateInstance(CLSID_COMAdminCatalog, NULL, CLSCTX_INPROC_SERVER,
> +                         IID_IUnknown, (void **)&pUnknown));
> +    chk(pUnknown->QueryInterface(IID_ICOMAdminCatalog, (void **)&pCatalog));
> +
> +    chk(pCatalog->GetCollection(_bstr_t(L"Applications"),
> +                                (IDispatch **)&pColl));
> +    chk(pColl->Populate());
> +
> +    chk(pColl->get_Count(&n));
> +    for (i = n - 1; i >= 0; i--) {
> +        chk(pColl->get_Item(i, (IDispatch **)&pObj));
> +        chk(pObj->get_Value(_bstr_t(L"Name"), &var));
> +        if (var == _variant_t(QGA_PROVIDER_LNAME)) {
> +            printf("Removing COM+ Application: %S\n", (wchar_t *)_bstr_t(var));
> +            chk(pColl->Remove(i));
> +        }
> +    }
> +    chk(pColl->SaveChanges(&n));
> +
> +out:
> +    if (pUnknown) {
> +        pUnknown->Release();
> +    }
> +    if (pCatalog) {
> +        pCatalog->Release();
> +    }
> +    if (pColl) {
> +        pColl->Release();
> +    }
> +    if (pObj) {
> +        pObj->Release();
> +    }
> +    CoUninitialize();
> +
> +    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 = (lstrlen(data) + 1) * sizeof(TCHAR);
> +    } else {
> +        size = 0;
> +    }
> +
> +    ret = RegSetValueEx(hKey, value, 0, REG_SZ, (LPBYTE)data, size);
> +    RegCloseKey(hKey);
> +
> +out:
> +    if (ret != ERROR_SUCCESS) {
> +        /* We cannot printf here, and need MessageBox to report an error. */
> +        errmsg_dialog(ret, "Cannot add registry ", key);
> +        return FALSE;
> +    }
> +    return TRUE;
> +}
> +
> +/* Register this dll as a VSS provider */
> +STDAPI DllRegisterServer(void)
> +{
> +    IVssAdmin *pVssAdmin = NULL;
> +    HRESULT hr = E_FAIL;
> +    char dllPath[MAX_PATH];
> +    char key[256];
> +
> +    CoInitialize(NULL);
> +
> +    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;
> +    }
> +
> +    sprintf(key, "CLSID\\%s\\InprocServer32", g_szClsid);
> +    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);
> +    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");
> +        goto out;
> +    }
> +
> +out:
> +    if (pVssAdmin) {
> +        pVssAdmin->Release();
> +    }
> +    CoUninitialize();
> +
> +    if (FAILED(hr)) {
> +        DllUnregisterServer();
> +    }
> +
> +    return hr;
> +}
> +
> +/* Unregister this VSS hardware provider from the system */
> +STDAPI DllUnregisterServer(void)
> +{
> +    TCHAR key[256];
> +    IVssAdmin *pVssAdmin = NULL;
> +
> +    CoInitialize(NULL);
> +
> +    HRESULT hr = CoCreateInstance(CLSID_VSSCoordinator,
> +         NULL, CLSCTX_ALL, IID_IVssAdmin, (void **)&pVssAdmin);
> +    if (SUCCEEDED(hr)) {
> +        hr = pVssAdmin->UnregisterProvider(g_gProviderId);
> +        pVssAdmin->Release();
> +    } else {
> +        errmsg_dialog(hr, "CoCreateInstance(VSSCoordinator) failed");
> +    }
> +
> +    sprintf(key, "CLSID\\%s", g_szClsid);
> +    SHDeleteKey(HKEY_CLASSES_ROOT, key);
> +    SHDeleteKey(HKEY_CLASSES_ROOT, g_szProgid);
> +
> +    CoUninitialize();
> +
> +    return S_OK; /* Uninstall should never fail */
> +}
> +
> +
> +/* Support functions for _bstr_t in MinGW: Originally written by: Diaa Sami */
> +
> +void __stdcall _com_issue_error(HRESULT hr)
> +{
> +    printf("_com_issue_error() called with parameter HRESULT = %lu", hr);
> +}
> +
> +namespace _com_util
> +{
> +    char * __stdcall ConvertBSTRToString(BSTR bstr)
> +    {
> +        const unsigned int stringLength = lstrlenW(bstr);
> +        char *const ascii = new char [stringLength + 1];
> +
> +        wcstombs(ascii, bstr, stringLength + 1);
> +
> +        return ascii;
> +    }
> +
> +    BSTR __stdcall ConvertStringToBSTR(const char *const ascii)
> +    {
> +        const unsigned int stringLength = lstrlenA(ascii);
> +        BSTR bstr = SysAllocStringLen(NULL, stringLength);
> +
> +        mbstowcs(bstr, ascii, stringLength + 1);
> +
> +        return bstr;
> +    }
> +}

> diff --git a/qga/vss-win32-provider/provider.cpp b/qga/vss-win32-provider/provider.cpp
> new file mode 100644
> index 0000000..90a3d25
> --- /dev/null
> +++ b/qga/vss-win32-provider/provider.cpp
> @@ -0,0 +1,479 @@
> +/*
> + * 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-win32.h"
> +#include "inc/win2003/vscoordint.h"
> +#include "inc/win2003/vsprov.h"
> +
> +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 block)
> +{
> +    if (block) {
> +        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;
> +    } else if (riid == IID_IVssSoftwareSnapshotProvider) {
> +        *ppObj = static_cast<void*>(
> +            static_cast<IVssSoftwareSnapshotProvider*>(this));
> +        AddRef();
> +        return S_OK;
> +    } else if (riid == IID_IVssProviderCreateSnapshotSet) {
> +        *ppObj = static_cast<void*>(
> +            static_cast<IVssProviderCreateSnapshotSet*>(this));
> +        AddRef();
> +        return S_OK;
> +    } else 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)
> +{
> +    *ppEnum = new CQGAVSSEnumObject;
> +    (*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)
> +{
> +    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 S_OK;
> +}
> +
> +
> +/*
> + * 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 hEvent, hEvent2;
> +
> +    hEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_FROZEN);
> +    if (hEvent == INVALID_HANDLE_VALUE) {
> +        hr = E_FAIL;
> +        goto out;
> +    }
> +
> +    hEvent2 = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_THAW);
> +    if (hEvent == INVALID_HANDLE_VALUE) {
> +        CloseHandle(hEvent);
> +        hr = E_FAIL;
> +        goto out;
> +    }
> +
> +    SetEvent(hEvent);
> +    if (WaitForSingleObject(hEvent2, 60*1000) != WAIT_OBJECT_0) {
> +        hr = E_ABORT;
> +    }
> +
> +    CloseHandle(hEvent2);
> +    CloseHandle(hEvent);
> +out:
> +    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 bLock) { return E_NOTIMPL; }
> +private:
> +    long m_nRefCount;
> +};
> +
> +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()
> +{
> +    LockModule(TRUE);
> +    return 2;
> +}
> +
> +STDMETHODIMP_(ULONG) CQGAVssProviderFactory::Release()
> +{
> +    LockModule(FALSE);
> +    return 1;
> +}
> +
> +STDMETHODIMP CQGAVssProviderFactory::CreateInstance(
> +    IUnknown *pUnknownOuter, REFIID iid, void **ppv)
> +{
> +    if (pUnknownOuter) {
> +        return CLASS_E_NOAGGREGATION;
> +    }
> +    CQGAVssProvider *pObj = new CQGAVssProvider;
> +    if (!pObj) {
> +        return E_OUTOFMEMORY;
> +    }
> +    return pObj->QueryInterface(iid, ppv);
> +}
> +
> +
> +/*
> + * DLL functions
> + */
> +
> +STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
> +{
> +    static CQGAVssProviderFactory factory;
> +
> +    *ppv = NULL;
> +    if (IsEqualCLSID(rclsid, CLSID_QGAVSSProvider)) {
> +        return factory.QueryInterface(riid, ppv);
> +    }
> +    return CLASS_E_CLASSNOTAVAILABLE;
> +}
> +
> +STDAPI DllCanUnloadNow()
> +{
> +    return g_nComObjsInUse == 0 ? S_OK : S_FALSE;
> +}
> +
> +EXTERN_C
> +BOOL WINAPI DllMain(HINSTANCE hinstDll, DWORD dwReason, LPVOID lpReserved)
> +{
> +    switch (dwReason) {
> +
> +    case DLL_PROCESS_ATTACH:
> +        g_hinstDll = hinstDll;
> +        DisableThreadLibraryCalls(hinstDll);
> +        break;
> +    }
> +
> +    return TRUE;
> +}

> diff --git a/qga/vss-win32-provider/qga-provider.def b/qga/vss-win32-provider/qga-provider.def
> new file mode 100644
> index 0000000..9f3afc8
> --- /dev/null
> +++ b/qga/vss-win32-provider/qga-provider.def
> @@ -0,0 +1,10 @@
> +LIBRARY      "QGA-PROVIDER.DLL"
> +
> +EXPORTS
> +	VSSCheckOSVersion	PRIVATE
> +	COMRegister		PRIVATE
> +	COMUnregister		PRIVATE
> +	DllCanUnloadNow		PRIVATE
> +	DllGetClassObject	PRIVATE
> +	DllRegisterServer	PRIVATE
> +	DllUnregisterServer	PRIVATE

> diff --git a/qga/vss-win32-provider/qga-provider.idl b/qga/vss-win32-provider/qga-provider.idl
> new file mode 100644
> index 0000000..17abca0
> --- /dev/null
> +++ b/qga/vss-win32-provider/qga-provider.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-provider/qga-provider.tlb b/qga/vss-win32-provider/qga-provider.tlb
> new file mode 100644
> index 0000000000000000000000000000000000000000..226452a1861371ffe0cad1019cf90fdfdcd5ef49
> GIT binary patch
> literal 1528
> zcmeYbb_-!*U}OLRP8Kl5;0UB3A_y8H!@$4<WGF*9|A9aP$W{R21|SCUVfs9Pj1;IC
> zKahR`)X0Ox{{ZC6An~sN`2tA%H9-9hNPHcj{0byK4>OPh6a(1_GM|T)fx!kz-UG<D
> zK;nbc0l9GX===tt`Vb`fD?q*q5+7YXI$u?Zf#C;G4-9~uhYKVCC4f!`hZ{(Z0*HVD
> zkhwswGqAt}ki;hd*$F@lQbP%-z+r|5P#hGW*vvKniaRx03p~wP?y>h_rLW<nKOg@=
> z6{hYgzgH7@QE<^MU>KC!yoBjb#vz`9Lwu4+R-SJ!kIOX4xLBUUGN9-NyTyP76j}@n
> z2f!qQ8-xepfXD+7rW+{SKz4&@7#qX~^1#sn3O|tFK>%ciE<<riN`6kNkzPqoQh0bc
> zNbM*XJ|Un0jALSb15^rEE6h+Y8tCpA798vm9#E8DmYI@T<dc~c4pSpwQKEn@FU<fE
> zfvHyrsVqoU0O~4AEUE;iEfI8i=bXgi;_z?|20Ng!&PAz-C8;S2NtFt|o-RHLWvNBQ
> znfZAN=6VJOdIqMZrV5EA3T{Q23NES13Py$shQ?OLW>&_Q3PuKoMqI)S5zj9Ngog_=
> gXfrXehlhjmFbIJB4$8MKU>*YlD1Z9^F{m5{03Vre%>V!Z
>
> literal 0
> HcmV?d00001
>

> diff --git a/qga/vss-win32.h b/qga/vss-win32.h
> new file mode 100644
> index 0000000..21655cf
> --- /dev/null
> +++ b/qga/vss-win32.h
> @@ -0,0 +1,86 @@
> +/*
> + * 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"
> +
> +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
> +};
> +
> +#endif
>
>

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze
  2013-06-25 16:03   ` Laszlo Ersek
@ 2013-06-25 16:19     ` Paolo Bonzini
  2013-06-25 18:23     ` Tomoki Sekiyama
  2013-06-26 14:32     ` Laszlo Ersek
  2 siblings, 0 replies; 70+ messages in thread
From: Paolo Bonzini @ 2013-06-25 16:19 UTC (permalink / raw)
  To: Laszlo Ersek
  Cc: libaiqing, mdroth, stefanha, qemu-devel, lcapitulino, vrozenfe,
	Tomoki Sekiyama, seiji.aguchi, areis

Il 25/06/2013 18:03, Laszlo Ersek ha scritto:
>> > @@ -113,6 +114,7 @@ nested-vars += \
>> >  	stub-obj-y \
>> >  	util-obj-y \
>> >  	qga-obj-y \
>> > +	qga-prv-obj-y \
>> >  	block-obj-y \
>> >  	common-obj-y
>> >  dummy := $(call unnest-vars)
> What does this do? Does "qga-prv-obj-y" stand for "all objects under
> 'qga'"?
> 
> Or does it mean "look into qga/Makefile.objs for further objects"?

The latter.  I'd rather have it spelled fully, though
(qga-win32-provider-obj-y).

> > +qga-obj-y += vss-win32-provider/
> > +qga-prv-obj-y += vss-win32-provider/
> > +endif
> 
> So we're probably just saying "look into
> vss-win32-provider/Makefile.objs for further object files", for both
> variables.

Yes.

> Looks like "qga-prv-obj-y" consists of nothing more than "provider.o"
> and "install.o", and that "qga-provider.dll" depends on them. In theory,
> would it be possible *not* to introduce "qga-prv-obj-y" in the
> higher-level Makefile.obj files, only here?

No, you need to specify it at all levels.

> Why does "qga-provider.dll" depend on ""qga-provider.tlb"? It is not
> used in the link command.
> 
> Also, why is it necessary to collect the files "provider.o", "install.o"
> and "qga-provider.def" into a DLL, and link qemu-ga.exe against that DLL
> (via qga-obj-y)? Is this a VSS requirement? Can't we simply link these
> objects into qemu-ga.exe statically, like the rest of "qga-obj-y"?

I think dynamic linking is needed because VSS is loading the provider as
a COM component here:

+            hr = pVssbc->AddToSnapshotSet(buf, g_gProviderId, &pid);

But I don't know why you need to link the DLL to qemu-ga.exe.

Paolo

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze
  2013-06-25 16:03   ` Laszlo Ersek
  2013-06-25 16:19     ` Paolo Bonzini
@ 2013-06-25 18:23     ` Tomoki Sekiyama
  2013-06-25 18:59       ` Paolo Bonzini
  2013-06-25 19:52       ` Laszlo Ersek
  2013-06-26 14:32     ` Laszlo Ersek
  2 siblings, 2 replies; 70+ messages in thread
From: Tomoki Sekiyama @ 2013-06-25 18:23 UTC (permalink / raw)
  To: Laszlo Ersek
  Cc: libaiqing, mdroth, stefanha, qemu-devel, lcapitulino, vrozenfe,
	pbonzini, Seiji Aguchi, areis

Thanks for your review.

On 6/25/13 12:03 , "Laszlo Ersek" <lersek@redhat.com> wrote:
>I'm trying to wrap my head around the build changes first.

>> diff --git a/configure b/configure
>> index cafe830..8e45fad 100755
>> --- a/configure
>> +++ b/configure
>> @@ -3490,9 +3490,12 @@ if test "$softmmu" = yes ; then
>>        virtfs=no
>>      fi
>>    fi
>> -  if [ "$linux" = "yes" -o "$bsd" = "yes" -o "$solaris" = "yes" ] ;
>>then
>> +  if [ "$linux" = "yes" -o "$bsd" = "yes" -o "$solaris" = "yes" -o
>>"$mingw32" = "yes" ] ; then
>>      if [ "$guest_agent" = "yes" ]; then
>>        tools="qemu-ga\$(EXESUF) $tools"
>> +      if [ "$mingw32" = "yes" ]; then
>> +        tools="qga/vss-win32-provider/qga-provider.dll
>>qga/vss-win32-provider/qga-provider.tlb $tools"
>> +      fi
>>      fi
>>    fi
>>  fi
>
>This adds three targets to "tools" on mingw32 -- "qemu-ga" hasn't been
>there until now. Is this actually a small, unrelated bugfix?

That is true, it is not necessary to build qemu-ga.exe.
The intention was to do everything by just `make`.

>I'm asking because I build upstream qemu-ga.exe on my RHEL-6 laptop as
>follows -- I don't have pixman installed, and configure forces me to
>specify --disable-tools as well:
> 
>$ ./configure --without-pixman --disable-system --disable-tools \
>      --cross-prefix=i686-pc-mingw32-
>$ make qemu-ga.exe
>
>Plain "make" after the above ./configure doesn't produce anything
>useful. Will I have to specify the DLL and TLB targets too on the
>command line? (Apologies, I can't try it out now, msiextract doesn't
>work for me.)

You don't have to specify DLL and TLB targets because qemu-ga.exe
has dependencies to them.

>> diff --git a/Makefile.objs b/Makefile.objs
>> index 286ce06..b6c1505 100644
>> --- a/Makefile.objs
>> +++ b/Makefile.objs
>> @@ -102,6 +102,7 @@ common-obj-y += disas/
>>  # 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-prv-obj-y = qga/
>>
>>  vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS)
>>
>> @@ -113,6 +114,7 @@ nested-vars += \
>>  	stub-obj-y \
>>  	util-obj-y \
>>  	qga-obj-y \
>> +	qga-prv-obj-y \
>>  	block-obj-y \
>>  	common-obj-y
>>  dummy := $(call unnest-vars)
>
>What does this do? Does "qga-prv-obj-y" stand for "all objects under
>'qga'"?

No, these are objects for qga/vss-win32-provider/, used to build
qga-provider.dll. It looks better to be fully spelled as Paolo
said in another mail.


>Or does it mean "look into qga/Makefile.objs for further objects"?

Yes...

>>diff --git a/qga/Makefile.objs b/qga/Makefile.objs
>> index b8d7cd0..8d93866 100644
>> --- a/qga/Makefile.objs
>> +++ b/qga/Makefile.objs
>> @@ -3,3 +3,9 @@ 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-y += qapi-generated/qga-qapi-types.o
>>qapi-generated/qga-qapi-visit.o
>>  qga-obj-y += qapi-generated/qga-qmp-marshal.o
>> +
>> +ifeq ($(CONFIG_QGA_VSS),y)
>> +QEMU_CFLAGS += -DHAS_VSS_SDK
>
>Isn't this superfluous? First, I can find no other reference to
>HAS_VSS_SDK in the series, second, CONFIG_QGA_VSS would be available
>directly as a macro to source files.

It is referenced in e.g. qga/commands-win32.c in patch 8/10.
But as you say, I will omit this, by the second reason.

>> +qga-obj-y += vss-win32-provider/
>> +qga-prv-obj-y += vss-win32-provider/
>> +endif
>
>So we're probably just saying "look into
>vss-win32-provider/Makefile.objs for further object files", for both
>variables.

Yes...

>> diff --git a/qga/vss-win32-provider/Makefile.objs
>>b/qga/vss-win32-provider/Makefile.objs
>> new file mode 100644
>> index 0000000..1fe1f8f
>> --- /dev/null
>> +++ b/qga/vss-win32-provider/Makefile.objs
>> @@ -0,0 +1,24 @@
>> +# rules to build qga-provider.dll
>> +
>> +qga-obj-y += qga-provider.dll
>> +qga-prv-obj-y += provider.o install.o
>> +
>> +obj-qga-prv-obj-y = $(addprefix $(obj)/, $(qga-prv-obj-y))
>> +$(obj-qga-prv-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-provider.dll: LDFLAGS = -shared
>>-Wl,--add-stdcall-alias,--enable-stdcall-fixup -lole32 -loleaut32
>>-lshlwapi -luuid -static
>> +$(obj)/qga-provider.dll: $(obj-qga-prv-obj-y)
>>$(SRC_PATH)/$(obj)/qga-provider.def $(obj)/qga-provider.tlb
>> +	$(call quiet-command,$(CXX) -o $@ $(qga-prv-obj-y)
>>$(SRC_PATH)/qga/vss-win32-provider/qga-provider.def $(CXXFLAGS)
>>$(LDFLAGS),"  LINK  $(TARGET_DIR)$@")
>
>I'm having a hard time following this.
>
>Looks like "qga-prv-obj-y" consists of nothing more than "provider.o"
>and "install.o", and that "qga-provider.dll" depends on them. In theory,
>would it be possible *not* to introduce "qga-prv-obj-y" in the
>higher-level Makefile.obj files, only here?

Unfortunately, we need this at all levels to handle nested directories
correctly, otherwise it fails on out-tree build.

>Why does "qga-provider.dll" depend on ""qga-provider.tlb"? It is not
>used in the link command.

This was because .tlb is required to install .dll correctly.
But it is not dependent on build time, so shouldn't be specified here.

>Also, why is it necessary to collect the files "provider.o", "install.o"
>and "qga-provider.def" into a DLL, and link qemu-ga.exe against that DLL
>(via qga-obj-y)? Is this a VSS requirement? Can't we simply link these
>objects into qemu-ga.exe statically, like the rest of "qga-obj-y"?
>
>Will some VSS service load the provider DLL independently of
>qemu-ga.exe?

Yes, VSS provider DLL is registered to VSS on installation of qemu-ga
service (using .TLB), and loaded into VSS service component when
snapshot is started.

>If so, (a) how will they communicate

Using COM interface.

>(b) shouldn't "qga-obj-y" be independent of "qga-provider.dll"

The DLL is linked to qemu-ga.exe because qemu-ga.exe kicks the
registration of the DLL (COMRegister(), which depends on the DLL's
Internal variable) at the installation of the qemu-ga service.

>Could you please draw a dependency graph between these files? Like
>
>  idl --> tlb
>  def --> dll <-- (provider.o, install.o)
> 
> Does one of "tlb" and "dll" depend on the other, or are they needed "in
> parallel"?

This graph is describing build dependencies correctly.
At runtime, DLL needs TLB, otherwise it cannot be loaded into VSS.

>... I'll try to continue here. I've peaked forward a little bit, and
>CQGAVssProvider::CommitSnapshots() seems to be the central method. It
>kicks the "frozen" event, then blocks until the "thaw" event is kicked
>back. I wonder how those will translate to QMP communication with
>libvirt.

CommitSnapshots() is called by VSS, when the requestor of qemu-ga
(introduced in next patch) initiates snapshot receiving
"guest-fsfreeze-freeze" command.
"frozen" event is used to tell qemu-ga.exe that the fs is frozen,
then qemu-ga.exe will tell libvirt to "guest-fsfreeze-freeze" is done.

"thaw" event is kicked by qemnu-ga.exe when it receives
"guest-fsfreeze-thaw" command. The command is finished when VSS notify
qeum-ga.exe requestor that VSS snapshot process is completed.

>Thanks!
>Laszlo

Again, thank you for taking your time for this!

Thanks,
Tomoki Sekiyama

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze
  2013-06-25 18:23     ` Tomoki Sekiyama
@ 2013-06-25 18:59       ` Paolo Bonzini
  2013-06-25 19:28         ` Tomoki Sekiyama
  2013-06-25 19:52       ` Laszlo Ersek
  1 sibling, 1 reply; 70+ messages in thread
From: Paolo Bonzini @ 2013-06-25 18:59 UTC (permalink / raw)
  To: Tomoki Sekiyama
  Cc: libaiqing, qemu-devel, stefanha, mdroth, lcapitulino, vrozenfe,
	Seiji Aguchi, Laszlo Ersek, areis

Il 25/06/2013 20:23, Tomoki Sekiyama ha scritto:
>> >(b) shouldn't "qga-obj-y" be independent of "qga-provider.dll"
> The DLL is linked to qemu-ga.exe because qemu-ga.exe kicks the
> registration of the DLL (COMRegister(), which depends on the DLL's
> Internal variable) at the installation of the qemu-ga service.
> 

Does the DLL also support DllRegisterServer?  I don't know Windows very
much, but if so I wonder if LoadLibrary/GetProcAddress is preferrable to
linking with the DLL.

Paolo

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze
  2013-06-25 18:59       ` Paolo Bonzini
@ 2013-06-25 19:28         ` Tomoki Sekiyama
  0 siblings, 0 replies; 70+ messages in thread
From: Tomoki Sekiyama @ 2013-06-25 19:28 UTC (permalink / raw)
  To: Paolo Bonzini
  Cc: libaiqing, qemu-devel, stefanha, mdroth, lcapitulino, vrozenfe,
	Seiji Aguchi, Laszlo Ersek, areis

>Il 25/06/2013 20:23, Tomoki Sekiyama ha scritto:
>>> >(b) shouldn't "qga-obj-y" be independent of "qga-provider.dll"
>> The DLL is linked to qemu-ga.exe because qemu-ga.exe kicks the
>> registration of the DLL (COMRegister(), which depends on the DLL's
>> Internal variable) at the installation of the qemu-ga service.
> 
> Does the DLL also support DllRegisterServer?  I don't know Windows very
> much, but if so I wonder if LoadLibrary/GetProcAddress is preferrable to
> linking with the DLL.
> 
> Paolo

I think that is feasible.

That will cut off qemu-ga.exe from qga-provider.{dll,tlb}, so then,
we should build them separately, like
`make qemu-ga.exe qga/vss-win32-provider/qga-provider.{dll,tlb}`.

Thanks,
Tomoki Sekiyama

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze
  2013-06-25 18:23     ` Tomoki Sekiyama
  2013-06-25 18:59       ` Paolo Bonzini
@ 2013-06-25 19:52       ` Laszlo Ersek
  1 sibling, 0 replies; 70+ messages in thread
From: Laszlo Ersek @ 2013-06-25 19:52 UTC (permalink / raw)
  To: Tomoki Sekiyama
  Cc: libaiqing, mdroth, stefanha, qemu-devel, lcapitulino, vrozenfe,
	pbonzini, Seiji Aguchi, areis

On 06/25/13 20:23, Tomoki Sekiyama wrote:
> On 6/25/13 12:03 , "Laszlo Ersek" <lersek@redhat.com> wrote:

>>> +ifeq ($(CONFIG_QGA_VSS),y)
>>> +QEMU_CFLAGS += -DHAS_VSS_SDK
>>
>> Isn't this superfluous? First, I can find no other reference to
>> HAS_VSS_SDK in the series, second, CONFIG_QGA_VSS would be available
>> directly as a macro to source files.
> 
> It is referenced in e.g. qga/commands-win32.c in patch 8/10.

Ah sorry. "ack" (that I sometimes use for recursive grepping, outside of
git clones) didn't return any hits in the directory where I had saved
your patch emails (for git-am), that's why I said "no references in the
series".

Perhaps "ack" ignored the .eml files due to their CRLF line endings...
It did find the references with option "--all".

(Of course I should have noticed something was fishy, since "ack" didn't
find the very occurrence I was questioning! :))

> But as you say, I will omit this, by the second reason.

OK, thanks.

>> Will some VSS service load the provider DLL independently of
>> qemu-ga.exe?
> 
> Yes, VSS provider DLL is registered to VSS on installation of qemu-ga
> service (using .TLB), and loaded into VSS service component when
> snapshot is started.
> 
>> If so, (a) how will they communicate
> 
> Using COM interface.

[...]

>> ... I'll try to continue here. I've peaked forward a little bit, and
>> CQGAVssProvider::CommitSnapshots() seems to be the central method. It
>> kicks the "frozen" event, then blocks until the "thaw" event is kicked
>> back. I wonder how those will translate to QMP communication with
>> libvirt.
> 
> CommitSnapshots() is called by VSS, when the requestor of qemu-ga
> (introduced in next patch) initiates snapshot receiving
> "guest-fsfreeze-freeze" command.
> "frozen" event is used to tell qemu-ga.exe that the fs is frozen,
> then qemu-ga.exe will tell libvirt to "guest-fsfreeze-freeze" is done.
> 
> "thaw" event is kicked by qemnu-ga.exe when it receives
> "guest-fsfreeze-thaw" command. The command is finished when VSS notify
> qeum-ga.exe requestor that VSS snapshot process is completed.

Very interesting. I'm curious about the requestor in the next patch.

Based on the VSS description in MSDN, I thought the requestor would make
some kind of blocking call (like a normal function call) into VSS that
could not return until the full process was done (ie. filesystems were
re-thawed).

This likely isn't the case, as CommitSnapshots() appears to signal
(wake) qemu-ga.exe asynchronously -- the requestor (qemu-ga) and the
provider (the DLL in the VSS component) seem to run in parallel.

The more I think I understand of your design the more I like it.

I'll attempt to continue the review tomorrow.

Thanks!
Laszlo

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze
  2013-06-06 15:06 ` [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze Tomoki Sekiyama
  2013-06-25 16:03   ` Laszlo Ersek
@ 2013-06-25 21:15   ` Paolo Bonzini
  2013-06-25 22:31     ` Tomoki Sekiyama
  1 sibling, 1 reply; 70+ messages in thread
From: Paolo Bonzini @ 2013-06-25 21:15 UTC (permalink / raw)
  To: Tomoki Sekiyama
  Cc: libaiqing, mdroth, stefanha, qemu-devel, lcapitulino, vrozenfe,
	seiji.aguchi, areis

Il 06/06/2013 17:06, Tomoki Sekiyama ha scritto:
> +STDAPI VSSCheckOSVersion(void);
> +
> +STDAPI COMRegister(void);
> +STDAPI COMUnregister(void);
> +
> +STDAPI DllRegisterServer(void);
> +STDAPI DllUnregisterServer(void);

Can you explain the difference between COMRegister/COMUnregister and
DllRegisterServer/DllUnregisterServer (and why the COM+ part need not be
done by regsvr32)?  Also, why does COMUnregister call
DllUnregisterServer but COMRegister does not call DllRegisterServer?

Also, is it needed to call VSSCheckOSVersion from the requestor?  I
would think that checking VSSAPI.DLL is stronger than checking the
version, and indeed you do that check too.

Sorry for the probably stupid questions, COM stuff is way beyond my
Windows knowledge.

Paolo

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze
  2013-06-25 21:15   ` Paolo Bonzini
@ 2013-06-25 22:31     ` Tomoki Sekiyama
  2013-06-26  5:59       ` Paolo Bonzini
  2013-06-30 13:16       ` Gal Hammer
  0 siblings, 2 replies; 70+ messages in thread
From: Tomoki Sekiyama @ 2013-06-25 22:31 UTC (permalink / raw)
  To: Paolo Bonzini
  Cc: libaiqing, mdroth, stefanha, qemu-devel, lcapitulino, vrozenfe,
	Seiji Aguchi, areis

From: Paolo Bonzini [paolo.bonzini@gmail.com] on behalf of Paolo Bonzini [pbonzini@redhat.com]
> Il 06/06/2013 17:06, Tomoki Sekiyama ha scritto:
>> +STDAPI VSSCheckOSVersion(void);
>> +
>> +STDAPI COMRegister(void);
>> +STDAPI COMUnregister(void);
>> +
>> +STDAPI DllRegisterServer(void);
>> +STDAPI DllUnregisterServer(void);
>
> Can you explain the difference between COMRegister/COMUnregister and
> DllRegisterServer/DllUnregisterServer (and why the COM+ part need not be
> done by regsvr32)?  Also, why does COMUnregister call
> DllUnregisterServer but COMRegister does not call DllRegisterServer?

COMRegister and COMUnregister are called by`qemu-ga -s install`,
to register/unregister the DLL into/from COM+ application catalogue.

DllRegisterServer is automatically called inside
pCatalog->InstallComponent() (like regsvr32 does), and register
this DLL as a VSS provider. DllUnregisterServer will do the oposite.

ICOMAdminCatalog (pCatalog) does not provide a method to uninstall
component, so COMUnregister calls DllUnregisterServer by itself.

> Also, is it needed to call VSSCheckOSVersion from the requestor?  I
> would think that checking VSSAPI.DLL is stronger than checking the
> version, and indeed you do that check too.

In Windows XP, VSSAPI.DLL exists but it has different functionality
and interfaces from newer Windows. 
http://msdn.microsoft.com/en-us/library/windows/desktop/aa384627(v=vs.85).aspx

It is checking the OS version because this patchset only supports
Windows 2003 or later.

Thanks,
Tomoki Sekiyama

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze
  2013-06-25 22:31     ` Tomoki Sekiyama
@ 2013-06-26  5:59       ` Paolo Bonzini
  2013-06-26 14:13         ` Tomoki Sekiyama
  2013-06-30 13:16       ` Gal Hammer
  1 sibling, 1 reply; 70+ messages in thread
From: Paolo Bonzini @ 2013-06-26  5:59 UTC (permalink / raw)
  To: Tomoki Sekiyama
  Cc: libaiqing, qemu-devel, stefanha, mdroth, lcapitulino, vrozenfe,
	Seiji Aguchi, areis

Il 26/06/2013 00:31, Tomoki Sekiyama ha scritto:
> From: Paolo Bonzini [paolo.bonzini@gmail.com] on behalf of Paolo Bonzini [pbonzini@redhat.com]
>> Il 06/06/2013 17:06, Tomoki Sekiyama ha scritto:
>>> +STDAPI VSSCheckOSVersion(void);
>>> +
>>> +STDAPI COMRegister(void);
>>> +STDAPI COMUnregister(void);
>>> +
>>> +STDAPI DllRegisterServer(void);
>>> +STDAPI DllUnregisterServer(void);
>>
>> Can you explain the difference between COMRegister/COMUnregister and
>> DllRegisterServer/DllUnregisterServer (and why the COM+ part need not be
>> done by regsvr32)?  Also, why does COMUnregister call
>> DllUnregisterServer but COMRegister does not call DllRegisterServer?
> 
> COMRegister and COMUnregister are called by`qemu-ga -s install`,
> to register/unregister the DLL into/from COM+ application catalogue.
> 
> DllRegisterServer is automatically called inside
> pCatalog->InstallComponent() (like regsvr32 does), and register
> this DLL as a VSS provider. DllUnregisterServer will do the oposite.
> 
> ICOMAdminCatalog (pCatalog) does not provide a method to uninstall
> component, so COMUnregister calls DllUnregisterServer by itself.

Understood, thanks.  Just one question remains: why is the COM+ part not
needed when you invoke regsvr32?

Thanks,

Paolo

>> Also, is it needed to call VSSCheckOSVersion from the requestor?  I
>> would think that checking VSSAPI.DLL is stronger than checking the
>> version, and indeed you do that check too.
> 
> In Windows XP, VSSAPI.DLL exists but it has different functionality
> and interfaces from newer Windows. 
> http://msdn.microsoft.com/en-us/library/windows/desktop/aa384627(v=vs.85).aspx
> 
> It is checking the OS version because this patchset only supports
> Windows 2003 or later.
> 
> Thanks,
> Tomoki Sekiyama
> 

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 04/10] Add a script to extract VSS SDK headers on POSIX system
  2013-06-25 15:01     ` Paolo Bonzini
@ 2013-06-26 10:52       ` Laszlo Ersek
  0 siblings, 0 replies; 70+ messages in thread
From: Laszlo Ersek @ 2013-06-26 10:52 UTC (permalink / raw)
  To: Paolo Bonzini
  Cc: libaiqing, qemu-devel, stefanha, mdroth, lcapitulino, vrozenfe,
	Tomoki Sekiyama, seiji.aguchi, areis

On 06/25/13 17:01, Paolo Bonzini wrote:
> Il 25/06/2013 17:01, Laszlo Ersek ha scritto:
>> On 06/06/13 17:06, Tomoki Sekiyama wrote:
>>
>>> +if ! command -v msiextract > /dev/null; then
>>> +  echo 'msiextract not found. Please install msitools.' >&2
>>> +  exit 1
>>> +fi
>>
>> (This is not a review comment -- I'm trying to test it:)
>>
>> What msiextract version (and dependencies, like libgcab, libgsf etc) are
>> you using? I'm unable to extract "vsssdk.msi"; msiextract spews a bunch
>> of assertion failures.
>>
>> 2e39646b7850a12673bc66ade85fece3  setup.exe
>> 433eb024ed0c669dd1563d952ca41091  vsssdk.msi
>>
>> My versions (RHEL-6.4.z distro):
>> - msitools-0.92 (built from source)
>> - gcab-0.4 (built from source)
>> - libgsf-1.14.15-5.el6 (patch in [1] doesn't seem to help, it only
>> changes the kinds of asserts that fail)
>> - glib2-2.22.5-7.el6
>> - libuuid-2.17.2-12.9.el6_4.3
> 
> The attached patch may help building a newer libgsf on RHEL6.

It did help building it, however the runtime error has stayed. I'm
starting to worry this is a more fundamental issue, namely in glib.

The first errors I'm getting on the msiextract stderr are:

  (msiextract:28858): GLib-GObject-WARNING **: invalid cast from
  `GDataInputStream' to `GSeekable'

  (msiextract:28858): GLib-GIO-CRITICAL **: g_seekable_seek: assertion
  `G_IS_SEEKABLE (seekable)' failed

(The second clearly being a consequence of the first.)

Problem is, the first warning is "wrong". According to the current, live
glib docs <https://developer.gnome.org/gio/stable/GDataInputStream.html>,

  GDataInputStream implements GSeekable.

The NEWS file in glib states,

  Overview of changes from GLib 2.32.1 to 2.33.1
  ==============================================
  [...]
  * GIO:
   - implement GSeekable for the data and buffered stream classes

Commits:

  git log --oneline --reverse 2.32.1..2.33.1 | grep -i seekable
  90739ba Make GBufferedInputStream implement GSeekable
  43895e3 Make GBufferedOutputStream implement GSeekable
  a44e801 Make GDataOutputStream implement GSeekable

Bug: https://bugzilla.gnome.org/show_bug.cgi?id=673034

GDataOutputStream and GBufferedOutputStream are both direct descendants
of GFilterOutputStream, and they both implement the GSeekable interface
in isolation.

However GDataInputStream is derived from GBufferedInputStream (it the
former is not the sibling of the latter, as for output).
GBufferedInputStream was made to implement GSeekable explicitly, and
GDataInputStream gets it via inheritance. (In 2.33.1, that is.)

I guess I'll extract the MSI once on F19 and stash the contents...

Thanks!
Laszlo

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze
  2013-06-26  5:59       ` Paolo Bonzini
@ 2013-06-26 14:13         ` Tomoki Sekiyama
  0 siblings, 0 replies; 70+ messages in thread
From: Tomoki Sekiyama @ 2013-06-26 14:13 UTC (permalink / raw)
  To: Paolo Bonzini
  Cc: libaiqing, qemu-devel, stefanha, mdroth, lcapitulino, vrozenfe,
	Seiji Aguchi, areis

On 6/26/13 1:59 , "Paolo Bonzini" <pbonzini@redhat.com> wrote:

>Il 26/06/2013 00:31, Tomoki Sekiyama ha scritto:
>> From: Paolo Bonzini [paolo.bonzini@gmail.com] on behalf of Paolo
>>Bonzini [pbonzini@redhat.com]
>>> Il 06/06/2013 17:06, Tomoki Sekiyama ha scritto:
>>>> +STDAPI VSSCheckOSVersion(void);
>>>> +
>>>> +STDAPI COMRegister(void);
>>>> +STDAPI COMUnregister(void);
>>>> +
>>>> +STDAPI DllRegisterServer(void);
>>>> +STDAPI DllUnregisterServer(void);
>>>
>>> Can you explain the difference between COMRegister/COMUnregister and
>>> DllRegisterServer/DllUnregisterServer (and why the COM+ part need not
>>>be
>>> done by regsvr32)?  Also, why does COMUnregister call
>>> DllUnregisterServer but COMRegister does not call DllRegisterServer?
>> 
>> COMRegister and COMUnregister are called by`qemu-ga -s install`,
>> to register/unregister the DLL into/from COM+ application catalogue.
>> 
>> DllRegisterServer is automatically called inside
>> pCatalog->InstallComponent() (like regsvr32 does), and register
>> this DLL as a VSS provider. DllUnregisterServer will do the oposite.
>> 
>> ICOMAdminCatalog (pCatalog) does not provide a method to uninstall
>> component, so COMUnregister calls DllUnregisterServer by itself.
>
>Understood, thanks.  Just one question remains: why is the COM+ part not
>needed when you invoke regsvr32?

A VSS Provider is implemented as a COM+ application.

To register COM+ applications, regsvr32 is not enough.
It only register COM component, which is a part of COM+ application.
We need to use a specialized installer (COMRegister() in this series)
to the whole COM+ application to the catalogue.

Thanks,
Tomoki Sekiyama

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze
  2013-06-25 16:03   ` Laszlo Ersek
  2013-06-25 16:19     ` Paolo Bonzini
  2013-06-25 18:23     ` Tomoki Sekiyama
@ 2013-06-26 14:32     ` Laszlo Ersek
  2013-06-26 21:27       ` Paolo Bonzini
                         ` (2 more replies)
  2 siblings, 3 replies; 70+ messages in thread
From: Laszlo Ersek @ 2013-06-26 14:32 UTC (permalink / raw)
  To: Tomoki Sekiyama
  Cc: libaiqing, qemu-devel, stefanha, mdroth, lcapitulino, vrozenfe,
	pbonzini, seiji.aguchi, areis

On 06/25/13 18:03, Laszlo Ersek wrote:
> On 06/06/13 17:06, Tomoki Sekiyama wrote:

Comparing the .def file and the provider header file:

>> diff --git a/qga/vss-win32-provider/qga-provider.def b/qga/vss-win32-provider/qga-provider.def
>> new file mode 100644
>> index 0000000..9f3afc8
>> --- /dev/null
>> +++ b/qga/vss-win32-provider/qga-provider.def
>> @@ -0,0 +1,10 @@
>> +LIBRARY      "QGA-PROVIDER.DLL"
>> +
>> +EXPORTS
>> +	VSSCheckOSVersion	PRIVATE
>> +	COMRegister		PRIVATE
>> +	COMUnregister		PRIVATE
>> +	DllCanUnloadNow		PRIVATE
>> +	DllGetClassObject	PRIVATE
>> +	DllRegisterServer	PRIVATE
>> +	DllUnregisterServer	PRIVATE

>> diff --git a/qga/vss-win32-provider.h b/qga/vss-win32-provider.h
>> new file mode 100644
>> index 0000000..a437e71
>> --- /dev/null
>> +++ b/qga/vss-win32-provider.h
>> @@ -0,0 +1,26 @@
>> +/*
>> + * QEMU Guest Agent win32 VSS provider 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_PROVIDER_H
>> +#define VSS_WIN32_PROVIDER_H
>> +
>> +#include <windows.h>
>> +
>> +STDAPI VSSCheckOSVersion(void);
>> +
>> +STDAPI COMRegister(void);
>> +STDAPI COMUnregister(void);
>> +
>> +STDAPI DllRegisterServer(void);
>> +STDAPI DllUnregisterServer(void);
>> +
>> +#endif

(a) what is the reason for not listing DllCanUnloadNow() and
DllGetClassObject() in the header file?

(b) Does STDAPI imply a return type? Or are you just going with the
implicit "int"? (Based on my encounters with EFIAPI, I think it's the
latter.)


>> diff --git a/qga/vss-win32.h b/qga/vss-win32.h
>> new file mode 100644
>> index 0000000..21655cf
>> --- /dev/null
>> +++ b/qga/vss-win32.h
>> @@ -0,0 +1,86 @@
>> +/*
>> + * 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)

(Sigh. The more windows code I'm looking at the more it convinces me
that EFI was 100% meant for windows originally. Sad.)


>> +
>> +/* 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"

No idea what I'm talking about, but since we're qualifying the second
component with the "QGAVSSEvent" prefix, shouldn't we just replace the
first component ("Global") with it? Or would it effect "event routing"?
(Tangential question, anyway.)


>> +
>> +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}");

OK, these match each other and "qga-provider.idl".


>> +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
>> +};
>> +
>> +#endif
>>
>>

OK.


>> diff --git a/qga/vss-win32-provider/install.cpp b/qga/vss-win32-provider/install.cpp
>> new file mode 100644
>> index 0000000..d6f1d55
>> --- /dev/null
>> +++ b/qga/vss-win32-provider/install.cpp
>> @@ -0,0 +1,493 @@
>> +/*
>> + * 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-win32.h"
>> +#include "inc/win2003/vscoordint.h"
>> +#include "../vss-win32-provider.h"
>> +
>> +#include <comadmin.h>
>> +#include <wbemidl.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} };

Would it be possible to make these static? Not too important of course.


>> +
>> +static void errmsg(DWORD err, const char *text)
>> +{
>> +    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);

(The FormatMessage() specification in MSDN is simply incredible.
"(char *)&msg", seriously? Your code is correct, but the interface is
unbelievable.)


>> +    printf("%.*s. (Error: %lx) %s\n", len, text, err, msg);

(a) Since we're printing an error message here, I suspect it should go
to stderr.

(b) In case "text" doesn't contain an opening paren, "len" is set to -1.
"A negative precision is taken as if the precision were omitted", IOW
the entire string will be printed. Clever!

(c) Does DWORD expand to "uint32_t"? In that case "%lx" is too big on
64-bit (LP64). We should probably use "%x" or "%"PRIx32. (Or do we build
qemu-ga.exe only for 32-bit? I seem to remember something like that.)


>> +    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\n", text, opt, err, msg);
>> +    MessageBox(NULL, buf, "Error from " QGA_PROVIDER_NAME, MB_OK|MB_ICONERROR);

Do you need a trailing newline for a message box?


>> +    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)
>> +
>> +template<class T>
>> +HRESULT put_Value(ICatalogObject *pObj, LPCWSTR name, T val)
>> +{
>> +    return pObj->put_Value(_bstr_t(name), _variant_t(val));
>> +}
>> +
>> +/* Check whether this OS version supports VSS providers */
>> +STDAPI VSSCheckOSVersion(void)
>> +{
>> +    OSVERSIONINFO OSver;
>> +
>> +    OSver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
>> +    GetVersionEx(&OSver);
>> +    if ((OSver.dwMajorVersion == 5 && OSver.dwMinorVersion >= 2) ||
>> +       OSver.dwMajorVersion > 5) {
>> +        return S_OK;
>> +    }
>> +    return S_FALSE;
>> +}
>> +

Seems OK.


>> +/* Lookup Administrators group name from winmgmt */
>> +static HRESULT GetAdminName(_bstr_t &name)
>> +{

I can see that you want to call GetAdminName() in chk(), hence you must
return a HRESULT and store the value through the parameter list.
However, can we keep the C++ features to a minimum? I'd prefer if the
non-const reference were replaced with a pointer-to-_bstr_t.
(Tangential, change only if you respin anyway and don't mind it.)


>> +    HRESULT hr;
>> +    IWbemLocator *pLoc = NULL;
>> +    IWbemServices *pSvc = NULL;
>> +    IEnumWbemClassObject *pEnum = NULL;
>> +    IWbemClassObject *pWobj = NULL;
>> +    ULONG returned;
>> +    _variant_t var;
>> +
>> +    chk(CoCreateInstance(CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER,
>> +                         IID_IWbemLocator, (LPVOID *)&pLoc));
>> +    chk(pLoc->ConnectServer(_bstr_t(L"ROOT\\CIMV2"), NULL, NULL, NULL,
>> +                            0, 0, 0, &pSvc));
>> +    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));

(signal to noise ratio converging to zero... :) I don't doubt this is
the official way though.)

Furthermore, aren't you missing a CoInitialize(NULL) call before
CoCreateInstance()? Other CoCreateInstance() calls are preceded by it.

... Well this is called only from COMRegister(), after such an init
call, so it should be OK.


>> +    if (!pEnum) {
>> +        errmsg(E_FAIL, "Failed to query for Administrators");
>> +        goto out;

I think you forgot to set "hr" to something ugly -- the code below the
"out" label will simply return it, and "hr" is currently something nice.
(The last chk() succeeded.)


>> +    }

This branch is probably for the case when ExecQuery() succeeds, but
doesn't return any rows, right?


>> +    chk(pEnum->Next(WBEM_INFINITE, 1, &pWobj, &returned));
>> +    if (returned == 0) {
>> +        errmsg(E_FAIL, "Failed to query for Administrators");
>> +        goto out;
>> +    }
>> +
>> +    chk(pWobj->Get(_bstr_t(L"Name"), 0, &var, 0, 0));
>> +    name = var;

This assigns a _variant_t to a _bstr_t (... for which I proposed *name =
var above, but it's irrelevant now).

Can this conversion (or the assignment operator) fail? If so, does
either throw an exception?


>> +out:
>> +    if (pLoc) {
>> +        pLoc->Release();
>> +    }
>> +    if (pSvc) {
>> +        pSvc->Release();
>> +    }
>> +    if (pEnum) {
>> +        pEnum->Release();
>> +    }
>> +    if (pWobj) {
>> +        pWobj->Release();
>> +    }
>> +    return hr;
>> +}

I'm sure this is idiomatic code, but do these pointers point to static /
long-term instances? I'm missing the equivalent of "delete pLoc" and so
on. Each Release() member function gets the implicit "this" parameter
here, but do they free it too? (Honestly, I can't decide which would be
worse, if they did or they didn't.)

... Ah, it's probably reference counting. OK.


>> +
>> +/* Register this module to COM+ Applications Catalog */
>> +STDAPI COMRegister(void)
>> +{
>> +    HRESULT hr = E_FAIL;
>> +    IUnknown *pUnknown = NULL;
>> +    ICOMAdminCatalog *pCatalog = NULL;
>> +    ICatalogCollection *pApps = NULL, *pRoles = NULL, *pUsersInRole = NULL;
>> +    ICatalogObject *pObj = NULL;
>> +    long n;
>> +    _bstr_t name;
>> +    _variant_t key;
>> +    CHAR dllPath[MAX_PATH], tlbPath[MAX_PATH];
>> +
>> +    if (!g_hinstDll) {
>> +        errmsg(E_FAIL, "Failed to initialize DLL");
>> +        goto out;

OK, "hr" is E_FAIL here.


>> +    }
>> +
>> +    if (VSSCheckOSVersion() == S_FALSE) {
>> +        printf("VSS provider is not supported in this OS version.\n");

I suggest stderr.


>> +        return S_FALSE; /* VSS feature is disabled */

Somewhat inconsistent with the above, you issue "return" although you
used "goto out" before. No resources are leaked so it's no problem in
practice. (You could have written "return" above.)


>> +    }
>> +
>> +    COMUnregister();

What requires / justifies this? (I can see another call below, on the
error path.) I think whatever COMUnregister() would release depends
first on the success of COMRegister(), doesn't it?

You've told Paolo,

On 06/26/13 00:31, Tomoki Sekiyama wrote:
> COMRegister and COMUnregister are called by`qemu-ga -s install`,
> to register/unregister the DLL into/from COM+ application catalogue.

and indeed I can see a COMRegister() call in 09/10. Nonetheless that
doesn't seem to justify this call to COMUnregister() -- if the DLL has
been previously registered in the application catalog, a new
registration attempt should fail.


>> +
>> +    chk(CoInitialize(NULL));

Can this fail in practice? If so, we could have error handling trouble,
because "out:" will call CoUninitialize() without any successful
initialization.


>> +    chk(CoCreateInstance(CLSID_COMAdminCatalog, NULL, CLSCTX_INPROC_SERVER,
>> +                         IID_IUnknown, (void **)&pUnknown));
>> +    chk(pUnknown->QueryInterface(IID_ICOMAdminCatalog, (void **)&pCatalog));
>> +
>> +    /* Install COM+ Component */
>> +
>> +    chk(pCatalog->GetCollection(_bstr_t(L"Applications"),
>> +                                (IDispatch **)&pApps));
>> +    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));

This looks like a globally visible change that is not rolled back if
something fails later in this function. Is the COMUnregister() call on
the error handling path supposed to undo these? Complicated :)

Then, should we check "n", or does the retval check suffice?


>> +    chk(pObj->get_Key(&key));
>> +
>> +    pObj->Release();
>> +    pObj = NULL;

Ah OK. You're reusing pObj below.


>> +
>> +    if (!GetModuleFileName(g_hinstDll, dllPath, sizeof(dllPath))) {
>> +        errmsg(GetLastError(), "GetModuleFileName failed");
>> +        goto out;

"hr" has not been set to a failure code.


>> +    }
>> +    n = strlen(dllPath);
>> +    if (n < 3) {
>> +        errmsg(E_FAIL, "Failed to lookup dll");

Presumably you wanted to set "hr" and jump to "out" as well.


>> +    }
>> +    strcpy(tlbPath, dllPath);
>> +    strcpy(tlbPath+n-3, "TLB");
>> +    printf("Registering " QGA_PROVIDER_NAME ":\n");
>> +    printf("  %s\n", dllPath);
>> +    printf("  %s\n", tlbPath);

These are arguably diagnostic messages (destined for stderr), but I
won't press it :)


>> +    if (!PathFileExists(tlbPath)) {
>> +        errmsg(ERROR_FILE_NOT_FOUND, "Failed to lookup tlb");
>> +        goto out;

"hr" not set properly.


>> +    }
>> +
>> +    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));
>> +    chk(pRoles->Populate());
>> +    chk(pRoles->Add((IDispatch **)&pObj));
>> +    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));
>> +
>> +    pObj->Release();
>> +    pObj = NULL;
>> +
>> +    /* Setup users in the role */
>> +
>> +    chk(pRoles->GetCollection(_bstr_t(L"UsersInRole"), key,
>> +                              (IDispatch **)&pUsersInRole));
>> +    chk(pUsersInRole->Populate());
>> +
>> +    chk(pUsersInRole->Add((IDispatch **)&pObj));
>> +    chk(GetAdminName(name));
>> +    chk(put_Value(pObj, L"User", _bstr_t(".\\") + name));
>> +
>> +    pObj->Release();
>> +    pObj = NULL;
>> +
>> +    chk(pUsersInRole->Add((IDispatch **)&pObj));
>> +    chk(put_Value(pObj, L"User", L"SYSTEM"));
>> +    chk(pUsersInRole->SaveChanges(&n));
>> +
>> +out:
>> +    if (pUnknown) {
>> +        pUnknown->Release();
>> +    }
>> +    if (pCatalog) {
>> +        pCatalog->Release();
>> +    }
>> +    if (pApps) {
>> +        pApps->Release();
>> +    }
>> +    if (pRoles) {
>> +        pRoles->Release();
>> +    }
>> +    if (pUsersInRole) {
>> +        pUsersInRole->Release();
>> +    }
>> +    if (pObj) {
>> +        pObj->Release();
>> +    }
>> +
>> +    if (FAILED(hr)) {
>> +        COMUnregister();

I've actually grown to accept this COMUnregister() call here... Undoing
all the crap from before staircase-wise would be a nightmare.


>> +    }
>> +    CoUninitialize();

Another question: can you nest CoInitialize() calls (and more
importantly, does CoUninitialize() respect that)? Consider the following
call chain:

COMRegister()
  CoInitialize()
  COMUnregister() -- on the "out:" path
    DllUnregisterServer()
      CoInitialize() -- init after init
      CoUninitialize()
    CoInitialize()
    CoUninitialize()
  CoUninitialize() -- right here, uninit after uninit

Will these work (especially the final two)?


>> +
>> +    return hr;
>> +}
>> +
>> +/* Unregister this module from COM+ Applications Catalog */
>> +STDAPI COMUnregister(void)
>> +{
>> +    HRESULT hr;
>> +    IUnknown *pUnknown = NULL;
>> +    ICOMAdminCatalog *pCatalog = NULL;
>> +    ICatalogCollection *pColl = NULL;
>> +    ICatalogObject *pObj = NULL;
>> +    _variant_t var;
>> +    long i, n;
>> +
>> +    if (VSSCheckOSVersion() == S_FALSE) {
>> +        printf("VSS provider is not supported in this OS version.\n");

I suggest stderr again.


>> +        return S_FALSE; /* VSS feature is disabled */
>> +    }
>> +
>> +    chk(DllUnregisterServer());

Apparently DllUnregisterServer() can't fail. (And it shouldn't, we
depend on it, since COMUnregister() is also used as destructor after
partial construction, so we must not bail here.) I propose to change the
return type of DllUnregisterServer() to void, just to drive the point
home.


>> +
>> +    chk(CoInitialize(NULL));

(same question as before -- can this fail? See unconditional uninit at
the bottom.)

I'll continue from here.

Thanks
Laszlo


>> +    chk(CoCreateInstance(CLSID_COMAdminCatalog, NULL, CLSCTX_INPROC_SERVER,
>> +                         IID_IUnknown, (void **)&pUnknown));
>> +    chk(pUnknown->QueryInterface(IID_ICOMAdminCatalog, (void **)&pCatalog));
>> +
>> +    chk(pCatalog->GetCollection(_bstr_t(L"Applications"),
>> +                                (IDispatch **)&pColl));
>> +    chk(pColl->Populate());
>> +
>> +    chk(pColl->get_Count(&n));
>> +    for (i = n - 1; i >= 0; i--) {
>> +        chk(pColl->get_Item(i, (IDispatch **)&pObj));
>> +        chk(pObj->get_Value(_bstr_t(L"Name"), &var));
>> +        if (var == _variant_t(QGA_PROVIDER_LNAME)) {
>> +            printf("Removing COM+ Application: %S\n", (wchar_t *)_bstr_t(var));
>> +            chk(pColl->Remove(i));
>> +        }
>> +    }
>> +    chk(pColl->SaveChanges(&n));
>> +
>> +out:
>> +    if (pUnknown) {
>> +        pUnknown->Release();
>> +    }
>> +    if (pCatalog) {
>> +        pCatalog->Release();
>> +    }
>> +    if (pColl) {
>> +        pColl->Release();
>> +    }
>> +    if (pObj) {
>> +        pObj->Release();
>> +    }
>> +    CoUninitialize();
>> +
>> +    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 = (lstrlen(data) + 1) * sizeof(TCHAR);
>> +    } else {
>> +        size = 0;
>> +    }
>> +
>> +    ret = RegSetValueEx(hKey, value, 0, REG_SZ, (LPBYTE)data, size);
>> +    RegCloseKey(hKey);
>> +
>> +out:
>> +    if (ret != ERROR_SUCCESS) {
>> +        /* We cannot printf here, and need MessageBox to report an error. */
>> +        errmsg_dialog(ret, "Cannot add registry ", key);
>> +        return FALSE;
>> +    }
>> +    return TRUE;
>> +}
>> +
>> +/* Register this dll as a VSS provider */
>> +STDAPI DllRegisterServer(void)
>> +{
>> +    IVssAdmin *pVssAdmin = NULL;
>> +    HRESULT hr = E_FAIL;
>> +    char dllPath[MAX_PATH];
>> +    char key[256];
>> +
>> +    CoInitialize(NULL);
>> +
>> +    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;
>> +    }
>> +
>> +    sprintf(key, "CLSID\\%s\\InprocServer32", g_szClsid);
>> +    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);
>> +    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");
>> +        goto out;
>> +    }
>> +
>> +out:
>> +    if (pVssAdmin) {
>> +        pVssAdmin->Release();
>> +    }
>> +    CoUninitialize();
>> +
>> +    if (FAILED(hr)) {
>> +        DllUnregisterServer();
>> +    }
>> +
>> +    return hr;
>> +}
>> +
>> +/* Unregister this VSS hardware provider from the system */
>> +STDAPI DllUnregisterServer(void)
>> +{
>> +    TCHAR key[256];
>> +    IVssAdmin *pVssAdmin = NULL;
>> +
>> +    CoInitialize(NULL);
>> +
>> +    HRESULT hr = CoCreateInstance(CLSID_VSSCoordinator,
>> +         NULL, CLSCTX_ALL, IID_IVssAdmin, (void **)&pVssAdmin);
>> +    if (SUCCEEDED(hr)) {
>> +        hr = pVssAdmin->UnregisterProvider(g_gProviderId);
>> +        pVssAdmin->Release();
>> +    } else {
>> +        errmsg_dialog(hr, "CoCreateInstance(VSSCoordinator) failed");
>> +    }
>> +
>> +    sprintf(key, "CLSID\\%s", g_szClsid);
>> +    SHDeleteKey(HKEY_CLASSES_ROOT, key);
>> +    SHDeleteKey(HKEY_CLASSES_ROOT, g_szProgid);
>> +
>> +    CoUninitialize();
>> +
>> +    return S_OK; /* Uninstall should never fail */
>> +}
>> +
>> +
>> +/* Support functions for _bstr_t in MinGW: Originally written by: Diaa Sami */
>> +
>> +void __stdcall _com_issue_error(HRESULT hr)
>> +{
>> +    printf("_com_issue_error() called with parameter HRESULT = %lu", hr);
>> +}
>> +
>> +namespace _com_util
>> +{
>> +    char * __stdcall ConvertBSTRToString(BSTR bstr)
>> +    {
>> +        const unsigned int stringLength = lstrlenW(bstr);
>> +        char *const ascii = new char [stringLength + 1];
>> +
>> +        wcstombs(ascii, bstr, stringLength + 1);
>> +
>> +        return ascii;
>> +    }
>> +
>> +    BSTR __stdcall ConvertStringToBSTR(const char *const ascii)
>> +    {
>> +        const unsigned int stringLength = lstrlenA(ascii);
>> +        BSTR bstr = SysAllocStringLen(NULL, stringLength);
>> +
>> +        mbstowcs(bstr, ascii, stringLength + 1);
>> +
>> +        return bstr;
>> +    }
>> +}
> 
>> diff --git a/qga/vss-win32-provider/provider.cpp b/qga/vss-win32-provider/provider.cpp
>> new file mode 100644
>> index 0000000..90a3d25
>> --- /dev/null
>> +++ b/qga/vss-win32-provider/provider.cpp
>> @@ -0,0 +1,479 @@
>> +/*
>> + * 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-win32.h"
>> +#include "inc/win2003/vscoordint.h"
>> +#include "inc/win2003/vsprov.h"
>> +
>> +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 block)
>> +{
>> +    if (block) {
>> +        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;
>> +    } else if (riid == IID_IVssSoftwareSnapshotProvider) {
>> +        *ppObj = static_cast<void*>(
>> +            static_cast<IVssSoftwareSnapshotProvider*>(this));
>> +        AddRef();
>> +        return S_OK;
>> +    } else if (riid == IID_IVssProviderCreateSnapshotSet) {
>> +        *ppObj = static_cast<void*>(
>> +            static_cast<IVssProviderCreateSnapshotSet*>(this));
>> +        AddRef();
>> +        return S_OK;
>> +    } else 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)
>> +{
>> +    *ppEnum = new CQGAVSSEnumObject;
>> +    (*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)
>> +{
>> +    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 S_OK;
>> +}
>> +
>> +
>> +/*
>> + * 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 hEvent, hEvent2;
>> +
>> +    hEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_FROZEN);
>> +    if (hEvent == INVALID_HANDLE_VALUE) {
>> +        hr = E_FAIL;
>> +        goto out;
>> +    }
>> +
>> +    hEvent2 = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_THAW);
>> +    if (hEvent == INVALID_HANDLE_VALUE) {
>> +        CloseHandle(hEvent);
>> +        hr = E_FAIL;
>> +        goto out;
>> +    }
>> +
>> +    SetEvent(hEvent);
>> +    if (WaitForSingleObject(hEvent2, 60*1000) != WAIT_OBJECT_0) {
>> +        hr = E_ABORT;
>> +    }
>> +
>> +    CloseHandle(hEvent2);
>> +    CloseHandle(hEvent);
>> +out:
>> +    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 bLock) { return E_NOTIMPL; }
>> +private:
>> +    long m_nRefCount;
>> +};
>> +
>> +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()
>> +{
>> +    LockModule(TRUE);
>> +    return 2;
>> +}
>> +
>> +STDMETHODIMP_(ULONG) CQGAVssProviderFactory::Release()
>> +{
>> +    LockModule(FALSE);
>> +    return 1;
>> +}
>> +
>> +STDMETHODIMP CQGAVssProviderFactory::CreateInstance(
>> +    IUnknown *pUnknownOuter, REFIID iid, void **ppv)
>> +{
>> +    if (pUnknownOuter) {
>> +        return CLASS_E_NOAGGREGATION;
>> +    }
>> +    CQGAVssProvider *pObj = new CQGAVssProvider;
>> +    if (!pObj) {
>> +        return E_OUTOFMEMORY;
>> +    }
>> +    return pObj->QueryInterface(iid, ppv);
>> +}
>> +
>> +
>> +/*
>> + * DLL functions
>> + */
>> +
>> +STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
>> +{
>> +    static CQGAVssProviderFactory factory;
>> +
>> +    *ppv = NULL;
>> +    if (IsEqualCLSID(rclsid, CLSID_QGAVSSProvider)) {
>> +        return factory.QueryInterface(riid, ppv);
>> +    }
>> +    return CLASS_E_CLASSNOTAVAILABLE;
>> +}
>> +
>> +STDAPI DllCanUnloadNow()
>> +{
>> +    return g_nComObjsInUse == 0 ? S_OK : S_FALSE;
>> +}
>> +
>> +EXTERN_C
>> +BOOL WINAPI DllMain(HINSTANCE hinstDll, DWORD dwReason, LPVOID lpReserved)
>> +{
>> +    switch (dwReason) {
>> +
>> +    case DLL_PROCESS_ATTACH:
>> +        g_hinstDll = hinstDll;
>> +        DisableThreadLibraryCalls(hinstDll);
>> +        break;
>> +    }
>> +
>> +    return TRUE;
>> +}
> 

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze
  2013-06-26 14:32     ` Laszlo Ersek
@ 2013-06-26 21:27       ` Paolo Bonzini
  2013-06-26 22:09       ` Tomoki Sekiyama
  2013-06-27 15:01       ` Laszlo Ersek
  2 siblings, 0 replies; 70+ messages in thread
From: Paolo Bonzini @ 2013-06-26 21:27 UTC (permalink / raw)
  To: Laszlo Ersek
  Cc: libaiqing, mdroth, stefanha, qemu-devel, lcapitulino, vrozenfe,
	Tomoki Sekiyama, seiji.aguchi, areis

Il 26/06/2013 16:32, Laszlo Ersek ha scritto:
>>> >> +#define EVENT_NAME_FROZEN "Global\\QGAVSSEvent-frozen"
>>> >> +#define EVENT_NAME_THAW   "Global\\QGAVSSEvent-thaw"
> No idea what I'm talking about, but since we're qualifying the second
> component with the "QGAVSSEvent" prefix, shouldn't we just replace the
> first component ("Global") with it? Or would it effect "event routing"?

Yes:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms684305%28v=vs.85%29.aspx
says

Terminal Services:  The name can have a "Global\" or "Local\" prefix to
explicitly open an object in the global or session namespace. The
remainder of the name can contain any character except the backslash
character (\). For more information, see Kernel Object Namespaces.

Paolo

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze
  2013-06-26 14:32     ` Laszlo Ersek
  2013-06-26 21:27       ` Paolo Bonzini
@ 2013-06-26 22:09       ` Tomoki Sekiyama
  2013-06-27 15:01       ` Laszlo Ersek
  2 siblings, 0 replies; 70+ messages in thread
From: Tomoki Sekiyama @ 2013-06-26 22:09 UTC (permalink / raw)
  To: Laszlo Ersek
  Cc: libaiqing, qemu-devel, stefanha, mdroth, lcapitulino, vrozenfe,
	pbonzini, Seiji Aguchi, areis

On 6/26/13 10:32 , "Laszlo Ersek" <lersek@redhat.com> wrote:

>On 06/25/13 18:03, Laszlo Ersek wrote:
>> On 06/06/13 17:06, Tomoki Sekiyama wrote:
>
>Comparing the .def file and the provider header file:
>...
>(a) what is the reason for not listing DllCanUnloadNow() and
>DllGetClassObject() in the header file?

Because these two are reserved functions for COM, and not
Referenced from qemu-ga.exe.

>(b) Does STDAPI imply a return type? Or are you just going with the
>implicit "int"? (Based on my encounters with EFIAPI, I think it's the
>latter.)

It is extracted as 'extern "C" HRESULT __stdcall'.
(__stdcall specifies calling convention.)

>>>+#define EVENT_NAME_FROZEN "Global\\QGAVSSEvent-frozen"
>>> +#define EVENT_NAME_THAW   "Global\\QGAVSSEvent-thaw"
>
>No idea what I'm talking about, but since we're qualifying the second
>component with the "QGAVSSEvent" prefix, shouldn't we just replace the
>first component ("Global") with it? Or would it effect "event routing"?
>(Tangential question, anyway.)

Latter. "Global\\" is a reserved namespace in Windows used for
communication between services and the other program, so we can't
omit this.

>>> +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} };
>
>Would it be possible to make these static? Not too important of course.

Some of these are declared as "extern" in headers in MinGW or VSS SDK,
so unfortunately we can't make them static.

>>>+
>>> +static void errmsg(DWORD err, const char *text)
>>> +{
>>> +    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);
>
>(The FormatMessage() specification in MSDN is simply incredible.
>"(char *)&msg", seriously? Your code is correct, but the interface is
>unbelievable.)

Yeah, really...

>>> +    printf("%.*s. (Error: %lx) %s\n", len, text, err, msg);
>
>(a) Since we're printing an error message here, I suspect it should go
>to stderr.

OK.

>(b) In case "text" doesn't contain an opening paren, "len" is set to -1.
>"A negative precision is taken as if the precision were omitted", IOW
>the entire string will be printed. Clever!

Maybe need some comments...

>(c) Does DWORD expand to "uint32_t"? In that case "%lx" is too big on
>64-bit (LP64). We should probably use "%x" or "%"PRIx32. (Or do we build
>qemu-ga.exe only for 32-bit? I seem to remember something like that.)

DWORD is typedef as unsigned long int. MinGW64 uses LLP64 model, so
it is 32bit both in 32-bit and 64-bit MinGW.
So if I use %x here, gcc outputs:

qga/vss-win32-provider/install.cpp:44:57: warning: format '%x' expects
argument of type 'unsigned int', but argument 4 has type 'DWORD {aka long
unsigned int}' [-Wformat]

>>>+    snprintf(buf, sizeof(buf), "%s%s. (Error: %lx) %s\n", text, opt,
>>>err, msg);
>>> +    MessageBox(NULL, buf, "Error from " QGA_PROVIDER_NAME,
>>>MB_OK|MB_ICONERROR);
>
>Do you need a trailing newline for a message box?

No. I will remove "\n". Thanks.

>>> +/* Lookup Administrators group name from winmgmt */
>>> +static HRESULT GetAdminName(_bstr_t &name)
>>> +{
>
>I can see that you want to call GetAdminName() in chk(), hence you must
>return a HRESULT and store the value through the parameter list.
>However, can we keep the C++ features to a minimum? I'd prefer if the
>non-const reference were replaced with a pointer-to-_bstr_t.
>(Tangential, change only if you respin anyway and don't mind it.)

OK, I will make this into 'static HRESULT GetAdminName(_bstr_t *name)'.

>>> +    if (!pEnum) {
>>> +        errmsg(E_FAIL, "Failed to query for Administrators");
>>> +        goto out;
>
>I think you forgot to set "hr" to something ugly -- the code below the
>"out" label will simply return it, and "hr" is currently something nice.
>(The last chk() succeeded.)

I will add "hr = E_FAIL;" here. Thanks.


>>> +    }
>
>This branch is probably for the case when ExecQuery() succeeds, but
>doesn't return any rows, right?

We must not come here (while the reference is accurate).
It is just in case.

>>> +    chk(pEnum->Next(WBEM_INFINITE, 1, &pWobj, &returned));
>>> +    if (returned == 0) {
>>> +        errmsg(E_FAIL, "Failed to query for Administrators");
>>> +        goto out;
>>> +    }
>>> +
>>> +    chk(pWobj->Get(_bstr_t(L"Name"), 0, &var, 0, 0));
>>> +    name = var;
>
>This assigns a _variant_t to a _bstr_t (... for which I proposed *name =
>var above, but it's irrelevant now).
>
>Can this conversion (or the assignment operator) fail? If so, does
>either throw an exception?

Hm, yes. I will add try ... catch here.

>>> +out:
>>> +    if (pLoc) {
>>> +        pLoc->Release();
>>> +    }
>>> +    if (pSvc) {
>>> +        pSvc->Release();
>>> +    }
>>> +    if (pEnum) {
>>> +        pEnum->Release();
>>> +    }
>>> +    if (pWobj) {
>>> +        pWobj->Release();
>>> +    }
>>> +    return hr;
>>> +}
>
>I'm sure this is idiomatic code, but do these pointers point to static /
>long-term instances? I'm missing the equivalent of "delete pLoc" and so
>on. Each Release() member function gets the implicit "this" parameter
>here, but do they free it too? (Honestly, I can't decide which would be
>worse, if they did or they didn't.)
>
>... Ah, it's probably reference counting. OK.

Right, it's decrementing reference counters.


>>>+    if (!g_hinstDll) {
>>>+        errmsg(E_FAIL, "Failed to initialize DLL");
>>>+        goto out;
>
>OK, "hr" is E_FAIL here.

Ah, this must be rewritten to "return", as described below.

>>>+    }
>>>+
>>>+    if (VSSCheckOSVersion() == S_FALSE) {
>>> +        printf("VSS provider is not supported in this OS version.\n");
>
>I suggest stderr.

OK.

>>> +        return S_FALSE; /* VSS feature is disabled */
>
>Somewhat inconsistent with the above, you issue "return" although you
>used "goto out" before. No resources are leaked so it's no problem in
>practice. (You could have written "return" above.)

This must be return, because we must not call CoUninitialize()
in the "out:" path. I will rewrite "goto out" above into "return".

>>> +    }
>>> +
>>> +    COMUnregister();
>
>What requires / justifies this? (I can see another call below, on the
>error path.) I think whatever COMUnregister() would release depends
>first on the success of COMRegister(), doesn't it?

It was intended to clean up something in the catalog with the same
name, just in case.

>You've told Paolo,
>
>On 06/26/13 00:31, Tomoki Sekiyama wrote:
>> COMRegister and COMUnregister are called by`qemu-ga -s install`,
>> to register/unregister the DLL into/from COM+ application catalogue.
>
>and indeed I can see a COMRegister() call in 09/10. Nonetheless that
>doesn't seem to justify this call to COMUnregister() -- if the DLL has
>been previously registered in the application catalog, a new
>registration attempt should fail.

Okey, then I will remove this COMUnregister().


>>> +
>>> +    chk(CoInitialize(NULL));
>
>Can this fail in practice? If so, we could have error handling trouble,
>because "out:" will call CoUninitialize() without any successful
>initialization.

Ah, it won't fail. I will remove chk() here.

>>>+    /* Install COM+ Component */
>>> +
>>> +    chk(pCatalog->GetCollection(_bstr_t(L"Applications"),
>>> +                                (IDispatch **)&pApps));
>>> +    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));
>
>This looks like a globally visible change that is not rolled back if
>something fails later in this function. Is the COMUnregister() call on
>the error handling path supposed to undo these? Complicated :)

This code is creating a tree, whose root is an "Application"
with the name of QGA_PROVIDER_LNAME.
COMUnregister will erase whole tree by erasing the root,
and every modification below will be rolled back.

>Then, should we check "n", or does the retval check suffice?

If something fails, it returns error (E_***), so I think the
retval check is good enough.


>>> +
>>> +    if (!GetModuleFileName(g_hinstDll, dllPath, sizeof(dllPath))) {
>>> +        errmsg(GetLastError(), "GetModuleFileName failed");
>>> +        goto out;
>
>"hr" has not been set to a failure code.

Oops, I will add a code to set hr here,

>>> +    }
>>> +    n = strlen(dllPath);
>>> +    if (n < 3) {
>>> +        errmsg(E_FAIL, "Failed to lookup dll");
>
>Presumably you wanted to set "hr" and jump to "out" as well.

And here too, with "goto out;".

>>> +    }
>>> +    strcpy(tlbPath, dllPath);
>>> +    strcpy(tlbPath+n-3, "TLB");
>>> +    printf("Registering " QGA_PROVIDER_NAME ":\n");
>>> +    printf("  %s\n", dllPath);
>>> +    printf("  %s\n", tlbPath);
>
>These are arguably diagnostic messages (destined for stderr), but I
>won't press it :)

OK.

>>> +    if (!PathFileExists(tlbPath)) {
>>> +        errmsg(ERROR_FILE_NOT_FOUND, "Failed to lookup tlb");
>>> +        goto out;
>
>"hr" not set properly.

And here too.


...


>>> +    CoUninitialize();
>
>Another question: can you nest CoInitialize() calls (and more
>importantly, does CoUninitialize() respect that)? Consider the following
>call chain:
>
>COMRegister()
>  CoInitialize()
>  COMUnregister() -- on the "out:" path
>    DllUnregisterServer()
>      CoInitialize() -- init after init
>      CoUninitialize()
>    CoInitialize()
>    CoUninitialize()
>  CoUninitialize() -- right here, uninit after uninit
>
>Will these work (especially the final two)?

Yes, it can be nested, as far as CoInitialize and CoUninitialize
are balanced.


>>>+/* Unregister this module from COM+ Applications Catalog */
>>> +STDAPI COMUnregister(void)
>>> +{
>>> +    HRESULT hr;
>>> +    IUnknown *pUnknown = NULL;
>>> +    ICOMAdminCatalog *pCatalog = NULL;
>>> +    ICatalogCollection *pColl = NULL;
>>> +    ICatalogObject *pObj = NULL;
>>> +    _variant_t var;
>>> +    long i, n;
>>> +
>>> +    if (VSSCheckOSVersion() == S_FALSE) {
>>> +        printf("VSS provider is not supported in this OS version.\n");
>
>I suggest stderr again.

OK.

>>> +        return S_FALSE; /* VSS feature is disabled */
>>> +    }
>>> +
>>> +    chk(DllUnregisterServer());
>
>Apparently DllUnregisterServer() can't fail. (And it shouldn't, we
>depend on it, since COMUnregister() is also used as destructor after
>partial construction, so we must not bail here.) I propose to change the
>return type of DllUnregisterServer() to void, just to drive the point
>home.

OK. I can't make it void (because it is a reserved interface for COM),
but will remove chk here.


>>> +
>>> +    chk(CoInitialize(NULL));
>
>(same question as before -- can this fail? See unconditional uninit at
>the bottom.)

Will remove chk from here too.


>I'll continue from here.

Thanks a lot for reviewing this long complicated windows code!


Tomoki Sekiyama

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze
  2013-06-26 14:32     ` Laszlo Ersek
  2013-06-26 21:27       ` Paolo Bonzini
  2013-06-26 22:09       ` Tomoki Sekiyama
@ 2013-06-27 15:01       ` Laszlo Ersek
  2013-06-27 22:25         ` Tomoki Sekiyama
  2 siblings, 1 reply; 70+ messages in thread
From: Laszlo Ersek @ 2013-06-27 15:01 UTC (permalink / raw)
  To: Tomoki Sekiyama
  Cc: libaiqing, mdroth, stefanha, qemu-devel, lcapitulino, vrozenfe,
	pbonzini, seiji.aguchi, areis

On 06/26/13 16:32, Laszlo Ersek wrote:
> On 06/25/13 18:03, Laszlo Ersek wrote:
>> On 06/06/13 17:06, Tomoki Sekiyama wrote:

>>> +/* Unregister this module from COM+ Applications Catalog */
>>> +STDAPI COMUnregister(void)
>>> +{
>>> +    HRESULT hr;
>>> +    IUnknown *pUnknown = NULL;
>>> +    ICOMAdminCatalog *pCatalog = NULL;
>>> +    ICatalogCollection *pColl = NULL;
>>> +    ICatalogObject *pObj = NULL;
>>> +    _variant_t var;
>>> +    long i, n;
>>> +
>>> +    if (VSSCheckOSVersion() == S_FALSE) {
>>> +        printf("VSS provider is not supported in this OS version.\n");
>>> +        return S_FALSE; /* VSS feature is disabled */
>>> +    }
>>> +
>>> +    chk(DllUnregisterServer());
>>> +
>>> +    chk(CoInitialize(NULL));

picking up here

>>> +    chk(CoCreateInstance(CLSID_COMAdminCatalog, NULL, CLSCTX_INPROC_SERVER,
>>> +                         IID_IUnknown, (void **)&pUnknown));
>>> +    chk(pUnknown->QueryInterface(IID_ICOMAdminCatalog, (void **)&pCatalog));
>>> +
>>> +    chk(pCatalog->GetCollection(_bstr_t(L"Applications"),
>>> +                                (IDispatch **)&pColl));
>>> +    chk(pColl->Populate());
>>> +
>>> +    chk(pColl->get_Count(&n));
>>> +    for (i = n - 1; i >= 0; i--) {
>>> +        chk(pColl->get_Item(i, (IDispatch **)&pObj));
>>> +        chk(pObj->get_Value(_bstr_t(L"Name"), &var));
>>> +        if (var == _variant_t(QGA_PROVIDER_LNAME)) {
>>> +            printf("Removing COM+ Application: %S\n", (wchar_t *)_bstr_t(var));

(stderr)

>>> +            chk(pColl->Remove(i));
>>> +        }

I think you leak a pObj reference here, at the end of the iteration. The
next round will set pObj to something else; I think we should call
pObj->Release() here, and set pObj to NULL (for the case when this is
the last iteration).

I'm not sure if you're allowed to call pObj->Release() after the
pColl()->Remove(i) call. So maybe call pObj->Release() in an else
branch. (In this case however the out: logic should be modified as
well.)

>>> +    }
>>> +    chk(pColl->SaveChanges(&n));

Right, there's not much to do if deregistration fails...

>>> +
>>> +out:
>>> +    if (pUnknown) {
>>> +        pUnknown->Release();
>>> +    }
>>> +    if (pCatalog) {
>>> +        pCatalog->Release();
>>> +    }
>>> +    if (pColl) {
>>> +        pColl->Release();
>>> +    }
>>> +    if (pObj) {
>>> +        pObj->Release();
>>> +    }
>>> +    CoUninitialize();
>>> +
>>> +    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 = (lstrlen(data) + 1) * sizeof(TCHAR);

I think we should drop the multiplication by sizeof(TCHAR), it's 1.
According to MSDN, TCHAR could be something wider than "char" (ie.
WCHAR) "for Unicode platforms".

However in all your CreateRegistryKey() calls, the argument passed as
"data" depends on sizeof(TCHAR)==1, directly or indirectly. I think it's
best to be honest about it. For example,

>>> +    } else {
>>> +        size = 0;
>>> +    }
>>> +
>>> +    ret = RegSetValueEx(hKey, value, 0, REG_SZ, (LPBYTE)data, size);
>>> +    RegCloseKey(hKey);
>>> +
>>> +out:
>>> +    if (ret != ERROR_SUCCESS) {
>>> +        /* We cannot printf here, and need MessageBox to report an error. */
>>> +        errmsg_dialog(ret, "Cannot add registry ", key);

right here we equate (const char *) with LPCTSTR (by virtue of the third
arg being "key").

You might also replace lstrlen() with strlen() for consistency.

(Tangential anyhow.)

>>> +        return FALSE;
>>> +    }
>>> +    return TRUE;
>>> +}
>>> +
>>> +/* Register this dll as a VSS provider */
>>> +STDAPI DllRegisterServer(void)
>>> +{
>>> +    IVssAdmin *pVssAdmin = NULL;
>>> +    HRESULT hr = E_FAIL;
>>> +    char dllPath[MAX_PATH];
>>> +    char key[256];
>>> +
>>> +    CoInitialize(NULL);
>>> +
>>> +    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;
>>> +    }
>>> +
>>> +    sprintf(key, "CLSID\\%s\\InprocServer32", g_szClsid);

(you could reuse "key" from the previous sprintf())

>>> +    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);

(indentation)

>>> +    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);

(indentation)

>>> +    if (FAILED(hr)) {
>>> +        errmsg_dialog(hr, "RegisterProvider failed");
>>> +        goto out;

(goto unnecessary)

>>> +    }
>>> +
>>> +out:
>>> +    if (pVssAdmin) {
>>> +        pVssAdmin->Release();
>>> +    }
>>> +    CoUninitialize();
>>> +
>>> +    if (FAILED(hr)) {
>>> +        DllUnregisterServer();
>>> +    }
>>> +
>>> +    return hr;
>>> +}
>>> +
>>> +/* Unregister this VSS hardware provider from the system */
>>> +STDAPI DllUnregisterServer(void)
>>> +{
>>> +    TCHAR key[256];
>>> +    IVssAdmin *pVssAdmin = NULL;
>>> +
>>> +    CoInitialize(NULL);
>>> +
>>> +    HRESULT hr = CoCreateInstance(CLSID_VSSCoordinator,
>>> +         NULL, CLSCTX_ALL, IID_IVssAdmin, (void **)&pVssAdmin);

(indentation, maybe)

>>> +    if (SUCCEEDED(hr)) {
>>> +        hr = pVssAdmin->UnregisterProvider(g_gProviderId);
>>> +        pVssAdmin->Release();
>>> +    } else {
>>> +        errmsg_dialog(hr, "CoCreateInstance(VSSCoordinator) failed");
>>> +    }
>>> +
>>> +    sprintf(key, "CLSID\\%s", g_szClsid);
>>> +    SHDeleteKey(HKEY_CLASSES_ROOT, key);
>>> +    SHDeleteKey(HKEY_CLASSES_ROOT, g_szProgid);
>>> +
>>> +    CoUninitialize();
>>> +
>>> +    return S_OK; /* Uninstall should never fail */
>>> +}

Seems OK.

>>> +
>>> +
>>> +/* Support functions for _bstr_t in MinGW: Originally written by: Diaa Sami */
>>> +

Where does this code originate from? What is its license?

>>> +void __stdcall _com_issue_error(HRESULT hr)
>>> +{
>>> +    printf("_com_issue_error() called with parameter HRESULT = %lu", hr);
>>> +}

This wouldn't be hard to reimplement anyway, just print the message to
stderr. Plus it's missing \n.

I googled the function name, and some people put up a message box here.
Not sure under what circumstances this function is called.


>>> +
>>> +namespace _com_util
>>> +{
>>> +    char * __stdcall ConvertBSTRToString(BSTR bstr)
>>> +    {
>>> +        const unsigned int stringLength = lstrlenW(bstr);
>>> +        char *const ascii = new char [stringLength + 1];
>>> +
>>> +        wcstombs(ascii, bstr, stringLength + 1);
>>> +
>>> +        return ascii;
>>> +    }

The BSTR, _bstr_t, LPCTSTR etc mess is incredible. Is BSTR just
(wchar_t*)?

These COM interfaces seem broken by the way; how does one report a
conversion error? wcstombs() is locale-dependent and can fail. I'll just
pretend that whatever "bstr" contains in UTF-16 will always be
representable in pure ASCII.

>>> +
>>> +    BSTR __stdcall ConvertStringToBSTR(const char *const ascii)
>>> +    {
>>> +        const unsigned int stringLength = lstrlenA(ascii);
>>> +        BSTR bstr = SysAllocStringLen(NULL, stringLength);
>>> +
>>> +        mbstowcs(bstr, ascii, stringLength + 1);
>>> +
>>> +        return bstr;
>>> +    }
>>> +}


>>> diff --git a/qga/vss-win32-provider/provider.cpp b/qga/vss-win32-provider/provider.cpp
>>> new file mode 100644
>>> index 0000000..90a3d25
>>> --- /dev/null
>>> +++ b/qga/vss-win32-provider/provider.cpp
>>> @@ -0,0 +1,479 @@
>>> +/*
>>> + * 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-win32.h"
>>> +#include "inc/win2003/vscoordint.h"
>>> +#include "inc/win2003/vsprov.h"
>>> +
>>> +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 block)

Did you mean "lock" instead of "block"?

>>> +{
>>> +    if (block) {
>>> +        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;

I think idiomatic C++ might want to put it on the member initializer
list, but this way is correct as well, and maybe more understandable
(and I asked for minimizing C++ features :)) so OK.

>>> +    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));

Storing the address of the base object, right?

Actually (based on what the other class does below), do you think it's
right to return the base object's address for IID_IUnknown too, rather
than static_cast<void*>(this)? ... We have a single base object here so
in practice these addresses should be the same.

>>> +        AddRef();
>>> +        return S_OK;
>>> +    }
>>> +    ppObj = NULL;

Indirection operator missing ("*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;

I guess we're dead sure the object was allocated with "new".

The destructor invoked here may also remove the last reference to the
module.

No further access to this->XXX is allowed.

http://www.parashift.com/c++-faq-lite/delete-this.html

OK.

>>> +    }
>>> +    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;
>>> +}

(This class seems to track references to the module and do nothing
else.)

>>> +
>>> +
>>> +/* 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;
>>> +    } else if (riid == IID_IVssSoftwareSnapshotProvider) {

(No "else" needed if "return" is the last statement in the "if"'s
block.)


>>> +        *ppObj = static_cast<void*>(
>>> +            static_cast<IVssSoftwareSnapshotProvider*>(this));
>>> +        AddRef();
>>> +        return S_OK;
>>> +    } else if (riid == IID_IVssProviderCreateSnapshotSet) {
>>> +        *ppObj = static_cast<void*>(
>>> +            static_cast<IVssProviderCreateSnapshotSet*>(this));
>>> +        AddRef();
>>> +        return S_OK;
>>> +    } else if (riid == IID_IVssProviderNotifications) {
>>> +        *ppObj = static_cast<void*>(
>>> +            static_cast<IVssProviderNotifications*>(this));
>>> +        AddRef();
>>> +        return S_OK;
>>> +    }
>>> +    *ppObj = NULL;
>>> +    return E_NOINTERFACE;
>>> +}

Seems OK. Could be reworked into a switch too to save some space, maybe,
but don't bother.


>>> +
>>> +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)
>>> +{
>>> +    *ppEnum = new CQGAVSSEnumObject;
>>> +    (*ppEnum)->AddRef();
>>> +    return S_OK;
>>> +}

OK, so the pattern is, refcount is always incremented in QueryXXXX(), no
matter whether we return the address of one of our own base objects (in
which case we increment our own refcount), or we create a new object
(strictly with "new") and increment the refcount to 1 on that. The
caller of QueryXXXX() is responsible for calling Release() later.

>>> +
>>> +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)
>>> +{
>>> +    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 S_OK;
>>> +}

Shouldn't you set *ppAsync to something here? (No idea, just asking --
S_OK could imply something on output.) Same for IsVolumeSnapshotted()
above.

>>> +
>>> +
>>> +/*
>>> + * 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 hEvent, hEvent2;
>>> +
>>> +    hEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_FROZEN);
>>> +    if (hEvent == INVALID_HANDLE_VALUE) {
>>> +        hr = E_FAIL;
>>> +        goto out;
>>> +    }
>>> +
>>> +    hEvent2 = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_THAW);
>>> +    if (hEvent == INVALID_HANDLE_VALUE) {
>>> +        CloseHandle(hEvent);
>>> +        hr = E_FAIL;
>>> +        goto out;
>>> +    }
>>> +
>>> +    SetEvent(hEvent);

I think we should add a comment here -- this is where qemu-ga.exe /
libvirt will create the snapshot.

>>> +    if (WaitForSingleObject(hEvent2, 60*1000) != WAIT_OBJECT_0) {
>>> +        hr = E_ABORT;
>>> +    }
>>> +
>>> +    CloseHandle(hEvent2);
>>> +    CloseHandle(hEvent);
>>> +out:
>>> +    return hr;
>>> +}

Seems correct in general.

However I think you could shave off a few lines from this function by
reorganizing the "out:" path, for example by moving CloseHandle(hEvent)
there, and replacing the first goto with a return -- currently the
goto's actually waste space; even plain returns would be more
compressed.

Second, maybe the magic constant 60*1000 (known from the VSS docs)
should be a macro.


>>> +
>>> +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;
>>> +}

(Side question: are these methods all abstract in the base class? Can we
save a few LOCs by simply inheriting some functions?)

>>> +
>>> +
>>> +/*
>>> + * 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 bLock) { return E_NOTIMPL; }
>>> +private:
>>> +    long m_nRefCount;
>>> +};
>>> +
>>> +STDMETHODIMP CQGAVssProviderFactory::QueryInterface(REFIID riid, void **ppv)
>>> +{
>>> +    if (riid == IID_IUnknown || riid == IID_IClassFactory) {
>>> +        *ppv = static_cast<void*>(this);

Again, in practice the pointer value should be correct for both
interfaces (single inheritance, only one base object), but I'd feel
safer if we had two static casts. Does the C++ standard (or some C++
ABI) guarantee that the first base object is always at offset 0 in the
derived object? (In C, the standard requires that there be no padding at
the beginning of a structure.)


>>> +        AddRef();
>>> +        return S_OK;
>>> +    }
>>> +    *ppv = NULL;
>>> +    return E_NOINTERFACE;
>>> +}
>>> +
>>> +STDMETHODIMP_(ULONG) CQGAVssProviderFactory::AddRef()
>>> +{
>>> +    LockModule(TRUE);
>>> +    return 2;
>>> +}
>>> +
>>> +STDMETHODIMP_(ULONG) CQGAVssProviderFactory::Release()
>>> +{
>>> +    LockModule(FALSE);
>>> +    return 1;
>>> +}

Would it be preferable to change the prototype of LockModule() to return
the post-increment / post-decrement value of "g_nComObjsInUse" (ie.
whatever InterlockedIncrement() or InterlockedDecrement() returns in
LockModule()), and just forward that retval in these two functions? The
values "2" and "1" seem quite arbitrary.

Also, the private data member "m_nRefCount" is unused. This class has
reference counting but no constructor or destructor.


>>> +
>>> +STDMETHODIMP CQGAVssProviderFactory::CreateInstance(
>>> +    IUnknown *pUnknownOuter, REFIID iid, void **ppv)
>>> +{
>>> +    if (pUnknownOuter) {
>>> +        return CLASS_E_NOAGGREGATION;
>>> +    }
>>> +    CQGAVssProvider *pObj = new CQGAVssProvider;
>>> +    if (!pObj) {
>>> +        return E_OUTOFMEMORY;
>>> +    }

(We generally assume that memory allocation never fails.)

>>> +    return pObj->QueryInterface(iid, ppv);

This may leak.

If CQGAVssProvider::QueryInterface() doesn't recognize the requested
interface, it will set *ppv to NULL, and the caller will have no chance
to call Release() on it later. That last bit is actually correct (we
haven't bumped the refcount to 1), but accordingly we should delete pObj
here in that case.


>>> +}
>>> +
>>> +
>>> +/*
>>> + * DLL functions
>>> + */
>>> +
>>> +STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
>>> +{

(Right, this is the factory factory function. Awesome :/)


>>> +    static CQGAVssProviderFactory factory;
>>> +
>>> +    *ppv = NULL;
>>> +    if (IsEqualCLSID(rclsid, CLSID_QGAVSSProvider)) {
>>> +        return factory.QueryInterface(riid, ppv);
>>> +    }
>>> +    return CLASS_E_CLASSNOTAVAILABLE;
>>> +}

Not sure what to suggest here. I just don't like the factory object
being static *and* having reference counting.

... Basically you translate references to the factory object to
references to the module. I guess I could see the logic in that if you
deleted the "m_nRefCount" member. However the externally visible
AddRef() and Release() return values are broken in any case. Somehow the
existence of AddRef() and Release() seems fundamentally broken for a
static object -- you simply can't go to refcount==0, which would be the
only situation when the DLL could be removed.

What about this:
- factories would be objects allocated with "new",
- real reference counting for them (with constructor and destructor
  too),
- the ctor/dtor would massage LockModule().

In this aspect CQGAVssProviderFactory would work exactly like the
CQGAVssProvider class, and DllGetClassObject() -- the factory factory
method -- would work like CQGAVssProviderFactory::CreateInstance() --
the factory method.

Of course I have no clue how a factory object must be released
officially, I assume though with the usual ->Release() call, which can
be optionally followed by DLL removal. These windows interfaces are
utterly over-engineered.


>>> +
>>> +STDAPI DllCanUnloadNow()
>>> +{
>>> +    return g_nComObjsInUse == 0 ? S_OK : S_FALSE;
>>> +}

Don't you need some kind of atomic or locked read here? We could read a
stale value here. Granted, most stale values would err on the safe side
(ie. read >0 instead of ==0), but in theory the other mistake is
possible, no?

>>> +
>>> +EXTERN_C
>>> +BOOL WINAPI DllMain(HINSTANCE hinstDll, DWORD dwReason, LPVOID lpReserved)
>>> +{
>>> +    switch (dwReason) {
>>> +
>>> +    case DLL_PROCESS_ATTACH:
>>> +        g_hinstDll = hinstDll;
>>> +        DisableThreadLibraryCalls(hinstDll);
>>> +        break;
>>> +    }
>>> +
>>> +    return TRUE;
>>> +}
>>

Seems fine I guess, though an "if" would be more idiomatic.

No more comments for this patch from me. As usual I don't insist on
fixing anything, I only raise points that you might want to address.

Laszlo

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze
  2013-06-27 15:01       ` Laszlo Ersek
@ 2013-06-27 22:25         ` Tomoki Sekiyama
  2013-06-28  7:05           ` Paolo Bonzini
  2013-06-28 10:44           ` Laszlo Ersek
  0 siblings, 2 replies; 70+ messages in thread
From: Tomoki Sekiyama @ 2013-06-27 22:25 UTC (permalink / raw)
  To: Laszlo Ersek
  Cc: libaiqing, mdroth, stefanha, qemu-devel, lcapitulino, vrozenfe,
	pbonzini, Seiji Aguchi, areis

On 6/27/13 11:01 , "Laszlo Ersek" <lersek@redhat.com> wrote:

>On 06/26/13 16:32, Laszlo Ersek wrote:
>> On 06/25/13 18:03, Laszlo Ersek wrote:
>>> On 06/06/13 17:06, Tomoki Sekiyama wrote:
>>>>+    chk(pColl->get_Count(&n));
>>>> +    for (i = n - 1; i >= 0; i--) {
>>>> +        chk(pColl->get_Item(i, (IDispatch **)&pObj));
>>>> +        chk(pObj->get_Value(_bstr_t(L"Name"), &var));
>>>> +        if (var == _variant_t(QGA_PROVIDER_LNAME)) {
>>>> +            printf("Removing COM+ Application: %S\n", (wchar_t
>>>>*)_bstr_t(var));
>
>(stderr)

Sure.

>>>> +            chk(pColl->Remove(i));
>>>> +        }
>
>I think you leak a pObj reference here, at the end of the iteration. The
>next round will set pObj to something else; I think we should call
>pObj->Release() here, and set pObj to NULL (for the case when this is
>the last iteration).

I will add "pObj->Release(); pObj = NULL;" here.

>I'm not sure if you're allowed to call pObj->Release() after the
>pColl()->Remove(i) call. So maybe call pObj->Release() in an else
>branch. (In this case however the out: logic should be modified as
>well.)

It's safe to call Release() after Remove(), by reference counter.

...

>>>> +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 = (lstrlen(data) + 1) * sizeof(TCHAR);
>
>I think we should drop the multiplication by sizeof(TCHAR), it's 1.
>According to MSDN, TCHAR could be something wider than "char" (ie.
>WCHAR) "for Unicode platforms".
>
>However in all your CreateRegistryKey() calls, the argument passed as
>"data" depends on sizeof(TCHAR)==1, directly or indirectly. I think it's
>best to be honest about it. For example,
>
>>>> +    } else {
>>>> +        size = 0;
>>>> +    }
>>>> +
>>>> +    ret = RegSetValueEx(hKey, value, 0, REG_SZ, (LPBYTE)data, size);
>>>> +    RegCloseKey(hKey);
>>>> +
>>>> +out:
>>>> +    if (ret != ERROR_SUCCESS) {
>>>> +        /* We cannot printf here, and need MessageBox to report an
>>>>error. */
>>>> +        errmsg_dialog(ret, "Cannot add registry ", key);
>
>right here we equate (const char *) with LPCTSTR (by virtue of the third
>arg being "key").
>
>You might also replace lstrlen() with strlen() for consistency.
>
>(Tangential anyhow.)

Right, I assume sizeof(TCHAR) == 1. I will take the simpler way.


>>>>+/* Register this dll as a VSS provider */
>>>> +STDAPI DllRegisterServer(void)
>>>> +{
...
>>>> +    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;
>>>> +    }
>>>> +
>>>> +    sprintf(key, "CLSID\\%s\\InprocServer32", g_szClsid);
>
>(you could reuse "key" from the previous sprintf())

OK.

...
> (indentation)
I will fix them.
...

>>>>+/* Support functions for _bstr_t in MinGW: Originally written by:
>>>>Diaa Sami */
>>>> +
>
>Where does this code originate from? What is its license?

This code is from MinGW User ML, and says "use this code however you like,
but don't remove the comment at the beginning".
But I will reimplement these by myself,

>>>> +void __stdcall _com_issue_error(HRESULT hr)
>>>> +{
>>>> +    printf("_com_issue_error() called with parameter HRESULT = %lu",
>>>>hr);
>>>> +}
>
>This wouldn't be hard to reimplement anyway, just print the message to
>stderr. Plus it's missing \n.

Maybe I can use errmsg() here.

>I googled the function name, and some people put up a message box here.
>Not sure under what circumstances this function is called.

It is used inside MinGW headers. I regard this as a user-customized error
notifier function.

>>>> +
>>>> +namespace _com_util
>>>> +{
>>>> +    char * __stdcall ConvertBSTRToString(BSTR bstr)
>>>> +    {
>>>> +        const unsigned int stringLength = lstrlenW(bstr);
>>>> +        char *const ascii = new char [stringLength + 1];
>>>> +
>>>> +        wcstombs(ascii, bstr, stringLength + 1);
>>>> +
>>>> +        return ascii;
>>>> +    }
>
>The BSTR, _bstr_t, LPCTSTR etc mess is incredible. Is BSTR just
>(wchar_t*)?

BSTR is usable like wchar_t*, but needs special allocator which prepend
size information at the memory area pointed.

>These COM interfaces seem broken by the way; how does one report a
>conversion error? wcstombs() is locale-dependent and can fail. I'll just
>pretend that whatever "bstr" contains in UTF-16 will always be
>representable in pure ASCII.

Hmm, althogh I believe only ASCII strings (which appear in this source) is
passed to this function, I will add some conversion error outputs.

Also, qemu-ga.exe actually use only ConvertStringToBSTR, so I will
omit ConvertBSTRToString.



>>>>+void LockModule(BOOL block)
>
>Did you mean "lock" instead of "block"?

Ah, it was bLock ... But will use lock here.

>>>>+STDMETHODIMP CQGAVSSEnumObject::QueryInterface(REFIID riid, void
>>>>**ppObj)
>>>> +{
>>>> +    if (riid == IID_IUnknown || riid == IID_IVssEnumObject) {
>>>> +        *ppObj =
>>>>static_cast<void*>(static_cast<IVssEnumObject*>(this));
>
>Storing the address of the base object, right?

Yes.

>Actually (based on what the other class does below), do you think it's
>right to return the base object's address for IID_IUnknown too, rather
>than static_cast<void*>(this)? ... We have a single base object here so
>in practice these addresses should be the same.

Both is OK I think, if IUnknown pointer value is valid to call
QueryInterface, AddRef and Release virtual methods, and is always the same
value for the same object to distinguish two objects.

>>>> +        AddRef();
>>>> +        return S_OK;
>>>> +    }
>>>> +    ppObj = NULL;
>
>Indirection operator missing ("*ppObj = NULL")?

Oops, I will fix this.

>>>>+STDMETHODIMP_(ULONG) CQGAVSSEnumObject::Release()
>>>> +{
>>>> +    long nRefCount = InterlockedDecrement(&m_nRefCount);
>>>> +    if (m_nRefCount == 0) {
>>>> +        delete this;
>
>I guess we're dead sure the object was allocated with "new".

Yes, it is only created by CQGAVssProvider::Query();

>>>>+STDMETHODIMP CQGAVssProvider::QueryInterface(REFIID riid, void
>>>>**ppObj)
>>>> +{
>>>> +    if (riid == IID_IUnknown) {
>>>> +        *ppObj = static_cast<void*>(this);
>>>> +        AddRef();
>>>> +        return S_OK;
>>>> +    } else if (riid == IID_IVssSoftwareSnapshotProvider) {
>
>(No "else" needed if "return" is the last statement in the "if"'s
>block.)

OK.

...

>>>> +STDMETHODIMP CQGAVssProvider::QueryRevertStatus(
>>>> +    VSS_PWSZ pwszVolume, IVssAsync **ppAsync)
>>>> +{
>>>> +    return S_OK;
>>>> +}
>
>Shouldn't you set *ppAsync to something here? (No idea, just asking --
>S_OK could imply something on output.)

This should be E_NOTIMPL, because we don't support revert via VSS.

>Same for IsVolumeSnapshotted() above.

Right, I will add return values.


>>>> +STDMETHODIMP CQGAVssProvider::CommitSnapshots(VSS_ID SnapshotSetId)
>>>> +{
...
>>>>+    SetEvent(hEvent);
>
>I think we should add a comment here -- this is where qemu-ga.exe /
>libvirt will create the snapshot.

OK.

>>>> +    if (WaitForSingleObject(hEvent2, 60*1000) != WAIT_OBJECT_0) {
>>>> +        hr = E_ABORT;
>>>> +    }
>>>> +
>>>> +    CloseHandle(hEvent2);
>>>> +    CloseHandle(hEvent);
>>>> +out:
>>>> +    return hr;
>>>> +}
>
>Seems correct in general.
>
>However I think you could shave off a few lines from this function by
>reorganizing the "out:" path, for example by moving CloseHandle(hEvent)
>there, and replacing the first goto with a return -- currently the
>goto's actually waste space; even plain returns would be more
>compressed.

I will replace goto with return.

>Second, maybe the magic constant 60*1000 (known from the VSS docs)
>should be a macro.

OK.


>>>> +/*
>>>> + * IVssProviderNotifications methods
>>>> + */
>>>> +
>>>> +STDMETHODIMP CQGAVssProvider::OnLoad(IUnknown *pCallback)
>>>> +{
>>>> +    return S_OK;
>>>> +}
>>>> +
>>>> +STDMETHODIMP CQGAVssProvider::OnUnload(BOOL bForceUnload)
>>>> +{
>>>> +    return S_OK;
>>>> +}
>
>(Side question: are these methods all abstract in the base class? Can we
>save a few LOCs by simply inheriting some functions?)

These are all abstract. :/


>>>>+STDMETHODIMP CQGAVssProviderFactory::QueryInterface(REFIID riid, void
>>>>**ppv)
>>>> +{
>>>> +    if (riid == IID_IUnknown || riid == IID_IClassFactory) {
>>>> +        *ppv = static_cast<void*>(this);
>
>Again, in practice the pointer value should be correct for both
>interfaces (single inheritance, only one base object), but I'd feel
>safer if we had two static casts. Does the C++ standard (or some C++
>ABI) guarantee that the first base object is always at offset 0 in the
>derived object? (In C, the standard requires that there be no padding at
>the beginning of a structure.)

OK, I will add double static_cast. (Actually these class methods are
 virtual, this should OK as vtable can be resolved....)


>>>> +        AddRef();
>>>> +        return S_OK;
>>>> +    }
>>>> +    *ppv = NULL;
>>>> +    return E_NOINTERFACE;
>>>> +}
>>>> +
>>>> +STDMETHODIMP_(ULONG) CQGAVssProviderFactory::AddRef()
>>>> +{
>>>> +    LockModule(TRUE);
>>>> +    return 2;
>>>> +}
>>>> +
>>>> +STDMETHODIMP_(ULONG) CQGAVssProviderFactory::Release()
>>>> +{
>>>> +    LockModule(FALSE);
>>>> +    return 1;
>>>> +}
>
>Would it be preferable to change the prototype of LockModule() to return
>the post-increment / post-decrement value of "g_nComObjsInUse" (ie.
>whatever InterlockedIncrement() or InterlockedDecrement() returns in
>LockModule()), and just forward that retval in these two functions? The
>values "2" and "1" seem quite arbitrary.
>
>Also, the private data member "m_nRefCount" is unused. This class has
>reference counting but no constructor or destructor.

Um, I will rework the reference counting for the factory class,
instead of omitting it.

>>>> +
>>>> +STDMETHODIMP CQGAVssProviderFactory::CreateInstance(
>>>> +    IUnknown *pUnknownOuter, REFIID iid, void **ppv)
>>>> +{
>>>> +    if (pUnknownOuter) {
>>>> +        return CLASS_E_NOAGGREGATION;
>>>> +    }
>>>> +    CQGAVssProvider *pObj = new CQGAVssProvider;
>>>> +    if (!pObj) {
>>>> +        return E_OUTOFMEMORY;
>>>> +    }
>
>(We generally assume that memory allocation never fails.)

Ah, OK...

>>>> +    return pObj->QueryInterface(iid, ppv);
>
>This may leak.
>
>If CQGAVssProvider::QueryInterface() doesn't recognize the requested
>interface, it will set *ppv to NULL, and the caller will have no chance
>to call Release() on it later. That last bit is actually correct (we
>haven't bumped the refcount to 1), but accordingly we should delete pObj
>here in that case.

True. I will fix this.

>>>> +STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
>>>> +{
>
>(Right, this is the factory factory function. Awesome :/)
>
>>>> +    static CQGAVssProviderFactory factory;
>>>> +
>>>> +    *ppv = NULL;
>>>> +    if (IsEqualCLSID(rclsid, CLSID_QGAVSSProvider)) {
>>>> +        return factory.QueryInterface(riid, ppv);
>>>> +    }
>>>> +    return CLASS_E_CLASSNOTAVAILABLE;
>>>> +}
>
>Not sure what to suggest here. I just don't like the factory object
>being static *and* having reference counting.
>
>... Basically you translate references to the factory object to
>references to the module. I guess I could see the logic in that if you
>deleted the "m_nRefCount" member. However the externally visible
>AddRef() and Release() return values are broken in any case. Somehow the
>existence of AddRef() and Release() seems fundamentally broken for a
>static object -- you simply can't go to refcount==0, which would be the
>only situation when the DLL could be removed.
>
>What about this:
>- factories would be objects allocated with "new",
>- real reference counting for them (with constructor and destructor
>  too),
>- the ctor/dtor would massage LockModule().
>
>In this aspect CQGAVssProviderFactory would work exactly like the
>CQGAVssProvider class, and DllGetClassObject() -- the factory factory
>method -- would work like CQGAVssProviderFactory::CreateInstance() --
>the factory method.

This sounds better than the current implementation.
I will rework like above.

>Of course I have no clue how a factory object must be released
>officially, I assume though with the usual ->Release() call, which can
>be optionally followed by DLL removal. These windows interfaces are
>utterly over-engineered.

Yeah, usually these stuff will be automatically generated by IDE
using C++ templates, but MinGW doesn't support such mechanism.

>>>> +STDAPI DllCanUnloadNow()
>>>> +{
>>>> +    return g_nComObjsInUse == 0 ? S_OK : S_FALSE;
>>>> +}
>
>Don't you need some kind of atomic or locked read here? We could read a
>stale value here. Granted, most stale values would err on the safe side
>(ie. read >0 instead of ==0), but in theory the other mistake is
>possible, no?

MSDN says "Simple reads and writes to properly-aligned 32bit variables
are atomic", and I couldn't find a function to atomic read provided.
(Maybe InterlockedCompareExchange(&g_nComObjsInUse, 0, 0)...?)

>>>> +EXTERN_C
>>>> +BOOL WINAPI DllMain(HINSTANCE hinstDll, DWORD dwReason, LPVOID
>>>>lpReserved)
>>>> +{
>>>> +    switch (dwReason) {
>>>> +
>>>> +    case DLL_PROCESS_ATTACH:
>>>> +        g_hinstDll = hinstDll;
>>>> +        DisableThreadLibraryCalls(hinstDll);
>>>> +        break;
>>>> +    }
>>>> +
>>>> +    return TRUE;
>>>> +}
>>>
>
>Seems fine I guess, though an "if" would be more idiomatic.

OK, make this in to if.


I really appreciate your review.

Thnaks,
Tomoki Sekiyama

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze
  2013-06-27 22:25         ` Tomoki Sekiyama
@ 2013-06-28  7:05           ` Paolo Bonzini
  2013-06-28 10:40             ` Laszlo Ersek
  2013-06-28 10:44           ` Laszlo Ersek
  1 sibling, 1 reply; 70+ messages in thread
From: Paolo Bonzini @ 2013-06-28  7:05 UTC (permalink / raw)
  To: Tomoki Sekiyama
  Cc: libaiqing, mdroth, stefanha, qemu-devel, lcapitulino, vrozenfe,
	Seiji Aguchi, Laszlo Ersek, areis

Il 28/06/2013 00:25, Tomoki Sekiyama ha scritto:
>>>>> >>>> +STDMETHODIMP CQGAVssProviderFactory::CreateInstance(
>>>>> >>>> +    IUnknown *pUnknownOuter, REFIID iid, void **ppv)
>>>>> >>>> +{
>>>>> >>>> +    if (pUnknownOuter) {
>>>>> >>>> +        return CLASS_E_NOAGGREGATION;
>>>>> >>>> +    }
>>>>> >>>> +    CQGAVssProvider *pObj = new CQGAVssProvider;
>>>>> >>>> +    if (!pObj) {
>>>>> >>>> +        return E_OUTOFMEMORY;
>>>>> >>>> +    }
>> >
>> >(We generally assume that memory allocation never fails.)
> Ah, OK...

Actually, we do because we use g_malloc/g_free.  The functions exit on
memory allocation failure.  I'm not sure the same is true of the new
operator... doesn't it throw an exception on allocation failure (that's
what I vaguely remember)?

Also, this is not running in the context of qemu-ga, so I think it is
better to be more conservative and trap memory allocation failure.


Paolo

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze
  2013-06-28 10:40             ` Laszlo Ersek
@ 2013-06-28 10:40               ` Paolo Bonzini
  2013-06-28 17:18                 ` Tomoki Sekiyama
  0 siblings, 1 reply; 70+ messages in thread
From: Paolo Bonzini @ 2013-06-28 10:40 UTC (permalink / raw)
  To: Laszlo Ersek
  Cc: libaiqing, mdroth, stefanha, qemu-devel, lcapitulino, vrozenfe,
	Tomoki Sekiyama, Seiji Aguchi, areis

Il 28/06/2013 12:40, Laszlo Ersek ha scritto:
>> > Also, this is not running in the context of qemu-ga, so I think it is
>> > better to be more conservative and trap memory allocation failure.
> In that case other "new" calls must assume the nothrow form too, plus
> other allocation functions should be checked as well (eg.
> SysAllocStringLen(), although its only use might be in the function that
> Tomoki plans to remove anyway).

Agreed (nothrow or try...catch, depending on what's better for the C++
runtime; by the way, Tomoki-san, I suspect this will require libstdc++
when compiled with GCC---can you confirm?).

Thanks,

Paolo

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze
  2013-06-28  7:05           ` Paolo Bonzini
@ 2013-06-28 10:40             ` Laszlo Ersek
  2013-06-28 10:40               ` Paolo Bonzini
  0 siblings, 1 reply; 70+ messages in thread
From: Laszlo Ersek @ 2013-06-28 10:40 UTC (permalink / raw)
  To: Paolo Bonzini
  Cc: libaiqing, mdroth, stefanha, qemu-devel, lcapitulino, vrozenfe,
	Tomoki Sekiyama, Seiji Aguchi, areis

On 06/28/13 09:05, Paolo Bonzini wrote:
> Il 28/06/2013 00:25, Tomoki Sekiyama ha scritto:
>>>>>>>>>> +STDMETHODIMP CQGAVssProviderFactory::CreateInstance(
>>>>>>>>>> +    IUnknown *pUnknownOuter, REFIID iid, void **ppv)
>>>>>>>>>> +{
>>>>>>>>>> +    if (pUnknownOuter) {
>>>>>>>>>> +        return CLASS_E_NOAGGREGATION;
>>>>>>>>>> +    }
>>>>>>>>>> +    CQGAVssProvider *pObj = new CQGAVssProvider;
>>>>>>>>>> +    if (!pObj) {
>>>>>>>>>> +        return E_OUTOFMEMORY;
>>>>>>>>>> +    }
>>>>
>>>> (We generally assume that memory allocation never fails.)
>> Ah, OK...
> 
> Actually, we do because we use g_malloc/g_free.  The functions exit on
> memory allocation failure.  I'm not sure the same is true of the new
> operator... doesn't it throw an exception on allocation failure (that's
> what I vaguely remember)?

It throws std::bad_alloc on failure. There's another new operator (the
nothrow form) thar returns 0 on failure.

  18.4.1.1 Single-object forms [lib.new.delete.single]; p9:

    [Example:
      T* p1 = new T;          // throws bad_alloc if it fails
      T* p2 = new(nothrow) T; // returns 0 if it fails
    —end example]

(
"nothrow" in the above is std::nothrow, an object with static storage
duration, of type "nothrow_t" -- it's a dummy argument so that operator
new() can have to prototypes. It is passed by const reference.

  18.4 Dynamic memory management [lib.support.dynamic]; p1:

  namespace std {
    class bad_alloc;
    struct nothrow_t {};
    extern const nothrow_t nothrow;
    /* ... */
  }

  void* operator new(std::size_t size) throw(std::bad_alloc);
  void* operator new(std::size_t size, const std::nothrow_t&) throw();
)

As far as I can remember, older C++ implementations had problems with
bad_alloc. I believe though that any gcc release frome after the stone
age should handle this correctly; see also -fcheck-new.

Of course I have no idea what happens when a C++ exception tries to
propagate past a "DLL boundary".


> Also, this is not running in the context of qemu-ga, so I think it is
> better to be more conservative and trap memory allocation failure.

In that case other "new" calls must assume the nothrow form too, plus
other allocation functions should be checked as well (eg.
SysAllocStringLen(), although its only use might be in the function that
Tomoki plans to remove anyway).

Laszlo

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 05/10] qemu-ga: Add configure options to specify path to Windows/VSS SDK
  2013-06-25 11:16   ` Laszlo Ersek
@ 2013-06-28 10:43     ` Daniel P. Berrange
  2013-06-28 10:54       ` Paolo Bonzini
  2013-06-28 11:05       ` Laszlo Ersek
  0 siblings, 2 replies; 70+ messages in thread
From: Daniel P. Berrange @ 2013-06-28 10:43 UTC (permalink / raw)
  To: Laszlo Ersek
  Cc: libaiqing, qemu-devel, stefanha, mdroth, lcapitulino, vrozenfe,
	Tomoki Sekiyama, pbonzini, seiji.aguchi, areis

On Tue, Jun 25, 2013 at 01:16:38PM +0200, Laszlo Ersek wrote:
> On 06/06/13 17:06, Tomoki Sekiyama wrote:
> > To enable VSS support in qemu-ga for Windows, header files included in
> > VSS SDK are required.
> > The VSS support is enabled by the configure option like below:
> >   ./configure --with-vss-sdk="/path/to/VSS SDK"
> > 
> > If the path is omitted, it tries to search the headers from default paths
> > and VSS support is enabled only if the SDK is found.
> > VSS support is disabled if --without-vss-sdk or --with-vss-sdk=no is
> > specified.
> > 
> > VSS SDK is available from:
> >   http://www.microsoft.com/en-us/download/details.aspx?id=23490
> > 
> > To cross-compile using mingw, you need to setup the SDK on Windows
> > environments to extract headers. You can also extract the SDK headers on
> > POSIX environments using scripts/extract-vss-headers and msitools.

What is the license of the VSS SDK ?  In particular is the license
compatible with QEMU to allow us to link to it from GPL code ?

Daniel
-- 
|: http://berrange.com      -o-    http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org              -o-             http://virt-manager.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org       -o-       http://live.gnome.org/gtk-vnc :|

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze
  2013-06-27 22:25         ` Tomoki Sekiyama
  2013-06-28  7:05           ` Paolo Bonzini
@ 2013-06-28 10:44           ` Laszlo Ersek
  1 sibling, 0 replies; 70+ messages in thread
From: Laszlo Ersek @ 2013-06-28 10:44 UTC (permalink / raw)
  To: Tomoki Sekiyama
  Cc: libaiqing, mdroth, stefanha, qemu-devel, lcapitulino, vrozenfe,
	pbonzini, Seiji Aguchi, areis

>>>>> +STDAPI DllCanUnloadNow()
>>>>> +{
>>>>> +    return g_nComObjsInUse == 0 ? S_OK : S_FALSE;
>>>>> +}
>>
>> Don't you need some kind of atomic or locked read here? We could read a
>> stale value here. Granted, most stale values would err on the safe side
>> (ie. read >0 instead of ==0), but in theory the other mistake is
>> possible, no?
> 
> MSDN says "Simple reads and writes to properly-aligned 32bit variables
> are atomic", and I couldn't find a function to atomic read provided.
> (Maybe InterlockedCompareExchange(&g_nComObjsInUse, 0, 0)...?)

Alright then. Thanks!
Laszlo

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 05/10] qemu-ga: Add configure options to specify path to Windows/VSS SDK
  2013-06-28 10:43     ` Daniel P. Berrange
@ 2013-06-28 10:54       ` Paolo Bonzini
  2013-06-28 11:01         ` Daniel P. Berrange
  2013-06-28 11:05       ` Laszlo Ersek
  1 sibling, 1 reply; 70+ messages in thread
From: Paolo Bonzini @ 2013-06-28 10:54 UTC (permalink / raw)
  To: Daniel P. Berrange
  Cc: libaiqing, seiji.aguchi, stefanha, mdroth, qemu-devel, vrozenfe,
	Tomoki Sekiyama, lcapitulino, Laszlo Ersek, areis

Il 28/06/2013 12:43, Daniel P. Berrange ha scritto:
> On Tue, Jun 25, 2013 at 01:16:38PM +0200, Laszlo Ersek wrote:
>> On 06/06/13 17:06, Tomoki Sekiyama wrote:
>>> To enable VSS support in qemu-ga for Windows, header files included in
>>> VSS SDK are required.
>>> The VSS support is enabled by the configure option like below:
>>>   ./configure --with-vss-sdk="/path/to/VSS SDK"
>>>
>>> If the path is omitted, it tries to search the headers from default paths
>>> and VSS support is enabled only if the SDK is found.
>>> VSS support is disabled if --without-vss-sdk or --with-vss-sdk=no is
>>> specified.
>>>
>>> VSS SDK is available from:
>>>   http://www.microsoft.com/en-us/download/details.aspx?id=23490
>>>
>>> To cross-compile using mingw, you need to setup the SDK on Windows
>>> environments to extract headers. You can also extract the SDK headers on
>>> POSIX environments using scripts/extract-vss-headers and msitools.
> 
> What is the license of the VSS SDK ?  In particular is the license
> compatible with QEMU to allow us to link to it from GPL code ?

Doesn't it fall under the "operating system component" exception?

Paolo

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 05/10] qemu-ga: Add configure options to specify path to Windows/VSS SDK
  2013-06-28 10:54       ` Paolo Bonzini
@ 2013-06-28 11:01         ` Daniel P. Berrange
  2013-06-28 11:18           ` Paolo Bonzini
  0 siblings, 1 reply; 70+ messages in thread
From: Daniel P. Berrange @ 2013-06-28 11:01 UTC (permalink / raw)
  To: Paolo Bonzini
  Cc: libaiqing, seiji.aguchi, stefanha, mdroth, qemu-devel, vrozenfe,
	Tomoki Sekiyama, lcapitulino, Laszlo Ersek, areis

On Fri, Jun 28, 2013 at 12:54:04PM +0200, Paolo Bonzini wrote:
> Il 28/06/2013 12:43, Daniel P. Berrange ha scritto:
> > On Tue, Jun 25, 2013 at 01:16:38PM +0200, Laszlo Ersek wrote:
> >> On 06/06/13 17:06, Tomoki Sekiyama wrote:
> >>> To enable VSS support in qemu-ga for Windows, header files included in
> >>> VSS SDK are required.
> >>> The VSS support is enabled by the configure option like below:
> >>>   ./configure --with-vss-sdk="/path/to/VSS SDK"
> >>>
> >>> If the path is omitted, it tries to search the headers from default paths
> >>> and VSS support is enabled only if the SDK is found.
> >>> VSS support is disabled if --without-vss-sdk or --with-vss-sdk=no is
> >>> specified.
> >>>
> >>> VSS SDK is available from:
> >>>   http://www.microsoft.com/en-us/download/details.aspx?id=23490
> >>>
> >>> To cross-compile using mingw, you need to setup the SDK on Windows
> >>> environments to extract headers. You can also extract the SDK headers on
> >>> POSIX environments using scripts/extract-vss-headers and msitools.
> > 
> > What is the license of the VSS SDK ?  In particular is the license
> > compatible with QEMU to allow us to link to it from GPL code ?
> 
> Doesn't it fall under the "operating system component" exception?

Maybe, maybe no. I think it depends what the VSS SDK actually includes.

IANAL, but my interpretation is that the exception would only apply
to stuff that is distributed as part of the base Windows install. Since
this is a separate download from Microsoft, it isn't clear to me that
the exception would apply in this case.

Does the VSS SDK actually include the .dll library that qemu-ga would
be linking aginst, or is the .dll part of base windows install, and
VSS SDK merely includes the header files needed for compilation ?

If in doubt, I always tend to err on the side of caution when dealing
with licensing for any microsoft provided software.

Daniel
-- 
|: http://berrange.com      -o-    http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org              -o-             http://virt-manager.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org       -o-       http://live.gnome.org/gtk-vnc :|

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 05/10] qemu-ga: Add configure options to specify path to Windows/VSS SDK
  2013-06-28 10:43     ` Daniel P. Berrange
  2013-06-28 10:54       ` Paolo Bonzini
@ 2013-06-28 11:05       ` Laszlo Ersek
  1 sibling, 0 replies; 70+ messages in thread
From: Laszlo Ersek @ 2013-06-28 11:05 UTC (permalink / raw)
  To: Daniel P. Berrange, Anthony Liguori
  Cc: libaiqing, qemu-devel, stefanha, mdroth, lcapitulino, vrozenfe,
	Tomoki Sekiyama, pbonzini, seiji.aguchi, areis

On 06/28/13 12:43, Daniel P. Berrange wrote:
> On Tue, Jun 25, 2013 at 01:16:38PM +0200, Laszlo Ersek wrote:
>> On 06/06/13 17:06, Tomoki Sekiyama wrote:
>>> To enable VSS support in qemu-ga for Windows, header files included in
>>> VSS SDK are required.
>>> The VSS support is enabled by the configure option like below:
>>>   ./configure --with-vss-sdk="/path/to/VSS SDK"
>>>
>>> If the path is omitted, it tries to search the headers from default paths
>>> and VSS support is enabled only if the SDK is found.
>>> VSS support is disabled if --without-vss-sdk or --with-vss-sdk=no is
>>> specified.
>>>
>>> VSS SDK is available from:
>>>   http://www.microsoft.com/en-us/download/details.aspx?id=23490
>>>
>>> To cross-compile using mingw, you need to setup the SDK on Windows
>>> environments to extract headers. You can also extract the SDK headers on
>>> POSIX environments using scripts/extract-vss-headers and msitools.
> 
> What is the license of the VSS SDK ?  In particular is the license
> compatible with QEMU to allow us to link to it from GPL code ?

Thanks for raising this -- I think we should first figure out what
constitutes derivative work of what in this case (according to the FSF's
interpretation). I'd like to take a passive (r/o) role in that
discussion. I'm adding Anthony to the To: list.

Thanks,
Laszlo

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 05/10] qemu-ga: Add configure options to specify path to Windows/VSS SDK
  2013-06-28 11:01         ` Daniel P. Berrange
@ 2013-06-28 11:18           ` Paolo Bonzini
  2013-06-28 11:30             ` Daniel P. Berrange
  0 siblings, 1 reply; 70+ messages in thread
From: Paolo Bonzini @ 2013-06-28 11:18 UTC (permalink / raw)
  To: Daniel P. Berrange
  Cc: libaiqing, seiji.aguchi, stefanha, mdroth, qemu-devel, vrozenfe,
	Tomoki Sekiyama, lcapitulino, Laszlo Ersek, areis

Il 28/06/2013 13:01, Daniel P. Berrange ha scritto:
>>> What is the license of the VSS SDK ?  In particular is the license
>>> compatible with QEMU to allow us to link to it from GPL code ?
>>
>> Doesn't it fall under the "operating system component" exception?
> 
> Maybe, maybe no. I think it depends what the VSS SDK actually includes.
> 
> IANAL, but my interpretation is that the exception would only apply
> to stuff that is distributed as part of the base Windows install. Since
> this is a separate download from Microsoft, it isn't clear to me that
> the exception would apply in this case.
> 
> Does the VSS SDK actually include the .dll library that qemu-ga would
> be linking aginst, or is the .dll part of base windows install, and
> VSS SDK merely includes the header files needed for compilation ?

Only the headers, the IDL files, and the "import libraries":

Program Files/Microsoft/VSSSDK72/lib/winxp/obj/i386/vss_uuid.lib
Program Files/Microsoft/VSSSDK72/lib/winxp/obj/i386/vssapi.lib
Program Files/Microsoft/VSSSDK72/lib/win2003/obj/ia64/vss_uuid.lib
Program Files/Microsoft/VSSSDK72/lib/win2003/obj/ia64/vssapi.lib
Program Files/Microsoft/VSSSDK72/lib/win2003/obj/i386/vssapi.lib
Program Files/Microsoft/VSSSDK72/lib/win2003/obj/i386/vss_uuid.lib
Program Files/Microsoft/VSSSDK72/lib/win2003/obj/amd64/vss_uuid.lib
Program Files/Microsoft/VSSSDK72/lib/win2003/obj/amd64/vssapi.lib
Program Files/Microsoft/VSSSDK72/inc/winxp/vss.h
Program Files/Microsoft/VSSSDK72/inc/winxp/vswriter.h
Program Files/Microsoft/VSSSDK72/inc/winxp/vsbackup.h
Program Files/Microsoft/VSSSDK72/inc/win2003/vdslun.h
Program Files/Microsoft/VSSSDK72/inc/win2003/vswriter.h
Program Files/Microsoft/VSSSDK72/inc/win2003/vsmgmt.h
Program Files/Microsoft/VSSSDK72/inc/win2003/vscoordint.h
Program Files/Microsoft/VSSSDK72/inc/win2003/vscoordint.idl
Program Files/Microsoft/VSSSDK72/inc/win2003/vsswprv.idl
Program Files/Microsoft/VSSSDK72/inc/win2003/vsswprv.h
Program Files/Microsoft/VSSSDK72/inc/win2003/vss.idl
Program Files/Microsoft/VSSSDK72/inc/win2003/vss.h
Program Files/Microsoft/VSSSDK72/inc/win2003/vdslun.idl
Program Files/Microsoft/VSSSDK72/inc/win2003/vsbackup.h
Program Files/Microsoft/VSSSDK72/inc/win2003/vsprov.idl
Program Files/Microsoft/VSSSDK72/inc/win2003/vsprov.h
Program Files/Microsoft/VSSSDK72/inc/win2003/vsmgmt.idl

Paolo

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 05/10] qemu-ga: Add configure options to specify path to Windows/VSS SDK
  2013-06-28 11:18           ` Paolo Bonzini
@ 2013-06-28 11:30             ` Daniel P. Berrange
  2013-06-28 12:18               ` Paolo Bonzini
  0 siblings, 1 reply; 70+ messages in thread
From: Daniel P. Berrange @ 2013-06-28 11:30 UTC (permalink / raw)
  To: Paolo Bonzini
  Cc: libaiqing, seiji.aguchi, stefanha, mdroth, qemu-devel, vrozenfe,
	Tomoki Sekiyama, lcapitulino, Laszlo Ersek, areis

On Fri, Jun 28, 2013 at 01:18:10PM +0200, Paolo Bonzini wrote:
> Il 28/06/2013 13:01, Daniel P. Berrange ha scritto:
> >>> What is the license of the VSS SDK ?  In particular is the license
> >>> compatible with QEMU to allow us to link to it from GPL code ?
> >>
> >> Doesn't it fall under the "operating system component" exception?
> > 
> > Maybe, maybe no. I think it depends what the VSS SDK actually includes.
> > 
> > IANAL, but my interpretation is that the exception would only apply
> > to stuff that is distributed as part of the base Windows install. Since
> > this is a separate download from Microsoft, it isn't clear to me that
> > the exception would apply in this case.
> > 
> > Does the VSS SDK actually include the .dll library that qemu-ga would
> > be linking aginst, or is the .dll part of base windows install, and
> > VSS SDK merely includes the header files needed for compilation ?
> 
> Only the headers, the IDL files, and the "import libraries":
> 
> Program Files/Microsoft/VSSSDK72/lib/winxp/obj/i386/vss_uuid.lib
> Program Files/Microsoft/VSSSDK72/lib/winxp/obj/i386/vssapi.lib
> Program Files/Microsoft/VSSSDK72/lib/win2003/obj/ia64/vss_uuid.lib
> Program Files/Microsoft/VSSSDK72/lib/win2003/obj/ia64/vssapi.lib
> Program Files/Microsoft/VSSSDK72/lib/win2003/obj/i386/vssapi.lib
> Program Files/Microsoft/VSSSDK72/lib/win2003/obj/i386/vss_uuid.lib
> Program Files/Microsoft/VSSSDK72/lib/win2003/obj/amd64/vss_uuid.lib
> Program Files/Microsoft/VSSSDK72/lib/win2003/obj/amd64/vssapi.lib
> Program Files/Microsoft/VSSSDK72/inc/winxp/vss.h
> Program Files/Microsoft/VSSSDK72/inc/winxp/vswriter.h
> Program Files/Microsoft/VSSSDK72/inc/winxp/vsbackup.h
> Program Files/Microsoft/VSSSDK72/inc/win2003/vdslun.h
> Program Files/Microsoft/VSSSDK72/inc/win2003/vswriter.h
> Program Files/Microsoft/VSSSDK72/inc/win2003/vsmgmt.h
> Program Files/Microsoft/VSSSDK72/inc/win2003/vscoordint.h
> Program Files/Microsoft/VSSSDK72/inc/win2003/vscoordint.idl
> Program Files/Microsoft/VSSSDK72/inc/win2003/vsswprv.idl
> Program Files/Microsoft/VSSSDK72/inc/win2003/vsswprv.h
> Program Files/Microsoft/VSSSDK72/inc/win2003/vss.idl
> Program Files/Microsoft/VSSSDK72/inc/win2003/vss.h
> Program Files/Microsoft/VSSSDK72/inc/win2003/vdslun.idl
> Program Files/Microsoft/VSSSDK72/inc/win2003/vsbackup.h
> Program Files/Microsoft/VSSSDK72/inc/win2003/vsprov.idl
> Program Files/Microsoft/VSSSDK72/inc/win2003/vsprov.h
> Program Files/Microsoft/VSSSDK72/inc/win2003/vsmgmt.idl

That sounds more reasonable then from the POV of the gpl system
components exception.

BTW, I notice that mingw64 includes the vss.h header and vssapi.a library,
so I wonder if that's sufficient to compile this code, without needing
to use the VSS SDK download at all.

Daniel
-- 
|: http://berrange.com      -o-    http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org              -o-             http://virt-manager.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org       -o-       http://live.gnome.org/gtk-vnc :|

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 05/10] qemu-ga: Add configure options to specify path to Windows/VSS SDK
  2013-06-28 11:30             ` Daniel P. Berrange
@ 2013-06-28 12:18               ` Paolo Bonzini
  0 siblings, 0 replies; 70+ messages in thread
From: Paolo Bonzini @ 2013-06-28 12:18 UTC (permalink / raw)
  To: Daniel P. Berrange
  Cc: libaiqing, stefanha, mdroth, qemu-devel, vrozenfe,
	Tomoki Sekiyama, lcapitulino, seiji.aguchi, Laszlo Ersek, areis

Il 28/06/2013 13:30, Daniel P. Berrange ha scritto:
> > > > Does the VSS SDK actually include the .dll library that qemu-ga would
> > > > be linking aginst, or is the .dll part of base windows install, and
> > > > VSS SDK merely includes the header files needed for compilation ?
> > 
> > Only the headers, the IDL files, and the "import libraries":
> 
> That sounds more reasonable then from the POV of the gpl system
> components exception.
> 
> BTW, I notice that mingw64 includes the vss.h header and vssapi.a library,
> so I wonder if that's sufficient to compile this code, without needing
> to use the VSS SDK download at all.

No, it is not unfortunately.  It only defines a small subset of the API
(IVssAsync and IVssEnumObject).

Paolo

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze
  2013-06-28 10:40               ` Paolo Bonzini
@ 2013-06-28 17:18                 ` Tomoki Sekiyama
  0 siblings, 0 replies; 70+ messages in thread
From: Tomoki Sekiyama @ 2013-06-28 17:18 UTC (permalink / raw)
  To: Paolo Bonzini, Laszlo Ersek
  Cc: libaiqing, mdroth, stefanha, qemu-devel, lcapitulino, vrozenfe,
	Seiji Aguchi, areis

On 6/28/13 6:40 , "Paolo Bonzini" <pbonzini@redhat.com> wrote:

>Il 28/06/2013 12:40, Laszlo Ersek ha scritto:
>>> > Also, this is not running in the context of qemu-ga, so I think it is
>>> > better to be more conservative and trap memory allocation failure.
>> In that case other "new" calls must assume the nothrow form too, plus
>> other allocation functions should be checked as well (eg.
>> SysAllocStringLen(), although its only use might be in the function that
>> Tomoki plans to remove anyway).
>
>Agreed (nothrow or try...catch, depending on what's better for the C++
>runtime;

I tried something like 'main(){ for(;;) new int[999]; }' and it caused:

Terminate called after throwing an instance of 'std::bad_alloc'
  what():  std::bad_alloc

abnormal program termination


I should catch and handle this.

> by the way, Tomoki-san, I suspect this will require libstdc++
>when compiled with GCC---can you confirm?).

Yes, we need "libstdc++-6.dll" from MinGW to run qemu-ga.exe.


>Thanks,
>
>Paolo

Thanks,
Tomoki Sekiyama

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 07/10] qemu-ga: Add Windows VSS requester to quiesce applications and filesystems
  2013-06-06 15:06 ` [Qemu-devel] [PATCH v4 07/10] qemu-ga: Add Windows VSS requester to quiesce applications and filesystems Tomoki Sekiyama
@ 2013-06-28 18:01   ` Laszlo Ersek
  2013-06-28 18:34     ` Laszlo Ersek
  2013-06-30  1:21     ` Tomoki Sekiyama
  0 siblings, 2 replies; 70+ messages in thread
From: Laszlo Ersek @ 2013-06-28 18:01 UTC (permalink / raw)
  To: Tomoki Sekiyama
  Cc: libaiqing, mdroth, stefanha, qemu-devel, lcapitulino, vrozenfe,
	pbonzini, seiji.aguchi, areis

On 06/06/13 17:06, Tomoki Sekiyama wrote:

> diff --git a/qga/vss-win32-requester.h b/qga/vss-win32-requester.h
> new file mode 100644
> index 0000000..f180f56
> --- /dev/null
> +++ b/qga/vss-win32-requester.h
> @@ -0,0 +1,31 @@
> +/*
> + * QEMU Guest Agent VSS Requester declarations
> + *
> + * Copyright Hitachi Data Systems Corp. 2013
> + *
> + * Authors:
> + *  Tomoki Sekiyama   <tomoki.sekiyama@hds.com>
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2 or later.
> + * See the COPYING file in the top-level directory.
> + */
> +
> +#ifndef VSS_WIN32_REQUESTER_H
> +#define VSS_WIN32_REQUESTER_H
> +
> +#ifdef __cplusplus
> +extern "C" {
> +#endif
> +
> +HRESULT vss_init(void);

Can you include the mingw header that defines the HRESULT type? As far
as I know we like to make headers standalone.

(OTOH I vaguely recall a patch where the order between a mingw header
and a (generated?) trace header could cause a build error... I think it
would be worth trying still.)


> +void vss_deinit(void);
> +int vss_initialized(void);

Since this is qemu-ga / qemu code, I think a "bool" return type would be
more usual. (The current prototype is correct too of course.)


> +
> +void qga_vss_fsfreeze_freeze(int *nr_volume, struct Error **err);
> +void qga_vss_fsfreeze_thaw(int *nr_volume, struct Error **err);

Can you drop the "struct" word in these prototypes?


> +
> +#ifdef __cplusplus
> +}
> +#endif
> +
> +#endif
>
>

> diff --git a/qga/vss-win32-requester.cpp b/qga/vss-win32-requester.cpp
> new file mode 100644
> index 0000000..7784926
> --- /dev/null
> +++ b/qga/vss-win32-requester.cpp
> @@ -0,0 +1,419 @@
> +/*
> + * QEMU Guest Agent win32 VSS Requester implementations
> + *
> + * Copyright Hitachi Data Systems Corp. 2013
> + *
> + * Authors:
> + *  Tomoki Sekiyama   <tomoki.sekiyama@hds.com>
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2 or later.
> + * See the COPYING file in the top-level directory.
> + */
> +
> +#include <stdio.h>
> +#include <assert.h>

Can you remove this #include and replace all assert() occurrences with
g_assert()?


> +extern "C" {
> +#include "guest-agent-core.h"
> +}
> +#include "vss-win32-requester.h"
> +#include "vss-win32-provider.h"
> +#include "vss-win32.h"
> +#include "inc/win2003/vswriter.h"
> +#include "inc/win2003/vsbackup.h"
> +
> +/* Functions in VSSAPI.DLL */
> +typedef HRESULT(STDAPICALLTYPE * t_CreateVssBackupComponents)(
> +    OUT IVssBackupComponents**);
> +typedef void(APIENTRY * t_VssFreeSnapshotProperties)(IN VSS_SNAPSHOT_PROP*);
> +
> +static t_CreateVssBackupComponents _CreateVssBackupComponents;
> +static t_VssFreeSnapshotProperties _VssFreeSnapshotProperties;

I apologize in advance for splitting hairs, but :)

  17.4.3.1.2 Global names [lib.global.names]; p1

  Certain sets of names and function signatures are always reserved to
  the implementation:

  - Each name that contains a double underscore (__) or begins with an
    underscore followed by an uppercase letter (2.11) is reserved to the
    implementation for any use.

Unless there's a pressing reason, could you drop the leading
underscores?


> +static IVssBackupComponents *pVssbc;
> +static IVssAsync *pAsyncSnapshot;
> +static HMODULE hLib;
> +static HANDLE hEvent = INVALID_HANDLE_VALUE, hEvent2 = INVALID_HANDLE_VALUE;
> +static int cFrozenVols;

Can you decorate each of these static variables with a short comment? It
does get clear(er) what they are used for by reading the code, but the
comments would save others time.

Also I'd recommend grouping them differently:

- first group: long term objects related to VSSAPI.DLL and released
  *only* in vss_deinit(): hLib, _CreateVssBackupComponents,
  _VssFreeSnapshotProperties;

- second group: objects that make sense only in preparation for, during,
  and right after a freeze: pVssbc, pAsyncSnapshot, hEvent, hEvent2,
  cFrozenVols. (You could even introduce a struct for these, but that's
  just an idea.)


> +
> +GCC_FMT_ATTR(1, 2)
> +static void errmsg(const char *fmt, ...)
> +{
> +    va_list ap;
> +    va_start(ap, fmt);
> +    char *msg = g_strdup_vprintf(fmt, ap);
> +    va_end(ap);
> +    MessageBox(NULL, msg, "Error in QEMU guest agent", MB_OK | MB_ICONWARNING);
> +    g_free(msg);
> +}

(Still splitting hairs, bear with me please:) can you rename this
function to errmsg_dialog() or something similar, for consistency with
06/10?


> +
> +static void error_set_win32(Error **errp, DWORD err,
> +                            ErrorClass eclass, const char *text)
> +{
> +    char *msg = NULL, *nul = strchr(text, '(');
> +    int len = nul ? nul - text : -1;
> +
> +    /* print error message in native encoding */
> +    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
> +                  FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
> +                  NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
> +                  (char *)&msg, 0, NULL);
> +    printf("%.*s. (Error: %lx) %s\n", len, text, err, msg);
> +    LocalFree(msg);

I don't think you should print anything here. error_set*() functions
just set the error when asked by a function F1. It is up to F1's caller,
let's call it F2, to propagate or to consume (= print to stderr or to
the monitor) the error received.

Errors encountered when processing VSS freeze/thaw requests should be
returned to the QMP caller like any other command error.

> +
> +    /* set error message in UTF-8 encoding */
> +    msg = g_win32_error_message(err);

Can we pass "err" (which is a HRESULT === DWORD === "long unsigned") to
g_win32_error_message()? The latter takes an "int".

MSDN seems to imply there are different "error code spaces", see
HRESULT_FROM_WIN32(). Anyway I'll assume we can do this.


> +    error_set(errp, eclass, "%.*s. (Error: %lx) %s", len, text, err, msg);
> +    g_free(msg);
> +}
> +#define error_setg_win32(errp, err, text) \
> +    error_set_win32(errp, err, ERROR_CLASS_GENERIC_ERROR, text)

This approach is fine in general for the VSS DLL I think, but for
qemu-ga.exe, I would propose a more flexible interface (could even
belong into "util/error.c", in an #ifdef _WIN32 block). It would imitate
error_setg_errno() in that it allowed the caller to pass in a format
string as well:

(a) Create a copy of error_set_errno() with "errno"/"strerror" replaced
by "win32"/"g_win32_error_message":

    void error_set_win32(Error **errp, int win32, 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 != 0) {
            char *msg2 = g_win32_error_message(win32);

            err->msg = g_strdup_printf("%s: %s (error: %x)", msg1, msg2,
                                       (unsigned)win32);
            g_free(msg2);
            g_free(msg1);
        } else {
            err->msg = msg1;
        }
        va_end(ap);
        err->err_class = err_class;

        *errp = err;
    }

(b) error_setg_win32() should imitate error_setg_errno():

    #define error_setg_win32(err, win32, fmt, ...) \
        error_set_errno(err, win32, ERROR_CLASS_GENERIC_ERROR, fmt, ## __VA_ARGS__)


This is just a suggestion, but it would bring your code closer to "qemu
coding style", and render free-form error messages more convenient for
you (eg. you could drop the snprintf() stuff in
qga_vss_fsfreeze_freeze()).


> +
> +#define __chk(status, text, errp, err_label)    \
> +    do {                                        \
> +        HRESULT __hr = (status);                \
> +        if (FAILED(__hr)) {                     \
> +            error_setg_win32(errp, __hr, text); \
> +            goto err_label;                     \
> +        }                                       \
> +    } while (0)
> +
> +#define _chk(status, msg) __chk(status, "Failed to " msg, err, out)
> +#define chk(status)  __chk(status, "Failed to " #status, err, out)

I'd prefer if you hand-expanded these macros in qemu-ga code. The
error_setg_win32() version suggested above should make it easy to format
any error message. You'd have to open-code the "goto" and the specific
label naturally, but that would also match the rest of qemu code better
IMHO. (More work to write once, but easier to read many times.)


> +
> +
> +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;
> +}

Hm. Are we expecting spurious wakeups in Wait()? MSDN sayeth if Wait()
returns with S_OK, then the async op completed, and QueryStatus() will
return the *final* status of the async op (emphasis mine). Meaning, I
would think, VSS_S_ASYNC_CANCELLED or VSS_S_ASYNC_FINISHED.

Anyhow, the function appears correct.


> +
> +HRESULT vss_init(void)
> +{
> +    HRESULT hr;
> +
> +    hr = VSSCheckOSVersion();
> +    if (hr == S_FALSE) {
> +        return hr;
> +    }

Ah, I think you may have addressed this already -- this is the reason
"vss-win32-provider/install.o" is needed when linking qemu-ga.exe.

VSSCheckOSVersion() is quite small, relies on no C++ *or* VSS features,
and is arguably a utility function. Can you move it to one of:
- util/osdep.c,
- os-win32.c,
- util/oslib-win32.c?

(I'm not sure exactly which one would be best, but I guess Paolo can tell.)

Furthermore, if VSSCheckOSVersion() fails, we should notify the user
(based at least on the fact that other errors in vss_init() get
reported). In "install.cpp" too you report VSSCheckOSVersion() failures
with printf().


> +
> +    hr = CoInitialize(NULL);
> +    if (FAILED(hr)) {
> +        errmsg("CoInitialize failed [%lx]", hr);
> +        goto out;
> +    };

>From what you said recently, CoInitialize() can't fail.

Independently, is errmsg() -- or as I'm proposing, errmsg_dialog() --
justified here? (I don't know about the context(s) yet you're going to
call vss_init() from.)


> +    hr = CoInitializeSecurity(
> +        NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
> +        RPC_C_IMP_LEVEL_IDENTIFY, NULL, EOAC_NONE, NULL);

I'll trust you on all these parameters... There's only 9 of them if I'm
counting right :)


> +    if (FAILED(hr)) {
> +        errmsg("CoInitializeSecurity failed [%lx]", hr);
> +        goto out;
> +    }
> +
> +    hLib = LoadLibraryA("VSSAPI.DLL");
> +    if (!hLib) {
> +        errmsg("LoadLibrary VSSAPI.DLL failed");
> +        hr = E_FAIL;
> +        goto out;
> +    }

(I presume CoUninitialize() will undo CoInitializeSecurity()'s effects
as well.)


> +
> +    _CreateVssBackupComponents = (t_CreateVssBackupComponents)
> +        GetProcAddress(hLib,
> +#ifdef _WIN64 /* 64bit environment */
> +        "?CreateVssBackupComponents@@YAJPEAPEAVIVssBackupComponents@@@Z"
> +#else /* 32bit environment */
> +        "?CreateVssBackupComponents@@YGJPAPAVIVssBackupComponents@@@Z"
> +#endif
> +        );
> +    _VssFreeSnapshotProperties = (t_VssFreeSnapshotProperties)
> +        GetProcAddress(hLib, "VssFreeSnapshotProperties");
> +    if (!_CreateVssBackupComponents || !_VssFreeSnapshotProperties) {
> +        errmsg("GetProcAddress failed");
> +        hr = E_FAIL;
> +        goto out;
> +    }
> +
> +    return S_OK;
> +out:
> +    vss_deinit();
> +    return hr;
> +}

OK.

> +
> +static void vss_cleanup(void)
> +{
> +    if (hEvent != INVALID_HANDLE_VALUE) {
> +        CloseHandle(hEvent);
> +        hEvent = INVALID_HANDLE_VALUE;
> +    }
> +    if (hEvent2 != INVALID_HANDLE_VALUE) {
> +        CloseHandle(hEvent2);
> +        hEvent2 = INVALID_HANDLE_VALUE;
> +    }
> +    if (pVssbc) {
> +        pVssbc->Release();
> +        pVssbc = NULL;
> +    }
> +}
> +
> +void vss_deinit(void)
> +{
> +    if (VSSCheckOSVersion() == S_FALSE) {
> +        return;
> +    }
> +
> +    vss_cleanup();
> +
> +    CoUninitialize();
> +
> +    _CreateVssBackupComponents = NULL;
> +    _VssFreeSnapshotProperties = NULL;
> +    if (hLib) {
> +        FreeLibrary(hLib);
> +        hLib = NULL;
> +    }
> +}

This separation of cleanup tasks seems apt (peeking forward a bit).

However, from all the static(-ally referenced) data, "pAsyncSnapshot" is
not handled in vss_cleanup(). Peeking forward a bit again, I think that
"pAsyncSnapshot" might be leaked if the loop in
qga_vss_fsfreeze_freeze() terminates with a QueryStatus() error.


> +
> +int vss_initialized(void)
> +{
> +    return hLib != NULL;
> +}
> +
> +static void vss_add_components(Error **err)
> +{
> +    unsigned int cWriters, i;
> +    VSS_ID id, idInstance, idWriter;
> +    BSTR bstrWriterName;
> +    VSS_USAGE_TYPE usage;
> +    VSS_SOURCE_TYPE source;
> +    unsigned int cComponents, c1, c2, j;
> +    IVssExamineWriterMetadata *pMetadata;
> +    IVssWMComponent *pComponent;
> +    PVSSCOMPONENTINFO pInfo = NULL;
> +
> +    chk(pVssbc->GetWriterMetadataCount(&cWriters));

This could jump to "out" with an indeterminate "pComponent" (and call
pComponent->Release()).

I think the chk() macro makes a false promise -- you can't avoid
examining error conditions individually. (Eliminating chk() & co., as
I've suggested, should help with this too.)

"pMetadata" too is indeterminate here.

> +
> +    for (i = 0; i < cWriters; i++) {
> +        chk(pVssbc->GetWriterMetadata(i, &id, &pMetadata));
> +        chk(pMetadata->GetIdentity(&idInstance, &idWriter,
> +                                    &bstrWriterName, &usage, &source));
> +        chk(pMetadata->GetFileCounts(&c1, &c2, &cComponents));
> +
> +        for (j = 0; j < cComponents; j++) {
> +            chk(pMetadata->GetComponent(j, &pComponent));
> +            chk(pComponent->GetComponentInfo(&pInfo));
> +            if (pInfo->bSelectable) {
> +                chk(pVssbc->AddComponent(idInstance, idWriter, pInfo->type,
> +                                         pInfo->bstrLogicalPath,
> +                                         pInfo->bstrComponentName));
> +            }
> +            pComponent->FreeComponentInfo(pInfo);
> +            pInfo = NULL;
> +            pComponent->Release();
> +            pComponent = NULL;
> +        }
> +
> +        pMetadata->Release();
> +        pMetadata = NULL;

Should we free "bstrWriterName" too? (If so, then we must check it under
"out" as well I guess.)


> +    }
> +out:
> +    if (pComponent) {
> +        if (pInfo) {
> +            pComponent->FreeComponentInfo(pInfo);
> +        }
> +        pComponent->Release();
> +    }
> +    if (pMetadata) {
> +        pMetadata->Release();
> +    }
> +}

Presumably, if this function (or something after it) fails, any
components added with pVssbc->AddComponent() will be removed by
pVssbc->AbortBackup() or pVssbc->Release() (in vss_cleanup()) on the
error path in qga_vss_fsfreeze_freeze().

> +
> +void qga_vss_fsfreeze_freeze(int *num_vols, Error **err)
> +{
> +    IVssAsync *pAsync;
> +    HANDLE h;
> +    HRESULT hr;
> +    LONG ctx;
> +    GUID guidSnapshotSet = GUID_NULL;
> +    SECURITY_DESCRIPTOR sd;
> +    SECURITY_ATTRIBUTES sa;
> +    WCHAR buf[64], *b = buf;
> +    int n = 0;
> +
> +    if (pVssbc) { /* already frozen */
> +        *num_vols = 0;
> +        return;
> +    }

The Linux / POSIX implementation makes calls to ga_set_frozen() /
ga_unset_frozen(). ga_set_frozen() temporarily disables some qga
commands (that would require a thawed filesystem to work), plus it
creates an isfrozen file in (IIRC) the state directory. The isfrozen
file serves as "persistent reminder" for the next qga startup, should
qga die while the system is frozen.

I think *with time* we should investigate how / if ga_set_frozen() can
be applied to the windows implementation of fsfreeze. However I don't
think we should block this series until that time (feel free to disagree
though and implement that call too if you wish!)


> +
> +    assert(_CreateVssBackupComponents != NULL);

(g_assert())

> +    chk(_CreateVssBackupComponents(&pVssbc));
> +    chk(pVssbc->InitializeForBackup());

Hopefully AbortBackup() is valid for "pVssbc" even when
InitializeForBackup() fails first.


> +    chk(pVssbc->SetBackupState(true, true, VSS_BT_FULL, false));
> +    /*
> +     * Currently writable snapshots are not supported.
> +     * To prevent the final commit (which requires to write to snapshots),
> +     * 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 = 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;
> +        chk(pVssbc->SetContext(ctx));
> +    } else {
> +        _chk(hr, "SetContext");
> +    }
> +
> +    chk(pVssbc->GatherWriterMetadata(&pAsync));
> +    _chk(WaitForAsync(pAsync), "GatherWriterMetadata");

If WaitForAsync() fails, this leaks pAsync.

> +    pAsync->Release();

OTOH once you start handling pAsync under "out", you'll need to null it
here.

> +
> +    vss_add_components(err);
> +    if (error_is_set(err)) {
> +        goto out;
> +    }
> +
> +    chk(pVssbc->StartSnapshotSet(&guidSnapshotSet));
> +
> +    h = FindFirstVolumeW(buf, sizeof(buf));
> +    while (h != INVALID_HANDLE_VALUE) {

"h" doesn't appear to change in the loop. Hence I'd prefer an "if" after
FindFirstVolumeW(), and a for (;;) here.

> +        if (GetDriveTypeW(buf) == DRIVE_FIXED) {
> +            VSS_ID pid;
> +            hr = pVssbc->AddToSnapshotSet(buf, g_gProviderId, &pid);
> +            if (FAILED(hr)) {
> +                WCHAR name[PATH_MAX];
> +                char msg[PATH_MAX+32];
> +                if (GetVolumePathNamesForVolumeNameW(
> +                        buf, name, sizeof(name), NULL) && *name) {
> +                    b = name;
> +                }
> +                snprintf(msg, sizeof(msg), "add %S to snapshot set", b);

(let's hope %S won't run into EILSEQ -- the same applies if you agree to
rebase this code to the proposed error_setg_win32())

> +                error_setg_win32(err, hr, msg);
> +                goto out;

You miss FindVolumeClose() here.


> +            }
> +            n++;

Can you rename
- "n" to "num_fixed_drives",
- "h" to "volume",
- "buf" to "short_volume_name",
- "name" to "volume_path_names"?


> +        }
> +        if (!FindNextVolumeW(h, buf, sizeof(buf))) {
> +            FindVolumeClose(h);
> +            break;
> +        }
> +    }

Just a suggestion: if "n" ("num_fixed_drives") is zero here, we could
short-circuit the fsfreeze command (no volume added to the snapshot
set). OTOH I can't imagine any windows installation without a fixed
drive, admittedly.

> +
> +    chk(pVssbc->PrepareForBackup(&pAsync));
> +    _chk(WaitForAsync(pAsync), "PrepareForBackup");
> +    pAsync->Release();

Again, we should release "pAsync" if WaitForAsync() fails, and null it
if WaitForAsync() succeeds.

> +
> +    chk(pVssbc->GatherWriterStatus(&pAsync));
> +    _chk(WaitForAsync(pAsync), "GatherWriterStatus");
> +    pAsync->Release();

ditto

> +
> +    /* 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;

(I'll assume "sd" and "sa" don't need cleanup.)

> +
> +    hEvent = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_FROZEN);
> +    if (hEvent == INVALID_HANDLE_VALUE) {
> +        error_setg_win32(err, GetLastError(), "CreateEvenet");

typo in "CreateEvenet"

> +        goto out;
> +    }
> +    hEvent2 = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_THAW);
> +    if (hEvent2 == INVALID_HANDLE_VALUE) {
> +        error_setg_win32(err, GetLastError(), "CreateEvenet");
> +        goto out;

The typo was replicated here.

> +    }
> +
> +    chk(pVssbc->DoSnapshotSet(&pAsyncSnapshot));

... The events are closed in vss_cleanup() on failure, OK.

Am I right to think that this is the point where VSS enters
CQGAVssProvider::CommitSnapshots()? In that case the ordering of the two
CreateEvent() calls vs. DoSnapshotSet() is correct, the OpenEvent()
calls in CQGAVssProvider::CommitSnapshots() will find the events.


> +
> +    /* Need to call QueryStatus several times to make VSS provider progress */

Very strange!

> +    for (int i = 0; i < 1000; i++) {

What do you think of a macro for the repeat count?

> +        HRESULT hr = S_OK;

Is the initialization necessary? If QueryStatus() succeeds, "hr" should
be overwritten, should it not? Just asking because the initialization
implies we might read "hr" without any further assignment to it.

> +        chk(pAsyncSnapshot->QueryStatus(&hr, NULL));
> +        if (hr != VSS_S_ASYNC_PENDING) {
> +            error_setg(err, "DoSnapshotSet exited without freeze event");
> +            goto out;
> +        }

First I thought this was race-prone, but I was wrong -- when
CQGAVssProvider::CommitSnapshots() kicks the frozen event and blocks on
the thawed event, the status is still VSS_S_ASYNC_PENDING. OK.

Additionally, as I mentioned previously, if either chk() or the "hr"
check fails, we'll leak "pAsyncSnapshot".

> +        DWORD ret = WaitForSingleObject(hEvent, 10);
> +        if (ret == WAIT_OBJECT_0) {
> +            break;
> +        }

Should we look for other possible return values? For example,
WAIT_FAILED would render the rest of the loop moot.

> +    }

10,000 msecs for the loop seems fine.

But, should we check here, after the loop, whether "ret" equals
WAIT_OBJECT_0?

WAIT_TIMEOUT would imply i==1000, a real timeout. WAIT_FAILED would be
another error. (Of course the QMP caller would have no idea what
happened to VSS in the background, but that's just the nature of
timeouts.)

> +
> +    *num_vols = cFrozenVols = n;
> +    return;

Right. At this point we return to libvirt, from the fsfreeze command,
the file systems are frozen, and CQGAVssProvider::CommitSnapshots() is
blocking, for 60 seconds, on the "thaw" event.

> +
> +out:
> +    if (pVssbc) {
> +        pVssbc->AbortBackup();
> +    }
> +    vss_cleanup();
> +}
> +
> +
> +void qga_vss_fsfreeze_thaw(int *num_vols, Error **err)
> +{
> +    IVssAsync *pAsync;
> +
> +    if (hEvent2 == INVALID_HANDLE_VALUE) {
> +        /*
> +         * In this case, DoSnapshotSet is aborted or not started,
> +         * and no volumes must be frozen. We return without an error.
> +         */
> +        *num_vols = 0;
> +        return;
> +    }
> +    SetEvent(hEvent2);
> +
> +    assert(pVssbc);
> +    assert(pAsyncSnapshot);

g_assert(), but otherwise: correct. (hEvent2 != INVALID_HANDLE_VALUE)
implies both of these pointers are valid.

> +
> +    HRESULT hr = WaitForAsync(pAsyncSnapshot);
> +    if (hr == (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. Still, as the
> +         * applications and file systems are frozen, we just ignore the error.

I think the comment is mis-worded, applications and file systems should
be thawed, not frozen, at this point, no?

> +         */
> +        pAsyncSnapshot->Release();
> +        pAsyncSnapshot = NULL;

As I've been parroting above :), these two steps should happen in
vss_cleanup() -- conditionally of course, like the rest there.

You could also move "cFrozenVols = 0" to that function (see the grouping
I recommended at the top).

> +        goto final;

Technically, the backup finished with an error. Should you not abort it,
like you do in case of other errors?

> +    }
> +    if (hr == (HRESULT)VSS_E_HOLD_WRITES_TIMEOUT) {
> +        _chk(hr, "DoSnapshotSet: Couldn't hold writes. "
> +             "Fsfreeze is limited up to 10 seconds");
> +    }
> +    _chk(hr, "DoSnapshotSet");
> +    pAsyncSnapshot->Release();
> +    pAsyncSnapshot = NULL;
> +
> +    chk(pVssbc->BackupComplete(&pAsync));
> +    _chk(WaitForAsync(pAsync), "BackupComplete");
> +    pAsync->Release();

This leaks pAsync if WaitForAsync fails.

> +
> +final:
> +    *num_vols = cFrozenVols;
> +    cFrozenVols = 0;
> +    goto done;
> +
> +out:
> +    if (pVssbc) {

"pVssbc" is never NULL here (see the assert, for example).

> +        pVssbc->AbortBackup();
> +    }
> +done:
> +    vss_cleanup();
> +}

For my taste, there are way too many complex goto's here. What do you
think of the following:

    void qga_vss_fsfreeze_thaw(int *num_vols, Error **err)
    {
        if (hEvent2 == INVALID_HANDLE_VALUE) {
            /*
             * In this case, DoSnapshotSet is aborted or not started,
             * and no volumes must be frozen. We return without an
             * error.
             */
            *num_vols = 0;
            return;
        }
        SetEvent(hEvent2);

        assert(pVssbc);
        assert(pAsyncSnapshot);

        HRESULT hr = WaitForAsync(pAsyncSnapshot);
        switch (hr) {
        case VSS_E_OBJECT_NOT_FOUND:
            /*
             * On Windows earlier than 2008 SP2 which does not support
             * VSS_VOLSNAP_ATTR_NO_AUTORECOVERY context, the final
             * commit is not skipped and VSS is aborted by
             * VSS_E_OBJECT_NOT_FOUND. Still, as the applications and
             * file systems are thawed, we just ignore the error.
             *
             * Technically, this is still an error, thus we must abort
             * the backup.
             */
            pVssbc->AbortBackup();
            break;

        case "SUCCESS": {
            IVssAsync *pAsync;

            hr = pVssbc->BackupComplete(&pAsync);
            if (SUCCEEDED(hr)) {
                hr = WaitForAsync(pAsync);
                pAsync->Release();
            }
            if (FAILED(hr)) {
                error_setg_win32(err, hr, "BackupComplete");
            }
            break;
        }

        case VSS_E_HOLD_WRITES_TIMEOUT:
            error_setg_win32(err, hr,
                             "DoSnapshotSet: couldn't hold writes, "
                             "fsfreeze is limited to 10 seconds");
            break;

        default:
            error_setg_win32(err, hr, "DoSnapshotSet");
        }

        if (error_is_set(err)) {
            pVssbc->AbortBackup();
        }
        *num_vols = cFrozenVols;
        vss_cleanup();
    }

No gotos (not even in macros), no leaks, and hopefully easier to
understand.

In case AbortBackup() is not appropriate for VSS_E_OBJECT_NOT_FOUND,
because VSS_E_OBJECT_NOT_FOUND *really* is synonymous with "SUCCESS",
then simply make VSS_E_OBJECT_NOT_FOUND fall through to "SUCCESS" after
the comment about Win < 2008 SP2.

I do think we should call either AbortBackup() or BackupComplete() for
VSS_E_OBJECT_NOT_FOUND.

No more comments for this patch.

Thanks,
Laszlo

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 07/10] qemu-ga: Add Windows VSS requester to quiesce applications and filesystems
  2013-06-28 18:01   ` Laszlo Ersek
@ 2013-06-28 18:34     ` Laszlo Ersek
  2013-06-30  1:21     ` Tomoki Sekiyama
  1 sibling, 0 replies; 70+ messages in thread
From: Laszlo Ersek @ 2013-06-28 18:34 UTC (permalink / raw)
  To: Tomoki Sekiyama
  Cc: libaiqing, mdroth, stefanha, qemu-devel, lcapitulino, vrozenfe,
	pbonzini, seiji.aguchi, areis

On 06/28/13 20:01, Laszlo Ersek wrote:

> (b) error_setg_win32() should imitate error_setg_errno():
> 
>     #define error_setg_win32(err, win32, fmt, ...) \
>         error_set_errno(err, win32, ERROR_CLASS_GENERIC_ERROR, fmt, ## __VA_ARGS__)

Typo of course, that's error_set_win32() in the replacement text. Sorry!

Laszlo

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 07/10] qemu-ga: Add Windows VSS requester to quiesce applications and filesystems
  2013-06-28 18:01   ` Laszlo Ersek
  2013-06-28 18:34     ` Laszlo Ersek
@ 2013-06-30  1:21     ` Tomoki Sekiyama
  2013-07-01  8:31       ` Laszlo Ersek
  1 sibling, 1 reply; 70+ messages in thread
From: Tomoki Sekiyama @ 2013-06-30  1:21 UTC (permalink / raw)
  To: Laszlo Ersek
  Cc: libaiqing, mdroth, stefanha, qemu-devel, lcapitulino, vrozenfe,
	pbonzini, Seiji Aguchi, areis

On 6/28/13 14:01 , "Laszlo Ersek" <lersek@redhat.com> wrote:
>On 06/06/13 17:06, Tomoki Sekiyama wrote:
>
>> diff --git a/qga/vss-win32-requester.h b/qga/vss-win32-requester.h
>> new file mode 100644
>> index 0000000..f180f56
>> --- /dev/null
>> +++ b/qga/vss-win32-requester.h
...
>>+HRESULT vss_init(void);
>
>Can you include the mingw header that defines the HRESULT type? As far
>as I know we like to make headers standalone.

I will make the return type gboolean, that represent if it is successful
or not. It is used only in main.c, as 'if (FAILED(vss_init())) { ...'.

>(OTOH I vaguely recall a patch where the order between a mingw header
>and a (generated?) trace header could cause a build error... I think it
>would be worth trying still.)
>
>> +void vss_deinit(void);
>> +int vss_initialized(void);
>
>Since this is qemu-ga / qemu code, I think a "bool" return type would be
>more usual. (The current prototype is correct too of course.)

Will use gboolean here as well.

>> +void qga_vss_fsfreeze_freeze(int *nr_volume, struct Error **err);
>> +void qga_vss_fsfreeze_thaw(int *nr_volume, struct Error **err);
>
>Can you drop the "struct" word in these prototypes?

Ok, I will also add a header for definition for "Error" (qapi/error.h).


>> diff --git a/qga/vss-win32-requester.cpp b/qga/vss-win32-requester.cpp
>> new file mode 100644
>> index 0000000..7784926
>> --- /dev/null
>> +++ b/qga/vss-win32-requester.cpp
...
>> +#include <stdio.h>
>> +#include <assert.h>
>
>Can you remove this #include and replace all assert() occurrences with
>g_assert()?

Sure.

>> +static t_CreateVssBackupComponents _CreateVssBackupComponents;
>> +static t_VssFreeSnapshotProperties _VssFreeSnapshotProperties;
>
>I apologize in advance for splitting hairs, but :)
>
>  17.4.3.1.2 Global names [lib.global.names]; p1
>
>  Certain sets of names and function signatures are always reserved to
>  the implementation:
>
>  - Each name that contains a double underscore (__) or begins with an
>    underscore followed by an uppercase letter (2.11) is reserved to the
>    implementation for any use.
>
>Unless there's a pressing reason, could you drop the leading
>underscores?

Oh, I didn't know that. Without underscores, they conflict with function
declared in the MinGW header. Maybe pCreateVssBackupComponents?

>Can you decorate each of these static variables with a short comment? It
>does get clear(er) what they are used for by reading the code, but the
>comments would save others time.

All right.

>Also I'd recommend grouping them differently:
>
>- first group: long term objects related to VSSAPI.DLL and released
>  *only* in vss_deinit(): hLib, _CreateVssBackupComponents,
>  _VssFreeSnapshotProperties;
>
>- second group: objects that make sense only in preparation for, during,
>  and right after a freeze: pVssbc, pAsyncSnapshot, hEvent, hEvent2,
>  cFrozenVols. (You could even introduce a struct for these, but that's
>  just an idea.)

OK. And making them a struct sounds like a good idea.

>> +
>> +GCC_FMT_ATTR(1, 2)
>> +static void errmsg(const char *fmt, ...)

I will remove this; fprintf(stderr,...) is good enough.

>> +
>> +static void error_set_win32(Error **errp, DWORD err,
>> +                            ErrorClass eclass, const char *text)
>> +{
...
>> +
>> +    /* set error message in UTF-8 encoding */
>> +    msg = g_win32_error_message(err);
>
>Can we pass "err" (which is a HRESULT === DWORD === "long unsigned") to
>g_win32_error_message()? The latter takes an "int".
> 
>MSDN seems to imply there are different "error code spaces", see
>HRESULT_FROM_WIN32(). Anyway I'll assume we can do this.

g_win32_error_message is actually a wrapper for FormatMessage, and
FormatMessage actually can handle hresult (at least, in most cases).

>> +#define error_setg_win32(errp, err, text) \
>> +    error_set_win32(errp, err, ERROR_CLASS_GENERIC_ERROR, text)
>
>This approach is fine in general for the VSS DLL I think, but for
>qemu-ga.exe, I would propose a more flexible interface (could even
>belong into "util/error.c", in an #ifdef _WIN32 block). It would imitate
>error_setg_errno() in that it allowed the caller to pass in a format
>string as well:
>
>(a) Create a copy of error_set_errno() with "errno"/"strerror" replaced
>by "win32"/"g_win32_error_message":
...
>(b) error_setg_win32() should imitate error_setg_errno():
...
>This is just a suggestion, but it would bring your code closer to "qemu
>coding style", and render free-form error messages more convenient for
>you (eg. you could drop the snprintf() stuff in
>qga_vss_fsfreeze_freeze()).

OK, I will take this way and split this into another patch.

>> +
>> +#define __chk(status, text, errp, err_label)    \
...
>> +#define _chk(status, msg) __chk(status, "Failed to " msg, err, out)
>> +#define chk(status)  __chk(status, "Failed to " #status, err, out)
>
>I'd prefer if you hand-expanded these macros in qemu-ga code. The
>error_setg_win32() version suggested above should make it easy to format
>any error message. You'd have to open-code the "goto" and the specific
>label naturally, but that would also match the rest of qemu code better
>IMHO. (More work to write once, but easier to read many times.)

Okey. (Actually async/wait patterns spoil the benefit of chk()...)

>> +HRESULT vss_init(void)
>> +{
>> +    HRESULT hr;
>> +
>> +    hr = VSSCheckOSVersion();
>> +    if (hr == S_FALSE) {
>> +        return hr;
>> +    }
>
>Ah, I think you may have addressed this already -- this is the reason
>"vss-win32-provider/install.o" is needed when linking qemu-ga.exe.

Actually no, main purpose was to call COMRegister()/COMUnregister().

>VSSCheckOSVersion() is quite small, relies on no C++ *or* VSS features,
>and is arguably a utility function. Can you move it to one of:
>- util/osdep.c,
>- os-win32.c,
>- util/oslib-win32.c?
>
>(I'm not sure exactly which one would be best, but I guess Paolo can
>tell.)

Hmm, these file contains function commonly used both in POSIX systems
and win32. I will move VSSCheckOSVersion into vss-win32-requester.c,
because it is used only in the path from the file.

>Furthermore, if VSSCheckOSVersion() fails, we should notify the user
>(based at least on the fact that other errors in vss_init() get
>reported). In "install.cpp" too you report VSSCheckOSVersion() failures
>with printf().

OK, I will add error messages.

>> +    hr = CoInitialize(NULL);
>> +    if (FAILED(hr)) {
>> +        errmsg("CoInitialize failed [%lx]", hr);
>> +        goto out;
>> +    };
>
>From what you said recently, CoInitialize() can't fail.

Will remove error check from here.

>Independently, is errmsg() -- or as I'm proposing, errmsg_dialog() --
>justified here? (I don't know about the context(s) yet you're going to
>call vss_init() from.)

It is only called on starting up qemu-ga.exe.
fprintf(stderr, ...) might be good enough here.
(I thought that a dialog might be good for qemu-ga as a system service,
 but it seems not appropriate.
 http://stackoverflow.com/questions/7735945/create-dialog-in-windows-services-at-vista-system )
 
>>+static void vss_cleanup(void)
...
>> +void vss_deinit(void)
...
>This separation of cleanup tasks seems apt (peeking forward a bit).
>
>However, from all the static(-ally referenced) data, "pAsyncSnapshot" is
>not handled in vss_cleanup(). Peeking forward a bit again, I think that
>"pAsyncSnapshot" might be leaked if the loop in
>qga_vss_fsfreeze_freeze() terminates with a QueryStatus() error.

Agreed. I will introduce a struct for static vars and make "vss_cleanup()"
clean up it.

>> +static void vss_add_components(Error **err)
...
>> +    chk(pVssbc->GetWriterMetadataCount(&cWriters));
>
>This could jump to "out" with an indeterminate "pComponent" (and call
>pComponent->Release()).
>
>I think the chk() macro makes a false promise -- you can't avoid
>examining error conditions individually. (Eliminating chk() & co., as
>I've suggested, should help with this too.)

Will remove chk()s and fix it.

>Should we free "bstrWriterName" too? (If so, then we must check it under
>"out" as well I guess.)

Right... will free it in "out".

>Presumably, if this function (or something after it) fails, any
>components added with pVssbc->AddComponent() will be removed by
>pVssbc->AbortBackup() or pVssbc->Release() (in vss_cleanup()) on the
>error path in qga_vss_fsfreeze_freeze().

I believe so.

>> +    chk(_CreateVssBackupComponents(&pVssbc));
>> +    chk(pVssbc->InitializeForBackup());
>
>Hopefully AbortBackup() is valid for "pVssbc" even when
>InitializeForBackup() fails first.

It just returns error in the worst case.

>> +    chk(pVssbc->GatherWriterMetadata(&pAsync));
>> +    _chk(WaitForAsync(pAsync), "GatherWriterMetadata");
>
>If WaitForAsync() fails, this leaks pAsync.
>
>> +    pAsync->Release();
>
>OTOH once you start handling pAsync under "out", you'll need to null it
>here.

Right. Will fix them (and remove chk()s).

>> +    h = FindFirstVolumeW(buf, sizeof(buf));
>> +    while (h != INVALID_HANDLE_VALUE) {
>
>"h" doesn't appear to change in the loop. Hence I'd prefer an "if" after
>FindFirstVolumeW(), and a for (;;) here.

OK. And "h == INVALID_HANDLE_VALUE" should be treated as an error.

>> +        if (GetDriveTypeW(buf) == DRIVE_FIXED) {
>> +            VSS_ID pid;
>> +            hr = pVssbc->AddToSnapshotSet(buf, g_gProviderId, &pid);
>> +            if (FAILED(hr)) {
>> +                WCHAR name[PATH_MAX];
>> +                char msg[PATH_MAX+32];
>> +                if (GetVolumePathNamesForVolumeNameW(
>> +                        buf, name, sizeof(name), NULL) && *name) {
>> +                    b = name;
>> +                }
>> +                snprintf(msg, sizeof(msg), "add %S to snapshot set",
>>b);
>
>(let's hope %S won't run into EILSEQ -- the same applies if you agree to
>rebase this code to the proposed error_setg_win32())

I confirmed that it works.

>> +                error_setg_win32(err, hr, msg);
>> +                goto out;
>
>You miss FindVolumeClose() here.

Oops...

>> +            }
>> +            n++;
>
>Can you rename
>- "n" to "num_fixed_drives",
>- "h" to "volume",
>- "buf" to "short_volume_name",
>- "name" to "volume_path_names"?

Sure.

>Just a suggestion: if "n" ("num_fixed_drives") is zero here, we could
>short-circuit the fsfreeze command (no volume added to the snapshot
>set). OTOH I can't imagine any windows installation without a fixed
>drive, admittedly.

Yeah... I will add "goto out;" for that case.

>> +        error_setg_win32(err, GetLastError(), "CreateEvenet");
>typo in "CreateEvenet"
>> +        error_setg_win32(err, GetLastError(), "CreateEvenet");
>The typo was replicated here.

Oops, will fix it.

>> +    chk(pVssbc->DoSnapshotSet(&pAsyncSnapshot));
>
>... The events are closed in vss_cleanup() on failure, OK.
>
>Am I right to think that this is the point where VSS enters
>CQGAVssProvider::CommitSnapshots()? In that case the ordering of the two
>CreateEvent() calls vs. DoSnapshotSet() is correct, the OpenEvent()
>calls in CQGAVssProvider::CommitSnapshots() will find the events.

Exactly.

>> +    for (int i = 0; i < 1000; i++) {
>What do you think of a macro for the repeat count?

Sure.

>> +        HRESULT hr = S_OK;
>
>Is the initialization necessary? If QueryStatus() succeeds, "hr" should
>be overwritten, should it not? Just asking because the initialization
>implies we might read "hr" without any further assignment to it.

Maybe we don't need it. (though I remember some sample code was doing this...)

>> +        DWORD ret = WaitForSingleObject(hEvent, 10);
>> +        if (ret == WAIT_OBJECT_0) {
>> +            break;
>> +        }
>
>Should we look for other possible return values? For example,
>WAIT_FAILED would render the rest of the loop moot.

Agreed. This should be "wait_status != WAIT_TIMEOUT" and
should check if "ret == WAIT_OBJECT_0" after the loop, as you said.

>> +
>> +    *num_vols = cFrozenVols = n;
>> +    return;
>
>Right. At this point we return to libvirt, from the fsfreeze command,
>the file systems are frozen, and CQGAVssProvider::CommitSnapshots() is
>blocking, for 60 seconds, on the "thaw" event.

Right.
(However, actually VSS will keep the system frozen only for 10 seconds.
 If we exceeds that 10 seconds timeout, it is reported in the thaw event. )

>> +    HRESULT hr = WaitForAsync(pAsyncSnapshot);
>> +    if (hr == (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.
>>Still, as the
>> +         * applications and file systems are frozen, we just ignore
>>the error.
>
>I think the comment is mis-worded, applications and file systems should
>be thawed, not frozen, at this point, no?

Ah, yes, it is already thawed at this point. What I meant here was,
"the systems had been frozen *until the guest-fsfreeze-thaw was issued*."

>> +        pAsyncSnapshot->Release();
>> +        pAsyncSnapshot = NULL;
>
>As I've been parroting above :), these two steps should happen in
>vss_cleanup() -- conditionally of course, like the rest there.
>
>You could also move "cFrozenVols = 0" to that function (see the grouping
>I recommended at the top).

Agreed.

>> +        goto final;
>
>Technically, the backup finished with an error. Should you not abort it,
>like you do in case of other errors?

It looks already aborted when we get VSS_E_OBJECT_NOT_FOUND.
AbortBackup just returns an error code.
However, calling AbortBackup just in case will not hurt anything.

>> +out:
>> +    if (pVssbc) {
>
>"pVssbc" is never NULL here (see the assert, for example).

Right. Will remove the check.

>> +        pVssbc->AbortBackup();
>> +    }
>> +done:
>> +    vss_cleanup();
>> +}
>
>For my taste, there are way too many complex goto's here. What do you
>think of the following:
...
>No gotos (not even in macros), no leaks, and hopefully easier to
>understand.

Thank you for the code. Looks really nice.

>In case AbortBackup() is not appropriate for VSS_E_OBJECT_NOT_FOUND,
>because VSS_E_OBJECT_NOT_FOUND *really* is synonymous with "SUCCESS",
>then simply make VSS_E_OBJECT_NOT_FOUND fall through to "SUCCESS" after
>the comment about Win < 2008 SP2.
>
>I do think we should call either AbortBackup() or BackupComplete() for
>VSS_E_OBJECT_NOT_FOUND.
>
>No more comments for this patch.
>
>Thanks,
>Laszlo

Thanks a lot!

Tomoki Sekiyama

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze
  2013-06-25 22:31     ` Tomoki Sekiyama
  2013-06-26  5:59       ` Paolo Bonzini
@ 2013-06-30 13:16       ` Gal Hammer
  2013-07-01 18:57         ` Tomoki Sekiyama
  1 sibling, 1 reply; 70+ messages in thread
From: Gal Hammer @ 2013-06-30 13:16 UTC (permalink / raw)
  To: Tomoki Sekiyama
  Cc: libaiqing, mdroth, stefanha, qemu-devel, lcapitulino, vrozenfe,
	Paolo Bonzini, Seiji Aguchi, areis

On 26/06/2013 01:31, Tomoki Sekiyama wrote:

>> Also, is it needed to call VSSCheckOSVersion from the requestor?  I
>> would think that checking VSSAPI.DLL is stronger than checking the
>> version, and indeed you do that check too.
>
> In Windows XP, VSSAPI.DLL exists but it has different functionality
> and interfaces from newer Windows.
> http://msdn.microsoft.com/en-us/library/windows/desktop/aa384627(v=vs.85).aspx
>
> It is checking the OS version because this patchset only supports
> Windows 2003 or later.

So why don't you query for the interface you supports rather than 
checking the OS version?

Thanks,

     Gal.

> Thanks,
> Tomoki Sekiyama
>

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 07/10] qemu-ga: Add Windows VSS requester to quiesce applications and filesystems
  2013-06-30  1:21     ` Tomoki Sekiyama
@ 2013-07-01  8:31       ` Laszlo Ersek
  0 siblings, 0 replies; 70+ messages in thread
From: Laszlo Ersek @ 2013-07-01  8:31 UTC (permalink / raw)
  To: Tomoki Sekiyama
  Cc: libaiqing, mdroth, stefanha, qemu-devel, lcapitulino, vrozenfe,
	pbonzini, Seiji Aguchi, areis

On 06/30/13 03:21, Tomoki Sekiyama wrote:
> On 6/28/13 14:01 , "Laszlo Ersek" <lersek@redhat.com> wrote:
>> On 06/06/13 17:06, Tomoki Sekiyama wrote:

>>> +static t_CreateVssBackupComponents _CreateVssBackupComponents;
>>> +static t_VssFreeSnapshotProperties _VssFreeSnapshotProperties;

>> Unless there's a pressing reason, could you drop the leading
>> underscores?
> 
> Oh, I didn't know that. Without underscores, they conflict with function
> declared in the MinGW header. Maybe pCreateVssBackupComponents?

Sure.

I hope to start reviewing the next patch soonish.

Thanks!
Laszlo

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 08/10] qemu-ga: call Windows VSS requester in fsfreeze command handler
  2013-06-06 15:06 ` [Qemu-devel] [PATCH v4 08/10] qemu-ga: call Windows VSS requester in fsfreeze command handler Tomoki Sekiyama
@ 2013-07-01 13:29   ` Laszlo Ersek
  2013-07-01 17:48     ` Eric Blake
  2013-07-01 17:59     ` Tomoki Sekiyama
  0 siblings, 2 replies; 70+ messages in thread
From: Laszlo Ersek @ 2013-07-01 13:29 UTC (permalink / raw)
  To: Tomoki Sekiyama
  Cc: libaiqing, mdroth, stefanha, qemu-devel, lcapitulino, vrozenfe,
	pbonzini, seiji.aguchi, areis

some comments below

On 06/06/13 17:06, Tomoki Sekiyama wrote:
> Support guest-fsfreeze-freeze and guest-fsfreeze-thaw commands for Windows
> guests. When fsfreeze command is issued, it calls the VSS requester to
> freeze filesystems and applications. On thaw command, it again tells the VSS
> requester to thaw them.
> 
> This also adds calling of initialize functions for the VSS requester.
> 
> Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
> ---
>  qga/commands-win32.c |   74 ++++++++++++++++++++++++++++++++++++++++++++++----
>  qga/main.c           |   33 ++++++++++++++++++++++
>  2 files changed, 100 insertions(+), 7 deletions(-)
> 
> diff --git a/qga/commands-win32.c b/qga/commands-win32.c
> index 24e4ad0..67dca60 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-requester.h"
>  #include "qga-qmp-commands.h"
>  #include "qapi/qmp/qerror.h"
>  
> @@ -151,34 +152,95 @@ void qmp_guest_file_flush(int64_t handle, Error **err)
>      error_set(err, QERR_UNSUPPORTED);
>  }
>  
> +#ifdef HAS_VSS_SDK
> +

(CONFIG_... as we've discussed)

>  /*
>   * Return status of freeze/thaw
>   */
>  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;
> +
> +    slog("guest-fsfreeze called");
> +
> +    if (!vss_initialized()) {
> +        error_set(err, QERR_UNSUPPORTED);
> +        return 0;
> +    }
> +
> +    /* cannot risk guest agent blocking itself on a write in this state */
> +    ga_set_frozen(ga_state);

Great! I wasn't expecting this, but in retrospect I don't know why :)

> +
> +    qga_vss_fsfreeze_freeze(&i, err);
> +    if (error_is_set(err)) {
> +        goto error;
> +    }
> +
> +    return i;
> +
> +error:
> +    qmp_guest_fsfreeze_thaw(NULL);

Passing NULL here as "errp" concerns me slightly. I've been assuming
that "errp" is never NULL due to the outermost QMP layer always wanting
to receive errors and to serialize them.

Specifically, a NULL "errp" would turn all error_set*(errp, ...) calls
into no-ops under qmp_guest_fsfreeze_thaw(), and all error_is_set(errp)
questions would answer with false. That said, nothing seems to be
affected right now.

Maybe a dummy local variable would be more future-proof... OTOH it would
be stylistically questionable :)


Because of the initial hEvent2 check in qga_vss_fsfreeze_thaw(), it
should be safe to call at any time AFAICS. OK.

>      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)
>  {
> +    int i;
> +
> +    if (!vss_initialized()) {
> +        error_set(err, QERR_UNSUPPORTED);
> +        return 0;
> +    }
> +
> +    qga_vss_fsfreeze_thaw(&i, err);

I wonder how libvirt interprets a failure to thaw -- does it expect
filesystems to remain frozen? (CC'ing Eric.)

For example, the VSS_E_HOLD_WRITES_TIMEOUT error is reported on the next
thaw attempt, but when this error occurs, filesystems have been
auto-thawed I think.

Anyway I can't suggest anything fail-safe here.

> +
> +    ga_unset_frozen(ga_state);
> +    return i;
> +}
> +
> +#else

(maybe mention the macro in a comment that the #else depends on here)

> +
> +GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err)
> +{
> +    error_set(err, QERR_UNSUPPORTED);
> +    return 0;
> +}
> +
> +int64_t qmp_guest_fsfreeze_freeze(Error **err)
> +{
> +    error_set(err, QERR_UNSUPPORTED);
> +    return 0;
> +}
> +
> +int64_t qmp_guest_fsfreeze_thaw(Error **err)
> +{
>      error_set(err, QERR_UNSUPPORTED);
>      return 0;
>  }
>  
> +#endif
> +
>  /*
>   * Walk list of mounted file systems in the guest, and discard unused
>   * areas.
> diff --git a/qga/main.c b/qga/main.c
> index 0e04e73..8bcedaf 100644
> --- a/qga/main.c
> +++ b/qga/main.c
> @@ -34,6 +34,10 @@
>  #include "qemu/bswap.h"
>  #ifdef _WIN32
>  #include "qga/service-win32.h"
> +#ifdef HAS_VSS_SDK
> +#include "qga/vss-win32-provider.h"
> +#include "qga/vss-win32-requester.h"
> +#endif

The protection of #include "qga/vss-win32-requester.h" is inconsistent
between "commands-win32.c" and "qga/main.c". For now the header only
brings in a few prototypes, which shouldn't cause trouble in
"commands-win32.c" even without VSS. Still consistent guarding would be
nice.


>  #include <windows.h>
>  #endif
>  #ifdef __linux__
> @@ -701,6 +705,25 @@ static gboolean channel_init(GAState *s, const gchar *method, const gchar *path)
>  }
>  
>  #ifdef _WIN32
> +
> +static gboolean vss_win32_init(void)
> +{
> +#ifdef HAS_VSS_SDK
> +    if (FAILED(vss_init())) {
> +        g_critical("failed to initialize VSS");
> +        return false;
> +    }
> +#endif
> +    return true;
> +}
> +
> +static void vss_win32_deinit(void)
> +{
> +#ifdef HAS_VSS_SDK
> +    vss_deinit();
> +#endif
> +}
> +
>  DWORD WINAPI service_ctrl_handler(DWORD ctrl, DWORD type, LPVOID data,
>                                    LPVOID ctx)
>  {
> @@ -743,8 +766,12 @@ VOID WINAPI service_main(DWORD argc, TCHAR *argv[])
>      service->status.dwWaitHint = 0;
>      SetServiceStatus(service->status_handle, &service->status);
>  
> +    if (!vss_win32_init()) {
> +        goto out_bad;
> +    }
>      g_main_loop_run(ga_state->main_loop);
> -
> +    vss_win32_deinit();
> +out_bad:
>      service->status.dwCurrentState = SERVICE_STOPPED;
>      SetServiceStatus(service->status_handle, &service->status);
>  }
> @@ -1175,7 +1202,11 @@ int main(int argc, char **argv)
>              { (char *)QGA_SERVICE_NAME, service_main }, { NULL, NULL } };
>          StartServiceCtrlDispatcher(service_table);
>      } else {
> +        if (!vss_win32_init()) {
> +            goto out_bad;
> +        }
>          g_main_loop_run(ga_state->main_loop);
> +        vss_win32_deinit();
>      }
>  #endif

Reviewed-by: Laszlo Ersek <lersek@redhat.com>


... This patch made me look at ga_command_state_cleanup_all().
Apparently the POSIX flavor thaws filesystems if qemu-ga exits before a
thaw command over QMP; see guest_fsfreeze_cleanup() and
ga_command_state_init(). Do you think something similar would be useful
for the Windows flavor as well?

Thanks
Laszlo

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 09/10] qemu-ga: install Windows VSS provider on `qemu-ga -s install'
  2013-06-06 15:06 ` [Qemu-devel] [PATCH v4 09/10] qemu-ga: install Windows VSS provider on `qemu-ga -s install' Tomoki Sekiyama
@ 2013-07-01 14:50   ` Laszlo Ersek
  2013-07-01 17:59     ` Tomoki Sekiyama
  0 siblings, 1 reply; 70+ messages in thread
From: Laszlo Ersek @ 2013-07-01 14:50 UTC (permalink / raw)
  To: Tomoki Sekiyama
  Cc: libaiqing, mdroth, stefanha, qemu-devel, lcapitulino, vrozenfe,
	pbonzini, seiji.aguchi, areis

On 06/06/13 17:06, Tomoki Sekiyama wrote:
> Register QGA VSS provider library into Windows when qemu-ga is installed as
> Windows service ('-s install' option). It is deregistered when the service
> is uninstalled ('-s uninstall' option).
> 
> Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
> ---
>  qga/main.c |    8 ++++++++
>  1 file changed, 8 insertions(+)
> 
> diff --git a/qga/main.c b/qga/main.c
> index 8bcedaf..739b958 100644
> --- a/qga/main.c
> +++ b/qga/main.c
> @@ -1058,8 +1058,16 @@ int main(int argc, char **argv)
>                  fixed_state_dir = (state_dir == dfl_pathnames.state_dir) ?
>                                    NULL :
>                                    state_dir;
> +#ifdef HAS_VSS_SDK
> +                if (FAILED(COMRegister())) {
> +                    return EXIT_FAILURE;
> +                }

COMRegister() seems to print error messages on failure, OK.

> +#endif
>                  return ga_install_service(path, log_filepath, fixed_state_dir);

Shouldn't you call COMUnregister() if ga_install_service() fails?
Otherwise we might end up with a half-installed service (COM app catalog
entry for VSS provider: yes, VSS provider DLL: yes, VSS client / qemu-ga
service: no).

>              } else if (strcmp(service, "uninstall") == 0) {
> +#ifdef HAS_VSS_SDK
> +                COMUnregister();
> +#endif
>                  return ga_uninstall_service();
>              } else {
>                  printf("Unknown service command.\n");
> 
> 

This seems OK; there isn't much to do (non-interactively) when a
destructor fails.

Thanks,
Laszlo

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 10/10] QMP/qemu-ga-client: make timeout longer for guest-fsfreeze-freeze command
  2013-06-06 15:07 ` [Qemu-devel] [PATCH v4 10/10] QMP/qemu-ga-client: make timeout longer for guest-fsfreeze-freeze command Tomoki Sekiyama
  2013-06-18 10:17   ` Paolo Bonzini
@ 2013-07-01 15:02   ` Laszlo Ersek
  1 sibling, 0 replies; 70+ messages in thread
From: Laszlo Ersek @ 2013-07-01 15:02 UTC (permalink / raw)
  To: Tomoki Sekiyama
  Cc: libaiqing, mdroth, stefanha, qemu-devel, lcapitulino, vrozenfe,
	pbonzini, seiji.aguchi, areis

On 06/06/13 17:07, Tomoki Sekiyama wrote:
> guest-fsfreeze-freeze command can take longer than 3 seconds when heavy
> disk I/O is running. To avoid unexpected timeout, this changes the timeout
> to 60 seconds (timeout of pre-commit phase of VSS).
> 
> Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
> ---
>  QMP/qemu-ga-client |    4 +++-
>  1 file changed, 3 insertions(+), 1 deletion(-)
> 
> diff --git a/QMP/qemu-ga-client b/QMP/qemu-ga-client
> index 46676c3..b5f7e7c 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)
> 
> 

Reviewed-by: Laszlo Ersek <lersek@redhat.com>

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 08/10] qemu-ga: call Windows VSS requester in fsfreeze command handler
  2013-07-01 13:29   ` Laszlo Ersek
@ 2013-07-01 17:48     ` Eric Blake
  2013-07-01 17:59     ` Tomoki Sekiyama
  1 sibling, 0 replies; 70+ messages in thread
From: Eric Blake @ 2013-07-01 17:48 UTC (permalink / raw)
  To: Laszlo Ersek
  Cc: libaiqing, mdroth, stefanha, qemu-devel, lcapitulino, vrozenfe,
	Tomoki Sekiyama, pbonzini, seiji.aguchi, areis

[-- Attachment #1: Type: text/plain, Size: 2196 bytes --]

On 07/01/2013 07:29 AM, Laszlo Ersek wrote:
> some comments below
> 
> On 06/06/13 17:06, Tomoki Sekiyama wrote:
>> Support guest-fsfreeze-freeze and guest-fsfreeze-thaw commands for Windows
>> guests. When fsfreeze command is issued, it calls the VSS requester to
>> freeze filesystems and applications. On thaw command, it again tells the VSS
>> requester to thaw them.
>>

>>  int64_t qmp_guest_fsfreeze_thaw(Error **err)
>>  {
>> +    int i;
>> +
>> +    if (!vss_initialized()) {
>> +        error_set(err, QERR_UNSUPPORTED);
>> +        return 0;
>> +    }
>> +
>> +    qga_vss_fsfreeze_thaw(&i, err);
> 
> I wonder how libvirt interprets a failure to thaw -- does it expect
> filesystems to remain frozen? (CC'ing Eric.)

On failure to thaw, libvirt marks the virDomainSnapshotCreateXML() API
call as failed, and logs the failure returned from the guest agent; but
beyond that, there really isn't anything further that libvirt can
attempt.  Guest freeze/thaw is a best-effort attempt, and we don't
really have any clean way to recover from a non-cooperative guest (ie.
it's no different from a Linux guest managing to kill off the guest
agent daemon in the guest at the wrong time).  In short, a failed
snapshot request has no guarantees on whether the guest is frozen or
thawed, and libvirt currently has no additional support to try a thaw in
isolation (although there is an intentionally unsupported 'virsh
qemu-agent-command' for attempting recovery manually).

> ... This patch made me look at ga_command_state_cleanup_all().
> Apparently the POSIX flavor thaws filesystems if qemu-ga exits before a
> thaw command over QMP; see guest_fsfreeze_cleanup() and
> ga_command_state_init(). Do you think something similar would be useful
> for the Windows flavor as well?

I agree that it is better to try and clean up a thaw on any error case
(including the error of qemu-ga exiting without seeing a QMP thaw
request), if only to minimize the chance of failure leaving the system
wedged with no chance of further recovery attempts.

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 621 bytes --]

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 08/10] qemu-ga: call Windows VSS requester in fsfreeze command handler
  2013-07-01 13:29   ` Laszlo Ersek
  2013-07-01 17:48     ` Eric Blake
@ 2013-07-01 17:59     ` Tomoki Sekiyama
  2013-07-02  6:36       ` Laszlo Ersek
  1 sibling, 1 reply; 70+ messages in thread
From: Tomoki Sekiyama @ 2013-07-01 17:59 UTC (permalink / raw)
  To: Laszlo Ersek
  Cc: libaiqing, mdroth, stefanha, qemu-devel, lcapitulino, vrozenfe,
	pbonzini, Seiji Aguchi, areis

On 7/1/13 9:29 , "Laszlo Ersek" <lersek@redhat.com> wrote:

>>+error:
>> +    qmp_guest_fsfreeze_thaw(NULL);
>
>Passing NULL here as "errp" concerns me slightly. I've been assuming
>that "errp" is never NULL due to the outermost QMP layer always wanting
>to receive errors and to serialize them.
>
>Specifically, a NULL "errp" would turn all error_set*(errp, ...) calls
>into no-ops under qmp_guest_fsfreeze_thaw(), and all error_is_set(errp)
>questions would answer with false. That said, nothing seems to be
>affected right now.
>
>Maybe a dummy local variable would be more future-proof... OTOH it would
>be stylistically questionable :)

OK, then it should be:
    Error *local_err = NULL;
...
error:
    qmp_guest_fsfreeze_thaw(&local_err);
    if (error_is_set(&local_err)) {
        error_free(local_err);
    }

>>+#ifdef HAS_VSS_SDK
>> +#include "qga/vss-win32-provider.h"
>> +#include "qga/vss-win32-requester.h"
>> +#endif
>
>The protection of #include "qga/vss-win32-requester.h" is inconsistent
>between "commands-win32.c" and "qga/main.c". For now the header only
>brings in a few prototypes, which shouldn't cause trouble in
>"commands-win32.c" even without VSS. Still consistent guarding would be
>nice.

I will remove #ifdef's from qga/main.c. Instead, I will add static inline
functions that does nothing when CONFIG_VSS_SDK isn't defined.
(And vss-win32-provider.h can also be removed by stop linking the DLL.)


>... This patch made me look at ga_command_state_cleanup_all().
>Apparently the POSIX flavor thaws filesystems if qemu-ga exits before a
>thaw command over QMP; see guest_fsfreeze_cleanup() and
>ga_command_state_init(). Do you think something similar would be useful
>for the Windows flavor as well?

Hm, however the backup is automatically terminated if IVssBackupComponents
is released, it would be nice to have cleanup. I will add them.

Thanks,
Tomoki Sekiyama

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 09/10] qemu-ga: install Windows VSS provider on `qemu-ga -s install'
  2013-07-01 14:50   ` Laszlo Ersek
@ 2013-07-01 17:59     ` Tomoki Sekiyama
  0 siblings, 0 replies; 70+ messages in thread
From: Tomoki Sekiyama @ 2013-07-01 17:59 UTC (permalink / raw)
  To: Laszlo Ersek
  Cc: libaiqing, mdroth, stefanha, qemu-devel, lcapitulino, vrozenfe,
	pbonzini, Seiji Aguchi, areis

On 7/1/13 10:50 , "Laszlo Ersek" <lersek@redhat.com> wrote:

>On 06/06/13 17:06, Tomoki Sekiyama wrote:
>>+#ifdef HAS_VSS_SDK
>> +                if (FAILED(COMRegister())) {
>> +                    return EXIT_FAILURE;
>> +                }
>
>COMRegister() seems to print error messages on failure, OK.
>
>> +#endif
>>                  return ga_install_service(path, log_filepath,
>>fixed_state_dir);
>
>Shouldn't you call COMUnregister() if ga_install_service() fails?
>Otherwise we might end up with a half-installed service (COM app catalog
>entry for VSS provider: yes, VSS provider DLL: yes, VSS client / qemu-ga
>service: no).

Agreed. I will fix this.

Thanks,
Tomoki Sekiyama

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze
  2013-06-30 13:16       ` Gal Hammer
@ 2013-07-01 18:57         ` Tomoki Sekiyama
  2013-07-02 12:36           ` Gal Hammer
  0 siblings, 1 reply; 70+ messages in thread
From: Tomoki Sekiyama @ 2013-07-01 18:57 UTC (permalink / raw)
  To: Gal Hammer
  Cc: libaiqing, mdroth, stefanha, qemu-devel, lcapitulino, vrozenfe,
	Paolo Bonzini, Seiji Aguchi, areis

On 6/30/13 9:16 , "Gal Hammer" <ghammer@redhat.com> wrote:

>On 26/06/2013 01:31, Tomoki Sekiyama wrote:
>
>>> Also, is it needed to call VSSCheckOSVersion from the requestor?  I
>>> would think that checking VSSAPI.DLL is stronger than checking the
>>> version, and indeed you do that check too.
>>
>> In Windows XP, VSSAPI.DLL exists but it has different functionality
>> and interfaces from newer Windows.
>> 
>>http://msdn.microsoft.com/en-us/library/windows/desktop/aa384627(v=vs.85)
>>.aspx
>>
>> It is checking the OS version because this patchset only supports
>> Windows 2003 or later.
>
>So why don't you query for the interface you supports rather than
>checking the OS version?

Hmm, I don't know what I should query for. Microsoft's document above only
provides a table of compatibility between VSS SDK version and OS version.

Of course I can install the component and test whether it works or not,
but I want to know that before installing something.

>Thanks,
>
>     Gal.

Thanks,
Tomoki Sekiyama

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 08/10] qemu-ga: call Windows VSS requester in fsfreeze command handler
  2013-07-01 17:59     ` Tomoki Sekiyama
@ 2013-07-02  6:36       ` Laszlo Ersek
  2013-07-02 14:09         ` Luiz Capitulino
  2013-07-02 14:58         ` Michael Roth
  0 siblings, 2 replies; 70+ messages in thread
From: Laszlo Ersek @ 2013-07-02  6:36 UTC (permalink / raw)
  To: Tomoki Sekiyama
  Cc: libaiqing, mdroth, stefanha, qemu-devel, lcapitulino, vrozenfe,
	pbonzini, Seiji Aguchi, areis

On 07/01/13 19:59, Tomoki Sekiyama wrote:
> On 7/1/13 9:29 , "Laszlo Ersek" <lersek@redhat.com> wrote:
> 
>>> +error:
>>> +    qmp_guest_fsfreeze_thaw(NULL);
>>
>> Passing NULL here as "errp" concerns me slightly. I've been assuming
>> that "errp" is never NULL due to the outermost QMP layer always wanting
>> to receive errors and to serialize them.
>>
>> Specifically, a NULL "errp" would turn all error_set*(errp, ...) calls
>> into no-ops under qmp_guest_fsfreeze_thaw(), and all error_is_set(errp)
>> questions would answer with false. That said, nothing seems to be
>> affected right now.
>>
>> Maybe a dummy local variable would be more future-proof... OTOH it would
>> be stylistically questionable :)
> 
> OK, then it should be:
>     Error *local_err = NULL;
> ...
> error:
>     qmp_guest_fsfreeze_thaw(&local_err);
>     if (error_is_set(&local_err)) {
>         error_free(local_err);
>     }

I think so, yes.

You could also log it before freeing it I guess:

    g_debug("cleanup thaw: %s", error_get_pretty(local_err));

or some such, but it's probably not important.

Thanks,
Laszlo

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze
  2013-07-01 18:57         ` Tomoki Sekiyama
@ 2013-07-02 12:36           ` Gal Hammer
  0 siblings, 0 replies; 70+ messages in thread
From: Gal Hammer @ 2013-07-02 12:36 UTC (permalink / raw)
  To: Tomoki Sekiyama
  Cc: libaiqing, qemu-devel, stefanha, mdroth, lcapitulino, vrozenfe,
	Paolo Bonzini, Seiji Aguchi, areis

On 01/07/2013 21:57, Tomoki Sekiyama wrote:

>> So why don't you query for the interface you supports rather than
>> checking the OS version?
>
> Hmm, I don't know what I should query for. Microsoft's document above only
> provides a table of compatibility between VSS SDK version and OS version.
>
> Of course I can install the component and test whether it works or not,
> but I want to know that before installing something.

I'll check it myself and try to suggest a patch.

Sorry for missing the face that it is done during installation and not 
on a "freeze" request.

     Gal.

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 08/10] qemu-ga: call Windows VSS requester in fsfreeze command handler
  2013-07-02  6:36       ` Laszlo Ersek
@ 2013-07-02 14:09         ` Luiz Capitulino
  2013-07-02 14:44           ` Laszlo Ersek
  2013-07-02 14:58         ` Michael Roth
  1 sibling, 1 reply; 70+ messages in thread
From: Luiz Capitulino @ 2013-07-02 14:09 UTC (permalink / raw)
  To: Laszlo Ersek
  Cc: libaiqing, stefanha, qemu-devel, mdroth, vrozenfe,
	Tomoki Sekiyama, pbonzini, Seiji Aguchi, areis

On Tue, 02 Jul 2013 08:36:11 +0200
Laszlo Ersek <lersek@redhat.com> wrote:

> On 07/01/13 19:59, Tomoki Sekiyama wrote:
> > On 7/1/13 9:29 , "Laszlo Ersek" <lersek@redhat.com> wrote:
> > 
> >>> +error:
> >>> +    qmp_guest_fsfreeze_thaw(NULL);
> >>
> >> Passing NULL here as "errp" concerns me slightly. I've been assuming
> >> that "errp" is never NULL due to the outermost QMP layer always wanting
> >> to receive errors and to serialize them.
> >>
> >> Specifically, a NULL "errp" would turn all error_set*(errp, ...) calls
> >> into no-ops under qmp_guest_fsfreeze_thaw(), and all error_is_set(errp)
> >> questions would answer with false. That said, nothing seems to be
> >> affected right now.
> >>
> >> Maybe a dummy local variable would be more future-proof... OTOH it would
> >> be stylistically questionable :)
> > 
> > OK, then it should be:
> >     Error *local_err = NULL;
> > ...
> > error:
> >     qmp_guest_fsfreeze_thaw(&local_err);
> >     if (error_is_set(&local_err)) {
> >         error_free(local_err);
> >     }
> 
> I think so, yes.
> 
> You could also log it before freeing it I guess:
> 
>     g_debug("cleanup thaw: %s", error_get_pretty(local_err));
> 
> or some such, but it's probably not important.

I'd rather do something like that, otherwise it doesn't seem to
make sense to pass Error as qmp_guest_fsfreeze_thaw() does
use a local Error.

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 08/10] qemu-ga: call Windows VSS requester in fsfreeze command handler
  2013-07-02 14:09         ` Luiz Capitulino
@ 2013-07-02 14:44           ` Laszlo Ersek
  2013-07-02 15:16             ` Luiz Capitulino
  0 siblings, 1 reply; 70+ messages in thread
From: Laszlo Ersek @ 2013-07-02 14:44 UTC (permalink / raw)
  To: Luiz Capitulino
  Cc: libaiqing, stefanha, qemu-devel, mdroth, vrozenfe,
	Tomoki Sekiyama, pbonzini, Seiji Aguchi, areis

On 07/02/13 16:09, Luiz Capitulino wrote:
> On Tue, 02 Jul 2013 08:36:11 +0200
> Laszlo Ersek <lersek@redhat.com> wrote:
> 
>> On 07/01/13 19:59, Tomoki Sekiyama wrote:
>>> On 7/1/13 9:29 , "Laszlo Ersek" <lersek@redhat.com> wrote:
>>>
>>>>> +error:
>>>>> +    qmp_guest_fsfreeze_thaw(NULL);
>>>>
>>>> Passing NULL here as "errp" concerns me slightly. I've been assuming
>>>> that "errp" is never NULL due to the outermost QMP layer always wanting
>>>> to receive errors and to serialize them.
>>>>
>>>> Specifically, a NULL "errp" would turn all error_set*(errp, ...) calls
>>>> into no-ops under qmp_guest_fsfreeze_thaw(), and all error_is_set(errp)
>>>> questions would answer with false. That said, nothing seems to be
>>>> affected right now.
>>>>
>>>> Maybe a dummy local variable would be more future-proof... OTOH it would
>>>> be stylistically questionable :)
>>>
>>> OK, then it should be:
>>>     Error *local_err = NULL;
>>> ...
>>> error:
>>>     qmp_guest_fsfreeze_thaw(&local_err);
>>>     if (error_is_set(&local_err)) {
>>>         error_free(local_err);
>>>     }
>>
>> I think so, yes.
>>
>> You could also log it before freeing it I guess:
>>
>>     g_debug("cleanup thaw: %s", error_get_pretty(local_err));
>>
>> or some such, but it's probably not important.
> 
> I'd rather do something like that, otherwise it doesn't seem to
> make sense to pass Error as qmp_guest_fsfreeze_thaw() does
> use a local Error.

No, the win32 version of qmp_guest_fsfreeze_thaw() being added by this
patch doesn't.

Thanks,
Laszlo

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 08/10] qemu-ga: call Windows VSS requester in fsfreeze command handler
  2013-07-02  6:36       ` Laszlo Ersek
  2013-07-02 14:09         ` Luiz Capitulino
@ 2013-07-02 14:58         ` Michael Roth
  1 sibling, 0 replies; 70+ messages in thread
From: Michael Roth @ 2013-07-02 14:58 UTC (permalink / raw)
  To: Laszlo Ersek
  Cc: libaiqing, stefanha, qemu-devel, lcapitulino, vrozenfe,
	Tomoki Sekiyama, pbonzini, Seiji Aguchi, areis

On Tue, Jul 2, 2013 at 1:36 AM, Laszlo Ersek <lersek@redhat.com> wrote:
> On 07/01/13 19:59, Tomoki Sekiyama wrote:
>> On 7/1/13 9:29 , "Laszlo Ersek" <lersek@redhat.com> wrote:
>>
>>>> +error:
>>>> +    qmp_guest_fsfreeze_thaw(NULL);
>>>
>>> Passing NULL here as "errp" concerns me slightly. I've been assuming
>>> that "errp" is never NULL due to the outermost QMP layer always wanting
>>> to receive errors and to serialize them.
>>>
>>> Specifically, a NULL "errp" would turn all error_set*(errp, ...) calls
>>> into no-ops under qmp_guest_fsfreeze_thaw(), and all error_is_set(errp)
>>> questions would answer with false. That said, nothing seems to be
>>> affected right now.
>>>
>>> Maybe a dummy local variable would be more future-proof... OTOH it would
>>> be stylistically questionable :)
>>
>> OK, then it should be:
>>     Error *local_err = NULL;
>> ...
>> error:
>>     qmp_guest_fsfreeze_thaw(&local_err);
>>     if (error_is_set(&local_err)) {
>>         error_free(local_err);
>>     }
>
> I think so, yes.
>
> You could also log it before freeing it I guess:
>
>     g_debug("cleanup thaw: %s", error_get_pretty(local_err));
>
> or some such, but it's probably not important.

One thing to keep in mind is there are some failure paths in
qmp_guest_fsfreeze_thaw(), for both win32 and posix, where we might not unset
frozen state via ga_set_unfrozen(). In which case, logging will still be
disabled.

It might make sense to nest those error strings inside the one returned by
qmp_guest_fsfreeze_freeze(), since that's the only reliable way to report it
and the client will be interested in that information. This also makes
introducing local_err immediately useful.

>
> Thanks,
> Laszlo
>

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 08/10] qemu-ga: call Windows VSS requester in fsfreeze command handler
  2013-07-02 14:44           ` Laszlo Ersek
@ 2013-07-02 15:16             ` Luiz Capitulino
  2013-07-02 15:28               ` Laszlo Ersek
  0 siblings, 1 reply; 70+ messages in thread
From: Luiz Capitulino @ 2013-07-02 15:16 UTC (permalink / raw)
  To: Laszlo Ersek
  Cc: libaiqing, stefanha, qemu-devel, mdroth, vrozenfe,
	Tomoki Sekiyama, pbonzini, Seiji Aguchi, areis

On Tue, 02 Jul 2013 16:44:55 +0200
Laszlo Ersek <lersek@redhat.com> wrote:

> On 07/02/13 16:09, Luiz Capitulino wrote:
> > On Tue, 02 Jul 2013 08:36:11 +0200
> > Laszlo Ersek <lersek@redhat.com> wrote:
> > 
> >> On 07/01/13 19:59, Tomoki Sekiyama wrote:
> >>> On 7/1/13 9:29 , "Laszlo Ersek" <lersek@redhat.com> wrote:
> >>>
> >>>>> +error:
> >>>>> +    qmp_guest_fsfreeze_thaw(NULL);
> >>>>
> >>>> Passing NULL here as "errp" concerns me slightly. I've been assuming
> >>>> that "errp" is never NULL due to the outermost QMP layer always wanting
> >>>> to receive errors and to serialize them.
> >>>>
> >>>> Specifically, a NULL "errp" would turn all error_set*(errp, ...) calls
> >>>> into no-ops under qmp_guest_fsfreeze_thaw(), and all error_is_set(errp)
> >>>> questions would answer with false. That said, nothing seems to be
> >>>> affected right now.
> >>>>
> >>>> Maybe a dummy local variable would be more future-proof... OTOH it would
> >>>> be stylistically questionable :)
> >>>
> >>> OK, then it should be:
> >>>     Error *local_err = NULL;
> >>> ...
> >>> error:
> >>>     qmp_guest_fsfreeze_thaw(&local_err);
> >>>     if (error_is_set(&local_err)) {
> >>>         error_free(local_err);
> >>>     }
> >>
> >> I think so, yes.
> >>
> >> You could also log it before freeing it I guess:
> >>
> >>     g_debug("cleanup thaw: %s", error_get_pretty(local_err));
> >>
> >> or some such, but it's probably not important.
> > 
> > I'd rather do something like that, otherwise it doesn't seem to
> > make sense to pass Error as qmp_guest_fsfreeze_thaw() does
> > use a local Error.
> 
> No, the win32 version of qmp_guest_fsfreeze_thaw() being added by this
> patch doesn't.

Oh, I looked into the POSIX one. But then, it's qmp_guest_fsfreeze_thaw()
that has to be fixed, isn't it?

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [Qemu-devel] [PATCH v4 08/10] qemu-ga: call Windows VSS requester in fsfreeze command handler
  2013-07-02 15:16             ` Luiz Capitulino
@ 2013-07-02 15:28               ` Laszlo Ersek
  0 siblings, 0 replies; 70+ messages in thread
From: Laszlo Ersek @ 2013-07-02 15:28 UTC (permalink / raw)
  To: Luiz Capitulino
  Cc: libaiqing, stefanha, qemu-devel, mdroth, vrozenfe,
	Tomoki Sekiyama, pbonzini, Seiji Aguchi, areis

On 07/02/13 17:16, Luiz Capitulino wrote:
> On Tue, 02 Jul 2013 16:44:55 +0200
> Laszlo Ersek <lersek@redhat.com> wrote:
> 
>> On 07/02/13 16:09, Luiz Capitulino wrote:
>>> On Tue, 02 Jul 2013 08:36:11 +0200
>>> Laszlo Ersek <lersek@redhat.com> wrote:
>>>
>>>> On 07/01/13 19:59, Tomoki Sekiyama wrote:
>>>>> On 7/1/13 9:29 , "Laszlo Ersek" <lersek@redhat.com> wrote:
>>>>>
>>>>>>> +error:
>>>>>>> +    qmp_guest_fsfreeze_thaw(NULL);
>>>>>>
>>>>>> Passing NULL here as "errp" concerns me slightly. I've been assuming
>>>>>> that "errp" is never NULL due to the outermost QMP layer always wanting
>>>>>> to receive errors and to serialize them.
>>>>>>
>>>>>> Specifically, a NULL "errp" would turn all error_set*(errp, ...) calls
>>>>>> into no-ops under qmp_guest_fsfreeze_thaw(), and all error_is_set(errp)
>>>>>> questions would answer with false. That said, nothing seems to be
>>>>>> affected right now.
>>>>>>
>>>>>> Maybe a dummy local variable would be more future-proof... OTOH it would
>>>>>> be stylistically questionable :)
>>>>>
>>>>> OK, then it should be:
>>>>>     Error *local_err = NULL;
>>>>> ...
>>>>> error:
>>>>>     qmp_guest_fsfreeze_thaw(&local_err);
>>>>>     if (error_is_set(&local_err)) {
>>>>>         error_free(local_err);
>>>>>     }
>>>>
>>>> I think so, yes.
>>>>
>>>> You could also log it before freeing it I guess:
>>>>
>>>>     g_debug("cleanup thaw: %s", error_get_pretty(local_err));
>>>>
>>>> or some such, but it's probably not important.
>>>
>>> I'd rather do something like that, otherwise it doesn't seem to
>>> make sense to pass Error as qmp_guest_fsfreeze_thaw() does
>>> use a local Error.
>>
>> No, the win32 version of qmp_guest_fsfreeze_thaw() being added by this
>> patch doesn't.
> 
> Oh, I looked into the POSIX one. But then, it's qmp_guest_fsfreeze_thaw()
> that has to be fixed, isn't it?

I didn't insist on that because the QMP command implementations in the
guest agent are all rooted in the dispatcher, and the dispatcher makes
sure for all commands that errp is never NULL. This is the only call
site where a NULL errp was manually passed, and we're discussing how to
fix it -- use a local_err, and maybe even print it.

Of course I don't insist on *not* reworking it either.

Thanks,
Laszlo

^ permalink raw reply	[flat|nested] 70+ messages in thread

end of thread, other threads:[~2013-07-02 15:26 UTC | newest]

Thread overview: 70+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-06-06 15:06 [Qemu-devel] [PATCH v4 00/10] qemu-ga: fsfreeze on Windows using VSS Tomoki Sekiyama
2013-06-06 15:06 ` [Qemu-devel] [PATCH v4 01/10] configure: Support configuring c++ compiler Tomoki Sekiyama
2013-06-18 10:17   ` Paolo Bonzini
2013-06-25  8:15   ` Laszlo Ersek
2013-06-06 15:06 ` [Qemu-devel] [PATCH v4 02/10] Add c++ keywords to QAPI helper script Tomoki Sekiyama
2013-06-25  8:16   ` Laszlo Ersek
2013-06-06 15:06 ` [Qemu-devel] [PATCH v4 03/10] checkpatch.pl: check .cpp files Tomoki Sekiyama
2013-06-25  8:17   ` Laszlo Ersek
2013-06-06 15:06 ` [Qemu-devel] [PATCH v4 04/10] Add a script to extract VSS SDK headers on POSIX system Tomoki Sekiyama
2013-06-25  8:30   ` Laszlo Ersek
2013-06-25 15:01   ` Laszlo Ersek
2013-06-25 15:01     ` Paolo Bonzini
2013-06-26 10:52       ` Laszlo Ersek
2013-06-06 15:06 ` [Qemu-devel] [PATCH v4 05/10] qemu-ga: Add configure options to specify path to Windows/VSS SDK Tomoki Sekiyama
2013-06-25 11:16   ` Laszlo Ersek
2013-06-28 10:43     ` Daniel P. Berrange
2013-06-28 10:54       ` Paolo Bonzini
2013-06-28 11:01         ` Daniel P. Berrange
2013-06-28 11:18           ` Paolo Bonzini
2013-06-28 11:30             ` Daniel P. Berrange
2013-06-28 12:18               ` Paolo Bonzini
2013-06-28 11:05       ` Laszlo Ersek
2013-06-06 15:06 ` [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze Tomoki Sekiyama
2013-06-25 16:03   ` Laszlo Ersek
2013-06-25 16:19     ` Paolo Bonzini
2013-06-25 18:23     ` Tomoki Sekiyama
2013-06-25 18:59       ` Paolo Bonzini
2013-06-25 19:28         ` Tomoki Sekiyama
2013-06-25 19:52       ` Laszlo Ersek
2013-06-26 14:32     ` Laszlo Ersek
2013-06-26 21:27       ` Paolo Bonzini
2013-06-26 22:09       ` Tomoki Sekiyama
2013-06-27 15:01       ` Laszlo Ersek
2013-06-27 22:25         ` Tomoki Sekiyama
2013-06-28  7:05           ` Paolo Bonzini
2013-06-28 10:40             ` Laszlo Ersek
2013-06-28 10:40               ` Paolo Bonzini
2013-06-28 17:18                 ` Tomoki Sekiyama
2013-06-28 10:44           ` Laszlo Ersek
2013-06-25 21:15   ` Paolo Bonzini
2013-06-25 22:31     ` Tomoki Sekiyama
2013-06-26  5:59       ` Paolo Bonzini
2013-06-26 14:13         ` Tomoki Sekiyama
2013-06-30 13:16       ` Gal Hammer
2013-07-01 18:57         ` Tomoki Sekiyama
2013-07-02 12:36           ` Gal Hammer
2013-06-06 15:06 ` [Qemu-devel] [PATCH v4 07/10] qemu-ga: Add Windows VSS requester to quiesce applications and filesystems Tomoki Sekiyama
2013-06-28 18:01   ` Laszlo Ersek
2013-06-28 18:34     ` Laszlo Ersek
2013-06-30  1:21     ` Tomoki Sekiyama
2013-07-01  8:31       ` Laszlo Ersek
2013-06-06 15:06 ` [Qemu-devel] [PATCH v4 08/10] qemu-ga: call Windows VSS requester in fsfreeze command handler Tomoki Sekiyama
2013-07-01 13:29   ` Laszlo Ersek
2013-07-01 17:48     ` Eric Blake
2013-07-01 17:59     ` Tomoki Sekiyama
2013-07-02  6:36       ` Laszlo Ersek
2013-07-02 14:09         ` Luiz Capitulino
2013-07-02 14:44           ` Laszlo Ersek
2013-07-02 15:16             ` Luiz Capitulino
2013-07-02 15:28               ` Laszlo Ersek
2013-07-02 14:58         ` Michael Roth
2013-06-06 15:06 ` [Qemu-devel] [PATCH v4 09/10] qemu-ga: install Windows VSS provider on `qemu-ga -s install' Tomoki Sekiyama
2013-07-01 14:50   ` Laszlo Ersek
2013-07-01 17:59     ` Tomoki Sekiyama
2013-06-06 15:07 ` [Qemu-devel] [PATCH v4 10/10] QMP/qemu-ga-client: make timeout longer for guest-fsfreeze-freeze command Tomoki Sekiyama
2013-06-18 10:17   ` Paolo Bonzini
2013-07-01 15:02   ` Laszlo Ersek
2013-06-10  9:26 ` [Qemu-devel] [PATCH v4 00/10] qemu-ga: fsfreeze on Windows using VSS Stefan Hajnoczi
2013-06-24 19:30 ` Tomoki Sekiyama
2013-06-24 21:13   ` Laszlo Ersek

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.