llvm.org GIT mirror llvm / 6bc8601
[Support] Make FileOutputBuffer work on Windows. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@169167 91177308-0d34-0410-b5e6-96231b3b80d8 Michael J. Spencer 7 years ago
3 changed file(s) with 60 addition(s) and 105 deletion(s). Raw diff Collapse all Expand all
1313 #ifndef LLVM_SUPPORT_FILEOUTPUTBUFFER_H
1414 #define LLVM_SUPPORT_FILEOUTPUTBUFFER_H
1515
16 #include "llvm/ADT/OwningPtr.h"
1617 #include "llvm/ADT/SmallString.h"
1718 #include "llvm/ADT/StringRef.h"
1819 #include "llvm/Support/DataTypes.h"
20 #include "llvm/Support/FileSystem.h"
1921
2022 namespace llvm {
21
2223 class error_code;
23 template class OwningPtr;
2424
2525 /// FileOutputBuffer - This interface provides simple way to create an in-memory
26 /// buffer which will be written to a file. During the lifetime of these
26 /// buffer which will be written to a file. During the lifetime of these
2727 /// objects, the content or existence of the specified file is undefined. That
2828 /// is, creating an OutputBuffer for a file may immediately remove the file.
29 /// If the FileOutputBuffer is committed, the target file's content will become
30 /// the buffer content at the time of the commit. If the FileOutputBuffer is
29 /// If the FileOutputBuffer is committed, the target file's content will become
30 /// the buffer content at the time of the commit. If the FileOutputBuffer is
3131 /// not committed, the file will be deleted in the FileOutputBuffer destructor.
3232 class FileOutputBuffer {
3333 public:
3434
3535 enum {
3636 F_executable = 1 /// set the 'x' bit on the resulting file
37 };
37 };
3838
3939 /// Factory method to create an OutputBuffer object which manages a read/write
4040 /// buffer of the specified size. When committed, the buffer will be written
41 /// to the file at the specified path.
42 static error_code create(StringRef FilePath, size_t Size,
43 OwningPtr &Result,
44 unsigned Flags=0);
45
41 /// to the file at the specified path.
42 static error_code create(StringRef FilePath, size_t Size,
43 OwningPtr &Result,
44 unsigned Flags = 0);
4645
4746 /// Returns a pointer to the start of the buffer.
48 uint8_t *getBufferStart() const {
49 return BufferStart;
47 uint8_t *getBufferStart() {
48 return (uint8_t*)Region->data();
5049 }
51
50
5251 /// Returns a pointer to the end of the buffer.
53 uint8_t *getBufferEnd() const {
54 return BufferEnd;
52 uint8_t *getBufferEnd() {
53 return (uint8_t*)Region->data() + Region->size();
5554 }
56
55
5756 /// Returns size of the buffer.
5857 size_t getBufferSize() const {
59 return BufferEnd - BufferStart;
58 return Region->size();
6059 }
61
60
6261 /// Returns path where file will show up if buffer is committed.
6362 StringRef getPath() const {
6463 return FinalPath;
6564 }
66
67 /// Flushes the content of the buffer to its file and deallocates the
65
66 /// Flushes the content of the buffer to its file and deallocates the
6867 /// buffer. If commit() is not called before this object's destructor
6968 /// is called, the file is deleted in the destructor. The optional parameter
7069 /// is used if it turns out you want the file size to be smaller than
7170 /// initially requested.
7271 error_code commit(int64_t NewSmallerSize = -1);
73
72
7473 /// If this object was previously committed, the destructor just deletes
7574 /// this object. If this object was not committed, the destructor
7675 /// deallocates the buffer and the target file is never written.
7776 ~FileOutputBuffer();
7877
79
8078 private:
8179 FileOutputBuffer(const FileOutputBuffer &) LLVM_DELETED_FUNCTION;
8280 FileOutputBuffer &operator=(const FileOutputBuffer &) LLVM_DELETED_FUNCTION;
83 protected:
84 FileOutputBuffer(uint8_t *Start, uint8_t *End,
85 StringRef Path, StringRef TempPath);
86
87 uint8_t *BufferStart;
88 uint8_t *BufferEnd;
81
82 FileOutputBuffer(llvm::sys::fs::mapped_file_region *R,
83 StringRef Path, StringRef TempPath);
84
85 OwningPtr Region;
8986 SmallString<128> FinalPath;
9087 SmallString<128> TempPath;
9188 };
92
93
94
9589 } // end namespace llvm
9690
9791 #endif
1313 #include "llvm/Support/FileOutputBuffer.h"
1414 #include "llvm/ADT/OwningPtr.h"
1515 #include "llvm/ADT/SmallVector.h"
16 #include "llvm/Support/FileSystem.h"
1716 #include "llvm/Support/raw_ostream.h"
1817 #include "llvm/Support/system_error.h"
1918
19 using llvm::sys::fs::mapped_file_region;
2020
2121 namespace llvm {
22
23
24 FileOutputBuffer::FileOutputBuffer(uint8_t *Start, uint8_t *End,
25 StringRef Path, StringRef TmpPath)
26 : BufferStart(Start), BufferEnd(End) {
27 FinalPath.assign(Path);
28 TempPath.assign(TmpPath);
22 FileOutputBuffer::FileOutputBuffer(mapped_file_region * R,
23 StringRef Path, StringRef TmpPath)
24 : Region(R)
25 , FinalPath(Path)
26 , TempPath(TmpPath) {
2927 }
3028
31
3229 FileOutputBuffer::~FileOutputBuffer() {
33 // If not already commited, delete buffer and remove temp file.
34 if ( BufferStart != NULL ) {
35 sys::fs::unmap_file_pages((void*)BufferStart, getBufferSize());
36 bool Existed;
37 sys::fs::remove(Twine(TempPath), Existed);
38 }
30 bool Existed;
31 sys::fs::remove(Twine(TempPath), Existed);
3932 }
4033
41
42 error_code FileOutputBuffer::create(StringRef FilePath,
43 size_t Size,
34 error_code FileOutputBuffer::create(StringRef FilePath,
35 size_t Size,
4436 OwningPtr &Result,
4537 unsigned Flags) {
4638 // If file already exists, it must be a regular file (to be mappable).
6860 EC = sys::fs::remove(FilePath, Existed);
6961 if (EC)
7062 return EC;
71
63
7264 // Create new file in same directory but with random name.
7365 SmallString<128> TempFilePath;
7466 int FD;
75 EC = sys::fs::unique_file(Twine(FilePath) + ".tmp%%%%%%%",
76 FD, TempFilePath, false, 0644);
67 EC = sys::fs::unique_file(Twine(FilePath) + ".tmp%%%%%%%",
68 FD, TempFilePath, false, 0644);
7769 if (EC)
7870 return EC;
79
80 // The unique_file() interface leaks lower layers and returns a file
81 // descriptor. There is no way to directly close it, so use this hack
82 // to hand it off to raw_fd_ostream to close for us.
83 {
84 raw_fd_ostream Dummy(FD, /*shouldClose=*/true);
85 }
86
87 // Resize file to requested initial size
88 EC = sys::fs::resize_file(Twine(TempFilePath), Size);
71
72 OwningPtr MappedFile(
73 new mapped_file_region(FD, mapped_file_region::readwrite, Size, 0, EC));
8974 if (EC)
9075 return EC;
91
76
9277 // If requested, make the output file executable.
9378 if ( Flags & F_executable ) {
9479 sys::fs::file_status Stat2;
9580 EC = sys::fs::status(Twine(TempFilePath), Stat2);
9681 if (EC)
9782 return EC;
98
83
9984 sys::fs::perms new_perms = Stat2.permissions();
10085 if ( new_perms & sys::fs::owner_read )
10186 new_perms |= sys::fs::owner_exe;
10994 return EC;
11095 }
11196
112 // Memory map new file.
113 void *Base;
114 EC = sys::fs::map_file_pages(Twine(TempFilePath), 0, Size, true, Base);
115 if (EC)
116 return EC;
117
118 // Create FileOutputBuffer object to own mapped range.
119 uint8_t *Start = reinterpret_cast(Base);
120 Result.reset(new FileOutputBuffer(Start, Start+Size, FilePath, TempFilePath));
121
97 Result.reset(new FileOutputBuffer(MappedFile.get(), FilePath, TempFilePath));
98 if (Result)
99 MappedFile.take();
100
122101 return error_code::success();
123 }
124
102 }
125103
126104 error_code FileOutputBuffer::commit(int64_t NewSmallerSize) {
127105 // Unmap buffer, letting OS flush dirty pages to file on disk.
128 void *Start = reinterpret_cast(BufferStart);
129 error_code EC = sys::fs::unmap_file_pages(Start, getBufferSize());
130 if (EC)
131 return EC;
132
106 Region.reset(0);
107
133108 // If requested, resize file as part of commit.
134109 if ( NewSmallerSize != -1 ) {
135 EC = sys::fs::resize_file(Twine(TempPath), NewSmallerSize);
110 error_code EC = sys::fs::resize_file(Twine(TempPath), NewSmallerSize);
136111 if (EC)
137112 return EC;
138113 }
139
114
140115 // Rename file to final name.
141116 return sys::fs::rename(Twine(TempPath), Twine(FinalPath));
142117 }
143
144
145118 } // namespace
146
66 //
77 //===----------------------------------------------------------------------===//
88
9 #include "llvm/Support/FileOutputBuffer.h"
10
911 #include "llvm/ADT/OwningPtr.h"
1012 #include "llvm/Support/ErrorHandling.h"
11 #include "llvm/Support/FileOutputBuffer.h"
1213 #include "llvm/Support/FileSystem.h"
1314 #include "llvm/Support/PathV2.h"
1415 #include "llvm/Support/raw_ostream.h"
2627 } else {}
2728
2829 namespace {
29
30
31 // NOTE: Temporarily run this test on unix only. Once the file mapping
32 // routines are ported to Windows, this conditional can be removed.
33 #if LLVM_ON_UNIX
34
35
3630 TEST(FileOutputBuffer, Test) {
3731 // Create unique temporary directory for these tests
3832 SmallString<128> TestDirectory;
4438 ::close(fd);
4539 TestDirectory = path::parent_path(TestDirectory);
4640 }
47
41
4842 // TEST 1: Verify commit case.
4943 SmallString<128> File1(TestDirectory);
5044 File1.append("/file1");
6054 }
6155 // Verify file exists and starts with special header.
6256 bool MagicMatches = false;
63 ASSERT_NO_ERROR(fs::has_magic(Twine(File1), Twine("AABBCCDDEEFFGGHHIIJJ"),
57 ASSERT_NO_ERROR(fs::has_magic(Twine(File1), Twine("AABBCCDDEEFFGGHHIIJJ"),
6458 MagicMatches));
6559 EXPECT_TRUE(MagicMatches);
6660 // Verify file is correct size.
8175 // Verify file does not exist (because buffer not commited).
8276 bool Exists = false;
8377 ASSERT_NO_ERROR(fs::exists(Twine(File2), Exists));
84 EXPECT_FALSE(Exists);
85
78 EXPECT_FALSE(Exists);
8679
8780 // TEST 3: Verify sizing down case.
8881 SmallString<128> File3(TestDirectory);
9992 }
10093 // Verify file exists and starts with special header.
10194 bool MagicMatches3 = false;
102 ASSERT_NO_ERROR(fs::has_magic(Twine(File3), Twine("AABBCCDDEEFFGGHHIIJJ"),
95 ASSERT_NO_ERROR(fs::has_magic(Twine(File3), Twine("AABBCCDDEEFFGGHHIIJJ"),
10396 MagicMatches3));
10497 EXPECT_TRUE(MagicMatches3);
10598 // Verify file is correct size.
107100 ASSERT_NO_ERROR(fs::file_size(Twine(File3), File3Size));
108101 ASSERT_EQ(File3Size, 5000ULL);
109102
110
111103 // TEST 4: Verify file can be made executable.
112104 SmallString<128> File4(TestDirectory);
113105 File4.append("/file4");
114106 {
115107 OwningPtr Buffer;
116 ASSERT_NO_ERROR(FileOutputBuffer::create(File4, 8192, Buffer,
108 ASSERT_NO_ERROR(FileOutputBuffer::create(File4, 8192, Buffer,
117109 FileOutputBuffer::F_executable));
118110 // Start buffer with special header.
119111 memcpy(Buffer->getBufferStart(), "AABBCCDDEEFFGGHHIIJJ", 20);
130122 uint32_t RemovedCount;
131123 ASSERT_NO_ERROR(fs::remove_all(TestDirectory.str(), RemovedCount));
132124 }
133
134 #endif // LLVM_ON_UNIX
135
136125 } // anonymous namespace