blob: 731740d012d90887cce11f5ec2f914efb349c166 [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;
Rui Ueyamafed8b572017-03-13 22:19:05 +000060 case sys::fs::file_type::directory_file:
61 return errc::is_a_directory;
Nick Kledzik5fce8c42012-08-01 02:29:50 +000062 default:
63 if (EC)
64 return EC;
Rafael Espindolad4b24ed2017-01-09 21:52:35 +000065 IsRegular = false;
Nick Kledzik5fce8c42012-08-01 02:29:50 +000066 }
67
Rafael Espindolad4b24ed2017-01-09 21:52:35 +000068 if (IsRegular) {
69 // Delete target file.
70 EC = sys::fs::remove(FilePath);
71 if (EC)
72 return EC;
73 }
Michael J. Spencer7fe24f52012-12-03 22:09:52 +000074
Nick Kledzik5fce8c42012-08-01 02:29:50 +000075 SmallString<128> TempFilePath;
76 int FD;
Rafael Espindolad4b24ed2017-01-09 21:52:35 +000077 if (IsRegular) {
78 unsigned Mode = sys::fs::all_read | sys::fs::all_write;
79 // If requested, make the output file executable.
80 if (Flags & F_executable)
81 Mode |= sys::fs::all_exe;
82 // Create new file in same directory but with random name.
83 EC = sys::fs::createUniqueFile(Twine(FilePath) + ".tmp%%%%%%%", FD,
84 TempFilePath, Mode);
85 } else {
86 // Create a temporary file. Since this is a special file, we will not move
87 // it and the new file can be in another filesystem. This avoids trying to
88 // create a temporary file in /dev when outputting to /dev/null for example.
89 EC = sys::fs::createTemporaryFile(sys::path::filename(FilePath), "", FD,
90 TempFilePath);
91 }
92
Nick Kledzik5fce8c42012-08-01 02:29:50 +000093 if (EC)
94 return EC;
Michael J. Spencer7fe24f52012-12-03 22:09:52 +000095
Rafael Espindola7dbb5772015-09-18 15:17:53 +000096 sys::RemoveFileOnSignal(TempFilePath);
97
Rui Ueyamada9bc2e2015-03-06 06:07:32 +000098#ifndef LLVM_ON_WIN32
99 // On Windows, CreateFileMapping (the mmap function on Windows)
100 // automatically extends the underlying file. We don't need to
101 // extend the file beforehand. _chsize (ftruncate on Windows) is
102 // pretty slow just like it writes specified amount of bytes,
103 // so we should avoid calling that.
Rafael Espindolac69f13b2014-12-12 18:13:23 +0000104 EC = sys::fs::resize_file(FD, Size);
105 if (EC)
106 return EC;
Rui Ueyamada9bc2e2015-03-06 06:07:32 +0000107#endif
Rafael Espindolac69f13b2014-12-12 18:13:23 +0000108
David Blaikief55e31a2014-09-02 17:49:23 +0000109 auto MappedFile = llvm::make_unique<mapped_file_region>(
Rafael Espindola7eb1f182014-12-11 20:12:55 +0000110 FD, mapped_file_region::readwrite, Size, 0, EC);
111 int Ret = close(FD);
Nick Kledzik5fce8c42012-08-01 02:29:50 +0000112 if (EC)
113 return EC;
Rafael Espindola7eb1f182014-12-11 20:12:55 +0000114 if (Ret)
115 return std::error_code(errno, std::generic_category());
Michael J. Spencer7fe24f52012-12-03 22:09:52 +0000116
Rafael Espindolad4b24ed2017-01-09 21:52:35 +0000117 std::unique_ptr<FileOutputBuffer> Buf(new FileOutputBuffer(
118 std::move(MappedFile), FilePath, TempFilePath, IsRegular));
Rafael Espindola169284a2015-08-13 00:31:39 +0000119 return std::move(Buf);
Michael J. Spencer7fe24f52012-12-03 22:09:52 +0000120}
Nick Kledzik5fce8c42012-08-01 02:29:50 +0000121
Rafael Espindola5753cf32014-12-12 17:35:34 +0000122std::error_code FileOutputBuffer::commit() {
Nick Kledzik5fce8c42012-08-01 02:29:50 +0000123 // Unmap buffer, letting OS flush dirty pages to file on disk.
David Blaikieb61064e2014-07-19 01:05:11 +0000124 Region.reset();
Michael J. Spencer7fe24f52012-12-03 22:09:52 +0000125
Rafael Espindolad4b24ed2017-01-09 21:52:35 +0000126 std::error_code EC;
127 if (IsRegular) {
128 // Rename file to final name.
129 EC = sys::fs::rename(Twine(TempPath), Twine(FinalPath));
130 sys::DontRemoveFileOnSignal(TempPath);
131 } else {
132 EC = sys::fs::copy_file(TempPath, FinalPath);
133 std::error_code RMEC = sys::fs::remove(TempPath);
134 sys::DontRemoveFileOnSignal(TempPath);
135 if (RMEC)
136 return RMEC;
137 }
Michael J. Spencer7fe24f52012-12-03 22:09:52 +0000138
Rafael Espindola7dbb5772015-09-18 15:17:53 +0000139 return EC;
Nick Kledzik5fce8c42012-08-01 02:29:50 +0000140}
Alexander Kornienkof00654e2015-06-23 09:49:53 +0000141} // namespace