[ARVADOS] created: 1.1.0-108-g7bc55d6

Git user git at public.curoverse.com
Mon Nov 6 13:24:54 EST 2017


        at  7bc55d65082b3a39639508fcaebd1185b7e04089 (commit)


commit 7bc55d65082b3a39639508fcaebd1185b7e04089
Author: Tom Clegg <tclegg at veritasgenetics.com>
Date:   Fri Nov 3 14:18:36 2017 -0400

    12306: Avoid calling realpath/lstat unnecessarily during --unmount.
    
    Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tclegg at veritasgenetics.com>

diff --git a/services/fuse/arvados_fuse/unmount.py b/services/fuse/arvados_fuse/unmount.py
index 09a5f62..17ced4b 100644
--- a/services/fuse/arvados_fuse/unmount.py
+++ b/services/fuse/arvados_fuse/unmount.py
@@ -30,6 +30,19 @@ def mountinfo():
     return mi
 
 
+def paths_to_unmount(path, mnttype):
+    paths = []
+    for m in mountinfo():
+        if m.path == path or m.path.startswith(path+"/"):
+            paths.append(m.path)
+            if not (m.is_fuse and (mnttype is None or
+                                   mnttype == m.mnttype)):
+                raise Exception(
+                    "cannot unmount {}: mount type is {}".format(
+                        path, m.mnttype))
+    return paths
+
+
 def unmount(path, subtype=None, timeout=10, recursive=False):
     """Unmount the fuse mount at path.
 
@@ -45,8 +58,6 @@ def unmount(path, subtype=None, timeout=10, recursive=False):
     fuse mount at all. Raises an exception if it cannot be unmounted.
     """
 
-    path = os.path.realpath(path)
-
     if subtype is None:
         mnttype = None
     elif subtype == '':
@@ -55,15 +66,13 @@ def unmount(path, subtype=None, timeout=10, recursive=False):
         mnttype = 'fuse.' + subtype
 
     if recursive:
-        paths = []
-        for m in mountinfo():
-            if m.path == path or m.path.startswith(path+"/"):
-                paths.append(m.path)
-                if not (m.is_fuse and (mnttype is None or
-                                       mnttype == m.mnttype)):
-                    raise Exception(
-                        "cannot unmount {}: mount type is {}".format(
-                            path, m.mnttype))
+        paths = paths_to_unmount(path, mnttype)
+        if not paths:
+            # We might not have found any mounts merely because path
+            # contains symlinks, so we should resolve them and try
+            # again. We didn't do this from the outset because
+            # realpath() can hang (see explanation below).
+            paths = paths_to_unmount(os.path.realpath(path), mnttype)
         for path in sorted(paths, key=len, reverse=True):
             unmount(path, timeout=timeout, recursive=False)
         return len(paths) > 0
@@ -80,13 +89,32 @@ def unmount(path, subtype=None, timeout=10, recursive=False):
         for m in mountinfo():
             if m.is_fuse and (mnttype is None or mnttype == m.mnttype):
                 try:
-                    if os.path.realpath(m.path) == path:
+                    if m.path == path:
                         was_mounted = True
                         mounted = True
                         break
                 except OSError:
                     continue
-        if not mounted:
+        if not was_mounted and path != os.path.realpath(path):
+            # If the specified path contains symlinks, it won't appear
+            # verbatim in mountinfo.
+            #
+            # It might seem like we should have called realpath() from
+            # the outset. But we can't: realpath() hangs (in lstat())
+            # if we call it on an unresponsive mount point, and this
+            # is an important and common scenario.
+            #
+            # By waiting until now to try realpath(), we avoid this
+            # problem in the most common cases, which are: (1) the
+            # specified path has no symlinks and is a mount point, in
+            # which case was_mounted==True and we can proceed without
+            # calling realpath(); and (2) the specified path is not a
+            # mount point (e.g., it was already unmounted by someone
+            # else, or it's a typo), and realpath() can determine that
+            # without hitting any other unresponsive mounts.
+            path = os.path.realpath(path)
+            continue
+        elif not mounted:
             return was_mounted
 
         if attempted:

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


hooks/post-receive
-- 




More information about the arvados-commits mailing list