]> git.ipfire.org Git - thirdparty/open-vm-tools.git/commitdiff
Track Linux filesystem id (FSID) for quiesced (frozen) filesystems
authorKaty Feng <fkaty@vmware.com>
Wed, 18 Jan 2023 03:08:33 +0000 (19:08 -0800)
committerKaty Feng <fkaty@vmware.com>
Wed, 18 Jan 2023 03:08:33 +0000 (19:08 -0800)
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.

open-vm-tools/lib/syncDriver/syncDriverLinux.c

index eef65a2eb5f17f403855374da2e188f848d6bb09..6d9a356879b5fac61d1a17384e5c25c5de66795c 100644 (file)
@@ -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"
 #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));