noinst_LTLIBRARIES = libSyncDriver.la
+libSyncDriver_la_CPPFLAGS =
+libSyncDriver_la_CPPFLAGS += @GLIB2_CPPFLAGS@
+
libSyncDriver_la_SOURCES =
libSyncDriver_la_SOURCES += syncDriverPosix.c
*/
SyncDriverErr
-NullDriver_Freeze(const char *paths,
+NullDriver_Freeze(const GSList *paths,
SyncDriverHandle *handle)
{
/*
* 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 {
#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
#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. */
typedef struct LinuxDriver {
SyncHandle driver;
- size_t fdCnt;
+ ssize_t fdCnt;
int *fds;
} LinuxDriver;
static SyncDriverErr
LinuxFiThaw(const SyncDriverHandle handle)
{
- size_t i;
+ ssize_t i;
LinuxDriver *sync = (LinuxDriver *) handle;
SyncDriverErr err = SD_SUCCESS;
- for (i = 0; i < sync->fdCnt; i++) {
+ /*
+ * Thaw in the reverse order of freeze
+ */
+ for (i = sync->fdCnt - 1; i >= 0; i--) {
if (ioctl(sync->fds[i], FITHAW) == -1) {
err = SD_ERROR;
}
LinuxFiClose(SyncDriverHandle handle)
{
LinuxDriver *sync = (LinuxDriver *) handle;
- size_t i;
+ ssize_t i;
- for (i = 0; i < sync->fdCnt; i++) {
+ /*
+ * Close in the reverse order of open
+ */
+ for (i = sync->fdCnt - 1; i >= 0; i--) {
close(sync->fds[i]);
}
free(sync->fds);
* 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.
*/
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;
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) {
sync->driver.thaw = LinuxFiThaw;
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) {
* as users with permission 700, so just ignore these.
*/
Debug(LGPFX "cannot access mounted directory '%s'.\n", path);
- free(path);
continue;
case EIO:
* 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 "failed to freeze '%s': %d (%s)\n",
path, ioctlerr, strerror(ioctlerr));
err = first && ioctlerr == ENOTTY ? SD_UNAVAILABLE : SD_ERROR;
- free(path);
break;
}
} else {
Warning(LGPFX "failed to thaw '%s': %d (%s)\n",
path, errno, strerror(errno));
}
- free(path);
close(fd);
err = SD_ERROR;
break;
count++;
}
- free(path);
first = FALSE;
}
#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"
/*
*-----------------------------------------------------------------------------
*
- * 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
*-----------------------------------------------------------------------------
*/
-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.
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;
}
}
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * 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);
+}
+
+
/*
*-----------------------------------------------------------------------------
*
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
*/
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);
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;
}
#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"
* 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.
*/
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;
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;
}
} else {
*handle = &sync->driver;
}
+ free(allPaths);
return sync != NULL ? SD_SUCCESS : SD_ERROR;
}