diff mercurial/win32.py @ 13775:930efdc6bfa4

windows: move unlink to win32.py no code change
author Adrian Buehlmann <adrian@cadifra.com>
date Sun, 27 Mar 2011 01:17:48 +0100
parents 67743d5f49b6
children a2f0fdb1e488
line wrap: on
line diff
--- a/mercurial/win32.py	Sun Mar 27 12:59:25 2011 +0200
+++ b/mercurial/win32.py	Sun Mar 27 01:17:48 2011 +0100
@@ -6,7 +6,7 @@
 # GNU General Public License version 2 or any later version.
 
 import encoding
-import ctypes, errno, os, struct, subprocess
+import ctypes, errno, os, struct, subprocess, random
 
 _kernel32 = ctypes.windll.kernel32
 
@@ -316,3 +316,43 @@
         raise ctypes.WinError()
 
     return pi.dwProcessId
+
+def unlink(f):
+    '''try to implement POSIX' unlink semantics on Windows'''
+
+    # POSIX allows to unlink and rename open files. Windows has serious
+    # problems with doing that:
+    # - Calling os.unlink (or os.rename) on a file f fails if f or any
+    #   hardlinked copy of f has been opened with Python's open(). There is no
+    #   way such a file can be deleted or renamed on Windows (other than
+    #   scheduling the delete or rename for the next reboot).
+    # - Calling os.unlink on a file that has been opened with Mercurial's
+    #   posixfile (or comparable methods) will delay the actual deletion of
+    #   the file for as long as the file is held open. The filename is blocked
+    #   during that time and cannot be used for recreating a new file under
+    #   that same name ("zombie file"). Directories containing such zombie files
+    #   cannot be removed or moved.
+    # A file that has been opened with posixfile can be renamed, so we rename
+    # f to a random temporary name before calling os.unlink on it. This allows
+    # callers to recreate f immediately while having other readers do their
+    # implicit zombie filename blocking on a temporary name.
+
+    for tries in xrange(10):
+        temp = '%s-%08x' % (f, random.randint(0, 0xffffffff))
+        try:
+            os.rename(f, temp)  # raises OSError EEXIST if temp exists
+            break
+        except OSError, e:
+            if e.errno != errno.EEXIST:
+                raise
+    else:
+        raise IOError, (errno.EEXIST, "No usable temporary filename found")
+
+    try:
+        os.unlink(temp)
+    except:
+        # Some very rude AV-scanners on Windows may cause this unlink to fail.
+        # Not aborting here just leaks the temp file, whereas aborting at this
+        # point may leave serious inconsistencies. Ideally, we would notify
+        # the user in this case here.
+        pass