| // 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, object_get_property, object_set_property, ok, ObjectQuery, Port, Property, |
| PropertyQuery, Rights, Signals, Status, Time, Topic, WaitAsyncOpts, |
| }; |
| |
| use fuchsia_zircon_sys as sys; |
| use std::ffi::{CStr, CString}; |
| use std::marker::PhantomData; |
| use std::mem::{self, ManuallyDrop}; |
| |
| #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] |
| #[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; |
| type PropTy = [u8; sys::ZX_MAX_NAME_LEN]; |
| } |
| |
| /// 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> { |
| inner: ManuallyDrop<T>, |
| marker: PhantomData<&'a T>, |
| } |
| |
| impl<'a, T> ::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: 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: Time) -> Result<Signals, Status> { |
| let mut pending = Signals::empty().bits(); |
| let status = unsafe { |
| sys::zx_object_wait_one( |
| self.raw_handle(), |
| signals.bits(), |
| deadline.into_nanos(), |
| &mut pending, |
| ) |
| }; |
| ok(status).map(|()| Signals::from_bits_truncate(pending)) |
| } |
| |
| 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) |
| } |
| } |
| |
| /// 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: Time) -> Result<Signals, Status> { |
| 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<CString, Status> { |
| let buf = object_get_property::<NameProperty>(self.as_handle_ref())?; |
| let nul_pos = buf.iter().position(|&x| x == b'\0').ok_or(Status::INTERNAL)?; |
| // We already checked for nul bytes, so simply unwrap. |
| Ok(CString::new(&buf[..nul_pos]).unwrap()) |
| } |
| |
| /// 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: &CStr) -> Result<(), Status> { |
| let bytes = name.to_bytes_with_nul(); |
| if bytes.len() > sys::ZX_MAX_NAME_LEN { |
| return Err(Status::INVALID_ARGS); |
| } |
| |
| let mut buf = [0u8; sys::ZX_MAX_NAME_LEN]; |
| buf[..bytes.len()].copy_from_slice(bytes); |
| object_set_property::<NameProperty>(self.as_handle_ref(), &buf) |
| } |
| |
| /// 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> { |
| let mut info = sys::zx_info_handle_basic_t::default(); |
| object_get_info::<HandleBasicInfoQuery>( |
| self.as_handle_ref(), |
| std::slice::from_mut(&mut info), |
| ) |
| .map(|_| HandleBasicInfo::from(info)) |
| } |
| |
| /// 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> { |
| let mut count = sys::zx_info_handle_count_t::default(); |
| object_get_info::<HandleCountInfoQuery>( |
| self.as_handle_ref(), |
| std::slice::from_mut(&mut count), |
| ) |
| .map(|_| HandleCountInfo::from(count)) |
| } |
| |
| /// 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) |
| } |
| } |
| |
| /// 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; |
| ]); |
| |
| 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, |
| pub reserved: u32, |
| } |
| |
| 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, reserved } = 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), |
| reserved: reserved, |
| } |
| } |
| } |
| |
| // 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. |
| /// Based on zx_handle_disposition_t, but does not match the same layout. |
| #[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] |
| pub struct HandleDisposition<'a> { |
| pub handle_op: HandleOp<'a>, |
| pub object_type: ObjectType, |
| pub rights: Rights, |
| pub result: Status, |
| } |
| |
| impl HandleDisposition<'_> { |
| pub fn into_raw<'a>(self) -> sys::zx_handle_disposition_t { |
| match self.handle_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(), |
| }, |
| } |
| } |
| } |
| |
| /// Information on handles that were read. |
| /// Based on 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, |
| } |
| |
| impl HandleInfo { |
| /// # Safety |
| /// |
| /// See [`Handle::from_raw`] for requirements about the validity and closing |
| /// of `raw.handle`. |
| /// |
| /// `raw.rights` must be a bitwise combination of one or more [`Rights`] |
| /// with no additional bits set. |
| /// |
| /// 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 { |
| handle: Handle::from_raw(raw.handle), |
| object_type: ObjectType(raw.ty), |
| rights: 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" |
| // fuchsia_zircon::Vmar that we need to use. |
| use fuchsia_zircon::{ |
| AsHandleRef, Channel, Handle, HandleBased, HandleDisposition, HandleInfo, HandleOp, |
| ObjectType, Rights, Vmo, |
| }; |
| use fuchsia_zircon_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 = CStr::from_bytes_with_nul(b"v\0").unwrap(); |
| assert!(vmo.set_name(short_name).is_ok()); |
| assert_eq!(vmo.get_name(), Ok(short_name.to_owned())); |
| } |
| |
| #[test] |
| fn set_get_max_len_name() { |
| let vmo = Vmo::create(1).unwrap(); |
| let max_len_name = CStr::from_bytes_with_nul(b"a_great_maximum_length_vmo_name\0").unwrap(); // 32 bytes |
| assert!(vmo.set_name(max_len_name).is_ok()); |
| assert_eq!(vmo.get_name(), Ok(max_len_name.to_owned())); |
| } |
| |
| #[test] |
| fn set_get_too_long_name() { |
| let vmo = Vmo::create(1).unwrap(); |
| let too_long_name = |
| CStr::from_bytes_with_nul(b"bad_really_too_too_long_vmo_name\0").unwrap(); // 33 bytes |
| assert_eq!(vmo.set_name(too_long_name), Err(Status::INVALID_ARGS)); |
| } |
| |
| #[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 { |
| handle_op: HandleOp::Move(unsafe { Handle::from_raw(RAW_HANDLE) }), |
| rights: Rights::EXECUTE, |
| object_type: ObjectType::VMO, |
| result: 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); |
| } |
| } |