910e62b5创建于 1月15日历史提交
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#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 {

// Linux pipes typically have a buffer size of 64k, though this can vary.
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());
}

}  // namespace

FdStringReader::~FdStringReader() = default;

// static
std::unique_ptr<FdStringReader> FdStringReader::ReadFromPipe(
    base::ScopedFD fd,
    Callback callback) {
  return base::WrapUnique(
      new FdStringReader(std::move(fd), std::move(callback)));
}

// static
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)) {
  // As an optimization, reserve buffer space now, so that the first
  // memory-allocation happens before the FD becomes readable (if it's not
  // already readable). Call reserve() instead of resize(), so that
  // read_data_ stays logically empty. This respects the invariant that
  // read_data_ holds exactly the data returned by all of the read() calls.
  read_data_.reserve(kBufferSize);

  // Unretained is safe because no callback will occur after the controller is
  // destroyed.
  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) {
    // Reserve space for new data. Depending on the read() result, the string
    // will be restored to its original size, or shrunk to just include the
    // amount of data read.
    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) {
        // Read was blocked, so return without doing anything. This method will
        // be called again when the FD becomes readable.
        return;
      }
      // A permanent (non-blocking-related) read error occurred. Reset the
      // watcher to ensure no more callbacks occur.
      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) {
      // End-of-stream reached. Reset the watcher to ensure no more callbacks
      // occur.
      read_data_.resize(original_size);
      fd_watcher_.reset();
      std::move(callback_).Run(base::ok(std::move(read_data_)));
      return;
    }
    // bytes_read > 0.

    // read() should never return more data than the buffer size. Crash here
    // instead of resizing the string beyond the reserved buffer.
    size_t bytes_read_as_size_t = base::checked_cast<size_t>(bytes_read);
    CHECK_LE(bytes_read_as_size_t, kBufferSize);

    // Store the data, and continue reading as much as possible. The addition of
    // kBufferSize was already checked so it's safe to add a smaller number
    // here without CheckAdd().
    read_data_.resize(original_size + bytes_read_as_size_t);
  }
}

void FdStringReader::OnReadComplete(Result result) {
  std::move(callback_).Run(std::move(result));
}

}  // namespace remoting