From 88826c7f64f3180711943b5311c4414d4b1dc1d1 Mon Sep 17 00:00:00 2001
From: Katy Feng <fkaty@vmware.com>
Date: Tue, 17 Jan 2023 19:08:33 -0800
Subject: [PATCH] Track Linux filesystem id (FSID) for quiesced (frozen)
filesystems
RH-Author: Ani Sinha <None>
RH-MergeRequest: 14: Track Linux filesystem id (FSID) for quiesced (frozen) filesystems
RH-Bugzilla: 1880404 1994590
RH-Acked-by: Cathy Avery <cavery@redhat.com>
RH-Commit: [1/1] c4ed73561eba36e7112cf96384f0c28f28489934
Tracking the filesystem FSID along with each file descriptor (FD)
as the ioctl FIFREEZE is done. An EBUSY could be seen because of
an attempt to freeze the same superblock more than once depending
on the OS configuration (e.g. usage of bind mounts). An EBUSY could
also mean another process has locked or frozen that filesystem.
When an EBUSY is received, the filesyste FSID is checked against the
list of filesystems that have already be quiesced. If not previously
seen, a warning that the filesystem is controlled by another process
is logged and the quiesced snapshot request will be rejected.
(cherry picked from commit 9d458c53a7a656d4d1ba3a28d090cce82ac4af0e)
Signed-off-by: Ani Sinha <anisinha@redhat.com>
---
.../lib/syncDriver/syncDriverLinux.c | 112 +++++++++++++++---
1 file changed, 96 insertions(+), 16 deletions(-)
diff --git a/open-vm-tools/lib/syncDriver/syncDriverLinux.c b/open-vm-tools/lib/syncDriver/syncDriverLinux.c
index eef65a2e..6d9a3568 100644
--- a/open-vm-tools/lib/syncDriver/syncDriverLinux.c
+++ b/open-vm-tools/lib/syncDriver/syncDriverLinux.c
@@ -1,5 +1,5 @@
/*********************************************************
- * Copyright (C) 2011-2018 VMware, Inc. All rights reserved.
+ * Copyright (C) 2011-2018, 2023 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 @@
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
+#include <sys/statfs.h>
#include "debug.h"
#include "dynbuf.h"
#include "syncDriverInt.h"
@@ -43,12 +44,53 @@
#endif
+
+typedef struct LinuxFsInfo {
+ int fd;
+ fsid_t fsid;
+} LinuxFsInfo;
+
typedef struct LinuxDriver {
SyncHandle driver;
size_t fdCnt;
- int *fds;
+ LinuxFsInfo *fds;
} LinuxDriver;
+static
+const fsid_t MISSING_FSID = {};
+
+
+/*
+ *******************************************************************************
+ * LinuxFiFsIdMatch --
+ *
+ * Check the collection of filesystems previously frozen for the specific
+ * FSID.
+ *
+ * @param[in] fds List of LinuxFsInfo data for filesystems previously
+ * frozen.
+ * @param[in] count Number of fds in the list.
+ * @param[in] nfsid The Filesystem ID of interest.
+ *
+ * @return TRUE if the FSID matches one previously processed. Otherwise FALSE
+ *
+ *******************************************************************************
+ */
+
+static Bool
+LinuxFiFsIdMatch(const LinuxFsInfo *fds,
+ const size_t count,
+ const fsid_t *nfsid) {
+ size_t i;
+
+ for (i = 0; i < count; i++) {
+ if (fds[i].fsid.__val[0] == nfsid->__val[0] &&
+ fds[i].fsid.__val[1] == nfsid->__val[1]) {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
/*
*******************************************************************************
@@ -75,9 +117,11 @@ LinuxFiThaw(const SyncDriverHandle handle)
* 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]);
+ int fd = sync->fds[i-1].fd;
+
+ Debug(LGPFX "Thawing fd=%d.\n", fd);
+ if (ioctl(fd, FITHAW) == -1) {
+ Debug(LGPFX "Thaw failed for fd=%d.\n", fd);
err = SD_ERROR;
}
}
@@ -108,8 +152,10 @@ LinuxFiClose(SyncDriverHandle handle)
* 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]);
+ int fd = sync->fds[i-1].fd;
+
+ Debug(LGPFX "Closing fd=%d.\n", fd);
+ close(fd);
}
free(sync->fds);
free(sync);
@@ -196,8 +242,11 @@ LinuxDriver_Freeze(const GSList *paths,
*/
while (paths != NULL) {
int fd;
+ LinuxFsInfo fsInfo;
struct stat sbuf;
+ struct statfs fsbuf;
const char *path = paths->data;
+
Debug(LGPFX "opening path '%s'.\n", path);
paths = g_slist_next(paths);
fd = open(path, O_RDONLY);
@@ -258,23 +307,53 @@ LinuxDriver_Freeze(const GSList *paths,
continue;
}
+ if (fstatfs(fd, &fsbuf) == 0) {
+ fsInfo.fsid = fsbuf.f_fsid;
+ } else {
+ Debug(LGPFX "failed to get file system id for path '%s'.\n", path);
+ fsInfo.fsid = MISSING_FSID;
+ }
Debug(LGPFX "freezing path '%s' (fd=%d).\n", path, fd);
if (ioctl(fd, FIFREEZE) == -1) {
int ioctlerr = errno;
+
+ close(fd);
+ Debug(LGPFX "freeze on '%s' returned: %d (%s)\n",
+ path, ioctlerr, strerror(ioctlerr));
+ /*
+ * Previously, an EBUSY error was ignored, assuming that we may try
+ * to freeze the same superblock more than once depending on the
+ * OS configuration (e.g., usage of bind mounts).
+ * Using the filesystem Id to check if this is a filesystem that we
+ * have seen previously and will ignore this FD only if that is
+ * the case. Log a warning otherwise since the quiesced snapshot
+ * attempt will fail.
+ */
+ if (ioctlerr == EBUSY) {
+ if (LinuxFiFsIdMatch(DynBuf_Get(&fds),
+ DynBuf_GetSize(&fds),
+ &fsInfo.fsid)) {
+ /*
+ * We have previous knowledge of this file system by another
+ * mount point. Safe to ignore.
+ */
+ Debug(LGPFX "skipping path '%s' - previously frozen", path);
+ continue;
+ }
+ /*
+ * It appears that this FS has been locked or frozen by another
+ * process. We cannot proceed with the quiesced snapshot request.
+ */
+ Warning(LGPFX "'%s' appears locked or frozen by another process. "
+ "Cannot complete the quiesced snapshot request.\n", path);
+ }
/*
* If the ioctl does not exist, Linux will return ENOTTY. If it's not
* supported on the device, we get EOPNOTSUPP. Ignore the latter,
* since freezing does not make sense for all fs types, and some
* Linux fs drivers may not have been hooked up in the running kernel.
- *
- * Also ignore EBUSY since we may try to freeze the same superblock
- * more than once depending on the OS configuration (e.g., usage of
- * bind mounts).
*/
- close(fd);
- Debug(LGPFX "freeze on '%s' returned: %d (%s)\n",
- path, ioctlerr, strerror(ioctlerr));
- if (ioctlerr != EBUSY && ioctlerr != EOPNOTSUPP) {
+ if (ioctlerr != EOPNOTSUPP) {
Debug(LGPFX "failed to freeze '%s': %d (%s)\n",
path, ioctlerr, strerror(ioctlerr));
err = first && ioctlerr == ENOTTY ? SD_UNAVAILABLE : SD_ERROR;
@@ -282,7 +361,8 @@ LinuxDriver_Freeze(const GSList *paths,
}
} else {
Debug(LGPFX "successfully froze '%s' (fd=%d).\n", path, fd);
- if (!DynBuf_Append(&fds, &fd, sizeof fd)) {
+ fsInfo.fd = fd;
+ if (!DynBuf_Append(&fds, &fsInfo, sizeof fsInfo)) {
if (ioctl(fd, FITHAW) == -1) {
Warning(LGPFX "failed to thaw '%s': %d (%s)\n",
path, errno, strerror(errno));
--
2.39.1