The unified diff between revisions [3099ff90..] and [e9aa88ea..] is displayed below. It can also be downloaded as a raw diff.

This diff has been restricted to the following files: 'win32/fs.cc'

#
#
# patch "win32/fs.cc"
#  from [bfc8bc3aaa07a3ec97a5d555ee4af563a118546d]
#    to [409c24f9c6ca1d1b9de8f951885c3044d16dfbd0]
#
============================================================
--- win32/fs.cc	bfc8bc3aaa07a3ec97a5d555ee4af563a118546d
+++ win32/fs.cc	409c24f9c6ca1d1b9de8f951885c3044d16dfbd0
@@ -21,7 +21,7 @@ std::string get_current_working_dir()
     F("cannot get working directory: %s") % strerror(errno));
   return std::string(buffer);
 }
-
+
 void change_current_working_dir(any_path const & to)
 {
   E(!chdir(to.as_external().c_str()),
@@ -114,3 +114,54 @@ get_path_status(any_path const & path)
   else
     return path::file;
 }
+
+static bool
+rename_clobberingly_impl(const char* from, const char* to)
+{
+  // MoveFileEx is only available on NT-based systems.  We will revert to a
+  // more compatible DeleteFile/MoveFile pair as a compatibility fall-back.
+  typedef BOOL (*MoveFileExFun)(LPCTSTR, LPCTSTR, DWORD);
+  static MoveFileExFun MoveFileEx = 0;
+  if (MoveFileEx == 0) {
+    HMODULE hModule = LoadLibrary("kernel32");
+    MoveFileEx = reinterpret_cast<MoveFileExFun>
+      (GetProcAddress(hModule, "MoveFileExA"));
+    if (MoveFileEx)
+      L(F("using MoveFileEx for renames"));
+  }
+
+  if (MoveFileEx) {
+    if (MoveFileEx(from, to, MOVEFILE_REPLACE_EXISTING))
+      return true;
+  } else {
+    // This is not even remotely atomic, but what can you do?
+    DeleteFile(to);
+    if (MoveFile(from, to))
+      return true;
+  }
+  return false;
+}
+
+void
+rename_clobberingly(any_path const & from, any_path const & to)
+{
+  const char* szFrom = from.as_external().c_str();
+  const char* szTo = to.as_external().c_str();
+  static const int renameAttempts = 16;
+  DWORD sleepTime = 1;
+
+  // If a clobbering rename attempt fails, we wait and try again, up to an
+  // (arbitrary) maximum of 16 attempts.  This is a gross hack to work
+  // around the common problem where another process (e.g. a virus checker)
+  // will exclusive open a file you've just touched.
+  for (int i = 0; i < renameAttempts; ++i) {
+    if (rename_clobberingly_impl(szFrom, szTo))
+      return;
+    L(F("attempted rename of '%s' to '%s' failed: %d")
+      % szFrom % szTo % GetLastError());
+    Sleep(sleepTime);
+    if (sleepTime < 250)
+      sleepTime *= 2;
+  }
+  E(false, F("renaming '%s' to '%s' failed: %d") % from % to);
+}