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_writer.h"

#include <unistd.h>

#include <utility>

#include "base/memory/ptr_util.h"
#include "base/numerics/safe_conversions.h"
#include "base/posix/eintr_wrapper.h"
#include "base/posix/safe_strerror.h"
#include "base/strings/strcat.h"

namespace remoting {

FdStringWriter::~FdStringWriter() = default;

// static
std::unique_ptr<FdStringWriter> FdStringWriter::Write(std::string data,
                                                      base::ScopedFD fd,
                                                      Callback callback) {
  return base::WrapUnique(
      new FdStringWriter(std::move(data), std::move(fd), std::move(callback)));
}

FdStringWriter::FdStringWriter(std::string data,
                               base::ScopedFD fd,
                               Callback callback)
    : fd_(std::move(fd)),
      callback_(std::move(callback)),
      write_data_(std::move(data)) {
  write_remaining_ = write_data_;

  // Unretained is safe because no callback will occur after the controller is
  // destroyed.
  fd_watcher_ = base::FileDescriptorWatcher::WatchWritable(
      fd_.get(), base::BindRepeating(&FdStringWriter::OnFdWritable,
                                     base::Unretained(this)));
}

void FdStringWriter::OnFdWritable() {
  while (true) {
    // Check at the start of the loop, in case the caller asked to write an
    // empty string.
    if (write_remaining_.empty()) {
      // All data was written successfully. Reset the watcher to ensure no more
      // callbacks occur.
      fd_watcher_.reset();
      std::move(callback_).Run(base::ok());
      return;
    }

    ssize_t bytes_written = HANDLE_EINTR(
        write(fd_.get(), write_remaining_.data(), write_remaining_.size()));
    if (bytes_written < 0) {
      if (errno == EAGAIN || errno == EWOULDBLOCK) {
        // Write was blocked, so return without doing anything. This method will
        // be called again when the FD becomes writable.
        return;
      }
      // A permanent (non-blocking-related) write 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({"write() failed: ", base::safe_strerror(errno)}))));
      return;
    }
    // bytes_written >= 0. POSIX write() should never return exactly 0 (unless
    // it was asked to write 0 bytes, but this is ruled out by the check at the
    // start).

    // write() should never return more bytes than it was asked to write. Crash
    // here instead of risking undefined behavior on the string_view.
    size_t bytes_written_as_size_t = base::checked_cast<size_t>(bytes_written);
    CHECK_LE(bytes_written_as_size_t, write_remaining_.size());

    // At least some of the data was successfully written, so update the write
    // position. Then continue writing until all data is written or the stream
    // is blocked.
    write_remaining_.remove_prefix(bytes_written_as_size_t);
  }
}

}  // namespace remoting