blob: 5358f53271487c38c7121e5a541ebdeeba31b91e [file] [log] [blame]
// Copyright 2021 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 <fidl/fuchsia.posix.socket/cpp/wire.h>
#include <lib/fit/defer.h>
#include <lib/zx/handle.h>
#include <lib/zx/vmo.h>
#include <lib/zxio/cpp/inception.h>
#include <lib/zxio/null.h>
#include <lib/zxio/zxio.h>
#include <stdarg.h>
#include "sdk/lib/zxio/private.h"
namespace fio = fuchsia_io;
namespace {
// A zxio_handle_holder is a zxio object instance that holds on to a handle and
// allows it to be closed or released via zxio_close() / zxio_release(). It is
// useful for wrapping objects that zxio does not understand.
struct zxio_handle_holder {
zxio_t io;
zx::handle handle;
};
static_assert(sizeof(zxio_handle_holder) <= sizeof(zxio_storage_t),
"zxio_handle_holder must fit inside zxio_storage_t.");
zxio_handle_holder& zxio_get_handle_holder(zxio_t* io) {
return *reinterpret_cast<zxio_handle_holder*>(io);
}
constexpr zxio_ops_t zxio_handle_holder_ops = []() {
zxio_ops_t ops = zxio_default_ops;
ops.close = [](zxio_t* io) {
zxio_get_handle_holder(io).~zxio_handle_holder();
return ZX_OK;
};
ops.release = [](zxio_t* io, zx_handle_t* out_handle) {
zx_handle_t handle = zxio_get_handle_holder(io).handle.release();
if (handle == ZX_HANDLE_INVALID) {
return ZX_ERR_BAD_HANDLE;
}
*out_handle = handle;
return ZX_OK;
};
return ops;
}();
void zxio_handle_holder_init(zxio_storage_t* storage, zx::handle handle) {
auto holder = new (storage) zxio_handle_holder{
.handle = std::move(handle),
};
zxio_init(&holder->io, &zxio_handle_holder_ops);
}
class ZxioCreateOnOpenEventHandler final : public fidl::WireSyncEventHandler<fio::Node> {
public:
ZxioCreateOnOpenEventHandler(fidl::ClientEnd<fio::Node> node, zxio_storage_t* storage,
zx_status_t& status)
: node_(std::move(node)), storage_(storage), status_(status) {}
protected:
void OnOpen(fidl::WireEvent<fio::Node::OnOpen>* event) final {
status_ = event->s;
if (event->s != ZX_OK)
return;
status_ = zxio_create_with_nodeinfo(std::move(node_), event->info, storage_);
}
void OnConnectionInfo(fidl::WireEvent<fio::Node::OnConnectionInfo>* event) final {
status_ = ZX_ERR_NOT_SUPPORTED;
}
private:
fidl::ClientEnd<fio::Node> node_;
zxio_storage_t* storage_;
zx_status_t& status_;
};
} // namespace
zx_status_t zxio_create_with_info(zx_handle_t raw_handle, const zx_info_handle_basic_t* handle_info,
zxio_storage_t* storage) {
zx::handle handle(raw_handle);
if (!handle.is_valid() || storage == nullptr || handle_info == nullptr) {
return ZX_ERR_INVALID_ARGS;
}
switch (handle_info->type) {
case ZX_OBJ_TYPE_CHANNEL: {
fidl::ClientEnd<fio::Node> node(zx::channel(std::move(handle)));
fidl::WireResult result = fidl::WireCall(node)->Describe();
if (!result.ok()) {
return result.status();
}
return zxio_create_with_nodeinfo(std::move(node), result.value().info, storage);
}
case ZX_OBJ_TYPE_LOG: {
zxio_debuglog_init(storage, zx::debuglog(std::move(handle)));
return ZX_OK;
}
case ZX_OBJ_TYPE_SOCKET: {
zx::socket socket(std::move(handle));
zx_info_socket_t info;
zx_status_t status = socket.get_info(ZX_INFO_SOCKET, &info, sizeof(info), nullptr, nullptr);
if (status != ZX_OK) {
return status;
}
return zxio_pipe_init(storage, std::move(socket), info);
}
case ZX_OBJ_TYPE_VMO: {
zx::vmo vmo(std::move(handle));
zx::stream stream;
uint32_t options = 0u;
if (handle_info->rights & ZX_RIGHT_READ) {
options |= ZX_STREAM_MODE_READ;
}
if (handle_info->rights & ZX_RIGHT_WRITE) {
options |= ZX_STREAM_MODE_WRITE;
}
// We pass 0 for the initial seek value because the |handle| we're given does not remember
// the seek value we had previously.
zx_status_t status = zx::stream::create(options, vmo, 0u, &stream);
if (status != ZX_OK) {
zxio_default_init(&storage->io);
return status;
}
return zxio_vmo_init(storage, std::move(vmo), std::move(stream));
}
default: {
zxio_handle_holder_init(storage, std::move(handle));
return ZX_ERR_NOT_SUPPORTED;
}
}
}
zx_status_t zxio_create(zx_handle_t raw_handle, zxio_storage_t* storage) {
zx::handle handle(raw_handle);
if (!handle.is_valid() || storage == nullptr) {
return ZX_ERR_INVALID_ARGS;
}
zx_info_handle_basic_t info = {};
zx_status_t status = handle.get_info(ZX_INFO_HANDLE_BASIC, &info, sizeof(info), nullptr, nullptr);
if (status != ZX_OK) {
zxio_default_init(&storage->io);
return status;
}
return zxio_create_with_info(handle.release(), &info, storage);
}
zx_status_t zxio_create_with_on_open(zx_handle_t raw_handle, zxio_storage_t* storage) {
fidl::ClientEnd<fio::Node> node{zx::channel(raw_handle)};
if (!node.is_valid() || storage == nullptr) {
return ZX_ERR_INVALID_ARGS;
}
fidl::UnownedClientEnd unowned_node = node.borrow();
zx_status_t handler_status;
ZxioCreateOnOpenEventHandler handler(std::move(node), storage, handler_status);
const fidl::Status status = handler.HandleOneEvent(unowned_node);
if (!status.ok()) {
if (status.reason() == fidl::Reason::kUnexpectedMessage) {
return ZX_ERR_IO;
}
return status.status();
}
return handler_status;
}
zx_status_t zxio_create_with_nodeinfo(fidl::ClientEnd<fio::Node> node, fio::wire::NodeInfo& info,
zxio_storage_t* storage) {
switch (info.Which()) {
case fio::wire::NodeInfo::Tag::kDevice: {
return zxio_remote_init(storage, node.TakeChannel().release(), ZX_HANDLE_INVALID);
}
case fio::wire::NodeInfo::Tag::kDirectory: {
return zxio_dir_init(storage, node.TakeChannel().release());
}
case fio::wire::NodeInfo::Tag::kFile: {
fio::wire::FileObject& file = info.file();
zx::event event = std::move(file.event);
zx::stream stream = std::move(file.stream);
return zxio_file_init(storage, node.TakeChannel().release(), event.release(),
stream.release());
}
case fio::wire::NodeInfo::Tag::kPipe: {
fio::wire::PipeObject& pipe = info.pipe();
zx::socket socket = std::move(pipe.socket);
zx_info_socket_t socket_info;
zx_status_t status =
socket.get_info(ZX_INFO_SOCKET, &socket_info, sizeof(socket_info), nullptr, nullptr);
if (status != ZX_OK) {
return status;
}
return zxio_pipe_init(storage, std::move(socket), socket_info);
}
case fio::wire::NodeInfo::Tag::kService: {
return zxio_remote_init(storage, node.TakeChannel().release(), ZX_HANDLE_INVALID);
}
case fio::wire::NodeInfo::Tag::kTty: {
fio::wire::Tty& tty = info.tty();
zx::eventpair event = std::move(tty.event);
return zxio_remote_init(storage, node.TakeChannel().release(), event.release());
}
case fio::wire::NodeInfo::Tag::kVmofile: {
fio::wire::Vmofile& file = info.vmofile();
fidl::ClientEnd<fio::File> control(node.TakeChannel());
const fidl::WireResult result =
fidl::WireCall(control.borrow())->Seek(fio::wire::SeekOrigin::kStart, 0);
if (!result.ok()) {
return result.status();
}
const auto& response = result.value();
if (response.is_error()) {
return response.error_value();
}
return zxio_vmofile_init(storage, fidl::BindSyncClient(std::move(control)),
std::move(file.vmo), file.offset, file.length,
response.value()->offset_from_start);
}
default: {
zxio_handle_holder_init(storage, node.TakeChannel());
return ZX_ERR_NOT_SUPPORTED;
}
}
}
zx_status_t zxio_create_with_type(zxio_storage_t* storage, zxio_object_type_t type, ...) {
va_list args;
va_start(args, type);
const fit::deferred_action va_cleanup = fit::defer([&args]() { va_end(args); });
switch (type) {
case ZXIO_OBJECT_TYPE_SYNCHRONOUS_DATAGRAM_SOCKET: {
zx::eventpair event(va_arg(args, zx_handle_t));
zx::channel client(va_arg(args, zx_handle_t));
if (!event.is_valid() || !client.is_valid() || storage == nullptr) {
return ZX_ERR_INVALID_ARGS;
}
return zxio_synchronous_datagram_socket_init(
storage, std::move(event),
fidl::ClientEnd<fuchsia_posix_socket::SynchronousDatagramSocket>(std::move(client)));
}
case ZXIO_OBJECT_TYPE_DATAGRAM_SOCKET: {
zx::socket socket(va_arg(args, zx_handle_t));
zx::channel client(va_arg(args, zx_handle_t));
zx_info_socket_t* info = va_arg(args, zx_info_socket_t*);
if (!socket.is_valid() || !client.is_valid() || storage == nullptr || info == nullptr) {
return ZX_ERR_INVALID_ARGS;
}
return zxio_datagram_socket_init(
storage, std::move(socket),
fidl::ClientEnd<fuchsia_posix_socket::DatagramSocket>(std::move(client)), *info);
}
case ZXIO_OBJECT_TYPE_DIR: {
zx::handle control(va_arg(args, zx_handle_t));
if (!control.is_valid() || storage == nullptr) {
return ZX_ERR_INVALID_ARGS;
}
return zxio_dir_init(storage, control.release());
}
case ZXIO_OBJECT_TYPE_NODE: {
zx::handle control(va_arg(args, zx_handle_t));
if (!control.is_valid() || storage == nullptr) {
return ZX_ERR_INVALID_ARGS;
}
return zxio_remote_init(storage, control.release(), ZX_HANDLE_INVALID);
}
case ZXIO_OBJECT_TYPE_STREAM_SOCKET: {
zx::socket socket(va_arg(args, zx_handle_t));
zx::channel client(va_arg(args, zx_handle_t));
zx_info_socket_t* info = va_arg(args, zx_info_socket_t*);
if (!socket.is_valid() || !client.is_valid() || storage == nullptr || info == nullptr) {
return ZX_ERR_INVALID_ARGS;
}
return zxio_stream_socket_init(
storage, std::move(socket),
fidl::ClientEnd<fuchsia_posix_socket::StreamSocket>(std::move(client)), *info);
}
case ZXIO_OBJECT_TYPE_PIPE: {
zx::socket socket(va_arg(args, zx_handle_t));
zx_info_socket_t* info = va_arg(args, zx_info_socket_t*);
if (!socket.is_valid() || storage == nullptr || info == nullptr) {
return ZX_ERR_INVALID_ARGS;
}
return zxio_pipe_init(storage, std::move(socket), *info);
}
case ZXIO_OBJECT_TYPE_RAW_SOCKET: {
zx::eventpair event(va_arg(args, zx_handle_t));
zx::channel client(va_arg(args, zx_handle_t));
if (!event.is_valid() || !client.is_valid() || storage == nullptr) {
return ZX_ERR_INVALID_ARGS;
}
return zxio_raw_socket_init(
storage, std::move(event),
fidl::ClientEnd<fuchsia_posix_socket_raw::Socket>(std::move(client)));
}
case ZXIO_OBJECT_TYPE_PACKET_SOCKET: {
zx::eventpair event(va_arg(args, zx_handle_t));
zx::channel client(va_arg(args, zx_handle_t));
if (!event.is_valid() || !client.is_valid() || storage == nullptr) {
return ZX_ERR_INVALID_ARGS;
}
return zxio_packet_socket_init(
storage, std::move(event),
fidl::ClientEnd<fuchsia_posix_socket_packet::Socket>(std::move(client)));
}
case ZXIO_OBJECT_TYPE_VMO: {
zx::vmo vmo(va_arg(args, zx_handle_t));
zx::stream stream(va_arg(args, zx_handle_t));
if (!vmo.is_valid() || !stream.is_valid() || storage == nullptr) {
return ZX_ERR_INVALID_ARGS;
}
return zxio_vmo_init(storage, std::move(vmo), std::move(stream));
}
}
return ZX_ERR_NOT_SUPPORTED;
}