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