blob: 8f642918b5713ef4b3ba740a072b90cf2161c9b0 [file] [log] [blame]
// Copyright 2019 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.io/cpp/wire.h>
#include <lib/zxio/null.h>
#include <lib/zxio/ops.h>
#include <sys/stat.h>
#include <zircon/syscalls.h>
#include "private.h"
class Vmo : public HasIo {
public:
Vmo(zx::vmo vmo, zx::stream stream)
: HasIo(kOps), vmo_(std::move(vmo)), stream_(std::move(stream)) {}
private:
static const zxio_ops_t kOps;
// The underlying VMO that stores the data.
zx::vmo vmo_;
// The stream through which we will read and write the VMO.
zx::stream stream_;
protected:
zx_status_t Close(const bool should_wait) {
this->~Vmo();
return ZX_OK;
}
zx_status_t Release(zx_handle_t* out_handle) {
*out_handle = vmo_.release();
;
return ZX_OK;
}
zx_status_t Clone(zx_handle_t* out_handle) {
zx::vmo vmo;
if (zx_status_t status = vmo_.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo); status != ZX_OK) {
return status;
}
*out_handle = vmo.release();
return ZX_OK;
}
zx_status_t AttrGet(zxio_node_attributes_t* inout_attr) {
uint64_t content_size;
if (zx_status_t status =
vmo_.get_property(ZX_PROP_VMO_CONTENT_SIZE, &content_size, sizeof(content_size));
status != ZX_OK) {
return status;
}
if (inout_attr->has.protocols) {
ZXIO_NODE_ATTR_SET(*inout_attr, protocols, ZXIO_NODE_PROTOCOL_FILE);
}
if (inout_attr->has.abilities) {
ZXIO_NODE_ATTR_SET(*inout_attr, abilities,
ZXIO_OPERATION_READ_BYTES | ZXIO_OPERATION_GET_ATTRIBUTES);
}
if (inout_attr->has.content_size) {
ZXIO_NODE_ATTR_SET(*inout_attr, content_size, content_size);
}
return ZX_OK;
}
zx_status_t Readv(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 stream_.readv(0, vector, vector_count, out_actual);
}
zx_status_t ReadvAt(zx_off_t offset, 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 stream_.readv_at(0, offset, vector, vector_count, out_actual);
}
zx_status_t Writev(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 stream_.writev(0, vector, vector_count, out_actual);
}
zx_status_t WritevAt(zx_off_t offset, 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 stream_.writev_at(0, offset, vector, vector_count, out_actual);
}
static_assert(ZXIO_SEEK_ORIGIN_START == ZX_STREAM_SEEK_ORIGIN_START, "ZXIO should match ZX");
static_assert(ZXIO_SEEK_ORIGIN_CURRENT == ZX_STREAM_SEEK_ORIGIN_CURRENT, "ZXIO should match ZX");
static_assert(ZXIO_SEEK_ORIGIN_END == ZX_STREAM_SEEK_ORIGIN_END, "ZXIO should match ZX");
zx_status_t Seek(zxio_seek_origin_t start, int64_t offset, size_t* out_offset) {
return stream_.seek(static_cast<zx_stream_seek_origin_t>(start), offset, out_offset);
}
zx_status_t Truncate(uint64_t length) { return vmo_.set_size(length); }
zx_status_t FlagsGet(uint32_t* out_flags) {
zx_info_handle_basic_t info;
zx_status_t get_status =
vmo_.get_info(ZX_INFO_HANDLE_BASIC, &info, sizeof(info), nullptr, nullptr);
if (get_status != ZX_OK) {
// Returns ZX_ERR_NOT_SUPPORTED, because a posix FD doesn't seem to have any way to lack
// sufficient rights to F_GETFL (AFAICT), so the most accurate description of this situation
// (AFAICT) is that FlagsGet() isn't supported on this particular zxio_t after all. We could
// just return ZX_ERR_NOT_SUPPORTED directly here, but in case the behavior of
// zxio_default_flags_get() changes, we really do want to delegate to the default behavior
// here, to make sure we continue to say "we don't have that op after all" essentially.
return zxio_default_flags_get(io(), out_flags);
}
ZX_ASSERT(info.type == ZX_OBJ_TYPE_VMO);
fuchsia_io::wire::OpenFlags flags{};
if (info.rights & ZX_RIGHT_READ) {
flags |= fuchsia_io::wire::OpenFlags::kRightReadable;
}
if (info.rights & ZX_RIGHT_WRITE) {
flags |= fuchsia_io::wire::OpenFlags::kRightWritable;
}
if (info.rights & ZX_RIGHT_EXECUTE) {
flags |= fuchsia_io::wire::OpenFlags::kRightExecutable;
}
*out_flags = static_cast<uint32_t>(flags);
return ZX_OK;
}
zx_status_t VmoGet(zxio_vmo_flags_t flags, zx_handle_t* out_vmo) {
if (out_vmo == nullptr) {
return ZX_ERR_INVALID_ARGS;
}
zx::vmo& vmo = vmo_;
uint64_t size;
if (zx_status_t status = vmo.get_prop_content_size(&size); status != ZX_OK) {
return status;
}
// Ensure that we return a VMO handle with only the rights requested by the client.
zx_rights_t rights = ZX_RIGHTS_BASIC | ZX_RIGHT_MAP | ZX_RIGHT_GET_PROPERTY;
rights |= flags & ZXIO_VMO_READ ? ZX_RIGHT_READ : 0;
rights |= flags & ZXIO_VMO_WRITE ? ZX_RIGHT_WRITE : 0;
rights |= flags & ZXIO_VMO_EXECUTE ? ZX_RIGHT_EXECUTE : 0;
if (flags & ZXIO_VMO_PRIVATE_CLONE) {
// Allow ZX_RIGHT_SET_PROPERTY only if creating a private child VMO so that the user can set
// ZX_PROP_NAME (or similar).
rights |= ZX_RIGHT_SET_PROPERTY;
uint32_t options = ZX_VMO_CHILD_SNAPSHOT_AT_LEAST_ON_WRITE;
if (flags & ZXIO_VMO_EXECUTE) {
// Creating a ZX_VMO_CHILD_SNAPSHOT_AT_LEAST_ON_WRITE child removes ZX_RIGHT_EXECUTE even if
// the parent VMO has it, and we can't arbitrarily add ZX_RIGHT_EXECUTE here on the client
// side. Adding ZX_VMO_CHILD_NO_WRITE still creates a snapshot and a new VMO object, which
// e.g. can have a unique ZX_PROP_NAME value, but the returned handle lacks ZX_RIGHT_WRITE
// and maintains ZX_RIGHT_EXECUTE.
if (flags & ZXIO_VMO_WRITE) {
return ZX_ERR_NOT_SUPPORTED;
}
options |= ZX_VMO_CHILD_NO_WRITE;
}
zx::vmo child_vmo;
zx_status_t status = vmo.create_child(options, 0u, size, &child_vmo);
if (status != ZX_OK) {
return status;
}
// ZX_VMO_CHILD_SNAPSHOT_AT_LEAST_ON_WRITE adds ZX_RIGHT_WRITE automatically, but we shouldn't
// return a handle with that right unless requested using ZXIO_VMO_WRITE.
//
// TODO(https://fxbug.dev/42112453): Supporting ZXIO_VMO_PRIVATE_CLONE & ZXIO_VMO_WRITE for Vmofiles is a
// bit weird and inconsistent. See bug for more info.
zx::vmo result;
status = child_vmo.replace(rights, &result);
if (status != ZX_OK) {
return status;
}
*out_vmo = result.release();
return ZX_OK;
}
// For !ZXIO_VMO_PRIVATE_CLONE we just duplicate another handle to the Vmofile's VMO with
// appropriately scoped rights.
zx::vmo result;
zx_status_t status = vmo.duplicate(rights, &result);
if (status != ZX_OK) {
return status;
}
*out_vmo = result.release();
return ZX_OK;
}
};
constexpr zxio_ops_t Vmo::kOps = []() {
using Adaptor = Adaptor<Vmo>;
zxio_ops_t ops = zxio_default_ops;
ops.close = Adaptor::From<&Vmo::Close>;
ops.release = Adaptor::From<&Vmo::Release>;
ops.clone = Adaptor::From<&Vmo::Clone>;
ops.attr_get = Adaptor::From<&Vmo::AttrGet>;
ops.readv = Adaptor::From<&Vmo::Readv>;
ops.readv_at = Adaptor::From<&Vmo::ReadvAt>;
ops.writev = Adaptor::From<&Vmo::Writev>;
ops.writev_at = Adaptor::From<&Vmo::WritevAt>;
ops.seek = Adaptor::From<&Vmo::Seek>;
ops.truncate = Adaptor::From<&Vmo::Truncate>;
ops.flags_get = Adaptor::From<&Vmo::FlagsGet>;
ops.vmo_get = Adaptor::From<&Vmo::VmoGet>;
return ops;
}();
zx_status_t zxio_vmo_init(zxio_storage_t* storage, zx::vmo vmo, zx::stream stream) {
new (storage) Vmo(std::move(vmo), std::move(stream));
return ZX_OK;
}