Patchwork [4,of,6,V2] vfs: copy if EPERM to avoid file stat ambiguity forcibly at closing

login
register
mail settings
Submitter Katsunori FUJIWARA
Date July 4, 2017, 2:29 p.m.
Message ID <5079632240f2bcec1539.1499178542@speaknoevil>
Download mbox | patch
Permalink /patch/21969/
State Accepted
Headers show

Comments

Katsunori FUJIWARA - July 4, 2017, 2:29 p.m.
# HG changeset patch
# User FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
# Date 1499177627 -32400
#      Tue Jul 04 23:13:47 2017 +0900
# Node ID 5079632240f2bcec1539cb5b3572ac588f19c9c7
# Parent  c85a31bc02311ead3ad37da313453d33b1881e4a
vfs: copy if EPERM to avoid file stat ambiguity forcibly at closing

Now, files (to be truncated) are opened with checkambig=True, only if
localrepository caches it.

Therefore, straightforward "copy if EPERM" is always reasonable to
avoid file stat ambiguity at closing.

This patch makes checkambigatclosing close wrapper copy the target
file, and advance mtime on it after renaming, if EPERM. This can avoid
file stat ambiguity, even if the target file is owned by another (see
issue5418 and issue5584 for detail).

This patch factors main logic out instead of changing
checkambigatclosing._checkambig() directly, in order to reuse it.

Patch

diff --git a/mercurial/vfs.py b/mercurial/vfs.py
--- a/mercurial/vfs.py
+++ b/mercurial/vfs.py
@@ -22,6 +22,23 @@  from . import (
     util,
 )
 
+def _avoidambig(path, oldstat):
+    """Avoid file stat ambiguity forcibly
+
+    This function causes copying ``path`` file, if it is owned by
+    another (see issue5418 and issue5584 for detail).
+    """
+    def checkandavoid():
+        newstat = util.filestat.frompath(path)
+        # return whether file stat ambiguity is (already) avoided
+        return (not newstat.isambig(oldstat) or
+                newstat.avoidambig(path, oldstat))
+    if not checkandavoid():
+        # simply copy to change owner of path to get privilege to
+        # advance mtime (see issue5418)
+        util.rename(util.mktempcopy(path), path)
+        checkandavoid()
+
 class abstractvfs(object):
     """Abstract base class; cannot be instantiated"""
 
@@ -630,10 +647,7 @@  class checkambigatclosing(closewrapbase)
     def _checkambig(self):
         oldstat = self._oldstat
         if oldstat.stat:
-            newstat = util.filestat.frompath(self._origfh.name)
-            if newstat.isambig(oldstat):
-                # stat of changed file is ambiguous to original one
-                newstat.avoidambig(self._origfh.name, oldstat)
+            _avoidambig(self._origfh.name, oldstat)
 
     def __exit__(self, exc_type, exc_value, exc_tb):
         self._origfh.__exit__(exc_type, exc_value, exc_tb)