windows: check target type before actual unlinking to follow POSIX semantics stable
authorFUJIWARA Katsunori <foozy@lares.dti.ne.jp>
Tue, 07 May 2013 05:04:11 +0900
branchstable
changeset 19159 1b329f8c7b24
parent 19156 ed1a212193dc
child 19160 0848be1f1aad
windows: check target type before actual unlinking to follow POSIX semantics Creation and writing into target file via vfs (a.k.a opener) is done after "unlink()" target file, if it exists. For example, it is assumed that the revision X consists of file 'A', and the revision Y consists of file 'A/B'. Merging revision X into Y tries to "unlink()" on directory 'A' of 'A/B', before creation of file 'A'. On POSIX environment, directories should be removed by "rmdir(2)", and "unlink(2)" on directories fails. "unlink()" of Mercurial (and Python) uses "unlink(2)" directly, so unlinking in the merge case above would fail. In the other hand, on Windows environment, "unlink()" of Mercurial tries to rename before actual unlinking, to follow POSIX semantics: already opened file can be unlinked safely. This causes unexpected success in unlinking in the merge case above, even though directory 'A' is renamed to another. This confuses users. This patch checks whether target is directory or not before renaming, and raises IOError(errno.EPERM) if so, to follow POSIX semantics.
mercurial/win32.py
--- a/mercurial/win32.py	Thu May 09 21:09:58 2013 +0900
+++ b/mercurial/win32.py	Tue May 07 05:04:11 2013 +0900
@@ -344,6 +344,12 @@
 def unlink(f):
     '''try to implement POSIX' unlink semantics on Windows'''
 
+    if os.path.isdir(f):
+        # use EPERM because it is POSIX prescribed value, even though
+        # unlink(2) on directories returns EISDIR on Linux
+        raise IOError(errno.EPERM,
+                      "Unlinking directory not permitted: '%s'" % f)
+
     # 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