1 //===-- FileSystem.cpp ----------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "lldb/Host/FileSystem.h"
10 
11 #include "lldb/Utility/DataBufferLLVM.h"
12 
13 #include "llvm/Support/Errc.h"
14 #include "llvm/Support/Errno.h"
15 #include "llvm/Support/Error.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 <cerrno>
22 #include <climits>
23 #include <cstdarg>
24 #include <cstdio>
25 #include <fcntl.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 <optional>
39 #include <vector>
40 
41 using namespace lldb;
42 using namespace lldb_private;
43 using namespace llvm;
44 
45 FileSystem &FileSystem::Instance() { return *InstanceImpl(); }
46 
47 void FileSystem::Terminate() {
48   lldbassert(InstanceImpl() && "Already terminated.");
49   InstanceImpl().reset();
50 }
51 
52 std::optional<FileSystem> &FileSystem::InstanceImpl() {
53   static std::optional<FileSystem> g_fs;
54   return g_fs;
55 }
56 
57 vfs::directory_iterator FileSystem::DirBegin(const FileSpec &file_spec,
58                                              std::error_code &ec) {
59   if (!file_spec) {
60     ec = std::error_code(static_cast<int>(errc::no_such_file_or_directory),
61                          std::system_category());
62     return {};
63   }
64   return DirBegin(file_spec.GetPath(), ec);
65 }
66 
67 vfs::directory_iterator FileSystem::DirBegin(const Twine &dir,
68                                              std::error_code &ec) {
69   return m_fs->dir_begin(dir, ec);
70 }
71 
72 llvm::ErrorOr<vfs::Status>
73 FileSystem::GetStatus(const FileSpec &file_spec) const {
74   if (!file_spec)
75     return std::error_code(static_cast<int>(errc::no_such_file_or_directory),
76                            std::system_category());
77   return GetStatus(file_spec.GetPath());
78 }
79 
80 llvm::ErrorOr<vfs::Status> FileSystem::GetStatus(const Twine &path) const {
81   return m_fs->status(path);
82 }
83 
84 sys::TimePoint<>
85 FileSystem::GetModificationTime(const FileSpec &file_spec) const {
86   if (!file_spec)
87     return sys::TimePoint<>();
88   return GetModificationTime(file_spec.GetPath());
89 }
90 
91 sys::TimePoint<> FileSystem::GetModificationTime(const Twine &path) const {
92   ErrorOr<vfs::Status> status = m_fs->status(path);
93   if (!status)
94     return sys::TimePoint<>();
95   return status->getLastModificationTime();
96 }
97 
98 uint64_t FileSystem::GetByteSize(const FileSpec &file_spec) const {
99   if (!file_spec)
100     return 0;
101   return GetByteSize(file_spec.GetPath());
102 }
103 
104 uint64_t FileSystem::GetByteSize(const Twine &path) const {
105   ErrorOr<vfs::Status> status = m_fs->status(path);
106   if (!status)
107     return 0;
108   return status->getSize();
109 }
110 
111 uint32_t FileSystem::GetPermissions(const FileSpec &file_spec) const {
112   return GetPermissions(file_spec.GetPath());
113 }
114 
115 uint32_t FileSystem::GetPermissions(const FileSpec &file_spec,
116                                     std::error_code &ec) const {
117   if (!file_spec)
118     return sys::fs::perms::perms_not_known;
119   return GetPermissions(file_spec.GetPath(), ec);
120 }
121 
122 uint32_t FileSystem::GetPermissions(const Twine &path) const {
123   std::error_code ec;
124   return GetPermissions(path, ec);
125 }
126 
127 uint32_t FileSystem::GetPermissions(const Twine &path,
128                                     std::error_code &ec) const {
129   ErrorOr<vfs::Status> status = m_fs->status(path);
130   if (!status) {
131     ec = status.getError();
132     return sys::fs::perms::perms_not_known;
133   }
134   return status->getPermissions();
135 }
136 
137 bool FileSystem::Exists(const Twine &path) const { return m_fs->exists(path); }
138 
139 bool FileSystem::Exists(const FileSpec &file_spec) const {
140   return file_spec && Exists(file_spec.GetPath());
141 }
142 
143 bool FileSystem::Readable(const Twine &path) const {
144   return GetPermissions(path) & sys::fs::perms::all_read;
145 }
146 
147 bool FileSystem::Readable(const FileSpec &file_spec) const {
148   return file_spec && Readable(file_spec.GetPath());
149 }
150 
151 bool FileSystem::IsDirectory(const Twine &path) const {
152   ErrorOr<vfs::Status> status = m_fs->status(path);
153   if (!status)
154     return false;
155   return status->isDirectory();
156 }
157 
158 bool FileSystem::IsDirectory(const FileSpec &file_spec) const {
159   return file_spec && IsDirectory(file_spec.GetPath());
160 }
161 
162 bool FileSystem::IsLocal(const Twine &path) const {
163   bool b = false;
164   m_fs->isLocal(path, b);
165   return b;
166 }
167 
168 bool FileSystem::IsLocal(const FileSpec &file_spec) const {
169   return file_spec && IsLocal(file_spec.GetPath());
170 }
171 
172 void FileSystem::EnumerateDirectory(Twine path, bool find_directories,
173                                     bool find_files, bool find_other,
174                                     EnumerateDirectoryCallbackType callback,
175                                     void *callback_baton) {
176   std::error_code EC;
177   vfs::recursive_directory_iterator Iter(*m_fs, path, EC);
178   vfs::recursive_directory_iterator End;
179   for (; Iter != End && !EC; Iter.increment(EC)) {
180     const auto &Item = *Iter;
181     ErrorOr<vfs::Status> Status = m_fs->status(Item.path());
182     if (!Status)
183       continue;
184     if (!find_files && Status->isRegularFile())
185       continue;
186     if (!find_directories && Status->isDirectory())
187       continue;
188     if (!find_other && Status->isOther())
189       continue;
190 
191     auto Result = callback(callback_baton, Status->getType(), Item.path());
192     if (Result == eEnumerateDirectoryResultQuit)
193       return;
194     if (Result == eEnumerateDirectoryResultNext) {
195       // Default behavior is to recurse. Opt out if the callback doesn't want
196       // this behavior.
197       Iter.no_push();
198     }
199   }
200 }
201 
202 std::error_code FileSystem::MakeAbsolute(SmallVectorImpl<char> &path) const {
203   return m_fs->makeAbsolute(path);
204 }
205 
206 std::error_code FileSystem::MakeAbsolute(FileSpec &file_spec) const {
207   SmallString<128> path;
208   file_spec.GetPath(path, false);
209 
210   auto EC = MakeAbsolute(path);
211   if (EC)
212     return EC;
213 
214   FileSpec new_file_spec(path, file_spec.GetPathStyle());
215   file_spec = new_file_spec;
216   return {};
217 }
218 
219 std::error_code FileSystem::GetRealPath(const Twine &path,
220                                         SmallVectorImpl<char> &output) const {
221   return m_fs->getRealPath(path, output);
222 }
223 
224 void FileSystem::Resolve(SmallVectorImpl<char> &path) {
225   if (path.empty())
226     return;
227 
228   // Resolve tilde in path.
229   SmallString<128> resolved(path.begin(), path.end());
230   assert(m_tilde_resolver && "must initialize tilde resolver in constructor");
231   m_tilde_resolver->ResolveFullPath(llvm::StringRef(path.begin(), path.size()),
232                                     resolved);
233 
234   // Try making the path absolute if it exists.
235   SmallString<128> absolute(resolved.begin(), resolved.end());
236   MakeAbsolute(absolute);
237 
238   path.clear();
239   if (Exists(absolute)) {
240     path.append(absolute.begin(), absolute.end());
241   } else {
242     path.append(resolved.begin(), resolved.end());
243   }
244 }
245 
246 void FileSystem::Resolve(FileSpec &file_spec) {
247   if (!file_spec)
248     return;
249 
250   // Extract path from the FileSpec.
251   SmallString<128> path;
252   file_spec.GetPath(path);
253 
254   // Resolve the path.
255   Resolve(path);
256 
257   // Update the FileSpec with the resolved path.
258   if (file_spec.GetFilename().IsEmpty())
259     file_spec.SetDirectory(path);
260   else
261     file_spec.SetPath(path);
262   file_spec.SetIsResolved(true);
263 }
264 
265 template <typename T>
266 static std::unique_ptr<T> GetMemoryBuffer(const llvm::Twine &path,
267                                           uint64_t size, uint64_t offset,
268                                           bool is_volatile) {
269   std::unique_ptr<T> buffer;
270   if (size == 0) {
271     auto buffer_or_error = T::getFile(path, is_volatile);
272     if (!buffer_or_error)
273       return nullptr;
274     buffer = std::move(*buffer_or_error);
275   } else {
276     auto buffer_or_error = T::getFileSlice(path, size, offset, is_volatile);
277     if (!buffer_or_error)
278       return nullptr;
279     buffer = std::move(*buffer_or_error);
280   }
281   return buffer;
282 }
283 
284 std::shared_ptr<WritableDataBuffer>
285 FileSystem::CreateWritableDataBuffer(const llvm::Twine &path, uint64_t size,
286                                      uint64_t offset) {
287   const bool is_volatile = !IsLocal(path);
288   auto buffer = GetMemoryBuffer<llvm::WritableMemoryBuffer>(path, size, offset,
289                                                             is_volatile);
290   if (!buffer)
291     return {};
292   return std::shared_ptr<WritableDataBufferLLVM>(
293       new WritableDataBufferLLVM(std::move(buffer)));
294 }
295 
296 std::shared_ptr<DataBuffer>
297 FileSystem::CreateDataBuffer(const llvm::Twine &path, uint64_t size,
298                              uint64_t offset) {
299   const bool is_volatile = !IsLocal(path);
300   auto buffer =
301       GetMemoryBuffer<llvm::MemoryBuffer>(path, size, offset, is_volatile);
302   if (!buffer)
303     return {};
304   return std::shared_ptr<DataBufferLLVM>(new DataBufferLLVM(std::move(buffer)));
305 }
306 
307 std::shared_ptr<WritableDataBuffer>
308 FileSystem::CreateWritableDataBuffer(const FileSpec &file_spec, uint64_t size,
309                                      uint64_t offset) {
310   return CreateWritableDataBuffer(file_spec.GetPath(), size, offset);
311 }
312 
313 std::shared_ptr<DataBuffer>
314 FileSystem::CreateDataBuffer(const FileSpec &file_spec, uint64_t size,
315                              uint64_t offset) {
316   return CreateDataBuffer(file_spec.GetPath(), size, offset);
317 }
318 
319 bool FileSystem::ResolveExecutableLocation(FileSpec &file_spec) {
320   // If the directory is set there's nothing to do.
321   ConstString directory = file_spec.GetDirectory();
322   if (directory)
323     return false;
324 
325   // We cannot look for a file if there's no file name.
326   ConstString filename = file_spec.GetFilename();
327   if (!filename)
328     return false;
329 
330   // Search for the file on the host.
331   const std::string filename_str(filename.GetCString());
332   llvm::ErrorOr<std::string> error_or_path =
333       llvm::sys::findProgramByName(filename_str);
334   if (!error_or_path)
335     return false;
336 
337   // findProgramByName returns "." if it can't find the file.
338   llvm::StringRef path = *error_or_path;
339   llvm::StringRef parent = llvm::sys::path::parent_path(path);
340   if (parent.empty() || parent == ".")
341     return false;
342 
343   // Make sure that the result exists.
344   FileSpec result(*error_or_path);
345   if (!Exists(result))
346     return false;
347 
348   file_spec = result;
349   return true;
350 }
351 
352 bool FileSystem::GetHomeDirectory(SmallVectorImpl<char> &path) const {
353   if (!m_home_directory.empty()) {
354     path.assign(m_home_directory.begin(), m_home_directory.end());
355     return true;
356   }
357   return llvm::sys::path::home_directory(path);
358 }
359 
360 bool FileSystem::GetHomeDirectory(FileSpec &file_spec) const {
361   SmallString<128> home_dir;
362   if (!GetHomeDirectory(home_dir))
363     return false;
364   file_spec.SetPath(home_dir);
365   return true;
366 }
367 
368 static int OpenWithFS(const FileSystem &fs, const char *path, int flags,
369                       int mode) {
370   return const_cast<FileSystem &>(fs).Open(path, flags, mode);
371 }
372 
373 static int GetOpenFlags(File::OpenOptions options) {
374   int open_flags = 0;
375   File::OpenOptions rw =
376       options & (File::eOpenOptionReadOnly | File::eOpenOptionWriteOnly |
377                  File::eOpenOptionReadWrite);
378   if (rw == File::eOpenOptionWriteOnly || rw == File::eOpenOptionReadWrite) {
379     if (rw == File::eOpenOptionReadWrite)
380       open_flags |= O_RDWR;
381     else
382       open_flags |= O_WRONLY;
383 
384     if (options & File::eOpenOptionAppend)
385       open_flags |= O_APPEND;
386 
387     if (options & File::eOpenOptionTruncate)
388       open_flags |= O_TRUNC;
389 
390     if (options & File::eOpenOptionCanCreate)
391       open_flags |= O_CREAT;
392 
393     if (options & File::eOpenOptionCanCreateNewOnly)
394       open_flags |= O_CREAT | O_EXCL;
395   } else if (rw == File::eOpenOptionReadOnly) {
396     open_flags |= O_RDONLY;
397 
398 #ifndef _WIN32
399     if (options & File::eOpenOptionDontFollowSymlinks)
400       open_flags |= O_NOFOLLOW;
401 #endif
402   }
403 
404 #ifndef _WIN32
405   if (options & File::eOpenOptionNonBlocking)
406     open_flags |= O_NONBLOCK;
407   if (options & File::eOpenOptionCloseOnExec)
408     open_flags |= O_CLOEXEC;
409 #else
410   open_flags |= O_BINARY;
411 #endif
412 
413   return open_flags;
414 }
415 
416 static mode_t GetOpenMode(uint32_t permissions) {
417   mode_t mode = 0;
418   if (permissions & lldb::eFilePermissionsUserRead)
419     mode |= S_IRUSR;
420   if (permissions & lldb::eFilePermissionsUserWrite)
421     mode |= S_IWUSR;
422   if (permissions & lldb::eFilePermissionsUserExecute)
423     mode |= S_IXUSR;
424   if (permissions & lldb::eFilePermissionsGroupRead)
425     mode |= S_IRGRP;
426   if (permissions & lldb::eFilePermissionsGroupWrite)
427     mode |= S_IWGRP;
428   if (permissions & lldb::eFilePermissionsGroupExecute)
429     mode |= S_IXGRP;
430   if (permissions & lldb::eFilePermissionsWorldRead)
431     mode |= S_IROTH;
432   if (permissions & lldb::eFilePermissionsWorldWrite)
433     mode |= S_IWOTH;
434   if (permissions & lldb::eFilePermissionsWorldExecute)
435     mode |= S_IXOTH;
436   return mode;
437 }
438 
439 Expected<FileUP> FileSystem::Open(const FileSpec &file_spec,
440                                   File::OpenOptions options,
441                                   uint32_t permissions, bool should_close_fd) {
442   const int open_flags = GetOpenFlags(options);
443   const mode_t open_mode =
444       (open_flags & O_CREAT) ? GetOpenMode(permissions) : 0;
445 
446   auto path = file_spec.GetPath();
447 
448   int descriptor = llvm::sys::RetryAfterSignal(
449       -1, OpenWithFS, *this, path.c_str(), open_flags, open_mode);
450 
451   if (!File::DescriptorIsValid(descriptor))
452     return llvm::errorCodeToError(
453         std::error_code(errno, std::system_category()));
454 
455   auto file = std::unique_ptr<File>(
456       new NativeFile(descriptor, options, should_close_fd));
457   assert(file->IsValid());
458   return std::move(file);
459 }
460 
461 void FileSystem::SetHomeDirectory(std::string home_directory) {
462   m_home_directory = std::move(home_directory);
463 }
464 
465 Status FileSystem::RemoveFile(const FileSpec &file_spec) {
466   return RemoveFile(file_spec.GetPath());
467 }
468 
469 Status FileSystem::RemoveFile(const llvm::Twine &path) {
470   return Status(llvm::sys::fs::remove(path));
471 }
472