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