xref: /freebsd/contrib/llvm-project/lldb/source/Host/common/FileSystem.cpp (revision cb14a3fe5122c879eae1fb480ed7ce82a699ddb6)
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 }
263 
264 template <typename T>
265 static std::unique_ptr<T> GetMemoryBuffer(const llvm::Twine &path,
266                                           uint64_t size, uint64_t offset,
267                                           bool is_volatile) {
268   std::unique_ptr<T> buffer;
269   if (size == 0) {
270     auto buffer_or_error = T::getFile(path, is_volatile);
271     if (!buffer_or_error)
272       return nullptr;
273     buffer = std::move(*buffer_or_error);
274   } else {
275     auto buffer_or_error = T::getFileSlice(path, size, offset, is_volatile);
276     if (!buffer_or_error)
277       return nullptr;
278     buffer = std::move(*buffer_or_error);
279   }
280   return buffer;
281 }
282 
283 std::shared_ptr<WritableDataBuffer>
284 FileSystem::CreateWritableDataBuffer(const llvm::Twine &path, uint64_t size,
285                                      uint64_t offset) {
286   const bool is_volatile = !IsLocal(path);
287   auto buffer = GetMemoryBuffer<llvm::WritableMemoryBuffer>(path, size, offset,
288                                                             is_volatile);
289   if (!buffer)
290     return {};
291   return std::shared_ptr<WritableDataBufferLLVM>(
292       new WritableDataBufferLLVM(std::move(buffer)));
293 }
294 
295 std::shared_ptr<DataBuffer>
296 FileSystem::CreateDataBuffer(const llvm::Twine &path, uint64_t size,
297                              uint64_t offset) {
298   const bool is_volatile = !IsLocal(path);
299   auto buffer =
300       GetMemoryBuffer<llvm::MemoryBuffer>(path, size, offset, is_volatile);
301   if (!buffer)
302     return {};
303   return std::shared_ptr<DataBufferLLVM>(new DataBufferLLVM(std::move(buffer)));
304 }
305 
306 std::shared_ptr<WritableDataBuffer>
307 FileSystem::CreateWritableDataBuffer(const FileSpec &file_spec, uint64_t size,
308                                      uint64_t offset) {
309   return CreateWritableDataBuffer(file_spec.GetPath(), size, offset);
310 }
311 
312 std::shared_ptr<DataBuffer>
313 FileSystem::CreateDataBuffer(const FileSpec &file_spec, uint64_t size,
314                              uint64_t offset) {
315   return CreateDataBuffer(file_spec.GetPath(), size, offset);
316 }
317 
318 bool FileSystem::ResolveExecutableLocation(FileSpec &file_spec) {
319   // If the directory is set there's nothing to do.
320   ConstString directory = file_spec.GetDirectory();
321   if (directory)
322     return false;
323 
324   // We cannot look for a file if there's no file name.
325   ConstString filename = file_spec.GetFilename();
326   if (!filename)
327     return false;
328 
329   // Search for the file on the host.
330   const std::string filename_str(filename.GetCString());
331   llvm::ErrorOr<std::string> error_or_path =
332       llvm::sys::findProgramByName(filename_str);
333   if (!error_or_path)
334     return false;
335 
336   // findProgramByName returns "." if it can't find the file.
337   llvm::StringRef path = *error_or_path;
338   llvm::StringRef parent = llvm::sys::path::parent_path(path);
339   if (parent.empty() || parent == ".")
340     return false;
341 
342   // Make sure that the result exists.
343   FileSpec result(*error_or_path);
344   if (!Exists(result))
345     return false;
346 
347   file_spec = result;
348   return true;
349 }
350 
351 bool FileSystem::GetHomeDirectory(SmallVectorImpl<char> &path) const {
352   if (!m_home_directory.empty()) {
353     path.assign(m_home_directory.begin(), m_home_directory.end());
354     return true;
355   }
356   return llvm::sys::path::home_directory(path);
357 }
358 
359 bool FileSystem::GetHomeDirectory(FileSpec &file_spec) const {
360   SmallString<128> home_dir;
361   if (!GetHomeDirectory(home_dir))
362     return false;
363   file_spec.SetPath(home_dir);
364   return true;
365 }
366 
367 static int OpenWithFS(const FileSystem &fs, const char *path, int flags,
368                       int mode) {
369   return const_cast<FileSystem &>(fs).Open(path, flags, mode);
370 }
371 
372 static int GetOpenFlags(File::OpenOptions options) {
373   int open_flags = 0;
374   File::OpenOptions rw =
375       options & (File::eOpenOptionReadOnly | File::eOpenOptionWriteOnly |
376                  File::eOpenOptionReadWrite);
377   if (rw == File::eOpenOptionWriteOnly || rw == File::eOpenOptionReadWrite) {
378     if (rw == File::eOpenOptionReadWrite)
379       open_flags |= O_RDWR;
380     else
381       open_flags |= O_WRONLY;
382 
383     if (options & File::eOpenOptionAppend)
384       open_flags |= O_APPEND;
385 
386     if (options & File::eOpenOptionTruncate)
387       open_flags |= O_TRUNC;
388 
389     if (options & File::eOpenOptionCanCreate)
390       open_flags |= O_CREAT;
391 
392     if (options & File::eOpenOptionCanCreateNewOnly)
393       open_flags |= O_CREAT | O_EXCL;
394   } else if (rw == File::eOpenOptionReadOnly) {
395     open_flags |= O_RDONLY;
396 
397 #ifndef _WIN32
398     if (options & File::eOpenOptionDontFollowSymlinks)
399       open_flags |= O_NOFOLLOW;
400 #endif
401   }
402 
403 #ifndef _WIN32
404   if (options & File::eOpenOptionNonBlocking)
405     open_flags |= O_NONBLOCK;
406   if (options & File::eOpenOptionCloseOnExec)
407     open_flags |= O_CLOEXEC;
408 #else
409   open_flags |= O_BINARY;
410 #endif
411 
412   return open_flags;
413 }
414 
415 static mode_t GetOpenMode(uint32_t permissions) {
416   mode_t mode = 0;
417   if (permissions & lldb::eFilePermissionsUserRead)
418     mode |= S_IRUSR;
419   if (permissions & lldb::eFilePermissionsUserWrite)
420     mode |= S_IWUSR;
421   if (permissions & lldb::eFilePermissionsUserExecute)
422     mode |= S_IXUSR;
423   if (permissions & lldb::eFilePermissionsGroupRead)
424     mode |= S_IRGRP;
425   if (permissions & lldb::eFilePermissionsGroupWrite)
426     mode |= S_IWGRP;
427   if (permissions & lldb::eFilePermissionsGroupExecute)
428     mode |= S_IXGRP;
429   if (permissions & lldb::eFilePermissionsWorldRead)
430     mode |= S_IROTH;
431   if (permissions & lldb::eFilePermissionsWorldWrite)
432     mode |= S_IWOTH;
433   if (permissions & lldb::eFilePermissionsWorldExecute)
434     mode |= S_IXOTH;
435   return mode;
436 }
437 
438 Expected<FileUP> FileSystem::Open(const FileSpec &file_spec,
439                                   File::OpenOptions options,
440                                   uint32_t permissions, bool should_close_fd) {
441   const int open_flags = GetOpenFlags(options);
442   const mode_t open_mode =
443       (open_flags & O_CREAT) ? GetOpenMode(permissions) : 0;
444 
445   auto path = file_spec.GetPath();
446 
447   int descriptor = llvm::sys::RetryAfterSignal(
448       -1, OpenWithFS, *this, path.c_str(), open_flags, open_mode);
449 
450   if (!File::DescriptorIsValid(descriptor))
451     return llvm::errorCodeToError(
452         std::error_code(errno, std::system_category()));
453 
454   auto file = std::unique_ptr<File>(
455       new NativeFile(descriptor, options, should_close_fd));
456   assert(file->IsValid());
457   return std::move(file);
458 }
459 
460 void FileSystem::SetHomeDirectory(std::string home_directory) {
461   m_home_directory = std::move(home_directory);
462 }
463 
464 Status FileSystem::RemoveFile(const FileSpec &file_spec) {
465   return RemoveFile(file_spec.GetPath());
466 }
467 
468 Status FileSystem::RemoveFile(const llvm::Twine &path) {
469   return Status(llvm::sys::fs::remove(path));
470 }
471