How Mercurial could be made working with Virus Scanners on Windows

Adrian Buehlmann adrian at cadifra.com
Wed Dec 8 06:53:39 CST 2010


On 2010-12-08 10:31, timeless wrote:
> On Wed, Dec 8, 2010 at 10:53 AM, Adrian Buehlmann <adrian at cadifra.com> wrote:
>> So a trick to harden mercurial against AV scanners is to rename files to
>> a random name before deleting them, so that the dreaded filename
>> blocking (the ghost state) is done on a random name and not on the
>> precious original name of the file,
> 
> i was more or less going to suggest this at some point, so +1 from me.

Feel free to start hacking.

The last thing I did was starting to hack on implementing Matt's proposal
to rename the existing util.unlink function into something else (he suggested
'unlinkpath' on IRC, which seems like a sensible name) and then reintroduce
a new, different, util.unlink which does the trick. And then use that new
util.unlink instead of (most?) os.unlink calls.

The new util.unlink is supposed to only delete the file (whereas unlinkpath
also removes empty directories, like the current util.unlink does).

The new util.unlink would probably be an alias for os.unlink for unix and
have a special implementation in windows.unlink for Windows.

A part that might be tricky in util.unlinkpath is that a directory with a
ghost file most likely can't be deleted.

diff --git a/hgext/mq.py b/hgext/mq.py
--- a/hgext/mq.py
+++ b/hgext/mq.py
@@ -1114,7 +1114,7 @@ class queue(object):
                 for f in all_files:
                     if f not in repo.dirstate:
                         try:
-                            util.unlink(repo.wjoin(f))
+                            util.unlinkpath(repo.wjoin(f))
                         except OSError, inst:
                             if inst.errno != errno.ENOENT:
                                 raise
@@ -1208,7 +1208,7 @@ class queue(object):
                     raise util.Abort(_("deletions found between repo revs"))
                 for f in a:
                     try:
-                        util.unlink(repo.wjoin(f))
+                        util.unlinkpath(repo.wjoin(f))
                     except OSError, e:
                         if e.errno != errno.ENOENT:
                             raise
diff --git a/hgext/rebase.py b/hgext/rebase.py
--- a/hgext/rebase.py
+++ b/hgext/rebase.py
@@ -215,7 +215,7 @@ def rebase(ui, repo, **opts):
         clearstatus(repo)
         ui.note(_("rebase completed\n"))
         if os.path.exists(repo.sjoin('undo')):
-            util.unlink(repo.sjoin('undo'))
+            util.unlinkpath(repo.sjoin('undo'))
         if skipped:
             ui.note(_("%d revisions have been skipped\n") % len(skipped))
     finally:
@@ -393,7 +393,7 @@ def storestatus(repo, originalwd, target
 def clearstatus(repo):
     'Remove the status files'
     if os.path.exists(repo.join("rebasestate")):
-        util.unlink(repo.join("rebasestate"))
+        util.unlinkpath(repo.join("rebasestate"))
 
 def restorestatus(repo):
     'Restore a previously stored status'
diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -3250,7 +3250,7 @@ def revert(ui, repo, *pats, **opts):
                     continue
                 audit_path(f)
                 try:
-                    util.unlink(repo.wjoin(f))
+                    util.unlinkpath(repo.wjoin(f))
                 except OSError:
                     pass
                 repo.dirstate.remove(f)
diff --git a/mercurial/context.py b/mercurial/context.py
--- a/mercurial/context.py
+++ b/mercurial/context.py
@@ -829,7 +829,7 @@ class workingctx(changectx):
         if unlink:
             for f in list:
                 try:
-                    util.unlink(self._repo.wjoin(f))
+                    util.unlinkpath(self._repo.wjoin(f))
                 except OSError, inst:
                     if inst.errno != errno.ENOENT:
                         raise
diff --git a/mercurial/merge.py b/mercurial/merge.py
--- a/mercurial/merge.py
+++ b/mercurial/merge.py
@@ -309,7 +309,7 @@ def applyupdates(repo, action, wctx, mct
             if f == '.hgsubstate': # subrepo states need updating
                 subrepo.submerge(repo, wctx, mctx, wctx)
             try:
-                util.unlink(repo.wjoin(f))
+                util.unlinkpath(repo.wjoin(f))
             except OSError, inst:
                 if inst.errno != errno.ENOENT:
                     repo.ui.warn(_("update failed to remove %s: %s!\n") %
@@ -347,7 +347,7 @@ def applyupdates(repo, action, wctx, mct
                 repo.ui.note(_("moving %s to %s\n") % (f, fd))
                 t = wctx.filectx(f).data()
                 repo.wwrite(fd, t, flags)
-                util.unlink(repo.wjoin(f))
+                util.unlinkpath(repo.wjoin(f))
             if f2:
                 repo.ui.note(_("getting %s to %s\n") % (f2, fd))
                 t = mctx.filectx(f2).data()
diff --git a/mercurial/util.py b/mercurial/util.py
--- a/mercurial/util.py
+++ b/mercurial/util.py
@@ -432,7 +432,7 @@ def checksignature(func):
 
     return check
 
-def unlink(f):
+def unlinkpath(f):
     """unlink and remove the directory if it is empty"""
     os.unlink(f)
     # try removing directories that might now be empty
diff --git a/mercurial/windows.py b/mercurial/windows.py
--- a/mercurial/windows.py
+++ b/mercurial/windows.py
@@ -275,7 +275,7 @@ def _removedirs(name):
             break
         head, tail = os.path.split(head)
 
-def unlink(f):
+def unlinkpath(f):
     """unlink and remove the directory if it is empty"""
     os.unlink(f)
     # try removing directories that might now be empty




More information about the Mercurial-devel mailing list