--- open-vm-tools-10.0.5-3227872/lib/include/vmware/guestrpc/vmbackup.h 2015-11-23 23:07:44.000000000 -0800 +++ open-vm-tools-10.0.5-3227872/lib/include/vmware/guestrpc/vmbackup.h 2017-01-11 02:08:21.000000000 -0800 @@ -1,5 +1,5 @@ /********************************************************* - * Copyright (C) 2007-2015 VMware, Inc. All rights reserved. + * Copyright (C) 2007-2016 VMware, Inc. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published @@ -54,6 +54,8 @@ #define VMBACKUP_PROTOCOL_ABORT VMBACKUP_PROTOCOL_PREFIX"abort" #define VMBACKUP_PROTOCOL_SNAPSHOT_DONE VMBACKUP_PROTOCOL_PREFIX"snapshotDone" #define VMBACKUP_PROTOCOL_EVENT_SET VMBACKUP_PROTOCOL_PREFIX"eventSet" +#define VMBACKUP_PROTOCOL_SNAPSHOT_COMPLETED \ + VMBACKUP_PROTOCOL_PREFIX"snapshotCompleted" /* These are responses to messages sent to the guest. */ #define VMBACKUP_PROTOCOL_ERROR "protocol.error" --- open-vm-tools-10.0.5-3227872/services/plugins/vmbackup/guestQuiesce.x 2015-11-23 23:07:44.000000000 -0800 +++ open-vm-tools-10.0.5-3227872/services/plugins/vmbackup/guestQuiesce.x 2017-01-11 02:08:21.000000000 -0800 @@ -1,5 +1,5 @@ /********************************************************* - * Copyright (C) 2010-2015 VMware, Inc. All rights reserved. + * Copyright (C) 2010-2016 VMware, Inc. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published @@ -24,12 +24,14 @@ */ enum GuestQuiesceParamsVersion { - GUESTQUIESCEPARAMS_V1 = 1 + GUESTQUIESCEPARAMS_V1 = 1, + GUESTQUIESCEPARAMS_V2 = 2 }; const GUESTQUIESCE_SCRIPTARG_MAX_LEN = 256; const GUESTQUIESCE_DISKUUID_MAX_LEN = 3200; /* (UUID_MAXLEN + 1) * 64 disks */ + /* Guest Quiescing parameters. */ struct GuestQuiesceParamsV1 { Bool createManifest; /* Create manifest describing the operations */ @@ -42,7 +44,19 @@ string diskUuids; /* disk Uuids */ }; +/* Guest Quiescing parameters V2. */ +struct GuestQuiesceParamsV2 { + struct GuestQuiesceParamsV1 paramsV1; + uint32 vssBackupContext; + uint32 vssBackupType; + Bool vssBootableSystemState; + Bool vssPartialFileSupport; +}; + + union GuestQuiesceParams switch (GuestQuiesceParamsVersion ver) { case GUESTQUIESCEPARAMS_V1: struct GuestQuiesceParamsV1 *guestQuiesceParamsV1; +case GUESTQUIESCEPARAMS_V2: + struct GuestQuiesceParamsV2 *guestQuiesceParamsV2; }; --- open-vm-tools-10.0.5-3227872/lib/include/vmware/tools/utils.h 2015-11-23 23:07:44.000000000 -0800 +++ open-vm-tools-10.0.5-3227872/lib/include/vmware/tools/utils.h 2017-01-11 02:07:50.000000000 -0800 @@ -1,5 +1,5 @@ /********************************************************* - * Copyright (C) 2008-2015 VMware, Inc. All rights reserved. + * Copyright (C) 2008-2016 VMware, Inc. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published @@ -121,6 +121,24 @@ const gchar *domain, GKeyFile *conf); +gboolean +VMTools_ConfigGetBoolean(GKeyFile *config, + const gchar *section, + const gchar *key, + gboolean defValue); + +gint +VMTools_ConfigGetInteger(GKeyFile *config, + const gchar *section, + const gchar *key, + gint defValue); + +gchar * +VMTools_ConfigGetString(GKeyFile *config, + const gchar *section, + const gchar *key, + gchar *defValue); + #if defined(G_PLATFORM_WIN32) gboolean --- open-vm-tools-10.0.5-3227872/libvmtools/vmtoolsConfig.c 2015-11-23 23:07:44.000000000 -0800 +++ open-vm-tools-10.0.5-3227872/libvmtools/vmtoolsConfig.c 2017-01-11 02:07:50.000000000 -0800 @@ -1,5 +1,5 @@ /********************************************************* - * Copyright (C) 2008-2015 VMware, Inc. All rights reserved. + * Copyright (C) 2008-2016 VMware, Inc. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published @@ -329,7 +329,12 @@ gchar *backup = NULL; gchar *defaultPath = NULL; gchar *localPath = NULL; + /* GStatBuf was added in 2.26. */ +#if GLIB_CHECK_VERSION(2, 26, 0) + GStatBuf confStat; +#else struct stat confStat; +#endif GHashTable *old = NULL; GError *err = NULL; GKeyFile *cfg = NULL; @@ -527,3 +532,122 @@ return ret; } + +/** + * Loads boolean value for a key from the specified config section. + * + * @param[in] config Config file to read the key from. + * @param[in] section Section to look for in the config file. + * @param[in] defValue Default value if the key is not found or error. + * + * @return value of the key if value was read successfully, else defValue. + */ + +gboolean +VMTools_ConfigGetBoolean(GKeyFile *config, + const gchar *section, + const gchar *key, + gboolean defValue) +{ + GError *err = NULL; + gboolean value; + + if (config == NULL || section == NULL || key == NULL) { + g_debug("%s: Returning default value for '[%s] %s'=%s.\n", + __FUNCTION__, section ? section : "(null)", + key ? key : "(null)", defValue ? "TRUE" : "FALSE"); + return defValue; + } + + value = g_key_file_get_boolean(config, section, key, &err); + if (err != NULL) { + if (err->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND && + err->code != G_KEY_FILE_ERROR_GROUP_NOT_FOUND) { + g_warning("%s: Failed to get value for '[%s] %s': %s (err=%d).\n", + __FUNCTION__, section, key, err->message, err->code); + } + g_debug("%s: Returning default value for '[%s] %s'=%s.\n", + __FUNCTION__, section, key, defValue ? "TRUE" : "FALSE"); + value = defValue; + g_clear_error(&err); + } + return value; +} + + +/** + * Loads integer value for a key from the specified config section. + * + * @param[in] config Config file to read the key from. + * @param[in] section Section to look for in the config file. + * @param[in] defValue Default value if the key is not found or error. + * + * @return value of the key if value was read successfully, else defValue. + */ + +gint +VMTools_ConfigGetInteger(GKeyFile *config, + const gchar *section, + const gchar *key, + gint defValue) +{ + GError *err = NULL; + gint value; + + ASSERT(config); + ASSERT(key); + ASSERT(section); + + value = g_key_file_get_integer(config, section, key, &err); + if (err != NULL) { + if (err->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND && + err->code != G_KEY_FILE_ERROR_GROUP_NOT_FOUND) { + g_warning("%s: Failed to get value for '[%s] %s': %s (err=%d).\n", + __FUNCTION__, section, key, err->message, err->code); + } + g_debug("%s: Returning default value for '[%s] %s'=%d.\n", + __FUNCTION__, section, key, defValue); + value = defValue; + g_clear_error(&err); + } + return value; +} + + +/** + * Loads string value for a key from the specified config section. + * + * @param[in] config Config file to read the key from. + * @param[in] section Section to look for in the config file. + * @param[in] defValue Default value if the key is not found or error. + * + * @return value of the key if value was read successfully, else defValue. + */ + +gchar * +VMTools_ConfigGetString(GKeyFile *config, + const gchar *section, + const gchar *key, + gchar *defValue) +{ + GError *err = NULL; + gchar *value; + + ASSERT(config); + ASSERT(key); + ASSERT(section); + + value = g_key_file_get_string(config, section, key, &err); + if (err != NULL) { + if (err->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND && + err->code != G_KEY_FILE_ERROR_GROUP_NOT_FOUND) { + g_warning("%s: Failed to get value for '[%s] %s': %s (err=%d).\n", + __FUNCTION__, section, key, err->message, err->code); + } + g_debug("%s: Returning default value for '[%s] %s'=%s.\n", + __FUNCTION__, section, key, defValue ? defValue : "(null)"); + value = defValue; + g_clear_error(&err); + } + return value; +} --- open-vm-tools-10.0.5-3227872/lib/syncDriver/Makefile.am 2015-11-23 23:07:44.000000000 -0800 +++ open-vm-tools-10.0.5-3227872/lib/syncDriver/Makefile.am 2017-01-11 02:07:53.000000000 -0800 @@ -1,5 +1,5 @@ ################################################################################ -### Copyright (C) 2007-2015 VMware, Inc. All rights reserved. +### Copyright (C) 2007-2016 VMware, Inc. All rights reserved. ### ### This program is free software; you can redistribute it and/or modify ### it under the terms of version 2 of the GNU General Public License as @@ -17,6 +17,9 @@ noinst_LTLIBRARIES = libSyncDriver.la +libSyncDriver_la_CPPFLAGS = +libSyncDriver_la_CPPFLAGS += @GLIB2_CPPFLAGS@ + libSyncDriver_la_SOURCES = libSyncDriver_la_SOURCES += syncDriverPosix.c --- open-vm-tools-10.0.5-3227872/lib/syncDriver/nullDriver.c 2015-11-23 23:07:44.000000000 -0800 +++ open-vm-tools-10.0.5-3227872/lib/syncDriver/nullDriver.c 2017-01-11 02:08:10.000000000 -0800 @@ -1,5 +1,5 @@ /********************************************************* - * Copyright (C) 2011-2015 VMware, Inc. All rights reserved. + * Copyright (C) 2011-2016 VMware, Inc. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published @@ -63,7 +63,7 @@ */ SyncDriverErr -NullDriver_Freeze(const char *paths, +NullDriver_Freeze(const GSList *paths, SyncDriverHandle *handle) { /* --- open-vm-tools-10.0.5-3227872/lib/syncDriver/syncDriverInt.h 2015-11-23 23:07:44.000000000 -0800 +++ open-vm-tools-10.0.5-3227872/lib/syncDriver/syncDriverInt.h 2017-01-11 02:08:10.000000000 -0800 @@ -1,5 +1,5 @@ /********************************************************* - * Copyright (C) 2011-2015 VMware, Inc. All rights reserved. + * Copyright (C) 2011-2016 VMware, Inc. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published @@ -25,19 +25,22 @@ * Internal definitions for the sync driver library. */ +#include #include "syncDriver.h" #define LGPFX "SyncDriver: " #if !defined(Win32) +#define SYNCDRIVER_PATH_SEPARATOR ':' + typedef enum { SD_SUCCESS, SD_ERROR, SD_UNAVAILABLE, } SyncDriverErr; -typedef SyncDriverErr (*SyncFreezeFn)(const char *paths, +typedef SyncDriverErr (*SyncFreezeFn)(const GSList *paths, SyncDriverHandle *handle); typedef struct SyncHandle { @@ -47,15 +50,15 @@ #if defined(linux) SyncDriverErr -LinuxDriver_Freeze(const char *userPaths, +LinuxDriver_Freeze(const GSList *userPaths, SyncDriverHandle *handle); SyncDriverErr -VmSync_Freeze(const char *userPaths, +VmSync_Freeze(const GSList *userPaths, SyncDriverHandle *handle); SyncDriverErr -NullDriver_Freeze(const char *userPaths, +NullDriver_Freeze(const GSList *userPaths, SyncDriverHandle *handle); #endif --- open-vm-tools-10.0.5-3227872/lib/syncDriver/syncDriverLinux.c 2015-11-23 23:07:44.000000000 -0800 +++ open-vm-tools-10.0.5-3227872/lib/syncDriver/syncDriverLinux.c 2017-01-11 02:08:10.000000000 -0800 @@ -1,5 +1,5 @@ /********************************************************* - * Copyright (C) 2011-2015 VMware, Inc. All rights reserved. + * Copyright (C) 2011-2017 VMware, Inc. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published @@ -32,7 +32,6 @@ #include #include "debug.h" #include "dynbuf.h" -#include "strutil.h" #include "syncDriverInt.h" /* Out toolchain headers are somewhat outdated and don't define these. */ @@ -70,8 +69,13 @@ LinuxDriver *sync = (LinuxDriver *) handle; SyncDriverErr err = SD_SUCCESS; - for (i = 0; i < sync->fdCnt; i++) { - if (ioctl(sync->fds[i], FITHAW) == -1) { + /* + * Thaw in the reverse order of freeze + */ + for (i = sync->fdCnt; i > 0; i--) { + Debug(LGPFX "Thawing fd=%d.\n", sync->fds[i-1]); + if (ioctl(sync->fds[i-1], FITHAW) == -1) { + Debug(LGPFX "Thaw failed for fd=%d.\n", sync->fds[i-1]); err = SD_ERROR; } } @@ -98,8 +102,12 @@ LinuxDriver *sync = (LinuxDriver *) handle; size_t i; - for (i = 0; i < sync->fdCnt; i++) { - close(sync->fds[i]); + /* + * Close in the reverse order of open + */ + for (i = sync->fdCnt; i > 0; i--) { + Debug(LGPFX "Closing fd=%d.\n", sync->fds[i-1]); + close(sync->fds[i-1]); } free(sync->fds); free(sync); @@ -120,7 +128,7 @@ * slow when guest is performing significant IO. Therefore, caller should * consider running this function in a separate thread. * - * @param[in] paths Paths to freeze (colon-separated). + * @param[in] paths List of paths to freeze. * @param[out] handle Handle to use for thawing. * * @return A SyncDriverErr. @@ -129,13 +137,10 @@ */ SyncDriverErr -LinuxDriver_Freeze(const char *paths, +LinuxDriver_Freeze(const GSList *paths, SyncDriverHandle *handle) { - char *path; - int fd; - size_t count = 0; - unsigned int index = 0; + ssize_t count = 0; Bool first = TRUE; DynBuf fds; LinuxDriver *sync = NULL; @@ -143,7 +148,7 @@ DynBuf_Init(&fds); - Debug(LGPFX "Freezing %s using Linux ioctls...\n", paths); + Debug(LGPFX "Freezing using Linux ioctls...\n"); sync = calloc(1, sizeof *sync); if (sync == NULL) { @@ -154,12 +159,20 @@ sync->driver.close = LinuxFiClose; /* + * Ensure we did not get an empty list + */ + VERIFY(paths != NULL); + + /* * Iterate through the requested paths. If we get an error for the first * path, and it's not EPERM, assume that the ioctls are not available in * the current kernel. */ - while ((path = StrUtil_GetNextToken(&index, paths, ":")) != NULL) { + while (paths != NULL) { + int fd; + const char *path = paths->data; Debug(LGPFX "opening path '%s'.\n", path); + paths = g_slist_next(paths); fd = open(path, O_RDONLY); if (fd == -1) { switch (errno) { @@ -169,7 +182,6 @@ * as users with permission 700, so just ignore these. */ Debug(LGPFX "cannot access mounted directory '%s'.\n", path); - free(path); continue; case EIO: @@ -179,19 +191,17 @@ * this should be enough. Just skip. */ Debug(LGPFX "I/O error reading directory '%s'.\n", path); - free(path); continue; default: Debug(LGPFX "failed to open '%s': %d (%s)\n", path, errno, strerror(errno)); err = SD_ERROR; - free(path); goto exit; } } - Debug(LGPFX "freezing path '%s'.\n", path); + Debug(LGPFX "freezing path '%s' (fd=%d).\n", path, fd); if (ioctl(fd, FIFREEZE) == -1) { int ioctlerr = errno; /* @@ -211,17 +221,15 @@ Debug(LGPFX "failed to freeze '%s': %d (%s)\n", path, ioctlerr, strerror(ioctlerr)); err = first && ioctlerr == ENOTTY ? SD_UNAVAILABLE : SD_ERROR; - free(path); break; } } else { - Debug(LGPFX "successfully froze '%s'.\n", path); + Debug(LGPFX "successfully froze '%s' (fd=%d).\n", path, fd); if (!DynBuf_Append(&fds, &fd, sizeof fd)) { if (ioctl(fd, FITHAW) == -1) { Warning(LGPFX "failed to thaw '%s': %d (%s)\n", path, errno, strerror(errno)); } - free(path); close(fd); err = SD_ERROR; break; @@ -229,7 +237,6 @@ count++; } - free(path); first = FALSE; } --- open-vm-tools-10.0.5-3227872/lib/syncDriver/syncDriverPosix.c 2015-11-23 23:07:44.000000000 -0800 +++ open-vm-tools-10.0.5-3227872/lib/syncDriver/syncDriverPosix.c 2017-01-11 02:08:10.000000000 -0800 @@ -1,5 +1,5 @@ /********************************************************* - * Copyright (C) 2005-2015 VMware, Inc. All rights reserved. + * Copyright (C) 2005-2016 VMware, Inc. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published @@ -25,9 +25,9 @@ #include #include #include +#include #include "vmware.h" #include "debug.h" -#include "dynbuf.h" #include "str.h" #include "syncDriverInt.h" #include "util.h" @@ -85,19 +85,21 @@ /* *----------------------------------------------------------------------------- * - * SyncDriverListMounts -- + * SyncDriverLocalMounts -- * - * Returns a newly allocated string containing a list of colon-separated - * mount paths in the system. No filtering is done, so all paths are added. - * This assumes that the driver allows "unfreezable" paths to be provided - * to the freeze command. + * Returns a singly-linked list of all local disk paths mounted in the + * system filtering out remote file systems. There is no filtering for + * other mount points because we assume that the underlying driver and + * IOCTL can deal with "unfreezable" paths. The returned list of paths + * is in the reverse order of the paths returned by GETNEXT_MNTINFO. + * Caller must free each path and the list itself. * * XXX: mntinfo.h mentions Solaris and Linux, but not FreeBSD. If we ever * have a FreeBSD sync driver, we should make sure this function also * works there. * * Results: - * The list of devices to freeze, or NULL on failure. + * GSList* on success, NULL on failure. * * Side effects: * None @@ -105,21 +107,20 @@ *----------------------------------------------------------------------------- */ -static char * -SyncDriverListMounts(void) +static GSList * +SyncDriverLocalMounts(void) { - char *paths = NULL; - DynBuf buf; + GSList *paths = NULL; MNTHANDLE mounts; DECLARE_MNTINFO(mntinfo); if ((mounts = OPEN_MNTFILE("r")) == NULL) { + Warning(LGPFX "Failed to open mount point table.\n"); return NULL; } - DynBuf_Init(&buf); - while (GETNEXT_MNTINFO(mounts, mntinfo)) { + char *path; /* * Skip remote mounts because they are not freezable and opening them * could lead to hangs. See PR 1196785. @@ -130,29 +131,21 @@ continue; } + path = Util_SafeStrdup(MNTINFO_MNTPT(mntinfo)); + /* - * Add a separator if it's not the first path, and add the path to the - * tail of the list. + * A mount point could depend on existence of a previous mount + * point like a loopback. In order to avoid deadlock/hang in + * freeze operation, a mount point needs to be frozen before + * its dependency is frozen. + * Typically, mount points are listed in the order they are + * mounted by the system i.e. dependent comes after the + * dependency. So, we need to keep them in reverse order of + * mount points to achieve the dependency order. */ - if ((DynBuf_GetSize(&buf) != 0 && !DynBuf_Append(&buf, ":", 1)) - || !DynBuf_Append(&buf, - MNTINFO_MNTPT(mntinfo), - strlen(MNTINFO_MNTPT(mntinfo)))) { - goto exit; - } - } - - if (!DynBuf_Append(&buf, "\0", 1)) { - goto exit; + paths = g_slist_prepend(paths, path); } - paths = DynBuf_AllocGet(&buf); - if (paths == NULL) { - Debug(LGPFX "Failed to allocate path list.\n"); - } - -exit: - DynBuf_Destroy(&buf); (void) CLOSE_MNTFILE(mounts); return paths; } @@ -185,6 +178,29 @@ /* *----------------------------------------------------------------------------- * + * SyncDriverFreePath -- + * + * A GFunc for freeing path strings. It is intended for g_slist_foreach. + * + * Results: + * None + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +static void +SyncDriverFreePath(gpointer data, gpointer userData) +{ + free(data); +} + + +/* + *----------------------------------------------------------------------------- + * * SyncDriver_Freeze -- * * Freeze I/O on the indicated drives. "all" means all drives. @@ -214,14 +230,11 @@ Bool enableNullDriver, // IN SyncDriverHandle *handle) // OUT { - char *paths = NULL; + GSList *paths = NULL; SyncDriverErr err = SD_UNAVAILABLE; size_t i = 0; /* - * First, convert the given path list to something the backends will - * understand: a colon-separated list of paths. - * * NOTE: Ignore disk UUIDs. We ignore the userPaths if it does * not start with '/' because all paths are absolute and it is * possible only when we get diskUUID as userPaths. So, all @@ -229,26 +242,42 @@ */ if (userPaths == NULL || Str_Strncmp(userPaths, "all", sizeof "all") == 0 || - *userPaths != '/') { - paths = SyncDriverListMounts(); - if (paths == NULL) { - Debug(LGPFX "Failed to list mount points.\n"); - return SD_ERROR; - } + userPaths[0] != '/') { + paths = SyncDriverLocalMounts(); } else { /* - * The sync driver API specifies spaces as separators, but the driver - * uses colons as the path separator on Unix. + * The sync driver API specifies spaces as separators. */ - char *c; - paths = Util_SafeStrdup(userPaths); - for (c = paths; *c != '\0'; c++) { - if (*c == ' ') { - *c = ':'; + while (*userPaths != '\0') { + const char *c; + char *path; + + if (*userPaths == ' ') { + /* + * Trim spaces from beginning + */ + userPaths++; + continue; + } + + c = strchr(userPaths, ' '); + if (c == NULL) { + path = Util_SafeStrdup(userPaths); + paths = g_slist_append(paths, path); + break; + } else { + path = Util_SafeStrndup(userPaths, c - userPaths); + paths = g_slist_append(paths, path); + userPaths = c; } } } + if (paths == NULL) { + Warning(LGPFX "No paths to freeze.\n"); + return SD_ERROR; + } + while (err == SD_UNAVAILABLE && i < ARRAYSIZE(gBackends)) { SyncFreezeFn freezeFn = gBackends[i]; Debug(LGPFX "Calling backend %d.\n", (int) i); @@ -262,7 +291,12 @@ err = freezeFn(paths, handle); } - free(paths); + /* + * g_slist_free_full requires glib >= v2.28 + */ + g_slist_foreach(paths, SyncDriverFreePath, NULL); + g_slist_free(paths); + return err == SD_SUCCESS; } --- open-vm-tools-10.0.5-3227872/lib/syncDriver/vmSyncDriver.c 2015-11-23 23:07:44.000000000 -0800 +++ open-vm-tools-10.0.5-3227872/lib/syncDriver/vmSyncDriver.c 2017-01-11 02:08:10.000000000 -0800 @@ -1,5 +1,5 @@ /********************************************************* - * Copyright (C) 2011-2015 VMware, Inc. All rights reserved. + * Copyright (C) 2011-2016 VMware, Inc. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published @@ -24,9 +24,11 @@ #include #include +#include #include "debug.h" #include "syncDriverInt.h" #include "syncDriverIoc.h" +#include "strutil.h" #include "util.h" #define SYNC_PROC_PATH "/proc/driver/vmware-sync" @@ -89,7 +91,7 @@ * Opens a description to the driver's proc node, and if successful, send an * ioctl to freeze the requested filesystems. * - * @param[in] paths Paths to freeze (colon-delimited). + * @param[in] paths List of paths to freeze. * @param[out] handle Where to store the handle to use for thawing. * * @return A SyncDriverErr. @@ -98,19 +100,41 @@ */ SyncDriverErr -VmSync_Freeze(const char *paths, +VmSync_Freeze(const GSList *paths, SyncDriverHandle *handle) { int file; + Bool first = TRUE; + char *allPaths = NULL; VmSyncDriver *sync = NULL; - Debug(LGPFX "Freezing %s using vmsync driver...\n", paths); + Debug(LGPFX "Freezing using vmsync driver...\n"); file = open(SYNC_PROC_PATH, O_RDONLY); if (file == -1) { return SD_UNAVAILABLE; } + /* + * Ensure we did not get an empty list + */ + VERIFY(paths != NULL); + + /* + * Concatenate all paths in the list into one string + */ + while (paths != NULL) { + if (!first) { + /* + * Append the separator (':') + */ + StrUtil_SafeStrcat(&allPaths, ":"); + } + StrUtil_SafeStrcat(&allPaths, paths->data); + first = FALSE; + paths = g_slist_next(paths); + } + sync = calloc(1, sizeof *sync); if (sync == NULL) { goto exit; @@ -120,7 +144,9 @@ sync->driver.close = VmSyncClose; sync->fd = file; - if (ioctl(file, SYNC_IOC_FREEZE, paths) == -1) { + Debug(LGPFX "Freezing %s using vmsync driver...\n", allPaths); + + if (ioctl(file, SYNC_IOC_FREEZE, allPaths) == -1) { free(sync); sync = NULL; } @@ -133,6 +159,7 @@ } else { *handle = &sync->driver; } + free(allPaths); return sync != NULL ? SD_SUCCESS : SD_ERROR; } --- open-vm-tools-10.0.5-3227872/services/plugins/vmbackup/README 2015-11-23 23:07:44.000000000 -0800 +++ open-vm-tools-10.0.5-3227872/services/plugins/vmbackup/README 2017-01-11 02:08:10.000000000 -0800 @@ -24,6 +24,10 @@ | d | \/ | SCRIPT_THAW -------- + | | + | f | g + \/ | + COMPLETE_WAIT------- The transitions mean the following events / conditions: @@ -33,6 +37,8 @@ c. vmbackup.snapshotDone RPC d. sync provider operation is finished e. error condition: runtime error, or vmbackup.abort RPC +f. thaw scripts run finished +g. get notification that snapshot succeeds Sending a vmbackup.start RPC while the state machine is not IDLE causes an error to be returned to the client, but the state machine is not changed. --- open-vm-tools-10.0.5-3227872/services/plugins/vmbackup/nullProvider.c 2015-11-23 23:07:44.000000000 -0800 +++ open-vm-tools-10.0.5-3227872/services/plugins/vmbackup/nullProvider.c 2017-01-11 02:08:10.000000000 -0800 @@ -1,5 +1,5 @@ /********************************************************* - * Copyright (C) 2010-2015 VMware, Inc. All rights reserved. + * Copyright (C) 2010-2016 VMware, Inc. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published @@ -32,6 +32,7 @@ #if defined(_WIN32) + /* ****************************************************************************** * VmBackupNullStart -- */ /** @@ -54,14 +55,128 @@ return VmBackup_SendEvent(VMBACKUP_EVENT_SNAPSHOT_COMMIT, 0, ""); } + +/* + ****************************************************************************** + * VmBackupNullSnapshotDone -- */ /** + * + * Does nothing, just keep the backup state machine alive. + * + * @param[in] state Backup state. + * @param[in] clientData Unused. + * + * @return TRUE. + * + ****************************************************************************** + */ + +static Bool +VmBackupNullSnapshotDone(VmBackupState *state, + void *clientData) +{ + VmBackup_SetCurrentOp(state, NULL, NULL, __FUNCTION__); + return TRUE; +} + #else /* ****************************************************************************** + * VmBackupNullReadyForSnapshot -- */ /** + * + * Sends an event to the VMX indicating that the guest is ready for a + * snapshot to be taken (i.e., scripts have run and Nulldriver is + * enabled). + * + * @param[in] state Backup state. + * + * @return TRUE, unless sending the message fails. + * + ****************************************************************************** + */ + +static Bool +VmBackupNullReadyForSnapshot(VmBackupState *state) +{ + Bool success; + + g_debug("*** %s\n", __FUNCTION__); + success = VmBackup_SendEvent(VMBACKUP_EVENT_SNAPSHOT_COMMIT, 0, ""); + if (success) { + state->freezeStatus = VMBACKUP_FREEZE_FINISHED; + } else { + g_warning("Failed to send commit event to host"); + state->freezeStatus = VMBACKUP_FREEZE_ERROR; + } + return success; +} + + +/* + ****************************************************************************** + * VmBackupNullOpQuery -- */ /** + * + * Checks the status of the operation that is enabling or disabling the + * Null driver. Nulldriver is enabled immediately and there is nothing + * to disable. + * + * @param[in] op VmBackupOp. + * + * @return VMBACKUP_STATUS_FINISHED always. + * + ****************************************************************************** + */ + +static VmBackupOpStatus +VmBackupNullOpQuery(VmBackupOp *op) // IN +{ + return VMBACKUP_STATUS_FINISHED; +} + + +/* + ****************************************************************************** + * VmBackupNullOpRelease -- */ /** + * + * Cleans up data held by the op object. + * + * @param[in] op VmBackupOp. + * + ****************************************************************************** + */ + +static void +VmBackupNullOpRelease(VmBackupOp *op) // IN +{ + g_free(op); +} + + +/* + ****************************************************************************** + * VmBackupNullOpCancel -- */ /** + * + * Cancel an ongoing Nulldriver operation. This doesn't actually + * do anything because there is no operation to cancel as such. + * + * @param[in] op VmBackupOp. + * + ****************************************************************************** + */ + +static void +VmBackupNullOpCancel(VmBackupOp *op) // IN +{ + /* Nothing to do */ +} + + +/* + ****************************************************************************** * VmBackupNullStart -- */ /** * - * Calls sync(2) on POSIX systems. Sends the "commit snapshot" event to the - * host. + * Calls sync(2) on POSIX systems. Sets up an asynchronous operation + * for tracking. * * @param[in] ctx Plugin context. * @param[in] state Backup state. @@ -73,28 +188,35 @@ VmBackupNullStart(ToolsAppCtx *ctx, void *clientData) { + VmBackupOp *op; VmBackupState *state = (VmBackupState*) clientData; + + g_debug("*** %s\n", __FUNCTION__); + + op = g_new0(VmBackupOp, 1); + op->queryFn = VmBackupNullOpQuery; + op->cancelFn = VmBackupNullOpCancel; + op->releaseFn = VmBackupNullOpRelease; + /* * This is more of a "let's at least do something" than something that * will actually ensure data integrity... */ sync(); - VmBackup_SetCurrentOp(state, NULL, NULL, __FUNCTION__); - if (!VmBackup_SendEvent(VMBACKUP_EVENT_SNAPSHOT_COMMIT, 0, "")) { - g_warning("Failed to send commit event to host"); - state->freezeStatus = VMBACKUP_FREEZE_ERROR; - } else { - state->freezeStatus = VMBACKUP_FREEZE_FINISHED; - } + + VmBackup_SetCurrentOp(state, + op, + VmBackupNullReadyForSnapshot, + __FUNCTION__); } -#endif /* ****************************************************************************** * VmBackupNullSnapshotDone -- */ /** * - * Does nothing, just keep the backup state machine alive. + * Does nothing except setting up an asynchronous operation to keep the + * backup state machine alive. * * @param[in] state Backup state. * @param[in] clientData Unused. @@ -108,10 +230,20 @@ VmBackupNullSnapshotDone(VmBackupState *state, void *clientData) { - VmBackup_SetCurrentOp(state, NULL, NULL, __FUNCTION__); + VmBackupOp *op; + + g_debug("*** %s\n", __FUNCTION__); + + op = g_new0(VmBackupOp, 1); + op->queryFn = VmBackupNullOpQuery; + op->cancelFn = VmBackupNullOpCancel; + op->releaseFn = VmBackupNullOpRelease; + + VmBackup_SetCurrentOp(state, op, NULL, __FUNCTION__); return TRUE; } +#endif /* ****************************************************************************** --- open-vm-tools-10.0.5-3227872/services/plugins/vmbackup/stateMachine.c 2015-11-23 23:07:44.000000000 -0800 +++ open-vm-tools-10.0.5-3227872/services/plugins/vmbackup/stateMachine.c 2017-01-11 02:08:10.000000000 -0800 @@ -1,5 +1,5 @@ /********************************************************* - * Copyright (C) 2007-2015 VMware, Inc. All rights reserved. + * Copyright (C) 2007-2017 VMware, Inc. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published @@ -74,6 +74,18 @@ NULL); \ } while (0) +/* + * Macros to read values from config file. + */ +#define VMBACKUP_CONFIG_GET_BOOL(config, key, defVal) \ + VMTools_ConfigGetBoolean(config, "vmbackup", key, defVal) + +#define VMBACKUP_CONFIG_GET_STR(config, key, defVal) \ + VMTools_ConfigGetString(config, "vmbackup", key, defVal) + +#define VMBACKUP_CONFIG_GET_INT(config, key, defVal) \ + VMTools_ConfigGetInteger(config, "vmbackup", key, defVal) + static VmBackupState *gBackupState = NULL; static Bool @@ -82,6 +94,9 @@ static Bool VmBackupEnableSyncWait(void); +static Bool +VmBackupEnableCompleteWait(void); + /** * Returns a string representation of the given state machine state. @@ -113,6 +128,9 @@ case VMBACKUP_MSTATE_SCRIPT_THAW: return "SCRIPT_THAW"; + case VMBACKUP_MSTATE_COMPLETE_WAIT: + return "COMPLETE_WAIT"; + case VMBACKUP_MSTATE_SCRIPT_ERROR: return "SCRIPT_ERROR"; @@ -249,7 +267,8 @@ #endif if (!success) { - g_warning("Failed to send vmbackup event: %s.\n", result); + g_warning("Failed to send vmbackup event: %s, result: %s.\n", + msg, result); } vm_free(result); g_free(msg); @@ -299,6 +318,9 @@ } gBackupState->provider->release(gBackupState->provider); + if (gBackupState->completer != NULL) { + gBackupState->completer->release(gBackupState->completer); + } g_static_mutex_free(&gBackupState->opLock); g_free(gBackupState->scriptArg); g_free(gBackupState->volumes); @@ -393,6 +415,7 @@ break; case VMBACKUP_MSTATE_SCRIPT_THAW: + case VMBACKUP_MSTATE_COMPLETE_WAIT: /* Next state is "idle". */ gBackupState->machineState = VMBACKUP_MSTATE_IDLE; break; @@ -417,6 +440,7 @@ ASSERT(gBackupState != NULL); if (gBackupState->machineState != VMBACKUP_MSTATE_SCRIPT_ERROR && gBackupState->machineState != VMBACKUP_MSTATE_SYNC_ERROR) { + const char *eventMsg = "Quiesce aborted."; /* Mark the current operation as cancelled. */ g_static_mutex_lock(&gBackupState->opLock); if (gBackupState->currentOp != NULL) { @@ -426,9 +450,21 @@ } g_static_mutex_unlock(&gBackupState->opLock); +#ifdef __linux__ + /* Thaw the guest if already quiesced */ + if (gBackupState->machineState == VMBACKUP_MSTATE_SYNC_FREEZE) { + g_debug("Guest already quiesced, thawing for abort\n"); + if (!gBackupState->provider->snapshotDone(gBackupState, + gBackupState->provider->clientData)) { + g_debug("Thaw during abort failed\n"); + eventMsg = "Quiesce could not be aborted."; + } + } +#endif + VmBackup_SendEvent(VMBACKUP_EVENT_REQUESTOR_ABORT, VMBACKUP_REMOTE_ABORT, - "Quiesce aborted."); + eventMsg); /* Transition to the error state. */ if (VmBackupOnError()) { @@ -459,51 +495,49 @@ /** - * Callback that checks for the status of the current operation. Calls the - * queued operations as needed. + * Post-process the current operation. Calls the queued + * operations as needed. * - * @param[in] clientData Unused. + * @param[out] pending Tells if currentOp is pending. * - * @return FALSE + * @return TRUE if currentOp finished or pending, FALSE on error. */ static gboolean -VmBackupAsyncCallback(void *clientData) +VmBackupPostProcessCurrentOp(gboolean *pending) { + gboolean retVal = TRUE; VmBackupOpStatus status = VMBACKUP_STATUS_FINISHED; g_debug("*** %s\n", __FUNCTION__); ASSERT(gBackupState != NULL); - g_source_unref(gBackupState->timerEvent); - gBackupState->timerEvent = NULL; + *pending = FALSE; g_static_mutex_lock(&gBackupState->opLock); + if (gBackupState->currentOp != NULL) { - g_debug("VmBackupAsyncCallback: checking %s\n", gBackupState->currentOpName); + g_debug("%s: checking %s\n", __FUNCTION__, gBackupState->currentOpName); status = VmBackup_QueryStatus(gBackupState->currentOp); } - g_static_mutex_unlock(&gBackupState->opLock); switch (status) { case VMBACKUP_STATUS_PENDING: + *pending = TRUE; goto exit; case VMBACKUP_STATUS_FINISHED: - g_static_mutex_lock(&gBackupState->opLock); if (gBackupState->currentOpName != NULL) { g_debug("Async request '%s' completed\n", gBackupState->currentOpName); VmBackup_Release(gBackupState->currentOp); gBackupState->currentOpName = NULL; } gBackupState->currentOp = NULL; - g_static_mutex_unlock(&gBackupState->opLock); break; default: { gchar *msg; - g_static_mutex_lock(&gBackupState->opLock); if (gBackupState->errorMsg != NULL) { msg = g_strdup_printf("'%s' operation failed: %s", gBackupState->currentOpName, @@ -519,8 +553,7 @@ VmBackup_Release(gBackupState->currentOp); gBackupState->currentOp = NULL; - g_static_mutex_unlock(&gBackupState->opLock); - VmBackupOnError(); + retVal = FALSE; goto exit; } } @@ -533,23 +566,62 @@ VmBackupCallback cb = gBackupState->callback; gBackupState->callback = NULL; + /* + * Callback should not acquire opLock, but instead assume that + * it holds the lock already. + */ if (cb(gBackupState)) { - g_static_mutex_lock(&gBackupState->opLock); if (gBackupState->currentOp != NULL || gBackupState->forceRequeue) { - g_static_mutex_unlock(&gBackupState->opLock); goto exit; } - g_static_mutex_unlock(&gBackupState->opLock); } else { - VmBackupOnError(); + retVal = FALSE; goto exit; } } +exit: + g_static_mutex_unlock(&gBackupState->opLock); + return retVal; +} + + +/** + * Callback to advance the state machine to next state once + * current operation finishes. + * + * @param[in] clientData Unused. + * + * @return FALSE + */ + +static gboolean +VmBackupAsyncCallback(void *clientData) +{ + gboolean opPending; + g_debug("*** %s\n", __FUNCTION__); + ASSERT(gBackupState != NULL); + + g_source_unref(gBackupState->timerEvent); + gBackupState->timerEvent = NULL; + /* - * At this point, the current operation can be declared finished, and the - * state machine can move to the next state. + * Move the state machine to the next state, if the + * currentOp has finished. */ + if (!VmBackupPostProcessCurrentOp(&opPending)) { + VmBackupOnError(); + goto exit; + } else { + /* + * State transition can't be done if currentOp + * has not finished yet. + */ + if (opPending) { + goto exit; + } + } + switch (gBackupState->machineState) { case VMBACKUP_MSTATE_SCRIPT_FREEZE: /* Next state is "sync freeze wait". */ @@ -583,8 +655,15 @@ } break; - case VMBACKUP_MSTATE_SCRIPT_ERROR: case VMBACKUP_MSTATE_SCRIPT_THAW: + /* Next state is "complete wait" or "idle". */ + if (!VmBackupEnableCompleteWait()) { + VmBackupOnError(); + } + break; + + case VMBACKUP_MSTATE_SCRIPT_ERROR: + case VMBACKUP_MSTATE_COMPLETE_WAIT: /* Next state is "idle". */ gBackupState->machineState = VMBACKUP_MSTATE_IDLE; break; @@ -676,6 +755,62 @@ /** + * Calls the completer's start function and moves the state + * machine to next state. + * + * @return Whether the completer's start callback was successful. + */ + +static Bool +VmBackupEnableCompleteWait(void) +{ + Bool ret = TRUE; + + g_debug("*** %s\n", __FUNCTION__); + + if (gBackupState->completer == NULL) { + gBackupState->machineState = VMBACKUP_MSTATE_IDLE; + goto exit; + } + + if (gBackupState->abortTimer != NULL) { + g_source_destroy(gBackupState->abortTimer); + g_source_unref(gBackupState->abortTimer); + + /* Host make timeout maximum as 15 minutes for complete process. */ + if (gBackupState->timeout > GUEST_QUIESCE_DEFAULT_TIMEOUT_IN_SEC) { + gBackupState->timeout = GUEST_QUIESCE_DEFAULT_TIMEOUT_IN_SEC; + } + + if (gBackupState->timeout != 0) { + g_debug("Using completer timeout: %u\n", gBackupState->timeout); + gBackupState->abortTimer = + g_timeout_source_new_seconds(gBackupState->timeout); + VMTOOLSAPP_ATTACH_SOURCE(gBackupState->ctx, + gBackupState->abortTimer, + VmBackupAbortTimer, + NULL, + NULL); + } + } + + if (gBackupState->completer->start(gBackupState, + gBackupState->completer->clientData)) { + /* Move to next state */ + gBackupState->machineState = VMBACKUP_MSTATE_COMPLETE_WAIT; + } else { + VmBackup_SendEvent(VMBACKUP_EVENT_REQUESTOR_ERROR, + VMBACKUP_SYNC_ERROR, + "Error when enabling the sync provider."); + ret = FALSE; + } + +exit: + return ret; +} + + +/** * After sync provider's start function returns and moves the state * machine to VMBACKUP_MSTATE_SYNC_FREEZE state. * @@ -709,36 +844,6 @@ /** - * Get boolean entry for the key from the config file. - * - * @param[in] config Config file to read the key from. - * @param[in] key Key to look for in the config file. - * @param[in] defaultValue Default value if the key is not found. - */ - -static gboolean -VmBackupConfigGetBoolean(GKeyFile *config, - const char *key, - gboolean defValue) -{ - GError *err = NULL; - gboolean value = defValue; - - if (key != NULL) { - value = g_key_file_get_boolean(config, - "vmbackup", - key, - &err); - if (err != NULL) { - g_clear_error(&err); - value = defValue; - } - } - return value; -} - - -/** * Starts the quiesce operation according to the supplied specification unless * some unexpected error occurs. * @@ -754,9 +859,9 @@ VmBackupStartCommon(RpcInData *data, gboolean forceQuiesce) { - GError *err = NULL; ToolsAppCtx *ctx = data->appCtx; VmBackupSyncProvider *provider = NULL; + VmBackupSyncCompleter *completer = NULL; size_t i; @@ -779,7 +884,7 @@ * only allow VSS provider */ #if defined(_WIN32) - if (VmBackupConfigGetBoolean(ctx->config, "enableVSS", TRUE)) { + if (VMBACKUP_CONFIG_GET_BOOL(ctx->config, "enableVSS", TRUE)) { provider = VmBackup_NewVssProvider(); } #elif defined(_LINUX) || defined(__linux__) @@ -788,9 +893,9 @@ * only allow SyncDriver provider */ if (gBackupState->quiesceFS && - VmBackupConfigGetBoolean(ctx->config, "enableSyncDriver", TRUE)) { + VMBACKUP_CONFIG_GET_BOOL(ctx->config, "enableSyncDriver", TRUE)) { provider = VmBackup_NewSyncDriverOnlyProvider(); - } + } #endif } else { /* If no quiescing is requested only allow null provider */ @@ -805,7 +910,7 @@ for (i = 0; i < ARRAYSIZE(providers); i++) { struct SyncProvider *sp = &providers[i]; - if (VmBackupConfigGetBoolean(ctx->config, sp->cfgEntry, TRUE)) { + if (VMBACKUP_CONFIG_GET_BOOL(ctx->config, sp->cfgEntry, TRUE)) { provider = sp->ctor(); if (provider != NULL) { break; @@ -816,15 +921,27 @@ ASSERT(provider != NULL); +#if defined(_WIN32) + if (VMBACKUP_CONFIG_GET_BOOL(ctx->config, "enableVSS", TRUE)) { + completer = VmBackup_NewVssCompleter(provider); + if (completer == NULL) { + provider->release(provider); + g_warning("Requested quiescing cannot be initialized."); + goto error; + } + } +#endif + /* Instantiate the backup state and start the operation. */ gBackupState->ctx = data->appCtx; gBackupState->pollPeriod = 1000; gBackupState->machineState = VMBACKUP_MSTATE_IDLE; gBackupState->freezeStatus = VMBACKUP_FREEZE_FINISHED; gBackupState->provider = provider; + gBackupState->completer = completer; gBackupState->needsPriv = FALSE; g_static_mutex_init(&gBackupState->opLock); - gBackupState->enableNullDriver = VmBackupConfigGetBoolean(ctx->config, + gBackupState->enableNullDriver = VMBACKUP_CONFIG_GET_BOOL(ctx->config, "enableNullDriver", TRUE); @@ -864,15 +981,8 @@ * See bug 506106. */ if (gBackupState->timeout == 0) { - gBackupState->timeout = (guint) g_key_file_get_integer( - gBackupState->ctx->config, - "vmbackup", - "timeout", - &err); - if (err != NULL) { - g_clear_error(&err); - gBackupState->timeout = 15 * 60; - } + gBackupState->timeout = VMBACKUP_CONFIG_GET_INT(ctx->config, "timeout", + GUEST_QUIESCE_DEFAULT_TIMEOUT_IN_SEC); } /* Treat "0" as no timeout. */ @@ -898,6 +1008,9 @@ if (gBackupState->provider) { gBackupState->provider->release(gBackupState->provider); } + if (gBackupState->completer) { + gBackupState->completer->release(gBackupState->completer); + } g_free(gBackupState->scriptArg); g_free(gBackupState->volumes); g_free(gBackupState); @@ -923,6 +1036,8 @@ static gboolean VmBackupStart(RpcInData *data) { + ToolsAppCtx *ctx = data->appCtx; + g_debug("*** %s\n", __FUNCTION__); if (gBackupState != NULL) { return RPCIN_SETRETVALS(data, "Quiesce operation already in progress.", @@ -936,12 +1051,26 @@ if (StrUtil_GetNextIntToken(&generateManifests, &index, data->args, " ")) { gBackupState->generateManifests = generateManifests; } - gBackupState->quiesceApps = TRUE; - gBackupState->quiesceFS = TRUE; - gBackupState->allowHWProvider = TRUE; - gBackupState->execScripts = TRUE; - gBackupState->scriptArg = NULL; - gBackupState->timeout = 0; + gBackupState->quiesceApps = VMBACKUP_CONFIG_GET_BOOL(ctx->config, + "quiesceApps", + TRUE); + gBackupState->quiesceFS = VMBACKUP_CONFIG_GET_BOOL(ctx->config, + "quiesceFS", + TRUE); + gBackupState->allowHWProvider = VMBACKUP_CONFIG_GET_BOOL(ctx->config, + "allowHWProvider", + TRUE); + gBackupState->execScripts = VMBACKUP_CONFIG_GET_BOOL(ctx->config, + "execScripts", + TRUE); + gBackupState->scriptArg = VMBACKUP_CONFIG_GET_STR(ctx->config, + "scriptArg", + NULL); + gBackupState->timeout = VMBACKUP_CONFIG_GET_INT(ctx->config, + "timeout", 0); + gBackupState->vssUseDefault = VMBACKUP_CONFIG_GET_BOOL(ctx->config, + "vssUseDefault", + TRUE); /* get volume uuids if provided */ if (data->args[index] != '\0') { @@ -949,7 +1078,9 @@ data->argsSize - index); } } - return VmBackupStartCommon(data, FALSE); + return VmBackupStartCommon(data, VMBACKUP_CONFIG_GET_BOOL(ctx->config, + "forceQuiesce", + FALSE)); } @@ -987,8 +1118,10 @@ static gboolean VmBackupStartWithOpts(RpcInData *data) { + ToolsAppCtx *ctx = data->appCtx; GuestQuiesceParams *params; - GuestQuiesceParamsV1 *paramsV1; + GuestQuiesceParamsV1 *paramsV1 = NULL; + GuestQuiesceParamsV2 *paramsV2; gboolean retval; g_debug("*** %s\n", __FUNCTION__); @@ -997,24 +1130,57 @@ FALSE); } params = (GuestQuiesceParams *)data->args; + +#if defined(_WIN32) + if (params->ver != GUESTQUIESCEPARAMS_V1 && + params->ver != GUESTQUIESCEPARAMS_V2) { + g_warning("%s: Incompatible quiesce parameter version. \n", __FUNCTION__); + return RPCIN_SETRETVALS(data, "Incompatible quiesce parameter version", + FALSE); + } +#else if (params->ver != GUESTQUIESCEPARAMS_V1) { g_warning("%s: Incompatible quiesce parameter version. \n", __FUNCTION__); return RPCIN_SETRETVALS(data, "Incompatible quiesce parameter version", FALSE); } +#endif + gBackupState = g_new0(VmBackupState, 1); - paramsV1 = params->GuestQuiesceParams_u.guestQuiesceParamsV1; - gBackupState->generateManifests = paramsV1->createManifest; - gBackupState->quiesceApps = paramsV1->quiesceApps; - gBackupState->quiesceFS = paramsV1->quiesceFS; - gBackupState->allowHWProvider = paramsV1->writableSnapshot; - gBackupState->execScripts = paramsV1->execScripts; - gBackupState->scriptArg = g_strndup(paramsV1->scriptArg, - strlen(paramsV1->scriptArg)); - gBackupState->timeout = paramsV1->timeout; - gBackupState->volumes = g_strndup(paramsV1->diskUuids, - strlen(paramsV1->diskUuids)); - retval = VmBackupStartCommon(data, TRUE); + + if (params->ver == GUESTQUIESCEPARAMS_V1) { + paramsV1 = params->GuestQuiesceParams_u.guestQuiesceParamsV1; + gBackupState->vssUseDefault = VMBACKUP_CONFIG_GET_BOOL(ctx->config, + "vssUseDefault", + TRUE); + } else if (params->ver == GUESTQUIESCEPARAMS_V2) { + paramsV2 = params->GuestQuiesceParams_u.guestQuiesceParamsV2; + paramsV1 = ¶msV2->paramsV1; + gBackupState->vssBackupContext = paramsV2->vssBackupContext; + gBackupState->vssBackupType = paramsV2->vssBackupType; + gBackupState->vssBootableSystemState = paramsV2->vssBootableSystemState; + gBackupState->vssPartialFileSupport = paramsV2->vssPartialFileSupport; + gBackupState->vssUseDefault = VMBACKUP_CONFIG_GET_BOOL(ctx->config, + "vssUseDefault", + FALSE); + } + + if (paramsV1 != NULL) { + gBackupState->generateManifests = paramsV1->createManifest; + gBackupState->quiesceApps = paramsV1->quiesceApps; + gBackupState->quiesceFS = paramsV1->quiesceFS; + gBackupState->allowHWProvider = paramsV1->writableSnapshot; + gBackupState->execScripts = paramsV1->execScripts; + gBackupState->scriptArg = g_strndup(paramsV1->scriptArg, + strlen(paramsV1->scriptArg)); + gBackupState->timeout = paramsV1->timeout; + gBackupState->volumes = g_strndup(paramsV1->diskUuids, + strlen(paramsV1->diskUuids)); + } + + retval = VmBackupStartCommon(data, VMBACKUP_CONFIG_GET_BOOL(ctx->config, + "forceQuiesce", + TRUE)); return retval; } @@ -1085,6 +1251,45 @@ /** + * Notifies the completer to complete the provider process + * machine in the COMPLETE_WAIT state. + * + * @param[in] data RPC data. + * + * @return TRUE + */ + +static gboolean +VmBackupSnapshotCompleted(RpcInData *data) +{ + g_debug("*** %s\n", __FUNCTION__); + + if (gBackupState == NULL || + gBackupState->completer == NULL) { + return RPCIN_SETRETVALS(data, "Error: no quiesce complete in progress", + FALSE); + } else if (gBackupState->machineState != VMBACKUP_MSTATE_COMPLETE_WAIT) { + g_warning("Error: unexpected state for snapshot complete message: %s", + VmBackupGetStateName(gBackupState->machineState)); + return RPCIN_SETRETVALS(data, + "Error: unexpected state for complete message.", + FALSE); + } else { + if (!gBackupState->completer->snapshotCompleted(gBackupState, + gBackupState->completer->clientData)) { + VmBackup_SendEvent(VMBACKUP_EVENT_REQUESTOR_ERROR, + VMBACKUP_SYNC_ERROR, + "Error when notifying the sync completer."); + if (VmBackupOnError()) { + VmBackupFinalize(); + } + } + return RPCIN_SETRETVALS(data, "", TRUE); + } +} + + +/** * Prints some information about the plugin's state to the log. * * @param[in] src The source object. @@ -1167,6 +1372,8 @@ { VMBACKUP_PROTOCOL_START_WITH_OPTS, VmBackupStartWithOpts, NULL, xdr_GuestQuiesceParams, NULL, sizeof (GuestQuiesceParams) }, { VMBACKUP_PROTOCOL_ABORT, VmBackupAbort, NULL, NULL, NULL, 0 }, + { VMBACKUP_PROTOCOL_SNAPSHOT_COMPLETED, VmBackupSnapshotCompleted, NULL, + NULL, NULL, 0 }, { VMBACKUP_PROTOCOL_SNAPSHOT_DONE, VmBackupSnapshotDone, NULL, NULL, NULL, 0 } }; ToolsPluginSignalCb sigs[] = { --- open-vm-tools-10.0.5-3227872/services/plugins/vmbackup/syncDriverOps.c 2015-11-23 23:07:44.000000000 -0800 +++ open-vm-tools-10.0.5-3227872/services/plugins/vmbackup/syncDriverOps.c 2017-01-11 02:08:10.000000000 -0800 @@ -1,5 +1,5 @@ /********************************************************* - * Copyright (C) 2007-2015 VMware, Inc. All rights reserved. + * Copyright (C) 2007-2017 VMware, Inc. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published @@ -61,10 +61,10 @@ */ static Bool -VmBackupDriverThaw(VmBackupDriverOp *op) +VmBackupDriverThaw(SyncDriverHandle *handle) { - Bool success = SyncDriver_Thaw(*op->syncHandle); - SyncDriver_CloseHandle(op->syncHandle); + Bool success = SyncDriver_Thaw(*handle); + SyncDriver_CloseHandle(handle); return success; } @@ -106,7 +106,7 @@ case SYNCDRIVER_IDLE: if (op->canceled) { - VmBackupDriverThaw(op); + VmBackupDriverThaw(op->syncHandle); } /* * This prevents the release callback from freeing the handle, which @@ -117,7 +117,7 @@ break; default: - VmBackupDriverThaw(op); + VmBackupDriverThaw(op->syncHandle); ret = VMBACKUP_STATUS_ERROR; break; } @@ -231,7 +231,7 @@ state->enableNullDriver : FALSE, op->syncHandle); } else { - success = VmBackupDriverThaw(op); + success = VmBackupDriverThaw(op->syncHandle); } if (!success) { g_warning("Error %s filesystems.", freeze ? "freezing" : "thawing"); @@ -264,15 +264,24 @@ static Bool VmBackupSyncDriverReadyForSnapshot(VmBackupState *state) { - Bool success; SyncDriverHandle *handle = state->clientData; g_debug("*** %s\n", __FUNCTION__); if (handle != NULL && *handle != SYNCDRIVER_INVALID_HANDLE) { + Bool success; success = VmBackup_SendEvent(VMBACKUP_EVENT_SNAPSHOT_COMMIT, 0, ""); if (success) { state->freezeStatus = VMBACKUP_FREEZE_FINISHED; } else { + /* + * If the vmx does not know this event (e.g. due to an RPC timeout), + * then filesystems need to be thawed here because snapshotDone + * will not be sent by the vmx. + */ + g_debug("VMX state changed; thawing filesystems.\n"); + if (!VmBackupDriverThaw(handle)) { + g_warning("Error thawing filesystems.\n"); + } state->freezeStatus = VMBACKUP_FREEZE_ERROR; } return success; @@ -488,7 +497,7 @@ static Bool VmBackupSyncDriverOnlySnapshotDone(VmBackupState *state, - void *clientData) + void *clientData) { VmBackupDriverOp *op; --- open-vm-tools-10.0.5-3227872/services/plugins/vmbackup/vmBackupInt.h 2015-11-23 23:07:44.000000000 -0800 +++ open-vm-tools-10.0.5-3227872/services/plugins/vmbackup/vmBackupInt.h 2017-01-11 02:08:10.000000000 -0800 @@ -1,5 +1,5 @@ /********************************************************* - * Copyright (C) 2008-2015 VMware, Inc. All rights reserved. + * Copyright (C) 2008-2016 VMware, Inc. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published @@ -35,6 +35,11 @@ #include "vmware/tools/threadPool.h" #endif +/* + * The default timeout in seconds for guest OS quiescing process + */ +#define GUEST_QUIESCE_DEFAULT_TIMEOUT_IN_SEC (15 * 60) + typedef enum { VMBACKUP_STATUS_PENDING, VMBACKUP_STATUS_FINISHED, @@ -62,6 +67,7 @@ VMBACKUP_MSTATE_SYNC_FREEZE, VMBACKUP_MSTATE_SYNC_THAW, VMBACKUP_MSTATE_SCRIPT_THAW, + VMBACKUP_MSTATE_COMPLETE_WAIT, VMBACKUP_MSTATE_SCRIPT_ERROR, VMBACKUP_MSTATE_SYNC_ERROR } VmBackupMState; @@ -81,6 +87,7 @@ struct VmBackupSyncProvider; +struct VmBackupSyncCompleter; /** * Holds information about the current state of the backup operation. @@ -114,7 +121,7 @@ Bool execScripts; Bool enableNullDriver; Bool needsPriv; - char *scriptArg; + gchar *scriptArg; guint timeout; gpointer clientData; void *scripts; @@ -124,10 +131,17 @@ VmBackupMState machineState; VmBackupFreezeStatus freezeStatus; struct VmBackupSyncProvider *provider; + struct VmBackupSyncCompleter *completer; + gint vssBackupContext; + gint vssBackupType; + Bool vssBootableSystemState; + Bool vssPartialFileSupport; + Bool vssUseDefault; } VmBackupState; typedef Bool (*VmBackupCallback)(VmBackupState *); typedef Bool (*VmBackupProviderCallback)(VmBackupState *, void *clientData); +typedef Bool (*VmBackupCompleterCallback)(VmBackupState *, void *clientData); /** @@ -147,6 +161,19 @@ void *clientData; } VmBackupSyncProvider; +/** + * Defines the interface between the state machine and the implementation + * of the "sync completer": either the VSS completer or the sync driver + * completer, currently. + */ + +typedef struct VmBackupSyncCompleter { + VmBackupCompleterCallback start; + VmBackupCompleterCallback snapshotCompleted; + void (*release)(struct VmBackupSyncCompleter *); + void *clientData; +} VmBackupSyncCompleter; + /** * Sets the current asynchronous operation being monitored, and an @@ -251,6 +278,9 @@ VmBackupSyncProvider * VmBackup_NewVssProvider(void); +VmBackupSyncCompleter * +VmBackup_NewVssCompleter(VmBackupSyncProvider *provider); + void VmBackup_UnregisterSnapshotProvider(void); #endif