1 # Copyright (C) The Arvados Authors. All rights reserved.
3 # SPDX-License-Identifier: AGPL-3.0
12 MountInfo = collections.namedtuple(
13 'MountInfo', ['is_fuse', 'major', 'minor', 'mnttype', 'path'])
18 with open('/proc/self/mountinfo') as f:
19 for m in f.readlines():
20 mntid, pmntid, dev, root, path, extra = m.split(" ", 5)
21 mnttype = extra.split(" - ")[1].split(" ", 1)[0]
22 major, minor = dev.split(":")
24 is_fuse=(mnttype == "fuse" or mnttype.startswith("fuse.")),
33 def unmount(path, subtype=None, timeout=10, recursive=False):
34 """Unmount the fuse mount at path.
36 Unmounting is done by writing 1 to the "abort" control file in
37 sysfs to kill the fuse driver process, then executing "fusermount
38 -u -z" to detach the mount point, and repeating these steps until
39 the mount is no longer listed in /proc/self/mountinfo.
41 This procedure should enable a non-root user to reliably unmount
42 their own fuse filesystem without risk of deadlock.
44 Returns True if unmounting was successful, False if it wasn't a
45 fuse mount at all. Raises an exception if it cannot be unmounted.
48 path = os.path.realpath(path)
55 mnttype = 'fuse.' + subtype
60 if m.path == path or m.path.startswith(path+"/"):
62 if not (m.is_fuse and (mnttype is None or
63 mnttype == m.mnttype)):
65 "cannot unmount {}: mount type is {}".format(
67 for path in sorted(paths, key=len, reverse=True):
68 unmount(path, timeout=timeout, recursive=False)
76 deadline = time.time() + timeout
81 if m.is_fuse and (mnttype is None or mnttype == m.mnttype):
83 if os.path.realpath(m.path) == path:
95 delay = min(delay, deadline - time.time())
97 raise Exception("timed out")
101 with open('/sys/fs/fuse/connections/{}/abort'.format(m.minor),
105 if e.errno != errno.ENOENT:
110 subprocess.check_call(["fusermount", "-u", "-z", path])
111 except subprocess.CalledProcessError: