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);
+}