posix: retry on symlink race in checklink stable
authorMatt Mackall <mpm@selenic.com>
Fri, 06 Nov 2015 15:23:10 -0600
branchstable
changeset 26883 c750ed59892a
parent 26882 c4895f9b8ab1
child 26884 762bf510b42c
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.
mercurial/posix.py
--- 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.