Skip to contentSkip to conversation

qemu-ga: fsfreeze on Windows using VSS

PATCH Superseded Tomoki Sekiyama 9 participants 70 messages Jun 6, 2013 – Jul 2, 2013

Subsystems (2)

v4 of 10 Superseded Latest is v10 →

lore
Share & tools
lore
#0 lore raw

v4 cover qemu-ga: fsfreeze on Windows using VSS

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
#1 lore raw

PATCH v4 1/10 configure: Support configuring c++ compiler

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.

---
 configure |   13 +++++++++++++
 rules.mak |    9 ++++++++-
 2 files changed, 21 insertions(+), 1 deletion(-)
configure +13 -0
index 1654413..a2fc3f3 100755
@@ -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
rules.mak +8 -1
index 4499745..abc2e84 100644
@@ -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 on v4 1/10
#2 lore raw

Re: [Qemu-devel] [PATCH v4 01/10] configure: Support configuring c++ compiler

Il 06/06/2013 17:06, Tomoki Sekiyama ha scritto:

Quoted 101 lines

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.

---
 configure |   13 +++++++++++++
 rules.mak |    9 ++++++++-
 2 files changed, 21 insertions(+), 1 deletion(-)
configure +13 -0
index 1654413..a2fc3f3 100755
@@ -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
rules.mak +7 -0
index 4499745..abc2e84 100644
@@ -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)$@")
 

rules.mak quiet-command = $(if $(V),$1,$(if $(2),@echo $2 && $1, @$1)) · line 83

Quoted 8 lines
rules.mak +1 -1
@@ -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 on v4 1/10
#3 lore raw

Re: [Qemu-devel] [PATCH v4 01/10] configure: Support configuring c++ compiler

In reply to Tomoki Sekiyama
Quoted 11 lines · Tomoki Sekiyama

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.

---
 configure |   13 +++++++++++++
 rules.mak |    9 ++++++++-
 2 files changed, 21 insertions(+), 1 deletion(-)
#4 lore raw

PATCH v4 2/10 Add c++ keywords to QAPI helper script

In reply to Tomoki Sekiyama

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

---
 hmp.c           |    2 +-
 hw/pci/pci.c    |    2 +-
 scripts/qapi.py |   12 +++++++++++-
 3 files changed, 13 insertions(+), 3 deletions(-)
hmp.c +1 -1
index 4fb76ec..cb0ed3b 100644
@@ -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",
hw/pci/pci.c +1 -1
index bb3879b..f4bef02 100644
@@ -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;
scripts/qapi.py +11 -1
index 02ad668..dea6ada 100644
@@ -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("*")
 
Reviewed-by on v4 2/10
#5 lore raw

Re: [Qemu-devel] [PATCH v4 02/10] Add c++ keywords to QAPI helper script

Quoted 9 lines · Tomoki Sekiyama

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

---
 hmp.c           |    2 +-
 hw/pci/pci.c    |    2 +-
 scripts/qapi.py |   12 +++++++++++-
 3 files changed, 13 insertions(+), 3 deletions(-)
#6 lore raw

PATCH v4 3/10 checkpatch.pl: check .cpp files

In reply to Tomoki Sekiyama

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())
---
 scripts/checkpatch.pl |   34 ++++++++++++++++++++++++++--------
 1 file changed, 26 insertions(+), 8 deletions(-)
scripts/checkpatch.pl +26 -8
index ec0aa4c..53a12a8 100755
@@ -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+>/) ||
Reviewed-by on v4 3/10
#7 lore raw

Re: [Qemu-devel] [PATCH v4 03/10] checkpatch.pl: check .cpp files

Quoted 4 lines · Tomoki Sekiyama

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

Quoted 8 lines
  - :: 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())
---
 scripts/checkpatch.pl |   34 ++++++++++++++++++++++++++--------
 1 file changed, 26 insertions(+), 8 deletions(-)

It's Perl, enough said.

#8 lore raw

PATCH v4 4/10 Add a script to extract VSS SDK headers on POSIX system

In reply to Tomoki Sekiyama

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 <[email protected]>

---
 scripts/extract-vsssdk-headers |   36 ++++++++++++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)
 create mode 100755 scripts/extract-vsssdk-headers
scripts/extract-vsssdk-headers new file +36 -0
new file mode 100755
index 0000000..95c0b38
@@ -0,0 +1,36 @@
+#! /bin/bash
+
+# extract-vsssdk-headers
+# Author: Paolo Bonzini <[email protected]>
+
+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
Reviewed-by on v4 4/10
#9 lore raw

Re: [Qemu-devel] [PATCH v4 04/10] Add a script to extract VSS SDK headers on POSIX system

scripts/extract-vsssdk-headers line 28

Quoted 2 lines · Tomoki Sekiyama
scripts/extract-vsssdk-headers new file +2 -0
+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...

#10 lore raw

Re: [Qemu-devel] [PATCH v4 04/10] Add a script to extract VSS SDK headers on POSIX system

In reply to Tomoki Sekiyama

scripts/extract-vsssdk-headers line 15

Quoted 4 lines · Tomoki Sekiyama
scripts/extract-vsssdk-headers new file +4 -0
+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

#11 lore raw

Re: [Qemu-devel] [PATCH v4 04/10] Add a script to extract VSS SDK headers on POSIX system

Il 25/06/2013 17:01, Laszlo Ersek ha scritto:

Quoted 16 lines · Tomoki Sekiyama

Review

Quoted 5 lines
(quoted diff) +4 -0
+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

Quoted 7 lines

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

Quoted 1 line

From 0eee72d26d06d7087d5d83efbc37564f67489019 Mon Sep 17 00:00:00 2001

From: Paolo Bonzini <[email protected]>
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(-)
configure +1 -1
index 30aa84d..029f5f9 100755
@@ -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
 "
 
configure.in +1 -1
index a830360..c6c7254 100644
@@ -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
 "
 
gsf/gsf-timestamp.c +9 -7
index 9d45f9a..a8e0a44 100644
@@ -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;
 }
 
Signature · 1 line
1.7.1
#12 lore raw

Re: [Qemu-devel] [PATCH v4 04/10] Add a script to extract VSS SDK headers on POSIX system

Quoted 25 lines · Paolo Bonzini

Il 25/06/2013 17:01, Laszlo Ersek ha scritto:

Quoted 16 lines · Tomoki Sekiyama

Review

Quoted 5 lines
(quoted diff) +4 -0
+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

Quoted 7 lines

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

Quoted 1 line

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

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

#13 lore raw

PATCH v4 5/10 qemu-ga: Add configure options to specify path to Windows/VSS SDK

In reply to Tomoki Sekiyama

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.

---
 .gitignore |    1 +
 Makefile   |    1 +
 configure  |   69 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 71 insertions(+)
.gitignore +1 -0
index 64e9466..67facd3 100644
@@ -79,6 +79,7 @@ fsdev/virtfs-proxy-helper.pod
 *.la
 *.pc
 .libs
+.sdk
 *.swp
 *.orig
 .pc
Makefile +1 -0
index a96736b..4851ba0 100644
@@ -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
 
configure +69 -0

111 lines not on this page · raw message

Reviewed-by on v4 5/10
#14 lore raw

Re: [Qemu-devel] [PATCH v4 05/10] qemu-ga: Add configure options to specify path to Windows/VSS SDK

Quoted 22 lines · Tomoki Sekiyama

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.

Quoted 6 lines
---
 .gitignore |    1 +
 Makefile   |    1 +
 configure  |   69 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 71 insertions(+)
#15 lore raw

Re: [Qemu-devel] [PATCH v4 05/10] qemu-ga: Add configure options to specify path to Windows/VSS SDK

Quoted 11 lines · Laszlo Ersek
Quoted 10 lines · Tomoki Sekiyama

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.

Quoted 6 lines
Quoted 6 lines

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

Signature · 4 lines
|: 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 :|
#16 lore raw

Re: [Qemu-devel] [PATCH v4 05/10] qemu-ga: Add configure options to specify path to Windows/VSS SDK

Il 28/06/2013 12:43, Daniel P. Berrange ha scritto:

Quoted 15 lines · Laszlo Ersek
Quoted 14 lines · Tomoki Sekiyama
Quoted 13 lines

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

Quoted 6 lines
Quoted 3 lines
Quoted 3 lines

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

#17 lore raw

Re: [Qemu-devel] [PATCH v4 05/10] qemu-ga: Add configure options to specify path to Windows/VSS SDK

Quoted 16 lines · Paolo Bonzini

Il 28/06/2013 12:43, Daniel P. Berrange ha scritto:

Quoted 15 lines · Laszlo Ersek
Quoted 14 lines · Tomoki Sekiyama
Quoted 13 lines

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

Quoted 8 lines
Quoted 6 lines
Quoted 3 lines
Quoted 3 lines

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

Signature · 4 lines
|: 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 :|
#18 lore raw

Re: [Qemu-devel] [PATCH v4 05/10] qemu-ga: Add configure options to specify path to Windows/VSS SDK

Il 28/06/2013 13:01, Daniel P. Berrange ha scritto:

Quoted 7 lines
Quoted 4 lines
Quoted 2 lines

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.

Quoted 8 lines

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

#19 lore raw

Re: [Qemu-devel] [PATCH v4 05/10] qemu-ga: Add configure options to specify path to Windows/VSS SDK

Quoted 36 lines · Paolo Bonzini

Il 28/06/2013 13:01, Daniel P. Berrange ha scritto:

Quoted 7 lines
Quoted 4 lines
Quoted 2 lines

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.

Quoted 8 lines

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

Quoted 8 lines

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

Signature · 4 lines
|: 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 :|
#20 lore raw

Re: [Qemu-devel] [PATCH v4 05/10] qemu-ga: Add configure options to specify path to Windows/VSS SDK

Il 28/06/2013 13:30, Daniel P. Berrange ha scritto:

Quoted 4 lines
Quoted 4 lines
Quoted 3 lines
Quoted 3 lines

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 ?

Quoted 8 lines
Quoted 1 line

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

#21 lore raw

Re: [Qemu-devel] [PATCH v4 05/10] qemu-ga: Add configure options to specify path to Windows/VSS SDK

In reply to Daniel P. Berrange
Quoted 15 lines · Daniel P. Berrange
Quoted 14 lines · Laszlo Ersek
Quoted 13 lines · Tomoki Sekiyama

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

Quoted 6 lines
Quoted 3 lines
Quoted 3 lines

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

#22 lore raw

PATCH v4 6/10 qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze

In reply to Tomoki Sekiyama

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.

---
 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
Makefile +1 -1
index 4851ba0..636358d 100644
@@ -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
Makefile.objs +2 -0
index 286ce06..b6c1505 100644
@@ -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)
configure +4 -1
index cafe830..8e45fad 100755
@@ -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
qga/Makefile.objs +6 -0
index b8d7cd0..8d93866 100644
@@ -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
qga/vss-win32-provider.h new file +26 -0
new file mode 100644
index 0000000..a437e71
@@ -0,0 +1,26 @@
+/*
+ * QEMU Guest Agent win32 VSS provider declarations
+ *
+ * Copyright Hitachi Data Systems Corp. 2013
+ *
+ * Authors:
+ *  Tomoki Sekiyama   <[email protected]>
+ *
+ * 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
qga/vss-win32-provider/Makefile.objs new file +24 -0
new file mode 100644
index 0000000..1fe1f8f
@@ -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
qga/vss-win32-provider/install.cpp new file +493 -0

493 lines not on this page · raw message

qga/vss-win32-provider/provider.cpp new file +479 -0

479 lines not on this page · raw message

qga/vss-win32-provider/qga-provider.def new file +10 -0
new file mode 100644
index 0000000..9f3afc8
@@ -0,0 +1,10 @@
+LIBRARY      "QGA-PROVIDER.DLL"
+
+EXPORTS
+	VSSCheckOSVersion	PRIVATE
+	COMRegister		PRIVATE
+	COMUnregister		PRIVATE
+	DllCanUnloadNow		PRIVATE
+	DllGetClassObject	PRIVATE
+	DllRegisterServer	PRIVATE
+	DllUnregisterServer	PRIVATE
qga/vss-win32-provider/qga-provider.idl new file +20 -0
new file mode 100644
index 0000000..17abca0
@@ -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;
+    };
+};
qga/vss-win32-provider/qga-provider.tlb new file binary +0 -0
new file mode 100644
index 0000000000000000000000000000000000000000..226452a1861371ffe0cad1019cf90fdfdcd5ef49
GIT binary patch
qga/vss-win32.h new file +86 -0

86 lines not on this page · raw message

#23 lore raw

Re: [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze

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

Quoted 31 lines · Tomoki Sekiyama

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.

---
 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
configure +0 -0
index cafe830..8e45fad 100755
@@ -3490,9 +3490,12 @@ if test "$softmmu" = yes ; then
       virtfs=no
     fi
   fi

configure if test "$softmmu" = yes ; then · line 3501

Quoted 10 lines
configure +4 -1
-  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.)

Makefile clean: · line 241

Quoted 13 lines
Makefile +1 -1
index 4851ba0..636358d 100644
@@ -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.

Quoted 13 lines
Makefile.objs +1 -0
index 286ce06..b6c1505 100644
@@ -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)
 

Makefile.objs nested-vars += \ · line 120

Quoted 7 lines
Makefile.objs +1 -0
@@ -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"?

qga/Makefile.objs qga-obj-$(CONFIG_POSIX) += commands-posix.o channel-posix.o · line 8

Quoted 11 lines
qga/Makefile.objs +3 -0
index b8d7cd0..8d93866 100644
@@ -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/Makefile.objs qga-obj-$(CONFIG_POSIX) += commands-posix.o channel-posix.o · line 11

Quoted 3 lines
qga/Makefile.objs +3 -0
+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.

Quoted 7 lines
qga/vss-win32-provider/Makefile.objs new file +1 -0
new file mode 100644
index 0000000..1fe1f8f
@@ -0,0 +1,24 @@
+# rules to build qga-provider.dll

qga/vss-win32-provider/Makefile.objs line 11

Quoted 10 lines
qga/vss-win32-provider/Makefile.objs new file +10 -0
+
+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")?

qga/vss-win32-provider/Makefile.objs line 24

Quoted 13 lines
qga/vss-win32-provider/Makefile.objs new file +13 -0
+
+
+# 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

Quoted 1,194 lines
 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
qga/vss-win32-provider.h new file +26 -0
new file mode 100644
index 0000000..a437e71
@@ -0,0 +1,26 @@
+/*
+ * QEMU Guest Agent win32 VSS provider declarations
+ *
+ * Copyright Hitachi Data Systems Corp. 2013
+ *
+ * Authors:
+ *  Tomoki Sekiyama   <[email protected]>
+ *
+ * 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
qga/vss-win32-provider/install.cpp new file +493 -0
new file mode 100644
index 0000000..d6f1d55
@@ -0,0 +1,493 @@
+/*
+ * QEMU Guest Agent win32 VSS Provider installer
+ *
+ * Copyright Hitachi Data Systems Corp. 2013
+ *
+ * Authors:
+ *  Tomoki Sekiyama   <[email protected]>
+ *
+ * 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;
+    }
+}
qga/vss-win32-provider/provider.cpp new file +479 -0
new file mode 100644
index 0000000..90a3d25
@@ -0,0 +1,479 @@
+/*
+ * QEMU Guest Agent win32 VSS Provider implementations
+ *
+ * Copyright Hitachi Data Systems Corp. 2013
+ *
+ * Authors:
+ *  Tomoki Sekiyama   <[email protected]>
+ *
+ * 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;
+}
qga/vss-win32-provider/qga-provider.def new file +10 -0
new file mode 100644
index 0000000..9f3afc8
@@ -0,0 +1,10 @@
+LIBRARY      "QGA-PROVIDER.DLL"
+
+EXPORTS
+	VSSCheckOSVersion	PRIVATE
+	COMRegister		PRIVATE
+	COMUnregister		PRIVATE
+	DllCanUnloadNow		PRIVATE
+	DllGetClassObject	PRIVATE
+	DllRegisterServer	PRIVATE
+	DllUnregisterServer	PRIVATE
qga/vss-win32-provider/qga-provider.idl new file +20 -0
new file mode 100644
index 0000000..17abca0
@@ -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;
+    };
+};
qga/vss-win32-provider/qga-provider.tlb new file binary +0 -0
new file mode 100644
index 0000000000000000000000000000000000000000..226452a1861371ffe0cad1019cf90fdfdcd5ef49
GIT binary patch
qga/vss-win32.h new file +86 -0
new file mode 100644
index 0000000..21655cf
@@ -0,0 +1,86 @@
+/*
+ * QEMU Guest Agent win32 VSS common declarations
+ *
+ * Copyright Hitachi Data Systems Corp. 2013
+ *
+ * Authors:
+ *  Tomoki Sekiyama   <[email protected]>
+ *
+ * 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
#24 lore raw

Re: [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze

Il 25/06/2013 18:03, Laszlo Ersek ha scritto:

Quoted 11 lines
Quoted 8 lines

Makefile.objs nested-vars += \ · line 120

Quoted 8 lines
Makefile.objs +1 -0
@@ -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'"?

Quoted 1 line

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

Quoted 7 lines

qga/Makefile.objs qga-obj-$(CONFIG_POSIX) += commands-posix.o channel-posix.o · line 11

Quoted 3 lines
qga/Makefile.objs +3 -0
+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.

Quoted 4 lines

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.

Quoted 7 lines

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

#25 lore raw

Re: [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze

In reply to Laszlo Ersek

Thanks for your review.

Quoted 18 lines · Laszlo Ersek

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

Quoted 16 lines
configure +0 -1
index cafe830..8e45fad 100755
@@ -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"
configure +2 -0
+      if [ "$mingw32" = "yes" ]; then
+        tools="qga/vss-win32-provider/qga-provider.dll
Quoted 8 lines
Quoted 5 lines

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.

Quoted 4 lines

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:

Quoted 8 lines
$ ./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.

Quoted 12 lines
Quoted 12 lines
Makefile.objs +1 -0
index 286ce06..b6c1505 100644
@@ -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)
 
Quoted 11 lines

Makefile.objs nested-vars += \ · line 120

Quoted 8 lines
Makefile.objs +1 -0
@@ -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.

Quoted 1 line

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

Yes...

Quoted 10 lines
Quoted 10 lines
qga/Makefile.objs +0 -0
index b8d7cd0..8d93866 100644
@@ -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

Quoted 8 lines

qga/Makefile.objs qga-obj-$(CONFIG_POSIX) += commands-posix.o channel-posix.o · line 8

Quoted 4 lines

qga-obj-y += qapi-generated/qga-qmp-marshal.o

qga/Makefile.objs +3 -0
+
+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.

Quoted 7 lines

qga/Makefile.objs qga-obj-$(CONFIG_POSIX) += commands-posix.o channel-posix.o · line 11

Quoted 3 lines
qga/Makefile.objs +3 -0
+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...

Quoted 29 lines
Quoted 24 lines
qga/vss-win32-provider/Makefile.objs new file +7 -2
new file mode 100644
index 0000000..1fe1f8f
@@ -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

qga/vss-win32-provider/Makefile.objs new file +3 -2
+
+$(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

qga/vss-win32-provider/Makefile.objs new file +1 -0
+	$(call quiet-command,$(CXX) -o $@ $(qga-prv-obj-y)
Quoted 2 lines

$(SRC_PATH)/qga/vss-win32-provider/qga-provider.def $(CXXFLAGS) $(LDFLAGS)," LINK $(TARGET_DIR)$@")

I'm having a hard time following this.

Quoted 4 lines

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.

Quoted 2 lines

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.

Quoted 7 lines

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.

Quoted 1 line

If so, (a) how will they communicate

Using COM interface.

Quoted 1 line

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

Quoted 7 lines

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.

Quoted 5 lines

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

Quoted 2 lines

Thanks!
Laszlo

Again, thank you for taking your time for this!

Thanks,
Tomoki Sekiyama

#26 lore raw

Re: [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze

Il 25/06/2013 20:23, Tomoki Sekiyama ha scritto:

Quoted 5 lines
Quoted 1 line
Quoted 1 line

(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

#27 lore raw

Re: [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze

Quoted 11 lines

Il 25/06/2013 20:23, Tomoki Sekiyama ha scritto:

Quoted 4 lines
Quoted 1 line
Quoted 1 line

(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

#28 lore raw

Re: [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze

In reply to Tomoki Sekiyama
Quoted 10 lines · Tomoki Sekiyama
Quoted 6 lines · Laszlo Ersek

qga/Makefile.objs qga-obj-$(CONFIG_POSIX) += commands-posix.o channel-posix.o · line 8

Quoted 2 lines
qga/Makefile.objs +2 -0
+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! :))

Quoted 1 line

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

OK, thanks.

Quoted 10 lines
Quoted 2 lines

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.

Quoted 1 line

If so, (a) how will they communicate

Using COM interface.

[...]

Quoted 12 lines
Quoted 5 lines

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

Quoted 3 lines

"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

#29 lore raw

Re: [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze

In reply to Laszlo Ersek
Quoted 1 line · Laszlo Ersek

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

Comparing the .def file and the provider header file:

Quoted 41 lines
Quoted 41 lines
qga/vss-win32-provider/qga-provider.def new file +10 -0
new file mode 100644
index 0000000..9f3afc8
@@ -0,0 +1,10 @@
+LIBRARY      "QGA-PROVIDER.DLL"
+
+EXPORTS
+	VSSCheckOSVersion	PRIVATE
+	COMRegister		PRIVATE
+	COMUnregister		PRIVATE
+	DllCanUnloadNow		PRIVATE
+	DllGetClassObject	PRIVATE
+	DllRegisterServer	PRIVATE
+	DllUnregisterServer	PRIVATE
qga/vss-win32-provider.h new file +18 -0
new file mode 100644
index 0000000..a437e71
@@ -0,0 +1,26 @@
+/*
+ * QEMU Guest Agent win32 VSS provider declarations
+ *
+ * Copyright Hitachi Data Systems Corp. 2013
+ *
+ * Authors:
+ *  Tomoki Sekiyama   <[email protected]>
+ *
+ * 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);
Quoted 8 lines

qga/vss-win32-provider.h line 26

Quoted 8 lines
qga/vss-win32-provider.h new file +8 -0
+
+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.)

Quoted 56 lines
Quoted 56 lines
qga/vss-win32.h new file +50 -0
new file mode 100644
index 0000000..21655cf
@@ -0,0 +1,86 @@
+/*
+ * QEMU Guest Agent win32 VSS common declarations
+ *
+ * Copyright Hitachi Data Systems Corp. 2013
+ *
+ * Authors:
+ *  Tomoki Sekiyama   <[email protected]>
+ *
+ * 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
+
+/*
Quoted 8 lines

qga/vss-win32.h line 58

Quoted 8 lines
qga/vss-win32.h new file +8 -0
+ * 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.)

Quoted 9 lines

qga/vss-win32.h line 67

Quoted 9 lines
qga/vss-win32.h new file +9 -0
+
+/* 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.)

Quoted 10 lines

qga/vss-win32.h line 77

Quoted 10 lines
qga/vss-win32.h new file +10 -0
+
+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".

Quoted 11 lines

qga/vss-win32.h line 86

Quoted 11 lines
qga/vss-win32.h new file +9 -0
+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.

Quoted 31 lines
Quoted 31 lines
qga/vss-win32-provider/install.cpp new file +25 -0
new file mode 100644
index 0000000..d6f1d55
@@ -0,0 +1,493 @@
+/*
+ * QEMU Guest Agent win32 VSS Provider installer
+ *
+ * Copyright Hitachi Data Systems Corp. 2013
+ *
+ * Authors:
+ *  Tomoki Sekiyama   <[email protected]>
+ *
+ * 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;
+
Quoted 8 lines

qga/vss-win32-provider/install.cpp line 33

Quoted 8 lines
qga/vss-win32-provider/install.cpp new file +8 -0
+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.

Quoted 10 lines

qga/vss-win32-provider/install.cpp line 43

Quoted 10 lines
qga/vss-win32-provider/install.cpp new file +10 -0
+
+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.)

Quoted 1 line

qga/vss-win32-provider/install.cpp line 44

Quoted 1 line
qga/vss-win32-provider/install.cpp new file +1 -0
+    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.)

Quoted 5 lines
Quoted 5 lines
qga/vss-win32-provider/install.cpp new file +5 -0
+    LocalFree(msg);
+}
+
+static void errmsg_dialog(DWORD err, const char *text, const char *opt = "")
+{
Quoted 8 lines

qga/vss-win32-provider/install.cpp line 57

Quoted 8 lines
qga/vss-win32-provider/install.cpp new file +8 -0
+    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?

Quoted 26 lines
Quoted 26 lines
qga/vss-win32-provider/install.cpp new file +26 -0
+    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);
Quoted 8 lines

qga/vss-win32-provider/install.cpp line 91

Quoted 8 lines
qga/vss-win32-provider/install.cpp new file +8 -0
+    GetVersionEx(&OSver);
+    if ((OSver.dwMajorVersion == 5 && OSver.dwMinorVersion >= 2) ||
+       OSver.dwMajorVersion > 5) {
+        return S_OK;
+    }
+    return S_FALSE;
+}
+

Seems OK.

Quoted 3 lines

qga/vss-win32-provider/install.cpp line 94

Quoted 3 lines
qga/vss-win32-provider/install.cpp new file +3 -0
+/* 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.)

Quoted 12 lines
Quoted 12 lines
qga/vss-win32-provider/install.cpp new file +12 -0
+    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));
Quoted 8 lines

qga/vss-win32-provider/install.cpp line 114

Quoted 8 lines
qga/vss-win32-provider/install.cpp new file +8 -0
+    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.

Quoted 3 lines

qga/vss-win32-provider/install.cpp line 117

Quoted 3 lines
qga/vss-win32-provider/install.cpp new file +3 -0
+    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.)

Quoted 1 line

qga/vss-win32-provider/install.cpp line 118

Quoted 1 line
qga/vss-win32-provider/install.cpp new file +1 -0
+    }

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

Quoted 8 lines

qga/vss-win32-provider/install.cpp line 126

Quoted 8 lines
qga/vss-win32-provider/install.cpp new file +8 -0
+    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?

Quoted 7 lines
Quoted 7 lines
qga/vss-win32-provider/install.cpp new file +7 -0
+out:
+    if (pLoc) {
+        pLoc->Release();
+    }
+    if (pSvc) {
+        pSvc->Release();
+    }
Quoted 8 lines

qga/vss-win32-provider/install.cpp line 141

Quoted 8 lines
qga/vss-win32-provider/install.cpp new file +8 -0
+    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.

Quoted 9 lines
Quoted 9 lines
qga/vss-win32-provider/install.cpp new file +9 -0
+
+/* 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;
Quoted 8 lines

qga/vss-win32-provider/install.cpp line 158

Quoted 8 lines
qga/vss-win32-provider/install.cpp new file +8 -0
+    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.

Quoted 4 lines

qga/vss-win32-provider/install.cpp line 162

Quoted 4 lines
qga/vss-win32-provider/install.cpp new file +4 -0
+    }
+
+    if (VSSCheckOSVersion() == S_FALSE) {
+        printf("VSS provider is not supported in this OS version.\n");

I suggest stderr.

Quoted 1 line

qga/vss-win32-provider/install.cpp line 163

Quoted 1 line
qga/vss-win32-provider/install.cpp new file +1 -0
+        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.)

Quoted 3 lines

qga/vss-win32-provider/install.cpp line 166

Quoted 3 lines
qga/vss-win32-provider/install.cpp new file +3 -0
+    }
+
+    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,

Quoted 2 lines · Tomoki Sekiyama

COMRegister and COMUnregister are called byqemu-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.

Quoted 2 lines

qga/vss-win32-provider/install.cpp line 168

Quoted 2 lines
qga/vss-win32-provider/install.cpp new file +2 -0
+
+    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.

Quoted 9 lines
Quoted 9 lines
qga/vss-win32-provider/install.cpp new file +9 -0
+    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());
Quoted 8 lines

qga/vss-win32-provider/install.cpp line 185

Quoted 8 lines
qga/vss-win32-provider/install.cpp new file +8 -0
+    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?

Quoted 4 lines

qga/vss-win32-provider/install.cpp line 189

Quoted 4 lines
qga/vss-win32-provider/install.cpp new file +4 -0
+    chk(pObj->get_Key(&key));
+
+    pObj->Release();
+    pObj = NULL;

Ah OK. You're reusing pObj below.

Quoted 4 lines

qga/vss-win32-provider/install.cpp line 193

Quoted 4 lines
qga/vss-win32-provider/install.cpp new file +4 -0
+
+    if (!GetModuleFileName(g_hinstDll, dllPath, sizeof(dllPath))) {
+        errmsg(GetLastError(), "GetModuleFileName failed");
+        goto out;

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

Quoted 4 lines

qga/vss-win32-provider/install.cpp line 197

Quoted 4 lines
qga/vss-win32-provider/install.cpp new file +4 -0
+    }
+    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.

Quoted 6 lines

qga/vss-win32-provider/install.cpp line 203

Quoted 6 lines
qga/vss-win32-provider/install.cpp new file +6 -0
+    }
+    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 :)

Quoted 3 lines

qga/vss-win32-provider/install.cpp line 206

Quoted 3 lines
qga/vss-win32-provider/install.cpp new file +3 -0
+    if (!PathFileExists(tlbPath)) {
+        errmsg(ERROR_FILE_NOT_FOUND, "Failed to lookup tlb");
+        goto out;

"hr" not set properly.

Quoted 51 lines
Quoted 51 lines
qga/vss-win32-provider/install.cpp new file +51 -0
+    }
+
+    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) {
Quoted 8 lines

qga/vss-win32-provider/install.cpp line 265

Quoted 8 lines
qga/vss-win32-provider/install.cpp new file +8 -0
+        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.

Quoted 2 lines

qga/vss-win32-provider/install.cpp line 267

Quoted 2 lines
qga/vss-win32-provider/install.cpp new file +2 -0
+    }
+    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)?

Quoted 9 lines
Quoted 9 lines
qga/vss-win32-provider/install.cpp new file +9 -0
+
+    return hr;
+}
+
+/* Unregister this module from COM+ Applications Catalog */
+STDAPI COMUnregister(void)
+{
+    HRESULT hr;
+    IUnknown *pUnknown = NULL;
Quoted 8 lines

qga/vss-win32-provider/install.cpp line 284

Quoted 8 lines
qga/vss-win32-provider/install.cpp new file +8 -0
+    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.

Quoted 4 lines

qga/vss-win32-provider/install.cpp line 288

Quoted 4 lines
qga/vss-win32-provider/install.cpp new file +4 -0
+        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.

Quoted 2 lines

qga/vss-win32-provider/install.cpp line 290

Quoted 2 lines
qga/vss-win32-provider/install.cpp new file +2 -0
+
+    chk(CoInitialize(NULL));

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

I'll continue from here.

Thanks
Laszlo

Quoted 690 lines
Quoted 689 lines
qga/vss-win32-provider/install.cpp new file +203 -0
+    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;
+    }
+}
qga/vss-win32-provider/provider.cpp new file +479 -0
new file mode 100644
index 0000000..90a3d25
@@ -0,0 +1,479 @@
+/*
+ * QEMU Guest Agent win32 VSS Provider implementations
+ *
+ * Copyright Hitachi Data Systems Corp. 2013
+ *
+ * Authors:
+ *  Tomoki Sekiyama   <[email protected]>
+ *
+ * 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;
+}
#30 lore raw

Re: [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze

Il 26/06/2013 16:32, Laszlo Ersek ha scritto:

Quoted 5 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines

qga/vss-win32.h line 67

Quoted 2 lines
qga/vss-win32.h new file +2 -0
+#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

#31 lore raw

Re: [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze

In reply to Laszlo Ersek
Quoted 7 lines · Laszlo Ersek
Quoted 1 line · Laszlo Ersek

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.

Quoted 3 lines

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

Quoted 7 lines
Quoted 2 lines

qga/vss-win32.h line 67

Quoted 2 lines
qga/vss-win32.h new file +2 -0
+#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.

Quoted 10 lines
Quoted 8 lines

qga/vss-win32-provider/install.cpp line 33

Quoted 8 lines
qga/vss-win32-provider/install.cpp new file +8 -0
+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.

Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
qga/vss-win32-provider/install.cpp new file +8 -0
+
+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 |
Quoted 8 lines
Quoted 4 lines
Quoted 4 lines

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

Quoted 4 lines
Quoted 1 line
Quoted 1 line

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

Quoted 3 lines

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

Quoted 3 lines

(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]

Quoted 6 lines
Quoted 4 lines
Quoted 4 lines

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

Quoted 9 lines
Quoted 3 lines

qga/vss-win32-provider/install.cpp line 94

Quoted 3 lines
qga/vss-win32-provider/install.cpp new file +3 -0
+/* 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)'.

Quoted 7 lines
Quoted 3 lines

qga/vss-win32-provider/install.cpp line 117

Quoted 3 lines
qga/vss-win32-provider/install.cpp new file +3 -0
+    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.

Quoted 4 lines
Quoted 1 line
Quoted 1 line

+ }

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.

Quoted 9 lines
Quoted 8 lines

qga/vss-win32-provider/install.cpp line 126

Quoted 8 lines
qga/vss-win32-provider/install.cpp new file +8 -0
+    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;
Quoted 5 lines

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.

Quoted 22 lines
Quoted 7 lines
Quoted 7 lines
qga/vss-win32-provider/install.cpp new file +7 -0
+out:
+    if (pLoc) {
+        pLoc->Release();
+    }
+    if (pSvc) {
+        pSvc->Release();
+    }
Quoted 8 lines

qga/vss-win32-provider/install.cpp line 141

Quoted 8 lines
qga/vss-win32-provider/install.cpp new file +8 -0
+    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.)

Quoted 1 line

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

Right, it's decrementing reference counters.

Quoted 5 lines
Quoted 3 lines

qga/vss-win32-provider/install.cpp line 158

Quoted 3 lines
qga/vss-win32-provider/install.cpp new file +3 -0
+    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.

Quoted 6 lines
Quoted 4 lines

qga/vss-win32-provider/install.cpp line 162

Quoted 4 lines
qga/vss-win32-provider/install.cpp new file +4 -0
+    }
+
+    if (VSSCheckOSVersion() == S_FALSE) {
+        printf("VSS provider is not supported in this OS version.\n");

I suggest stderr.

OK.

Quoted 5 lines
Quoted 1 line
Quoted 1 line

+ 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".

Quoted 7 lines
Quoted 3 lines

qga/vss-win32-provider/install.cpp line 166

Quoted 3 lines
qga/vss-win32-provider/install.cpp new file +3 -0
+    }
+
+    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.

Quoted 10 lines

You've told Paolo,

Quoted 2 lines · Tomoki Sekiyama

COMRegister and COMUnregister are called byqemu-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().

Quoted 6 lines
Quoted 2 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+
+    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.

Quoted 14 lines
Quoted 5 lines
Quoted 5 lines
qga/vss-win32-provider/install.cpp new file +5 -0
+    /* Install COM+ Component */
+
+    chk(pCatalog->GetCollection(_bstr_t(L"Applications"),
+                                (IDispatch **)&pApps));
+    chk(pApps->Populate());
Quoted 8 lines

qga/vss-win32-provider/install.cpp line 185

Quoted 8 lines
qga/vss-win32-provider/install.cpp new file +8 -0
+    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));
Quoted 3 lines

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.

Quoted 1 line

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.

Quoted 6 lines
Quoted 4 lines

qga/vss-win32-provider/install.cpp line 193

Quoted 4 lines
qga/vss-win32-provider/install.cpp new file +4 -0
+
+    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,

Quoted 6 lines
Quoted 4 lines

qga/vss-win32-provider/install.cpp line 197

Quoted 4 lines
qga/vss-win32-provider/install.cpp new file +4 -0
+    }
+    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;".

Quoted 9 lines
Quoted 6 lines

qga/vss-win32-provider/install.cpp line 203

Quoted 6 lines
qga/vss-win32-provider/install.cpp new file +6 -0
+    }
+    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.

Quoted 5 lines
Quoted 3 lines

qga/vss-win32-provider/install.cpp line 206

Quoted 3 lines
qga/vss-win32-provider/install.cpp new file +3 -0
+    if (!PathFileExists(tlbPath)) {
+        errmsg(ERROR_FILE_NOT_FOUND, "Failed to lookup tlb");
+        goto out;

"hr" not set properly.

And here too.

...

Quoted 16 lines
Quoted 1 line
Quoted 1 line

+ 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

Quoted 1 line

Will these work (especially the final two)?

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

Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
qga/vss-win32-provider/install.cpp new file +7 -0
+/* Unregister this module from COM+ Applications Catalog */
+STDAPI COMUnregister(void)
+{
+    HRESULT hr;
+    IUnknown *pUnknown = NULL;
+    ICOMAdminCatalog *pCatalog = NULL;
+    ICatalogCollection *pColl = NULL;
Quoted 8 lines
Quoted 6 lines

qga/vss-win32-provider/install.cpp line 284

Quoted 6 lines
qga/vss-win32-provider/install.cpp new file +6 -0
+    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.

Quoted 10 lines
Quoted 4 lines

qga/vss-win32-provider/install.cpp line 288

Quoted 4 lines
qga/vss-win32-provider/install.cpp new file +4 -0
+        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.

Quoted 5 lines
Quoted 2 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+
+    chk(CoInitialize(NULL));

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

Will remove chk from here too.

Quoted 1 line

I'll continue from here.

Thanks a lot for reviewing this long complicated windows code!

Tomoki Sekiyama

#32 lore raw

Re: [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze

In reply to Laszlo Ersek
Quoted 14 lines · Laszlo Ersek
Quoted 13 lines · Laszlo Ersek
Quoted 11 lines · Tomoki Sekiyama
qga/vss-win32-provider/install.cpp new file +11 -0
+/* 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;
+
Quoted 8 lines
Quoted 8 lines

qga/vss-win32-provider/install.cpp line 290

Quoted 8 lines
qga/vss-win32-provider/install.cpp new file +8 -0
+    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

Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
qga/vss-win32-provider/install.cpp new file +6 -0
+    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));
Quoted 8 lines
Quoted 8 lines

qga/vss-win32-provider/install.cpp line 304

Quoted 8 lines
qga/vss-win32-provider/install.cpp new file +8 -0
+    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)

Quoted 2 lines
Quoted 2 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+            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.)

Quoted 2 lines
Quoted 2 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+    }
+    chk(pColl->SaveChanges(&n));

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

Quoted 26 lines
Quoted 26 lines
Quoted 26 lines
qga/vss-win32-provider/install.cpp new file +26 -0
+
+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;
+
Quoted 8 lines
Quoted 8 lines

qga/vss-win32-provider/install.cpp line 342

Quoted 8 lines
qga/vss-win32-provider/install.cpp new file +8 -0
+    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,

Quoted 11 lines
Quoted 11 lines

qga/vss-win32-provider/install.cpp line 353

Quoted 11 lines
qga/vss-win32-provider/install.cpp new file +11 -0
+    } 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.)

Quoted 29 lines
Quoted 29 lines
Quoted 29 lines
qga/vss-win32-provider/install.cpp new file +29 -0
+        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");
Quoted 8 lines
Quoted 8 lines

qga/vss-win32-provider/install.cpp line 390

Quoted 8 lines
qga/vss-win32-provider/install.cpp new file +8 -0
+        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())

Quoted 12 lines
Quoted 12 lines
Quoted 12 lines
qga/vss-win32-provider/install.cpp new file +12 -0
+    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;
+    }
Quoted 8 lines
Quoted 8 lines

qga/vss-win32-provider/install.cpp line 410

Quoted 8 lines
qga/vss-win32-provider/install.cpp new file +8 -0
+
+    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)

Quoted 9 lines
Quoted 9 lines

qga/vss-win32-provider/install.cpp line 419

Quoted 9 lines
qga/vss-win32-provider/install.cpp new file +9 -0
+    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)

Quoted 3 lines
Quoted 3 lines

qga/vss-win32-provider/install.cpp line 422

Quoted 3 lines
qga/vss-win32-provider/install.cpp new file +3 -0
+    if (FAILED(hr)) {
+        errmsg_dialog(hr, "RegisterProvider failed");
+        goto out;

(goto unnecessary)

Quoted 17 lines
Quoted 17 lines
Quoted 17 lines
qga/vss-win32-provider/install.cpp new file +17 -0
+    }
+
+out:
+    if (pVssAdmin) {
+        pVssAdmin->Release();
+    }
+    CoUninitialize();
+
+    if (FAILED(hr)) {
+        DllUnregisterServer();
+    }
+
+    return hr;
+}
+
+/* Unregister this VSS hardware provider from the system */
+STDAPI DllUnregisterServer(void)
Quoted 8 lines
Quoted 8 lines

qga/vss-win32-provider/install.cpp line 447

Quoted 8 lines
qga/vss-win32-provider/install.cpp new file +8 -0
+{
+    TCHAR key[256];
+    IVssAdmin *pVssAdmin = NULL;
+
+    CoInitialize(NULL);
+
+    HRESULT hr = CoCreateInstance(CLSID_VSSCoordinator,
+         NULL, CLSCTX_ALL, IID_IVssAdmin, (void **)&pVssAdmin);

(indentation, maybe)

Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
qga/vss-win32-provider/install.cpp new file +7 -0
+    if (SUCCEEDED(hr)) {
+        hr = pVssAdmin->UnregisterProvider(g_gProviderId);
+        pVssAdmin->Release();
+    } else {
+        errmsg_dialog(hr, "CoCreateInstance(VSSCoordinator) failed");
+    }
+
Quoted 8 lines
Quoted 8 lines

qga/vss-win32-provider/install.cpp line 462

Quoted 8 lines
qga/vss-win32-provider/install.cpp new file +8 -0
+    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.

Quoted 4 lines
Quoted 4 lines

qga/vss-win32-provider/install.cpp line 466

Quoted 4 lines
qga/vss-win32-provider/install.cpp new file +4 -0
+
+
+/* Support functions for _bstr_t in MinGW: Originally written by: Diaa Sami */
+

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

Quoted 4 lines
Quoted 4 lines

qga/vss-win32-provider/install.cpp line 470

Quoted 4 lines
qga/vss-win32-provider/install.cpp new file +4 -0
+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.

Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
qga/vss-win32-provider/install.cpp new file +4 -0
+
+namespace _com_util
+{
+    char * __stdcall ConvertBSTRToString(BSTR bstr)
Quoted 8 lines
Quoted 8 lines

qga/vss-win32-provider/install.cpp line 482

Quoted 8 lines
qga/vss-win32-provider/install.cpp new file +8 -0
+    {
+        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.

Quoted 52 lines
Quoted 52 lines
Quoted 52 lines
qga/vss-win32-provider/install.cpp new file +11 -0
+
+    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;
+    }
+}
qga/vss-win32-provider/provider.cpp new file +33 -0
new file mode 100644
index 0000000..90a3d25
@@ -0,0 +1,479 @@
+/*
+ * QEMU Guest Agent win32 VSS Provider implementations
+ *
+ * Copyright Hitachi Data Systems Corp. 2013
+ *
+ * Authors:
+ *  Tomoki Sekiyama   <[email protected]>
+ *
+ * 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} };
Quoted 8 lines
Quoted 8 lines

qga/vss-win32-provider/provider.cpp line 41

Quoted 8 lines
qga/vss-win32-provider/provider.cpp new file +8 -0
+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"?

Quoted 27 lines
Quoted 27 lines
Quoted 27 lines
qga/vss-win32-provider/provider.cpp new file +27 -0
+{
+    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();
Quoted 8 lines
Quoted 8 lines

qga/vss-win32-provider/provider.cpp line 76

Quoted 8 lines
qga/vss-win32-provider/provider.cpp new file +8 -0
+
+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.

Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
qga/vss-win32-provider/provider.cpp new file +4 -0
+    LockModule(TRUE);
+}
+
+CQGAVSSEnumObject::~CQGAVSSEnumObject()
Quoted 8 lines
Quoted 8 lines

qga/vss-win32-provider/provider.cpp line 88

Quoted 8 lines
qga/vss-win32-provider/provider.cpp new file +8 -0
+{
+    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.

Quoted 4 lines
Quoted 4 lines

qga/vss-win32-provider/provider.cpp line 92

Quoted 4 lines
qga/vss-win32-provider/provider.cpp new file +4 -0
+        AddRef();
+        return S_OK;
+    }
+    ppObj = NULL;

Indirection operator missing ("*ppObj = NULL")?

Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
qga/vss-win32-provider/provider.cpp new file +5 -0
+    return E_NOINTERFACE;
+}
+
+STDMETHODIMP_(ULONG) CQGAVSSEnumObject::AddRef()
+{
Quoted 8 lines
Quoted 8 lines

qga/vss-win32-provider/provider.cpp line 105

Quoted 8 lines
qga/vss-win32-provider/provider.cpp new file +8 -0
+    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.

Quoted 17 lines
Quoted 17 lines
Quoted 17 lines
qga/vss-win32-provider/provider.cpp new file +17 -0
+    }
+    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)
Quoted 8 lines
Quoted 8 lines

qga/vss-win32-provider/provider.cpp line 130

Quoted 8 lines
qga/vss-win32-provider/provider.cpp new file +8 -0
+{
+    return S_OK;
+}
+
+STDMETHODIMP CQGAVSSEnumObject::Clone(IVssEnumObject **ppenum)
+{
+    return E_NOTIMPL;
+}

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

Quoted 71 lines
Quoted 71 lines
Quoted 71 lines
qga/vss-win32-provider/provider.cpp new file +71 -0
+
+
+/* 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);
+}
Quoted 8 lines
Quoted 8 lines

qga/vss-win32-provider/provider.cpp line 209

Quoted 8 lines
qga/vss-win32-provider/provider.cpp new file +8 -0
+
+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.)

Quoted 10 lines
Quoted 10 lines
Quoted 10 lines
qga/vss-win32-provider/provider.cpp new file +10 -0
+        *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) {
Quoted 8 lines
Quoted 8 lines

qga/vss-win32-provider/provider.cpp line 227

Quoted 8 lines
qga/vss-win32-provider/provider.cpp new file +8 -0
+        *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.

Quoted 31 lines
Quoted 31 lines
Quoted 31 lines
qga/vss-win32-provider/provider.cpp new file +31 -0
+
+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;
+}
+
Quoted 8 lines
Quoted 8 lines

qga/vss-win32-provider/provider.cpp line 266

Quoted 8 lines
qga/vss-win32-provider/provider.cpp new file +8 -0
+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.

Quoted 37 lines
Quoted 37 lines
Quoted 37 lines
qga/vss-win32-provider/provider.cpp new file +37 -0
+
+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)
+{
Quoted 8 lines
Quoted 8 lines

qga/vss-win32-provider/provider.cpp line 311

Quoted 8 lines
qga/vss-win32-provider/provider.cpp new file +8 -0
+    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.

Quoted 27 lines
Quoted 27 lines
Quoted 27 lines
qga/vss-win32-provider/provider.cpp new file +27 -0
+
+
+/*
+ * 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;
+    }
+
Quoted 8 lines
Quoted 8 lines

qga/vss-win32-provider/provider.cpp line 346

Quoted 8 lines
qga/vss-win32-provider/provider.cpp new file +8 -0
+    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.

Quoted 9 lines
Quoted 9 lines

qga/vss-win32-provider/provider.cpp line 355

Quoted 9 lines
qga/vss-win32-provider/provider.cpp new file +9 -0
+    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.

Quoted 27 lines
Quoted 27 lines
Quoted 27 lines
qga/vss-win32-provider/provider.cpp new file +27 -0
+
+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)
Quoted 8 lines
Quoted 8 lines

qga/vss-win32-provider/provider.cpp line 390

Quoted 8 lines
qga/vss-win32-provider/provider.cpp new file +8 -0
+{
+    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?)

Quoted 15 lines
Quoted 15 lines
Quoted 15 lines
qga/vss-win32-provider/provider.cpp new file +15 -0
+
+
+/*
+ * 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; }
Quoted 8 lines
Quoted 8 lines

qga/vss-win32-provider/provider.cpp line 413

Quoted 8 lines
qga/vss-win32-provider/provider.cpp new file +8 -0
+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.)

Quoted 10 lines
Quoted 10 lines
Quoted 10 lines
qga/vss-win32-provider/provider.cpp new file +10 -0
+        AddRef();
+        return S_OK;
+    }
+    *ppv = NULL;
+    return E_NOINTERFACE;
+}
+
+STDMETHODIMP_(ULONG) CQGAVssProviderFactory::AddRef()
+{
+    LockModule(TRUE);
Quoted 8 lines
Quoted 8 lines

qga/vss-win32-provider/provider.cpp line 431

Quoted 8 lines
qga/vss-win32-provider/provider.cpp new file +8 -0
+    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.

Quoted 11 lines
Quoted 11 lines

qga/vss-win32-provider/provider.cpp line 442

Quoted 11 lines
qga/vss-win32-provider/provider.cpp new file +11 -0
+
+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.)

Quoted 1 line
Quoted 1 line

qga/vss-win32-provider/provider.cpp line 443

Quoted 1 line
qga/vss-win32-provider/provider.cpp new file +1 -0
+    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.

Quoted 9 lines
Quoted 9 lines

qga/vss-win32-provider/provider.cpp line 452

Quoted 9 lines
qga/vss-win32-provider/provider.cpp new file +9 -0
+}
+
+
+/*
+ * DLL functions
+ */
+
+STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
+{

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

Quoted 8 lines
Quoted 8 lines

qga/vss-win32-provider/provider.cpp line 460

Quoted 8 lines
qga/vss-win32-provider/provider.cpp new file +8 -0
+    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.

Quoted 5 lines
Quoted 5 lines

qga/vss-win32-provider/provider.cpp line 465

Quoted 5 lines
qga/vss-win32-provider/provider.cpp new file +5 -0
+
+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?

Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
qga/vss-win32-provider/provider.cpp new file +7 -0
+
+EXTERN_C
+BOOL WINAPI DllMain(HINSTANCE hinstDll, DWORD dwReason, LPVOID lpReserved)
+{
+    switch (dwReason) {
+
+    case DLL_PROCESS_ATTACH:
Quoted 8 lines
Quoted 8 lines

qga/vss-win32-provider/provider.cpp line 479

Quoted 7 lines
qga/vss-win32-provider/provider.cpp new file +7 -0
+        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

#33 lore raw

Re: [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze

Quoted 11 lines · Laszlo Ersek
Quoted 9 lines · Laszlo Ersek
Quoted 8 lines · Laszlo Ersek
Quoted 7 lines · Tomoki Sekiyama
qga/vss-win32-provider/install.cpp new file +6 -0
+    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));

Quoted 1 line

(stderr)

Sure.

Quoted 7 lines
Quoted 2 lines
Quoted 2 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+            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.

Quoted 4 lines

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.

...

Quoted 40 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines

+static BOOL CreateRegistryKey(LPCTSTR key, LPCTSTR value, LPCTSTR data)

qga/vss-win32-provider/install.cpp new file +5 -0
+{
+    HKEY  hKey;
+    LONG  ret;
+    DWORD size;
+
Quoted 8 lines
Quoted 8 lines

qga/vss-win32-provider/install.cpp line 342

Quoted 8 lines
qga/vss-win32-provider/install.cpp new file +8 -0
+    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,

Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
qga/vss-win32-provider/install.cpp new file +4 -0
+    } else {
+        size = 0;
+    }
+
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
qga/vss-win32-provider/install.cpp new file +6 -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").

Quoted 3 lines

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

(Tangential anyhow.)

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

Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
qga/vss-win32-provider/install.cpp new file +3 -0
+/* Register this dll as a VSS provider */
+STDAPI DllRegisterServer(void)
+{

...

Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
qga/vss-win32-provider/install.cpp new file +9 -0
+    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;
+    }
Quoted 8 lines
Quoted 6 lines
Quoted 6 lines

qga/vss-win32-provider/install.cpp line 390

Quoted 6 lines
qga/vss-win32-provider/install.cpp new file +6 -0
+    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.

...

Quoted 1 line

(indentation)

I will fix them.
...

Quoted 5 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines

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

Quoted 8 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
qga/vss-win32-provider/install.cpp new file +3 -0
+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.

Quoted 2 lines

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.

Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
qga/vss-win32-provider/install.cpp new file +7 -0
+
+namespace _com_util
+{
+    char * __stdcall ConvertBSTRToString(BSTR bstr)
+    {
+        const unsigned int stringLength = lstrlenW(bstr);
+        char *const ascii = new char [stringLength + 1];
Quoted 8 lines
Quoted 5 lines
Quoted 5 lines

qga/vss-win32-provider/install.cpp line 482

Quoted 5 lines
qga/vss-win32-provider/install.cpp new file +5 -0
+
+        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.

Quoted 4 lines

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.

Quoted 3 lines
Quoted 1 line
Quoted 1 line
Quoted 1 line

+void LockModule(BOOL block)

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

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

Quoted 8 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines

+STDMETHODIMP CQGAVSSEnumObject::QueryInterface(REFIID riid, void **ppObj)

qga/vss-win32-provider/provider.cpp new file +3 -0
+{
+    if (riid == IID_IUnknown || riid == IID_IVssEnumObject) {
+        *ppObj =

static_cast<void*>(static_cast<IVssEnumObject*>(this));

Storing the address of the base object, right?

Yes.

Quoted 4 lines

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.

Quoted 6 lines
Quoted 4 lines
Quoted 4 lines

qga/vss-win32-provider/provider.cpp line 92

Quoted 4 lines
qga/vss-win32-provider/provider.cpp new file +4 -0
+        AddRef();
+        return S_OK;
+    }
+    ppObj = NULL;

Indirection operator missing ("*ppObj = NULL")?

Oops, I will fix this.

Quoted 7 lines
Quoted 5 lines
Quoted 5 lines

qga/vss-win32-provider/provider.cpp line 105

Quoted 5 lines
qga/vss-win32-provider/provider.cpp new file +5 -0
+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();

Quoted 11 lines
Quoted 8 lines
Quoted 8 lines

qga/vss-win32-provider/provider.cpp line 209

Quoted 8 lines

+STDMETHODIMP CQGAVssProvider::QueryInterface(REFIID riid, void **ppObj)

qga/vss-win32-provider/provider.cpp new file +6 -0
+{
+    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.

...

Quoted 8 lines
Quoted 5 lines
Quoted 5 lines

qga/vss-win32-provider/provider.cpp line 311

Quoted 5 lines
qga/vss-win32-provider/provider.cpp new file +5 -0
+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.

Quoted 1 line

Same for IsVolumeSnapshotted() above.

Right, I will add return values.

Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
qga/vss-win32-provider/provider.cpp new file +2 -0
+STDMETHODIMP CQGAVssProvider::CommitSnapshots(VSS_ID SnapshotSetId)
+{

...

Quoted 4 lines
Quoted 1 line
Quoted 1 line
Quoted 1 line

+ SetEvent(hEvent);

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

OK.

Quoted 12 lines
Quoted 9 lines
Quoted 9 lines

qga/vss-win32-provider/provider.cpp line 355

Quoted 9 lines
qga/vss-win32-provider/provider.cpp new file +9 -0
+    if (WaitForSingleObject(hEvent2, 60*1000) != WAIT_OBJECT_0) {
+        hr = E_ABORT;
+    }
+
+    CloseHandle(hEvent2);
+    CloseHandle(hEvent);
+out:
+    return hr;
+}

Seems correct in general.

Quoted 5 lines

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.

Quoted 2 lines

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

OK.

Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
qga/vss-win32-provider/provider.cpp new file +8 -0
+/*
+ * IVssProviderNotifications methods
+ */
+
+STDMETHODIMP CQGAVssProvider::OnLoad(IUnknown *pCallback)
+{
+    return S_OK;
+}
Quoted 8 lines
Quoted 5 lines
Quoted 5 lines

qga/vss-win32-provider/provider.cpp line 390

Quoted 5 lines
qga/vss-win32-provider/provider.cpp new file +5 -0
+
+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. :/

Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines

+STDMETHODIMP CQGAVssProviderFactory::QueryInterface(REFIID riid, void **ppv)

qga/vss-win32-provider/provider.cpp new file +2 -0
+{
+    if (riid == IID_IUnknown || riid == IID_IClassFactory) {
Quoted 8 lines
Quoted 1 line
Quoted 1 line
Quoted 1 line

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

Quoted 25 lines
Quoted 10 lines
Quoted 10 lines
Quoted 10 lines
qga/vss-win32-provider/provider.cpp new file +10 -0
+        AddRef();
+        return S_OK;
+    }
+    *ppv = NULL;
+    return E_NOINTERFACE;
+}
+
+STDMETHODIMP_(ULONG) CQGAVssProviderFactory::AddRef()
+{
+    LockModule(TRUE);
Quoted 8 lines
Quoted 8 lines

qga/vss-win32-provider/provider.cpp line 431

Quoted 8 lines
qga/vss-win32-provider/provider.cpp new file +8 -0
+    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.

Quoted 2 lines

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.

Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
qga/vss-win32-provider/provider.cpp new file +5 -0
+
+STDMETHODIMP CQGAVssProviderFactory::CreateInstance(
+    IUnknown *pUnknownOuter, REFIID iid, void **ppv)
+{
+    if (pUnknownOuter) {
Quoted 8 lines
Quoted 6 lines
Quoted 6 lines

qga/vss-win32-provider/provider.cpp line 442

Quoted 6 lines
qga/vss-win32-provider/provider.cpp new file +6 -0
+        return CLASS_E_NOAGGREGATION;
+    }
+    CQGAVssProvider *pObj = new CQGAVssProvider;
+    if (!pObj) {
+        return E_OUTOFMEMORY;
+    }

(We generally assume that memory allocation never fails.)

Ah, OK...

Quoted 9 lines
Quoted 1 line
Quoted 1 line
Quoted 1 line

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

Quoted 31 lines
Quoted 2 lines
Quoted 2 lines

qga/vss-win32-provider/provider.cpp line 452

Quoted 2 lines
qga/vss-win32-provider/provider.cpp new file +2 -0
+STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
+{

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

Quoted 8 lines
Quoted 8 lines

qga/vss-win32-provider/provider.cpp line 460

Quoted 8 lines
qga/vss-win32-provider/provider.cpp new file +8 -0
+    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().

Quoted 4 lines

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.

Quoted 4 lines

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.

Quoted 9 lines
Quoted 4 lines
Quoted 4 lines

qga/vss-win32-provider/provider.cpp line 465

Quoted 4 lines
qga/vss-win32-provider/provider.cpp new file +4 -0
+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)...?)

Quoted 16 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
(quoted diff) +2 -0
+EXTERN_C
+BOOL WINAPI DllMain(HINSTANCE hinstDll, DWORD dwReason, LPVOID

lpReserved)

qga/vss-win32-provider/provider.cpp new file +4 -0
+{
+    switch (dwReason) {
+
+    case DLL_PROCESS_ATTACH:
Quoted 8 lines
Quoted 8 lines

qga/vss-win32-provider/provider.cpp line 479

Quoted 7 lines
qga/vss-win32-provider/provider.cpp new file +7 -0
+        g_hinstDll = hinstDll;
+        DisableThreadLibraryCalls(hinstDll);
+        break;
+    }
+
+    return TRUE;
+}
Quoted 1 line

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

#34 lore raw

Re: [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze

Il 28/06/2013 00:25, Tomoki Sekiyama ha scritto:

Quoted 12 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
qga/vss-win32-provider/provider.cpp new file +4 -0
+STDMETHODIMP CQGAVssProviderFactory::CreateInstance(
+    IUnknown *pUnknownOuter, REFIID iid, void **ppv)
+{
+    if (pUnknownOuter) {
Quoted 8 lines
Quoted 8 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines

qga/vss-win32-provider/provider.cpp line 442

Quoted 6 lines
qga/vss-win32-provider/provider.cpp new file +6 -0
+        return CLASS_E_NOAGGREGATION;
+    }
+    CQGAVssProvider *pObj = new CQGAVssProvider;
+    if (!pObj) {
+        return E_OUTOFMEMORY;
+    }

(We generally assume that memory allocation never fails.)

Quoted 1 line

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

#35 lore raw

Re: [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze

Quoted 13 lines · Paolo Bonzini

Il 28/06/2013 00:25, Tomoki Sekiyama ha scritto:

Quoted 12 lines
Quoted 12 lines
Quoted 12 lines
Quoted 10 lines
Quoted 10 lines
Quoted 10 lines
Quoted 10 lines
Quoted 10 lines

qga/vss-win32-provider/provider.cpp line 442

Quoted 10 lines
qga/vss-win32-provider/provider.cpp new file +10 -0
+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.)

Quoted 6 lines
Quoted 1 line

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

Quoted 2 lines

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

#36 lore raw

Re: [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze

Il 28/06/2013 12:40, Laszlo Ersek ha scritto:

Quoted 6 lines
Quoted 2 lines
Quoted 2 lines

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

#37 lore raw

Re: [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze

Quoted 10 lines · Paolo Bonzini

Il 28/06/2013 12:40, Laszlo Ersek ha scritto:

Quoted 6 lines
Quoted 2 lines
Quoted 2 lines

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.

Quoted 2 lines

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.

Quoted 3 lines

Thanks,

Paolo

Thanks,
Tomoki Sekiyama

#38 lore raw

Re: [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze

In reply to Tomoki Sekiyama
Quoted 10 lines
Quoted 9 lines
Quoted 4 lines
Quoted 4 lines

qga/vss-win32-provider/provider.cpp line 465

Quoted 4 lines
qga/vss-win32-provider/provider.cpp new file +4 -0
+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?

Quoted 3 lines

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

#39 lore raw

Re: [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze

In reply to Tomoki Sekiyama

Il 06/06/2013 17:06, Tomoki Sekiyama ha scritto:

qga/vss-win32-provider.h line 24

Quoted 7 lines
qga/vss-win32-provider.h new file +7 -0
+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

#40 lore raw

Re: [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze

From: Paolo Bonzini [[email protected]] on behalf of Paolo Bonzini [[email protected]]

Quoted 13 lines

Il 06/06/2013 17:06, Tomoki Sekiyama ha scritto:

qga/vss-win32-provider.h line 24

Quoted 7 lines
qga/vss-win32-provider.h new file +7 -0
+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 byqemu-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.

Quoted 3 lines

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

#41 lore raw

Re: [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze

Il 26/06/2013 00:31, Tomoki Sekiyama ha scritto:

Quoted 18 lines

From: Paolo Bonzini [[email protected]] on behalf of Paolo Bonzini [[email protected]]

Quoted 13 lines

Il 06/06/2013 17:06, Tomoki Sekiyama ha scritto:

qga/vss-win32-provider.h line 24

Quoted 7 lines
qga/vss-win32-provider.h new file +7 -0
+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 byqemu-ga -s install, to register/unregister the DLL into/from COM+ application catalogue.

Quoted 6 lines

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

Quoted 14 lines
Quoted 3 lines

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

#42 lore raw

Re: [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze

Quoted 25 lines · Paolo Bonzini

Il 26/06/2013 00:31, Tomoki Sekiyama ha scritto:

Quoted 24 lines

From: Paolo Bonzini [[email protected]] on behalf of Paolo Bonzini [[email protected]]

Quoted 14 lines

Il 06/06/2013 17:06, Tomoki Sekiyama ha scritto:

qga/vss-win32-provider.h line 24

Quoted 7 lines
qga/vss-win32-provider.h new file +7 -0
+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 byqemu-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.

Quoted 5 lines
Quoted 2 lines

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

#43 lore raw

Re: [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze

In reply to Tomoki Sekiyama
Quoted 10 lines · Tomoki Sekiyama
Quoted 3 lines

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.

Quoted 3 lines

Thanks,
Tomoki Sekiyama

#44 lore raw

Re: [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze

Quoted 9 lines · Gal Hammer
Quoted 7 lines · Tomoki Sekiyama
Quoted 3 lines

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.

Quoted 8 lines
Quoted 5 lines

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.

Quoted 3 lines

Thanks,

Gal.

Thanks,
Tomoki Sekiyama

#45 lore raw

Re: [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze

Quoted 8 lines · Tomoki Sekiyama
Quoted 2 lines

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.

#46 lore raw

PATCH v4 7/10 qemu-ga: Add Windows VSS requester to quiesce applications and filesystems

In reply to Tomoki Sekiyama

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

---
 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
qga/Makefile.objs +2 -1
index 8d93866..f17a380 100644
@@ -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
qga/vss-win32-requester.cpp new file +419 -0

419 lines not on this page · raw message

qga/vss-win32-requester.h new file +31 -0
new file mode 100644
index 0000000..f180f56
@@ -0,0 +1,31 @@
+/*
+ * QEMU Guest Agent VSS Requester declarations
+ *
+ * Copyright Hitachi Data Systems Corp. 2013
+ *
+ * Authors:
+ *  Tomoki Sekiyama   <[email protected]>
+ *
+ * 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
#47 lore raw

Re: [Qemu-devel] [PATCH v4 07/10] qemu-ga: Add Windows VSS requester to quiesce applications and filesystems

Quoted 16 lines · Tomoki Sekiyama
qga/vss-win32-requester.h new file +10 -0
new file mode 100644
index 0000000..f180f56
@@ -0,0 +1,31 @@
+/*
+ * QEMU Guest Agent VSS Requester declarations
+ *
+ * Copyright Hitachi Data Systems Corp. 2013
+ *
+ * Authors:
+ *  Tomoki Sekiyama   <[email protected]>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.

qga/vss-win32-requester.h line 20

Quoted 10 lines
qga/vss-win32-requester.h new file +10 -0
+ */
+
+#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.)

qga/vss-win32-requester.h line 22

Quoted 2 lines
qga/vss-win32-requester.h new file +2 -0
+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.)

qga/vss-win32-requester.h line 25

Quoted 3 lines
qga/vss-win32-requester.h new file +3 -0
+
+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?

Quoted 19 lines
qga/vss-win32-requester.h new file +6 -0
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
qga/vss-win32-requester.cpp new file +4 -0
new file mode 100644
index 0000000..7784926
@@ -0,0 +1,419 @@
+/*
+ * QEMU Guest Agent win32 VSS Requester implementations
+ *
+ * Copyright Hitachi Data Systems Corp. 2013

qga/vss-win32-requester.cpp line 14

Quoted 10 lines
qga/vss-win32-requester.cpp new file +10 -0
+ *
+ * Authors:
+ *  Tomoki Sekiyama   <[email protected]>
+ *
+ * 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()?

Quoted 6 lines
qga/vss-win32-requester.cpp new file +6 -0
+extern "C" {
+#include "guest-agent-core.h"
+}
+#include "vss-win32-requester.h"
+#include "vss-win32-provider.h"
+#include "vss-win32.h"

qga/vss-win32-requester.cpp line 30

Quoted 10 lines
qga/vss-win32-requester.cpp new file +10 -0
+#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?

qga/vss-win32-requester.cpp line 35

Quoted 5 lines
qga/vss-win32-requester.cpp new file +5 -0
+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.)

qga/vss-win32-requester.cpp line 46

Quoted 11 lines
qga/vss-win32-requester.cpp new file +11 -0
+
+GCC_FMT_ATTR(1, 2)
+static void errmsg(const char *fmt, ...)
+{
+    va_list ap;
+    va_start(ap, fmt);
+    char *msg = g_strdup_vprintf(fmt, ap);
+    va_end(ap);
+    MessageBox(NULL, msg, "Error in QEMU guest agent", MB_OK | MB_ICONWARNING);
+    g_free(msg);
+}

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

Quoted 4 lines
qga/vss-win32-requester.cpp new file +4 -0
+
+static void error_set_win32(Error **errp, DWORD err,
+                            ErrorClass eclass, const char *text)
+{

qga/vss-win32-requester.cpp line 60

Quoted 10 lines
qga/vss-win32-requester.cpp new file +10 -0
+    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.

qga/vss-win32-requester.cpp line 63

Quoted 3 lines
qga/vss-win32-requester.cpp new file +3 -0
+
+    /* 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.

qga/vss-win32-requester.cpp line 68

Quoted 5 lines
qga/vss-win32-requester.cpp new file +5 -0
+    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()).

qga/vss-win32-requester.cpp line 80

Quoted 12 lines
qga/vss-win32-requester.cpp new file +12 -0
+
+#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.)

Quoted 11 lines
qga/vss-win32-requester.cpp new file +11 -0
+
+
+HRESULT WaitForAsync(IVssAsync *pAsync)
+{
+    HRESULT ret, hr;
+
+    do {
+        hr = pAsync->Wait();
+        if (FAILED(hr)) {
+            ret = hr;
+            break;

qga/vss-win32-requester.cpp line 101

Quoted 10 lines
qga/vss-win32-requester.cpp new file +10 -0
+        }
+        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.

qga/vss-win32-requester.cpp line 110

Quoted 9 lines
qga/vss-win32-requester.cpp new file +9 -0
+
+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().

Quoted 8 lines
qga/vss-win32-requester.cpp new file +6 -0
+
+    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.)

qga/vss-win32-requester.cpp line 119

Quoted 3 lines
qga/vss-win32-requester.cpp new file +3 -0
+    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 :)

qga/vss-win32-requester.cpp line 130

Quoted 11 lines
qga/vss-win32-requester.cpp new file +11 -0
+    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.)

Quoted 12 lines
qga/vss-win32-requester.cpp new file +12 -0
+
+    _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) {

qga/vss-win32-requester.cpp line 152

Quoted 10 lines
qga/vss-win32-requester.cpp new file +10 -0
+        errmsg("GetProcAddress failed");
+        hr = E_FAIL;
+        goto out;
+    }
+
+    return S_OK;
+out:
+    vss_deinit();
+    return hr;
+}

OK.

Quoted 24 lines
qga/vss-win32-requester.cpp new file +24 -0
+
+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();

qga/vss-win32-requester.cpp line 186

Quoted 10 lines
qga/vss-win32-requester.cpp new file +10 -0
+
+    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.

Quoted 9 lines
qga/vss-win32-requester.cpp new file +9 -0
+
+int vss_initialized(void)
+{
+    return hLib != NULL;
+}
+
+static void vss_add_components(Error **err)
+{
+    unsigned int cWriters, i;

qga/vss-win32-requester.cpp line 205

Quoted 10 lines
qga/vss-win32-requester.cpp new file +10 -0
+    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.

Quoted 13 lines
qga/vss-win32-requester.cpp new file +13 -0
+
+    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,

qga/vss-win32-requester.cpp line 228

Quoted 10 lines
qga/vss-win32-requester.cpp new file +10 -0
+                                         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.)

qga/vss-win32-requester.cpp line 240

Quoted 12 lines
qga/vss-win32-requester.cpp new file +12 -0
+    }
+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().

Quoted 7 lines
qga/vss-win32-requester.cpp new file +7 -0
+
+void qga_vss_fsfreeze_freeze(int *num_vols, Error **err)
+{
+    IVssAsync *pAsync;
+    HANDLE h;
+    HRESULT hr;
+    LONG ctx;

qga/vss-win32-requester.cpp line 257

Quoted 10 lines
qga/vss-win32-requester.cpp new file +10 -0
+    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!)

qga/vss-win32-requester.cpp

Quoted 2 lines
qga/vss-win32-requester.cpp new file +2 -0
+
+    assert(_CreateVssBackupComponents != NULL);

(g_assert())

qga/vss-win32-requester.cpp line 261

Quoted 2 lines
qga/vss-win32-requester.cpp new file +2 -0
+    chk(_CreateVssBackupComponents(&pVssbc));
+    chk(pVssbc->InitializeForBackup());

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

Quoted 9 lines
qga/vss-win32-requester.cpp new file +9 -0
+    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);

qga/vss-win32-requester.cpp line 280

Quoted 10 lines
qga/vss-win32-requester.cpp new file +10 -0
+    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.

Quoted 1 line

+ pAsync->Release();

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

qga/vss-win32-requester.cpp line 291

Quoted 10 lines
qga/vss-win32-requester.cpp new file +10 -0
+
+    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.

qga/vss-win32-requester.cpp line 302

Quoted 11 lines
qga/vss-win32-requester.cpp new file +11 -0
+        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())

qga/vss-win32-requester.cpp

Quoted 2 lines
qga/vss-win32-requester.cpp new file +2 -0
+                error_setg_win32(err, hr, msg);
+                goto out;

You miss FindVolumeClose() here.

qga/vss-win32-requester.cpp

Quoted 2 lines
qga/vss-win32-requester.cpp new file +2 -0
+            }
+            n++;

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

qga/vss-win32-requester.cpp line 312

Quoted 6 lines
qga/vss-win32-requester.cpp new file +6 -0
+        }
+        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.

qga/vss-win32-requester.cpp line 316

Quoted 4 lines
qga/vss-win32-requester.cpp new file +4 -0
+
+    chk(pVssbc->PrepareForBackup(&pAsync));
+    _chk(WaitForAsync(pAsync), "PrepareForBackup");
+    pAsync->Release();

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

qga/vss-win32-requester.cpp line 320

Quoted 4 lines
qga/vss-win32-requester.cpp new file +4 -0
+
+    chk(pVssbc->GatherWriterStatus(&pAsync));
+    _chk(WaitForAsync(pAsync), "GatherWriterStatus");
+    pAsync->Release();

ditto

qga/vss-win32-requester.cpp line 327

Quoted 7 lines
qga/vss-win32-requester.cpp new file +7 -0
+
+    /* 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.)

qga/vss-win32-requester.cpp line 331

Quoted 4 lines
qga/vss-win32-requester.cpp new file +4 -0
+
+    hEvent = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_FROZEN);
+    if (hEvent == INVALID_HANDLE_VALUE) {
+        error_setg_win32(err, GetLastError(), "CreateEvenet");

typo in "CreateEvenet"

qga/vss-win32-requester.cpp line 337

Quoted 6 lines
qga/vss-win32-requester.cpp new file +6 -0
+        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.

qga/vss-win32-requester.cpp line 340

Quoted 3 lines
qga/vss-win32-requester.cpp new file +3 -0
+    }
+
+    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.

qga/vss-win32-requester.cpp line 342

Quoted 2 lines
qga/vss-win32-requester.cpp new file +2 -0
+
+    /* Need to call QueryStatus several times to make VSS provider progress */

Very strange!

Quoted 1 line

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

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

Quoted 1 line

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

qga/vss-win32-requester.cpp line 349

Quoted 5 lines
qga/vss-win32-requester.cpp new file +5 -0
+        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".

qga/vss-win32-requester.cpp line 353

Quoted 4 lines
qga/vss-win32-requester.cpp new file +4 -0
+        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.

Quoted 1 line

+ }

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

qga/vss-win32-requester.cpp line 357

Quoted 3 lines
qga/vss-win32-requester.cpp new file +3 -0
+
+    *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.

Quoted 15 lines
qga/vss-win32-requester.cpp new file +15 -0
+
+out:
+    if (pVssbc) {
+        pVssbc->AbortBackup();
+    }
+    vss_cleanup();
+}
+
+
+void qga_vss_fsfreeze_thaw(int *num_vols, Error **err)
+{
+    IVssAsync *pAsync;
+
+    if (hEvent2 == INVALID_HANDLE_VALUE) {
+        /*

qga/vss-win32-requester.cpp line 382

Quoted 10 lines
qga/vss-win32-requester.cpp new file +10 -0
+         * 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.

qga/vss-win32-requester.cpp line 390

Quoted 8 lines
qga/vss-win32-requester.cpp new file +8 -0
+
+    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?

qga/vss-win32-requester.cpp line 393

Quoted 3 lines
qga/vss-win32-requester.cpp new file +3 -0
+         */
+        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).

Quoted 1 line

+ goto final;

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

qga/vss-win32-requester.cpp line 406

Quoted 12 lines
qga/vss-win32-requester.cpp new file +12 -0
+    }
+    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.

qga/vss-win32-requester.cpp line 414

Quoted 8 lines
qga/vss-win32-requester.cpp new file +8 -0
+
+final:
+    *num_vols = cFrozenVols;
+    cFrozenVols = 0;
+    goto done;
+
+out:
+    if (pVssbc) {

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

qga/vss-win32-requester.cpp line 419

Quoted 5 lines
qga/vss-win32-requester.cpp new file +5 -0
+        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

#48 lore raw

Re: [Qemu-devel] [PATCH v4 07/10] qemu-ga: Add Windows VSS requester to quiesce applications and filesystems

Quoted 4 lines · Laszlo Ersek

(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

#49 lore raw

Re: [Qemu-devel] [PATCH v4 07/10] qemu-ga: Add Windows VSS requester to quiesce applications and filesystems

In reply to Laszlo Ersek
Quoted 7 lines · Laszlo Ersek
Quoted 5 lines · Tomoki Sekiyama
qga/vss-win32-requester.h new file +0 -0
new file mode 100644
index 0000000..f180f56

...

Quoted 4 lines
Quoted 1 line

+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())) { ...'.

Quoted 9 lines

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

qga/vss-win32-requester.h line 22

Quoted 2 lines
qga/vss-win32-requester.h new file +2 -0
+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.

Quoted 4 lines

qga/vss-win32-requester.h line 25

Quoted 2 lines
qga/vss-win32-requester.h new file +2 -0
+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).

Quoted 5 lines
Quoted 5 lines
qga/vss-win32-requester.cpp new file +0 -0
new file mode 100644
index 0000000..7784926

...

Quoted 5 lines

qga/vss-win32-requester.cpp line 14

Quoted 2 lines
qga/vss-win32-requester.cpp new file +2 -0
+#include <stdio.h>
+#include <assert.h>

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

Sure.

Quoted 10 lines

qga/vss-win32-requester.cpp line 30

Quoted 2 lines
qga/vss-win32-requester.cpp new file +2 -0
+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:

Quoted 6 lines

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

Quoted 3 lines

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.

Quoted 10 lines

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.

Quoted 3 lines

qga/vss-win32-requester.cpp line 38

Quoted 3 lines
qga/vss-win32-requester.cpp new file +3 -0
+
+GCC_FMT_ATTR(1, 2)
+static void errmsg(const char *fmt, ...)

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

Quoted 4 lines
Quoted 4 lines
qga/vss-win32-requester.cpp new file +4 -0
+
+static void error_set_win32(Error **errp, DWORD err,
+                            ErrorClass eclass, const char *text)
+{

...

Quoted 9 lines

qga/vss-win32-requester.cpp line 63

Quoted 3 lines
qga/vss-win32-requester.cpp new file +3 -0
+
+    /* 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).

Quoted 11 lines

qga/vss-win32-requester.cpp line 68

Quoted 2 lines
qga/vss-win32-requester.cpp new file +2 -0
+#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":

...

Quoted 1 line

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

...

Quoted 4 lines

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.

Quoted 2 lines
Quoted 2 lines
qga/vss-win32-requester.cpp new file +2 -0
+
+#define __chk(status, text, errp, err_label)    \

...

Quoted 8 lines

qga/vss-win32-requester.cpp line 80

Quoted 2 lines
qga/vss-win32-requester.cpp new file +2 -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.)

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

Quoted 11 lines

qga/vss-win32-requester.cpp line 110

Quoted 8 lines
qga/vss-win32-requester.cpp new file +8 -0
+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().

Quoted 8 lines

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.

Quoted 4 lines

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.

Quoted 7 lines

qga/vss-win32-requester.cpp line 116

Quoted 5 lines
qga/vss-win32-requester.cpp new file +5 -0
+    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.

Quoted 3 lines

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 )

Quoted 1 line
Quoted 1 line

+static void vss_cleanup(void)

...

Quoted 1 line
Quoted 1 line

+void vss_deinit(void)

...

Quoted 6 lines

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.

Quoted 1 line
Quoted 1 line

+static void vss_add_components(Error **err)

...

Quoted 8 lines
Quoted 1 line

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

Quoted 2 lines

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

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

Quoted 4 lines

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.

Quoted 5 lines

qga/vss-win32-requester.cpp line 261

Quoted 2 lines
qga/vss-win32-requester.cpp new file +2 -0
+    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.

Quoted 9 lines

qga/vss-win32-requester.cpp line 280

Quoted 2 lines
qga/vss-win32-requester.cpp new file +2 -0
+    chk(pVssbc->GatherWriterMetadata(&pAsync));
+    _chk(WaitForAsync(pAsync), "GatherWriterMetadata");

If WaitForAsync() fails, this leaks pAsync.

Quoted 1 line

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

Quoted 5 lines

qga/vss-win32-requester.cpp line 291

Quoted 2 lines
qga/vss-win32-requester.cpp new file +2 -0
+    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.

Quoted 13 lines
Quoted 11 lines
qga/vss-win32-requester.cpp new file +11 -0
+        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",
Quoted 1 line

b);

Quoted 2 lines

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

Quoted 4 lines

qga/vss-win32-requester.cpp

Quoted 2 lines
qga/vss-win32-requester.cpp new file +2 -0
+                error_setg_win32(err, hr, msg);
+                goto out;

You miss FindVolumeClose() here.

Oops...

Quoted 8 lines

qga/vss-win32-requester.cpp

Quoted 2 lines
qga/vss-win32-requester.cpp new file +2 -0
+            }
+            n++;

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

Sure.

Quoted 4 lines

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.

Quoted 4 lines
Quoted 1 line

+ error_setg_win32(err, GetLastError(), "CreateEvenet");

typo in "CreateEvenet"

Quoted 1 line

+ error_setg_win32(err, GetLastError(), "CreateEvenet");

The typo was replicated here.

Oops, will fix it.

Quoted 8 lines
Quoted 1 line

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

Quoted 2 lines
Quoted 1 line

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

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

Sure.

Quoted 5 lines
Quoted 1 line

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

Quoted 7 lines

qga/vss-win32-requester.cpp line 353

Quoted 4 lines
qga/vss-win32-requester.cpp new file +4 -0
+        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.

Quoted 7 lines

qga/vss-win32-requester.cpp line 357

Quoted 3 lines
qga/vss-win32-requester.cpp new file +3 -0
+
+    *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. )

Quoted 11 lines
Quoted 10 lines
qga/vss-win32-requester.cpp new file +5 -0
+    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.

Quoted 2 lines

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*."

Quoted 8 lines

qga/vss-win32-requester.cpp

Quoted 2 lines
qga/vss-win32-requester.cpp new file +2 -0
+        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.

Quoted 4 lines
Quoted 1 line

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

Quoted 4 lines

qga/vss-win32-requester.cpp

Quoted 2 lines
qga/vss-win32-requester.cpp new file +2 -0
+out:
+    if (pVssbc) {

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

Right. Will remove the check.

Quoted 8 lines

qga/vss-win32-requester.cpp line 419

Quoted 5 lines
qga/vss-win32-requester.cpp new file +5 -0
+        pVssbc->AbortBackup();
+    }
+done:
+    vss_cleanup();
+}

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

...

Quoted 2 lines

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

Thank you for the code. Looks really nice.

Quoted 5 lines

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.

Quoted 7 lines

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

#50 lore raw

Re: [Qemu-devel] [PATCH v4 07/10] qemu-ga: Add Windows VSS requester to quiesce applications and filesystems

Quoted 11 lines · Tomoki Sekiyama
Quoted 7 lines · Laszlo Ersek

qga/vss-win32-requester.cpp line 30

Quoted 2 lines · Tomoki Sekiyama
qga/vss-win32-requester.cpp new file +2 -0
+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

#51 lore raw

PATCH v4 8/10 qemu-ga: call Windows VSS requester in fsfreeze command handler

In reply to Tomoki Sekiyama

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.

---
 qga/commands-win32.c |   74 ++++++++++++++++++++++++++++++++++++++++++++++----
 qga/main.c           |   33 ++++++++++++++++++++++
 2 files changed, 100 insertions(+), 7 deletions(-)
qga/commands-win32.c +68 -6
index 24e4ad0..67dca60 100644
@@ -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.
qga/main.c +32 -1
index 0e04e73..8bcedaf 100644
@@ -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
 
Reviewed-by on v4 8/10
#52 lore raw

Re: [Qemu-devel] [PATCH v4 08/10] qemu-ga: call Windows VSS requester in fsfreeze command handler

some comments below

Quoted 26 lines · Tomoki Sekiyama

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.

---
 qga/commands-win32.c |   74 ++++++++++++++++++++++++++++++++++++++++++++++----
 qga/main.c           |   33 ++++++++++++++++++++++
 2 files changed, 100 insertions(+), 7 deletions(-)
qga/commands-win32.c +1 -0
index 24e4ad0..67dca60 100644
@@ -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"
 

qga/commands-win32.c void qmp_guest_file_flush(int64_t handle, Error **err) · line 156

Quoted 5 lines
qga/commands-win32.c +2 -0
@@ -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)

Quoted 29 lines
qga/commands-win32.c +13 -5
 /*
  * 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;

qga/commands-win32.c void qmp_guest_file_flush(int64_t handle, Error **err) · line 190

Quoted 10 lines
qga/commands-win32.c +10 -0
+
+    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/commands-win32.c void qmp_guest_file_flush(int64_t handle, Error **err) · line 200

Quoted 10 lines
qga/commands-win32.c +10 -0
+
+    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.

Quoted 7 lines
qga/commands-win32.c +1 -1
     return 0;
 }
 
 /*
- * Walk list of frozen file systems in the guest, and thaw them.
+ * Thaw local file systems using Volume Shadow-copy Service.
  */

qga/commands-win32.c void qmp_guest_file_flush(int64_t handle, Error **err) · line 216

Quoted 10 lines
qga/commands-win32.c +8 -0
 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.

qga/commands-win32.c void qmp_guest_file_flush(int64_t handle, Error **err) · line 222

Quoted 6 lines
qga/commands-win32.c +6 -0
+
+    ga_unset_frozen(ga_state);
+    return i;
+}
+
+#else

(maybe mention the macro in a comment that the #else depends on here)

Quoted 29 lines
qga/commands-win32.c +17 -0
+
+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.
qga/main.c +0 -0
index 0e04e73..8bcedaf 100644

qga/main.c line 40

Quoted 7 lines
qga/main.c +4 -0
@@ -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.

Quoted 44 lines
qga/main.c +24 -1
 #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);
 }

qga/main.c int main(int argc, char **argv) · line 1211

Quoted 10 lines
qga/main.c +4 -0
@@ -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

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

#53 lore raw

Re: [Qemu-devel] [PATCH v4 08/10] qemu-ga: call Windows VSS requester in fsfreeze command handler

Quoted 7 lines · Laszlo Ersek

qga/commands-win32.c void qmp_guest_file_flush(int64_t handle, Error **err) · line 200

Quoted 2 lines
qga/commands-win32.c +2 -0
+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.

Quoted 7 lines

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);
    }
Quoted 10 lines

qga/main.c line 40

Quoted 4 lines
qga/main.c +4 -0
+#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.)

Quoted 5 lines

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

#54 lore raw

Re: [Qemu-devel] [PATCH v4 08/10] qemu-ga: call Windows VSS requester in fsfreeze command handler

Quoted 17 lines · Tomoki Sekiyama
Quoted 7 lines · Laszlo Ersek

qga/commands-win32.c void qmp_guest_file_flush(int64_t handle, Error **err) · line 200

Quoted 2 lines
qga/commands-win32.c +2 -0
+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.

Quoted 7 lines

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

Quoted 8 lines

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

#55 lore raw

Re: [Qemu-devel] [PATCH v4 08/10] qemu-ga: call Windows VSS requester in fsfreeze command handler

Quoted 27 lines · On Tue, 02 Jul 2013 08:36:11 +0200 Laszlo Ersek <[email protected]> wrote:
Quoted 17 lines · Tomoki Sekiyama
Quoted 7 lines · Laszlo Ersek

qga/commands-win32.c void qmp_guest_file_flush(int64_t handle, Error **err) · line 200

Quoted 2 lines
qga/commands-win32.c +2 -0
+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.

Quoted 7 lines

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

Quoted 8 lines

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);
    }
Quoted 7 lines

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.

#56 lore raw

Re: [Qemu-devel] [PATCH v4 08/10] qemu-ga: call Windows VSS requester in fsfreeze command handler

Quoted 34 lines · Luiz Capitulino
Quoted 31 lines · On Tue, 02 Jul 2013 08:36:11 +0200 Laszlo Ersek <[email protected]> wrote:
Quoted 17 lines · Tomoki Sekiyama
Quoted 7 lines · Laszlo Ersek

qga/commands-win32.c void qmp_guest_file_flush(int64_t handle, Error **err) · line 200

Quoted 2 lines
qga/commands-win32.c +2 -0
+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.

Quoted 7 lines

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

Quoted 8 lines

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:

Quoted 7 lines
Quoted 3 lines
    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

#57 lore raw

Re: [Qemu-devel] [PATCH v4 08/10] qemu-ga: call Windows VSS requester in fsfreeze command handler

Quoted 37 lines · On Tue, 02 Jul 2013 16:44:55 +0200 Laszlo Ersek <[email protected]> wrote:
Quoted 36 lines · Luiz Capitulino
Quoted 33 lines · On Tue, 02 Jul 2013 08:36:11 +0200 Laszlo Ersek <[email protected]> wrote:
Quoted 17 lines · Tomoki Sekiyama
Quoted 7 lines · Laszlo Ersek

qga/commands-win32.c void qmp_guest_file_flush(int64_t handle, Error **err) · line 200

Quoted 2 lines
qga/commands-win32.c +2 -0
+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.

Quoted 7 lines

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

Quoted 8 lines

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));
Quoted 8 lines
Quoted 5 lines
Quoted 1 line

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?

#58 lore raw

Re: [Qemu-devel] [PATCH v4 08/10] qemu-ga: call Windows VSS requester in fsfreeze command handler

Quoted 46 lines · Luiz Capitulino
Quoted 43 lines · On Tue, 02 Jul 2013 16:44:55 +0200 Laszlo Ersek <[email protected]> wrote:
Quoted 41 lines · Luiz Capitulino
Quoted 27 lines · On Tue, 02 Jul 2013 08:36:11 +0200 Laszlo Ersek <[email protected]> wrote:
Quoted 17 lines · Tomoki Sekiyama
Quoted 7 lines · Laszlo Ersek

Review

Quoted 2 lines
(quoted diff) +2 -0
+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.

Quoted 7 lines

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

Quoted 8 lines

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);
    }
Quoted 7 lines

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.

Quoted 5 lines
Quoted 2 lines

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

#59 lore raw

Re: [Qemu-devel] [PATCH v4 08/10] qemu-ga: call Windows VSS requester in fsfreeze command handler

In reply to Laszlo Ersek
Quoted 27 lines · Laszlo Ersek
Quoted 17 lines · Tomoki Sekiyama
Quoted 7 lines · Laszlo Ersek

qga/commands-win32.c void qmp_guest_file_flush(int64_t handle, Error **err) · line 200

Quoted 2 lines
qga/commands-win32.c +2 -0
+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.

Quoted 7 lines

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

Quoted 8 lines

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);
    }
Quoted 7 lines

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.

Quoted 4 lines

Thanks,
Laszlo

#60 lore raw

Re: [Qemu-devel] [PATCH v4 08/10] qemu-ga: call Windows VSS requester in fsfreeze command handler

In reply to Laszlo Ersek
Quoted 9 lines · Laszlo Ersek

some comments below

Quoted 5 lines · Tomoki Sekiyama

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.

Quoted 13 lines

qga/commands-win32.c void qmp_guest_file_flush(int64_t handle, Error **err) · line 216

Quoted 10 lines

int64_t qmp_guest_fsfreeze_thaw(Error **err)
{

qga/commands-win32.c +8 -0
+    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).

Quoted 5 lines

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

Signature · 2 lines
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org
#61 lore raw

PATCH v4 9/10 qemu-ga: install Windows VSS provider on `qemu-ga -s install'

In reply to Tomoki Sekiyama

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

---
 qga/main.c |    8 ++++++++
 1 file changed, 8 insertions(+)
qga/main.c +8 -0
index 8bcedaf..739b958 100644
@@ -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");
#62 lore raw

Re: [Qemu-devel] [PATCH v4 09/10] qemu-ga: install Windows VSS provider on `qemu-ga -s install'

Quoted 14 lines · Tomoki Sekiyama

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

---
 qga/main.c |    8 ++++++++
 1 file changed, 8 insertions(+)
qga/main.c +0 -0
index 8bcedaf..739b958 100644

qga/main.c int main(int argc, char **argv) · line 1064

Quoted 7 lines
qga/main.c +4 -0
@@ -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.

qga/main.c int main(int argc, char **argv) · line 1066

Quoted 2 lines
qga/main.c +1 -0
+#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).

qga/main.c int main(int argc, char **argv) · line 1073

Quoted 9 lines
qga/main.c +3 -0
             } 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

#63 lore raw

Re: [Qemu-devel] [PATCH v4 09/10] qemu-ga: install Windows VSS provider on `qemu-ga -s install'

Quoted 8 lines · Laszlo Ersek

qga/main.c int main(int argc, char **argv) · line 1064

Quoted 4 lines · Tomoki Sekiyama
qga/main.c +4 -0
+#ifdef HAS_VSS_SDK
+                if (FAILED(COMRegister())) {
+                    return EXIT_FAILURE;
+                }

COMRegister() seems to print error messages on failure, OK.

Quoted 8 lines
Quoted 3 lines

+#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

#64 lore raw

PATCH v4 10/10 QMP/qemu-ga-client: make timeout longer for guest-fsfreeze-freeze command

In reply to Tomoki Sekiyama

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

---
 QMP/qemu-ga-client |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
QMP/qemu-ga-client +3 -1
index 46676c3..b5f7e7c 100755
@@ -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 on v4 10/10
#65 lore raw

Re: [Qemu-devel] [PATCH v4 10/10] QMP/qemu-ga-client: make timeout longer for guest-fsfreeze-freeze command

Il 06/06/2013 17:07, Tomoki Sekiyama ha scritto:

Quoted 15 lines

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

---
 QMP/qemu-ga-client |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
QMP/qemu-ga-client +0 -0
index 46676c3..b5f7e7c 100755

QMP/qemu-ga-client def main(address, cmd, args): · line 275

Quoted 10 lines
QMP/qemu-ga-client +3 -1
@@ -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 on v4 10/10
#66 lore raw

Re: [Qemu-devel] [PATCH v4 10/10] QMP/qemu-ga-client: make timeout longer for guest-fsfreeze-freeze command

In reply to Tomoki Sekiyama
Quoted 16 lines · Tomoki Sekiyama

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

---
 QMP/qemu-ga-client |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
QMP/qemu-ga-client +0 -0
index 46676c3..b5f7e7c 100755

QMP/qemu-ga-client def main(address, cmd, args): · line 275

Quoted 10 lines
QMP/qemu-ga-client +3 -1
@@ -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)
#67 lore raw
In reply to Tomoki Sekiyama
Quoted 13 lines · Tomoki Sekiyama

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)

Quoted 8 lines

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

#68 lore raw
In reply to Tomoki Sekiyama

Any other comments for this series?

Quoted 115 lines · Tomoki Sekiyama

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

#69 lore raw
Quoted 1 line · Tomoki Sekiyama

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