[PATCH 4 of 5 V2] util: add a getfstype method

Jun Wu quark at fb.com
Fri Mar 3 01:13:30 EST 2017


# HG changeset patch
# User Jun Wu <quark at fb.com>
# Date 1488521308 28800
#      Thu Mar 02 22:08:28 2017 -0800
# Node ID d79c818940ff7e29c76ff5e985b920885aa4e7c1
# Parent  1cf153ec3faaef92c9ad3515372a6d8591195d6e
# Available At https://bitbucket.org/quark-zju/hg-draft
#              hg pull https://bitbucket.org/quark-zju/hg-draft -r d79c818940ff
util: add a getfstype method

We have been very conservative about things like hardlink, flock etc.
because of some buggy (network) filesystem implementations. That's sad
because there are a lot of good (local) filesystem implementations where
optimizations like hardlinks could be just used safely.

This patch adds a "getfstype" method as a first step to solve the problem.
It only supports Linux for now, as Linux is widely used and could be
supported relatively cleanly. We can add support for other platforms later.

diff --git a/mercurial/posix.py b/mercurial/posix.py
--- a/mercurial/posix.py
+++ b/mercurial/posix.py
@@ -658,2 +658,40 @@ def bindunixsocket(sock, path):
         os.fchdir(bakwdfd)
         os.close(bakwdfd)
+
+if pycompat.sysplatform.startswith('linux'):
+    # for Linux, reading /etc/mtab (symlink to /proc/self/mounts) is a sane way
+    # to get the current filesystem mount information
+    def _getfstypetable():
+        result = {}
+        try:
+            with open('/etc/mtab', 'r') as f:
+                for line in f.read().splitlines():
+                    name, path, fs, ops, freq, passno = line.split(' ', 5)
+                    result[path] = fs
+        except OSError:
+            # /etc/mtab is not guaranteed available
+            pass
+        return result
+else:
+    # unknown platform
+    def _getfstypetable():
+        return {}
+
+# cache _getfstypetable() to avoid repetitive expensive queries, it assumes
+# mount table does not change during the lifetime of the process, which is
+# reasonable for short-lived process
+_fstypetable = None
+
+def getfstype(path):
+    """Given a path, return filesystem type or None (best-effort)"""
+    global _fstypetable
+    if _fstypetable is None:
+        _fstypetable = _getfstypetable()
+    nextpath = os.path.abspath(path)
+    while True:
+        path = nextpath
+        if path in _fstypetable:
+            return _fstypetable[path]
+        nextpath = os.path.dirname(path)
+        if nextpath == path:
+            return None
diff --git a/mercurial/util.py b/mercurial/util.py
--- a/mercurial/util.py
+++ b/mercurial/util.py
@@ -93,4 +93,5 @@ expandglobs = platform.expandglobs
 explainexit = platform.explainexit
 findexe = platform.findexe
+getfstype = platform.getfstype
 gethgcmd = platform.gethgcmd
 getuser = platform.getuser
diff --git a/mercurial/windows.py b/mercurial/windows.py
--- a/mercurial/windows.py
+++ b/mercurial/windows.py
@@ -478,2 +478,7 @@ def readpipe(pipe):
 def bindunixsocket(sock, path):
     raise NotImplementedError('unsupported platform')
+
+def getfstype(path):
+    """Given a path, return filesystem type or None (best-effort)"""
+    # not implemented for Windows
+    return None


More information about the Mercurial-devel mailing list