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 
     16 #include "llvm/ADT/OwningPtr.h"
     17 #include "llvm/ADT/SmallVector.h"
     18 #include "llvm/Support/FileSystem.h"
     19 #include "llvm/Support/raw_ostream.h"
     20 #include "llvm/Support/system_error.h"
     21 
     22 
     23 namespace llvm {
     24 
     25 
     26 FileOutputBuffer::FileOutputBuffer(uint8_t *Start, uint8_t *End,
     27                                   StringRef Path, StringRef TmpPath)
     28   : BufferStart(Start), BufferEnd(End) {
     29   FinalPath.assign(Path);
     30   TempPath.assign(TmpPath);
     31 }
     32 
     33 
     34 FileOutputBuffer::~FileOutputBuffer() {
     35   // If not already commited, delete buffer and remove temp file.
     36   if ( BufferStart != NULL ) {
     37     sys::fs::unmap_file_pages((void*)BufferStart, getBufferSize());
     38     bool Existed;
     39     sys::fs::remove(Twine(TempPath), Existed);
     40   }
     41 }
     42 
     43 
     44 error_code FileOutputBuffer::create(StringRef FilePath,
     45                                     size_t Size,
     46                                     OwningPtr<FileOutputBuffer> &Result,
     47                                     unsigned Flags) {
     48   // If file already exists, it must be a regular file (to be mappable).
     49   sys::fs::file_status Stat;
     50   error_code EC = sys::fs::status(FilePath, Stat);
     51   switch (Stat.type()) {
     52     case sys::fs::file_type::file_not_found:
     53       // If file does not exist, we'll create one.
     54       break;
     55     case sys::fs::file_type::regular_file: {
     56         // If file is not currently writable, error out.
     57         // FIXME: There is no sys::fs:: api for checking this.
     58         // FIXME: In posix, you use the access() call to check this.
     59       }
     60       break;
     61     default:
     62       if (EC)
     63         return EC;
     64       else
     65         return make_error_code(errc::operation_not_permitted);
     66   }
     67 
     68   // Delete target file.
     69   bool Existed;
     70   EC = sys::fs::remove(FilePath, Existed);
     71   if (EC)
     72     return EC;
     73 
     74   // Create new file in same directory but with random name.
     75   SmallString<128> TempFilePath;
     76   int FD;
     77   EC = sys::fs::unique_file(Twine(FilePath) + ".tmp%%%%%%%",
     78                                                 FD, TempFilePath, false, 0644);
     79   if (EC)
     80     return EC;
     81 
     82   // The unique_file() interface leaks lower layers and returns a file
     83   // descriptor.  There is no way to directly close it, so use this hack
     84   // to hand it off to raw_fd_ostream to close for us.
     85   {
     86     raw_fd_ostream Dummy(FD, /*shouldClose=*/true);
     87   }
     88 
     89   // Resize file to requested initial size
     90   EC = sys::fs::resize_file(Twine(TempFilePath), Size);
     91   if (EC)
     92     return EC;
     93 
     94   // If requested, make the output file executable.
     95   if ( Flags & F_executable ) {
     96     sys::fs::file_status Stat2;
     97     EC = sys::fs::status(Twine(TempFilePath), Stat2);
     98     if (EC)
     99       return EC;
    100 
    101     sys::fs::perms new_perms = Stat2.permissions();
    102     if ( new_perms & sys::fs::owner_read )
    103       new_perms |= sys::fs::owner_exe;
    104     if ( new_perms & sys::fs::group_read )
    105       new_perms |= sys::fs::group_exe;
    106     if ( new_perms & sys::fs::others_read )
    107       new_perms |= sys::fs::others_exe;
    108     new_perms |= sys::fs::add_perms;
    109     EC = sys::fs::permissions(Twine(TempFilePath), new_perms);
    110     if (EC)
    111       return EC;
    112   }
    113 
    114   // Memory map new file.
    115   void *Base;
    116   EC = sys::fs::map_file_pages(Twine(TempFilePath), 0, Size, true, Base);
    117   if (EC)
    118     return EC;
    119 
    120   // Create FileOutputBuffer object to own mapped range.
    121   uint8_t *Start = reinterpret_cast<uint8_t*>(Base);
    122   Result.reset(new FileOutputBuffer(Start, Start+Size, FilePath, TempFilePath));
    123 
    124   return error_code::success();
    125 }
    126 
    127 
    128 error_code FileOutputBuffer::commit(int64_t NewSmallerSize) {
    129   // Unmap buffer, letting OS flush dirty pages to file on disk.
    130   void *Start = reinterpret_cast<void*>(BufferStart);
    131   error_code EC = sys::fs::unmap_file_pages(Start, getBufferSize());
    132   if (EC)
    133     return EC;
    134 
    135   // If requested, resize file as part of commit.
    136   if ( NewSmallerSize != -1 ) {
    137     EC = sys::fs::resize_file(Twine(TempPath), NewSmallerSize);
    138     if (EC)
    139       return EC;
    140   }
    141 
    142   // Rename file to final name.
    143   return sys::fs::rename(Twine(TempPath), Twine(FinalPath));
    144 }
    145 
    146 
    147 } // namespace
    148 
    149