diff mercurial/posix.py @ 26883:c750ed59892a stable

posix: retry on symlink race in checklink Multiple threads might attempt to check links with the same temporary name. This would cause one side to get an EEXIST error and wrongly fail the support check. Here, we simply retry if our temporary name exists.
author Matt Mackall <mpm@selenic.com>
date Fri, 06 Nov 2015 15:23:10 -0600
parents 99b6afff09ae
children 8b2fbe3f59b1 1aa5083cbebb
line wrap: on
line diff
--- a/mercurial/posix.py	Fri Nov 06 13:01:15 2015 -0500
+++ b/mercurial/posix.py	Fri Nov 06 15:23:10 2015 -0600
@@ -170,22 +170,26 @@
     """check whether the given path is on a symlink-capable filesystem"""
     # mktemp is not racy because symlink creation will fail if the
     # file already exists
-    name = tempfile.mktemp(dir=path, prefix='hg-checklink-')
-    try:
-        fd = tempfile.NamedTemporaryFile(dir=path, prefix='hg-checklink-')
+    while True:
+        name = tempfile.mktemp(dir=path, prefix='hg-checklink-')
         try:
-            os.symlink(os.path.basename(fd.name), name)
-            os.unlink(name)
-            return True
-        finally:
-            fd.close()
-    except AttributeError:
-        return False
-    except OSError as inst:
-        # sshfs might report failure while successfully creating the link
-        if inst[0] == errno.EIO and os.path.exists(name):
-            os.unlink(name)
-        return False
+            fd = tempfile.NamedTemporaryFile(dir=path, prefix='hg-checklink-')
+            try:
+                os.symlink(os.path.basename(fd.name), name)
+                os.unlink(name)
+                return True
+            except OSError as inst:
+                # link creation might race, try again
+                if inst[0] == errno.EEXIST:
+                    continue
+                # sshfs might report failure while successfully creating the link
+                if inst[0] == errno.EIO and os.path.exists(name):
+                    os.unlink(name)
+                return False
+            finally:
+                fd.close()
+        except AttributeError:
+            return False
 
 def checkosfilename(path):
     '''Check that the base-relative path is a valid filename on this platform.