blob: 8882cce6c4b04bc10d02a5c017ed16cad2aa59fd [file] [log] [blame]
// Copyright 2018 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 "zxio.h"
#include <fidl/fuchsia.io/cpp/wire.h>
#include <lib/fdio/fdio.h>
#include <lib/fdio/io.h>
#include <lib/zxio/bsdsocket.h>
#include <lib/zxio/cpp/create_with_type.h>
#include <lib/zxio/cpp/transitional.h>
#include <lib/zxio/null.h>
#include <lib/zxio/watcher.h>
#include <lib/zxio/zxio.h>
#include <poll.h>
#include <stdarg.h>
#include <sys/ioctl.h>
#include <zircon/rights.h>
#include <zircon/syscalls.h>
#include <zircon/types.h>
#include <fbl/auto_lock.h>
#include "fdio_unistd.h"
namespace fio = fuchsia_io;
namespace fdio_internal {
zx::result<fdio_ptr> zxio::create() {
fdio_ptr io = fbl::MakeRefCounted<zxio>();
if (io == nullptr) {
return zx::error(ZX_ERR_NO_MEMORY);
}
zxio_default_init(&io->zxio_storage().io);
return zx::ok(io);
}
zx::result<fdio_ptr> zxio::create_null() {
fdio_ptr io = fbl::MakeRefCounted<zxio>();
if (io == nullptr) {
return zx::error(ZX_ERR_NO_MEMORY);
}
zxio_null_init(&io->zxio_storage().io);
return zx::ok(io);
}
zx_status_t zxio::close(const bool should_wait) {
return zxio_close(&zxio_storage().io, should_wait);
}
zx_status_t zxio::borrow_channel(zx_handle_t* out_borrowed) {
return zxio_borrow(&zxio_storage().io, out_borrowed);
}
zx_status_t zxio::clone(zx_handle_t* out_handle) {
return zxio_clone(&zxio_storage().io, out_handle);
}
zx_status_t zxio::unwrap(zx_handle_t* out_handle) {
return zxio_release(&zxio_storage().io, out_handle);
}
void zxio::wait_begin(uint32_t events, zx_handle_t* out_handle, zx_signals_t* out_signals) {
return zxio_wait_begin_inner(&zxio_storage().io, events, ZXIO_SIGNAL_NONE, out_handle,
out_signals);
}
void zxio::wait_end(zx_signals_t signals, uint32_t* out_events) {
return zxio_wait_end_inner(&zxio_storage().io, signals, out_events, nullptr);
}
Errno zxio::posix_ioctl(int request, va_list va) {
switch (request) {
case TIOCGWINSZ: {
uint32_t width, height;
zx_status_t status = zxio_get_window_size(&zxio_storage().io, &width, &height);
if (status != ZX_OK) {
return Errno(ENOTTY);
}
struct winsize size = {
.ws_row = static_cast<uint16_t>(height),
.ws_col = static_cast<uint16_t>(width),
};
struct winsize* out_size = va_arg(va, struct winsize*);
*out_size = size;
return Errno(Errno::Ok);
}
case TIOCSWINSZ: {
const struct winsize* in_size = va_arg(va, const struct winsize*);
zx_status_t status =
zxio_set_window_size(&zxio_storage().io, in_size->ws_col, in_size->ws_row);
if (status != ZX_OK) {
return Errno(ENOTTY);
}
return Errno(Errno::Ok);
}
case FIONREAD: {
size_t available = 0u;
zx_status_t status = zxio_get_read_buffer_available(&zxio_storage().io, &available);
if (status != ZX_OK) {
return Errno(ENOTTY);
}
if (available > INT_MAX) {
available = INT_MAX;
}
int* actual = va_arg(va, int*);
*actual = static_cast<int>(available);
return Errno(Errno::Ok);
}
default:
int16_t out_code;
zx_status_t status = zxio_ioctl(&zxio_storage().io, request, &out_code, va);
if (status != ZX_OK) {
return Errno(ENOTTY);
}
return Errno(out_code);
}
}
zx_status_t zxio::get_token(zx_handle_t* out) { return zxio_token_get(&zxio_storage().io, out); }
zx_status_t zxio::get_attr(zxio_node_attributes_t* out) {
return zxio_attr_get(&zxio_storage().io, out);
}
zx_status_t zxio::set_attr(const zxio_node_attributes_t* attr) {
return zxio_attr_set(&zxio_storage().io, attr);
}
zx_status_t zxio::dirent_iterator_init(zxio_dirent_iterator_t* iterator, zxio_t* directory) {
return zxio_dirent_iterator_init(iterator, directory);
}
zx_status_t zxio::dirent_iterator_next(zxio_dirent_iterator_t* iterator,
zxio_dirent_t* inout_entry) {
return zxio_dirent_iterator_next(iterator, inout_entry);
}
zx_status_t zxio::dirent_iterator_rewind(zxio_dirent_iterator_t* iterator) {
return zxio_dirent_iterator_rewind(iterator);
}
void zxio::dirent_iterator_destroy(zxio_dirent_iterator_t* iterator) {
return zxio_dirent_iterator_destroy(iterator);
}
zx_status_t zxio::watch_directory(zxio_watch_directory_cb cb, zx_time_t deadline, void* context) {
return zxio_watch_directory(&zxio_storage().io, cb, deadline, context);
}
zx_status_t zxio::unlink(std::string_view name, int flags) {
return zxio_unlink(&zxio_storage().io, name.data(), name.length(), flags);
}
zx_status_t zxio::truncate(uint64_t off) { return zxio_truncate(&zxio_storage().io, off); }
zx_status_t zxio::rename(std::string_view src, zx_handle_t dst_token, std::string_view dst) {
return zxio_rename(&zxio_storage().io, src.data(), src.length(), dst_token, dst.data(),
dst.length());
}
zx_status_t zxio::link(std::string_view src, zx_handle_t dst_token, std::string_view dst) {
return zxio_link(&zxio_storage().io, src.data(), src.length(), dst_token, dst.data(),
dst.length());
}
zx_status_t zxio::get_flags(fio::wire::OpenFlags* out_flags) {
return zxio_flags_get(&zxio_storage().io, reinterpret_cast<uint32_t*>(out_flags));
}
zx_status_t zxio::set_flags(fio::wire::OpenFlags flags) {
return zxio_flags_set(&zxio_storage().io, static_cast<uint32_t>(flags));
}
zx_status_t zxio::recvmsg(struct msghdr* msg, int flags, size_t* out_actual, int16_t* out_code) {
*out_code = 0;
return zxio_recvmsg_inner(&zxio_storage().io, msg, flags, out_actual);
}
zx_status_t zxio::sendmsg(const struct msghdr* msg, int flags, size_t* out_actual,
int16_t* out_code) {
*out_code = 0;
return zxio_sendmsg_inner(&zxio_storage().io, msg, flags, out_actual);
}
zx::result<fdio_ptr> pipe::create(zx::socket socket) {
fdio_ptr io = fbl::MakeRefCounted<pipe>();
if (io == nullptr) {
return zx::error(ZX_ERR_NO_MEMORY);
}
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 zx::error(status);
}
status = ::zxio::CreatePipe(&io->zxio_storage(), std::move(socket), info);
if (status != ZX_OK) {
return zx::error(status);
}
return zx::ok(io);
}
zx::result<std::pair<fdio_ptr, fdio_ptr>> pipe::create_pair(uint32_t options) {
zx::socket h0, h1;
zx_status_t status = zx::socket::create(options, &h0, &h1);
if (status != ZX_OK) {
return zx::error(status);
}
zx::result a = pipe::create(std::move(h0));
if (a.is_error()) {
return a.take_error();
}
zx::result b = pipe::create(std::move(h1));
if (b.is_error()) {
return b.take_error();
}
return zx::ok(std::make_pair(a.value(), b.value()));
}
zx_status_t pipe::recvmsg(struct msghdr* msg, int flags, size_t* out_actual, int16_t* out_code) {
*out_code = 0;
zx_status_t status = zxio_recvmsg_inner(&zxio_storage().io, msg, flags, out_actual);
// We've reached end-of-file, which is signaled by successfully reading zero
// bytes.
//
// If we see |ZX_ERR_BAD_STATE|, that implies reading has been disabled for
// this endpoint.
if (status == ZX_ERR_PEER_CLOSED || status == ZX_ERR_BAD_STATE) {
*out_actual = 0;
status = ZX_OK;
}
return status;
}
zx::result<fdio_ptr> open_async(zxio_t* directory, std::string_view path,
fio::wire::OpenFlags flags) {
zx::result endpoints = fidl::CreateEndpoints<fio::Node>();
if (endpoints.is_error()) {
return endpoints.take_error();
}
zx_status_t status = zxio_open_async(directory, static_cast<uint32_t>(flags), path.data(),
path.length(), endpoints->server.channel().release());
if (status != ZX_OK) {
return zx::error(status);
}
if (flags & fio::wire::OpenFlags::kDescribe) {
return fdio::create_with_on_open(std::move(endpoints->client));
}
return remote::create(std::move(endpoints->client));
}
zx::result<fdio_ptr> remote::open(std::string_view path, fio::wire::OpenFlags flags) {
return open_async(&zxio_storage().io, path, flags);
}
void remote::wait_begin(uint32_t events, zx_handle_t* handle, zx_signals_t* out_signals) {
// POLLERR is always detected.
events |= POLLERR;
zxio_signals_t signals = ZXIO_SIGNAL_NONE;
if (events & POLLIN) {
signals |= ZXIO_SIGNAL_READABLE;
}
if (events & POLLPRI) {
signals |= ZXIO_SIGNAL_OUT_OF_BAND;
}
if (events & POLLOUT) {
signals |= ZXIO_SIGNAL_WRITABLE;
}
if (events & POLLERR) {
signals |= ZXIO_SIGNAL_ERROR;
}
if (events & POLLHUP) {
signals |= ZXIO_SIGNAL_PEER_CLOSED;
}
if (events & POLLRDHUP) {
signals |= ZXIO_SIGNAL_READ_DISABLED;
}
zxio_wait_begin(&zxio_storage().io, signals, handle, out_signals);
}
void remote::wait_end(zx_signals_t signals, uint32_t* out_events) {
zxio_signals_t zxio_signals = 0;
zxio_wait_end(&zxio_storage().io, signals, &zxio_signals);
uint32_t events = 0;
if (zxio_signals & ZXIO_SIGNAL_READABLE) {
events |= POLLIN;
}
if (zxio_signals & ZXIO_SIGNAL_OUT_OF_BAND) {
events |= POLLPRI;
}
if (zxio_signals & ZXIO_SIGNAL_WRITABLE) {
events |= POLLOUT;
}
if (zxio_signals & ZXIO_SIGNAL_ERROR) {
events |= POLLERR;
}
if (zxio_signals & ZXIO_SIGNAL_PEER_CLOSED) {
events |= POLLHUP;
}
if (zxio_signals & ZXIO_SIGNAL_READ_DISABLED) {
events |= POLLRDHUP;
}
*out_events = events;
}
zx::result<fdio_ptr> remote::create(fidl::ClientEnd<fuchsia_io::Node> node) {
fdio_ptr io = fbl::MakeRefCounted<remote>();
if (io == nullptr) {
return zx::error(ZX_ERR_NO_MEMORY);
}
zx_status_t status = ::zxio::CreateNode(&io->zxio_storage(), std::move(node));
if (status != ZX_OK) {
return zx::error(status);
}
return zx::ok(io);
}
zx::result<fdio_ptr> remote::create(zx::vmo vmo, zx::stream stream) {
fdio_ptr io = fbl::MakeRefCounted<remote>();
if (io == nullptr) {
return zx::error(ZX_ERR_NO_MEMORY);
}
zx_status_t status = ::zxio::CreateVmo(&io->zxio_storage(), std::move(vmo), std::move(stream));
if (status != ZX_OK) {
return zx::error(status);
}
return zx::ok(io);
}
} // namespace fdio_internal