Index: include/llvm/Support/FileSystem.h =================================================================== --- include/llvm/Support/FileSystem.h +++ include/llvm/Support/FileSystem.h @@ -378,6 +378,13 @@ /// @param to The path to rename to. This is created. std::error_code rename(const Twine &from, const Twine &to); +/// Rename from From to To, but don't replace To if it exists. +/// +/// This is not atomic on posix. Both names will be visible for a short time. +/// +/// FD should correspond to From. It is used instead of From when possible. +std::error_code rename_dont_replace(const Twine &From, int FD, const Twine &To); + /// @brief Copy the contents of \a From to \a To. /// /// @param From The path to copy from. @@ -751,6 +758,11 @@ // Keep this with the temporary name. Error keep(); + // Change the name of the temporary file. If a file with Name already exists, + // this does nothing and returns an Error. This will still be a temporary + // file, so keep or discard should still be called. + Error rename(const Twine &Name); + // Delete the file. Error discard(); Index: lib/Support/LockFileManager.cpp =================================================================== --- lib/Support/LockFileManager.cpp +++ lib/Support/LockFileManager.cpp @@ -205,21 +205,23 @@ } while (true) { - // Create a link from the lock file name. If this succeeds, we're done. - std::error_code EC = - sys::fs::create_link(UniqueLockFile->TmpName, LockFileName); - if (!EC) { + // Rename the lock. If this succeeds, we're done. + Error E = UniqueLockFile->rename(LockFileName); + if (!E) { RemoveTempFile.cancel(); return; } - - if (EC != errc::file_exists) { - std::string S("failed to create link "); - raw_string_ostream OSS(S); - OSS << LockFileName.str() << " to " << UniqueLockFile->TmpName; - setError(EC, OSS.str()); + handleAllErrors(std::move(E), [&](const ECError &E) { + std::error_code EC = E.convertToErrorCode(); + if (EC != errc::file_exists) { + std::string S("failed to create link "); + raw_string_ostream OSS(S); + OSS << LockFileName.str() << " to " << UniqueLockFile->TmpName; + setError(EC, OSS.str()); + } + }); + if (ErrorCode) return; - } // Someone else managed to create the lock file first. Read the process ID // from the lock file. @@ -234,7 +236,7 @@ // There is a lock file that nobody owns; try to clean it up and get // ownership. - if ((EC = sys::fs::remove(LockFileName))) { + if (std::error_code EC = sys::fs::remove(LockFileName)) { std::string S("failed to remove lockfile "); S.append(LockFileName.str()); setError(EC, S); @@ -269,8 +271,7 @@ if (getState() != LFS_Owned) return; - // Since we own the lock, remove the lock file and our own unique lock file. - sys::fs::remove(LockFileName); + // Since we own the lock, remove it. consumeError(UniqueLockFile->discard()); } Index: lib/Support/Path.cpp =================================================================== --- lib/Support/Path.cpp +++ lib/Support/Path.cpp @@ -829,10 +829,20 @@ return Error::success(); } +Error TempFile::rename(const Twine &Name) { + SmallString<128> NameStorage; + StringRef N = Name.toStringRef(NameStorage); + if (std::error_code EC = rename_dont_replace(TmpName, FD, N)) + return errorCodeToError(EC); + TmpName = N; + return Error::success(); +} + Expected TempFile::create(const Twine &Model, unsigned Mode) { int FD; SmallString<128> ResultPath; - if (std::error_code EC = createUniqueFile(Model, FD, ResultPath, Mode)) + if (std::error_code EC = createUniqueFile(Model, FD, ResultPath, Mode, + sys::fs::F_RW | sys::fs::F_Delete)) return errorCodeToError(EC); // Make sure we delete the file when RemoveFileOnSignal fails. Index: lib/Support/Unix/Path.inc =================================================================== --- lib/Support/Unix/Path.inc +++ lib/Support/Unix/Path.inc @@ -421,6 +421,22 @@ return std::error_code(); } +std::error_code rename_dont_replace(const Twine &From, int FD, + const Twine &To) { + // FIXME: on linux we could use + // renameat2(AT_FDCWD, from, AT_FDCWD, to, RENAME_NOREPLACE) + + // The natural way of implementing this would be to create a hard + // link named To and then unlink From. + // It seems we have to support SMB file systems and hard links don't work on + // those. + // What we do instead is create a soft link and rename From over To. That way + // we avoid hard links and any reader will always see a valid file. + if (std::error_code EC = create_link(From, To)) + return EC; + return rename(From, To); +} + std::error_code resize_file(int FD, uint64_t Size) { #if defined(HAVE_POSIX_FALLOCATE) // If we have posix_fallocate use it. Unlike ftruncate it always allocates Index: lib/Support/Windows/Path.inc =================================================================== --- lib/Support/Windows/Path.inc +++ lib/Support/Windows/Path.inc @@ -537,6 +537,12 @@ return rename_handle(FromHandle, To); } +std::error_code rename_dont_replace(const Twine &From, int FD, + const Twine &To) { + HANDLE FromHandle = reinterpret_cast(_get_osfhandle(FD)); + return rename_internal(FromHandle, To, false); +} + std::error_code resize_file(int FD, uint64_t Size) { #ifdef HAVE__CHSIZE_S errno_t error = ::_chsize_s(FD, Size); @@ -1035,7 +1041,7 @@ Access |= DELETE; HANDLE H = - ::CreateFileW(PathUTF16.begin(), Access, + ::CreateFileW(PathUTF16.data(), Access, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, CreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL);