blob: 9cc9cf3af60dca4167ced6fa9d8ead458acdd0ea [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.
//! Type-safe bindings for Zircon handles.
//!
use crate::{
object_get_info_single, object_get_property, object_set_property, ok, sys, MonotonicInstant,
Name, ObjectQuery, Port, Property, PropertyQuery, Rights, Signals, Status, Topic,
WaitAsyncOpts,
};
use std::marker::PhantomData;
use std::mem::{self, ManuallyDrop};
#[derive(
Debug,
Copy,
Clone,
Eq,
PartialEq,
Ord,
PartialOrd,
Hash,
zerocopy::FromBytes,
zerocopy::KnownLayout,
zerocopy::Immutable,
)]
#[repr(transparent)]
pub struct Koid(sys::zx_koid_t);
impl Koid {
pub fn from_raw(raw: sys::zx_koid_t) -> Koid {
Koid(raw)
}
pub fn raw_koid(&self) -> sys::zx_koid_t {
self.0
}
}
/// An object representing a Zircon
/// [handle](https://fuchsia.dev/fuchsia-src/concepts/objects/handles).
///
/// Internally, it is represented as a 32-bit integer, but this wrapper enforces
/// strict ownership semantics. The `Drop` implementation closes the handle.
///
/// This type represents the most general reference to a kernel object, and can
/// be interconverted to and from more specific types. Those conversions are not
/// enforced in the type system; attempting to use them will result in errors
/// returned by the kernel. These conversions don't change the underlying
/// representation, but do change the type and thus what operations are available.
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[repr(transparent)]
pub struct Handle(sys::zx_handle_t);
impl AsHandleRef for Handle {
fn as_handle_ref(&self) -> HandleRef<'_> {
Unowned { inner: ManuallyDrop::new(Handle(self.0)), marker: PhantomData }
}
}
impl HandleBased for Handle {}
impl Drop for Handle {
fn drop(&mut self) {
if self.0 != sys::ZX_HANDLE_INVALID {
unsafe { sys::zx_handle_close(self.0) };
}
}
}
impl Handle {
/// Initialize a handle backed by ZX_HANDLE_INVALID, the only safe non-handle.
#[inline(always)]
pub const fn invalid() -> Handle {
Handle(sys::ZX_HANDLE_INVALID)
}
/// If a raw handle is obtained from some other source, this method converts
/// it into a type-safe owned handle.
///
/// # Safety
///
/// `raw` must either be a valid handle (i.e. not dangling), or
/// `ZX_HANDLE_INVALID`. If `raw` is a valid handle, then either:
/// - `raw` may be closed manually and the returned `Handle` must not be
/// dropped.
/// - Or `raw` must not be closed until the returned `Handle` is dropped, at
/// which time it will close `raw`.
pub const unsafe fn from_raw(raw: sys::zx_handle_t) -> Handle {
Handle(raw)
}
pub fn is_invalid(&self) -> bool {
self.0 == sys::ZX_HANDLE_INVALID
}
pub fn replace(self, rights: Rights) -> Result<Handle, Status> {
let handle = self.0;
let mut out = 0;
let status = unsafe { sys::zx_handle_replace(handle, rights.bits(), &mut out) };
// zx_handle_replace always invalidates |handle| so we can't run our drop handler.
std::mem::forget(self);
ok(status).map(|()| Handle(out))
}
}
struct NameProperty();
unsafe impl PropertyQuery for NameProperty {
const PROPERTY: Property = Property::NAME;
// SAFETY: this type is correctly sized and the kernel guarantees that it will be
// null-terminated like the type requires.
type PropTy = Name;
}
/// A borrowed value of type `T`.
///
/// This is primarily used for working with borrowed values of `HandleBased` types.
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[repr(transparent)]
pub struct Unowned<'a, T: Into<Handle>> {
inner: ManuallyDrop<T>,
marker: PhantomData<&'a T>,
}
impl<T: Into<Handle>> Drop for Unowned<'_, T> {
fn drop(&mut self) {
// SAFETY: This is safe because we don't use this ManuallyDrop again.
let handle: Handle = unsafe { ManuallyDrop::take(&mut self.inner).into() };
mem::forget(handle);
}
}
impl<'a, T: Into<Handle>> ::std::ops::Deref for Unowned<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&*self.inner
}
}
impl<T: HandleBased> Clone for Unowned<'_, T> {
fn clone(&self) -> Self {
unsafe { Self::from_raw_handle(self.inner.as_handle_ref().raw_handle()) }
}
}
pub type HandleRef<'a> = Unowned<'a, Handle>;
impl<'a, T: Into<Handle>> Unowned<'a, T> {
/// Returns a new object that borrows the underyling handle. This will work for any type that
/// implements `From<U>` where `U` is handle-like i.e. it implements `AsHandleRef` and
/// `From<Handle>`.
pub fn new<U: AsHandleRef + From<Handle>>(inner: &'a U) -> Self
where
T: From<U>,
{
// SAFETY: This is safe because we are converting from &U to U to allow us to create T, and
// then when we drop, we convert T into a handle that we forget.
Unowned {
inner: ManuallyDrop::new(T::from(U::from(unsafe {
Handle::from_raw(inner.as_handle_ref().raw_handle())
}))),
marker: PhantomData,
}
}
}
impl<'a, T: HandleBased> Unowned<'a, T> {
/// Create a `HandleRef` from a raw handle. Use this method when you are given a raw handle but
/// should not take ownership of it. Examples include process-global handles like the root
/// VMAR. This method should be called with an explicitly provided lifetime that must not
/// outlive the lifetime during which the handle is owned by the current process. It is unsafe
/// because most of the time, it is better to use a `Handle` to prevent leaking resources.
///
/// # Safety
///
/// `handle` must be a valid handle (i.e. not dangling), or
/// `ZX_HANDLE_INVALID`. If `handle` is a valid handle, then it must not be
/// closed for the lifetime `'a`.
pub unsafe fn from_raw_handle(handle: sys::zx_handle_t) -> Self {
Unowned { inner: ManuallyDrop::new(T::from(Handle::from_raw(handle))), marker: PhantomData }
}
pub fn raw_handle(&self) -> sys::zx_handle_t {
self.inner.raw_handle()
}
pub fn duplicate(&self, rights: Rights) -> Result<T, Status> {
let mut out = 0;
let status =
unsafe { sys::zx_handle_duplicate(self.raw_handle(), rights.bits(), &mut out) };
ok(status).map(|()| T::from(Handle(out)))
}
pub fn signal(&self, clear_mask: Signals, set_mask: Signals) -> Result<(), Status> {
let status =
unsafe { sys::zx_object_signal(self.raw_handle(), clear_mask.bits(), set_mask.bits()) };
ok(status)
}
pub fn wait(&self, signals: Signals, deadline: MonotonicInstant) -> WaitResult {
let mut pending = Signals::empty().bits();
let status = unsafe {
sys::zx_object_wait_one(
self.raw_handle(),
signals.bits(),
deadline.into_nanos(),
&mut pending,
)
};
let signals = Signals::from_bits_truncate(pending);
match ok(status) {
Ok(()) => WaitResult::Ok(signals),
Err(Status::TIMED_OUT) => WaitResult::TimedOut(signals),
Err(Status::CANCELED) => WaitResult::Canceled(signals),
Err(e) => WaitResult::Err(e),
}
}
pub fn wait_async(
&self,
port: &Port,
key: u64,
signals: Signals,
options: WaitAsyncOpts,
) -> Result<(), Status> {
let status = unsafe {
sys::zx_object_wait_async(
self.raw_handle(),
port.raw_handle(),
key,
signals.bits(),
options.bits(),
)
};
ok(status)
}
}
/// Result from `HandleRef::wait` and `AsHandleRef::wait_handle`. Conveys the
/// result of the
/// [zx_object_wait_one](https://fuchsia.dev/reference/syscalls/object_wait_one)
/// syscall and the signals that were asserted on the object when the syscall
/// completed.
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum WaitResult {
/// The syscall completed with `ZX_OK` and the provided signals were observed.
Ok(Signals),
/// The syscall completed with `ZX_ERR_TIMED_OUT` and the provided signals
/// were observed. These signals may reflect state changes that occurred
/// after the deadline passed, but before the syscall returned.
TimedOut(Signals),
/// The syscall completed with `ZX_ERR_CANCELED` and the provided signals
/// were observed. The signals will include `ZX_SIGNAL_HANDLE_CLOSED`.
///
/// Note that the state of these signals may be racy and difficult to
/// interpret. Often, the correct behavior in this case is to treat this as
/// an error.
Canceled(Signals),
/// The syscall completed with a status other than `ZX_OK`, `ZX_ERR_TIMED_OUT`,
/// or `ZX_ERR_CANCELED`. No signals are returned in this scenario.
Err(Status),
}
impl WaitResult {
/// Convert this `WaitResult` into a `Result<Signals, Status>`. The signals
/// are discarded in all cases except `WaitResult::Ok`.
pub const fn to_result(self) -> Result<Signals, Status> {
match self {
WaitResult::Ok(signals) => Ok(signals),
WaitResult::TimedOut(_signals) => Err(Status::TIMED_OUT),
WaitResult::Canceled(_signals) => Err(Status::CANCELED),
WaitResult::Err(status) => Err(status),
}
}
// The following definitions are all copied from `std::result::Result`. They
// allow a `WaitResult` to be treated like a `Result` in many circumstance. All
// simply delegate to `to_result()`.
#[must_use = "if you intended to assert that this is ok, consider `.unwrap()` instead"]
#[inline]
pub const fn is_ok(&self) -> bool {
self.to_result().is_ok()
}
#[must_use = "if you intended to assert that this is err, consider `.unwrap_err()` instead"]
#[inline]
pub const fn is_err(&self) -> bool {
self.to_result().is_err()
}
#[inline]
pub fn map<U, F: FnOnce(Signals) -> U>(self, op: F) -> Result<U, Status> {
self.to_result().map(op)
}
#[inline]
pub fn map_err<F, O: FnOnce(Status) -> F>(self, op: O) -> Result<Signals, F> {
self.to_result().map_err(op)
}
#[inline]
#[track_caller]
pub fn expect(self, msg: &str) -> Signals {
self.to_result().expect(msg)
}
#[inline]
#[track_caller]
pub fn expect_err(self, msg: &str) -> Status {
self.to_result().expect_err(msg)
}
#[inline(always)]
#[track_caller]
pub fn unwrap(self) -> Signals {
self.to_result().unwrap()
}
}
impl<'a> Unowned<'a, Handle> {
/// Convert this HandleRef to one of a specific type.
pub fn cast<T: HandleBased>(self) -> Unowned<'a, T> {
// SAFETY: this function's guarantees are upheld by the self input.
unsafe { Unowned::from_raw_handle(self.raw_handle()) }
}
}
/// A trait to get a reference to the underlying handle of an object.
pub trait AsHandleRef {
/// Get a reference to the handle. One important use of such a reference is
/// for `object_wait_many`.
fn as_handle_ref(&self) -> HandleRef<'_>;
/// Interpret the reference as a raw handle (an integer type). Two distinct
/// handles will have different raw values (so it can perhaps be used as a
/// key in a data structure).
fn raw_handle(&self) -> sys::zx_handle_t {
self.as_handle_ref().inner.0
}
/// Set and clear userspace-accessible signal bits on an object. Wraps the
/// [zx_object_signal](https://fuchsia.dev/fuchsia-src/reference/syscalls/object_signal.md)
/// syscall.
fn signal_handle(&self, clear_mask: Signals, set_mask: Signals) -> Result<(), Status> {
self.as_handle_ref().signal(clear_mask, set_mask)
}
/// Waits on a handle. Wraps the
/// [zx_object_wait_one](https://fuchsia.dev/fuchsia-src/reference/syscalls/object_wait_one.md)
/// syscall.
fn wait_handle(&self, signals: Signals, deadline: MonotonicInstant) -> WaitResult {
self.as_handle_ref().wait(signals, deadline)
}
/// Causes packet delivery on the given port when the object changes state and matches signals.
/// [zx_object_wait_async](https://fuchsia.dev/fuchsia-src/reference/syscalls/object_wait_async.md)
/// syscall.
fn wait_async_handle(
&self,
port: &Port,
key: u64,
signals: Signals,
options: WaitAsyncOpts,
) -> Result<(), Status> {
self.as_handle_ref().wait_async(port, key, signals, options)
}
/// Get the [Property::NAME] property for this object.
///
/// Wraps a call to the
/// [zx_object_get_property](https://fuchsia.dev/fuchsia-src/reference/syscalls/object_get_property.md)
/// syscall for the ZX_PROP_NAME property.
fn get_name(&self) -> Result<Name, Status> {
object_get_property::<NameProperty>(self.as_handle_ref())
}
/// Set the [Property::NAME] property for this object.
///
/// The name's length must be less than [sys::ZX_MAX_NAME_LEN], i.e.
/// name.[to_bytes_with_nul()](CStr::to_bytes_with_nul()).len() <= [sys::ZX_MAX_NAME_LEN], or
/// Err([Status::INVALID_ARGS]) will be returned.
///
/// Wraps a call to the
/// [zx_object_get_property](https://fuchsia.dev/fuchsia-src/reference/syscalls/object_get_property.md)
/// syscall for the ZX_PROP_NAME property.
fn set_name(&self, name: &Name) -> Result<(), Status> {
object_set_property::<NameProperty>(self.as_handle_ref(), &name)
}
/// Wraps the
/// [zx_object_get_info](https://fuchsia.dev/fuchsia-src/reference/syscalls/object_get_info.md)
/// syscall for the ZX_INFO_HANDLE_BASIC topic.
fn basic_info(&self) -> Result<HandleBasicInfo, Status> {
Ok(HandleBasicInfo::from(object_get_info_single::<HandleBasicInfoQuery>(
self.as_handle_ref(),
)?))
}
/// Wraps the
/// [zx_object_get_info](https://fuchsia.dev/fuchsia-src/reference/syscalls/object_get_info.md)
/// syscall for the ZX_INFO_HANDLE_COUNT topic.
fn count_info(&self) -> Result<HandleCountInfo, Status> {
Ok(HandleCountInfo::from(object_get_info_single::<HandleCountInfoQuery>(
self.as_handle_ref(),
)?))
}
/// Returns the koid (kernel object ID) for this handle.
fn get_koid(&self) -> Result<Koid, Status> {
self.basic_info().map(|info| info.koid)
}
}
impl<'a, T: HandleBased> AsHandleRef for Unowned<'a, T> {
fn as_handle_ref(&self) -> HandleRef<'_> {
Unowned { inner: ManuallyDrop::new(Handle(self.raw_handle())), marker: PhantomData }
}
}
impl<T: AsHandleRef> AsHandleRef for &T {
fn as_handle_ref(&self) -> HandleRef<'_> {
(*self).as_handle_ref()
}
}
/// A trait implemented by all handle-based types.
///
/// Note: it is reasonable for user-defined objects wrapping a handle to implement
/// this trait. For example, a specific interface in some protocol might be
/// represented as a newtype of `Channel`, and implement the `as_handle_ref`
/// method and the `From<Handle>` trait to facilitate conversion from and to the
/// interface.
pub trait HandleBased: AsHandleRef + From<Handle> + Into<Handle> {
/// Duplicate a handle, possibly reducing the rights available. Wraps the
/// [zx_handle_duplicate](https://fuchsia.dev/fuchsia-src/reference/syscalls/handle_duplicate.md)
/// syscall.
fn duplicate_handle(&self, rights: Rights) -> Result<Self, Status> {
self.as_handle_ref().duplicate(rights).map(|handle| Self::from(handle))
}
/// Create a replacement for a handle, possibly reducing the rights available. This invalidates
/// the original handle. Wraps the
/// [zx_handle_replace](https://fuchsia.dev/fuchsia-src/reference/syscalls/handle_replace.md)
/// syscall.
fn replace_handle(self, rights: Rights) -> Result<Self, Status> {
<Self as Into<Handle>>::into(self).replace(rights).map(|handle| Self::from(handle))
}
/// Converts the value into its inner handle.
///
/// This is a convenience function which simply forwards to the `Into` trait.
fn into_handle(self) -> Handle {
self.into()
}
/// Converts the handle into it's raw representation.
///
/// The caller takes ownership over the raw handle, and must close or transfer it to avoid a handle leak.
fn into_raw(self) -> sys::zx_handle_t {
let h = self.into_handle();
let r = h.0;
mem::forget(h);
r
}
/// Creates an instance of this type from a handle.
///
/// This is a convenience function which simply forwards to the `From` trait.
fn from_handle(handle: Handle) -> Self {
Self::from(handle)
}
/// Creates an instance of another handle-based type from this value's inner handle.
fn into_handle_based<H: HandleBased>(self) -> H {
H::from_handle(self.into_handle())
}
/// Creates an instance of this type from the inner handle of another
/// handle-based type.
fn from_handle_based<H: HandleBased>(h: H) -> Self {
Self::from_handle(h.into_handle())
}
fn is_invalid_handle(&self) -> bool {
self.as_handle_ref().is_invalid()
}
}
/// A trait implemented by all handles for objects which have a peer.
pub trait Peered: HandleBased {
/// Set and clear userspace-accessible signal bits on the object's peer. Wraps the
/// [zx_object_signal_peer][osp] syscall.
///
/// [osp]: https://fuchsia.dev/fuchsia-src/reference/syscalls/object_signal_peer.md
fn signal_peer(&self, clear_mask: Signals, set_mask: Signals) -> Result<(), Status> {
let handle = self.raw_handle();
let status =
unsafe { sys::zx_object_signal_peer(handle, clear_mask.bits(), set_mask.bits()) };
ok(status)
}
/// Returns true if the handle has received the `PEER_CLOSED` signal.
///
/// # Errors
///
/// See https://fuchsia.dev/reference/syscalls/object_wait_one?hl=en#errors for a full list of
/// errors. Note that `Status::TIMED_OUT` errors are converted to `Ok(false)` and all other
/// errors are propagated.
fn is_closed(&self) -> Result<bool, Status> {
match self.wait_handle(Signals::OBJECT_PEER_CLOSED, MonotonicInstant::INFINITE_PAST) {
WaitResult::Ok(signals) => Ok(signals.contains(Signals::OBJECT_PEER_CLOSED)),
WaitResult::TimedOut(_) => Ok(false),
WaitResult::Canceled(_) => Err(Status::CANCELED),
WaitResult::Err(e) => Err(e),
}
}
}
/// Zircon object types.
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
#[repr(transparent)]
pub struct ObjectType(sys::zx_obj_type_t);
assoc_values!(ObjectType, [
NONE = sys::ZX_OBJ_TYPE_NONE;
PROCESS = sys::ZX_OBJ_TYPE_PROCESS;
THREAD = sys::ZX_OBJ_TYPE_THREAD;
VMO = sys::ZX_OBJ_TYPE_VMO;
CHANNEL = sys::ZX_OBJ_TYPE_CHANNEL;
EVENT = sys::ZX_OBJ_TYPE_EVENT;
PORT = sys::ZX_OBJ_TYPE_PORT;
INTERRUPT = sys::ZX_OBJ_TYPE_INTERRUPT;
PCI_DEVICE = sys::ZX_OBJ_TYPE_PCI_DEVICE;
DEBUGLOG = sys::ZX_OBJ_TYPE_DEBUGLOG;
SOCKET = sys::ZX_OBJ_TYPE_SOCKET;
RESOURCE = sys::ZX_OBJ_TYPE_RESOURCE;
EVENTPAIR = sys::ZX_OBJ_TYPE_EVENTPAIR;
JOB = sys::ZX_OBJ_TYPE_JOB;
VMAR = sys::ZX_OBJ_TYPE_VMAR;
FIFO = sys::ZX_OBJ_TYPE_FIFO;
GUEST = sys::ZX_OBJ_TYPE_GUEST;
VCPU = sys::ZX_OBJ_TYPE_VCPU;
TIMER = sys::ZX_OBJ_TYPE_TIMER;
IOMMU = sys::ZX_OBJ_TYPE_IOMMU;
BTI = sys::ZX_OBJ_TYPE_BTI;
PROFILE = sys::ZX_OBJ_TYPE_PROFILE;
PMT = sys::ZX_OBJ_TYPE_PMT;
SUSPEND_TOKEN = sys::ZX_OBJ_TYPE_SUSPEND_TOKEN;
PAGER = sys::ZX_OBJ_TYPE_PAGER;
EXCEPTION = sys::ZX_OBJ_TYPE_EXCEPTION;
CLOCK = sys::ZX_OBJ_TYPE_CLOCK;
STREAM = sys::ZX_OBJ_TYPE_STREAM;
MSI = sys::ZX_OBJ_TYPE_MSI;
IOB = sys::ZX_OBJ_TYPE_IOB;
COUNTER = sys::ZX_OBJ_TYPE_COUNTER;
]);
impl ObjectType {
/// Creates an `ObjectType` from the underlying zircon type.
pub const fn from_raw(raw: sys::zx_obj_type_t) -> Self {
Self(raw)
}
/// Converts `ObjectType` into the underlying zircon type.
pub const fn into_raw(self) -> sys::zx_obj_type_t {
self.0
}
}
/// Basic information about a handle.
///
/// Wrapper for data returned from [Handle::basic_info()].
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct HandleBasicInfo {
pub koid: Koid,
pub rights: Rights,
pub object_type: ObjectType,
pub related_koid: Koid,
}
impl Default for HandleBasicInfo {
fn default() -> Self {
Self::from(sys::zx_info_handle_basic_t::default())
}
}
impl From<sys::zx_info_handle_basic_t> for HandleBasicInfo {
fn from(info: sys::zx_info_handle_basic_t) -> Self {
let sys::zx_info_handle_basic_t { koid, rights, type_, related_koid, .. } = info;
// Note lossy conversion of Rights and HandleProperty here if either of those types are out
// of date or incomplete.
HandleBasicInfo {
koid: Koid(koid),
rights: Rights::from_bits_truncate(rights),
object_type: ObjectType(type_),
related_koid: Koid(related_koid),
}
}
}
// zx_info_handle_basic_t is able to be safely replaced with a byte representation and is a PoD
// type.
struct HandleBasicInfoQuery;
unsafe impl ObjectQuery for HandleBasicInfoQuery {
const TOPIC: Topic = Topic::HANDLE_BASIC;
type InfoTy = sys::zx_info_handle_basic_t;
}
sys::zx_info_handle_count_t!(HandleCountInfo);
impl From<sys::zx_info_handle_count_t> for HandleCountInfo {
fn from(sys::zx_info_handle_count_t { handle_count }: sys::zx_info_handle_count_t) -> Self {
HandleCountInfo { handle_count }
}
}
// zx_info_handle_count_t is able to be safely replaced with a byte representation and is a PoD
// type.
struct HandleCountInfoQuery;
unsafe impl ObjectQuery for HandleCountInfoQuery {
const TOPIC: Topic = Topic::HANDLE_COUNT;
type InfoTy = sys::zx_info_handle_count_t;
}
/// Handle operation.
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum HandleOp<'a> {
Move(Handle),
Duplicate(HandleRef<'a>),
}
/// Operation to perform on handles during write. ABI-compatible with `zx_handle_disposition_t`.
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[repr(C)]
pub struct HandleDisposition<'a> {
// Must be either ZX_HANDLE_OP_MOVE or ZX_HANDLE_OP_DUPLICATE.
operation: sys::zx_handle_op_t,
// ZX_HANDLE_OP_MOVE==owned, ZX_HANDLE_OP_DUPLICATE==borrowed.
handle: sys::zx_handle_t,
// Preserve a borrowed handle's lifetime. Does not occupy any layout.
_handle_lifetime: std::marker::PhantomData<&'a ()>,
pub object_type: ObjectType,
pub rights: Rights,
pub result: Status,
}
static_assertions::assert_eq_size!(HandleDisposition<'_>, sys::zx_handle_disposition_t);
static_assertions::const_assert_eq!(
std::mem::offset_of!(HandleDisposition<'_>, operation),
std::mem::offset_of!(sys::zx_handle_disposition_t, operation)
);
static_assertions::const_assert_eq!(
std::mem::offset_of!(HandleDisposition<'_>, handle),
std::mem::offset_of!(sys::zx_handle_disposition_t, handle)
);
static_assertions::const_assert_eq!(
std::mem::offset_of!(HandleDisposition<'_>, object_type),
std::mem::offset_of!(sys::zx_handle_disposition_t, type_)
);
static_assertions::const_assert_eq!(
std::mem::offset_of!(HandleDisposition<'_>, rights),
std::mem::offset_of!(sys::zx_handle_disposition_t, rights)
);
static_assertions::const_assert_eq!(
std::mem::offset_of!(HandleDisposition<'_>, result),
std::mem::offset_of!(sys::zx_handle_disposition_t, result)
);
impl<'a> HandleDisposition<'a> {
#[inline]
pub fn new(
handle_op: HandleOp<'a>,
object_type: ObjectType,
rights: Rights,
status: Status,
) -> Self {
let (operation, handle) = match handle_op {
HandleOp::Move(h) => (sys::ZX_HANDLE_OP_MOVE, h.into_raw()),
HandleOp::Duplicate(h) => (sys::ZX_HANDLE_OP_DUPLICATE, h.raw_handle()),
};
Self {
operation,
handle,
_handle_lifetime: std::marker::PhantomData,
object_type,
rights: rights,
result: status,
}
}
pub fn raw_handle(&self) -> sys::zx_handle_t {
self.handle
}
pub fn is_move(&self) -> bool {
self.operation == sys::ZX_HANDLE_OP_MOVE
}
pub fn is_duplicate(&self) -> bool {
self.operation == sys::ZX_HANDLE_OP_DUPLICATE
}
pub fn take_op(&mut self) -> HandleOp<'a> {
match self.operation {
sys::ZX_HANDLE_OP_MOVE => {
// SAFETY: this is guaranteed to be a valid handle number by a combination of this
// type's public API and the kernel's guarantees.
HandleOp::Move(unsafe {
Handle::from_raw(std::mem::replace(&mut self.handle, sys::ZX_HANDLE_INVALID))
})
}
sys::ZX_HANDLE_OP_DUPLICATE => {
// SAFETY: this is guaranteed to be a valid handle number by a combination of this
// type's public API and the kernel's guarantees.
HandleOp::Duplicate(unsafe { Unowned::from_raw_handle(self.handle) })
}
_ => unreachable!(),
}
}
pub fn into_raw(mut self) -> sys::zx_handle_disposition_t {
match self.take_op() {
HandleOp::Move(mut handle) => sys::zx_handle_disposition_t {
operation: sys::ZX_HANDLE_OP_MOVE,
handle: std::mem::replace(&mut handle, Handle::invalid()).into_raw(),
type_: self.object_type.0,
rights: self.rights.bits(),
result: self.result.into_raw(),
},
HandleOp::Duplicate(handle_ref) => sys::zx_handle_disposition_t {
operation: sys::ZX_HANDLE_OP_DUPLICATE,
handle: handle_ref.raw_handle(),
type_: self.object_type.0,
rights: self.rights.bits(),
result: self.result.into_raw(),
},
}
}
}
impl<'a> Drop for HandleDisposition<'a> {
fn drop(&mut self) {
// Ensure we clean up owned handle variants.
if self.operation == sys::ZX_HANDLE_OP_MOVE {
unsafe { drop(Handle::from_raw(self.handle)) };
}
}
}
/// Information on handles that were read.
///
/// ABI-compatible with zx_handle_info_t.
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[repr(C)]
pub struct HandleInfo {
pub handle: Handle,
pub object_type: ObjectType,
pub rights: Rights,
// Necessary for ABI compatibility with zx_handle_info_t.
pub(crate) _unused: u32,
}
static_assertions::assert_eq_size!(HandleInfo, sys::zx_handle_info_t);
static_assertions::const_assert_eq!(
std::mem::offset_of!(HandleInfo, handle),
std::mem::offset_of!(sys::zx_handle_info_t, handle)
);
static_assertions::const_assert_eq!(
std::mem::offset_of!(HandleInfo, object_type),
std::mem::offset_of!(sys::zx_handle_info_t, ty)
);
static_assertions::const_assert_eq!(
std::mem::offset_of!(HandleInfo, rights),
std::mem::offset_of!(sys::zx_handle_info_t, rights)
);
static_assertions::const_assert_eq!(
std::mem::offset_of!(HandleInfo, _unused),
std::mem::offset_of!(sys::zx_handle_info_t, unused)
);
impl HandleInfo {
/// Make a new `HandleInfo`.
pub const fn new(handle: Handle, object_type: ObjectType, rights: Rights) -> Self {
Self { handle, object_type, rights, _unused: 0 }
}
/// # Safety
///
/// See [`Handle::from_raw`] for requirements about the validity and closing
/// of `raw.handle`.
///
/// Note that while `raw.ty` _should_ correspond to the type of the handle,
/// that this is not required for safety.
pub const unsafe fn from_raw(raw: sys::zx_handle_info_t) -> HandleInfo {
HandleInfo::new(
// SAFETY: invariants to not double-close are upheld by the caller.
unsafe { Handle::from_raw(raw.handle) },
ObjectType(raw.ty),
Rights::from_bits_retain(raw.rights),
)
}
}
#[cfg(test)]
mod tests {
use super::*;
// The unit tests are built with a different crate name, but fuchsia_runtime returns a "real"
// zx::Vmar that we need to use.
use zx::{
AsHandleRef, Channel, Handle, HandleBased, HandleDisposition, HandleInfo, HandleOp, Name,
ObjectType, Rights, Vmo,
};
use zx_sys as sys;
#[test]
fn into_raw() {
let vmo = Vmo::create(1).unwrap();
let h = vmo.into_raw();
let vmo2 = Vmo::from(unsafe { Handle::from_raw(h) });
assert!(vmo2.write(b"1", 0).is_ok());
}
/// Test duplication by means of a VMO
#[test]
fn duplicate() {
let hello_length: usize = 5;
// Create a VMO and write some data to it.
let vmo = Vmo::create(hello_length as u64).unwrap();
assert!(vmo.write(b"hello", 0).is_ok());
// Replace, reducing rights to read.
let readonly_vmo = vmo.duplicate_handle(Rights::READ).unwrap();
// Make sure we can read but not write.
let mut read_vec = vec![0; hello_length];
assert!(readonly_vmo.read(&mut read_vec, 0).is_ok());
assert_eq!(read_vec, b"hello");
assert_eq!(readonly_vmo.write(b"", 0), Err(Status::ACCESS_DENIED));
// Write new data to the original handle, and read it from the new handle
assert!(vmo.write(b"bye", 0).is_ok());
assert!(readonly_vmo.read(&mut read_vec, 0).is_ok());
assert_eq!(read_vec, b"byelo");
}
// Test replace by means of a VMO
#[test]
fn replace() {
let hello_length: usize = 5;
// Create a VMO and write some data to it.
let vmo = Vmo::create(hello_length as u64).unwrap();
assert!(vmo.write(b"hello", 0).is_ok());
// Replace, reducing rights to read.
let readonly_vmo = vmo.replace_handle(Rights::READ).unwrap();
// Make sure we can read but not write.
let mut read_vec = vec![0; hello_length];
assert!(readonly_vmo.read(&mut read_vec, 0).is_ok());
assert_eq!(read_vec, b"hello");
assert_eq!(readonly_vmo.write(b"", 0), Err(Status::ACCESS_DENIED));
}
#[test]
fn set_get_name() {
// We need some concrete object to exercise the AsHandleRef<'_> set/get_name functions.
let vmo = Vmo::create(1).unwrap();
let short_name = Name::new("v").unwrap();
assert!(vmo.set_name(&short_name).is_ok());
assert_eq!(vmo.get_name().unwrap(), short_name);
}
#[test]
fn set_get_max_len_name() {
let vmo = Vmo::create(1).unwrap();
let max_len_name = Name::new("a_great_maximum_length_vmo_name").unwrap(); // 31 bytes
assert!(vmo.set_name(&max_len_name).is_ok());
assert_eq!(vmo.get_name().unwrap(), max_len_name);
}
#[test]
fn basic_info_channel() {
let (side1, side2) = Channel::create();
let info1 = side1.basic_info().expect("side1 basic_info failed");
let info2 = side2.basic_info().expect("side2 basic_info failed");
assert_eq!(info1.koid, info2.related_koid);
assert_eq!(info2.koid, info1.related_koid);
for info in &[info1, info2] {
assert!(info.koid.raw_koid() >= sys::ZX_KOID_FIRST);
assert_eq!(info.object_type, ObjectType::CHANNEL);
assert!(info.rights.contains(Rights::READ | Rights::WRITE | Rights::WAIT));
}
let side1_repl = side1.replace_handle(Rights::READ).expect("side1 replace_handle failed");
let info1_repl = side1_repl.basic_info().expect("side1_repl basic_info failed");
assert_eq!(info1_repl.koid, info1.koid);
assert_eq!(info1_repl.rights, Rights::READ);
}
#[test]
fn basic_info_vmar() {
// VMARs aren't waitable.
let root_vmar = fuchsia_runtime::vmar_root_self();
let info = root_vmar.basic_info().expect("vmar basic_info failed");
assert_eq!(info.object_type, ObjectType::VMAR);
assert!(!info.rights.contains(Rights::WAIT));
}
#[test]
fn count_info() {
let vmo0 = Vmo::create(1).unwrap();
let count_info = vmo0.count_info().expect("vmo0 count_info failed");
assert_eq!(count_info.handle_count, 1);
let vmo1 = vmo0.duplicate_handle(Rights::SAME_RIGHTS).expect("vmo duplicate_handle failed");
let count_info = vmo1.count_info().expect("vmo1 count_info failed");
assert_eq!(count_info.handle_count, 2);
}
#[test]
fn raw_handle_disposition() {
const RAW_HANDLE: sys::zx_handle_t = 1;
let hd = HandleDisposition::new(
HandleOp::Move(unsafe { Handle::from_raw(RAW_HANDLE) }),
ObjectType::VMO,
Rights::EXECUTE,
Status::OK,
);
let raw_hd = hd.into_raw();
assert_eq!(raw_hd.operation, sys::ZX_HANDLE_OP_MOVE);
assert_eq!(raw_hd.handle, RAW_HANDLE);
assert_eq!(raw_hd.rights, sys::ZX_RIGHT_EXECUTE);
assert_eq!(raw_hd.type_, sys::ZX_OBJ_TYPE_VMO);
assert_eq!(raw_hd.result, sys::ZX_OK);
}
#[test]
fn handle_info_from_raw() {
const RAW_HANDLE: sys::zx_handle_t = 1;
let raw_hi = sys::zx_handle_info_t {
handle: RAW_HANDLE,
ty: sys::ZX_OBJ_TYPE_VMO,
rights: sys::ZX_RIGHT_EXECUTE,
unused: 128,
};
let hi = unsafe { HandleInfo::from_raw(raw_hi) };
assert_eq!(hi.handle.into_raw(), RAW_HANDLE);
assert_eq!(hi.object_type, ObjectType::VMO);
assert_eq!(hi.rights, Rights::EXECUTE);
}
#[test]
fn basic_peer_closed() {
let (lhs, rhs) = crate::EventPair::create();
assert!(!lhs.is_closed().unwrap());
assert!(!rhs.is_closed().unwrap());
drop(rhs);
assert!(lhs.is_closed().unwrap());
}
}