1 //===-- FileSystem.cpp ------------------------------------------*- 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 #include "lldb/Host/FileSystem.h"
11 
12 #include "lldb/Utility/LLDBAssert.h"
13 #include "lldb/Utility/TildeExpressionResolver.h"
14 
15 #include "llvm/Support/Errno.h"
16 #include "llvm/Support/FileSystem.h"
17 #include "llvm/Support/Path.h"
18 #include "llvm/Support/Program.h"
19 #include "llvm/Support/Threading.h"
20 
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <limits.h>
24 #include <stdarg.h>
25 #include <stdio.h>
26 
27 #ifdef _WIN32
28 #include "lldb/Host/windows/windows.h"
29 #else
30 #include <sys/ioctl.h>
31 #include <sys/stat.h>
32 #include <termios.h>
33 #include <unistd.h>
34 #endif
35 
36 #include <algorithm>
37 #include <fstream>
38 #include <vector>
39 
40 using namespace lldb;
41 using namespace lldb_private;
42 using namespace llvm;
43 
44 FileSystem &FileSystem::Instance() { return *InstanceImpl(); }
45 
46 void FileSystem::Initialize() {
47   lldbassert(!InstanceImpl() && "Already initialized.");
48   InstanceImpl().emplace();
49 }
50 
51 void FileSystem::Initialize(IntrusiveRefCntPtr<vfs::FileSystem> fs) {
52   lldbassert(!InstanceImpl() && "Already initialized.");
53   InstanceImpl().emplace(fs);
54 }
55 
56 void FileSystem::Terminate() {
57   lldbassert(InstanceImpl() && "Already terminated.");
58   InstanceImpl().reset();
59 }
60 
61 Optional<FileSystem> &FileSystem::InstanceImpl() {
62   static Optional<FileSystem> g_fs;
63   return g_fs;
64 }
65 
66 sys::TimePoint<>
67 FileSystem::GetModificationTime(const FileSpec &file_spec,
68                                 bool nanosecond_precision) const {
69   return GetModificationTime(file_spec.GetPath(), nanosecond_precision);
70 }
71 
72 sys::TimePoint<>
73 FileSystem::GetModificationTime(const Twine &path,
74                                 bool nanosecond_precision) const {
75   ErrorOr<vfs::Status> status = m_fs->status(path);
76   if (!status)
77     return sys::TimePoint<>();
78   if (nanosecond_precision)
79     return status->getLastModificationTime();
80   else
81     return std::chrono::time_point_cast<std::chrono::seconds>(
82         status->getLastModificationTime());
83 }
84 
85 uint64_t FileSystem::GetByteSize(const FileSpec &file_spec) const {
86   return GetByteSize(file_spec.GetPath());
87 }
88 
89 uint64_t FileSystem::GetByteSize(const Twine &path) const {
90   ErrorOr<vfs::Status> status = m_fs->status(path);
91   if (!status)
92     return 0;
93   return status->getSize();
94 }
95 
96 uint32_t FileSystem::GetPermissions(const FileSpec &file_spec) const {
97   return GetPermissions(file_spec.GetPath());
98 }
99 
100 uint32_t FileSystem::GetPermissions(const FileSpec &file_spec,
101                                     std::error_code &ec) const {
102   return GetPermissions(file_spec.GetPath(), ec);
103 }
104 
105 uint32_t FileSystem::GetPermissions(const Twine &path) const {
106   std::error_code ec;
107   return GetPermissions(path, ec);
108 }
109 
110 uint32_t FileSystem::GetPermissions(const Twine &path,
111                                     std::error_code &ec) const {
112   ErrorOr<vfs::Status> status = m_fs->status(path);
113   if (!status) {
114     ec = status.getError();
115     return sys::fs::perms::perms_not_known;
116   }
117   return status->getPermissions();
118 }
119 
120 bool FileSystem::Exists(const Twine &path) const { return m_fs->exists(path); }
121 
122 bool FileSystem::Exists(const FileSpec &file_spec) const {
123   return Exists(file_spec.GetPath());
124 }
125 
126 bool FileSystem::Readable(const Twine &path) const {
127   return GetPermissions(path) & sys::fs::perms::all_read;
128 }
129 
130 bool FileSystem::Readable(const FileSpec &file_spec) const {
131   return Readable(file_spec.GetPath());
132 }
133 
134 bool FileSystem::IsDirectory(const Twine &path) const {
135   ErrorOr<vfs::Status> status = m_fs->status(path);
136   if (!status)
137     return false;
138   return status->isDirectory();
139 }
140 
141 bool FileSystem::IsDirectory(const FileSpec &file_spec) const {
142   return IsDirectory(file_spec.GetPath());
143 }
144 
145 bool FileSystem::IsLocal(const Twine &path) const {
146   bool b = false;
147   m_fs->isLocal(path, b);
148   return b;
149 }
150 
151 bool FileSystem::IsLocal(const FileSpec &file_spec) const {
152   return IsLocal(file_spec.GetPath());
153 }
154 
155 void FileSystem::EnumerateDirectory(Twine path, bool find_directories,
156                                     bool find_files, bool find_other,
157                                     EnumerateDirectoryCallbackType callback,
158                                     void *callback_baton) {
159   std::error_code EC;
160   vfs::recursive_directory_iterator Iter(*m_fs, path, EC);
161   vfs::recursive_directory_iterator End;
162   for (; Iter != End && !EC; Iter.increment(EC)) {
163     const auto &Item = *Iter;
164     ErrorOr<vfs::Status> Status = m_fs->status(Item.path());
165     if (!Status)
166       break;
167     if (!find_files && Status->isRegularFile())
168       continue;
169     if (!find_directories && Status->isDirectory())
170       continue;
171     if (!find_other && Status->isOther())
172       continue;
173 
174     auto Result = callback(callback_baton, Status->getType(), Item.path());
175     if (Result == eEnumerateDirectoryResultQuit)
176       return;
177     if (Result == eEnumerateDirectoryResultNext) {
178       // Default behavior is to recurse. Opt out if the callback doesn't want
179       // this behavior.
180       Iter.no_push();
181     }
182   }
183 }
184 
185 std::error_code FileSystem::MakeAbsolute(SmallVectorImpl<char> &path) const {
186   return m_fs->makeAbsolute(path);
187 }
188 
189 std::error_code FileSystem::MakeAbsolute(FileSpec &file_spec) const {
190   SmallString<128> path;
191   file_spec.GetPath(path, false);
192 
193   auto EC = MakeAbsolute(path);
194   if (EC)
195     return EC;
196 
197   FileSpec new_file_spec(path, file_spec.GetPathStyle());
198   file_spec = new_file_spec;
199   return {};
200 }
201 
202 std::error_code FileSystem::GetRealPath(const Twine &path,
203                                         SmallVectorImpl<char> &output) const {
204   return m_fs->getRealPath(path, output);
205 }
206 
207 void FileSystem::Resolve(SmallVectorImpl<char> &path) {
208   if (path.empty())
209     return;
210 
211   // Resolve tilde.
212   SmallString<128> original_path(path.begin(), path.end());
213   StandardTildeExpressionResolver Resolver;
214   Resolver.ResolveFullPath(original_path, path);
215 
216   // Try making the path absolute if it exists.
217   SmallString<128> absolute_path(path.begin(), path.end());
218   MakeAbsolute(path);
219   if (!Exists(path)) {
220     path.clear();
221     path.append(original_path.begin(), original_path.end());
222   }
223 }
224 
225 void FileSystem::Resolve(FileSpec &file_spec) {
226   // Extract path from the FileSpec.
227   SmallString<128> path;
228   file_spec.GetPath(path);
229 
230   // Resolve the path.
231   Resolve(path);
232 
233   // Update the FileSpec with the resolved path.
234   file_spec.SetPath(path);
235   file_spec.SetIsResolved(true);
236 }
237 
238 std::shared_ptr<DataBufferLLVM>
239 FileSystem::CreateDataBuffer(const llvm::Twine &path, uint64_t size,
240                              uint64_t offset) {
241   const bool is_volatile = !IsLocal(path);
242 
243   std::unique_ptr<llvm::WritableMemoryBuffer> buffer;
244   if (size == 0) {
245     auto buffer_or_error =
246         llvm::WritableMemoryBuffer::getFile(path, -1, is_volatile);
247     if (!buffer_or_error)
248       return nullptr;
249     buffer = std::move(*buffer_or_error);
250   } else {
251     auto buffer_or_error = llvm::WritableMemoryBuffer::getFileSlice(
252         path, size, offset, is_volatile);
253     if (!buffer_or_error)
254       return nullptr;
255     buffer = std::move(*buffer_or_error);
256   }
257   return std::shared_ptr<DataBufferLLVM>(new DataBufferLLVM(std::move(buffer)));
258 }
259 
260 std::shared_ptr<DataBufferLLVM>
261 FileSystem::CreateDataBuffer(const FileSpec &file_spec, uint64_t size,
262                              uint64_t offset) {
263   return CreateDataBuffer(file_spec.GetPath(), size, offset);
264 }
265 
266 bool FileSystem::ResolveExecutableLocation(FileSpec &file_spec) {
267   // If the directory is set there's nothing to do.
268   const ConstString &directory = file_spec.GetDirectory();
269   if (directory)
270     return false;
271 
272   // We cannot look for a file if there's no file name.
273   const ConstString &filename = file_spec.GetFilename();
274   if (!filename)
275     return false;
276 
277   // Search for the file on the host.
278   const std::string filename_str(filename.GetCString());
279   llvm::ErrorOr<std::string> error_or_path =
280       llvm::sys::findProgramByName(filename_str);
281   if (!error_or_path)
282     return false;
283 
284   // findProgramByName returns "." if it can't find the file.
285   llvm::StringRef path = *error_or_path;
286   llvm::StringRef parent = llvm::sys::path::parent_path(path);
287   if (parent.empty() || parent == ".")
288     return false;
289 
290   // Make sure that the result exists.
291   FileSpec result(*error_or_path);
292   if (!Exists(result))
293     return false;
294 
295   file_spec = result;
296   return true;
297 }
298 
299 static int OpenWithFS(const FileSystem &fs, const char *path, int flags,
300                       int mode) {
301   return const_cast<FileSystem &>(fs).Open(path, flags, mode);
302 }
303 
304 static int GetOpenFlags(uint32_t options) {
305   const bool read = options & File::eOpenOptionRead;
306   const bool write = options & File::eOpenOptionWrite;
307 
308   int open_flags = 0;
309   if (write) {
310     if (read)
311       open_flags |= O_RDWR;
312     else
313       open_flags |= O_WRONLY;
314 
315     if (options & File::eOpenOptionAppend)
316       open_flags |= O_APPEND;
317 
318     if (options & File::eOpenOptionTruncate)
319       open_flags |= O_TRUNC;
320 
321     if (options & File::eOpenOptionCanCreate)
322       open_flags |= O_CREAT;
323 
324     if (options & File::eOpenOptionCanCreateNewOnly)
325       open_flags |= O_CREAT | O_EXCL;
326   } else if (read) {
327     open_flags |= O_RDONLY;
328 
329 #ifndef _WIN32
330     if (options & File::eOpenOptionDontFollowSymlinks)
331       open_flags |= O_NOFOLLOW;
332 #endif
333   }
334 
335 #ifndef _WIN32
336   if (options & File::eOpenOptionNonBlocking)
337     open_flags |= O_NONBLOCK;
338   if (options & File::eOpenOptionCloseOnExec)
339     open_flags |= O_CLOEXEC;
340 #else
341   open_flags |= O_BINARY;
342 #endif
343 
344   return open_flags;
345 }
346 
347 static mode_t GetOpenMode(uint32_t permissions) {
348   mode_t mode = 0;
349   if (permissions & lldb::eFilePermissionsUserRead)
350     mode |= S_IRUSR;
351   if (permissions & lldb::eFilePermissionsUserWrite)
352     mode |= S_IWUSR;
353   if (permissions & lldb::eFilePermissionsUserExecute)
354     mode |= S_IXUSR;
355   if (permissions & lldb::eFilePermissionsGroupRead)
356     mode |= S_IRGRP;
357   if (permissions & lldb::eFilePermissionsGroupWrite)
358     mode |= S_IWGRP;
359   if (permissions & lldb::eFilePermissionsGroupExecute)
360     mode |= S_IXGRP;
361   if (permissions & lldb::eFilePermissionsWorldRead)
362     mode |= S_IROTH;
363   if (permissions & lldb::eFilePermissionsWorldWrite)
364     mode |= S_IWOTH;
365   if (permissions & lldb::eFilePermissionsWorldExecute)
366     mode |= S_IXOTH;
367   return mode;
368 }
369 
370 Status FileSystem::Open(File &File, const FileSpec &file_spec, uint32_t options,
371                         uint32_t permissions) {
372   if (File.IsValid())
373     File.Close();
374 
375   const int open_flags = GetOpenFlags(options);
376   const mode_t open_mode =
377       (open_flags & O_CREAT) ? GetOpenMode(permissions) : 0;
378   const std::string path = file_spec.GetPath();
379 
380   int descriptor = llvm::sys::RetryAfterSignal(
381       -1, OpenWithFS, *this, path.c_str(), open_flags, open_mode);
382 
383   Status error;
384   if (!File::DescriptorIsValid(descriptor)) {
385     File.SetDescriptor(descriptor, false);
386     error.SetErrorToErrno();
387   } else {
388     File.SetDescriptor(descriptor, true);
389     File.SetOptions(options);
390   }
391   return error;
392 }
393