brintos

brintos / llvm-project-archived public Read only

0
0
Text · 27.3 KiB · 4fad93f Raw
975 lines · cpp
1//===-- File.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-exception6//7//===----------------------------------------------------------------------===//8 9#include "lldb/Host/File.h"10 11#include <cerrno>12#include <climits>13#include <cstdarg>14#include <cstdio>15#include <fcntl.h>16#include <optional>17 18#ifdef _WIN3219#include "lldb/Host/windows/windows.h"20#else21#include <sys/ioctl.h>22#include <sys/stat.h>23#include <termios.h>24#include <unistd.h>25#endif26 27#include "lldb/Host/Config.h"28#include "lldb/Host/FileSystem.h"29#include "lldb/Host/Host.h"30#include "lldb/Utility/DataBufferHeap.h"31#include "lldb/Utility/FileSpec.h"32#include "lldb/Utility/Log.h"33#include "lldb/Utility/VASPrintf.h"34#include "llvm/ADT/StringExtras.h"35#include "llvm/Support/ConvertUTF.h"36#include "llvm/Support/Errno.h"37#include "llvm/Support/FileSystem.h"38#include "llvm/Support/Process.h"39#include "llvm/Support/raw_ostream.h"40 41using namespace lldb;42using namespace lldb_private;43using llvm::Expected;44 45Expected<const char *>46File::GetStreamOpenModeFromOptions(File::OpenOptions options) {47  File::OpenOptions rw =48      options & (File::eOpenOptionReadOnly | File::eOpenOptionWriteOnly |49                 File::eOpenOptionReadWrite);50 51  if (options & File::eOpenOptionAppend) {52    if (rw == File::eOpenOptionReadWrite) {53      if (options & File::eOpenOptionCanCreateNewOnly)54        return "a+x";55      else56        return "a+";57    } else if (rw == File::eOpenOptionWriteOnly) {58      if (options & File::eOpenOptionCanCreateNewOnly)59        return "ax";60      else61        return "a";62    }63  } else if (rw == File::eOpenOptionReadWrite) {64    if (options & File::eOpenOptionCanCreate) {65      if (options & File::eOpenOptionCanCreateNewOnly)66        return "w+x";67      else68        return "w+";69    } else70      return "r+";71  } else if (rw == File::eOpenOptionWriteOnly) {72    return "w";73  } else if (rw == File::eOpenOptionReadOnly) {74    return "r";75  }76  return llvm::createStringError(77      llvm::inconvertibleErrorCode(),78      "invalid options, cannot convert to mode string");79}80 81Expected<File::OpenOptions> File::GetOptionsFromMode(llvm::StringRef mode) {82  OpenOptions opts =83      llvm::StringSwitch<OpenOptions>(mode)84          .Cases({"r", "rb"}, eOpenOptionReadOnly)85          .Cases({"w", "wb"}, eOpenOptionWriteOnly)86          .Cases({"a", "ab"}, eOpenOptionWriteOnly | eOpenOptionAppend |87                                  eOpenOptionCanCreate)88          .Cases({"r+", "rb+", "r+b"}, eOpenOptionReadWrite)89          .Cases({"w+", "wb+", "w+b"}, eOpenOptionReadWrite |90                                           eOpenOptionCanCreate |91                                           eOpenOptionTruncate)92          .Cases({"a+", "ab+", "a+b"}, eOpenOptionReadWrite |93                                           eOpenOptionAppend |94                                           eOpenOptionCanCreate)95          .Default(eOpenOptionInvalid);96  if (opts != eOpenOptionInvalid)97    return opts;98  return llvm::createStringError(99      llvm::inconvertibleErrorCode(),100      "invalid mode, cannot convert to File::OpenOptions");101}102 103int File::kInvalidDescriptor = -1;104FILE *File::kInvalidStream = nullptr;105 106Status File::Read(void *buf, size_t &num_bytes) {107  return std::error_code(ENOTSUP, std::system_category());108}109Status File::Write(const void *buf, size_t &num_bytes) {110  return std::error_code(ENOTSUP, std::system_category());111}112 113bool File::IsValid() const { return false; }114 115Status File::Close() { return Flush(); }116 117IOObject::WaitableHandle File::GetWaitableHandle() {118  return IOObject::kInvalidHandleValue;119}120 121Status File::GetFileSpec(FileSpec &file_spec) const {122  file_spec.Clear();123  return std::error_code(ENOTSUP, std::system_category());124}125 126int File::GetDescriptor() const { return kInvalidDescriptor; }127 128FILE *File::GetStream() { return nullptr; }129 130off_t File::SeekFromStart(off_t offset, Status *error_ptr) {131  if (error_ptr)132    *error_ptr = std::error_code(ENOTSUP, std::system_category());133  return -1;134}135 136off_t File::SeekFromCurrent(off_t offset, Status *error_ptr) {137  if (error_ptr)138    *error_ptr = std::error_code(ENOTSUP, std::system_category());139  return -1;140}141 142off_t File::SeekFromEnd(off_t offset, Status *error_ptr) {143  if (error_ptr)144    *error_ptr = std::error_code(ENOTSUP, std::system_category());145  return -1;146}147 148Status File::Read(void *dst, size_t &num_bytes, off_t &offset) {149  return std::error_code(ENOTSUP, std::system_category());150}151 152Status File::Write(const void *src, size_t &num_bytes, off_t &offset) {153  return std::error_code(ENOTSUP, std::system_category());154}155 156Status File::Flush() { return Status(); }157 158Status File::Sync() { return Flush(); }159 160void File::CalculateInteractiveAndTerminal() {161  const int fd = GetDescriptor();162  if (!DescriptorIsValid(fd)) {163    m_is_interactive = eLazyBoolNo;164    m_is_real_terminal = eLazyBoolNo;165    m_supports_colors = eLazyBoolNo;166    return;167  }168  m_is_interactive = eLazyBoolNo;169  m_is_real_terminal = eLazyBoolNo;170#if defined(_WIN32)171  if (_isatty(fd)) {172    m_is_interactive = eLazyBoolYes;173    m_is_real_terminal = eLazyBoolYes;174#if defined(ENABLE_VIRTUAL_TERMINAL_PROCESSING)175    m_supports_colors = eLazyBoolYes;176#endif177  }178#else179  if (isatty(fd)) {180    m_is_interactive = eLazyBoolYes;181    struct winsize window_size;182    if (::ioctl(fd, TIOCGWINSZ, &window_size) == 0) {183      if (window_size.ws_col > 0) {184        m_is_real_terminal = eLazyBoolYes;185        if (llvm::sys::Process::FileDescriptorHasColors(fd))186          m_supports_colors = eLazyBoolYes;187      }188    }189  }190#endif191}192 193bool File::GetIsInteractive() {194  if (m_is_interactive == eLazyBoolCalculate)195    CalculateInteractiveAndTerminal();196  return m_is_interactive == eLazyBoolYes;197}198 199bool File::GetIsRealTerminal() {200  if (m_is_real_terminal == eLazyBoolCalculate)201    CalculateInteractiveAndTerminal();202  return m_is_real_terminal == eLazyBoolYes;203}204 205bool File::GetIsTerminalWithColors() {206  if (m_supports_colors == eLazyBoolCalculate)207    CalculateInteractiveAndTerminal();208  return m_supports_colors == eLazyBoolYes;209}210 211size_t File::Printf(const char *format, ...) {212  va_list args;213  va_start(args, format);214  size_t result = PrintfVarArg(format, args);215  va_end(args);216  return result;217}218 219size_t File::PrintfVarArg(const char *format, va_list args) {220  llvm::SmallString<0> s;221  if (VASprintf(s, format, args)) {222    size_t written = s.size();223    Write(s.data(), written);224    return written;225  }226  return 0;227}228 229Expected<File::OpenOptions> File::GetOptions() const {230  return llvm::createStringError(231      llvm::inconvertibleErrorCode(),232      "GetOptions() not implemented for this File class");233}234 235uint32_t File::GetPermissions(Status &error) const {236  int fd = GetDescriptor();237  if (!DescriptorIsValid(fd)) {238    error = std::error_code(ENOTSUP, std::system_category());239    return 0;240  }241  struct stat file_stats;242  if (::fstat(fd, &file_stats) == -1) {243    error = Status::FromErrno();244    return 0;245  }246  error.Clear();247  return file_stats.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO);248}249 250NativeFile::NativeFile() = default;251 252NativeFile::NativeFile(FILE *fh, OpenOptions options, bool transfer_ownership)253    : m_stream(fh), m_options(options), m_own_stream(transfer_ownership) {254#ifdef _WIN32255  // In order to properly display non ASCII characters in Windows, we need to256  // use Windows APIs to print to the console. This is only required if the257  // stream outputs to a console.258  int fd = _fileno(fh);259  is_windows_console =260      ::GetFileType((HANDLE)::_get_osfhandle(fd)) == FILE_TYPE_CHAR;261#else262#ifndef NDEBUG263  int fd = fileno(fh);264  if (fd != -1) {265    int required_mode = ConvertOpenOptionsForPOSIXOpen(options) & O_ACCMODE;266    int mode = fcntl(fd, F_GETFL);267    if (mode != -1) {268      mode &= O_ACCMODE;269      // Check that the file is open with a valid subset of the requested file270      // access mode, e.g. if we expected the file to be writable then ensure it271      // was opened with O_WRONLY or O_RDWR.272      assert(273          (required_mode == O_RDWR && mode == O_RDWR) ||274          (required_mode == O_RDONLY && (mode == O_RDWR || mode == O_RDONLY) ||275           (required_mode == O_WRONLY &&276            (mode == O_RDWR || mode == O_WRONLY))) &&277              "invalid file access mode");278    }279  }280#endif281#endif282}283 284NativeFile::NativeFile(int fd, OpenOptions options, bool transfer_ownership)285    : m_descriptor(fd), m_own_descriptor(transfer_ownership),286      m_options(options) {287#ifdef _WIN32288  // In order to properly display non ASCII characters in Windows, we need to289  // use Windows APIs to print to the console. This is only required if the290  // file outputs to a console.291  is_windows_console =292      ::GetFileType((HANDLE)::_get_osfhandle(fd)) == FILE_TYPE_CHAR;293#endif294}295 296bool NativeFile::IsValid() const {297  std::scoped_lock<std::mutex, std::mutex> lock(m_descriptor_mutex,298                                                m_stream_mutex);299  return DescriptorIsValidUnlocked() || StreamIsValidUnlocked();300}301 302Expected<File::OpenOptions> NativeFile::GetOptions() const { return m_options; }303 304int NativeFile::GetDescriptor() const {305  if (ValueGuard descriptor_guard = DescriptorIsValid()) {306    return m_descriptor;307  }308 309  // Don't open the file descriptor if we don't need to, just get it from the310  // stream if we have one.311  if (ValueGuard stream_guard = StreamIsValid()) {312#if defined(_WIN32)313    return _fileno(m_stream);314#else315    return fileno(m_stream);316#endif317  }318 319  // Invalid descriptor and invalid stream, return invalid descriptor.320  return kInvalidDescriptor;321}322 323IOObject::WaitableHandle NativeFile::GetWaitableHandle() {324#ifdef _WIN32325  return (HANDLE)_get_osfhandle(GetDescriptor());326#else327  return GetDescriptor();328#endif329}330 331FILE *NativeFile::GetStream() {332  ValueGuard stream_guard = StreamIsValid();333  if (!stream_guard) {334    if (ValueGuard descriptor_guard = DescriptorIsValid()) {335      auto mode = GetStreamOpenModeFromOptions(m_options);336      if (!mode)337        llvm::consumeError(mode.takeError());338      else {339        if (!m_own_descriptor) {340// We must duplicate the file descriptor if we don't own it because when you341// call fdopen, the stream will own the fd342#ifdef _WIN32343          m_descriptor = ::_dup(m_descriptor);344#else345          m_descriptor = dup(m_descriptor);346#endif347          m_own_descriptor = true;348        }349 350        m_stream = llvm::sys::RetryAfterSignal(nullptr, ::fdopen, m_descriptor,351                                               mode.get());352 353        // If we got a stream, then we own the stream and should no longer own354        // the descriptor because fclose() will close it for us355 356        if (m_stream) {357          m_own_stream = true;358          m_own_descriptor = false;359        }360      }361    }362  }363  return m_stream;364}365 366Status NativeFile::Close() {367  std::scoped_lock<std::mutex, std::mutex> lock(m_descriptor_mutex,368                                                m_stream_mutex);369 370  Status error;371 372  if (StreamIsValidUnlocked()) {373    if (m_own_stream) {374      if (::fclose(m_stream) == EOF)375        error = Status::FromErrno();376    } else {377      File::OpenOptions rw =378          m_options & (File::eOpenOptionReadOnly | File::eOpenOptionWriteOnly |379                       File::eOpenOptionReadWrite);380 381      if (rw == eOpenOptionWriteOnly || rw == eOpenOptionReadWrite) {382        if (::fflush(m_stream) == EOF)383          error = Status::FromErrno();384      }385    }386  }387 388  if (DescriptorIsValidUnlocked() && m_own_descriptor) {389    if (::close(m_descriptor) != 0)390      error = Status::FromErrno();391  }392 393  m_stream = kInvalidStream;394  m_own_stream = false;395  m_descriptor = kInvalidDescriptor;396  m_own_descriptor = false;397  m_options = OpenOptions(0);398  m_is_interactive = eLazyBoolCalculate;399  m_is_real_terminal = eLazyBoolCalculate;400  return error;401}402 403Status NativeFile::GetFileSpec(FileSpec &file_spec) const {404  Status error;405#ifdef F_GETPATH406  if (IsValid()) {407    char path[PATH_MAX];408    if (::fcntl(GetDescriptor(), F_GETPATH, path) == -1)409      error = Status::FromErrno();410    else411      file_spec.SetFile(path, FileSpec::Style::native);412  } else {413    error = Status::FromErrorString("invalid file handle");414  }415#elif defined(__linux__)416  char proc[64];417  char path[PATH_MAX];418  if (::snprintf(proc, sizeof(proc), "/proc/self/fd/%d", GetDescriptor()) < 0)419    error = Status::FromErrorString("cannot resolve file descriptor");420  else {421    ssize_t len;422    if ((len = ::readlink(proc, path, sizeof(path) - 1)) == -1)423      error = Status::FromErrno();424    else {425      path[len] = '\0';426      file_spec.SetFile(path, FileSpec::Style::native);427    }428  }429#else430  error = Status::FromErrorString(431      "NativeFile::GetFileSpec is not supported on this platform");432#endif433 434  if (error.Fail())435    file_spec.Clear();436  return error;437}438 439off_t NativeFile::SeekFromStart(off_t offset, Status *error_ptr) {440  off_t result = 0;441  if (ValueGuard descriptor_guard = DescriptorIsValid()) {442    result = ::lseek(m_descriptor, offset, SEEK_SET);443 444    if (error_ptr) {445      if (result == -1)446        *error_ptr = Status::FromErrno();447      else448        error_ptr->Clear();449    }450    return result;451  }452 453  if (ValueGuard stream_guard = StreamIsValid()) {454    result = ::fseek(m_stream, offset, SEEK_SET);455 456    if (error_ptr) {457      if (result == -1)458        *error_ptr = Status::FromErrno();459      else460        error_ptr->Clear();461    }462    return result;463  }464 465  if (error_ptr)466    *error_ptr = Status::FromErrorString("invalid file handle");467  return result;468}469 470off_t NativeFile::SeekFromCurrent(off_t offset, Status *error_ptr) {471  off_t result = -1;472  if (ValueGuard descriptor_guard = DescriptorIsValid()) {473    result = ::lseek(m_descriptor, offset, SEEK_CUR);474 475    if (error_ptr) {476      if (result == -1)477        *error_ptr = Status::FromErrno();478      else479        error_ptr->Clear();480    }481    return result;482  }483 484  if (ValueGuard stream_guard = StreamIsValid()) {485    result = ::fseek(m_stream, offset, SEEK_CUR);486 487    if (error_ptr) {488      if (result == -1)489        *error_ptr = Status::FromErrno();490      else491        error_ptr->Clear();492    }493    return result;494  }495 496  if (error_ptr)497    *error_ptr = Status::FromErrorString("invalid file handle");498  return result;499}500 501off_t NativeFile::SeekFromEnd(off_t offset, Status *error_ptr) {502  off_t result = -1;503  if (ValueGuard descriptor_guard = DescriptorIsValid()) {504    result = ::lseek(m_descriptor, offset, SEEK_END);505 506    if (error_ptr) {507      if (result == -1)508        *error_ptr = Status::FromErrno();509      else510        error_ptr->Clear();511    }512    return result;513  }514 515  if (ValueGuard stream_guard = StreamIsValid()) {516    result = ::fseek(m_stream, offset, SEEK_END);517 518    if (error_ptr) {519      if (result == -1)520        *error_ptr = Status::FromErrno();521      else522        error_ptr->Clear();523    }524  }525 526  if (error_ptr)527    *error_ptr = Status::FromErrorString("invalid file handle");528  return result;529}530 531Status NativeFile::Flush() {532  Status error;533  if (ValueGuard stream_guard = StreamIsValid()) {534    if (llvm::sys::RetryAfterSignal(EOF, ::fflush, m_stream) == EOF)535      error = Status::FromErrno();536    return error;537  }538 539  {540    ValueGuard descriptor_guard = DescriptorIsValid();541    if (!descriptor_guard)542      error = Status::FromErrorString("invalid file handle");543  }544  return error;545}546 547Status NativeFile::Sync() {548  Status error;549  if (ValueGuard descriptor_guard = DescriptorIsValid()) {550#ifdef _WIN32551    int err = FlushFileBuffers((HANDLE)_get_osfhandle(m_descriptor));552    if (err == 0)553      error = Status::FromErrorString("unknown error");554#else555    if (llvm::sys::RetryAfterSignal(-1, ::fsync, m_descriptor) == -1)556      error = Status::FromErrno();557#endif558  } else {559    error = Status::FromErrorString("invalid file handle");560  }561  return error;562}563 564#if defined(__APPLE__)565// Darwin kernels only can read/write <= INT_MAX bytes566#define MAX_READ_SIZE INT_MAX567#define MAX_WRITE_SIZE INT_MAX568#endif569 570Status NativeFile::Read(void *buf, size_t &num_bytes) {571  Status error;572 573  // Ensure the file is open for reading.574  if ((m_options & File::OpenOptionsModeMask) == eOpenOptionWriteOnly)575    return Status(std::make_error_code(std::errc::bad_file_descriptor));576 577#if defined(MAX_READ_SIZE)578  if (num_bytes > MAX_READ_SIZE) {579    uint8_t *p = (uint8_t *)buf;580    size_t bytes_left = num_bytes;581    // Init the num_bytes read to zero582    num_bytes = 0;583 584    while (bytes_left > 0) {585      size_t curr_num_bytes;586      if (bytes_left > MAX_READ_SIZE)587        curr_num_bytes = MAX_READ_SIZE;588      else589        curr_num_bytes = bytes_left;590 591      error = Read(p + num_bytes, curr_num_bytes);592 593      // Update how many bytes were read594      num_bytes += curr_num_bytes;595      if (bytes_left < curr_num_bytes)596        bytes_left = 0;597      else598        bytes_left -= curr_num_bytes;599 600      if (error.Fail())601        break;602    }603    return error;604  }605#endif606 607  ssize_t bytes_read = -1;608  if (ValueGuard descriptor_guard = DescriptorIsValid()) {609    bytes_read =610        llvm::sys::RetryAfterSignal(-1, ::read, m_descriptor, buf, num_bytes);611    if (bytes_read == -1) {612      error = Status::FromErrno();613      num_bytes = 0;614    } else615      num_bytes = bytes_read;616    return error;617  }618 619  if (ValueGuard file_lock = StreamIsValid()) {620    bytes_read = ::fread(buf, 1, num_bytes, m_stream);621 622    if (bytes_read == 0) {623      if (::feof(m_stream))624        error = Status::FromErrorString("feof");625      else if (::ferror(m_stream))626        error = Status::FromErrorString("ferror");627      num_bytes = 0;628    } else629      num_bytes = bytes_read;630    return error;631  }632 633  num_bytes = 0;634  error = Status::FromErrorString("invalid file handle");635  return error;636}637 638Status NativeFile::Write(const void *buf, size_t &num_bytes) {639  Status error;640 641  // Ensure the file is open for writing.642  if ((m_options & File::OpenOptionsModeMask) == File::eOpenOptionReadOnly)643    return Status(std::make_error_code(std::errc::bad_file_descriptor));644 645#if defined(MAX_WRITE_SIZE)646  if (num_bytes > MAX_WRITE_SIZE) {647    const uint8_t *p = (const uint8_t *)buf;648    size_t bytes_left = num_bytes;649    // Init the num_bytes written to zero650    num_bytes = 0;651 652    while (bytes_left > 0) {653      size_t curr_num_bytes;654      if (bytes_left > MAX_WRITE_SIZE)655        curr_num_bytes = MAX_WRITE_SIZE;656      else657        curr_num_bytes = bytes_left;658 659      error = Write(p + num_bytes, curr_num_bytes);660 661      // Update how many bytes were read662      num_bytes += curr_num_bytes;663      if (bytes_left < curr_num_bytes)664        bytes_left = 0;665      else666        bytes_left -= curr_num_bytes;667 668      if (error.Fail())669        break;670    }671    return error;672  }673#endif674 675  ssize_t bytes_written = -1;676  if (ValueGuard descriptor_guard = DescriptorIsValid()) {677    bytes_written =678        llvm::sys::RetryAfterSignal(-1, ::write, m_descriptor, buf, num_bytes);679    if (bytes_written == -1) {680      error = Status::FromErrno();681      num_bytes = 0;682    } else683      num_bytes = bytes_written;684    return error;685  }686 687  if (ValueGuard stream_guard = StreamIsValid()) {688#ifdef _WIN32689    if (is_windows_console) {690      llvm::raw_fd_ostream(_fileno(m_stream), false)691          .write((const char *)buf, num_bytes);692      return error;693    }694#endif695    bytes_written = ::fwrite(buf, 1, num_bytes, m_stream);696 697    if (bytes_written == 0) {698      if (::feof(m_stream))699        error = Status::FromErrorString("feof");700      else if (::ferror(m_stream))701        error = Status::FromErrorString("ferror");702      num_bytes = 0;703    } else704      num_bytes = bytes_written;705    return error;706  }707 708  num_bytes = 0;709  error = Status::FromErrorString("invalid file handle");710  return error;711}712 713Status NativeFile::Read(void *buf, size_t &num_bytes, off_t &offset) {714  Status error;715 716#if defined(MAX_READ_SIZE)717  if (num_bytes > MAX_READ_SIZE) {718    uint8_t *p = (uint8_t *)buf;719    size_t bytes_left = num_bytes;720    // Init the num_bytes read to zero721    num_bytes = 0;722 723    while (bytes_left > 0) {724      size_t curr_num_bytes;725      if (bytes_left > MAX_READ_SIZE)726        curr_num_bytes = MAX_READ_SIZE;727      else728        curr_num_bytes = bytes_left;729 730      error = Read(p + num_bytes, curr_num_bytes, offset);731 732      // Update how many bytes were read733      num_bytes += curr_num_bytes;734      if (bytes_left < curr_num_bytes)735        bytes_left = 0;736      else737        bytes_left -= curr_num_bytes;738 739      if (error.Fail())740        break;741    }742    return error;743  }744#endif745 746#ifndef _WIN32747  int fd = GetDescriptor();748  if (fd != kInvalidDescriptor) {749    ssize_t bytes_read =750        llvm::sys::RetryAfterSignal(-1, ::pread, fd, buf, num_bytes, offset);751    if (bytes_read < 0) {752      num_bytes = 0;753      error = Status::FromErrno();754    } else {755      offset += bytes_read;756      num_bytes = bytes_read;757    }758  } else {759    num_bytes = 0;760    error = Status::FromErrorString("invalid file handle");761  }762#else763  std::lock_guard<std::mutex> guard(offset_access_mutex);764  long cur = ::lseek(m_descriptor, 0, SEEK_CUR);765  SeekFromStart(offset);766  error = Read(buf, num_bytes);767  if (!error.Fail())768    SeekFromStart(cur);769#endif770  return error;771}772 773Status NativeFile::Write(const void *buf, size_t &num_bytes, off_t &offset) {774  Status error;775 776#if defined(MAX_WRITE_SIZE)777  if (num_bytes > MAX_WRITE_SIZE) {778    const uint8_t *p = (const uint8_t *)buf;779    size_t bytes_left = num_bytes;780    // Init the num_bytes written to zero781    num_bytes = 0;782 783    while (bytes_left > 0) {784      size_t curr_num_bytes;785      if (bytes_left > MAX_WRITE_SIZE)786        curr_num_bytes = MAX_WRITE_SIZE;787      else788        curr_num_bytes = bytes_left;789 790      error = Write(p + num_bytes, curr_num_bytes, offset);791 792      // Update how many bytes were read793      num_bytes += curr_num_bytes;794      if (bytes_left < curr_num_bytes)795        bytes_left = 0;796      else797        bytes_left -= curr_num_bytes;798 799      if (error.Fail())800        break;801    }802    return error;803  }804#endif805 806  int fd = GetDescriptor();807  if (fd != kInvalidDescriptor) {808#ifndef _WIN32809    ssize_t bytes_written = llvm::sys::RetryAfterSignal(810        -1, ::pwrite, m_descriptor, buf, num_bytes, offset);811    if (bytes_written < 0) {812      num_bytes = 0;813      error = Status::FromErrno();814    } else {815      offset += bytes_written;816      num_bytes = bytes_written;817    }818#else819    std::lock_guard<std::mutex> guard(offset_access_mutex);820    long cur = ::lseek(m_descriptor, 0, SEEK_CUR);821    SeekFromStart(offset);822    error = Write(buf, num_bytes);823    long after = ::lseek(m_descriptor, 0, SEEK_CUR);824 825    if (!error.Fail())826      SeekFromStart(cur);827 828    offset = after;829#endif830  } else {831    num_bytes = 0;832    error = Status::FromErrorString("invalid file handle");833  }834  return error;835}836 837size_t NativeFile::PrintfVarArg(const char *format, va_list args) {838  if (StreamIsValid()) {839    return ::vfprintf(m_stream, format, args);840  } else {841    return File::PrintfVarArg(format, args);842  }843}844 845mode_t File::ConvertOpenOptionsForPOSIXOpen(OpenOptions open_options) {846  mode_t mode = 0;847  File::OpenOptions rw =848      open_options & (File::eOpenOptionReadOnly | File::eOpenOptionWriteOnly |849                      File::eOpenOptionReadWrite);850  if (rw == eOpenOptionReadWrite)851    mode |= O_RDWR;852  else if (rw == eOpenOptionWriteOnly)853    mode |= O_WRONLY;854  else if (rw == eOpenOptionReadOnly)855    mode |= O_RDONLY;856 857  if (open_options & eOpenOptionAppend)858    mode |= O_APPEND;859 860  if (open_options & eOpenOptionTruncate)861    mode |= O_TRUNC;862 863  if (open_options & eOpenOptionNonBlocking)864    mode |= O_NONBLOCK;865 866  if (open_options & eOpenOptionCanCreateNewOnly)867    mode |= O_CREAT | O_EXCL;868  else if (open_options & eOpenOptionCanCreate)869    mode |= O_CREAT;870 871  return mode;872}873 874llvm::Expected<SerialPort::Options>875SerialPort::OptionsFromURL(llvm::StringRef urlqs) {876  SerialPort::Options serial_options;877  for (llvm::StringRef x : llvm::split(urlqs, '&')) {878    if (x.consume_front("baud=")) {879      unsigned int baud_rate;880      if (!llvm::to_integer(x, baud_rate, 10))881        return llvm::createStringError(llvm::inconvertibleErrorCode(),882                                       "Invalid baud rate: %s",883                                       x.str().c_str());884      serial_options.BaudRate = baud_rate;885    } else if (x.consume_front("parity=")) {886      serial_options.Parity =887          llvm::StringSwitch<std::optional<Terminal::Parity>>(x)888              .Case("no", Terminal::Parity::No)889              .Case("even", Terminal::Parity::Even)890              .Case("odd", Terminal::Parity::Odd)891              .Case("mark", Terminal::Parity::Mark)892              .Case("space", Terminal::Parity::Space)893              .Default(std::nullopt);894      if (!serial_options.Parity)895        return llvm::createStringError(896            llvm::inconvertibleErrorCode(),897            "Invalid parity (must be no, even, odd, mark or space): %s",898            x.str().c_str());899    } else if (x.consume_front("parity-check=")) {900      serial_options.ParityCheck =901          llvm::StringSwitch<std::optional<Terminal::ParityCheck>>(x)902              .Case("no", Terminal::ParityCheck::No)903              .Case("replace", Terminal::ParityCheck::ReplaceWithNUL)904              .Case("ignore", Terminal::ParityCheck::Ignore)905              // "mark" mode is not currently supported as it requires special906              // input processing907              // .Case("mark", Terminal::ParityCheck::Mark)908              .Default(std::nullopt);909      if (!serial_options.ParityCheck)910        return llvm::createStringError(911            llvm::inconvertibleErrorCode(),912            "Invalid parity-check (must be no, replace, ignore or mark): %s",913            x.str().c_str());914    } else if (x.consume_front("stop-bits=")) {915      unsigned int stop_bits;916      if (!llvm::to_integer(x, stop_bits, 10) ||917          (stop_bits != 1 && stop_bits != 2))918        return llvm::createStringError(919            llvm::inconvertibleErrorCode(),920            "Invalid stop bit number (must be 1 or 2): %s", x.str().c_str());921      serial_options.StopBits = stop_bits;922    } else923      return llvm::createStringError(llvm::inconvertibleErrorCode(),924                                     "Unknown parameter: %s", x.str().c_str());925  }926  return serial_options;927}928 929llvm::Expected<std::unique_ptr<SerialPort>>930SerialPort::Create(int fd, OpenOptions options, Options serial_options,931                   bool transfer_ownership) {932  std::unique_ptr<SerialPort> out{933      new SerialPort(fd, options, serial_options, transfer_ownership)};934 935  if (!out->GetIsInteractive())936    return llvm::createStringError(llvm::inconvertibleErrorCode(),937                                   "the specified file is not a teletype");938 939  Terminal term{fd};940  if (llvm::Error error = term.SetRaw())941    return std::move(error);942  if (serial_options.BaudRate) {943    if (llvm::Error error = term.SetBaudRate(*serial_options.BaudRate))944      return std::move(error);945  }946  if (serial_options.Parity) {947    if (llvm::Error error = term.SetParity(*serial_options.Parity))948      return std::move(error);949  }950  if (serial_options.ParityCheck) {951    if (llvm::Error error = term.SetParityCheck(*serial_options.ParityCheck))952      return std::move(error);953  }954  if (serial_options.StopBits) {955    if (llvm::Error error = term.SetStopBits(*serial_options.StopBits))956      return std::move(error);957  }958 959  return std::move(out);960}961 962SerialPort::SerialPort(int fd, OpenOptions options,963                       SerialPort::Options serial_options,964                       bool transfer_ownership)965    : NativeFile(fd, options, transfer_ownership), m_state(fd) {}966 967Status SerialPort::Close() {968  m_state.Restore();969  return NativeFile::Close();970}971 972char File::ID = 0;973char NativeFile::ID = 0;974char SerialPort::ID = 0;975