1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-25 20:23:11 +01:00
llvm-mirror/lib/Support/FileOutputBuffer.cpp
Reid Kleckner a24c77d404 Fix a real temp file leak in FileOutputBuffer
If we failed to commit the buffer but did not die to a signal, the temp
file would remain on disk on Windows. Having an open file mapping and
file handle prevents the file from being deleted. I am choosing not to
add an assertion of success on the temp file removal, since virus
scanners and other environmental things can often cause removal to fail
in real world tools.

Also fix more temp file leaks in unit tests.

llvm-svn: 280445
2016-09-02 01:10:53 +00:00

118 lines
3.6 KiB
C++

//===- FileOutputBuffer.cpp - File Output Buffer ----------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Utility for creating a in-memory buffer that will be written to a file.
//
//===----------------------------------------------------------------------===//
#include "llvm/Support/FileOutputBuffer.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/Signals.h"
#include <system_error>
#if !defined(_MSC_VER) && !defined(__MINGW32__)
#include <unistd.h>
#else
#include <io.h>
#endif
using llvm::sys::fs::mapped_file_region;
namespace llvm {
FileOutputBuffer::FileOutputBuffer(std::unique_ptr<mapped_file_region> R,
StringRef Path, StringRef TmpPath)
: Region(std::move(R)), FinalPath(Path), TempPath(TmpPath) {}
FileOutputBuffer::~FileOutputBuffer() {
// Close the mapping before deleting the temp file, so that the removal
// succeeds.
Region.reset();
sys::fs::remove(Twine(TempPath));
}
ErrorOr<std::unique_ptr<FileOutputBuffer>>
FileOutputBuffer::create(StringRef FilePath, size_t Size, unsigned Flags) {
// If file already exists, it must be a regular file (to be mappable).
sys::fs::file_status Stat;
std::error_code EC = sys::fs::status(FilePath, Stat);
switch (Stat.type()) {
case sys::fs::file_type::file_not_found:
// If file does not exist, we'll create one.
break;
case sys::fs::file_type::regular_file: {
// If file is not currently writable, error out.
// FIXME: There is no sys::fs:: api for checking this.
// FIXME: In posix, you use the access() call to check this.
}
break;
default:
if (EC)
return EC;
else
return make_error_code(errc::operation_not_permitted);
}
// Delete target file.
EC = sys::fs::remove(FilePath);
if (EC)
return EC;
unsigned Mode = sys::fs::all_read | sys::fs::all_write;
// If requested, make the output file executable.
if (Flags & F_executable)
Mode |= sys::fs::all_exe;
// Create new file in same directory but with random name.
SmallString<128> TempFilePath;
int FD;
EC = sys::fs::createUniqueFile(Twine(FilePath) + ".tmp%%%%%%%", FD,
TempFilePath, Mode);
if (EC)
return EC;
sys::RemoveFileOnSignal(TempFilePath);
#ifndef LLVM_ON_WIN32
// On Windows, CreateFileMapping (the mmap function on Windows)
// automatically extends the underlying file. We don't need to
// extend the file beforehand. _chsize (ftruncate on Windows) is
// pretty slow just like it writes specified amount of bytes,
// so we should avoid calling that.
EC = sys::fs::resize_file(FD, Size);
if (EC)
return EC;
#endif
auto MappedFile = llvm::make_unique<mapped_file_region>(
FD, mapped_file_region::readwrite, Size, 0, EC);
int Ret = close(FD);
if (EC)
return EC;
if (Ret)
return std::error_code(errno, std::generic_category());
std::unique_ptr<FileOutputBuffer> Buf(
new FileOutputBuffer(std::move(MappedFile), FilePath, TempFilePath));
return std::move(Buf);
}
std::error_code FileOutputBuffer::commit() {
// Unmap buffer, letting OS flush dirty pages to file on disk.
Region.reset();
// Rename file to final name.
std::error_code EC = sys::fs::rename(Twine(TempPath), Twine(FinalPath));
sys::DontRemoveFileOnSignal(TempPath);
return EC;
}
} // namespace