Home | History | Annotate | Download | only in Support
      1 //===- FileOutputBuffer.cpp - File Output Buffer ----------------*- C++ -*-===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 //
     10 // Utility for creating a in-memory buffer that will be written to a file.
     11 //
     12 //===----------------------------------------------------------------------===//
     13 
     14 #include "llvm/Support/FileOutputBuffer.h"
     15 #include "llvm/ADT/STLExtras.h"
     16 #include "llvm/ADT/SmallString.h"
     17 #include "llvm/Support/Errc.h"
     18 #include "llvm/Support/Signals.h"
     19 #include <system_error>
     20 
     21 #if !defined(_MSC_VER) && !defined(__MINGW32__)
     22 #include <unistd.h>
     23 #else
     24 #include <io.h>
     25 #endif
     26 
     27 using llvm::sys::fs::mapped_file_region;
     28 
     29 namespace llvm {
     30 FileOutputBuffer::FileOutputBuffer(std::unique_ptr<mapped_file_region> R,
     31                                    StringRef Path, StringRef TmpPath)
     32     : Region(std::move(R)), FinalPath(Path), TempPath(TmpPath) {}
     33 
     34 FileOutputBuffer::~FileOutputBuffer() {
     35   sys::fs::remove(Twine(TempPath));
     36 }
     37 
     38 ErrorOr<std::unique_ptr<FileOutputBuffer>>
     39 FileOutputBuffer::create(StringRef FilePath, size_t Size, unsigned Flags) {
     40   // If file already exists, it must be a regular file (to be mappable).
     41   sys::fs::file_status Stat;
     42   std::error_code EC = sys::fs::status(FilePath, Stat);
     43   switch (Stat.type()) {
     44     case sys::fs::file_type::file_not_found:
     45       // If file does not exist, we'll create one.
     46       break;
     47     case sys::fs::file_type::regular_file: {
     48         // If file is not currently writable, error out.
     49         // FIXME: There is no sys::fs:: api for checking this.
     50         // FIXME: In posix, you use the access() call to check this.
     51       }
     52       break;
     53     default:
     54       if (EC)
     55         return EC;
     56       else
     57         return make_error_code(errc::operation_not_permitted);
     58   }
     59 
     60   // Delete target file.
     61   EC = sys::fs::remove(FilePath);
     62   if (EC)
     63     return EC;
     64 
     65   unsigned Mode = sys::fs::all_read | sys::fs::all_write;
     66   // If requested, make the output file executable.
     67   if (Flags & F_executable)
     68     Mode |= sys::fs::all_exe;
     69 
     70   // Create new file in same directory but with random name.
     71   SmallString<128> TempFilePath;
     72   int FD;
     73   EC = sys::fs::createUniqueFile(Twine(FilePath) + ".tmp%%%%%%%", FD,
     74                                  TempFilePath, Mode);
     75   if (EC)
     76     return EC;
     77 
     78   sys::RemoveFileOnSignal(TempFilePath);
     79 
     80 #ifndef LLVM_ON_WIN32
     81   // On Windows, CreateFileMapping (the mmap function on Windows)
     82   // automatically extends the underlying file. We don't need to
     83   // extend the file beforehand. _chsize (ftruncate on Windows) is
     84   // pretty slow just like it writes specified amount of bytes,
     85   // so we should avoid calling that.
     86   EC = sys::fs::resize_file(FD, Size);
     87   if (EC)
     88     return EC;
     89 #endif
     90 
     91   auto MappedFile = llvm::make_unique<mapped_file_region>(
     92       FD, mapped_file_region::readwrite, Size, 0, EC);
     93   int Ret = close(FD);
     94   if (EC)
     95     return EC;
     96   if (Ret)
     97     return std::error_code(errno, std::generic_category());
     98 
     99   std::unique_ptr<FileOutputBuffer> Buf(
    100       new FileOutputBuffer(std::move(MappedFile), FilePath, TempFilePath));
    101   return std::move(Buf);
    102 }
    103 
    104 std::error_code FileOutputBuffer::commit() {
    105   // Unmap buffer, letting OS flush dirty pages to file on disk.
    106   Region.reset();
    107 
    108 
    109   // Rename file to final name.
    110   std::error_code EC = sys::fs::rename(Twine(TempPath), Twine(FinalPath));
    111   sys::DontRemoveFileOnSignal(TempPath);
    112   return EC;
    113 }
    114 } // namespace
    115