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