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