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