diff options
Diffstat (limited to 'lldb/source/Host/common/File.cpp')
| -rw-r--r-- | lldb/source/Host/common/File.cpp | 758 | 
1 files changed, 758 insertions, 0 deletions
diff --git a/lldb/source/Host/common/File.cpp b/lldb/source/Host/common/File.cpp new file mode 100644 index 000000000000..9dae24d766f6 --- /dev/null +++ b/lldb/source/Host/common/File.cpp @@ -0,0 +1,758 @@ +//===-- File.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/File.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 "llvm/Support/ConvertUTF.h" +#include "llvm/Support/Errno.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Process.h" + +#include "lldb/Host/Config.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Host/Host.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/FileSpec.h" +#include "lldb/Utility/Log.h" + +using namespace lldb; +using namespace lldb_private; +using llvm::Expected; + +Expected<const char *> +File::GetStreamOpenModeFromOptions(File::OpenOptions options) { +  if (options & File::eOpenOptionAppend) { +    if (options & File::eOpenOptionRead) { +      if (options & File::eOpenOptionCanCreateNewOnly) +        return "a+x"; +      else +        return "a+"; +    } else if (options & File::eOpenOptionWrite) { +      if (options & File::eOpenOptionCanCreateNewOnly) +        return "ax"; +      else +        return "a"; +    } +  } else if (options & File::eOpenOptionRead && +             options & File::eOpenOptionWrite) { +    if (options & File::eOpenOptionCanCreate) { +      if (options & File::eOpenOptionCanCreateNewOnly) +        return "w+x"; +      else +        return "w+"; +    } else +      return "r+"; +  } else if (options & File::eOpenOptionRead) { +    return "r"; +  } else if (options & File::eOpenOptionWrite) { +    return "w"; +  } +  return llvm::createStringError( +      llvm::inconvertibleErrorCode(), +      "invalid options, cannot convert to mode string"); +} + +Expected<File::OpenOptions> File::GetOptionsFromMode(llvm::StringRef mode) { +  OpenOptions opts = +      llvm::StringSwitch<OpenOptions>(mode) +          .Cases("r", "rb", eOpenOptionRead) +          .Cases("w", "wb", eOpenOptionWrite) +          .Cases("a", "ab", +                 eOpenOptionWrite | eOpenOptionAppend | eOpenOptionCanCreate) +          .Cases("r+", "rb+", "r+b", eOpenOptionRead | eOpenOptionWrite) +          .Cases("w+", "wb+", "w+b", +                 eOpenOptionRead | eOpenOptionWrite | eOpenOptionCanCreate | +                     eOpenOptionTruncate) +          .Cases("a+", "ab+", "a+b", +                 eOpenOptionRead | eOpenOptionWrite | eOpenOptionAppend | +                     eOpenOptionCanCreate) +          .Default(OpenOptions()); +  if (opts) +    return opts; +  return llvm::createStringError( +      llvm::inconvertibleErrorCode(), +      "invalid mode, cannot convert to File::OpenOptions"); +} + +int File::kInvalidDescriptor = -1; +FILE *File::kInvalidStream = nullptr; + +Status File::Read(void *buf, size_t &num_bytes) { +  return std::error_code(ENOTSUP, std::system_category()); +} +Status File::Write(const void *buf, size_t &num_bytes) { +  return std::error_code(ENOTSUP, std::system_category()); +} + +bool File::IsValid() const { return false; } + +Status File::Close() { return Flush(); } + +IOObject::WaitableHandle File::GetWaitableHandle() { +  return IOObject::kInvalidHandleValue; +} + +Status File::GetFileSpec(FileSpec &file_spec) const { +  file_spec.Clear(); +  return std::error_code(ENOTSUP, std::system_category()); +} + +int File::GetDescriptor() const { return kInvalidDescriptor; } + +FILE *File::GetStream() { return nullptr; } + +off_t File::SeekFromStart(off_t offset, Status *error_ptr) { +  if (error_ptr) +    *error_ptr = std::error_code(ENOTSUP, std::system_category()); +  return -1; +} + +off_t File::SeekFromCurrent(off_t offset, Status *error_ptr) { +  if (error_ptr) +    *error_ptr = std::error_code(ENOTSUP, std::system_category()); +  return -1; +} + +off_t File::SeekFromEnd(off_t offset, Status *error_ptr) { +  if (error_ptr) +    *error_ptr = std::error_code(ENOTSUP, std::system_category()); +  return -1; +} + +Status File::Read(void *dst, size_t &num_bytes, off_t &offset) { +  return std::error_code(ENOTSUP, std::system_category()); +} + +Status File::Write(const void *src, size_t &num_bytes, off_t &offset) { +  return std::error_code(ENOTSUP, std::system_category()); +} + +Status File::Flush() { return Status(); } + +Status File::Sync() { return Flush(); } + +void File::CalculateInteractiveAndTerminal() { +  const int fd = GetDescriptor(); +  if (!DescriptorIsValid(fd)) { +    m_is_interactive = eLazyBoolNo; +    m_is_real_terminal = eLazyBoolNo; +    m_supports_colors = eLazyBoolNo; +    return; +  } +  m_is_interactive = eLazyBoolNo; +  m_is_real_terminal = eLazyBoolNo; +#if defined(_WIN32) +  if (_isatty(fd)) { +    m_is_interactive = eLazyBoolYes; +    m_is_real_terminal = eLazyBoolYes; +#if defined(ENABLE_VIRTUAL_TERMINAL_PROCESSING) +    m_supports_colors = eLazyBoolYes; +#endif +  } +#else +  if (isatty(fd)) { +    m_is_interactive = eLazyBoolYes; +    struct winsize window_size; +    if (::ioctl(fd, TIOCGWINSZ, &window_size) == 0) { +      if (window_size.ws_col > 0) { +        m_is_real_terminal = eLazyBoolYes; +        if (llvm::sys::Process::FileDescriptorHasColors(fd)) +          m_supports_colors = eLazyBoolYes; +      } +    } +  } +#endif +} + +bool File::GetIsInteractive() { +  if (m_is_interactive == eLazyBoolCalculate) +    CalculateInteractiveAndTerminal(); +  return m_is_interactive == eLazyBoolYes; +} + +bool File::GetIsRealTerminal() { +  if (m_is_real_terminal == eLazyBoolCalculate) +    CalculateInteractiveAndTerminal(); +  return m_is_real_terminal == eLazyBoolYes; +} + +bool File::GetIsTerminalWithColors() { +  if (m_supports_colors == eLazyBoolCalculate) +    CalculateInteractiveAndTerminal(); +  return m_supports_colors == eLazyBoolYes; +} + +size_t File::Printf(const char *format, ...) { +  va_list args; +  va_start(args, format); +  size_t result = PrintfVarArg(format, args); +  va_end(args); +  return result; +} + +size_t File::PrintfVarArg(const char *format, va_list args) { +  size_t result = 0; +  char *s = nullptr; +  result = vasprintf(&s, format, args); +  if (s != nullptr) { +    if (result > 0) { +      size_t s_len = result; +      Write(s, s_len); +      result = s_len; +    } +    free(s); +  } +  return result; +} + +Expected<File::OpenOptions> File::GetOptions() const { +  return llvm::createStringError( +      llvm::inconvertibleErrorCode(), +      "GetOptions() not implemented for this File class"); +} + +uint32_t File::GetPermissions(Status &error) const { +  int fd = GetDescriptor(); +  if (!DescriptorIsValid(fd)) { +    error = std::error_code(ENOTSUP, std::system_category()); +    return 0; +  } +  struct stat file_stats; +  if (::fstat(fd, &file_stats) == -1) { +    error.SetErrorToErrno(); +    return 0; +  } +  error.Clear(); +  return file_stats.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO); +} + +Expected<File::OpenOptions> NativeFile::GetOptions() const { return m_options; } + +int NativeFile::GetDescriptor() const { +  if (DescriptorIsValid()) +    return m_descriptor; + +  // Don't open the file descriptor if we don't need to, just get it from the +  // stream if we have one. +  if (StreamIsValid()) { +#if defined(_WIN32) +    return _fileno(m_stream); +#else +    return fileno(m_stream); +#endif +  } + +  // Invalid descriptor and invalid stream, return invalid descriptor. +  return kInvalidDescriptor; +} + +IOObject::WaitableHandle NativeFile::GetWaitableHandle() { +  return GetDescriptor(); +} + +FILE *NativeFile::GetStream() { +  if (!StreamIsValid()) { +    if (DescriptorIsValid()) { +      auto mode = GetStreamOpenModeFromOptions(m_options); +      if (!mode) +        llvm::consumeError(mode.takeError()); +      else { +        if (!m_own_descriptor) { +// We must duplicate the file descriptor if we don't own it because when you +// call fdopen, the stream will own the fd +#ifdef _WIN32 +          m_descriptor = ::_dup(GetDescriptor()); +#else +          m_descriptor = dup(GetDescriptor()); +#endif +          m_own_descriptor = true; +        } + +        m_stream = llvm::sys::RetryAfterSignal(nullptr, ::fdopen, m_descriptor, +                                               mode.get()); + +        // If we got a stream, then we own the stream and should no longer own +        // the descriptor because fclose() will close it for us + +        if (m_stream) { +          m_own_stream = true; +          m_own_descriptor = false; +        } +      } +    } +  } +  return m_stream; +} + +Status NativeFile::Close() { +  Status error; +  if (StreamIsValid()) { +    if (m_own_stream) { +      if (::fclose(m_stream) == EOF) +        error.SetErrorToErrno(); +    } else { +      if (::fflush(m_stream) == EOF) +        error.SetErrorToErrno(); +    } +  } +  if (DescriptorIsValid() && m_own_descriptor) { +    if (::close(m_descriptor) != 0) +      error.SetErrorToErrno(); +  } +  m_descriptor = kInvalidDescriptor; +  m_stream = kInvalidStream; +  m_options = OpenOptions(0); +  m_own_stream = false; +  m_own_descriptor = false; +  m_is_interactive = eLazyBoolCalculate; +  m_is_real_terminal = eLazyBoolCalculate; +  return error; +} + +Status NativeFile::GetFileSpec(FileSpec &file_spec) const { +  Status error; +#ifdef F_GETPATH +  if (IsValid()) { +    char path[PATH_MAX]; +    if (::fcntl(GetDescriptor(), F_GETPATH, path) == -1) +      error.SetErrorToErrno(); +    else +      file_spec.SetFile(path, FileSpec::Style::native); +  } else { +    error.SetErrorString("invalid file handle"); +  } +#elif defined(__linux__) +  char proc[64]; +  char path[PATH_MAX]; +  if (::snprintf(proc, sizeof(proc), "/proc/self/fd/%d", GetDescriptor()) < 0) +    error.SetErrorString("cannot resolve file descriptor"); +  else { +    ssize_t len; +    if ((len = ::readlink(proc, path, sizeof(path) - 1)) == -1) +      error.SetErrorToErrno(); +    else { +      path[len] = '\0'; +      file_spec.SetFile(path, FileSpec::Style::native); +    } +  } +#else +  error.SetErrorString( +      "NativeFile::GetFileSpec is not supported on this platform"); +#endif + +  if (error.Fail()) +    file_spec.Clear(); +  return error; +} + +off_t NativeFile::SeekFromStart(off_t offset, Status *error_ptr) { +  off_t result = 0; +  if (DescriptorIsValid()) { +    result = ::lseek(m_descriptor, offset, SEEK_SET); + +    if (error_ptr) { +      if (result == -1) +        error_ptr->SetErrorToErrno(); +      else +        error_ptr->Clear(); +    } +  } else if (StreamIsValid()) { +    result = ::fseek(m_stream, offset, SEEK_SET); + +    if (error_ptr) { +      if (result == -1) +        error_ptr->SetErrorToErrno(); +      else +        error_ptr->Clear(); +    } +  } else if (error_ptr) { +    error_ptr->SetErrorString("invalid file handle"); +  } +  return result; +} + +off_t NativeFile::SeekFromCurrent(off_t offset, Status *error_ptr) { +  off_t result = -1; +  if (DescriptorIsValid()) { +    result = ::lseek(m_descriptor, offset, SEEK_CUR); + +    if (error_ptr) { +      if (result == -1) +        error_ptr->SetErrorToErrno(); +      else +        error_ptr->Clear(); +    } +  } else if (StreamIsValid()) { +    result = ::fseek(m_stream, offset, SEEK_CUR); + +    if (error_ptr) { +      if (result == -1) +        error_ptr->SetErrorToErrno(); +      else +        error_ptr->Clear(); +    } +  } else if (error_ptr) { +    error_ptr->SetErrorString("invalid file handle"); +  } +  return result; +} + +off_t NativeFile::SeekFromEnd(off_t offset, Status *error_ptr) { +  off_t result = -1; +  if (DescriptorIsValid()) { +    result = ::lseek(m_descriptor, offset, SEEK_END); + +    if (error_ptr) { +      if (result == -1) +        error_ptr->SetErrorToErrno(); +      else +        error_ptr->Clear(); +    } +  } else if (StreamIsValid()) { +    result = ::fseek(m_stream, offset, SEEK_END); + +    if (error_ptr) { +      if (result == -1) +        error_ptr->SetErrorToErrno(); +      else +        error_ptr->Clear(); +    } +  } else if (error_ptr) { +    error_ptr->SetErrorString("invalid file handle"); +  } +  return result; +} + +Status NativeFile::Flush() { +  Status error; +  if (StreamIsValid()) { +    if (llvm::sys::RetryAfterSignal(EOF, ::fflush, m_stream) == EOF) +      error.SetErrorToErrno(); +  } else if (!DescriptorIsValid()) { +    error.SetErrorString("invalid file handle"); +  } +  return error; +} + +Status NativeFile::Sync() { +  Status error; +  if (DescriptorIsValid()) { +#ifdef _WIN32 +    int err = FlushFileBuffers((HANDLE)_get_osfhandle(m_descriptor)); +    if (err == 0) +      error.SetErrorToGenericError(); +#else +    if (llvm::sys::RetryAfterSignal(-1, ::fsync, m_descriptor) == -1) +      error.SetErrorToErrno(); +#endif +  } else { +    error.SetErrorString("invalid file handle"); +  } +  return error; +} + +#if defined(__APPLE__) +// Darwin kernels only can read/write <= INT_MAX bytes +#define MAX_READ_SIZE INT_MAX +#define MAX_WRITE_SIZE INT_MAX +#endif + +Status NativeFile::Read(void *buf, size_t &num_bytes) { +  Status error; + +#if defined(MAX_READ_SIZE) +  if (num_bytes > MAX_READ_SIZE) { +    uint8_t *p = (uint8_t *)buf; +    size_t bytes_left = num_bytes; +    // Init the num_bytes read to zero +    num_bytes = 0; + +    while (bytes_left > 0) { +      size_t curr_num_bytes; +      if (bytes_left > MAX_READ_SIZE) +        curr_num_bytes = MAX_READ_SIZE; +      else +        curr_num_bytes = bytes_left; + +      error = Read(p + num_bytes, curr_num_bytes); + +      // Update how many bytes were read +      num_bytes += curr_num_bytes; +      if (bytes_left < curr_num_bytes) +        bytes_left = 0; +      else +        bytes_left -= curr_num_bytes; + +      if (error.Fail()) +        break; +    } +    return error; +  } +#endif + +  ssize_t bytes_read = -1; +  if (DescriptorIsValid()) { +    bytes_read = llvm::sys::RetryAfterSignal(-1, ::read, m_descriptor, buf, num_bytes); +    if (bytes_read == -1) { +      error.SetErrorToErrno(); +      num_bytes = 0; +    } else +      num_bytes = bytes_read; +  } else if (StreamIsValid()) { +    bytes_read = ::fread(buf, 1, num_bytes, m_stream); + +    if (bytes_read == 0) { +      if (::feof(m_stream)) +        error.SetErrorString("feof"); +      else if (::ferror(m_stream)) +        error.SetErrorString("ferror"); +      num_bytes = 0; +    } else +      num_bytes = bytes_read; +  } else { +    num_bytes = 0; +    error.SetErrorString("invalid file handle"); +  } +  return error; +} + +Status NativeFile::Write(const void *buf, size_t &num_bytes) { +  Status error; + +#if defined(MAX_WRITE_SIZE) +  if (num_bytes > MAX_WRITE_SIZE) { +    const uint8_t *p = (const uint8_t *)buf; +    size_t bytes_left = num_bytes; +    // Init the num_bytes written to zero +    num_bytes = 0; + +    while (bytes_left > 0) { +      size_t curr_num_bytes; +      if (bytes_left > MAX_WRITE_SIZE) +        curr_num_bytes = MAX_WRITE_SIZE; +      else +        curr_num_bytes = bytes_left; + +      error = Write(p + num_bytes, curr_num_bytes); + +      // Update how many bytes were read +      num_bytes += curr_num_bytes; +      if (bytes_left < curr_num_bytes) +        bytes_left = 0; +      else +        bytes_left -= curr_num_bytes; + +      if (error.Fail()) +        break; +    } +    return error; +  } +#endif + +  ssize_t bytes_written = -1; +  if (DescriptorIsValid()) { +    bytes_written = +        llvm::sys::RetryAfterSignal(-1, ::write, m_descriptor, buf, num_bytes); +    if (bytes_written == -1) { +      error.SetErrorToErrno(); +      num_bytes = 0; +    } else +      num_bytes = bytes_written; +  } else if (StreamIsValid()) { +    bytes_written = ::fwrite(buf, 1, num_bytes, m_stream); + +    if (bytes_written == 0) { +      if (::feof(m_stream)) +        error.SetErrorString("feof"); +      else if (::ferror(m_stream)) +        error.SetErrorString("ferror"); +      num_bytes = 0; +    } else +      num_bytes = bytes_written; + +  } else { +    num_bytes = 0; +    error.SetErrorString("invalid file handle"); +  } + +  return error; +} + +Status NativeFile::Read(void *buf, size_t &num_bytes, off_t &offset) { +  Status error; + +#if defined(MAX_READ_SIZE) +  if (num_bytes > MAX_READ_SIZE) { +    uint8_t *p = (uint8_t *)buf; +    size_t bytes_left = num_bytes; +    // Init the num_bytes read to zero +    num_bytes = 0; + +    while (bytes_left > 0) { +      size_t curr_num_bytes; +      if (bytes_left > MAX_READ_SIZE) +        curr_num_bytes = MAX_READ_SIZE; +      else +        curr_num_bytes = bytes_left; + +      error = Read(p + num_bytes, curr_num_bytes, offset); + +      // Update how many bytes were read +      num_bytes += curr_num_bytes; +      if (bytes_left < curr_num_bytes) +        bytes_left = 0; +      else +        bytes_left -= curr_num_bytes; + +      if (error.Fail()) +        break; +    } +    return error; +  } +#endif + +#ifndef _WIN32 +  int fd = GetDescriptor(); +  if (fd != kInvalidDescriptor) { +    ssize_t bytes_read = +        llvm::sys::RetryAfterSignal(-1, ::pread, fd, buf, num_bytes, offset); +    if (bytes_read < 0) { +      num_bytes = 0; +      error.SetErrorToErrno(); +    } else { +      offset += bytes_read; +      num_bytes = bytes_read; +    } +  } else { +    num_bytes = 0; +    error.SetErrorString("invalid file handle"); +  } +#else +  std::lock_guard<std::mutex> guard(offset_access_mutex); +  long cur = ::lseek(m_descriptor, 0, SEEK_CUR); +  SeekFromStart(offset); +  error = Read(buf, num_bytes); +  if (!error.Fail()) +    SeekFromStart(cur); +#endif +  return error; +} + +Status NativeFile::Write(const void *buf, size_t &num_bytes, off_t &offset) { +  Status error; + +#if defined(MAX_WRITE_SIZE) +  if (num_bytes > MAX_WRITE_SIZE) { +    const uint8_t *p = (const uint8_t *)buf; +    size_t bytes_left = num_bytes; +    // Init the num_bytes written to zero +    num_bytes = 0; + +    while (bytes_left > 0) { +      size_t curr_num_bytes; +      if (bytes_left > MAX_WRITE_SIZE) +        curr_num_bytes = MAX_WRITE_SIZE; +      else +        curr_num_bytes = bytes_left; + +      error = Write(p + num_bytes, curr_num_bytes, offset); + +      // Update how many bytes were read +      num_bytes += curr_num_bytes; +      if (bytes_left < curr_num_bytes) +        bytes_left = 0; +      else +        bytes_left -= curr_num_bytes; + +      if (error.Fail()) +        break; +    } +    return error; +  } +#endif + +  int fd = GetDescriptor(); +  if (fd != kInvalidDescriptor) { +#ifndef _WIN32 +    ssize_t bytes_written = +        llvm::sys::RetryAfterSignal(-1, ::pwrite, m_descriptor, buf, num_bytes, offset); +    if (bytes_written < 0) { +      num_bytes = 0; +      error.SetErrorToErrno(); +    } else { +      offset += bytes_written; +      num_bytes = bytes_written; +    } +#else +    std::lock_guard<std::mutex> guard(offset_access_mutex); +    long cur = ::lseek(m_descriptor, 0, SEEK_CUR); +    SeekFromStart(offset); +    error = Write(buf, num_bytes); +    long after = ::lseek(m_descriptor, 0, SEEK_CUR); + +    if (!error.Fail()) +      SeekFromStart(cur); + +    offset = after; +#endif +  } else { +    num_bytes = 0; +    error.SetErrorString("invalid file handle"); +  } +  return error; +} + +size_t NativeFile::PrintfVarArg(const char *format, va_list args) { +  if (StreamIsValid()) { +    return ::vfprintf(m_stream, format, args); +  } else { +    return File::PrintfVarArg(format, args); +  } +} + +mode_t File::ConvertOpenOptionsForPOSIXOpen(OpenOptions open_options) { +  mode_t mode = 0; +  if (open_options & eOpenOptionRead && open_options & eOpenOptionWrite) +    mode |= O_RDWR; +  else if (open_options & eOpenOptionWrite) +    mode |= O_WRONLY; + +  if (open_options & eOpenOptionAppend) +    mode |= O_APPEND; + +  if (open_options & eOpenOptionTruncate) +    mode |= O_TRUNC; + +  if (open_options & eOpenOptionNonBlocking) +    mode |= O_NONBLOCK; + +  if (open_options & eOpenOptionCanCreateNewOnly) +    mode |= O_CREAT | O_EXCL; +  else if (open_options & eOpenOptionCanCreate) +    mode |= O_CREAT; + +  return mode; +} + +char File::ID = 0; +char NativeFile::ID = 0;  | 
