1 //===-- FileSystem.cpp ------------------------------------------*- C++ -*-===//
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/LLDBAssert.h"
12 #include "lldb/Utility/TildeExpressionResolver.h"
13 
14 #include "llvm/Support/Errno.h"
15 #include "llvm/Support/FileSystem.h"
16 #include "llvm/Support/Path.h"
17 #include "llvm/Support/Program.h"
18 #include "llvm/Support/Threading.h"
19 
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <limits.h>
23 #include <stdarg.h>
24 #include <stdio.h>
25 
26 #ifdef _WIN32
27 #include "lldb/Host/windows/windows.h"
28 #else
29 #include <sys/ioctl.h>
30 #include <sys/stat.h>
31 #include <termios.h>
32 #include <unistd.h>
33 #endif
34 
35 #include <algorithm>
36 #include <fstream>
37 #include <vector>
38 
39 using namespace lldb;
40 using namespace lldb_private;
41 using namespace llvm;
42 
43 FileSystem &FileSystem::Instance() { return *InstanceImpl(); }
44 
45 void FileSystem::Initialize() {
46   lldbassert(!InstanceImpl() && "Already initialized.");
47   InstanceImpl().emplace();
48 }
49 
50 void FileSystem::Initialize(IntrusiveRefCntPtr<vfs::FileSystem> fs) {
51   lldbassert(!InstanceImpl() && "Already initialized.");
52   InstanceImpl().emplace(fs);
53 }
54 
55 void FileSystem::Terminate() {
56   lldbassert(InstanceImpl() && "Already terminated.");
57   InstanceImpl().reset();
58 }
59 
60 Optional<FileSystem> &FileSystem::InstanceImpl() {
61   static Optional<FileSystem> g_fs;
62   return g_fs;
63 }
64 
65 vfs::directory_iterator FileSystem::DirBegin(const FileSpec &file_spec,
66                                              std::error_code &ec) {
67   return DirBegin(file_spec.GetPath(), ec);
68 }
69 
70 vfs::directory_iterator FileSystem::DirBegin(const Twine &dir,
71                                              std::error_code &ec) {
72   return m_fs->dir_begin(dir, ec);
73 }
74 
75 llvm::ErrorOr<vfs::Status>
76 FileSystem::GetStatus(const FileSpec &file_spec) const {
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   return GetModificationTime(file_spec.GetPath());
87 }
88 
89 sys::TimePoint<> FileSystem::GetModificationTime(const Twine &path) const {
90   ErrorOr<vfs::Status> status = m_fs->status(path);
91   if (!status)
92     return sys::TimePoint<>();
93   return status->getLastModificationTime();
94 }
95 
96 uint64_t FileSystem::GetByteSize(const FileSpec &file_spec) const {
97   return GetByteSize(file_spec.GetPath());
98 }
99 
100 uint64_t FileSystem::GetByteSize(const Twine &path) const {
101   ErrorOr<vfs::Status> status = m_fs->status(path);
102   if (!status)
103     return 0;
104   return status->getSize();
105 }
106 
107 uint32_t FileSystem::GetPermissions(const FileSpec &file_spec) const {
108   return GetPermissions(file_spec.GetPath());
109 }
110 
111 uint32_t FileSystem::GetPermissions(const FileSpec &file_spec,
112                                     std::error_code &ec) const {
113   return GetPermissions(file_spec.GetPath(), ec);
114 }
115 
116 uint32_t FileSystem::GetPermissions(const Twine &path) const {
117   std::error_code ec;
118   return GetPermissions(path, ec);
119 }
120 
121 uint32_t FileSystem::GetPermissions(const Twine &path,
122                                     std::error_code &ec) const {
123   ErrorOr<vfs::Status> status = m_fs->status(path);
124   if (!status) {
125     ec = status.getError();
126     return sys::fs::perms::perms_not_known;
127   }
128   return status->getPermissions();
129 }
130 
131 bool FileSystem::Exists(const Twine &path) const { return m_fs->exists(path); }
132 
133 bool FileSystem::Exists(const FileSpec &file_spec) const {
134   return Exists(file_spec.GetPath());
135 }
136 
137 bool FileSystem::Readable(const Twine &path) const {
138   return GetPermissions(path) & sys::fs::perms::all_read;
139 }
140 
141 bool FileSystem::Readable(const FileSpec &file_spec) const {
142   return Readable(file_spec.GetPath());
143 }
144 
145 bool FileSystem::IsDirectory(const Twine &path) const {
146   ErrorOr<vfs::Status> status = m_fs->status(path);
147   if (!status)
148     return false;
149   return status->isDirectory();
150 }
151 
152 bool FileSystem::IsDirectory(const FileSpec &file_spec) const {
153   return IsDirectory(file_spec.GetPath());
154 }
155 
156 bool FileSystem::IsLocal(const Twine &path) const {
157   bool b = false;
158   m_fs->isLocal(path, b);
159   return b;
160 }
161 
162 bool FileSystem::IsLocal(const FileSpec &file_spec) const {
163   return IsLocal(file_spec.GetPath());
164 }
165 
166 void FileSystem::EnumerateDirectory(Twine path, bool find_directories,
167                                     bool find_files, bool find_other,
168                                     EnumerateDirectoryCallbackType callback,
169                                     void *callback_baton) {
170   std::error_code EC;
171   vfs::recursive_directory_iterator Iter(*m_fs, path, EC);
172   vfs::recursive_directory_iterator End;
173   for (; Iter != End && !EC; Iter.increment(EC)) {
174     const auto &Item = *Iter;
175     ErrorOr<vfs::Status> Status = m_fs->status(Item.path());
176     if (!Status)
177       break;
178     if (!find_files && Status->isRegularFile())
179       continue;
180     if (!find_directories && Status->isDirectory())
181       continue;
182     if (!find_other && Status->isOther())
183       continue;
184 
185     auto Result = callback(callback_baton, Status->getType(), Item.path());
186     if (Result == eEnumerateDirectoryResultQuit)
187       return;
188     if (Result == eEnumerateDirectoryResultNext) {
189       // Default behavior is to recurse. Opt out if the callback doesn't want
190       // this behavior.
191       Iter.no_push();
192     }
193   }
194 }
195 
196 std::error_code FileSystem::MakeAbsolute(SmallVectorImpl<char> &path) const {
197   return m_fs->makeAbsolute(path);
198 }
199 
200 std::error_code FileSystem::MakeAbsolute(FileSpec &file_spec) const {
201   SmallString<128> path;
202   file_spec.GetPath(path, false);
203 
204   auto EC = MakeAbsolute(path);
205   if (EC)
206     return EC;
207 
208   FileSpec new_file_spec(path, file_spec.GetPathStyle());
209   file_spec = new_file_spec;
210   return {};
211 }
212 
213 std::error_code FileSystem::GetRealPath(const Twine &path,
214                                         SmallVectorImpl<char> &output) const {
215   return m_fs->getRealPath(path, output);
216 }
217 
218 void FileSystem::Resolve(SmallVectorImpl<char> &path) {
219   if (path.empty())
220     return;
221 
222   // Resolve tilde.
223   SmallString<128> original_path(path.begin(), path.end());
224   StandardTildeExpressionResolver Resolver;
225   Resolver.ResolveFullPath(original_path, path);
226 
227   // Try making the path absolute if it exists.
228   SmallString<128> absolute_path(path.begin(), path.end());
229   MakeAbsolute(path);
230   if (!Exists(path)) {
231     path.clear();
232     path.append(original_path.begin(), original_path.end());
233   }
234 }
235 
236 void FileSystem::Resolve(FileSpec &file_spec) {
237   // Extract path from the FileSpec.
238   SmallString<128> path;
239   file_spec.GetPath(path);
240 
241   // Resolve the path.
242   Resolve(path);
243 
244   // Update the FileSpec with the resolved path.
245   file_spec.SetPath(path);
246   file_spec.SetIsResolved(true);
247 }
248 
249 std::shared_ptr<DataBufferLLVM>
250 FileSystem::CreateDataBuffer(const llvm::Twine &path, uint64_t size,
251                              uint64_t offset) {
252   const bool is_volatile = !IsLocal(path);
253 
254   std::unique_ptr<llvm::WritableMemoryBuffer> buffer;
255   if (size == 0) {
256     auto buffer_or_error =
257         llvm::WritableMemoryBuffer::getFile(path, -1, is_volatile);
258     if (!buffer_or_error)
259       return nullptr;
260     buffer = std::move(*buffer_or_error);
261   } else {
262     auto buffer_or_error = llvm::WritableMemoryBuffer::getFileSlice(
263         path, size, offset, is_volatile);
264     if (!buffer_or_error)
265       return nullptr;
266     buffer = std::move(*buffer_or_error);
267   }
268   return std::shared_ptr<DataBufferLLVM>(new DataBufferLLVM(std::move(buffer)));
269 }
270 
271 std::shared_ptr<DataBufferLLVM>
272 FileSystem::CreateDataBuffer(const FileSpec &file_spec, uint64_t size,
273                              uint64_t offset) {
274   return CreateDataBuffer(file_spec.GetPath(), size, offset);
275 }
276 
277 bool FileSystem::ResolveExecutableLocation(FileSpec &file_spec) {
278   // If the directory is set there's nothing to do.
279   const ConstString &directory = file_spec.GetDirectory();
280   if (directory)
281     return false;
282 
283   // We cannot look for a file if there's no file name.
284   const ConstString &filename = file_spec.GetFilename();
285   if (!filename)
286     return false;
287 
288   // Search for the file on the host.
289   const std::string filename_str(filename.GetCString());
290   llvm::ErrorOr<std::string> error_or_path =
291       llvm::sys::findProgramByName(filename_str);
292   if (!error_or_path)
293     return false;
294 
295   // findProgramByName returns "." if it can't find the file.
296   llvm::StringRef path = *error_or_path;
297   llvm::StringRef parent = llvm::sys::path::parent_path(path);
298   if (parent.empty() || parent == ".")
299     return false;
300 
301   // Make sure that the result exists.
302   FileSpec result(*error_or_path);
303   if (!Exists(result))
304     return false;
305 
306   file_spec = result;
307   return true;
308 }
309 
310 static int OpenWithFS(const FileSystem &fs, const char *path, int flags,
311                       int mode) {
312   return const_cast<FileSystem &>(fs).Open(path, flags, mode);
313 }
314 
315 static int GetOpenFlags(uint32_t options) {
316   const bool read = options & File::eOpenOptionRead;
317   const bool write = options & File::eOpenOptionWrite;
318 
319   int open_flags = 0;
320   if (write) {
321     if (read)
322       open_flags |= O_RDWR;
323     else
324       open_flags |= O_WRONLY;
325 
326     if (options & File::eOpenOptionAppend)
327       open_flags |= O_APPEND;
328 
329     if (options & File::eOpenOptionTruncate)
330       open_flags |= O_TRUNC;
331 
332     if (options & File::eOpenOptionCanCreate)
333       open_flags |= O_CREAT;
334 
335     if (options & File::eOpenOptionCanCreateNewOnly)
336       open_flags |= O_CREAT | O_EXCL;
337   } else if (read) {
338     open_flags |= O_RDONLY;
339 
340 #ifndef _WIN32
341     if (options & File::eOpenOptionDontFollowSymlinks)
342       open_flags |= O_NOFOLLOW;
343 #endif
344   }
345 
346 #ifndef _WIN32
347   if (options & File::eOpenOptionNonBlocking)
348     open_flags |= O_NONBLOCK;
349   if (options & File::eOpenOptionCloseOnExec)
350     open_flags |= O_CLOEXEC;
351 #else
352   open_flags |= O_BINARY;
353 #endif
354 
355   return open_flags;
356 }
357 
358 static mode_t GetOpenMode(uint32_t permissions) {
359   mode_t mode = 0;
360   if (permissions & lldb::eFilePermissionsUserRead)
361     mode |= S_IRUSR;
362   if (permissions & lldb::eFilePermissionsUserWrite)
363     mode |= S_IWUSR;
364   if (permissions & lldb::eFilePermissionsUserExecute)
365     mode |= S_IXUSR;
366   if (permissions & lldb::eFilePermissionsGroupRead)
367     mode |= S_IRGRP;
368   if (permissions & lldb::eFilePermissionsGroupWrite)
369     mode |= S_IWGRP;
370   if (permissions & lldb::eFilePermissionsGroupExecute)
371     mode |= S_IXGRP;
372   if (permissions & lldb::eFilePermissionsWorldRead)
373     mode |= S_IROTH;
374   if (permissions & lldb::eFilePermissionsWorldWrite)
375     mode |= S_IWOTH;
376   if (permissions & lldb::eFilePermissionsWorldExecute)
377     mode |= S_IXOTH;
378   return mode;
379 }
380 
381 Status FileSystem::Open(File &File, const FileSpec &file_spec, uint32_t options,
382                         uint32_t permissions) {
383   if (File.IsValid())
384     File.Close();
385 
386   const int open_flags = GetOpenFlags(options);
387   const mode_t open_mode =
388       (open_flags & O_CREAT) ? GetOpenMode(permissions) : 0;
389   const std::string path = file_spec.GetPath();
390 
391   int descriptor = llvm::sys::RetryAfterSignal(
392       -1, OpenWithFS, *this, path.c_str(), open_flags, open_mode);
393 
394   Status error;
395   if (!File::DescriptorIsValid(descriptor)) {
396     File.SetDescriptor(descriptor, false);
397     error.SetErrorToErrno();
398   } else {
399     File.SetDescriptor(descriptor, true);
400     File.SetOptions(options);
401   }
402   return error;
403 }
404