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