]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
ceph-fuse: ignore fuse mount failure if path is already mounted
authorNikhilkumar Shelke <nshelke@redhat.com>
Fri, 7 Jan 2022 06:26:13 +0000 (11:56 +0530)
committerNikhilkumar Shelke <nshelke@redhat.com>
Mon, 7 Mar 2022 08:43:42 +0000 (14:13 +0530)
Fixes: https://tracker.ceph.com/issues/46075
Signed-off-by: Nikhilkumar Shelke <nshelke@redhat.com>
src/client/fuse_ll.cc
src/mount.fuse.ceph

index c2de8940fb7222a78a2c1b04d2ab8f16bb3f9684..e060ac880877919907b709a529b0030dc9f29bb0 100644 (file)
 #include <errno.h>
 #include <fcntl.h>
 #include <unistd.h>
+#include <libgen.h>
+#include <sys/vfs.h>
+#include <sys/xattr.h>
+#include <linux/magic.h>
 
 // ceph
 #include "common/errno.h"
 #define MINOR(dev)     ((unsigned int) ((dev) & MINORMASK))
 #define MKDEV(ma,mi)   (((ma) << MINORBITS) | (mi))
 
+#ifndef FUSE_SUPER_MAGIC
+#define FUSE_SUPER_MAGIC 0x65735546
+#endif
+
+#define _CEPH_CLIENT_ID        "ceph.client_id"
+
 using namespace std;
 
 static const ceph::unordered_map<int,int> cephfs_errno_to_system_errno = {
@@ -163,6 +173,76 @@ public:
   struct fuse_args args;
 };
 
+static int already_fuse_mounted(const char *path, bool &already_mounted)
+{
+  struct statx path_statx;
+  struct statx parent_statx;
+  char path_copy[PATH_MAX] = {0};
+  char *parent_path = NULL;
+  int err = 0;
+
+  already_mounted = false;
+
+  strncpy(path_copy, path, sizeof(path_copy)-1);
+  parent_path = dirname(path_copy);
+
+  // get stat information for original path
+  if (-1 == statx(AT_FDCWD, path, AT_STATX_DONT_SYNC, STATX_INO, &path_statx)) {
+    err = errno;
+    derr << "fuse_ll: already_fuse_mounted: statx(" << path << ") failed with error "
+      << cpp_strerror(err) << dendl;
+    return err;
+  }
+
+  // if path isn't directory, then it can't be a mountpoint.
+  if (!(path_statx.stx_mode & S_IFDIR)) {
+    err = EINVAL;
+    derr << "fuse_ll: already_fuse_mounted: "
+      << path << " is not a directory" << dendl;
+    return err;
+  }
+
+  // get stat information for parent path
+  if (-1 == statx(AT_FDCWD, parent_path, AT_STATX_DONT_SYNC, STATX_INO, &parent_statx)) {
+    err = errno;
+    derr << "fuse_ll: already_fuse_mounted: statx(" << parent_path << ") failed with error "
+      << cpp_strerror(err) << dendl;
+    return err;
+  }
+
+  // if original path and parent have different device ids,
+  // then the path is a mount point
+  // or, if they refer to the same path, then it's probably
+  // the root directory '/' and therefore path is a mountpoint
+  if( path_statx.stx_dev_major != parent_statx.stx_dev_major ||
+      path_statx.stx_dev_minor != parent_statx.stx_dev_minor ||
+      ( path_statx.stx_dev_major == parent_statx.stx_dev_major &&
+       path_statx.stx_dev_minor == parent_statx.stx_dev_minor &&
+       path_statx.stx_ino == parent_statx.stx_ino
+      )
+    ) {
+    struct statfs path_statfs;
+    if (-1 == statfs(path, &path_statfs)) {
+      err = errno;
+      derr << "fuse_ll: already_fuse_mounted: statfs(" << path << ") failed with error "
+        << cpp_strerror(err) << dendl;
+      return err;
+    }
+
+    if(FUSE_SUPER_MAGIC == path_statfs.f_type) {
+      // if getxattr returns positive length means value exist for ceph.client_id
+      // then ceph fuse is already mounted on path
+      char client_id[128] = {0};
+      if (getxattr(path, _CEPH_CLIENT_ID, &client_id, sizeof(client_id)) > 0) {
+       already_mounted = true;
+       derr << path << " already mounted by " << client_id << dendl;
+      }
+    }
+  }
+
+  return err;
+}
+
 static int getgroups(fuse_req_t req, gid_t **sgids)
 {
 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8)
@@ -1349,6 +1429,20 @@ int CephFuse::Handle::init(int argc, const char *argv[])
 
 int CephFuse::Handle::start()
 {
+  bool is_mounted = false;
+#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
+  int err = already_fuse_mounted(opts.mountpoint, is_mounted);
+#else
+  int err = already_fuse_mounted(mountpoint, is_mounted);
+#endif
+  if (err) {
+    return err;
+  }
+
+  if (is_mounted) {
+    return EBUSY;
+  }
+
 #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
   se = fuse_session_new(&args, &fuse_ll_oper, sizeof(fuse_ll_oper), this);
   if (!se) {
index c61625a8c2f58bd2b02b8347ba17552f2eac2486..5f115d8efc306bcf1648f2ea32472987e1112299 100755 (executable)
@@ -27,6 +27,7 @@ id=myuser,conf=/etc/ceph/foo.conf  /mnt/ceph   fuse.ceph   defaults   0 0
 
 import sys
 import argparse
+import errno
 from subprocess import Popen
 
 def ceph_options(mntops):
@@ -73,7 +74,8 @@ def main(arguments):
     mount_cmd.communicate()
 
     if (mount_cmd.returncode != 0):
-        print("Mount failed with status code: {}".format(mount_cmd.returncode))
+        if (mount_cmd.returncode != errno.EBUSY):
+            print("Mount failed with status code: {}".format(mount_cmd.returncode))
 
 if __name__ == '__main__':
     sys.exit(main(sys.argv[1:]))