| // 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 "flutter/glue/data_pipe_utils.h" |
| |
| #include <stdio.h> |
| #include <unistd.h> |
| |
| #include <algorithm> |
| #include <limits> |
| #include <utility> |
| |
| #include "lib/ftl/files/file_descriptor.h" |
| #include "mojo/public/cpp/environment/async_waiter.h" |
| #include "mojo/public/cpp/environment/environment.h" |
| |
| namespace glue { |
| namespace { |
| |
| // CopyToFileHandler ----------------------------------------------------------- |
| |
| class CopyToFileHandler { |
| public: |
| CopyToFileHandler(mojo::ScopedDataPipeConsumerHandle source, |
| ftl::UniqueFD destination, |
| ftl::TaskRunner* task_runner, |
| const std::function<void(bool)>& callback); |
| |
| private: |
| ~CopyToFileHandler(); |
| |
| void SendCallback(bool value); |
| void OnHandleReady(MojoResult result); |
| static void WaitComplete(void* context, MojoResult result); |
| |
| mojo::ScopedDataPipeConsumerHandle source_; |
| ftl::UniqueFD destination_; |
| ftl::RefPtr<ftl::TaskRunner> task_runner_; |
| std::function<void(bool)> callback_; |
| const MojoAsyncWaiter* waiter_; |
| MojoAsyncWaitID wait_id_; |
| |
| FTL_DISALLOW_COPY_AND_ASSIGN(CopyToFileHandler); |
| }; |
| |
| CopyToFileHandler::CopyToFileHandler(mojo::ScopedDataPipeConsumerHandle source, |
| ftl::UniqueFD destination, |
| ftl::TaskRunner* task_runner, |
| const std::function<void(bool)>& callback) |
| : source_(std::move(source)), |
| destination_(std::move(destination)), |
| task_runner_(task_runner), |
| callback_(callback), |
| waiter_(mojo::Environment::GetDefaultAsyncWaiter()), |
| wait_id_(0) { |
| task_runner_->PostTask([this]() { OnHandleReady(MOJO_RESULT_OK); }); |
| } |
| |
| CopyToFileHandler::~CopyToFileHandler() {} |
| |
| void CopyToFileHandler::SendCallback(bool value) { |
| FTL_DCHECK(!wait_id_); |
| auto callback = callback_; |
| delete this; |
| callback(value); |
| } |
| |
| void CopyToFileHandler::OnHandleReady(MojoResult result) { |
| if (result == MOJO_RESULT_OK) { |
| const void* buffer = nullptr; |
| uint32_t size = 0; |
| result = BeginReadDataRaw(source_.get(), &buffer, &size, |
| MOJO_READ_DATA_FLAG_NONE); |
| if (result == MOJO_RESULT_OK) { |
| bool write_success = ftl::WriteFileDescriptor( |
| destination_.get(), static_cast<const char*>(buffer), size); |
| result = EndReadDataRaw(source_.get(), size); |
| if (!write_success || result != MOJO_RESULT_OK) { |
| SendCallback(false); |
| } else { |
| task_runner_->PostTask([this]() { OnHandleReady(MOJO_RESULT_OK); }); |
| } |
| return; |
| } |
| } |
| if (result == MOJO_RESULT_FAILED_PRECONDITION) { |
| SendCallback(true); |
| return; |
| } |
| if (result == MOJO_RESULT_SHOULD_WAIT) { |
| wait_id_ = |
| waiter_->AsyncWait(source_.get().value(), MOJO_HANDLE_SIGNAL_READABLE, |
| MOJO_DEADLINE_INDEFINITE, &WaitComplete, this); |
| return; |
| } |
| SendCallback(false); |
| } |
| |
| void CopyToFileHandler::WaitComplete(void* context, MojoResult result) { |
| CopyToFileHandler* handler = static_cast<CopyToFileHandler*>(context); |
| handler->wait_id_ = 0; |
| handler->OnHandleReady(result); |
| } |
| |
| // CopyFromFileHandler --------------------------------------------------------- |
| |
| class CopyFromFileHandler { |
| public: |
| CopyFromFileHandler(ftl::UniqueFD source, |
| mojo::ScopedDataPipeProducerHandle destination, |
| ftl::TaskRunner* task_runner, |
| const std::function<void(bool)>& callback); |
| |
| private: |
| ~CopyFromFileHandler(); |
| |
| void SendCallback(bool value); |
| void OnHandleReady(MojoResult result); |
| static void WaitComplete(void* context, MojoResult result); |
| |
| ftl::UniqueFD source_; |
| mojo::ScopedDataPipeProducerHandle destination_; |
| ftl::RefPtr<ftl::TaskRunner> task_runner_; |
| std::function<void(bool)> callback_; |
| const MojoAsyncWaiter* waiter_; |
| MojoAsyncWaitID wait_id_; |
| |
| FTL_DISALLOW_COPY_AND_ASSIGN(CopyFromFileHandler); |
| }; |
| |
| CopyFromFileHandler::CopyFromFileHandler( |
| ftl::UniqueFD source, |
| mojo::ScopedDataPipeProducerHandle destination, |
| ftl::TaskRunner* task_runner, |
| const std::function<void(bool)>& callback) |
| : source_(std::move(source)), |
| destination_(std::move(destination)), |
| task_runner_(task_runner), |
| callback_(callback), |
| waiter_(mojo::Environment::GetDefaultAsyncWaiter()), |
| wait_id_(0) { |
| task_runner_->PostTask([this]() { OnHandleReady(MOJO_RESULT_OK); }); |
| } |
| |
| CopyFromFileHandler::~CopyFromFileHandler() {} |
| |
| void CopyFromFileHandler::SendCallback(bool value) { |
| FTL_DCHECK(!wait_id_); |
| auto callback = callback_; |
| delete this; |
| callback(value); |
| } |
| |
| void CopyFromFileHandler::OnHandleReady(MojoResult result) { |
| if (result == MOJO_RESULT_OK) { |
| void* buffer = nullptr; |
| uint32_t size = 0; |
| result = BeginWriteDataRaw(destination_.get(), &buffer, &size, |
| MOJO_WRITE_DATA_FLAG_NONE); |
| if (result == MOJO_RESULT_OK) { |
| FTL_DCHECK(size < static_cast<uint32_t>(std::numeric_limits<int>::max())); |
| ssize_t bytes_read = ftl::ReadFileDescriptor( |
| source_.get(), static_cast<char*>(buffer), size); |
| result = EndWriteDataRaw(destination_.get(), |
| std::max<ssize_t>(0l, bytes_read)); |
| if (bytes_read == -1 || result != MOJO_RESULT_OK) { |
| SendCallback(false); |
| } else if (bytes_read < size) { |
| // Reached EOF. Stop the process. |
| SendCallback(true); |
| } else { |
| task_runner_->PostTask([this]() { OnHandleReady(MOJO_RESULT_OK); }); |
| } |
| return; |
| } |
| } |
| if (result == MOJO_RESULT_SHOULD_WAIT) { |
| wait_id_ = waiter_->AsyncWait( |
| destination_.get().value(), MOJO_HANDLE_SIGNAL_WRITABLE, |
| MOJO_DEADLINE_INDEFINITE, &WaitComplete, this); |
| return; |
| } |
| SendCallback(false); |
| } |
| |
| void CopyFromFileHandler::WaitComplete(void* context, MojoResult result) { |
| CopyFromFileHandler* handler = static_cast<CopyFromFileHandler*>(context); |
| handler->wait_id_ = 0; |
| handler->OnHandleReady(result); |
| } |
| |
| } // namespace |
| |
| void CopyToFileDescriptor(mojo::ScopedDataPipeConsumerHandle source, |
| ftl::UniqueFD destination, |
| ftl::TaskRunner* task_runner, |
| const std::function<void(bool)>& callback) { |
| new CopyToFileHandler(std::move(source), std::move(destination), task_runner, |
| callback); |
| } |
| |
| void CopyFromFileDescriptor(ftl::UniqueFD source, |
| mojo::ScopedDataPipeProducerHandle destination, |
| ftl::TaskRunner* task_runner, |
| const std::function<void(bool)>& callback) { |
| new CopyFromFileHandler(std::move(source), std::move(destination), |
| task_runner, callback); |
| } |
| |
| } // namespace glue |