qemu-ga: fsfreeze on Windows using VSS
Subsystems (2)
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
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
@@ -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
@@ -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
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.
Signed-off-by Tomoki Sekiyama <[email protected]>--- configure | 13 +++++++++++++ rules.mak | 9 ++++++++- 2 files changed, 21 insertions(+), 1 deletion(-)configure +13
-0@@ -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_makrules.mak +7
-0@@ -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
Re: [Qemu-devel] [PATCH v4 01/10] configure: Support configuring c++ compiler
In reply to Tomoki SekiyamaQuoted 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.
Signed-off-by Tomoki Sekiyama <[email protected]>--- configure | 13 +++++++++++++ rules.mak | 9 ++++++++- 2 files changed, 21 insertions(+), 1 deletion(-)
PATCH v4 2/10 Add c++ keywords to QAPI helper script
In reply to Tomoki SekiyamaAdd 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
@@ -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
@@ -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
@@ -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("*")
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.Signed-off-by Tomoki Sekiyama <[email protected]>--- hmp.c | 2 +- hw/pci/pci.c | 2 +- scripts/qapi.py | 12 +++++++++++- 3 files changed, 13 insertions(+), 3 deletions(-)
PATCH v4 3/10 checkpatch.pl: check .cpp files
In reply to Tomoki SekiyamaEnable 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
@@ -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+>/) ||
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())Signed-off-by Tomoki Sekiyama <[email protected]>--- scripts/checkpatch.pl | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-)
It's Perl, enough said.
PATCH v4 4/10 Add a script to extract VSS SDK headers on POSIX system
In reply to Tomoki SekiyamaVSS 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
@@ -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
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...
Re: [Qemu-devel] [PATCH v4 04/10] Add a script to extract VSS SDK headers on POSIX system
In reply to Tomoki Sekiyamascripts/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.exe433eb024ed0c669dd1563d952ca41091 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
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.
2e39646b7850a12673bc66ade85fece3setup.exe433eb024ed0c669dd1563d952ca41091vsssdk.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
0eee72d26d06d7087d5d83efbc37564f67489019Mon 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
@@ -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
@@ -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
@@ -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
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.
2e39646b7850a12673bc66ade85fece3setup.exe433eb024ed0c669dd1563d952ca41091vsssdk.msiQuoted 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
PATCH v4 5/10 qemu-ga: Add configure options to specify path to Windows/VSS SDK
In reply to Tomoki SekiyamaTo 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
@@ -79,6 +79,7 @@ fsdev/virtfs-proxy-helper.pod *.la *.pc .libs +.sdk *.swp *.orig .pc
Makefile +1 -0
@@ -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
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=23490To 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-headersand 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
Signed-off-by Tomoki Sekiyama <[email protected]>--- .gitignore | 1 + Makefile | 1 + configure | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+)
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=23490To 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-headersand 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 :|
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-headersand 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
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-headersand 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 :|
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
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
ProgramFiles/Microsoft/VSSSDK72/inc/winxp/vswriter.hProgramFiles/Microsoft/VSSSDK72/inc/winxp/vsbackup.hProgramFiles/Microsoft/VSSSDK72/inc/win2003/vdslun.hProgramFiles/Microsoft/VSSSDK72/inc/win2003/vswriter.hProgramFiles/Microsoft/VSSSDK72/inc/win2003/vsmgmt.hProgramFiles/Microsoft/VSSSDK72/inc/win2003/vscoordint.hProgram 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.hProgram Files/Microsoft/VSSSDK72/inc/win2003/vss.idl ProgramFiles/Microsoft/VSSSDK72/inc/win2003/vss.hProgram Files/Microsoft/VSSSDK72/inc/win2003/vdslun.idl ProgramFiles/Microsoft/VSSSDK72/inc/win2003/vsbackup.hProgram Files/Microsoft/VSSSDK72/inc/win2003/vsprov.idl ProgramFiles/Microsoft/VSSSDK72/inc/win2003/vsprov.hProgram 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 :|
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.hheader 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
Re: [Qemu-devel] [PATCH v4 05/10] qemu-ga: Add configure options to specify path to Windows/VSS SDK
In reply to Daniel P. BerrangeQuoted 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-headersand 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
PATCH v4 6/10 qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze
In reply to Tomoki SekiyamaImplements 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
qga/vss-win32.h new file +86 -0
86 lines not on this page · raw message
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.
Signed-off-by Tomoki Sekiyama <[email protected]>--- 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.objsconfigure +0
-0@@ -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.exePlain "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@@ -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@@ -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@@ -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@@ -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.hqga/vss-win32-provider.h new file +26
-0@@ -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@@ -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@@ -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@@ -0,0 +1,10 @@ +LIBRARY "QGA-PROVIDER.DLL" + +EXPORTS + VSSCheckOSVersion PRIVATE + COMRegister PRIVATE + COMUnregister PRIVATE + DllCanUnloadNow PRIVATE + DllGetClassObject PRIVATE + DllRegisterServer PRIVATE + DllUnregisterServer PRIVATEqga/vss-win32-provider/qga-provider.idl new file +20
-0@@ -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
-0qga/vss-win32.h new file +86
-0@@ -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
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/ +endifSo 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
Re: [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze
In reply to Laszlo ErsekThanks 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@@ -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" ] ; thenif [ "$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
fiThis 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.exePlain "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@@ -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@@ -3,3 +3,9 @@ qga-obj-$(CONFIG_POSIX) += commands-posix.ochannel-posix.o
qga-obj-$(CONFIG_WIN32) += commands-win32.o channel-win32.o
service-win32.o
qga-obj-y +=qapi-generated/qga-qapi-types.oqapi-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.oqga/Makefile.objs +3
-0+ +ifeq ($(CONFIG_QGA_VSS),y) +QEMU_CFLAGS += -DHAS_VSS_SDKIsn't this superfluous? First, I can find no other reference to
HAS_VSS_SDKin the series, second,CONFIG_QGA_VSSwould 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/ +endifSo 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@@ -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
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
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, likemake qemu-ga.exe qga/vss-win32-provider/qga-provider.{dll,tlb}.
Thanks,
Tomoki Sekiyama
Re: [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze
In reply to Tomoki SekiyamaQuoted 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_SDKIsn't this superfluous? First, I can find no other reference to
HAS_VSS_SDKin the series, second,CONFIG_QGA_VSSwould be available directly as a macro to source files.It is referenced in e.g.
qga/commands-win32.cin 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
Re: [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze
In reply to Laszlo ErsekQuoted 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@@ -0,0 +1,10 @@ +LIBRARY "QGA-PROVIDER.DLL" + +EXPORTS + VSSCheckOSVersion PRIVATE + COMRegister PRIVATE + COMUnregister PRIVATE + DllCanUnloadNow PRIVATE + DllGetClassObject PRIVATE + DllRegisterServer PRIVATE + DllUnregisterServer PRIVATEqga/vss-win32-provider.h new file +18
-0@@ -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@@ -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@@ -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 by
qemu-ga -s install, to register/unregister the DLL into/from COM+ application catalogue.
and indeed I can see a COMRegister() call in 09/10. Nonetheless that doesn't seem to justify this call to COMUnregister() -- if the DLL has been previously registered in the application catalog, a new registration attempt should fail.
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@@ -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; +}
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
Re: [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze
In reply to Laszlo ErsekQuoted 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 listingDllCanUnloadNow()andDllGetClassObject()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()inchk(), 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_tto 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_FAILhere.
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 ofCOMRegister(), 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 by
qemu-ga -s install, to register/unregister the DLL into/from COM+ application catalogue.and indeed I can see a
COMRegister()call in 09/10. Nonetheless that doesn't seem to justify this call toCOMUnregister()-- 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, doesCoUninitialize()respect that)? Consider the following call chain:
COMRegister()
CoInitialize()
COMUnregister()-- on the "out:" path
DllUnregisterServer()
CoInitialize()-- init after initCoUninitialize()
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, sinceCOMUnregister()is also used as destructor after partial construction, so we must not bail here.) I propose to change the return type ofDllUnregisterServer()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
Re: [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze
In reply to Laszlo ErsekQuoted 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@@ -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
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 callpObj->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 anerror. */
+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()withstrlen()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_tin 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_IUnknowntoo, 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_OKcould 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. whateverInterlockedIncrement()orInterlockedDecrement()returns inLockModule()), 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 callRelease()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 visibleAddRef()andRelease()return values are broken in any case. Somehow the existence ofAddRef()andRelease()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 massageLockModule().
Quoted 4 lines
In this aspect CQGAVssProviderFactory would work exactly like the CQGAVssProvider class, and
DllGetClassObject()-- the factory factory method -- would work likeCQGAVssProviderFactory::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, LPVOIDlpReserved)
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
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
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
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
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
Re: [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze
In reply to Tomoki SekiyamaQuoted 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
Re: [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze
In reply to Tomoki SekiyamaIl 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
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
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 by
qemu-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).aspxIt is checking the OS version because this patchset only supports Windows 2003 or later.
Thanks,
Tomoki Sekiyama
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 by
qemu-ga -s install, to register/unregister the DLL into/from COM+ application catalogue.DllRegisterServer is automatically called inside
pCatalog->InstallComponent()(like regsvr32 does), and register this DLL as a VSS provider. DllUnregisterServer will do the oposite.
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
Re: [Qemu-devel] [PATCH v4 06/10] qemu-ga: Add Windows VSS provider to quiesce applications on fsfreeze
In reply to Tomoki SekiyamaQuoted 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).aspxIt 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
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)
.aspxIt 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
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.
PATCH v4 7/10 qemu-ga: Add Windows VSS requester to quiesce applications and filesystems
In reply to Tomoki SekiyamaAdd 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
@@ -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
@@ -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
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@@ -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@@ -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
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 imitateerror_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
Re: [Qemu-devel] [PATCH v4 07/10] qemu-ga: Add Windows VSS requester to quiesce applications and filesystems
In reply to Laszlo ErsekQuoted 7 lines · Laszlo Ersek
Quoted 5 lines · Tomoki Sekiyama
qga/vss-win32-requester.h new file +0
-0
...
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
...
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 withg_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_WIN32block). It would imitateerror_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 imitateerror_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 inqga_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 invss_init()get reported). In "install.cpp" too you reportVSSCheckOSVersion()failures withprintf().
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 callvss_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 inqga_vss_fsfreeze_freeze()terminates with aQueryStatus()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. (Eliminatingchk()& 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 bypVssbc->AbortBackup()orpVssbc->Release()(invss_cleanup()) on the error path inqga_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 whenInitializeForBackup()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 twoCreateEvent()calls vs.DoSnapshotSet()is correct, theOpenEvent()calls inCQGAVssProvider::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_FAILEDwould 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 commitis not
+ * skipped and VSS is aborted byVSS_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 forVSS_E_OBJECT_NOT_FOUND, becauseVSS_E_OBJECT_NOT_FOUND*really* is synonymous with "SUCCESS", then simply makeVSS_E_OBJECT_NOT_FOUNDfall through to "SUCCESS" after the comment about Win < 2008 SP2.
Quoted 7 lines
I do think we should call either
AbortBackup()orBackupComplete()forVSS_E_OBJECT_NOT_FOUND.No more comments for this patch.
Thanks,
Laszlo
Thanks a lot!
Tomoki Sekiyama
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
PATCH v4 8/10 qemu-ga: call Windows VSS requester in fsfreeze command handler
In reply to Tomoki SekiyamaSupport 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
@@ -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
@@ -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
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.
Signed-off-by Tomoki Sekiyama <[email protected]>--- qga/commands-win32.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++---- qga/main.c | 33 ++++++++++++++++++++++ 2 files changed, 100 insertions(+), 7 deletions(-)qga/commands-win32.c +1
-0@@ -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
-1return 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
-0int64_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
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
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 allerror_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; seeguest_fsfreeze_cleanup()andga_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
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 allerror_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
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 allerror_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.
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 allerror_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
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 allerror_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?
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 allerror_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
Re: [Qemu-devel] [PATCH v4 08/10] qemu-ga: call Windows VSS requester in fsfreeze command handler
In reply to Laszlo ErsekQuoted 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 allerror_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
Re: [Qemu-devel] [PATCH v4 08/10] qemu-ga: call Windows VSS requester in fsfreeze command handler
In reply to Laszlo ErsekQuoted 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_tqmp_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; seeguest_fsfreeze_cleanup()andga_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
PATCH v4 9/10 qemu-ga: install Windows VSS provider on `qemu-ga -s install'
In reply to Tomoki SekiyamaRegister 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
@@ -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" );
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).
Signed-off-by Tomoki Sekiyama <[email protected]>--- qga/main.c | 8 ++++++++ 1 file changed, 8 insertions(+)qga/main.c +0
-0
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
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()ifga_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
PATCH v4 10/10 QMP/qemu-ga-client: make timeout longer for guest-fsfreeze-freeze command
In reply to Tomoki Sekiyamaguest-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
@@ -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)
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).
Signed-off-by Tomoki Sekiyama <[email protected]>--- QMP/qemu-ga-client | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)QMP/qemu-ga-client +0
-0
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)
Re: [Qemu-devel] [PATCH v4 10/10] QMP/qemu-ga-client: make timeout longer for guest-fsfreeze-freeze command
In reply to Tomoki SekiyamaQuoted 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).
Signed-off-by Tomoki Sekiyama <[email protected]>--- QMP/qemu-ga-client | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)QMP/qemu-ga-client +0
-0
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)
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 inqapi.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 Baiqingchanges from v1:
- Fix out-tree build by stop using recursiveMakefile
- 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
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 inqapi.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 Baiqingchanges from v1:
- Fix out-tree build by stop using recursiveMakefile
- Added script to extract VSS SDK headers on POSIX systems using msitools (thanks Paolo)
- Remove some unnecessary header filesv3: 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-headerssetup.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' andnet start qemu-ga' in the guestsAny 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.hcreate 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 100644qga/vss-win32-requester.hcreate mode 100644qga/vss-win32.h
create mode 100755scripts/extract-vsssdk-headers
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