[ARVADOS] created: 9a7d92434f19ca790be65685502a3fc3f651a510

Git user git at public.curoverse.com
Thu Mar 16 16:44:00 EDT 2017


        at  9a7d92434f19ca790be65685502a3fc3f651a510 (commit)


commit 9a7d92434f19ca790be65685502a3fc3f651a510
Author: Tom Clegg <tom at curoverse.com>
Date:   Thu Mar 16 16:43:55 2017 -0400

    11209: Add arv-mount --unmount and --replace flags

diff --git a/services/fuse/arvados_fuse/command.py b/services/fuse/arvados_fuse/command.py
index 66f8a4d..61bb85c 100644
--- a/services/fuse/arvados_fuse/command.py
+++ b/services/fuse/arvados_fuse/command.py
@@ -90,6 +90,10 @@ class ArgumentParser(argparse.ArgumentParser):
 
         self.add_argument('--crunchstat-interval', type=float, help="Write stats to stderr every N seconds (default disabled)", default=0)
 
+        self.add_argument('--unmount', action='store_true', default=False,
+                          help="Forcefully unmount the specified mountpoint and exit")
+        self.add_argument('--replace', action='store_true', default=False,
+                          help="Forcefully unmount any existing fuse mount before mounting")
         self.add_argument('--unmount-timeout',
                           type=float, default=2.0,
                           help="Time to wait for graceful shutdown after --exec program exits and filesystem is unmounted")
@@ -118,6 +122,8 @@ class Mount(object):
             exit(1)
 
     def __enter__(self):
+        if self.args.replace:
+            unmount(self.args.mountpoint, timeout=self.args.unmount_timeout)
         llfuse.init(self.operations, self.args.mountpoint, self._fuse_options())
         if self.listen_for_events and not self.args.disable_event_listening:
             self.operations.listen_for_events()
@@ -139,7 +145,13 @@ class Mount(object):
                                 self.args.unmount_timeout)
 
     def run(self):
-        if self.args.exec_args:
+        if self.args.unmount:
+            if not unmount(self.args.mountpoint,
+                           timeout=self.args.unmount_timeout):
+                self.logger.error(
+                    "Not a fuse mount: %s", self.args.mountpoint)
+                exit(1)
+        elif self.args.exec_args:
             self._run_exec()
         else:
             self._run_standalone()
diff --git a/services/fuse/arvados_fuse/unmount.py b/services/fuse/arvados_fuse/unmount.py
new file mode 100644
index 0000000..8be549e
--- /dev/null
+++ b/services/fuse/arvados_fuse/unmount.py
@@ -0,0 +1,61 @@
+import errno
+import os
+import subprocess
+import time
+
+def unmount(path, timeout=10):
+    """Unmount the fuse mount at path.
+
+    Unmounting is done by writing 1 to the "abort" control file in
+    sysfs to kill the fuse driver process, then executing "fusermount
+    -u -z" to detach the mount point, and repeating these steps until
+    the mount is no longer listed in /proc/self/mountinfo.
+
+    This procedure should enable a non-root user to reliably unmount
+    their own fuse filesystem without risk of deadlock.
+
+    Returns True if unmounting was successful, False if it wasn't a
+    fuse mount at all. Raises an exception if it cannot be unmounted.
+    """
+
+    path = os.path.realpath(path)
+
+    was_mounted = False
+    t0 = time.time()
+    delay = 0
+    while True:
+        if timeout and t0 + timeout < time.time():
+            raise Exception("timed out")
+
+        mounted = False
+        with open('/proc/self/mountinfo') as mi:
+            for m in mi.readlines():
+                mntid, pmntid, dev, root, mnt, extra = m.split(" ", 5)
+                mnttype = extra.split(" - ")[1].split(" ")[0]
+                if not (mnttype == "fuse" or mnttype.startswith("fuse.")):
+                    continue
+                try:
+                    if os.path.realpath(mnt) == path:
+                        was_mounted = True
+                        mounted = True
+                        break
+                except OSError:
+                    continue
+        if not mounted:
+            return was_mounted
+
+        major, minor = dev.split(":")
+        try:
+            with open('/sys/fs/fuse/connections/'+str(minor)+'/abort', 'w') as f:
+                f.write("1")
+        except OSError as e:
+            if e.errno != errno.ENOENT:
+                raise
+        try:
+            subprocess.check_call(["fusermount", "-u", "-z", path])
+        except subprocess.CalledProcessError:
+            pass
+
+        time.sleep(delay)
+        if delay == 0:
+            delay = 1

-----------------------------------------------------------------------


hooks/post-receive
-- 




More information about the arvados-commits mailing list