#include "remoting/host/linux/fd_string_reader.h"
#include <unistd.h>
#include <utility>
#include "base/containers/span.h"
#include "base/files/file.h"
#include "base/files/memory_mapped_file.h"
#include "base/memory/ptr_util.h"
#include "base/numerics/checked_math.h"
#include "base/posix/eintr_wrapper.h"
#include "base/posix/safe_strerror.h"
#include "base/strings/strcat.h"
#include "base/task/thread_pool.h"
namespace remoting {
namespace {
constexpr size_t kBufferSize = 1U << 16;
FdStringReader::Result ReadContentsOfFile(base::ScopedFD fd) {
base::File file(fd.release());
base::MemoryMappedFile mapped_file;
if (!mapped_file.Initialize(std::move(file))) {
return base::unexpected(
Loggable(FROM_HERE, "Failed to initialize MemoryMappedFile"));
}
auto contents = base::as_chars(mapped_file.bytes());
return std::string(contents.begin(), contents.end());
}
}
FdStringReader::~FdStringReader() = default;
std::unique_ptr<FdStringReader> FdStringReader::ReadFromPipe(
base::ScopedFD fd,
Callback callback) {
return base::WrapUnique(
new FdStringReader(std::move(fd), std::move(callback)));
}
std::unique_ptr<FdStringReader> FdStringReader::ReadFromFile(
base::ScopedFD fd,
Callback callback) {
auto reader = base::WrapUnique(new FdStringReader(std::move(callback)));
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE, {base::MayBlock()},
base::BindOnce(&ReadContentsOfFile, std::move(fd)),
base::BindOnce(&FdStringReader::OnReadComplete,
reader->weak_factory_.GetWeakPtr()));
return reader;
}
FdStringReader::FdStringReader(base::ScopedFD fd, Callback callback)
: fd_(std::move(fd)), callback_(std::move(callback)) {
read_data_.reserve(kBufferSize);
fd_watcher_ = base::FileDescriptorWatcher::WatchReadable(
fd_.get(), base::BindRepeating(&FdStringReader::OnFdReadable,
base::Unretained(this)));
}
FdStringReader::FdStringReader(Callback callback)
: callback_(std::move(callback)) {}
void FdStringReader::OnFdReadable() {
while (true) {
size_t original_size = read_data_.size();
size_t new_size = base::CheckAdd(original_size, kBufferSize).ValueOrDie();
read_data_.resize(new_size);
auto writable_part =
base::as_writable_byte_span(read_data_).last(kBufferSize);
ssize_t bytes_read = HANDLE_EINTR(
read(fd_.get(), writable_part.data(), writable_part.size()));
if (bytes_read < 0) {
read_data_.resize(original_size);
if (errno == EAGAIN || errno == EWOULDBLOCK) {
return;
}
fd_watcher_.reset();
std::move(callback_).Run(base::unexpected(Loggable(
FROM_HERE,
base::StrCat({"read() failed: ", base::safe_strerror(errno)}))));
return;
}
if (bytes_read == 0) {
read_data_.resize(original_size);
fd_watcher_.reset();
std::move(callback_).Run(base::ok(std::move(read_data_)));
return;
}
size_t bytes_read_as_size_t = base::checked_cast<size_t>(bytes_read);
CHECK_LE(bytes_read_as_size_t, kBufferSize);
read_data_.resize(original_size + bytes_read_as_size_t);
}
}
void FdStringReader::OnReadComplete(Result result) {
std::move(callback_).Run(std::move(result));
}
}