| // Copyright 2016 The Fuchsia Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "src/lib/fsl/socket/files.h" | |
| #include <lib/async/cpp/wait.h> | |
| #include <lib/fit/function.h> | |
| #include <stdio.h> | |
| #include <unistd.h> | |
| #include <algorithm> | |
| #include <limits> | |
| #include <utility> | |
| #include <vector> | |
| #include "src/lib/files/file_descriptor.h" | |
| namespace fsl { | |
| namespace { | |
| // CopyToFileHandler ----------------------------------------------------------- | |
| class CopyToFileHandler { | |
| public: | |
| CopyToFileHandler(zx::socket source, fbl::unique_fd destination, async_dispatcher_t* dispatcher, | |
| fit::function<void(bool, fbl::unique_fd)> callback); | |
| private: | |
| ~CopyToFileHandler(); | |
| void SendCallback(bool value); | |
| void OnHandleReady(async_dispatcher_t* dispatcher, async::WaitBase* wait, zx_status_t status, | |
| const zx_packet_signal_t* signal); | |
| zx::socket source_; | |
| fbl::unique_fd destination_; | |
| fit::function<void(bool, fbl::unique_fd)> callback_; | |
| async::WaitMethod<CopyToFileHandler, &CopyToFileHandler::OnHandleReady> wait_; | |
| FXL_DISALLOW_COPY_AND_ASSIGN(CopyToFileHandler); | |
| }; | |
| CopyToFileHandler::CopyToFileHandler(zx::socket source, fbl::unique_fd destination, | |
| async_dispatcher_t* dispatcher, | |
| fit::function<void(bool, fbl::unique_fd)> callback) | |
| : source_(std::move(source)), | |
| destination_(std::move(destination)), | |
| callback_(std::move(callback)), | |
| wait_(this, source_.get(), ZX_SOCKET_READABLE | ZX_SOCKET_PEER_CLOSED) { | |
| zx_status_t status = wait_.Begin(dispatcher); | |
| FX_CHECK(status == ZX_OK); | |
| } | |
| CopyToFileHandler::~CopyToFileHandler() = default; | |
| void CopyToFileHandler::SendCallback(bool value) { | |
| auto callback = std::move(callback_); | |
| auto destination = std::move(destination_); | |
| delete this; | |
| callback(value, std::move(destination)); | |
| } | |
| void CopyToFileHandler::OnHandleReady(async_dispatcher_t* dispatcher, async::WaitBase* wait, | |
| zx_status_t status, const zx_packet_signal_t* signal) { | |
| if (status != ZX_OK) { | |
| SendCallback(false); | |
| return; | |
| } | |
| if (signal->observed & ZX_ERR_PEER_CLOSED) { | |
| SendCallback(true); | |
| return; | |
| } | |
| std::vector<char> buffer(64 * 1024); | |
| size_t size = 0; | |
| status = source_.read(0u, buffer.data(), buffer.size(), &size); | |
| if (status == ZX_ERR_PEER_CLOSED) { | |
| SendCallback(true); | |
| return; | |
| } | |
| if (status != ZX_ERR_SHOULD_WAIT) { | |
| if (status != ZX_OK || !fxl::WriteFileDescriptor(destination_.get(), buffer.data(), size)) { | |
| SendCallback(false); | |
| return; | |
| } | |
| } | |
| status = wait->Begin(dispatcher); | |
| if (status != ZX_OK) { | |
| SendCallback(false); | |
| } | |
| } | |
| // CopyFromFileHandler --------------------------------------------------------- | |
| class CopyFromFileHandler { | |
| public: | |
| CopyFromFileHandler(fbl::unique_fd source, zx::socket destination, async_dispatcher_t* dispatcher, | |
| fit::function<void(bool, fbl::unique_fd)> callback); | |
| private: | |
| ~CopyFromFileHandler(); | |
| void SendCallback(bool value); | |
| void OnHandleReady(async_dispatcher_t* dispatcher, async::WaitBase* wait, zx_status_t status, | |
| const zx_packet_signal_t* signal); | |
| fbl::unique_fd source_; | |
| zx::socket destination_; | |
| async::WaitMethod<CopyFromFileHandler, &CopyFromFileHandler::OnHandleReady> wait_; | |
| fit::function<void(bool, fbl::unique_fd)> callback_; | |
| std::vector<char> buffer_; | |
| size_t buffer_offset_; | |
| size_t buffer_end_; | |
| FXL_DISALLOW_COPY_AND_ASSIGN(CopyFromFileHandler); | |
| }; | |
| CopyFromFileHandler::CopyFromFileHandler(fbl::unique_fd source, zx::socket destination, | |
| async_dispatcher_t* dispatcher, | |
| fit::function<void(bool, fbl::unique_fd)> callback) | |
| : source_(std::move(source)), | |
| destination_(std::move(destination)), | |
| wait_(this, destination_.get(), ZX_SOCKET_WRITABLE | ZX_SOCKET_PEER_CLOSED), | |
| callback_(std::move(callback)), | |
| buffer_(64 * 1024) { | |
| zx_status_t status = wait_.Begin(dispatcher); | |
| FX_CHECK(status == ZX_OK); | |
| } | |
| CopyFromFileHandler::~CopyFromFileHandler() = default; | |
| void CopyFromFileHandler::SendCallback(bool value) { | |
| auto callback = std::move(callback_); | |
| auto source = std::move(source_); | |
| delete this; | |
| callback(value, std::move(source)); | |
| } | |
| void CopyFromFileHandler::OnHandleReady(async_dispatcher_t* dispatcher, async::WaitBase* wait, | |
| zx_status_t status, const zx_packet_signal_t* signal) { | |
| if (status != ZX_OK) { | |
| SendCallback(false); | |
| return; | |
| } | |
| if (signal->observed & ZX_ERR_PEER_CLOSED) { | |
| SendCallback(false); | |
| return; | |
| } | |
| if (buffer_offset_ == buffer_end_) { | |
| ssize_t bytes_read = fxl::ReadFileDescriptor(source_.get(), buffer_.data(), buffer_.size()); | |
| if (bytes_read <= 0) { | |
| SendCallback(bytes_read == 0); | |
| return; | |
| } | |
| buffer_offset_ = 0; | |
| buffer_end_ = bytes_read; | |
| } | |
| size_t bytes_written = 0; | |
| status = destination_.write(0u, buffer_.data() + buffer_offset_, buffer_end_ - buffer_offset_, | |
| &bytes_written); | |
| if (status != ZX_ERR_SHOULD_WAIT) { | |
| if (status != ZX_OK) { | |
| SendCallback(false); | |
| return; | |
| } | |
| buffer_offset_ += bytes_written; | |
| } | |
| status = wait->Begin(dispatcher); | |
| if (status != ZX_OK) { | |
| SendCallback(false); | |
| } | |
| } | |
| } // namespace | |
| void CopyToFileDescriptor(zx::socket source, fbl::unique_fd destination, | |
| async_dispatcher_t* dispatcher, | |
| fit::function<void(bool, fbl::unique_fd)> callback) { | |
| new CopyToFileHandler(std::move(source), std::move(destination), dispatcher, std::move(callback)); | |
| } | |
| void CopyFromFileDescriptor(fbl::unique_fd source, zx::socket destination, | |
| async_dispatcher_t* dispatcher, | |
| fit::function<void(bool, fbl::unique_fd)> callback) { | |
| new CopyFromFileHandler(std::move(source), std::move(destination), dispatcher, | |
| std::move(callback)); | |
| } | |
| } // namespace fsl |