[Support] Add overload writeFileAtomically(std::function Writer)

Differential Revision: https://reviews.llvm.org/D67424

llvm-svn: 371890
This commit is contained in:
Jan Korous 2019-09-13 20:08:27 +00:00
parent c6ffefd2d1
commit f69c91780f
7 changed files with 143 additions and 128 deletions

View File

@ -18,6 +18,7 @@
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/FileUtilities.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include <functional>
@ -35,34 +36,6 @@ std::string getShardPathFromFilePath(llvm::StringRef ShardRoot,
return ShardRootSS.str();
}
llvm::Error
writeAtomically(llvm::StringRef OutPath,
llvm::function_ref<void(llvm::raw_ostream &)> Writer) {
// Write to a temporary file first.
llvm::SmallString<128> TempPath;
int FD;
auto EC =
llvm::sys::fs::createUniqueFile(OutPath + ".tmp.%%%%%%%%", FD, TempPath);
if (EC)
return llvm::errorCodeToError(EC);
// Make sure temp file is destroyed on failure.
auto RemoveOnFail =
llvm::make_scope_exit([TempPath] { llvm::sys::fs::remove(TempPath); });
llvm::raw_fd_ostream OS(FD, /*shouldClose=*/true);
Writer(OS);
OS.close();
if (OS.has_error())
return llvm::errorCodeToError(OS.error());
// Then move to real location.
EC = llvm::sys::fs::rename(TempPath, OutPath);
if (EC)
return llvm::errorCodeToError(EC);
// If everything went well, we already moved the file to another name. So
// don't delete the file, as the name might be taken by another file.
RemoveOnFail.release();
return llvm::ErrorSuccess();
}
// Uses disk as a storage for index shards. Creates a directory called
// ".clangd/index/" under the path provided during construction.
class DiskBackedIndexStorage : public BackgroundIndexStorage {
@ -100,9 +73,12 @@ public:
llvm::Error storeShard(llvm::StringRef ShardIdentifier,
IndexFileOut Shard) const override {
return writeAtomically(
getShardPathFromFilePath(DiskShardRoot, ShardIdentifier),
[&Shard](llvm::raw_ostream &OS) { OS << Shard; });
auto ShardPath = getShardPathFromFilePath(DiskShardRoot, ShardIdentifier);
return llvm::writeFileAtomically(ShardPath + ".tmp.%%%%%%%%", ShardPath,
[&Shard](llvm::raw_ostream &OS) {
OS << Shard;
return llvm::Error::success();
});
}
};

View File

@ -84,6 +84,7 @@
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/ErrorOr.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/FileUtilities.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Timer.h"
#include "llvm/Support/VirtualFileSystem.h"
@ -2301,26 +2302,19 @@ bool ASTUnit::Save(StringRef File) {
SmallString<128> TempPath;
TempPath = File;
TempPath += "-%%%%%%%%";
int fd;
if (llvm::sys::fs::createUniqueFile(TempPath, fd, TempPath))
return true;
// FIXME: Can we somehow regenerate the stat cache here, or do we need to
// unconditionally create a stat cache when we parse the file?
llvm::raw_fd_ostream Out(fd, /*shouldClose=*/true);
serialize(Out);
Out.close();
if (Out.has_error()) {
Out.clear_error();
if (llvm::Error Err = llvm::writeFileAtomically(
TempPath, File, [this](llvm::raw_ostream &Out) {
return serialize(Out) ? llvm::make_error<llvm::StringError>(
"ASTUnit serialization failed",
llvm::inconvertibleErrorCode())
: llvm::Error::success();
})) {
consumeError(std::move(Err));
return true;
}
if (llvm::sys::fs::rename(TempPath, File)) {
llvm::sys::fs::remove(TempPath);
return true;
}
return false;
}

View File

@ -10,7 +10,6 @@
//
//===----------------------------------------------------------------------===//
#include "ASTReaderInternals.h"
#include "clang/Basic/FileManager.h"
#include "clang/Lex/HeaderSearch.h"
@ -21,10 +20,12 @@
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Bitstream/BitstreamReader.h"
#include "llvm/Bitstream/BitstreamWriter.h"
#include "llvm/Support/DJB.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/FileUtilities.h"
#include "llvm/Support/LockFileManager.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/OnDiskHashTable.h"
@ -912,37 +913,9 @@ GlobalModuleIndex::writeIndex(FileManager &FileMgr,
"failed writing index");
}
// Write the global index file to a temporary file.
llvm::SmallString<128> IndexTmpPath;
int TmpFD;
if (llvm::sys::fs::createUniqueFile(IndexPath + "-%%%%%%%%", TmpFD,
IndexTmpPath))
return llvm::createStringError(std::errc::io_error,
"failed creating unique file");
// Open the temporary global index file for output.
llvm::raw_fd_ostream Out(TmpFD, true);
if (Out.has_error())
return llvm::createStringError(Out.error(), "failed outputting to stream");
// Write the index.
Out.write(OutputBuffer.data(), OutputBuffer.size());
Out.close();
if (Out.has_error())
return llvm::createStringError(Out.error(), "failed writing to stream");
// Remove the old index file. It isn't relevant any more.
llvm::sys::fs::remove(IndexPath);
// Rename the newly-written index file to the proper name.
if (std::error_code Err = llvm::sys::fs::rename(IndexTmpPath, IndexPath)) {
// Remove the file on failure, don't check whether removal succeeded.
llvm::sys::fs::remove(IndexTmpPath);
return llvm::createStringError(Err, "failed renaming file \"%s\" to \"%s\"",
IndexTmpPath.c_str(), IndexPath.c_str());
}
return llvm::Error::success();
return llvm::writeFileAtomically(
(IndexPath + "-%%%%%%%%").str(), IndexPath,
llvm::StringRef(OutputBuffer.data(), OutputBuffer.size()));
}
namespace {

View File

@ -22,6 +22,7 @@
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/FileUtilities.h"
#include "llvm/Support/raw_ostream.h"
#include "Acceptor.h"
#include "LLDBServerUtilities.h"
@ -103,29 +104,34 @@ static Status save_socket_id_to_file(const std::string &socket_id,
llvm::SmallString<64> temp_file_path;
temp_file_spec.AppendPathComponent("port-file.%%%%%%");
int FD;
auto err_code = llvm::sys::fs::createUniqueFile(temp_file_spec.GetPath(), FD,
temp_file_path);
if (err_code)
return Status("Failed to create temp file: %s", err_code.message().c_str());
llvm::FileRemover tmp_file_remover(temp_file_path);
Status status;
if (auto Err =
handleErrors(llvm::writeFileAtomically(
temp_file_path, temp_file_spec.GetPath(), socket_id),
[&status, &temp_file_path,
&file_spec](const AtomicFileWriteError &E) {
std::string ErrorMsgBuffer;
llvm::raw_string_ostream S(ErrorMsgBuffer);
E.log(S);
{
llvm::raw_fd_ostream temp_file(FD, true);
temp_file << socket_id;
temp_file.close();
if (temp_file.has_error())
return Status("Failed to write to port file.");
switch (E.Error) {
case atomic_write_error::failed_to_create_uniq_file:
status = Status("Failed to create temp file: %s",
ErrorMsgBuffer.c_str());
case atomic_write_error::output_stream_error:
status = Status("Failed to write to port file.");
case atomic_write_error::failed_to_rename_temp_file:
status = Status("Failed to rename file %s to %s: %s",
ErrorMsgBuffer.c_str(),
file_spec.GetPath().c_str(),
ErrorMsgBuffer.c_str());
}
})) {
return Status("Failed to atomically write file %s",
file_spec.GetPath().c_str());
}
err_code = llvm::sys::fs::rename(temp_file_path, file_spec.GetPath());
if (err_code)
return Status("Failed to rename file %s to %s: %s", temp_file_path.c_str(),
file_spec.GetPath().c_str(), err_code.message().c_str());
tmp_file_remover.releaseFile();
return Status();
return status;
}
// main

View File

@ -16,6 +16,7 @@
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
@ -75,10 +76,40 @@ namespace llvm {
void releaseFile() { DeleteIt = false; }
};
enum class atomic_write_error {
failed_to_create_uniq_file = 0,
output_stream_error,
failed_to_rename_temp_file
};
class AtomicFileWriteError : public llvm::ErrorInfo<AtomicFileWriteError> {
public:
AtomicFileWriteError(atomic_write_error Error) : Error(Error) {}
void log(raw_ostream &OS) const override;
const atomic_write_error Error;
static char ID;
private:
// Users are not expected to use error_code.
std::error_code convertToErrorCode() const override {
return llvm::inconvertibleErrorCode();
}
};
// atomic_write_error + whatever the Writer can return
/// Creates a unique file with name according to the given \p TempPathModel,
/// writes content of \p Buffer to the file and renames it to \p FinalPath.
///
/// \returns \c AtomicFileWriteError in case of error.
llvm::Error writeFileAtomically(StringRef TempPathModel, StringRef FinalPath,
StringRef Buffer);
llvm::Error
writeFileAtomically(StringRef TempPathModel, StringRef FinalPath,
std::function<llvm::Error(llvm::raw_ostream &)> Writer);
} // End llvm namespace
#endif

View File

@ -39,6 +39,7 @@
#include "llvm/Support/CachePruning.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/FileUtilities.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/SHA1.h"
#include "llvm/Support/SmallVectorMemoryBuffer.h"
@ -368,23 +369,26 @@ public:
// Write to a temporary to avoid race condition
SmallString<128> TempFilename;
SmallString<128> CachePath(EntryPath);
int TempFD;
llvm::sys::path::remove_filename(CachePath);
sys::path::append(TempFilename, CachePath, "Thin-%%%%%%.tmp.o");
std::error_code EC =
sys::fs::createUniqueFile(TempFilename, TempFD, TempFilename);
if (EC) {
errs() << "Error: " << EC.message() << "\n";
report_fatal_error("ThinLTO: Can't get a temporary file");
if (auto Err = handleErrors(
llvm::writeFileAtomically(TempFilename, EntryPath,
OutputBuffer.getBuffer()),
[](const llvm::AtomicFileWriteError &E) {
std::string ErrorMsgBuffer;
llvm::raw_string_ostream S(ErrorMsgBuffer);
E.log(S);
if (E.Error ==
llvm::atomic_write_error::failed_to_create_uniq_file) {
errs() << "Error: " << ErrorMsgBuffer << "\n";
report_fatal_error("ThinLTO: Can't get a temporary file");
}
})) {
// FIXME
consumeError(std::move(Err));
}
{
raw_fd_ostream OS(TempFD, /* ShouldClose */ true);
OS << OutputBuffer.getBuffer();
}
// Rename temp file to final destination; rename is atomic
EC = sys::fs::rename(TempFilename, EntryPath);
if (EC)
sys::fs::remove(TempFilename);
}
};

View File

@ -12,7 +12,9 @@
//===----------------------------------------------------------------------===//
#include "llvm/Support/FileUtilities.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/ErrorOr.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
@ -266,36 +268,65 @@ int llvm::DiffFilesWithTolerance(StringRef NameA,
return CompareFailed;
}
Error llvm::writeFileAtomically(StringRef TempPathModel, StringRef FinalPath,
StringRef Buffer) {
void llvm::AtomicFileWriteError::log(raw_ostream &OS) const {
OS << "atomic_write_error: ";
switch (Error) {
case atomic_write_error::failed_to_create_uniq_file:
OS << "failed_to_create_uniq_file";
return;
case atomic_write_error::output_stream_error:
OS << "output_stream_error";
return;
case atomic_write_error::failed_to_rename_temp_file:
OS << "failed_to_rename_temp_file";
return;
}
llvm_unreachable("unknown atomic_write_error value in "
"failed_to_rename_temp_file::log()");
}
llvm::Error llvm::writeFileAtomically(StringRef TempPathModel,
StringRef FinalPath, StringRef Buffer) {
return writeFileAtomically(TempPathModel, FinalPath,
[&Buffer](llvm::raw_ostream &OS) {
OS.write(Buffer.data(), Buffer.size());
return llvm::Error::success();
});
}
llvm::Error llvm::writeFileAtomically(
StringRef TempPathModel, StringRef FinalPath,
std::function<llvm::Error(llvm::raw_ostream &)> Writer) {
SmallString<128> GeneratedUniqPath;
int TempFD;
if (const std::error_code Error = sys::fs::createUniqueFile(
TempPathModel.str(), TempFD, GeneratedUniqPath)) {
return createStringError(
Error, "failed to create temporary file with model \"%s\"",
TempPathModel.str().c_str());
if (sys::fs::createUniqueFile(TempPathModel.str(), TempFD,
GeneratedUniqPath)) {
return llvm::make_error<AtomicFileWriteError>(
atomic_write_error::failed_to_create_uniq_file);
}
llvm::FileRemover RemoveTmpFileOnFail(GeneratedUniqPath);
raw_fd_ostream OS(TempFD, /*shouldClose=*/true);
OS.write(Buffer.data(), Buffer.size());
OS.close();
TempFD = -1;
if (llvm::Error Err = Writer(OS)) {
return Err;
}
OS.close();
if (OS.has_error()) {
const std::error_code Error = OS.error();
OS.clear_error();
return createStringError(Error, "failed to write to \"%s\"",
GeneratedUniqPath.c_str());
return llvm::make_error<AtomicFileWriteError>(
atomic_write_error::output_stream_error);
}
if (const std::error_code Error =
sys::fs::rename(/*from=*/GeneratedUniqPath.c_str(),
/*to=*/FinalPath.str().c_str())) {
return createStringError(Error, "failed to rename file \"%s\" to \"%s\"",
GeneratedUniqPath.c_str(),
FinalPath.str().c_str());
return llvm::make_error<AtomicFileWriteError>(
atomic_write_error::failed_to_rename_temp_file);
}
RemoveTmpFileOnFail.releaseFile();
return Error::success();
}
char llvm::AtomicFileWriteError::ID;