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