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,
187                                         bool expand_tilde) const {
188   return m_fs->getRealPath(path, output, expand_tilde);
189 }
190 
191 void FileSystem::Resolve(SmallVectorImpl<char> &path) {
192   if (path.empty())
193     return;
194 
195   // Resolve tilde.
196   SmallString<128> original_path(path.begin(), path.end());
197   StandardTildeExpressionResolver resolver(*this);
198   resolver.ResolveFullPath(original_path, path);
199 
200   // Try making the path absolute if it exists.
201   SmallString<128> absolute_path(path.begin(), path.end());
202   MakeAbsolute(path);
203   if (!Exists(path)) {
204     path.clear();
205     path.append(original_path.begin(), original_path.end());
206   }
207 }
208 
209 void FileSystem::Resolve(FileSpec &file_spec) {
210   // Extract path from the FileSpec.
211   SmallString<128> path;
212   file_spec.GetPath(path);
213 
214   // Resolve the path.
215   Resolve(path);
216 
217   // Update the FileSpec with the resolved path.
218   file_spec.SetPath(path);
219   file_spec.SetIsResolved(true);
220 }
221 
222 bool FileSystem::ResolveExecutableLocation(FileSpec &file_spec) {
223   // If the directory is set there's nothing to do.
224   const ConstString &directory = file_spec.GetDirectory();
225   if (directory)
226     return false;
227 
228   // We cannot look for a file if there's no file name.
229   const ConstString &filename = file_spec.GetFilename();
230   if (!filename)
231     return false;
232 
233   // Search for the file on the host.
234   const std::string filename_str(filename.GetCString());
235   llvm::ErrorOr<std::string> error_or_path =
236       llvm::sys::findProgramByName(filename_str);
237   if (!error_or_path)
238     return false;
239 
240   // findProgramByName returns "." if it can't find the file.
241   llvm::StringRef path = *error_or_path;
242   llvm::StringRef parent = llvm::sys::path::parent_path(path);
243   if (parent.empty() || parent == ".")
244     return false;
245 
246   // Make sure that the result exists.
247   FileSpec result(*error_or_path);
248   if (!Exists(result))
249     return false;
250 
251   file_spec = result;
252   return true;
253 }
254 
255 static int OpenWithFS(const FileSystem &fs, const char *path, int flags,
256                       int mode) {
257   return const_cast<FileSystem &>(fs).Open(path, flags, mode);
258 }
259 
260 static int GetOpenFlags(uint32_t options) {
261   const bool read = options & File::eOpenOptionRead;
262   const bool write = options & File::eOpenOptionWrite;
263 
264   int open_flags = 0;
265   if (write) {
266     if (read)
267       open_flags |= O_RDWR;
268     else
269       open_flags |= O_WRONLY;
270 
271     if (options & File::eOpenOptionAppend)
272       open_flags |= O_APPEND;
273 
274     if (options & File::eOpenOptionTruncate)
275       open_flags |= O_TRUNC;
276 
277     if (options & File::eOpenOptionCanCreate)
278       open_flags |= O_CREAT;
279 
280     if (options & File::eOpenOptionCanCreateNewOnly)
281       open_flags |= O_CREAT | O_EXCL;
282   } else if (read) {
283     open_flags |= O_RDONLY;
284 
285 #ifndef _WIN32
286     if (options & File::eOpenOptionDontFollowSymlinks)
287       open_flags |= O_NOFOLLOW;
288 #endif
289   }
290 
291 #ifndef _WIN32
292   if (options & File::eOpenOptionNonBlocking)
293     open_flags |= O_NONBLOCK;
294   if (options & File::eOpenOptionCloseOnExec)
295     open_flags |= O_CLOEXEC;
296 #else
297   open_flags |= O_BINARY;
298 #endif
299 
300   return open_flags;
301 }
302 
303 static mode_t GetOpenMode(uint32_t permissions) {
304   mode_t mode = 0;
305   if (permissions & lldb::eFilePermissionsUserRead)
306     mode |= S_IRUSR;
307   if (permissions & lldb::eFilePermissionsUserWrite)
308     mode |= S_IWUSR;
309   if (permissions & lldb::eFilePermissionsUserExecute)
310     mode |= S_IXUSR;
311   if (permissions & lldb::eFilePermissionsGroupRead)
312     mode |= S_IRGRP;
313   if (permissions & lldb::eFilePermissionsGroupWrite)
314     mode |= S_IWGRP;
315   if (permissions & lldb::eFilePermissionsGroupExecute)
316     mode |= S_IXGRP;
317   if (permissions & lldb::eFilePermissionsWorldRead)
318     mode |= S_IROTH;
319   if (permissions & lldb::eFilePermissionsWorldWrite)
320     mode |= S_IWOTH;
321   if (permissions & lldb::eFilePermissionsWorldExecute)
322     mode |= S_IXOTH;
323   return mode;
324 }
325 
326 Status FileSystem::Open(File &File, const FileSpec &file_spec, uint32_t options,
327                         uint32_t permissions) {
328   if (File.IsValid())
329     File.Close();
330 
331   const int open_flags = GetOpenFlags(options);
332   const mode_t open_mode =
333       (open_flags & O_CREAT) ? GetOpenMode(permissions) : 0;
334   const std::string path = file_spec.GetPath();
335 
336   int descriptor = llvm::sys::RetryAfterSignal(
337       -1, OpenWithFS, *this, path.c_str(), open_flags, open_mode);
338 
339   Status error;
340   if (!File::DescriptorIsValid(descriptor)) {
341     File.SetDescriptor(descriptor, false);
342     error.SetErrorToErrno();
343   } else {
344     File.SetDescriptor(descriptor, true);
345     File.SetOptions(options);
346   }
347   return error;
348 }
349