]> granicus.if.org Git - clang/commitdiff
For ASTUnit::Save, write the AST to a temporary and then rename it to the actual...
authorArgyrios Kyrtzidis <akyrtzi@gmail.com>
Thu, 21 Jul 2011 18:44:49 +0000 (18:44 +0000)
committerArgyrios Kyrtzidis <akyrtzi@gmail.com>
Thu, 21 Jul 2011 18:44:49 +0000 (18:44 +0000)
Should avoid race conditions. Addresses rdar://9788943.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@135691 91177308-0d34-0410-b5e6-96231b3b80d8

lib/Frontend/ASTUnit.cpp

index 89bc0919e57742c06777ab856aab2608841bf3dc..c50dea6aa347d635d90c69106a2c8f0e41ad2184 100644 (file)
@@ -45,6 +45,7 @@
 #include "llvm/Support/Path.h"
 #include "llvm/Support/raw_ostream.h"
 #include "llvm/Support/Timer.h"
+#include "llvm/Support/FileSystem.h"
 #include "llvm/Support/CrashRecoveryContext.h"
 #include <cstdlib>
 #include <cstdio>
@@ -2304,18 +2305,39 @@ void ASTUnit::CodeComplete(llvm::StringRef File, unsigned Line, unsigned Column,
 CXSaveError ASTUnit::Save(llvm::StringRef File) {
   if (getDiagnostics().hasUnrecoverableErrorOccurred())
     return CXSaveError_TranslationErrors;
-  
+
+  // Write to a temporary file and later rename it to the actual file, to avoid
+  // possible race conditions.
+  llvm::sys::Path TempPath(File);
+  if (TempPath.makeUnique(/*reuse_current=*/false, /*ErrMsg*/0))
+    return CXSaveError_Unknown;
+  // makeUnique may or may not have created the file. Try deleting before
+  // opening so that we can use F_Excl for exclusive access.
+  TempPath.eraseFromDisk();
+
   // FIXME: Can we somehow regenerate the stat cache here, or do we need to 
   // unconditionally create a stat cache when we parse the file?
   std::string ErrorInfo;
-  llvm::raw_fd_ostream Out(File.str().c_str(), ErrorInfo,
-                           llvm::raw_fd_ostream::F_Binary);
+  llvm::raw_fd_ostream Out(TempPath.c_str(), ErrorInfo,
+                           llvm::raw_fd_ostream::F_Binary |
+                           // if TempPath already exists, we should not try to
+                           // overwrite it, we want to avoid race conditions.
+                           llvm::raw_fd_ostream::F_Excl);
   if (!ErrorInfo.empty() || Out.has_error())
     return CXSaveError_Unknown;
 
   serialize(Out);
   Out.close();
-  return Out.has_error()? CXSaveError_Unknown : CXSaveError_None;
+  if (Out.has_error())
+    return CXSaveError_Unknown;
+
+  if (llvm::error_code ec = llvm::sys::fs::rename(TempPath.str(), File)) {
+    bool exists;
+    llvm::sys::fs::remove(TempPath.str(), exists);
+    return CXSaveError_Unknown;
+  }
+
+  return CXSaveError_None;
 }
 
 bool ASTUnit::serialize(llvm::raw_ostream &OS) {