17ae0e2c9SDimitry Andric //===- FileOutputBuffer.cpp - File Output Buffer ----------------*- C++ -*-===//
27ae0e2c9SDimitry Andric //
37ae0e2c9SDimitry Andric // The LLVM Compiler Infrastructure
47ae0e2c9SDimitry Andric //
57ae0e2c9SDimitry Andric // This file is distributed under the University of Illinois Open Source
67ae0e2c9SDimitry Andric // License. See LICENSE.TXT for details.
77ae0e2c9SDimitry Andric //
87ae0e2c9SDimitry Andric //===----------------------------------------------------------------------===//
97ae0e2c9SDimitry Andric //
107ae0e2c9SDimitry Andric // Utility for creating a in-memory buffer that will be written to a file.
117ae0e2c9SDimitry Andric //
127ae0e2c9SDimitry Andric //===----------------------------------------------------------------------===//
137ae0e2c9SDimitry Andric
1439d628a0SDimitry Andric #include "llvm/Support/FileOutputBuffer.h"
15ff0cc061SDimitry Andric #include "llvm/ADT/STLExtras.h"
16ff0cc061SDimitry Andric #include "llvm/ADT/SmallString.h"
17ff0cc061SDimitry Andric #include "llvm/Support/Errc.h"
182cab237bSDimitry Andric #include "llvm/Support/Memory.h"
19f1a29dd3SDimitry Andric #include "llvm/Support/Path.h"
2091bc56edSDimitry Andric #include <system_error>
217ae0e2c9SDimitry Andric
2239d628a0SDimitry Andric #if !defined(_MSC_VER) && !defined(__MINGW32__)
2339d628a0SDimitry Andric #include <unistd.h>
2439d628a0SDimitry Andric #else
2539d628a0SDimitry Andric #include <io.h>
2639d628a0SDimitry Andric #endif
2739d628a0SDimitry Andric
282cab237bSDimitry Andric using namespace llvm;
292cab237bSDimitry Andric using namespace llvm::sys;
307ae0e2c9SDimitry Andric
312cab237bSDimitry Andric namespace {
322cab237bSDimitry Andric // A FileOutputBuffer which creates a temporary file in the same directory
332cab237bSDimitry Andric // as the final output file. The final output file is atomically replaced
342cab237bSDimitry Andric // with the temporary file on commit().
352cab237bSDimitry Andric class OnDiskBuffer : public FileOutputBuffer {
362cab237bSDimitry Andric public:
OnDiskBuffer(StringRef Path,fs::TempFile Temp,std::unique_ptr<fs::mapped_file_region> Buf)372cab237bSDimitry Andric OnDiskBuffer(StringRef Path, fs::TempFile Temp,
382cab237bSDimitry Andric std::unique_ptr<fs::mapped_file_region> Buf)
392cab237bSDimitry Andric : FileOutputBuffer(Path), Buffer(std::move(Buf)), Temp(std::move(Temp)) {}
407ae0e2c9SDimitry Andric
getBufferStart() const412cab237bSDimitry Andric uint8_t *getBufferStart() const override { return (uint8_t *)Buffer->data(); }
422cab237bSDimitry Andric
getBufferEnd() const432cab237bSDimitry Andric uint8_t *getBufferEnd() const override {
442cab237bSDimitry Andric return (uint8_t *)Buffer->data() + Buffer->size();
452cab237bSDimitry Andric }
462cab237bSDimitry Andric
getBufferSize() const472cab237bSDimitry Andric size_t getBufferSize() const override { return Buffer->size(); }
482cab237bSDimitry Andric
commit()492cab237bSDimitry Andric Error commit() override {
502cab237bSDimitry Andric // Unmap buffer, letting OS flush dirty pages to file on disk.
512cab237bSDimitry Andric Buffer.reset();
522cab237bSDimitry Andric
532cab237bSDimitry Andric // Atomically replace the existing file with the new one.
542cab237bSDimitry Andric return Temp.keep(FinalPath);
552cab237bSDimitry Andric }
562cab237bSDimitry Andric
~OnDiskBuffer()572cab237bSDimitry Andric ~OnDiskBuffer() override {
58d88c1a5aSDimitry Andric // Close the mapping before deleting the temp file, so that the removal
59d88c1a5aSDimitry Andric // succeeds.
602cab237bSDimitry Andric Buffer.reset();
612cab237bSDimitry Andric consumeError(Temp.discard());
627ae0e2c9SDimitry Andric }
637ae0e2c9SDimitry Andric
discard()64*b5893f02SDimitry Andric void discard() override {
65*b5893f02SDimitry Andric // Delete the temp file if it still was open, but keeping the mapping
66*b5893f02SDimitry Andric // active.
67*b5893f02SDimitry Andric consumeError(Temp.discard());
68*b5893f02SDimitry Andric }
69*b5893f02SDimitry Andric
702cab237bSDimitry Andric private:
712cab237bSDimitry Andric std::unique_ptr<fs::mapped_file_region> Buffer;
722cab237bSDimitry Andric fs::TempFile Temp;
732cab237bSDimitry Andric };
742cab237bSDimitry Andric
752cab237bSDimitry Andric // A FileOutputBuffer which keeps data in memory and writes to the final
762cab237bSDimitry Andric // output file on commit(). This is used only when we cannot use OnDiskBuffer.
772cab237bSDimitry Andric class InMemoryBuffer : public FileOutputBuffer {
782cab237bSDimitry Andric public:
InMemoryBuffer(StringRef Path,MemoryBlock Buf,unsigned Mode)792cab237bSDimitry Andric InMemoryBuffer(StringRef Path, MemoryBlock Buf, unsigned Mode)
802cab237bSDimitry Andric : FileOutputBuffer(Path), Buffer(Buf), Mode(Mode) {}
812cab237bSDimitry Andric
getBufferStart() const822cab237bSDimitry Andric uint8_t *getBufferStart() const override { return (uint8_t *)Buffer.base(); }
832cab237bSDimitry Andric
getBufferEnd() const842cab237bSDimitry Andric uint8_t *getBufferEnd() const override {
852cab237bSDimitry Andric return (uint8_t *)Buffer.base() + Buffer.size();
867ae0e2c9SDimitry Andric }
877ae0e2c9SDimitry Andric
getBufferSize() const882cab237bSDimitry Andric size_t getBufferSize() const override { return Buffer.size(); }
897ae0e2c9SDimitry Andric
commit()902cab237bSDimitry Andric Error commit() override {
914ba319b5SDimitry Andric using namespace sys::fs;
92f1a29dd3SDimitry Andric int FD;
932cab237bSDimitry Andric std::error_code EC;
944ba319b5SDimitry Andric if (auto EC =
954ba319b5SDimitry Andric openFileForWrite(FinalPath, FD, CD_CreateAlways, OF_None, Mode))
962cab237bSDimitry Andric return errorCodeToError(EC);
972cab237bSDimitry Andric raw_fd_ostream OS(FD, /*shouldClose=*/true, /*unbuffered=*/true);
982cab237bSDimitry Andric OS << StringRef((const char *)Buffer.base(), Buffer.size());
992cab237bSDimitry Andric return Error::success();
100f1a29dd3SDimitry Andric }
101f1a29dd3SDimitry Andric
1022cab237bSDimitry Andric private:
1032cab237bSDimitry Andric OwningMemoryBlock Buffer;
1042cab237bSDimitry Andric unsigned Mode;
1052cab237bSDimitry Andric };
1062cab237bSDimitry Andric } // namespace
1077ae0e2c9SDimitry Andric
1082cab237bSDimitry Andric static Expected<std::unique_ptr<InMemoryBuffer>>
createInMemoryBuffer(StringRef Path,size_t Size,unsigned Mode)1092cab237bSDimitry Andric createInMemoryBuffer(StringRef Path, size_t Size, unsigned Mode) {
1102cab237bSDimitry Andric std::error_code EC;
1112cab237bSDimitry Andric MemoryBlock MB = Memory::allocateMappedMemory(
1122cab237bSDimitry Andric Size, nullptr, sys::Memory::MF_READ | sys::Memory::MF_WRITE, EC);
1132cab237bSDimitry Andric if (EC)
1142cab237bSDimitry Andric return errorCodeToError(EC);
1152cab237bSDimitry Andric return llvm::make_unique<InMemoryBuffer>(Path, MB, Mode);
1162cab237bSDimitry Andric }
1172cab237bSDimitry Andric
1182cab237bSDimitry Andric static Expected<std::unique_ptr<OnDiskBuffer>>
createOnDiskBuffer(StringRef Path,size_t Size,bool InitExisting,unsigned Mode)1194ba319b5SDimitry Andric createOnDiskBuffer(StringRef Path, size_t Size, bool InitExisting,
1204ba319b5SDimitry Andric unsigned Mode) {
1212cab237bSDimitry Andric Expected<fs::TempFile> FileOrErr =
1222cab237bSDimitry Andric fs::TempFile::create(Path + ".tmp%%%%%%%", Mode);
1232cab237bSDimitry Andric if (!FileOrErr)
1242cab237bSDimitry Andric return FileOrErr.takeError();
1252cab237bSDimitry Andric fs::TempFile File = std::move(*FileOrErr);
1267d523365SDimitry Andric
1274ba319b5SDimitry Andric if (InitExisting) {
1284ba319b5SDimitry Andric if (auto EC = sys::fs::copy_file(Path, File.FD))
1294ba319b5SDimitry Andric return errorCodeToError(EC);
1304ba319b5SDimitry Andric } else {
1314ba319b5SDimitry Andric #ifndef _WIN32
132ff0cc061SDimitry Andric // On Windows, CreateFileMapping (the mmap function on Windows)
133ff0cc061SDimitry Andric // automatically extends the underlying file. We don't need to
134ff0cc061SDimitry Andric // extend the file beforehand. _chsize (ftruncate on Windows) is
135ff0cc061SDimitry Andric // pretty slow just like it writes specified amount of bytes,
1362cab237bSDimitry Andric // so we should avoid calling that function.
1372cab237bSDimitry Andric if (auto EC = fs::resize_file(File.FD, Size)) {
1382cab237bSDimitry Andric consumeError(File.discard());
1392cab237bSDimitry Andric return errorCodeToError(EC);
1402cab237bSDimitry Andric }
141ff0cc061SDimitry Andric #endif
1424ba319b5SDimitry Andric }
1437ae0e2c9SDimitry Andric
1442cab237bSDimitry Andric // Mmap it.
145f1a29dd3SDimitry Andric std::error_code EC;
1462cab237bSDimitry Andric auto MappedFile = llvm::make_unique<fs::mapped_file_region>(
1472cab237bSDimitry Andric File.FD, fs::mapped_file_region::readwrite, Size, 0, EC);
1482cab237bSDimitry Andric if (EC) {
1492cab237bSDimitry Andric consumeError(File.discard());
1502cab237bSDimitry Andric return errorCodeToError(EC);
1512cab237bSDimitry Andric }
1522cab237bSDimitry Andric return llvm::make_unique<OnDiskBuffer>(Path, std::move(File),
1532cab237bSDimitry Andric std::move(MappedFile));
154f1a29dd3SDimitry Andric }
155f1a29dd3SDimitry Andric
1562cab237bSDimitry Andric // Create an instance of FileOutputBuffer.
1572cab237bSDimitry Andric Expected<std::unique_ptr<FileOutputBuffer>>
create(StringRef Path,size_t Size,unsigned Flags)1582cab237bSDimitry Andric FileOutputBuffer::create(StringRef Path, size_t Size, unsigned Flags) {
1592cab237bSDimitry Andric unsigned Mode = fs::all_read | fs::all_write;
1602cab237bSDimitry Andric if (Flags & F_executable)
1612cab237bSDimitry Andric Mode |= fs::all_exe;
1622cab237bSDimitry Andric
1632cab237bSDimitry Andric fs::file_status Stat;
1642cab237bSDimitry Andric fs::status(Path, Stat);
1652cab237bSDimitry Andric
1664ba319b5SDimitry Andric if ((Flags & F_modify) && Size == size_t(-1)) {
1674ba319b5SDimitry Andric if (Stat.type() == fs::file_type::regular_file)
1684ba319b5SDimitry Andric Size = Stat.getSize();
1694ba319b5SDimitry Andric else if (Stat.type() == fs::file_type::file_not_found)
1704ba319b5SDimitry Andric return errorCodeToError(errc::no_such_file_or_directory);
1714ba319b5SDimitry Andric else
1724ba319b5SDimitry Andric return errorCodeToError(errc::invalid_argument);
1734ba319b5SDimitry Andric }
1744ba319b5SDimitry Andric
1752cab237bSDimitry Andric // Usually, we want to create OnDiskBuffer to create a temporary file in
1762cab237bSDimitry Andric // the same directory as the destination file and atomically replaces it
1772cab237bSDimitry Andric // by rename(2).
1782cab237bSDimitry Andric //
1792cab237bSDimitry Andric // However, if the destination file is a special file, we don't want to
1802cab237bSDimitry Andric // use rename (e.g. we don't want to replace /dev/null with a regular
1812cab237bSDimitry Andric // file.) If that's the case, we create an in-memory buffer, open the
1822cab237bSDimitry Andric // destination file and write to it on commit().
1832cab237bSDimitry Andric switch (Stat.type()) {
1842cab237bSDimitry Andric case fs::file_type::directory_file:
1852cab237bSDimitry Andric return errorCodeToError(errc::is_a_directory);
1862cab237bSDimitry Andric case fs::file_type::regular_file:
1872cab237bSDimitry Andric case fs::file_type::file_not_found:
1882cab237bSDimitry Andric case fs::file_type::status_error:
1894ba319b5SDimitry Andric return createOnDiskBuffer(Path, Size, !!(Flags & F_modify), Mode);
1902cab237bSDimitry Andric default:
1912cab237bSDimitry Andric return createInMemoryBuffer(Path, Size, Mode);
1927ae0e2c9SDimitry Andric }
1932cab237bSDimitry Andric }
194