blob: 5b8845d3a93fa2964d1908688aa1630a92363811 [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 <fuchsia/io/llcpp/fidl.h>
#include <lib/zx/channel.h>
#include <lib/zxio/inception.h>
#include <lib/zxio/null.h>
#include <lib/zxio/ops.h>
#include <zircon/syscalls.h>
#include "private.h"
namespace fio = llcpp::fuchsia::io;
namespace {
// C++ wrapper around zxio_remote_t.
class Remote {
public:
explicit Remote(zxio_t* io) : rio_(reinterpret_cast<zxio_remote_t*>(io)) {}
[[nodiscard]] zx::unowned_channel control() const { return zx::unowned_channel(rio_->control); }
zx::handle Release() {
zx::handle control(rio_->control);
rio_->control = ZX_HANDLE_INVALID;
if (rio_->event != ZX_HANDLE_INVALID) {
zx_handle_close(rio_->event);
rio_->event = ZX_HANDLE_INVALID;
}
return control;
}
private:
zxio_remote_t* rio_;
};
zx_status_t zxio_remote_close(zxio_t* io) {
Remote rio(io);
auto result = fio::Node::Call::Close(rio.control());
rio.Release().reset();
return result.ok() ? result.Unwrap()->s : result.status();
}
zx_status_t zxio_remote_release(zxio_t* io, zx_handle_t* out_handle) {
Remote rio(io);
*out_handle = rio.Release().release();
return ZX_OK;
}
zx_status_t zxio_remote_clone(zxio_t* io, zx_handle_t* out_handle) {
Remote rio(io);
zx::channel local, remote;
zx_status_t status = zx::channel::create(0, &local, &remote);
if (status != ZX_OK) {
return status;
}
uint32_t flags = fio::CLONE_FLAG_SAME_RIGHTS;
auto result = fio::Node::Call::Clone(rio.control(), flags, std::move(remote));
if (result.status() != ZX_OK) {
return result.status();
}
*out_handle = local.release();
return ZX_OK;
}
zx_status_t zxio_remote_sync(zxio_t* io) {
Remote rio(io);
auto result = fio::Node::Call::Sync(rio.control());
return result.ok() ? result.Unwrap()->s : result.status();
}
zx_status_t zxio_remote_attr_get(zxio_t* io, zxio_node_attr_t* out_attr) {
Remote rio(io);
auto result = fio::Node::Call::GetAttr(rio.control());
if (result.status() != ZX_OK) {
return result.status();
}
if (auto status = result.Unwrap()->s; status != ZX_OK) {
return status;
}
*out_attr = result.Unwrap()->attributes;
return ZX_OK;
}
zx_status_t zxio_remote_attr_set(zxio_t* io, uint32_t flags, const zxio_node_attr_t* attr) {
Remote rio(io);
auto result = fio::Node::Call::SetAttr(rio.control(), flags, *attr);
return result.ok() ? result.Unwrap()->s : result.status();
}
template <typename F>
static zx_status_t zxio_remote_do_vector(zxio_t* io, const zx_iovec_t* vector, size_t vector_count,
zxio_flags_t flags, size_t* out_actual, F fn) {
if (flags) {
return ZX_ERR_NOT_SUPPORTED;
}
Remote rio(io);
return zxio_do_vector(vector, vector_count, out_actual,
[&](void* data, size_t capacity, size_t* out_actual) {
auto buffer = static_cast<uint8_t*>(data);
size_t total = 0;
while (capacity > 0) {
size_t chunk = std::min(capacity, fio::MAX_BUF);
size_t actual;
zx_status_t status = fn(rio.control(), buffer, chunk, &actual);
if (status != ZX_OK) {
return status;
}
total += actual;
if (actual != chunk) {
break;
}
buffer += actual;
capacity -= actual;
}
*out_actual = total;
return ZX_OK;
});
}
zx_status_t zxio_remote_read_vector(zxio_t* io, const zx_iovec_t* vector, size_t vector_count,
zxio_flags_t flags, size_t* out_actual) {
return zxio_remote_do_vector(
io, vector, vector_count, flags, out_actual,
[](zx::unowned_channel control, uint8_t* buffer, size_t capacity, size_t* out_actual) {
// Explicitly allocating message buffers to avoid heap allocation.
fidl::Buffer<fio::File::ReadRequest> request_buffer;
fidl::Buffer<fio::File::ReadResponse> response_buffer;
auto result = fio::File::Call::Read(std::move(control), request_buffer.view(), capacity,
response_buffer.view());
zx_status_t status;
if ((status = result.status()) != ZX_OK) {
return status;
}
if ((status = result->s) != ZX_OK) {
return status;
}
const auto& data = result->data;
size_t actual = data.count();
if (actual > capacity) {
return ZX_ERR_IO;
}
memcpy(buffer, data.begin(), actual);
*out_actual = actual;
return ZX_OK;
});
}
zx_status_t zxio_remote_read_vector_at(zxio_t* io, zx_off_t offset, const zx_iovec_t* vector,
size_t vector_count, zxio_flags_t flags,
size_t* out_actual) {
return zxio_remote_do_vector(
io, vector, vector_count, flags, out_actual,
[&offset](zx::unowned_channel control, uint8_t* buffer, size_t capacity, size_t* out_actual) {
fidl::Buffer<fio::File::ReadAtRequest> request_buffer;
fidl::Buffer<fio::File::ReadAtResponse> response_buffer;
auto result = fio::File::Call::ReadAt(std::move(control), request_buffer.view(), capacity,
offset, response_buffer.view());
zx_status_t status;
if ((status = result.status()) != ZX_OK) {
return status;
}
if ((status = result->s) != ZX_OK) {
return status;
}
const auto& data = result->data;
size_t actual = data.count();
if (actual > capacity) {
return ZX_ERR_IO;
}
offset += actual;
memcpy(buffer, data.begin(), actual);
*out_actual = actual;
return ZX_OK;
});
}
zx_status_t zxio_remote_write_vector(zxio_t* io, const zx_iovec_t* vector, size_t vector_count,
zxio_flags_t flags, size_t* out_actual) {
return zxio_remote_do_vector(
io, vector, vector_count, flags, out_actual,
[](zx::unowned_channel control, uint8_t* buffer, size_t capacity, size_t* out_actual) {
// Explicitly allocating message buffers to avoid heap allocation.
fidl::Buffer<fio::File::WriteRequest> request_buffer;
fidl::Buffer<fio::File::WriteResponse> response_buffer;
auto result =
fio::File::Call::Write(std::move(control), request_buffer.view(),
fidl::VectorView(buffer, capacity), response_buffer.view());
zx_status_t status;
if ((status = result.status()) != ZX_OK) {
return status;
}
if ((status = result->s) != ZX_OK) {
return status;
}
size_t actual = result->actual;
if (actual > capacity) {
return ZX_ERR_IO;
}
*out_actual = actual;
return ZX_OK;
});
}
zx_status_t zxio_remote_write_vector_at(zxio_t* io, zx_off_t offset, const zx_iovec_t* vector,
size_t vector_count, zxio_flags_t flags,
size_t* out_actual) {
return zxio_remote_do_vector(
io, vector, vector_count, flags, out_actual,
[&offset](zx::unowned_channel control, uint8_t* buffer, size_t capacity, size_t* out_actual) {
// Explicitly allocating message buffers to avoid heap allocation.
fidl::Buffer<fio::File::WriteAtRequest> request_buffer;
fidl::Buffer<fio::File::WriteAtResponse> response_buffer;
auto result = fio::File::Call::WriteAt(std::move(control), request_buffer.view(),
fidl::VectorView(buffer, capacity), offset,
response_buffer.view());
zx_status_t status;
if ((status = result.status()) != ZX_OK) {
return status;
}
if ((status = result->s) != ZX_OK) {
return status;
}
size_t actual = result->actual;
if (actual > capacity) {
return ZX_ERR_IO;
}
offset += actual;
*out_actual = actual;
return ZX_OK;
});
}
zx_status_t zxio_remote_seek(zxio_t* io, zx_off_t offset, zxio_seek_origin_t start,
size_t* out_offset) {
Remote rio(io);
auto result = fio::File::Call::Seek(rio.control(), offset, static_cast<fio::SeekOrigin>(start));
if (result.status() != ZX_OK) {
return result.status();
}
if (auto status = result.Unwrap()->s; status != ZX_OK) {
return status;
}
*out_offset = result.Unwrap()->offset;
return ZX_OK;
}
zx_status_t zxio_remote_truncate(zxio_t* io, size_t length) {
Remote rio(io);
auto result = fio::File::Call::Truncate(rio.control(), length);
return result.ok() ? result.Unwrap()->s : result.status();
}
zx_status_t zxio_remote_flags_get(zxio_t* io, uint32_t* out_flags) {
Remote rio(io);
auto result = fio::File::Call::GetFlags(rio.control());
if (result.status() != ZX_OK) {
return result.status();
}
if (auto status = result.Unwrap()->s; status != ZX_OK) {
return status;
}
*out_flags = result.Unwrap()->flags;
return ZX_OK;
}
zx_status_t zxio_remote_flags_set(zxio_t* io, uint32_t flags) {
Remote rio(io);
auto result = fio::File::Call::SetFlags(rio.control(), flags);
return result.ok() ? result.Unwrap()->s : result.status();
}
zx_status_t zxio_remote_vmo_get(zxio_t* io, uint32_t flags, zx_handle_t* out_vmo,
size_t* out_size) {
Remote rio(io);
auto result = fio::File::Call::GetBuffer(rio.control(), flags);
if (result.status() != ZX_OK) {
return result.status();
}
fio::File::GetBufferResponse* response = result.Unwrap();
if (response->s != ZX_OK) {
return response->s;
}
llcpp::fuchsia::mem::Buffer* buffer = response->buffer;
if (!buffer) {
return ZX_ERR_IO;
}
if (buffer->vmo == ZX_HANDLE_INVALID) {
return ZX_ERR_IO;
}
*out_vmo = buffer->vmo.release();
*out_size = buffer->size;
return ZX_OK;
}
zx_status_t zxio_remote_open_async(zxio_t* io, uint32_t flags, uint32_t mode, const char* path,
size_t path_len, zx_handle_t request) {
Remote rio(io);
auto result = fio::Directory::Call::Open(
rio.control(), flags, mode, fidl::StringView(path, path_len), zx::channel(request));
return result.status();
}
zx_status_t zxio_remote_unlink(zxio_t* io, const char* path) {
Remote rio(io);
auto result = fio::Directory::Call::Unlink(rio.control(), fidl::StringView(path, strlen(path)));
return result.ok() ? result.Unwrap()->s : result.status();
}
zx_status_t zxio_remote_token_get(zxio_t* io, zx_handle_t* out_token) {
Remote rio(io);
auto result = fio::Directory::Call::GetToken(rio.control());
if (result.status() != ZX_OK) {
return result.status();
}
if (auto status = result.Unwrap()->s; status != ZX_OK) {
return status;
}
*out_token = result.Unwrap()->token.release();
return ZX_OK;
}
zx_status_t zxio_remote_rename(zxio_t* io, const char* src_path, zx_handle_t dst_token,
const char* dst_path) {
Remote rio(io);
auto result = fio::Directory::Call::Rename(
rio.control(), fidl::StringView(src_path, strlen(src_path)), zx::handle(dst_token),
fidl::StringView(dst_path, strlen(dst_path)));
return result.ok() ? result.Unwrap()->s : result.status();
}
zx_status_t zxio_remote_link(zxio_t* io, const char* src_path, zx_handle_t dst_token,
const char* dst_path) {
Remote rio(io);
auto result = fio::Directory::Call::Link(
rio.control(), fidl::StringView(src_path, strlen(src_path)), zx::handle(dst_token),
fidl::StringView(dst_path, strlen(dst_path)));
return result.ok() ? result.Unwrap()->s : result.status();
}
zx_status_t zxio_remote_readdir(zxio_t* io, void* buffer, size_t capacity, size_t* out_actual) {
Remote rio(io);
// Explicitly allocating message buffers to avoid heap allocation.
fidl::Buffer<fio::Directory::ReadDirentsRequest> request_buffer;
fidl::Buffer<fio::Directory::ReadDirentsResponse> response_buffer;
auto result = fio::Directory::Call::ReadDirents(rio.control(), request_buffer.view(), capacity,
response_buffer.view());
if (result.status() != ZX_OK) {
return result.status();
}
fio::Directory::ReadDirentsResponse* response = result.Unwrap();
if (response->s != ZX_OK) {
return response->s;
}
const auto& dirents = response->dirents;
if (dirents.count() > capacity) {
return ZX_ERR_IO;
}
memcpy(buffer, dirents.data(), dirents.count());
*out_actual = dirents.count();
return response->s;
}
zx_status_t zxio_remote_rewind(zxio_t* io) {
Remote rio(io);
auto result = fio::Directory::Call::Rewind(rio.control());
return result.ok() ? result.Unwrap()->s : result.status();
}
zx_status_t zxio_remote_isatty(zxio_t* io, bool* tty) {
Remote rio(io);
auto result = fio::Node::Call::Describe(rio.control());
if (result.status() != ZX_OK) {
return result.status();
}
*tty = result.Unwrap()->info.is_tty();
return ZX_OK;
}
} // namespace
static constexpr zxio_ops_t zxio_remote_ops = []() {
zxio_ops_t ops = zxio_default_ops;
ops.close = zxio_remote_close;
ops.release = zxio_remote_release;
ops.clone = zxio_remote_clone;
ops.sync = zxio_remote_sync;
ops.attr_get = zxio_remote_attr_get;
ops.attr_set = zxio_remote_attr_set;
ops.read_vector = zxio_remote_read_vector;
ops.read_vector_at = zxio_remote_read_vector_at;
ops.write_vector = zxio_remote_write_vector;
ops.write_vector_at = zxio_remote_write_vector_at;
ops.seek = zxio_remote_seek;
ops.truncate = zxio_remote_truncate;
ops.flags_get = zxio_remote_flags_get;
ops.flags_set = zxio_remote_flags_set;
ops.vmo_get = zxio_remote_vmo_get;
ops.open_async = zxio_remote_open_async;
ops.unlink = zxio_remote_unlink;
ops.token_get = zxio_remote_token_get;
ops.rename = zxio_remote_rename;
ops.link = zxio_remote_link;
ops.readdir = zxio_remote_readdir;
ops.rewind = zxio_remote_rewind;
ops.isatty = zxio_remote_isatty;
return ops;
}();
zx_status_t zxio_remote_init(zxio_storage_t* storage, zx_handle_t control, zx_handle_t event) {
auto remote = reinterpret_cast<zxio_remote_t*>(storage);
zxio_init(&remote->io, &zxio_remote_ops);
remote->control = control;
remote->event = event;
return ZX_OK;
}
namespace {
zx_status_t zxio_dir_read_vector(zxio_t* io, const zx_iovec_t* vector, size_t vector_count,
zxio_flags_t flags, size_t* out_actual) {
if (flags) {
return ZX_ERR_NOT_SUPPORTED;
}
return zxio_do_vector(vector, vector_count, out_actual,
[](void* buffer, size_t capacity, size_t* out_actual) {
if (capacity > 0) {
return ZX_ERR_WRONG_TYPE;
}
*out_actual = 0;
return ZX_OK;
});
}
zx_status_t zxio_dir_read_vector_at(zxio_t* io, zx_off_t offset, const zx_iovec_t* vector,
size_t vector_count, zxio_flags_t flags, size_t* out_actual) {
return zxio_dir_read_vector(io, vector, vector_count, flags, out_actual);
}
} // namespace
static constexpr zxio_ops_t zxio_dir_ops = []() {
zxio_ops_t ops = zxio_default_ops;
ops.close = zxio_remote_close;
ops.release = zxio_remote_release;
ops.clone = zxio_remote_clone;
ops.sync = zxio_remote_sync;
ops.attr_get = zxio_remote_attr_get;
ops.attr_set = zxio_remote_attr_set;
// use specialized read functions that succeed for zero-sized reads.
ops.read_vector = zxio_dir_read_vector;
ops.read_vector_at = zxio_dir_read_vector_at;
ops.flags_get = zxio_remote_flags_get;
ops.flags_set = zxio_remote_flags_set;
ops.open_async = zxio_remote_open_async;
ops.unlink = zxio_remote_unlink;
ops.token_get = zxio_remote_token_get;
ops.rename = zxio_remote_rename;
ops.link = zxio_remote_link;
ops.readdir = zxio_remote_readdir;
ops.rewind = zxio_remote_rewind;
return ops;
}();
zx_status_t zxio_dir_init(zxio_storage_t* storage, zx_handle_t control) {
auto remote = reinterpret_cast<zxio_remote_t*>(storage);
zxio_init(&remote->io, &zxio_dir_ops);
remote->control = control;
remote->event = ZX_HANDLE_INVALID;
return ZX_OK;
}
static constexpr zxio_ops_t zxio_file_ops = []() {
zxio_ops_t ops = zxio_default_ops;
ops.close = zxio_remote_close;
ops.release = zxio_remote_release;
ops.clone = zxio_remote_clone;
ops.sync = zxio_remote_sync;
ops.attr_get = zxio_remote_attr_get;
ops.attr_set = zxio_remote_attr_set;
ops.read_vector = zxio_remote_read_vector;
ops.read_vector_at = zxio_remote_read_vector_at;
ops.write_vector = zxio_remote_write_vector;
ops.write_vector_at = zxio_remote_write_vector_at;
ops.seek = zxio_remote_seek;
ops.truncate = zxio_remote_truncate;
ops.flags_get = zxio_remote_flags_get;
ops.flags_set = zxio_remote_flags_set;
ops.vmo_get = zxio_remote_vmo_get;
return ops;
}();
zx_status_t zxio_file_init(zxio_storage_t* storage, zx_handle_t control, zx_handle_t event) {
auto remote = reinterpret_cast<zxio_remote_t*>(storage);
zxio_init(&remote->io, &zxio_file_ops);
remote->control = control;
remote->event = event;
return ZX_OK;
}