blob: 57e5a8d7871cd1887638bcb7d09ca7b5a09ba67e [file] [log] [blame]
Nick Kledzik5fce8c42012-08-01 02:29:50 +00001//===- 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
Chandler Carruthd9903882015-01-14 11:23:27 +000014#include "llvm/Support/FileOutputBuffer.h"
Benjamin Kramer16132e62015-03-23 18:07:13 +000015#include "llvm/ADT/STLExtras.h"
16#include "llvm/ADT/SmallString.h"
17#include "llvm/Support/Errc.h"
Rafael Espindolad4b24ed2017-01-09 21:52:35 +000018#include "llvm/Support/Path.h"
Rafael Espindola7dbb5772015-09-18 15:17:53 +000019#include "llvm/Support/Signals.h"
Rafael Espindolaa6e9c3e2014-06-12 17:38:55 +000020#include <system_error>
Nick Kledzik5fce8c42012-08-01 02:29:50 +000021
Rafael Espindola7eb1f182014-12-11 20:12:55 +000022#if !defined(_MSC_VER) && !defined(__MINGW32__)
23#include <unistd.h>
24#else
25#include <io.h>
26#endif
27
Michael J. Spencer7fe24f52012-12-03 22:09:52 +000028using llvm::sys::fs::mapped_file_region;
Nick Kledzik5fce8c42012-08-01 02:29:50 +000029
30namespace llvm {
David Blaikief55e31a2014-09-02 17:49:23 +000031FileOutputBuffer::FileOutputBuffer(std::unique_ptr<mapped_file_region> R,
Rafael Espindolad4b24ed2017-01-09 21:52:35 +000032 StringRef Path, StringRef TmpPath,
33 bool IsRegular)
34 : Region(std::move(R)), FinalPath(Path), TempPath(TmpPath),
35 IsRegular(IsRegular) {}
Nick Kledzik5fce8c42012-08-01 02:29:50 +000036
Nick Kledzik5fce8c42012-08-01 02:29:50 +000037FileOutputBuffer::~FileOutputBuffer() {
Reid Kleckner1a4398a2016-09-02 01:10:53 +000038 // Close the mapping before deleting the temp file, so that the removal
39 // succeeds.
40 Region.reset();
Rafael Espindola81e7fd02014-01-10 21:40:29 +000041 sys::fs::remove(Twine(TempPath));
Nick Kledzik5fce8c42012-08-01 02:29:50 +000042}
43
Rafael Espindola169284a2015-08-13 00:31:39 +000044ErrorOr<std::unique_ptr<FileOutputBuffer>>
45FileOutputBuffer::create(StringRef FilePath, size_t Size, unsigned Flags) {
Rafael Espindolad4b24ed2017-01-09 21:52:35 +000046 // Check file is not a regular file, in which case we cannot remove it.
Nick Kledzik5fce8c42012-08-01 02:29:50 +000047 sys::fs::file_status Stat;
Rafael Espindoladb4ed0b2014-06-13 02:24:39 +000048 std::error_code EC = sys::fs::status(FilePath, Stat);
Rafael Espindolad4b24ed2017-01-09 21:52:35 +000049 bool IsRegular = true;
Nick Kledzik5fce8c42012-08-01 02:29:50 +000050 switch (Stat.type()) {
51 case sys::fs::file_type::file_not_found:
52 // If file does not exist, we'll create one.
53 break;
54 case sys::fs::file_type::regular_file: {
55 // If file is not currently writable, error out.
56 // FIXME: There is no sys::fs:: api for checking this.
57 // FIXME: In posix, you use the access() call to check this.
58 }
59 break;
60 default:
61 if (EC)
62 return EC;
Rafael Espindolad4b24ed2017-01-09 21:52:35 +000063 IsRegular = false;
Nick Kledzik5fce8c42012-08-01 02:29:50 +000064 }
65
Rafael Espindolad4b24ed2017-01-09 21:52:35 +000066 if (IsRegular) {
67 // Delete target file.
68 EC = sys::fs::remove(FilePath);
69 if (EC)
70 return EC;
71 }
Michael J. Spencer7fe24f52012-12-03 22:09:52 +000072
Nick Kledzik5fce8c42012-08-01 02:29:50 +000073 SmallString<128> TempFilePath;
74 int FD;
Rafael Espindolad4b24ed2017-01-09 21:52:35 +000075 if (IsRegular) {
76 unsigned Mode = sys::fs::all_read | sys::fs::all_write;
77 // If requested, make the output file executable.
78 if (Flags & F_executable)
79 Mode |= sys::fs::all_exe;
80 // Create new file in same directory but with random name.
81 EC = sys::fs::createUniqueFile(Twine(FilePath) + ".tmp%%%%%%%", FD,
82 TempFilePath, Mode);
83 } else {
84 // Create a temporary file. Since this is a special file, we will not move
85 // it and the new file can be in another filesystem. This avoids trying to
86 // create a temporary file in /dev when outputting to /dev/null for example.
87 EC = sys::fs::createTemporaryFile(sys::path::filename(FilePath), "", FD,
88 TempFilePath);
89 }
90
Nick Kledzik5fce8c42012-08-01 02:29:50 +000091 if (EC)
92 return EC;
Michael J. Spencer7fe24f52012-12-03 22:09:52 +000093
Rafael Espindola7dbb5772015-09-18 15:17:53 +000094 sys::RemoveFileOnSignal(TempFilePath);
95
Rui Ueyamada9bc2e2015-03-06 06:07:32 +000096#ifndef LLVM_ON_WIN32
97 // On Windows, CreateFileMapping (the mmap function on Windows)
98 // automatically extends the underlying file. We don't need to
99 // extend the file beforehand. _chsize (ftruncate on Windows) is
100 // pretty slow just like it writes specified amount of bytes,
101 // so we should avoid calling that.
Rafael Espindolac69f13b2014-12-12 18:13:23 +0000102 EC = sys::fs::resize_file(FD, Size);
103 if (EC)
104 return EC;
Rui Ueyamada9bc2e2015-03-06 06:07:32 +0000105#endif
Rafael Espindolac69f13b2014-12-12 18:13:23 +0000106
David Blaikief55e31a2014-09-02 17:49:23 +0000107 auto MappedFile = llvm::make_unique<mapped_file_region>(
Rafael Espindola7eb1f182014-12-11 20:12:55 +0000108 FD, mapped_file_region::readwrite, Size, 0, EC);
109 int Ret = close(FD);
Nick Kledzik5fce8c42012-08-01 02:29:50 +0000110 if (EC)
111 return EC;
Rafael Espindola7eb1f182014-12-11 20:12:55 +0000112 if (Ret)
113 return std::error_code(errno, std::generic_category());
Michael J. Spencer7fe24f52012-12-03 22:09:52 +0000114
Rafael Espindolad4b24ed2017-01-09 21:52:35 +0000115 std::unique_ptr<FileOutputBuffer> Buf(new FileOutputBuffer(
116 std::move(MappedFile), FilePath, TempFilePath, IsRegular));
Rafael Espindola169284a2015-08-13 00:31:39 +0000117 return std::move(Buf);
Michael J. Spencer7fe24f52012-12-03 22:09:52 +0000118}
Nick Kledzik5fce8c42012-08-01 02:29:50 +0000119
Rafael Espindola5753cf32014-12-12 17:35:34 +0000120std::error_code FileOutputBuffer::commit() {
Nick Kledzik5fce8c42012-08-01 02:29:50 +0000121 // Unmap buffer, letting OS flush dirty pages to file on disk.
David Blaikieb61064e2014-07-19 01:05:11 +0000122 Region.reset();
Michael J. Spencer7fe24f52012-12-03 22:09:52 +0000123
Rafael Espindolad4b24ed2017-01-09 21:52:35 +0000124 std::error_code EC;
125 if (IsRegular) {
126 // Rename file to final name.
127 EC = sys::fs::rename(Twine(TempPath), Twine(FinalPath));
128 sys::DontRemoveFileOnSignal(TempPath);
129 } else {
130 EC = sys::fs::copy_file(TempPath, FinalPath);
131 std::error_code RMEC = sys::fs::remove(TempPath);
132 sys::DontRemoveFileOnSignal(TempPath);
133 if (RMEC)
134 return RMEC;
135 }
Michael J. Spencer7fe24f52012-12-03 22:09:52 +0000136
Rafael Espindola7dbb5772015-09-18 15:17:53 +0000137 return EC;
Nick Kledzik5fce8c42012-08-01 02:29:50 +0000138}
Alexander Kornienkof00654e2015-06-23 09:49:53 +0000139} // namespace