
This patch removes all of the Set.* methods from Status. This cleanup is part of a series of patches that make it harder use the anti-pattern of keeping a long-lives Status object around and updating it while dropping any errors it contains on the floor. This patch is largely NFC, the more interesting next steps this enables is to: 1. remove Status.Clear() 2. assert that Status::operator=() never overwrites an error 3. remove Status::operator=() Note that step (2) will bring 90% of the benefits for users, and step (3) will dramatically clean up the error handling code in various places. In the end my goal is to convert all APIs that are of the form ` ResultTy DoFoo(Status& error) ` to ` llvm::Expected<ResultTy> DoFoo() ` How to read this patch? The interesting changes are in Status.h and Status.cpp, all other changes are mostly ` perl -pi -e 's/\.SetErrorString/ = Status::FromErrorString/g' $(git grep -l SetErrorString lldb/source) ` plus the occasional manual cleanup.
908 lines
25 KiB
C++
908 lines
25 KiB
C++
//===-- File.cpp ----------------------------------------------------------===//
|
|
//
|
|
// 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 <cerrno>
|
|
#include <climits>
|
|
#include <cstdarg>
|
|
#include <cstdio>
|
|
#include <fcntl.h>
|
|
#include <optional>
|
|
|
|
#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 "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"
|
|
#include "lldb/Utility/VASPrintf.h"
|
|
#include "llvm/ADT/StringExtras.h"
|
|
#include "llvm/Support/ConvertUTF.h"
|
|
#include "llvm/Support/Errno.h"
|
|
#include "llvm/Support/FileSystem.h"
|
|
#include "llvm/Support/Process.h"
|
|
|
|
using namespace lldb;
|
|
using namespace lldb_private;
|
|
using llvm::Expected;
|
|
|
|
Expected<const char *>
|
|
File::GetStreamOpenModeFromOptions(File::OpenOptions options) {
|
|
File::OpenOptions rw =
|
|
options & (File::eOpenOptionReadOnly | File::eOpenOptionWriteOnly |
|
|
File::eOpenOptionReadWrite);
|
|
|
|
if (options & File::eOpenOptionAppend) {
|
|
if (rw == File::eOpenOptionReadWrite) {
|
|
if (options & File::eOpenOptionCanCreateNewOnly)
|
|
return "a+x";
|
|
else
|
|
return "a+";
|
|
} else if (rw == File::eOpenOptionWriteOnly) {
|
|
if (options & File::eOpenOptionCanCreateNewOnly)
|
|
return "ax";
|
|
else
|
|
return "a";
|
|
}
|
|
} else if (rw == File::eOpenOptionReadWrite) {
|
|
if (options & File::eOpenOptionCanCreate) {
|
|
if (options & File::eOpenOptionCanCreateNewOnly)
|
|
return "w+x";
|
|
else
|
|
return "w+";
|
|
} else
|
|
return "r+";
|
|
} else if (rw == File::eOpenOptionWriteOnly) {
|
|
return "w";
|
|
} else if (rw == File::eOpenOptionReadOnly) {
|
|
return "r";
|
|
}
|
|
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", eOpenOptionReadOnly)
|
|
.Cases("w", "wb", eOpenOptionWriteOnly)
|
|
.Cases("a", "ab",
|
|
eOpenOptionWriteOnly | eOpenOptionAppend |
|
|
eOpenOptionCanCreate)
|
|
.Cases("r+", "rb+", "r+b", eOpenOptionReadWrite)
|
|
.Cases("w+", "wb+", "w+b",
|
|
eOpenOptionReadWrite | eOpenOptionCanCreate |
|
|
eOpenOptionTruncate)
|
|
.Cases("a+", "ab+", "a+b",
|
|
eOpenOptionReadWrite | eOpenOptionAppend |
|
|
eOpenOptionCanCreate)
|
|
.Default(eOpenOptionInvalid);
|
|
if (opts != eOpenOptionInvalid)
|
|
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) {
|
|
llvm::SmallString<0> s;
|
|
if (VASprintf(s, format, args)) {
|
|
size_t written = s.size();
|
|
Write(s.data(), written);
|
|
return written;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
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 = Status::FromErrno();
|
|
return 0;
|
|
}
|
|
error.Clear();
|
|
return file_stats.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
|
|
}
|
|
|
|
bool NativeFile::IsValid() const {
|
|
std::scoped_lock<std::mutex, std::mutex> lock(m_descriptor_mutex, m_stream_mutex);
|
|
return DescriptorIsValidUnlocked() || StreamIsValidUnlocked();
|
|
}
|
|
|
|
Expected<File::OpenOptions> NativeFile::GetOptions() const { return m_options; }
|
|
|
|
int NativeFile::GetDescriptor() const {
|
|
if (ValueGuard descriptor_guard = 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 (ValueGuard stream_guard = 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() {
|
|
ValueGuard stream_guard = StreamIsValid();
|
|
if (!stream_guard) {
|
|
if (ValueGuard descriptor_guard = 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(m_descriptor);
|
|
#else
|
|
m_descriptor = dup(m_descriptor);
|
|
#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() {
|
|
std::scoped_lock<std::mutex, std::mutex> lock(m_descriptor_mutex, m_stream_mutex);
|
|
|
|
Status error;
|
|
|
|
if (StreamIsValidUnlocked()) {
|
|
if (m_own_stream) {
|
|
if (::fclose(m_stream) == EOF)
|
|
error = Status::FromErrno();
|
|
} else {
|
|
File::OpenOptions rw =
|
|
m_options & (File::eOpenOptionReadOnly | File::eOpenOptionWriteOnly |
|
|
File::eOpenOptionReadWrite);
|
|
|
|
if (rw == eOpenOptionWriteOnly || rw == eOpenOptionReadWrite) {
|
|
if (::fflush(m_stream) == EOF)
|
|
error = Status::FromErrno();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (DescriptorIsValidUnlocked() && m_own_descriptor) {
|
|
if (::close(m_descriptor) != 0)
|
|
error = Status::FromErrno();
|
|
}
|
|
|
|
m_stream = kInvalidStream;
|
|
m_own_stream = false;
|
|
m_descriptor = kInvalidDescriptor;
|
|
m_own_descriptor = false;
|
|
m_options = OpenOptions(0);
|
|
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 = Status::FromErrno();
|
|
else
|
|
file_spec.SetFile(path, FileSpec::Style::native);
|
|
} else {
|
|
error = Status::FromErrorString("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 = Status::FromErrorString("cannot resolve file descriptor");
|
|
else {
|
|
ssize_t len;
|
|
if ((len = ::readlink(proc, path, sizeof(path) - 1)) == -1)
|
|
error = Status::FromErrno();
|
|
else {
|
|
path[len] = '\0';
|
|
file_spec.SetFile(path, FileSpec::Style::native);
|
|
}
|
|
}
|
|
#else
|
|
error = Status::FromErrorString(
|
|
"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 (ValueGuard descriptor_guard = DescriptorIsValid()) {
|
|
result = ::lseek(m_descriptor, offset, SEEK_SET);
|
|
|
|
if (error_ptr) {
|
|
if (result == -1)
|
|
*error_ptr = Status::FromErrno();
|
|
else
|
|
error_ptr->Clear();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
if (ValueGuard stream_guard = StreamIsValid()) {
|
|
result = ::fseek(m_stream, offset, SEEK_SET);
|
|
|
|
if (error_ptr) {
|
|
if (result == -1)
|
|
*error_ptr = Status::FromErrno();
|
|
else
|
|
error_ptr->Clear();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
if (error_ptr)
|
|
*error_ptr = Status::FromErrorString("invalid file handle");
|
|
return result;
|
|
}
|
|
|
|
off_t NativeFile::SeekFromCurrent(off_t offset, Status *error_ptr) {
|
|
off_t result = -1;
|
|
if (ValueGuard descriptor_guard = DescriptorIsValid()) {
|
|
result = ::lseek(m_descriptor, offset, SEEK_CUR);
|
|
|
|
if (error_ptr) {
|
|
if (result == -1)
|
|
*error_ptr = Status::FromErrno();
|
|
else
|
|
error_ptr->Clear();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
if (ValueGuard stream_guard = StreamIsValid()) {
|
|
result = ::fseek(m_stream, offset, SEEK_CUR);
|
|
|
|
if (error_ptr) {
|
|
if (result == -1)
|
|
*error_ptr = Status::FromErrno();
|
|
else
|
|
error_ptr->Clear();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
if (error_ptr)
|
|
*error_ptr = Status::FromErrorString("invalid file handle");
|
|
return result;
|
|
}
|
|
|
|
off_t NativeFile::SeekFromEnd(off_t offset, Status *error_ptr) {
|
|
off_t result = -1;
|
|
if (ValueGuard descriptor_guard = DescriptorIsValid()) {
|
|
result = ::lseek(m_descriptor, offset, SEEK_END);
|
|
|
|
if (error_ptr) {
|
|
if (result == -1)
|
|
*error_ptr = Status::FromErrno();
|
|
else
|
|
error_ptr->Clear();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
if (ValueGuard stream_guard = StreamIsValid()) {
|
|
result = ::fseek(m_stream, offset, SEEK_END);
|
|
|
|
if (error_ptr) {
|
|
if (result == -1)
|
|
*error_ptr = Status::FromErrno();
|
|
else
|
|
error_ptr->Clear();
|
|
}
|
|
}
|
|
|
|
if (error_ptr)
|
|
*error_ptr = Status::FromErrorString("invalid file handle");
|
|
return result;
|
|
}
|
|
|
|
Status NativeFile::Flush() {
|
|
Status error;
|
|
if (ValueGuard stream_guard = StreamIsValid()) {
|
|
if (llvm::sys::RetryAfterSignal(EOF, ::fflush, m_stream) == EOF)
|
|
error = Status::FromErrno();
|
|
return error;
|
|
}
|
|
|
|
{
|
|
ValueGuard descriptor_guard = DescriptorIsValid();
|
|
if (!descriptor_guard)
|
|
error = Status::FromErrorString("invalid file handle");
|
|
}
|
|
return error;
|
|
}
|
|
|
|
Status NativeFile::Sync() {
|
|
Status error;
|
|
if (ValueGuard descriptor_guard = 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 = Status::FromErrno();
|
|
#endif
|
|
} else {
|
|
error = Status::FromErrorString("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 (ValueGuard descriptor_guard = DescriptorIsValid()) {
|
|
bytes_read =
|
|
llvm::sys::RetryAfterSignal(-1, ::read, m_descriptor, buf, num_bytes);
|
|
if (bytes_read == -1) {
|
|
error = Status::FromErrno();
|
|
num_bytes = 0;
|
|
} else
|
|
num_bytes = bytes_read;
|
|
return error;
|
|
}
|
|
|
|
if (ValueGuard file_lock = StreamIsValid()) {
|
|
bytes_read = ::fread(buf, 1, num_bytes, m_stream);
|
|
|
|
if (bytes_read == 0) {
|
|
if (::feof(m_stream))
|
|
error = Status::FromErrorString("feof");
|
|
else if (::ferror(m_stream))
|
|
error = Status::FromErrorString("ferror");
|
|
num_bytes = 0;
|
|
} else
|
|
num_bytes = bytes_read;
|
|
return error;
|
|
}
|
|
|
|
num_bytes = 0;
|
|
error = Status::FromErrorString("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 (ValueGuard descriptor_guard = DescriptorIsValid()) {
|
|
bytes_written =
|
|
llvm::sys::RetryAfterSignal(-1, ::write, m_descriptor, buf, num_bytes);
|
|
if (bytes_written == -1) {
|
|
error = Status::FromErrno();
|
|
num_bytes = 0;
|
|
} else
|
|
num_bytes = bytes_written;
|
|
return error;
|
|
}
|
|
|
|
if (ValueGuard stream_guard = StreamIsValid()) {
|
|
bytes_written = ::fwrite(buf, 1, num_bytes, m_stream);
|
|
|
|
if (bytes_written == 0) {
|
|
if (::feof(m_stream))
|
|
error = Status::FromErrorString("feof");
|
|
else if (::ferror(m_stream))
|
|
error = Status::FromErrorString("ferror");
|
|
num_bytes = 0;
|
|
} else
|
|
num_bytes = bytes_written;
|
|
return error;
|
|
}
|
|
|
|
num_bytes = 0;
|
|
error = Status::FromErrorString("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 = Status::FromErrno();
|
|
} else {
|
|
offset += bytes_read;
|
|
num_bytes = bytes_read;
|
|
}
|
|
} else {
|
|
num_bytes = 0;
|
|
error = Status::FromErrorString("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 = Status::FromErrno();
|
|
} 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 = Status::FromErrorString("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;
|
|
File::OpenOptions rw =
|
|
open_options & (File::eOpenOptionReadOnly | File::eOpenOptionWriteOnly |
|
|
File::eOpenOptionReadWrite);
|
|
if (rw == eOpenOptionReadWrite)
|
|
mode |= O_RDWR;
|
|
else if (rw == eOpenOptionWriteOnly)
|
|
mode |= O_WRONLY;
|
|
else if (rw == eOpenOptionReadOnly)
|
|
mode |= O_RDONLY;
|
|
|
|
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;
|
|
}
|
|
|
|
llvm::Expected<SerialPort::Options>
|
|
SerialPort::OptionsFromURL(llvm::StringRef urlqs) {
|
|
SerialPort::Options serial_options;
|
|
for (llvm::StringRef x : llvm::split(urlqs, '&')) {
|
|
if (x.consume_front("baud=")) {
|
|
unsigned int baud_rate;
|
|
if (!llvm::to_integer(x, baud_rate, 10))
|
|
return llvm::createStringError(llvm::inconvertibleErrorCode(),
|
|
"Invalid baud rate: %s",
|
|
x.str().c_str());
|
|
serial_options.BaudRate = baud_rate;
|
|
} else if (x.consume_front("parity=")) {
|
|
serial_options.Parity =
|
|
llvm::StringSwitch<std::optional<Terminal::Parity>>(x)
|
|
.Case("no", Terminal::Parity::No)
|
|
.Case("even", Terminal::Parity::Even)
|
|
.Case("odd", Terminal::Parity::Odd)
|
|
.Case("mark", Terminal::Parity::Mark)
|
|
.Case("space", Terminal::Parity::Space)
|
|
.Default(std::nullopt);
|
|
if (!serial_options.Parity)
|
|
return llvm::createStringError(
|
|
llvm::inconvertibleErrorCode(),
|
|
"Invalid parity (must be no, even, odd, mark or space): %s",
|
|
x.str().c_str());
|
|
} else if (x.consume_front("parity-check=")) {
|
|
serial_options.ParityCheck =
|
|
llvm::StringSwitch<std::optional<Terminal::ParityCheck>>(x)
|
|
.Case("no", Terminal::ParityCheck::No)
|
|
.Case("replace", Terminal::ParityCheck::ReplaceWithNUL)
|
|
.Case("ignore", Terminal::ParityCheck::Ignore)
|
|
// "mark" mode is not currently supported as it requires special
|
|
// input processing
|
|
// .Case("mark", Terminal::ParityCheck::Mark)
|
|
.Default(std::nullopt);
|
|
if (!serial_options.ParityCheck)
|
|
return llvm::createStringError(
|
|
llvm::inconvertibleErrorCode(),
|
|
"Invalid parity-check (must be no, replace, ignore or mark): %s",
|
|
x.str().c_str());
|
|
} else if (x.consume_front("stop-bits=")) {
|
|
unsigned int stop_bits;
|
|
if (!llvm::to_integer(x, stop_bits, 10) ||
|
|
(stop_bits != 1 && stop_bits != 2))
|
|
return llvm::createStringError(
|
|
llvm::inconvertibleErrorCode(),
|
|
"Invalid stop bit number (must be 1 or 2): %s", x.str().c_str());
|
|
serial_options.StopBits = stop_bits;
|
|
} else
|
|
return llvm::createStringError(llvm::inconvertibleErrorCode(),
|
|
"Unknown parameter: %s", x.str().c_str());
|
|
}
|
|
return serial_options;
|
|
}
|
|
|
|
llvm::Expected<std::unique_ptr<SerialPort>>
|
|
SerialPort::Create(int fd, OpenOptions options, Options serial_options,
|
|
bool transfer_ownership) {
|
|
std::unique_ptr<SerialPort> out{
|
|
new SerialPort(fd, options, serial_options, transfer_ownership)};
|
|
|
|
if (!out->GetIsInteractive())
|
|
return llvm::createStringError(llvm::inconvertibleErrorCode(),
|
|
"the specified file is not a teletype");
|
|
|
|
Terminal term{fd};
|
|
if (llvm::Error error = term.SetRaw())
|
|
return std::move(error);
|
|
if (serial_options.BaudRate) {
|
|
if (llvm::Error error = term.SetBaudRate(*serial_options.BaudRate))
|
|
return std::move(error);
|
|
}
|
|
if (serial_options.Parity) {
|
|
if (llvm::Error error = term.SetParity(*serial_options.Parity))
|
|
return std::move(error);
|
|
}
|
|
if (serial_options.ParityCheck) {
|
|
if (llvm::Error error = term.SetParityCheck(*serial_options.ParityCheck))
|
|
return std::move(error);
|
|
}
|
|
if (serial_options.StopBits) {
|
|
if (llvm::Error error = term.SetStopBits(*serial_options.StopBits))
|
|
return std::move(error);
|
|
}
|
|
|
|
return std::move(out);
|
|
}
|
|
|
|
SerialPort::SerialPort(int fd, OpenOptions options,
|
|
SerialPort::Options serial_options,
|
|
bool transfer_ownership)
|
|
: NativeFile(fd, options, transfer_ownership), m_state(fd) {}
|
|
|
|
Status SerialPort::Close() {
|
|
m_state.Restore();
|
|
return NativeFile::Close();
|
|
}
|
|
|
|
char File::ID = 0;
|
|
char NativeFile::ID = 0;
|
|
char SerialPort::ID = 0;
|