/*********************************************************
- * 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
#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"
#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;
+}
/*
*******************************************************************************
* 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;
}
}
* 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);
*/
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);
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;
}
} 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));