Blob Blame History Raw
--- 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<GUESTQUIESCE_DISKUUID_MAX_LEN>;   /* 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 <glib.h>
 #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 <sys/ioctl.h>
 #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 <stdio.h>
 #include <sys/param.h>
 #include <sys/mount.h>
+#include <glib.h>
 #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 <fcntl.h>
 #include <sys/ioctl.h>
+#include <glib.h>
 #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 = &paramsV2->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