blob: 46d5702cad1d5797544839a9395ea2d1b338610f [file] [log] [blame]
Nick Kledzikadfe2632012-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
14#include "llvm/Support/FileOutputBuffer.h"
Nick Kledzikadfe2632012-08-01 02:29:50 +000015#include "llvm/ADT/OwningPtr.h"
16#include "llvm/ADT/SmallVector.h"
17#include "llvm/Support/FileSystem.h"
18#include "llvm/Support/raw_ostream.h"
19#include "llvm/Support/system_error.h"
20
21
22namespace llvm {
23
24
25FileOutputBuffer::FileOutputBuffer(uint8_t *Start, uint8_t *End,
26 StringRef Path, StringRef TmpPath)
27 : BufferStart(Start), BufferEnd(End) {
28 FinalPath.assign(Path);
29 TempPath.assign(TmpPath);
30}
31
32
33FileOutputBuffer::~FileOutputBuffer() {
34 // If not already commited, delete buffer and remove temp file.
35 if ( BufferStart != NULL ) {
36 sys::fs::unmap_file_pages((void*)BufferStart, getBufferSize());
37 bool Existed;
38 sys::fs::remove(Twine(TempPath), Existed);
39 }
40}
41
42
43error_code FileOutputBuffer::create(StringRef FilePath,
44 size_t Size,
45 OwningPtr<FileOutputBuffer> &Result,
46 unsigned Flags) {
47 // If file already exists, it must be a regular file (to be mappable).
48 sys::fs::file_status Stat;
49 error_code EC = sys::fs::status(FilePath, Stat);
50 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;
63 else
64 return make_error_code(errc::operation_not_permitted);
65 }
66
67 // Delete target file.
68 bool Existed;
69 EC = sys::fs::remove(FilePath, Existed);
70 if (EC)
71 return EC;
72
73 // Create new file in same directory but with random name.
74 SmallString<128> TempFilePath;
75 int FD;
76 EC = sys::fs::unique_file(Twine(FilePath) + ".tmp%%%%%%%",
77 FD, TempFilePath, false, 0644);
78 if (EC)
79 return EC;
80
81 // The unique_file() interface leaks lower layers and returns a file
82 // descriptor. There is no way to directly close it, so use this hack
83 // to hand it off to raw_fd_ostream to close for us.
84 {
85 raw_fd_ostream Dummy(FD, /*shouldClose=*/true);
86 }
87
88 // Resize file to requested initial size
89 EC = sys::fs::resize_file(Twine(TempFilePath), Size);
90 if (EC)
91 return EC;
92
93 // If requested, make the output file executable.
94 if ( Flags & F_executable ) {
95 sys::fs::file_status Stat2;
96 EC = sys::fs::status(Twine(TempFilePath), Stat2);
97 if (EC)
98 return EC;
99
100 sys::fs::perms new_perms = Stat2.permissions();
101 if ( new_perms & sys::fs::owner_read )
102 new_perms |= sys::fs::owner_exe;
103 if ( new_perms & sys::fs::group_read )
104 new_perms |= sys::fs::group_exe;
105 if ( new_perms & sys::fs::others_read )
106 new_perms |= sys::fs::others_exe;
107 new_perms |= sys::fs::add_perms;
108 EC = sys::fs::permissions(Twine(TempFilePath), new_perms);
109 if (EC)
110 return EC;
111 }
112
113 // Memory map new file.
114 void *Base;
115 EC = sys::fs::map_file_pages(Twine(TempFilePath), 0, Size, true, Base);
116 if (EC)
117 return EC;
118
119 // Create FileOutputBuffer object to own mapped range.
120 uint8_t *Start = reinterpret_cast<uint8_t*>(Base);
121 Result.reset(new FileOutputBuffer(Start, Start+Size, FilePath, TempFilePath));
122
123 return error_code::success();
124}
125
126
127error_code FileOutputBuffer::commit(int64_t NewSmallerSize) {
128 // Unmap buffer, letting OS flush dirty pages to file on disk.
129 void *Start = reinterpret_cast<void*>(BufferStart);
130 error_code EC = sys::fs::unmap_file_pages(Start, getBufferSize());
131 if (EC)
132 return EC;
133
134 // If requested, resize file as part of commit.
135 if ( NewSmallerSize != -1 ) {
136 EC = sys::fs::resize_file(Twine(TempPath), NewSmallerSize);
137 if (EC)
138 return EC;
139 }
140
141 // Rename file to final name.
142 return sys::fs::rename(Twine(TempPath), Twine(FinalPath));
143}
144
145
146} // namespace
147