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 void FileSystem::EnumerateDirectory(Twine path, bool find_directories,
128                                     bool find_files, bool find_other,
129                                     EnumerateDirectoryCallbackType callback,
130                                     void *callback_baton) {
131   std::error_code EC;
132   vfs::recursive_directory_iterator Iter(*m_fs, path, EC);
133   vfs::recursive_directory_iterator End;
134   for (; Iter != End && !EC; Iter.increment(EC)) {
135     const auto &Item = *Iter;
136     ErrorOr<vfs::Status> Status = m_fs->status(Item.path());
137     if (!Status)
138       break;
139     if (!find_files && Status->isRegularFile())
140       continue;
141     if (!find_directories && Status->isDirectory())
142       continue;
143     if (!find_other && Status->isOther())
144       continue;
145 
146     auto Result = callback(callback_baton, Status->getType(), Item.path());
147     if (Result == eEnumerateDirectoryResultQuit)
148       return;
149     if (Result == eEnumerateDirectoryResultNext) {
150       // Default behavior is to recurse. Opt out if the callback doesn't want
151       // this behavior.
152       Iter.no_push();
153     }
154   }
155 }
156 
157 std::error_code FileSystem::MakeAbsolute(SmallVectorImpl<char> &path) const {
158   return m_fs->makeAbsolute(path);
159 }
160 
161 std::error_code FileSystem::MakeAbsolute(FileSpec &file_spec) const {
162   SmallString<128> path;
163   file_spec.GetPath(path, false);
164 
165   auto EC = MakeAbsolute(path);
166   if (EC)
167     return EC;
168 
169   FileSpec new_file_spec(path, file_spec.GetPathStyle());
170   file_spec = new_file_spec;
171   return {};
172 }
173 
174 std::error_code FileSystem::GetRealPath(const Twine &path,
175                                         SmallVectorImpl<char> &output) const {
176   return m_fs->getRealPath(path, output);
177 }
178 
179 void FileSystem::Resolve(SmallVectorImpl<char> &path) {
180   if (path.empty())
181     return;
182 
183   // Resolve tilde.
184   SmallString<128> original_path(path.begin(), path.end());
185   StandardTildeExpressionResolver Resolver;
186   Resolver.ResolveFullPath(original_path, path);
187 
188   // Try making the path absolute if it exists.
189   SmallString<128> absolute_path(path.begin(), path.end());
190   MakeAbsolute(path);
191   if (!Exists(path)) {
192     path.clear();
193     path.append(original_path.begin(), original_path.end());
194   }
195 }
196 
197 void FileSystem::Resolve(FileSpec &file_spec) {
198   // Extract path from the FileSpec.
199   SmallString<128> path;
200   file_spec.GetPath(path);
201 
202   // Resolve the path.
203   Resolve(path);
204 
205   // Update the FileSpec with the resolved path.
206   file_spec.SetPath(path);
207   file_spec.SetIsResolved(true);
208 }
209 
210 bool FileSystem::ResolveExecutableLocation(FileSpec &file_spec) {
211   // If the directory is set there's nothing to do.
212   const ConstString &directory = file_spec.GetDirectory();
213   if (directory)
214     return false;
215 
216   // We cannot look for a file if there's no file name.
217   const ConstString &filename = file_spec.GetFilename();
218   if (!filename)
219     return false;
220 
221   // Search for the file on the host.
222   const std::string filename_str(filename.GetCString());
223   llvm::ErrorOr<std::string> error_or_path =
224       llvm::sys::findProgramByName(filename_str);
225   if (!error_or_path)
226     return false;
227 
228   // findProgramByName returns "." if it can't find the file.
229   llvm::StringRef path = *error_or_path;
230   llvm::StringRef parent = llvm::sys::path::parent_path(path);
231   if (parent.empty() || parent == ".")
232     return false;
233 
234   // Make sure that the result exists.
235   FileSpec result(*error_or_path);
236   if (!Exists(result))
237     return false;
238 
239   file_spec = result;
240   return true;
241 }
242 
243 static int OpenWithFS(const FileSystem &fs, const char *path, int flags,
244                       int mode) {
245   return const_cast<FileSystem &>(fs).Open(path, flags, mode);
246 }
247 
248 static int GetOpenFlags(uint32_t options) {
249   const bool read = options & File::eOpenOptionRead;
250   const bool write = options & File::eOpenOptionWrite;
251 
252   int open_flags = 0;
253   if (write) {
254     if (read)
255       open_flags |= O_RDWR;
256     else
257       open_flags |= O_WRONLY;
258 
259     if (options & File::eOpenOptionAppend)
260       open_flags |= O_APPEND;
261 
262     if (options & File::eOpenOptionTruncate)
263       open_flags |= O_TRUNC;
264 
265     if (options & File::eOpenOptionCanCreate)
266       open_flags |= O_CREAT;
267 
268     if (options & File::eOpenOptionCanCreateNewOnly)
269       open_flags |= O_CREAT | O_EXCL;
270   } else if (read) {
271     open_flags |= O_RDONLY;
272 
273 #ifndef _WIN32
274     if (options & File::eOpenOptionDontFollowSymlinks)
275       open_flags |= O_NOFOLLOW;
276 #endif
277   }
278 
279 #ifndef _WIN32
280   if (options & File::eOpenOptionNonBlocking)
281     open_flags |= O_NONBLOCK;
282   if (options & File::eOpenOptionCloseOnExec)
283     open_flags |= O_CLOEXEC;
284 #else
285   open_flags |= O_BINARY;
286 #endif
287 
288   return open_flags;
289 }
290 
291 static mode_t GetOpenMode(uint32_t permissions) {
292   mode_t mode = 0;
293   if (permissions & lldb::eFilePermissionsUserRead)
294     mode |= S_IRUSR;
295   if (permissions & lldb::eFilePermissionsUserWrite)
296     mode |= S_IWUSR;
297   if (permissions & lldb::eFilePermissionsUserExecute)
298     mode |= S_IXUSR;
299   if (permissions & lldb::eFilePermissionsGroupRead)
300     mode |= S_IRGRP;
301   if (permissions & lldb::eFilePermissionsGroupWrite)
302     mode |= S_IWGRP;
303   if (permissions & lldb::eFilePermissionsGroupExecute)
304     mode |= S_IXGRP;
305   if (permissions & lldb::eFilePermissionsWorldRead)
306     mode |= S_IROTH;
307   if (permissions & lldb::eFilePermissionsWorldWrite)
308     mode |= S_IWOTH;
309   if (permissions & lldb::eFilePermissionsWorldExecute)
310     mode |= S_IXOTH;
311   return mode;
312 }
313 
314 Status FileSystem::Open(File &File, const FileSpec &file_spec, uint32_t options,
315                         uint32_t permissions) {
316   if (File.IsValid())
317     File.Close();
318 
319   const int open_flags = GetOpenFlags(options);
320   const mode_t open_mode =
321       (open_flags & O_CREAT) ? GetOpenMode(permissions) : 0;
322   const std::string path = file_spec.GetPath();
323 
324   int descriptor = llvm::sys::RetryAfterSignal(
325       -1, OpenWithFS, *this, path.c_str(), open_flags, open_mode);
326 
327   Status error;
328   if (!File::DescriptorIsValid(descriptor)) {
329     File.SetDescriptor(descriptor, false);
330     error.SetErrorToErrno();
331   } else {
332     File.SetDescriptor(descriptor, true);
333     File.SetOptions(options);
334   }
335   return error;
336 }
337