forked from OSchip/llvm-project
				
			
		
			
				
	
	
		
			467 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			467 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
//===-- FileSystem.cpp ------------------------------------------*- C++ -*-===//
 | 
						|
//
 | 
						|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 | 
						|
// See https://llvm.org/LICENSE.txt for license information.
 | 
						|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 | 
						|
//
 | 
						|
//===----------------------------------------------------------------------===//
 | 
						|
 | 
						|
#include "lldb/Host/FileSystem.h"
 | 
						|
 | 
						|
#include "lldb/Utility/LLDBAssert.h"
 | 
						|
#include "lldb/Utility/TildeExpressionResolver.h"
 | 
						|
 | 
						|
#include "llvm/Support/Errc.h"
 | 
						|
#include "llvm/Support/Errno.h"
 | 
						|
#include "llvm/Support/Error.h"
 | 
						|
#include "llvm/Support/FileSystem.h"
 | 
						|
#include "llvm/Support/Path.h"
 | 
						|
#include "llvm/Support/Program.h"
 | 
						|
#include "llvm/Support/Threading.h"
 | 
						|
 | 
						|
#include <errno.h>
 | 
						|
#include <fcntl.h>
 | 
						|
#include <limits.h>
 | 
						|
#include <stdarg.h>
 | 
						|
#include <stdio.h>
 | 
						|
 | 
						|
#ifdef _WIN32
 | 
						|
#include "lldb/Host/windows/windows.h"
 | 
						|
#else
 | 
						|
#include <sys/ioctl.h>
 | 
						|
#include <sys/stat.h>
 | 
						|
#include <termios.h>
 | 
						|
#include <unistd.h>
 | 
						|
#endif
 | 
						|
 | 
						|
#include <algorithm>
 | 
						|
#include <fstream>
 | 
						|
#include <vector>
 | 
						|
 | 
						|
using namespace lldb;
 | 
						|
using namespace lldb_private;
 | 
						|
using namespace llvm;
 | 
						|
 | 
						|
FileSystem &FileSystem::Instance() { return *InstanceImpl(); }
 | 
						|
 | 
						|
void FileSystem::Initialize() {
 | 
						|
  lldbassert(!InstanceImpl() && "Already initialized.");
 | 
						|
  InstanceImpl().emplace();
 | 
						|
}
 | 
						|
 | 
						|
void FileSystem::Initialize(FileCollector &collector) {
 | 
						|
  lldbassert(!InstanceImpl() && "Already initialized.");
 | 
						|
  InstanceImpl().emplace(collector);
 | 
						|
}
 | 
						|
 | 
						|
llvm::Error FileSystem::Initialize(const FileSpec &mapping) {
 | 
						|
  lldbassert(!InstanceImpl() && "Already initialized.");
 | 
						|
 | 
						|
  llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> buffer =
 | 
						|
      llvm::vfs::getRealFileSystem()->getBufferForFile(mapping.GetPath());
 | 
						|
 | 
						|
  if (!buffer)
 | 
						|
    return llvm::errorCodeToError(buffer.getError());
 | 
						|
 | 
						|
  InstanceImpl().emplace(
 | 
						|
      llvm::vfs::getVFSFromYAML(std::move(buffer.get()), nullptr, ""), true);
 | 
						|
 | 
						|
  return llvm::Error::success();
 | 
						|
}
 | 
						|
 | 
						|
void FileSystem::Initialize(IntrusiveRefCntPtr<vfs::FileSystem> fs) {
 | 
						|
  lldbassert(!InstanceImpl() && "Already initialized.");
 | 
						|
  InstanceImpl().emplace(fs);
 | 
						|
}
 | 
						|
 | 
						|
void FileSystem::Terminate() {
 | 
						|
  lldbassert(InstanceImpl() && "Already terminated.");
 | 
						|
  InstanceImpl().reset();
 | 
						|
}
 | 
						|
 | 
						|
Optional<FileSystem> &FileSystem::InstanceImpl() {
 | 
						|
  static Optional<FileSystem> g_fs;
 | 
						|
  return g_fs;
 | 
						|
}
 | 
						|
 | 
						|
vfs::directory_iterator FileSystem::DirBegin(const FileSpec &file_spec,
 | 
						|
                                             std::error_code &ec) {
 | 
						|
  return DirBegin(file_spec.GetPath(), ec);
 | 
						|
}
 | 
						|
 | 
						|
vfs::directory_iterator FileSystem::DirBegin(const Twine &dir,
 | 
						|
                                             std::error_code &ec) {
 | 
						|
  return m_fs->dir_begin(dir, ec);
 | 
						|
}
 | 
						|
 | 
						|
llvm::ErrorOr<vfs::Status>
 | 
						|
FileSystem::GetStatus(const FileSpec &file_spec) const {
 | 
						|
  return GetStatus(file_spec.GetPath());
 | 
						|
}
 | 
						|
 | 
						|
llvm::ErrorOr<vfs::Status> FileSystem::GetStatus(const Twine &path) const {
 | 
						|
  return m_fs->status(path);
 | 
						|
}
 | 
						|
 | 
						|
sys::TimePoint<>
 | 
						|
FileSystem::GetModificationTime(const FileSpec &file_spec) const {
 | 
						|
  return GetModificationTime(file_spec.GetPath());
 | 
						|
}
 | 
						|
 | 
						|
sys::TimePoint<> FileSystem::GetModificationTime(const Twine &path) const {
 | 
						|
  ErrorOr<vfs::Status> status = m_fs->status(path);
 | 
						|
  if (!status)
 | 
						|
    return sys::TimePoint<>();
 | 
						|
  return status->getLastModificationTime();
 | 
						|
}
 | 
						|
 | 
						|
uint64_t FileSystem::GetByteSize(const FileSpec &file_spec) const {
 | 
						|
  return GetByteSize(file_spec.GetPath());
 | 
						|
}
 | 
						|
 | 
						|
uint64_t FileSystem::GetByteSize(const Twine &path) const {
 | 
						|
  ErrorOr<vfs::Status> status = m_fs->status(path);
 | 
						|
  if (!status)
 | 
						|
    return 0;
 | 
						|
  return status->getSize();
 | 
						|
}
 | 
						|
 | 
						|
uint32_t FileSystem::GetPermissions(const FileSpec &file_spec) const {
 | 
						|
  return GetPermissions(file_spec.GetPath());
 | 
						|
}
 | 
						|
 | 
						|
uint32_t FileSystem::GetPermissions(const FileSpec &file_spec,
 | 
						|
                                    std::error_code &ec) const {
 | 
						|
  return GetPermissions(file_spec.GetPath(), ec);
 | 
						|
}
 | 
						|
 | 
						|
uint32_t FileSystem::GetPermissions(const Twine &path) const {
 | 
						|
  std::error_code ec;
 | 
						|
  return GetPermissions(path, ec);
 | 
						|
}
 | 
						|
 | 
						|
uint32_t FileSystem::GetPermissions(const Twine &path,
 | 
						|
                                    std::error_code &ec) const {
 | 
						|
  ErrorOr<vfs::Status> status = m_fs->status(path);
 | 
						|
  if (!status) {
 | 
						|
    ec = status.getError();
 | 
						|
    return sys::fs::perms::perms_not_known;
 | 
						|
  }
 | 
						|
  return status->getPermissions();
 | 
						|
}
 | 
						|
 | 
						|
bool FileSystem::Exists(const Twine &path) const { return m_fs->exists(path); }
 | 
						|
 | 
						|
bool FileSystem::Exists(const FileSpec &file_spec) const {
 | 
						|
  return Exists(file_spec.GetPath());
 | 
						|
}
 | 
						|
 | 
						|
bool FileSystem::Readable(const Twine &path) const {
 | 
						|
  return GetPermissions(path) & sys::fs::perms::all_read;
 | 
						|
}
 | 
						|
 | 
						|
bool FileSystem::Readable(const FileSpec &file_spec) const {
 | 
						|
  return Readable(file_spec.GetPath());
 | 
						|
}
 | 
						|
 | 
						|
bool FileSystem::IsDirectory(const Twine &path) const {
 | 
						|
  ErrorOr<vfs::Status> status = m_fs->status(path);
 | 
						|
  if (!status)
 | 
						|
    return false;
 | 
						|
  return status->isDirectory();
 | 
						|
}
 | 
						|
 | 
						|
bool FileSystem::IsDirectory(const FileSpec &file_spec) const {
 | 
						|
  return IsDirectory(file_spec.GetPath());
 | 
						|
}
 | 
						|
 | 
						|
bool FileSystem::IsLocal(const Twine &path) const {
 | 
						|
  bool b = false;
 | 
						|
  m_fs->isLocal(path, b);
 | 
						|
  return b;
 | 
						|
}
 | 
						|
 | 
						|
bool FileSystem::IsLocal(const FileSpec &file_spec) const {
 | 
						|
  return IsLocal(file_spec.GetPath());
 | 
						|
}
 | 
						|
 | 
						|
void FileSystem::EnumerateDirectory(Twine path, bool find_directories,
 | 
						|
                                    bool find_files, bool find_other,
 | 
						|
                                    EnumerateDirectoryCallbackType callback,
 | 
						|
                                    void *callback_baton) {
 | 
						|
  std::error_code EC;
 | 
						|
  vfs::recursive_directory_iterator Iter(*m_fs, path, EC);
 | 
						|
  vfs::recursive_directory_iterator End;
 | 
						|
  for (; Iter != End && !EC; Iter.increment(EC)) {
 | 
						|
    const auto &Item = *Iter;
 | 
						|
    ErrorOr<vfs::Status> Status = m_fs->status(Item.path());
 | 
						|
    if (!Status)
 | 
						|
      break;
 | 
						|
    if (!find_files && Status->isRegularFile())
 | 
						|
      continue;
 | 
						|
    if (!find_directories && Status->isDirectory())
 | 
						|
      continue;
 | 
						|
    if (!find_other && Status->isOther())
 | 
						|
      continue;
 | 
						|
 | 
						|
    auto Result = callback(callback_baton, Status->getType(), Item.path());
 | 
						|
    if (Result == eEnumerateDirectoryResultQuit)
 | 
						|
      return;
 | 
						|
    if (Result == eEnumerateDirectoryResultNext) {
 | 
						|
      // Default behavior is to recurse. Opt out if the callback doesn't want
 | 
						|
      // this behavior.
 | 
						|
      Iter.no_push();
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
std::error_code FileSystem::MakeAbsolute(SmallVectorImpl<char> &path) const {
 | 
						|
  return m_fs->makeAbsolute(path);
 | 
						|
}
 | 
						|
 | 
						|
std::error_code FileSystem::MakeAbsolute(FileSpec &file_spec) const {
 | 
						|
  SmallString<128> path;
 | 
						|
  file_spec.GetPath(path, false);
 | 
						|
 | 
						|
  auto EC = MakeAbsolute(path);
 | 
						|
  if (EC)
 | 
						|
    return EC;
 | 
						|
 | 
						|
  FileSpec new_file_spec(path, file_spec.GetPathStyle());
 | 
						|
  file_spec = new_file_spec;
 | 
						|
  return {};
 | 
						|
}
 | 
						|
 | 
						|
std::error_code FileSystem::GetRealPath(const Twine &path,
 | 
						|
                                        SmallVectorImpl<char> &output) const {
 | 
						|
  return m_fs->getRealPath(path, output);
 | 
						|
}
 | 
						|
 | 
						|
void FileSystem::Resolve(SmallVectorImpl<char> &path) {
 | 
						|
  if (path.empty())
 | 
						|
    return;
 | 
						|
 | 
						|
  // Resolve tilde.
 | 
						|
  SmallString<128> original_path(path.begin(), path.end());
 | 
						|
  StandardTildeExpressionResolver Resolver;
 | 
						|
  Resolver.ResolveFullPath(original_path, path);
 | 
						|
 | 
						|
  // Try making the path absolute if it exists.
 | 
						|
  SmallString<128> absolute_path(path.begin(), path.end());
 | 
						|
  MakeAbsolute(path);
 | 
						|
  if (!Exists(path)) {
 | 
						|
    path.clear();
 | 
						|
    path.append(original_path.begin(), original_path.end());
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void FileSystem::Resolve(FileSpec &file_spec) {
 | 
						|
  // Extract path from the FileSpec.
 | 
						|
  SmallString<128> path;
 | 
						|
  file_spec.GetPath(path);
 | 
						|
 | 
						|
  // Resolve the path.
 | 
						|
  Resolve(path);
 | 
						|
 | 
						|
  // Update the FileSpec with the resolved path.
 | 
						|
  if (file_spec.GetFilename().IsEmpty())
 | 
						|
    file_spec.GetDirectory().SetString(path);
 | 
						|
  else
 | 
						|
    file_spec.SetPath(path);
 | 
						|
  file_spec.SetIsResolved(true);
 | 
						|
}
 | 
						|
 | 
						|
std::shared_ptr<DataBufferLLVM>
 | 
						|
FileSystem::CreateDataBuffer(const llvm::Twine &path, uint64_t size,
 | 
						|
                             uint64_t offset) {
 | 
						|
  if (m_collector)
 | 
						|
    m_collector->AddFile(path);
 | 
						|
 | 
						|
  const bool is_volatile = !IsLocal(path);
 | 
						|
  const ErrorOr<std::string> external_path = GetExternalPath(path);
 | 
						|
 | 
						|
  if (!external_path)
 | 
						|
    return nullptr;
 | 
						|
 | 
						|
  std::unique_ptr<llvm::WritableMemoryBuffer> buffer;
 | 
						|
  if (size == 0) {
 | 
						|
    auto buffer_or_error =
 | 
						|
        llvm::WritableMemoryBuffer::getFile(*external_path, -1, is_volatile);
 | 
						|
    if (!buffer_or_error)
 | 
						|
      return nullptr;
 | 
						|
    buffer = std::move(*buffer_or_error);
 | 
						|
  } else {
 | 
						|
    auto buffer_or_error = llvm::WritableMemoryBuffer::getFileSlice(
 | 
						|
        *external_path, size, offset, is_volatile);
 | 
						|
    if (!buffer_or_error)
 | 
						|
      return nullptr;
 | 
						|
    buffer = std::move(*buffer_or_error);
 | 
						|
  }
 | 
						|
  return std::shared_ptr<DataBufferLLVM>(new DataBufferLLVM(std::move(buffer)));
 | 
						|
}
 | 
						|
 | 
						|
std::shared_ptr<DataBufferLLVM>
 | 
						|
FileSystem::CreateDataBuffer(const FileSpec &file_spec, uint64_t size,
 | 
						|
                             uint64_t offset) {
 | 
						|
  return CreateDataBuffer(file_spec.GetPath(), size, offset);
 | 
						|
}
 | 
						|
 | 
						|
bool FileSystem::ResolveExecutableLocation(FileSpec &file_spec) {
 | 
						|
  // If the directory is set there's nothing to do.
 | 
						|
  ConstString directory = file_spec.GetDirectory();
 | 
						|
  if (directory)
 | 
						|
    return false;
 | 
						|
 | 
						|
  // We cannot look for a file if there's no file name.
 | 
						|
  ConstString filename = file_spec.GetFilename();
 | 
						|
  if (!filename)
 | 
						|
    return false;
 | 
						|
 | 
						|
  // Search for the file on the host.
 | 
						|
  const std::string filename_str(filename.GetCString());
 | 
						|
  llvm::ErrorOr<std::string> error_or_path =
 | 
						|
      llvm::sys::findProgramByName(filename_str);
 | 
						|
  if (!error_or_path)
 | 
						|
    return false;
 | 
						|
 | 
						|
  // findProgramByName returns "." if it can't find the file.
 | 
						|
  llvm::StringRef path = *error_or_path;
 | 
						|
  llvm::StringRef parent = llvm::sys::path::parent_path(path);
 | 
						|
  if (parent.empty() || parent == ".")
 | 
						|
    return false;
 | 
						|
 | 
						|
  // Make sure that the result exists.
 | 
						|
  FileSpec result(*error_or_path);
 | 
						|
  if (!Exists(result))
 | 
						|
    return false;
 | 
						|
 | 
						|
  file_spec = result;
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
static int OpenWithFS(const FileSystem &fs, const char *path, int flags,
 | 
						|
                      int mode) {
 | 
						|
  return const_cast<FileSystem &>(fs).Open(path, flags, mode);
 | 
						|
}
 | 
						|
 | 
						|
static int GetOpenFlags(uint32_t options) {
 | 
						|
  const bool read = options & File::eOpenOptionRead;
 | 
						|
  const bool write = options & File::eOpenOptionWrite;
 | 
						|
 | 
						|
  int open_flags = 0;
 | 
						|
  if (write) {
 | 
						|
    if (read)
 | 
						|
      open_flags |= O_RDWR;
 | 
						|
    else
 | 
						|
      open_flags |= O_WRONLY;
 | 
						|
 | 
						|
    if (options & File::eOpenOptionAppend)
 | 
						|
      open_flags |= O_APPEND;
 | 
						|
 | 
						|
    if (options & File::eOpenOptionTruncate)
 | 
						|
      open_flags |= O_TRUNC;
 | 
						|
 | 
						|
    if (options & File::eOpenOptionCanCreate)
 | 
						|
      open_flags |= O_CREAT;
 | 
						|
 | 
						|
    if (options & File::eOpenOptionCanCreateNewOnly)
 | 
						|
      open_flags |= O_CREAT | O_EXCL;
 | 
						|
  } else if (read) {
 | 
						|
    open_flags |= O_RDONLY;
 | 
						|
 | 
						|
#ifndef _WIN32
 | 
						|
    if (options & File::eOpenOptionDontFollowSymlinks)
 | 
						|
      open_flags |= O_NOFOLLOW;
 | 
						|
#endif
 | 
						|
  }
 | 
						|
 | 
						|
#ifndef _WIN32
 | 
						|
  if (options & File::eOpenOptionNonBlocking)
 | 
						|
    open_flags |= O_NONBLOCK;
 | 
						|
  if (options & File::eOpenOptionCloseOnExec)
 | 
						|
    open_flags |= O_CLOEXEC;
 | 
						|
#else
 | 
						|
  open_flags |= O_BINARY;
 | 
						|
#endif
 | 
						|
 | 
						|
  return open_flags;
 | 
						|
}
 | 
						|
 | 
						|
static mode_t GetOpenMode(uint32_t permissions) {
 | 
						|
  mode_t mode = 0;
 | 
						|
  if (permissions & lldb::eFilePermissionsUserRead)
 | 
						|
    mode |= S_IRUSR;
 | 
						|
  if (permissions & lldb::eFilePermissionsUserWrite)
 | 
						|
    mode |= S_IWUSR;
 | 
						|
  if (permissions & lldb::eFilePermissionsUserExecute)
 | 
						|
    mode |= S_IXUSR;
 | 
						|
  if (permissions & lldb::eFilePermissionsGroupRead)
 | 
						|
    mode |= S_IRGRP;
 | 
						|
  if (permissions & lldb::eFilePermissionsGroupWrite)
 | 
						|
    mode |= S_IWGRP;
 | 
						|
  if (permissions & lldb::eFilePermissionsGroupExecute)
 | 
						|
    mode |= S_IXGRP;
 | 
						|
  if (permissions & lldb::eFilePermissionsWorldRead)
 | 
						|
    mode |= S_IROTH;
 | 
						|
  if (permissions & lldb::eFilePermissionsWorldWrite)
 | 
						|
    mode |= S_IWOTH;
 | 
						|
  if (permissions & lldb::eFilePermissionsWorldExecute)
 | 
						|
    mode |= S_IXOTH;
 | 
						|
  return mode;
 | 
						|
}
 | 
						|
 | 
						|
Status FileSystem::Open(File &File, const FileSpec &file_spec, uint32_t options,
 | 
						|
                        uint32_t permissions, bool should_close_fd) {
 | 
						|
  if (m_collector)
 | 
						|
    m_collector->AddFile(file_spec);
 | 
						|
 | 
						|
  if (File.IsValid())
 | 
						|
    File.Close();
 | 
						|
 | 
						|
  const int open_flags = GetOpenFlags(options);
 | 
						|
  const mode_t open_mode =
 | 
						|
      (open_flags & O_CREAT) ? GetOpenMode(permissions) : 0;
 | 
						|
 | 
						|
  auto path = GetExternalPath(file_spec);
 | 
						|
  if (!path)
 | 
						|
    return Status(path.getError());
 | 
						|
 | 
						|
  int descriptor = llvm::sys::RetryAfterSignal(
 | 
						|
      -1, OpenWithFS, *this, path->c_str(), open_flags, open_mode);
 | 
						|
 | 
						|
  Status error;
 | 
						|
  if (!File::DescriptorIsValid(descriptor)) {
 | 
						|
    File.SetDescriptor(descriptor, false);
 | 
						|
    error.SetErrorToErrno();
 | 
						|
  } else {
 | 
						|
    File.SetDescriptor(descriptor, should_close_fd);
 | 
						|
    File.SetOptions(options);
 | 
						|
  }
 | 
						|
  return error;
 | 
						|
}
 | 
						|
 | 
						|
ErrorOr<std::string> FileSystem::GetExternalPath(const llvm::Twine &path) {
 | 
						|
  if (!m_mapped)
 | 
						|
    return path.str();
 | 
						|
 | 
						|
  // If VFS mapped we know the underlying FS is a RedirectingFileSystem.
 | 
						|
  ErrorOr<vfs::RedirectingFileSystem::Entry *> E =
 | 
						|
      static_cast<vfs::RedirectingFileSystem &>(*m_fs).lookupPath(path);
 | 
						|
  if (!E) {
 | 
						|
    if (E.getError() == llvm::errc::no_such_file_or_directory) {
 | 
						|
      return path.str();
 | 
						|
    }
 | 
						|
    return E.getError();
 | 
						|
  }
 | 
						|
 | 
						|
  auto *F = dyn_cast<vfs::RedirectingFileSystem::RedirectingFileEntry>(*E);
 | 
						|
  if (!F)
 | 
						|
    return make_error_code(llvm::errc::not_supported);
 | 
						|
 | 
						|
  return F->getExternalContentsPath().str();
 | 
						|
}
 | 
						|
 | 
						|
ErrorOr<std::string> FileSystem::GetExternalPath(const FileSpec &file_spec) {
 | 
						|
  return GetExternalPath(file_spec.GetPath());
 | 
						|
}
 |