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
14ebe69fe11e48d322045d5949c83283927a0d790bStephen Hines#include "llvm/Support/FileOutputBuffer.h"
154c5e43da7792f75567b693105cc53e3f1992ad98Pirama Arumuga Nainar#include "llvm/ADT/STLExtras.h"
164c5e43da7792f75567b693105cc53e3f1992ad98Pirama Arumuga Nainar#include "llvm/ADT/SmallString.h"
174c5e43da7792f75567b693105cc53e3f1992ad98Pirama Arumuga Nainar#include "llvm/Support/Errc.h"
18c6a4f5e819217e1e12c458aed8e7b122e23a3a58Stephen Hines#include <system_error>
19adfe2637b839efe041165f27c9ad57e3befb2be0Nick Kledzik
20ebe69fe11e48d322045d5949c83283927a0d790bStephen Hines#if !defined(_MSC_VER) && !defined(__MINGW32__)
21ebe69fe11e48d322045d5949c83283927a0d790bStephen Hines#include <unistd.h>
22ebe69fe11e48d322045d5949c83283927a0d790bStephen Hines#else
23ebe69fe11e48d322045d5949c83283927a0d790bStephen Hines#include <io.h>
24ebe69fe11e48d322045d5949c83283927a0d790bStephen Hines#endif
25ebe69fe11e48d322045d5949c83283927a0d790bStephen Hines
266bc86018d189e9ab62d30ef8190bac961dd22248Michael J. Spencerusing llvm::sys::fs::mapped_file_region;
27adfe2637b839efe041165f27c9ad57e3befb2be0Nick Kledzik
28adfe2637b839efe041165f27c9ad57e3befb2be0Nick Kledziknamespace llvm {
2937ed9c199ca639565f6ce88105f9e39e898d82d0Stephen HinesFileOutputBuffer::FileOutputBuffer(std::unique_ptr<mapped_file_region> R,
306bc86018d189e9ab62d30ef8190bac961dd22248Michael J. Spencer                                   StringRef Path, StringRef TmpPath)
3137ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines    : Region(std::move(R)), FinalPath(Path), TempPath(TmpPath) {}
32adfe2637b839efe041165f27c9ad57e3befb2be0Nick Kledzik
33adfe2637b839efe041165f27c9ad57e3befb2be0Nick KledzikFileOutputBuffer::~FileOutputBuffer() {
3436b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines  sys::fs::remove(Twine(TempPath));
35adfe2637b839efe041165f27c9ad57e3befb2be0Nick Kledzik}
36adfe2637b839efe041165f27c9ad57e3befb2be0Nick Kledzik
37c6a4f5e819217e1e12c458aed8e7b122e23a3a58Stephen Hinesstd::error_code
38c6a4f5e819217e1e12c458aed8e7b122e23a3a58Stephen HinesFileOutputBuffer::create(StringRef FilePath, size_t Size,
39c6a4f5e819217e1e12c458aed8e7b122e23a3a58Stephen Hines                         std::unique_ptr<FileOutputBuffer> &Result,
40c6a4f5e819217e1e12c458aed8e7b122e23a3a58Stephen Hines                         unsigned Flags) {
41adfe2637b839efe041165f27c9ad57e3befb2be0Nick Kledzik  // If file already exists, it must be a regular file (to be mappable).
42adfe2637b839efe041165f27c9ad57e3befb2be0Nick Kledzik  sys::fs::file_status Stat;
43c6a4f5e819217e1e12c458aed8e7b122e23a3a58Stephen Hines  std::error_code EC = sys::fs::status(FilePath, Stat);
44adfe2637b839efe041165f27c9ad57e3befb2be0Nick Kledzik  switch (Stat.type()) {
45adfe2637b839efe041165f27c9ad57e3befb2be0Nick Kledzik    case sys::fs::file_type::file_not_found:
46adfe2637b839efe041165f27c9ad57e3befb2be0Nick Kledzik      // If file does not exist, we'll create one.
47adfe2637b839efe041165f27c9ad57e3befb2be0Nick Kledzik      break;
48adfe2637b839efe041165f27c9ad57e3befb2be0Nick Kledzik    case sys::fs::file_type::regular_file: {
49adfe2637b839efe041165f27c9ad57e3befb2be0Nick Kledzik        // If file is not currently writable, error out.
50adfe2637b839efe041165f27c9ad57e3befb2be0Nick Kledzik        // FIXME: There is no sys::fs:: api for checking this.
51adfe2637b839efe041165f27c9ad57e3befb2be0Nick Kledzik        // FIXME: In posix, you use the access() call to check this.
52adfe2637b839efe041165f27c9ad57e3befb2be0Nick Kledzik      }
53adfe2637b839efe041165f27c9ad57e3befb2be0Nick Kledzik      break;
54adfe2637b839efe041165f27c9ad57e3befb2be0Nick Kledzik    default:
55adfe2637b839efe041165f27c9ad57e3befb2be0Nick Kledzik      if (EC)
56adfe2637b839efe041165f27c9ad57e3befb2be0Nick Kledzik        return EC;
57adfe2637b839efe041165f27c9ad57e3befb2be0Nick Kledzik      else
58adfe2637b839efe041165f27c9ad57e3befb2be0Nick Kledzik        return make_error_code(errc::operation_not_permitted);
59adfe2637b839efe041165f27c9ad57e3befb2be0Nick Kledzik  }
60adfe2637b839efe041165f27c9ad57e3befb2be0Nick Kledzik
61adfe2637b839efe041165f27c9ad57e3befb2be0Nick Kledzik  // Delete target file.
6236b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines  EC = sys::fs::remove(FilePath);
63adfe2637b839efe041165f27c9ad57e3befb2be0Nick Kledzik  if (EC)
64adfe2637b839efe041165f27c9ad57e3befb2be0Nick Kledzik    return EC;
656bc86018d189e9ab62d30ef8190bac961dd22248Michael J. Spencer
6673e97d0f48cf9ed3093019064f6340617ac8de9eRafael Espindola  unsigned Mode = sys::fs::all_read | sys::fs::all_write;
6773e97d0f48cf9ed3093019064f6340617ac8de9eRafael Espindola  // If requested, make the output file executable.
6873e97d0f48cf9ed3093019064f6340617ac8de9eRafael Espindola  if (Flags & F_executable)
6973e97d0f48cf9ed3093019064f6340617ac8de9eRafael Espindola    Mode |= sys::fs::all_exe;
7073e97d0f48cf9ed3093019064f6340617ac8de9eRafael Espindola
71adfe2637b839efe041165f27c9ad57e3befb2be0Nick Kledzik  // Create new file in same directory but with random name.
72adfe2637b839efe041165f27c9ad57e3befb2be0Nick Kledzik  SmallString<128> TempFilePath;
73adfe2637b839efe041165f27c9ad57e3befb2be0Nick Kledzik  int FD;
7473e97d0f48cf9ed3093019064f6340617ac8de9eRafael Espindola  EC = sys::fs::createUniqueFile(Twine(FilePath) + ".tmp%%%%%%%", FD,
7573e97d0f48cf9ed3093019064f6340617ac8de9eRafael Espindola                                 TempFilePath, Mode);
76adfe2637b839efe041165f27c9ad57e3befb2be0Nick Kledzik  if (EC)
77adfe2637b839efe041165f27c9ad57e3befb2be0Nick Kledzik    return EC;
786bc86018d189e9ab62d30ef8190bac961dd22248Michael J. Spencer
794c5e43da7792f75567b693105cc53e3f1992ad98Pirama Arumuga Nainar#ifndef LLVM_ON_WIN32
804c5e43da7792f75567b693105cc53e3f1992ad98Pirama Arumuga Nainar  // On Windows, CreateFileMapping (the mmap function on Windows)
814c5e43da7792f75567b693105cc53e3f1992ad98Pirama Arumuga Nainar  // automatically extends the underlying file. We don't need to
824c5e43da7792f75567b693105cc53e3f1992ad98Pirama Arumuga Nainar  // extend the file beforehand. _chsize (ftruncate on Windows) is
834c5e43da7792f75567b693105cc53e3f1992ad98Pirama Arumuga Nainar  // pretty slow just like it writes specified amount of bytes,
844c5e43da7792f75567b693105cc53e3f1992ad98Pirama Arumuga Nainar  // so we should avoid calling that.
85ebe69fe11e48d322045d5949c83283927a0d790bStephen Hines  EC = sys::fs::resize_file(FD, Size);
86ebe69fe11e48d322045d5949c83283927a0d790bStephen Hines  if (EC)
87ebe69fe11e48d322045d5949c83283927a0d790bStephen Hines    return EC;
884c5e43da7792f75567b693105cc53e3f1992ad98Pirama Arumuga Nainar#endif
89ebe69fe11e48d322045d5949c83283927a0d790bStephen Hines
9037ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines  auto MappedFile = llvm::make_unique<mapped_file_region>(
91ebe69fe11e48d322045d5949c83283927a0d790bStephen Hines      FD, mapped_file_region::readwrite, Size, 0, EC);
92ebe69fe11e48d322045d5949c83283927a0d790bStephen Hines  int Ret = close(FD);
93adfe2637b839efe041165f27c9ad57e3befb2be0Nick Kledzik  if (EC)
94adfe2637b839efe041165f27c9ad57e3befb2be0Nick Kledzik    return EC;
95ebe69fe11e48d322045d5949c83283927a0d790bStephen Hines  if (Ret)
96ebe69fe11e48d322045d5949c83283927a0d790bStephen Hines    return std::error_code(errno, std::generic_category());
976bc86018d189e9ab62d30ef8190bac961dd22248Michael J. Spencer
9837ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines  Result.reset(
9937ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines      new FileOutputBuffer(std::move(MappedFile), FilePath, TempFilePath));
100adfe2637b839efe041165f27c9ad57e3befb2be0Nick Kledzik
101c6a4f5e819217e1e12c458aed8e7b122e23a3a58Stephen Hines  return std::error_code();
1026bc86018d189e9ab62d30ef8190bac961dd22248Michael J. Spencer}
103adfe2637b839efe041165f27c9ad57e3befb2be0Nick Kledzik
104ebe69fe11e48d322045d5949c83283927a0d790bStephen Hinesstd::error_code FileOutputBuffer::commit() {
105adfe2637b839efe041165f27c9ad57e3befb2be0Nick Kledzik  // Unmap buffer, letting OS flush dirty pages to file on disk.
10637ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines  Region.reset();
1076bc86018d189e9ab62d30ef8190bac961dd22248Michael J. Spencer
1086bc86018d189e9ab62d30ef8190bac961dd22248Michael J. Spencer
109adfe2637b839efe041165f27c9ad57e3befb2be0Nick Kledzik  // Rename file to final name.
110adfe2637b839efe041165f27c9ad57e3befb2be0Nick Kledzik  return sys::fs::rename(Twine(TempPath), Twine(FinalPath));
111adfe2637b839efe041165f27c9ad57e3befb2be0Nick Kledzik}
112adfe2637b839efe041165f27c9ad57e3befb2be0Nick Kledzik} // namespace
113