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/Errc.h"
     15 #include "llvm/Support/FileOutputBuffer.h"
     16 #include "llvm/ADT/SmallVector.h"
     17 #include "llvm/Support/raw_ostream.h"
     18 #include <system_error>
     19 
     20 using llvm::sys::fs::mapped_file_region;
     21 
     22 namespace llvm {
     23 FileOutputBuffer::FileOutputBuffer(mapped_file_region * R,
     24                                    StringRef Path, StringRef TmpPath)
     25   : Region(R)
     26   , FinalPath(Path)
     27   , TempPath(TmpPath) {
     28 }
     29 
     30 FileOutputBuffer::~FileOutputBuffer() {
     31   sys::fs::remove(Twine(TempPath));
     32 }
     33 
     34 std::error_code
     35 FileOutputBuffer::create(StringRef FilePath, size_t Size,
     36                          std::unique_ptr<FileOutputBuffer> &Result,
     37                          unsigned Flags) {
     38   // If file already exists, it must be a regular file (to be mappable).
     39   sys::fs::file_status Stat;
     40   std::error_code EC = sys::fs::status(FilePath, Stat);
     41   switch (Stat.type()) {
     42     case sys::fs::file_type::file_not_found:
     43       // If file does not exist, we'll create one.
     44       break;
     45     case sys::fs::file_type::regular_file: {
     46         // If file is not currently writable, error out.
     47         // FIXME: There is no sys::fs:: api for checking this.
     48         // FIXME: In posix, you use the access() call to check this.
     49       }
     50       break;
     51     default:
     52       if (EC)
     53         return EC;
     54       else
     55         return make_error_code(errc::operation_not_permitted);
     56   }
     57 
     58   // Delete target file.
     59   EC = sys::fs::remove(FilePath);
     60   if (EC)
     61     return EC;
     62 
     63   unsigned Mode = sys::fs::all_read | sys::fs::all_write;
     64   // If requested, make the output file executable.
     65   if (Flags & F_executable)
     66     Mode |= sys::fs::all_exe;
     67 
     68   // Create new file in same directory but with random name.
     69   SmallString<128> TempFilePath;
     70   int FD;
     71   EC = sys::fs::createUniqueFile(Twine(FilePath) + ".tmp%%%%%%%", FD,
     72                                  TempFilePath, Mode);
     73   if (EC)
     74     return EC;
     75 
     76   std::unique_ptr<mapped_file_region> MappedFile(new mapped_file_region(
     77       FD, true, mapped_file_region::readwrite, Size, 0, EC));
     78   if (EC)
     79     return EC;
     80 
     81   Result.reset(new FileOutputBuffer(MappedFile.get(), FilePath, TempFilePath));
     82   if (Result)
     83     MappedFile.release();
     84 
     85   return std::error_code();
     86 }
     87 
     88 std::error_code FileOutputBuffer::commit(int64_t NewSmallerSize) {
     89   // Unmap buffer, letting OS flush dirty pages to file on disk.
     90   Region.reset(nullptr);
     91 
     92   // If requested, resize file as part of commit.
     93   if ( NewSmallerSize != -1 ) {
     94     std::error_code EC = sys::fs::resize_file(Twine(TempPath), NewSmallerSize);
     95     if (EC)
     96       return EC;
     97   }
     98 
     99   // Rename file to final name.
    100   return sys::fs::rename(Twine(TempPath), Twine(FinalPath));
    101 }
    102 } // namespace
    103