| // Copyright 2017 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 channel objects. |
| |
| use crate::ok; |
| use crate::{ |
| size_to_u32_sat, usize_into_u32, AsHandleRef, Handle, HandleBased, HandleDisposition, |
| HandleInfo, HandleOp, HandleRef, ObjectType, Peered, Rights, Status, Time, |
| }; |
| use fuchsia_zircon_sys as sys; |
| use std::mem::{self, MaybeUninit}; |
| |
| impl HandleDisposition<'_> { |
| const fn invalid<'a>() -> HandleDisposition<'a> { |
| HandleDisposition { |
| handle_op: HandleOp::Move(Handle::invalid()), |
| object_type: ObjectType::NONE, |
| rights: Rights::NONE, |
| result: Status::OK, |
| } |
| } |
| } |
| |
| /// An object representing a Zircon |
| /// [channel](https://fuchsia.dev/fuchsia-src/concepts/objects/channel.md). |
| /// |
| /// As essentially a subtype of `Handle`, it can be freely interconverted. |
| #[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] |
| #[repr(transparent)] |
| pub struct Channel(Handle); |
| impl_handle_based!(Channel); |
| impl Peered for Channel {} |
| |
| impl Channel { |
| /// Create a channel, resulting in a pair of `Channel` objects representing both |
| /// sides of the channel. Messages written into one may be read from the opposite. |
| /// |
| /// Wraps the |
| /// [zx_channel_create](https://fuchsia.dev/fuchsia-src/reference/syscalls/channel_create.md) |
| /// syscall. |
| /// |
| /// # Panics |
| /// |
| /// If the process' job policy denies channel creation or the kernel reports no memory |
| /// available to create a new channel. |
| pub fn create() -> (Self, Self) { |
| unsafe { |
| let mut handle0 = 0; |
| let mut handle1 = 0; |
| let opts = 0; |
| ok(sys::zx_channel_create(opts, &mut handle0, &mut handle1)).expect( |
| "channel creation always succeeds except with OOM or when job policy denies it", |
| ); |
| (Self(Handle::from_raw(handle0)), Self(Handle::from_raw(handle1))) |
| } |
| } |
| |
| /// Read a message from a channel. Wraps the |
| /// [zx_channel_read](https://fuchsia.dev/fuchsia-src/reference/syscalls/channel_read.md) |
| /// syscall. |
| /// |
| /// If the slice lacks the capacity to hold the pending message, |
| /// returns an `Err` with the number of bytes and number of handles needed. |
| /// Otherwise returns an `Ok` with the result as usual. |
| /// If both the outer and inner `Result`s are `Ok`, then the caller can |
| /// assume that the `handles` array is initialized. |
| /// |
| /// Note that `read_slice` may call `read_raw` with some uninitialized |
| /// elements because it resizes the input vector to its capacity |
| /// without initializing all of the elements. |
| pub fn read_raw( |
| &self, |
| bytes: &mut [u8], |
| handles: &mut [MaybeUninit<Handle>], |
| ) -> Result<(Result<(), Status>, usize, usize), (usize, usize)> { |
| let opts = 0; |
| unsafe { |
| let raw_handle = self.raw_handle(); |
| let mut actual_bytes = 0; |
| let mut actual_handles = 0; |
| let status = ok(sys::zx_channel_read( |
| raw_handle, |
| opts, |
| bytes.as_mut_ptr(), |
| handles.as_mut_ptr() as *mut _, |
| bytes.len() as u32, |
| handles.len() as u32, |
| &mut actual_bytes, |
| &mut actual_handles, |
| )); |
| if status == Err(Status::BUFFER_TOO_SMALL) { |
| Err((actual_bytes as usize, actual_handles as usize)) |
| } else { |
| Ok((status, actual_bytes as usize, actual_handles as usize)) |
| } |
| } |
| } |
| |
| /// Read a message from a channel. |
| /// |
| /// Note that this method can cause internal reallocations in the `MessageBuf` |
| /// if it is lacks capacity to hold the full message. If such reallocations |
| /// are not desirable, use `read_raw` instead. |
| pub fn read(&self, buf: &mut MessageBuf) -> Result<(), Status> { |
| let (bytes, handles) = buf.split_mut(); |
| self.read_split(bytes, handles) |
| } |
| |
| /// Read a message from a channel into a separate byte vector and handle vector. |
| /// |
| /// Note that this method can cause internal reallocations in the `Vec`s |
| /// if they lacks capacity to hold the full message. If such reallocations |
| /// are not desirable, use `read_raw` instead. |
| pub fn read_split(&self, bytes: &mut Vec<u8>, handles: &mut Vec<Handle>) -> Result<(), Status> { |
| loop { |
| unsafe { |
| bytes.set_len(bytes.capacity()); |
| handles.set_len(handles.capacity()); |
| } |
| let handle_slice: &mut [Handle] = handles; |
| match self.read_raw(bytes, unsafe { mem::transmute(handle_slice) }) { |
| Ok((result, num_bytes, num_handles)) => { |
| unsafe { |
| bytes.set_len(num_bytes); |
| handles.set_len(num_handles); |
| } |
| return result; |
| } |
| Err((num_bytes, num_handles)) => { |
| ensure_capacity(bytes, num_bytes); |
| ensure_capacity(handles, num_handles); |
| } |
| } |
| } |
| } |
| |
| /// Read a message from a channel. |
| /// Wraps the [zx_channel_read_etc](https://fuchsia.dev/fuchsia-src/reference/syscalls/channel_read_etc.md) |
| /// syscall. |
| /// |
| /// This differs from `read_raw` in that it returns extended information on |
| /// the handles. |
| /// |
| /// If the slice lacks the capacity to hold the pending message, |
| /// returns an `Err` with the number of bytes and number of handles needed. |
| /// Otherwise returns an `Ok` with the result as usual. |
| /// If both the outer and inner `Result`s are `Ok`, then the caller can |
| /// assume that the `handle_infos` array is initialized. |
| /// |
| /// Note that `read_etc_slice` may call `read_etc_raw` with some |
| /// uninitialized elements because it resizes the input vector to its |
| /// capacity without initializing all of the elements. |
| pub fn read_etc_raw( |
| &self, |
| bytes: &mut [u8], |
| handle_infos: &mut [MaybeUninit<HandleInfo>], |
| ) -> Result<(Result<(), Status>, usize, usize), (usize, usize)> { |
| let opts = 0; |
| unsafe { |
| let raw_handle = self.raw_handle(); |
| let mut zx_handle_infos: [MaybeUninit<sys::zx_handle_info_t>; |
| sys::ZX_CHANNEL_MAX_MSG_HANDLES as usize] = MaybeUninit::uninit().assume_init(); |
| let mut actual_bytes = 0; |
| let mut actual_handle_infos = 0; |
| let status = ok(sys::zx_channel_read_etc( |
| raw_handle, |
| opts, |
| bytes.as_mut_ptr(), |
| zx_handle_infos.as_mut_ptr() as *mut sys::zx_handle_info_t, |
| bytes.len() as u32, |
| handle_infos.len() as u32, |
| &mut actual_bytes, |
| &mut actual_handle_infos, |
| )); |
| if status == Err(Status::BUFFER_TOO_SMALL) { |
| Err((actual_bytes as usize, actual_handle_infos as usize)) |
| } else { |
| Ok(( |
| status.map(|()| { |
| for i in 0..actual_handle_infos as usize { |
| std::mem::swap( |
| &mut handle_infos[i], |
| &mut MaybeUninit::new(HandleInfo::from_raw( |
| zx_handle_infos[i].assume_init(), |
| )), |
| ); |
| } |
| }), |
| actual_bytes as usize, |
| actual_handle_infos as usize, |
| )) |
| } |
| } |
| } |
| |
| /// Read a message from a channel. |
| /// |
| /// This differs from `read` in that it returns extended information on |
| /// the handles. |
| /// |
| /// Note that this method can cause internal reallocations in the `MessageBufEtc` |
| /// if it is lacks capacity to hold the full message. If such reallocations |
| /// are not desirable, use `read_etc_raw` instead. |
| pub fn read_etc(&self, buf: &mut MessageBufEtc) -> Result<(), Status> { |
| let (bytes, handles) = buf.split_mut(); |
| self.read_etc_split(bytes, handles) |
| } |
| |
| /// Read a message from a channel into a separate byte vector and handle vector. |
| /// |
| /// This differs from `read_split` in that it returns extended information on |
| /// the handles. |
| /// |
| /// Note that this method can cause internal reallocations in the `Vec`s |
| /// if they lacks capacity to hold the full message. If such reallocations |
| /// are not desirable, use `read_raw` instead. |
| pub fn read_etc_split( |
| &self, |
| bytes: &mut Vec<u8>, |
| handle_infos: &mut Vec<HandleInfo>, |
| ) -> Result<(), Status> { |
| loop { |
| unsafe { |
| bytes.set_len(bytes.capacity()); |
| handle_infos.set_len(handle_infos.capacity()); |
| } |
| let handle_info_slice: &mut [HandleInfo] = handle_infos; |
| match self.read_etc_raw(bytes, unsafe { std::mem::transmute(handle_info_slice) }) { |
| Ok((result, num_bytes, num_handle_infos)) => { |
| unsafe { |
| bytes.set_len(num_bytes); |
| handle_infos.set_len(num_handle_infos); |
| } |
| return result; |
| } |
| Err((num_bytes, num_handle_infos)) => { |
| ensure_capacity(bytes, num_bytes); |
| ensure_capacity(handle_infos, num_handle_infos); |
| } |
| } |
| } |
| } |
| |
| /// Write a message to a channel. Wraps the |
| /// [zx_channel_write](https://fuchsia.dev/fuchsia-src/reference/syscalls/channel_write.md) |
| /// syscall. |
| pub fn write(&self, bytes: &[u8], handles: &mut [Handle]) -> Result<(), Status> { |
| let opts = 0; |
| let n_bytes = usize_into_u32(bytes.len()).map_err(|_| Status::OUT_OF_RANGE)?; |
| let n_handles = usize_into_u32(handles.len()).map_err(|_| Status::OUT_OF_RANGE)?; |
| unsafe { |
| let status = sys::zx_channel_write( |
| self.raw_handle(), |
| opts, |
| bytes.as_ptr(), |
| n_bytes, |
| handles.as_ptr() as *const sys::zx_handle_t, |
| n_handles, |
| ); |
| // Handles are consumed by zx_channel_write so prevent the destructor from being called. |
| for handle in handles { |
| std::mem::forget(std::mem::replace(handle, Handle::invalid())); |
| } |
| ok(status)?; |
| Ok(()) |
| } |
| } |
| |
| /// Write a message to a channel. Wraps the |
| /// [zx_channel_write_etc](https://fuchsia.dev/fuchsia-src/reference/syscalls/channel_write_etc.md) |
| /// syscall. |
| pub fn write_etc( |
| &self, |
| bytes: &[u8], |
| handle_dispositions: &mut [HandleDisposition<'_>], |
| ) -> Result<(), Status> { |
| let opts = 0; |
| let n_bytes = usize_into_u32(bytes.len()).map_err(|_| Status::OUT_OF_RANGE)?; |
| let n_handle_dispositions = |
| usize_into_u32(handle_dispositions.len()).map_err(|_| Status::OUT_OF_RANGE)?; |
| if n_handle_dispositions > sys::ZX_CHANNEL_MAX_MSG_HANDLES { |
| // don't let the kernel check this bound for us because we have a fixed size array below |
| return Err(Status::OUT_OF_RANGE); |
| } |
| unsafe { |
| let mut zx_handle_dispositions: [std::mem::MaybeUninit<sys::zx_handle_disposition_t>; |
| sys::ZX_CHANNEL_MAX_MSG_HANDLES as usize] = |
| std::mem::MaybeUninit::uninit().assume_init(); |
| for i in 0..n_handle_dispositions as usize { |
| let handle_disposition = |
| std::mem::replace(&mut handle_dispositions[i], HandleDisposition::invalid()); |
| zx_handle_dispositions[i] = |
| std::mem::MaybeUninit::new(handle_disposition.into_raw()); |
| } |
| let status = sys::zx_channel_write_etc( |
| self.raw_handle(), |
| opts, |
| bytes.as_ptr(), |
| n_bytes, |
| zx_handle_dispositions.as_mut_ptr() as *mut sys::zx_handle_disposition_t, |
| n_handle_dispositions, |
| ); |
| ok(status)?; |
| Ok(()) |
| } |
| } |
| |
| /// Send a message consisting of the given bytes and handles to a channel and await a reply. |
| /// |
| /// The first four bytes of the written and read back messages are treated as a transaction ID |
| /// of type `zx_txid_t`. The kernel generates a txid for the written message, replacing that |
| /// part of the message as read from userspace. In other words, the first four bytes of |
| /// `bytes` will be ignored, and the first four bytes of the response will contain a |
| /// kernel-generated txid. |
| /// |
| /// Wraps the |
| /// [zx_channel_call](https://fuchsia.dev/fuchsia-src/reference/syscalls/channel_call.md) |
| /// syscall. |
| /// |
| /// Note that unlike [`read`][read], the caller must ensure that the MessageBuf has enough |
| /// capacity for the bytes and handles which will be received, as replies which are too large |
| /// are discarded. |
| /// |
| /// On failure returns the both the main and read status. |
| /// |
| /// [read]: struct.Channel.html#method.read |
| pub fn call( |
| &self, |
| timeout: Time, |
| bytes: &[u8], |
| handles: &mut [Handle], |
| buf: &mut MessageBuf, |
| ) -> Result<(), Status> { |
| let write_num_bytes = usize_into_u32(bytes.len()).map_err(|_| Status::OUT_OF_RANGE)?; |
| let write_num_handles = usize_into_u32(handles.len()).map_err(|_| Status::OUT_OF_RANGE)?; |
| buf.clear(); |
| let read_num_bytes: u32 = size_to_u32_sat(buf.bytes.capacity()); |
| let read_num_handles: u32 = size_to_u32_sat(buf.handles.capacity()); |
| let args = sys::zx_channel_call_args_t { |
| wr_bytes: bytes.as_ptr(), |
| wr_handles: handles.as_ptr() as *const sys::zx_handle_t, |
| rd_bytes: buf.bytes.as_mut_ptr(), |
| rd_handles: buf.handles.as_mut_ptr() as *mut _, |
| wr_num_bytes: write_num_bytes, |
| wr_num_handles: write_num_handles, |
| rd_num_bytes: read_num_bytes, |
| rd_num_handles: read_num_handles, |
| }; |
| let mut actual_read_bytes: u32 = 0; |
| let mut actual_read_handles: u32 = 0; |
| let options = 0; |
| let status = unsafe { |
| Status::from_raw(sys::zx_channel_call( |
| self.raw_handle(), |
| options, |
| timeout.into_nanos(), |
| &args, |
| &mut actual_read_bytes, |
| &mut actual_read_handles, |
| )) |
| }; |
| unsafe { |
| // Outgoing handles are consumed by zx_channel_call so prevent the destructor from being called. |
| for handle in handles { |
| std::mem::forget(std::mem::replace(handle, Handle::invalid())); |
| } |
| buf.bytes.set_len(actual_read_bytes as usize); |
| buf.handles.set_len(actual_read_handles as usize); |
| } |
| if Status::OK == status { |
| Ok(()) |
| } else { |
| Err(status) |
| } |
| } |
| |
| /// Send a message consisting of the given bytes and handles to a channel and await a reply. |
| /// |
| /// The first four bytes of the written and read back messages are treated as a transaction ID |
| /// of type `zx_txid_t`. The kernel generates a txid for the written message, replacing that |
| /// part of the message as read from userspace. In other words, the first four bytes of |
| /// `bytes` will be ignored, and the first four bytes of the response will contain a |
| /// kernel-generated txid. |
| /// |
| /// This differs from `call`, in that it uses extended handle info. |
| /// |
| /// Wraps the |
| /// [zx_channel_call_etc](https://fuchsia.dev/fuchsia-src/reference/syscalls/channel_call_etc.md) |
| /// syscall. |
| /// |
| /// Note that unlike [`read_etc`][read_etc], the caller must ensure that the MessageBufEtc |
| /// has enough capacity for the bytes and handles which will be received, as replies which are |
| /// too large are discarded. |
| /// |
| /// On failure returns the both the main and read status. |
| /// |
| /// [read_etc]: struct.Channel.html#method.read_etc |
| pub fn call_etc( |
| &self, |
| timeout: Time, |
| bytes: &[u8], |
| handle_dispositions: &mut [HandleDisposition<'_>], |
| buf: &mut MessageBufEtc, |
| ) -> Result<(), Status> { |
| let write_num_bytes = usize_into_u32(bytes.len()).map_err(|_| Status::OUT_OF_RANGE)?; |
| let write_num_handle_dispositions = |
| usize_into_u32(handle_dispositions.len()).map_err(|_| Status::OUT_OF_RANGE)?; |
| if write_num_handle_dispositions > sys::ZX_CHANNEL_MAX_MSG_HANDLES { |
| // don't let the kernel check this bound for us because we have a fixed size array below |
| return Err(Status::OUT_OF_RANGE); |
| } |
| let mut zx_handle_dispositions: [std::mem::MaybeUninit<sys::zx_handle_disposition_t>; |
| sys::ZX_CHANNEL_MAX_MSG_HANDLES as usize] = |
| [std::mem::MaybeUninit::uninit(); sys::ZX_CHANNEL_MAX_MSG_HANDLES as usize]; |
| for i in 0..write_num_handle_dispositions as usize { |
| let handle_disposition = |
| std::mem::replace(&mut handle_dispositions[i], HandleDisposition::invalid()); |
| zx_handle_dispositions[i].write(handle_disposition.into_raw()); |
| } |
| buf.clear(); |
| let read_num_bytes: u32 = size_to_u32_sat(buf.bytes.capacity()); |
| let read_num_handle_infos: u32 = size_to_u32_sat(buf.handle_infos.capacity()); |
| let mut zx_handle_infos: [std::mem::MaybeUninit<sys::zx_handle_info_t>; |
| sys::ZX_CHANNEL_MAX_MSG_HANDLES as usize] = |
| unsafe { std::mem::MaybeUninit::uninit().assume_init() }; |
| let mut args = sys::zx_channel_call_etc_args_t { |
| wr_bytes: bytes.as_ptr(), |
| wr_handles: zx_handle_dispositions.as_mut_ptr() as *mut sys::zx_handle_disposition_t, |
| rd_bytes: buf.bytes.as_mut_ptr(), |
| rd_handles: zx_handle_infos.as_mut_ptr() as *mut sys::zx_handle_info_t, |
| wr_num_bytes: write_num_bytes, |
| wr_num_handles: write_num_handle_dispositions, |
| rd_num_bytes: read_num_bytes, |
| rd_num_handles: read_num_handle_infos, |
| }; |
| let mut actual_read_bytes: u32 = 0; |
| let mut actual_read_handle_infos: u32 = 0; |
| let options = 0; |
| let status = unsafe { |
| Status::from_raw(sys::zx_channel_call_etc( |
| self.raw_handle(), |
| options, |
| timeout.into_nanos(), |
| &mut args, |
| &mut actual_read_bytes, |
| &mut actual_read_handle_infos, |
| )) |
| }; |
| unsafe { |
| buf.ensure_capacity_handle_infos(actual_read_handle_infos as usize); |
| for i in 0..actual_read_handle_infos as usize { |
| buf.handle_infos.push(HandleInfo::from_raw(zx_handle_infos[i].assume_init())); |
| } |
| buf.bytes.set_len(actual_read_bytes as usize); |
| } |
| if Status::OK == status { |
| Ok(()) |
| } else { |
| Err(status) |
| } |
| } |
| } |
| |
| #[test] |
| pub fn test_handle_repr() { |
| assert_eq!(::std::mem::size_of::<sys::zx_handle_t>(), 4); |
| assert_eq!(::std::mem::size_of::<Handle>(), 4); |
| assert_eq!(::std::mem::align_of::<sys::zx_handle_t>(), ::std::mem::align_of::<Handle>()); |
| |
| // This test asserts that repr(transparent) still works for Handle -> zx_handle_t |
| |
| let n: Vec<sys::zx_handle_t> = vec![0, 100, 2 << 32 - 1]; |
| let v: Vec<Handle> = n.iter().map(|h| unsafe { Handle::from_raw(*h) }).collect(); |
| |
| for (handle, raw) in v.iter().zip(n.iter()) { |
| unsafe { |
| assert_eq!( |
| *(handle as *const _ as *const [u8; 4]), |
| *(raw as *const _ as *const [u8; 4]) |
| ); |
| } |
| } |
| |
| for h in v.into_iter() { |
| ::std::mem::forget(h); |
| } |
| } |
| |
| impl AsRef<Channel> for Channel { |
| fn as_ref(&self) -> &Self { |
| &self |
| } |
| } |
| |
| /// A buffer for _receiving_ messages from a channel. |
| /// |
| /// A `MessageBuf` is essentially a byte buffer and a vector of |
| /// handles, but move semantics for "taking" handles requires special handling. |
| /// |
| /// Note that for sending messages to a channel, the caller manages the buffers, |
| /// using a plain byte slice and `Vec<Handle>`. |
| #[derive(Debug, Default)] |
| pub struct MessageBuf { |
| bytes: Vec<u8>, |
| handles: Vec<Handle>, |
| } |
| |
| impl MessageBuf { |
| /// Create a new, empty, message buffer. |
| pub fn new() -> Self { |
| Default::default() |
| } |
| |
| /// Create a new non-empty message buffer. |
| pub fn new_with(v: Vec<u8>, h: Vec<Handle>) -> Self { |
| Self { bytes: v, handles: h } |
| } |
| |
| /// Splits apart the message buf into a vector of bytes and a vector of handles. |
| pub fn split_mut(&mut self) -> (&mut Vec<u8>, &mut Vec<Handle>) { |
| (&mut self.bytes, &mut self.handles) |
| } |
| |
| /// Splits apart the message buf into a vector of bytes and a vector of handles. |
| pub fn split(self) -> (Vec<u8>, Vec<Handle>) { |
| (self.bytes, self.handles) |
| } |
| |
| /// Ensure that the buffer has the capacity to hold at least `n_bytes` bytes. |
| pub fn ensure_capacity_bytes(&mut self, n_bytes: usize) { |
| ensure_capacity(&mut self.bytes, n_bytes); |
| } |
| |
| /// Ensure that the buffer has the capacity to hold at least `n_handles` handles. |
| pub fn ensure_capacity_handles(&mut self, n_handles: usize) { |
| ensure_capacity(&mut self.handles, n_handles); |
| } |
| |
| /// Ensure that at least n_bytes bytes are initialized (0 fill). |
| pub fn ensure_initialized_bytes(&mut self, n_bytes: usize) { |
| if n_bytes <= self.bytes.len() { |
| return; |
| } |
| self.bytes.resize(n_bytes, 0); |
| } |
| |
| /// Get a reference to the bytes of the message buffer, as a `&[u8]` slice. |
| pub fn bytes(&self) -> &[u8] { |
| self.bytes.as_slice() |
| } |
| |
| /// The number of handles in the message buffer. Note this counts the number |
| /// available when the message was received; `take_handle` does not affect |
| /// the count. |
| pub fn n_handles(&self) -> usize { |
| self.handles.len() |
| } |
| |
| /// Take the handle at the specified index from the message buffer. If the |
| /// method is called again with the same index, it will return `None`, as |
| /// will happen if the index exceeds the number of handles available. |
| pub fn take_handle(&mut self, index: usize) -> Option<Handle> { |
| self.handles.get_mut(index).and_then(|handle| { |
| if handle.is_invalid() { |
| None |
| } else { |
| Some(mem::replace(handle, Handle::invalid())) |
| } |
| }) |
| } |
| |
| /// Clear the bytes and handles contained in the buf. This will drop any |
| /// contained handles, resulting in their resources being freed. |
| pub fn clear(&mut self) { |
| self.bytes.clear(); |
| self.handles.clear(); |
| } |
| } |
| |
| /// A buffer for _receiving_ messages from a channel. |
| /// |
| /// This differs from `MessageBuf` in that it holds `HandleInfo` with |
| /// extended handle information. |
| /// |
| /// A `MessageBufEtc` is essentially a byte buffer and a vector of handle |
| /// infos, but move semantics for "taking" handles requires special handling. |
| /// |
| /// Note that for sending messages to a channel, the caller manages the buffers, |
| /// using a plain byte slice and `Vec<HandleDisposition>`. |
| #[derive(Debug, Default)] |
| pub struct MessageBufEtc { |
| bytes: Vec<u8>, |
| handle_infos: Vec<HandleInfo>, |
| } |
| |
| impl MessageBufEtc { |
| /// Create a new, empty, message buffer. |
| pub fn new() -> Self { |
| Default::default() |
| } |
| |
| /// Create a new non-empty message buffer. |
| pub fn new_with(v: Vec<u8>, h: Vec<HandleInfo>) -> Self { |
| Self { bytes: v, handle_infos: h } |
| } |
| |
| /// Splits apart the message buf into a vector of bytes and a vector of handle infos. |
| pub fn split_mut(&mut self) -> (&mut Vec<u8>, &mut Vec<HandleInfo>) { |
| (&mut self.bytes, &mut self.handle_infos) |
| } |
| |
| /// Splits apart the message buf into a vector of bytes and a vector of handle infos. |
| pub fn split(self) -> (Vec<u8>, Vec<HandleInfo>) { |
| (self.bytes, self.handle_infos) |
| } |
| |
| /// Ensure that the buffer has the capacity to hold at least `n_bytes` bytes. |
| pub fn ensure_capacity_bytes(&mut self, n_bytes: usize) { |
| ensure_capacity(&mut self.bytes, n_bytes); |
| } |
| |
| /// Ensure that the buffer has the capacity to hold at least `n_handles` handle infos. |
| pub fn ensure_capacity_handle_infos(&mut self, n_handle_infos: usize) { |
| ensure_capacity(&mut self.handle_infos, n_handle_infos); |
| } |
| |
| /// Ensure that at least n_bytes bytes are initialized (0 fill). |
| pub fn ensure_initialized_bytes(&mut self, n_bytes: usize) { |
| if n_bytes <= self.bytes.len() { |
| return; |
| } |
| self.bytes.resize(n_bytes, 0); |
| } |
| |
| /// Get a reference to the bytes of the message buffer, as a `&[u8]` slice. |
| pub fn bytes(&self) -> &[u8] { |
| self.bytes.as_slice() |
| } |
| |
| /// The number of handles in the message buffer. Note this counts the number |
| /// available when the message was received; `take_handle` does not affect |
| /// the count. |
| pub fn n_handle_infos(&self) -> usize { |
| self.handle_infos.len() |
| } |
| |
| /// Take the handle at the specified index from the message buffer. If the |
| /// method is called again with the same index, it will return `None`, as |
| /// will happen if the index exceeds the number of handles available. |
| pub fn take_handle_info(&mut self, index: usize) -> Option<HandleInfo> { |
| self.handle_infos.get_mut(index).and_then(|handle_info| { |
| if handle_info.handle.is_invalid() { |
| None |
| } else { |
| Some(mem::replace( |
| handle_info, |
| HandleInfo { |
| handle: Handle::invalid(), |
| object_type: ObjectType::NONE, |
| rights: Rights::NONE, |
| }, |
| )) |
| } |
| }) |
| } |
| |
| /// Clear the bytes and handles contained in the buf. This will drop any |
| /// contained handles, resulting in their resources being freed. |
| pub fn clear(&mut self) { |
| self.bytes.clear(); |
| self.handle_infos.clear(); |
| } |
| } |
| |
| fn ensure_capacity<T>(vec: &mut Vec<T>, size: usize) { |
| let len = vec.len(); |
| if size > len { |
| vec.reserve(size - len); |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| use crate::{DurationNum, Port, Signals, Vmo}; |
| use std::thread; |
| |
| #[test] |
| fn channel_basic() { |
| let (p1, p2) = Channel::create(); |
| |
| let mut empty = vec![]; |
| assert!(p1.write(b"hello", &mut empty).is_ok()); |
| |
| let mut buf = MessageBuf::new(); |
| assert!(p2.read(&mut buf).is_ok()); |
| assert_eq!(buf.bytes(), b"hello"); |
| } |
| |
| #[test] |
| fn channel_basic_etc() { |
| let (p1, p2) = Channel::create(); |
| |
| let mut empty = vec![]; |
| assert!(p1.write_etc(b"hello", &mut empty).is_ok()); |
| |
| let mut buf = MessageBufEtc::new(); |
| assert!(p2.read_etc(&mut buf).is_ok()); |
| assert_eq!(buf.bytes(), b"hello"); |
| } |
| |
| #[test] |
| fn channel_basic_etc_with_handle_move() { |
| let (p1, p2) = Channel::create(); |
| |
| let mut handles = vec![HandleDisposition { |
| handle_op: HandleOp::Move(Port::create().into()), |
| rights: Rights::TRANSFER, |
| object_type: ObjectType::PORT, |
| result: Status::OK, |
| }]; |
| match p1.write_etc(b"", &mut handles) { |
| Err(err) => { |
| panic!("error: {}", err); |
| } |
| _ => {} |
| } |
| |
| let mut buf = MessageBufEtc::new(); |
| assert!(p2.read_etc(&mut buf).is_ok()); |
| assert_eq!(buf.bytes(), b""); |
| assert_eq!(buf.n_handle_infos(), 1); |
| let out_handles = buf.handle_infos; |
| assert_eq!(out_handles.len(), 1); |
| assert_ne!(out_handles[0].handle, Handle::invalid()); |
| assert_eq!(out_handles[0].rights, Rights::TRANSFER); |
| assert_eq!(out_handles[0].object_type, ObjectType::PORT); |
| } |
| |
| #[test] |
| fn channel_basic_etc_with_handle_duplicate() { |
| let (p1, p2) = Channel::create(); |
| |
| let port = Port::create(); |
| let mut handles = vec![HandleDisposition { |
| handle_op: HandleOp::Duplicate(port.as_handle_ref()), |
| rights: Rights::SAME_RIGHTS, |
| object_type: ObjectType::NONE, |
| result: Status::OK, |
| }]; |
| p1.write_etc(b"", &mut handles).unwrap(); |
| |
| let orig_port_info = port.basic_info().unwrap(); |
| let mut buf = MessageBufEtc::new(); |
| assert!(p2.read_etc(&mut buf).is_ok()); |
| assert_eq!(buf.bytes(), b""); |
| assert_eq!(buf.n_handle_infos(), 1); |
| let out_handles = buf.handle_infos; |
| assert_eq!(out_handles.len(), 1); |
| assert_ne!(out_handles[0].handle.raw_handle(), 0); |
| assert_ne!(out_handles[0].handle.raw_handle(), port.raw_handle()); |
| assert_eq!(out_handles[0].rights, orig_port_info.rights); |
| assert_eq!(out_handles[0].object_type, ObjectType::PORT); |
| } |
| |
| #[test] |
| fn channel_read_raw_too_small() { |
| let (p1, p2) = Channel::create(); |
| |
| let mut empty = vec![]; |
| assert!(p1.write(b"hello", &mut empty).is_ok()); |
| |
| let result = p2.read_raw(&mut vec![], &mut vec![]); |
| assert_eq!(result, Err((5, 0))); |
| } |
| |
| #[test] |
| fn channel_read_etc_raw_too_small() { |
| let (p1, p2) = Channel::create(); |
| |
| let mut empty = vec![]; |
| assert!(p1.write_etc(b"hello", &mut empty).is_ok()); |
| |
| let result = p2.read_etc_raw(&mut vec![], &mut vec![]); |
| assert_eq!(result, Err((5, 0))); |
| } |
| |
| fn too_many_bytes() -> Vec<u8> { |
| vec![b'A'; (sys::ZX_CHANNEL_MAX_MSG_BYTES + 1) as usize] |
| } |
| |
| fn too_many_handles() -> Vec<Handle> { |
| let mut handles = vec![]; |
| for _ in 0..sys::ZX_CHANNEL_MAX_MSG_HANDLES + 1 { |
| handles.push(crate::Event::create().into()); |
| } |
| handles |
| } |
| |
| fn too_many_dispositions() -> Vec<HandleDisposition<'static>> { |
| let mut handles = vec![]; |
| for _ in 0..sys::ZX_CHANNEL_MAX_MSG_HANDLES + 1 { |
| handles.push(HandleDisposition { |
| handle_op: HandleOp::Move(crate::Event::create().into()), |
| object_type: ObjectType::EVENT, |
| rights: Rights::TRANSFER, |
| result: Status::OK, |
| }); |
| } |
| handles |
| } |
| |
| #[test] |
| fn channel_write_too_many_bytes() { |
| Channel::create().0.write(&too_many_bytes(), &mut vec![]).unwrap_err(); |
| } |
| |
| #[test] |
| fn channel_write_too_many_handles() { |
| Channel::create().0.write(&vec![], &mut too_many_handles()[..]).unwrap_err(); |
| } |
| |
| #[test] |
| fn channel_write_etc_too_many_bytes() { |
| Channel::create().0.write_etc(&too_many_bytes(), &mut []).unwrap_err(); |
| } |
| |
| #[test] |
| fn channel_write_etc_too_many_handles() { |
| Channel::create().0.write_etc(&vec![], &mut too_many_dispositions()[..]).unwrap_err(); |
| } |
| |
| #[test] |
| fn channel_call_too_many_bytes() { |
| Channel::create() |
| .0 |
| .call(Time::INFINITE, &too_many_bytes(), &mut vec![], &mut MessageBuf::new()) |
| .unwrap_err(); |
| } |
| |
| #[test] |
| fn channel_call_too_many_handles() { |
| Channel::create() |
| .0 |
| .call(Time::INFINITE, &vec![], &mut too_many_handles()[..], &mut MessageBuf::new()) |
| .unwrap_err(); |
| } |
| |
| #[test] |
| fn channel_call_etc_too_many_bytes() { |
| Channel::create() |
| .0 |
| .call_etc(Time::INFINITE, &too_many_bytes(), &mut vec![], &mut MessageBufEtc::new()) |
| .unwrap_err(); |
| } |
| |
| #[test] |
| fn channel_call_etc_too_many_handles() { |
| Channel::create() |
| .0 |
| .call_etc( |
| Time::INFINITE, |
| &vec![], |
| &mut too_many_dispositions()[..], |
| &mut MessageBufEtc::new(), |
| ) |
| .unwrap_err(); |
| } |
| |
| #[test] |
| fn channel_send_handle() { |
| let hello_length: usize = 5; |
| |
| // Create a pair of channels and a virtual memory object. |
| let (p1, p2) = Channel::create(); |
| let vmo = Vmo::create(hello_length as u64).unwrap(); |
| |
| // Duplicate VMO handle and send it down the channel. |
| let duplicate_vmo_handle = vmo.duplicate_handle(Rights::SAME_RIGHTS).unwrap().into(); |
| let mut handles_to_send: Vec<Handle> = vec![duplicate_vmo_handle]; |
| assert!(p1.write(b"", &mut handles_to_send).is_ok()); |
| // The handle vector should only contain invalid handles. |
| for handle in handles_to_send { |
| assert!(handle.is_invalid()); |
| } |
| |
| // Read the handle from the receiving channel. |
| let mut buf = MessageBuf::new(); |
| assert!(p2.read(&mut buf).is_ok()); |
| assert_eq!(buf.n_handles(), 1); |
| // Take the handle from the buffer. |
| let received_handle = buf.take_handle(0).unwrap(); |
| // Should not affect number of handles. |
| assert_eq!(buf.n_handles(), 1); |
| // Trying to take it again should fail. |
| assert!(buf.take_handle(0).is_none()); |
| |
| // Now to test that we got the right handle, try writing something to it... |
| let received_vmo = Vmo::from(received_handle); |
| assert!(received_vmo.write(b"hello", 0).is_ok()); |
| |
| // ... and reading it back from the original VMO. |
| let mut read_vec = vec![0; hello_length]; |
| assert!(vmo.read(&mut read_vec, 0).is_ok()); |
| assert_eq!(read_vec, b"hello"); |
| } |
| |
| #[test] |
| fn channel_call_timeout() { |
| let ten_ms = 10.millis(); |
| |
| // Create a pair of channels and a virtual memory object. |
| let (p1, p2) = Channel::create(); |
| let vmo = Vmo::create(0 as u64).unwrap(); |
| |
| // Duplicate VMO handle and send it along with the call. |
| let duplicate_vmo_handle = vmo.duplicate_handle(Rights::SAME_RIGHTS).unwrap().into(); |
| let mut handles_to_send: Vec<Handle> = vec![duplicate_vmo_handle]; |
| let mut buf = MessageBuf::new(); |
| assert_eq!( |
| p1.call(Time::after(ten_ms), b"0000call", &mut handles_to_send, &mut buf), |
| Err(Status::TIMED_OUT) |
| ); |
| // Despite not getting a response, the handles were sent so the handle slice |
| // should only contain invalid handles. |
| for handle in handles_to_send { |
| assert!(handle.is_invalid()); |
| } |
| |
| // Should be able to read call even though it timed out waiting for a response. |
| let mut buf = MessageBuf::new(); |
| assert!(p2.read(&mut buf).is_ok()); |
| assert_eq!(&buf.bytes()[4..], b"call"); |
| assert_eq!(buf.n_handles(), 1); |
| } |
| |
| #[test] |
| fn channel_call_etc_timeout() { |
| let ten_ms = 10.millis(); |
| |
| // Create a pair of channels and a virtual memory object. |
| let (p1, p2) = Channel::create(); |
| |
| // Duplicate VMO handle and send it along with the call. |
| let mut empty: Vec<HandleDisposition<'_>> = vec![]; |
| let mut buf = MessageBufEtc::new(); |
| assert_eq!( |
| p1.call_etc(Time::after(ten_ms), b"0000call", &mut empty, &mut buf), |
| Err(Status::TIMED_OUT) |
| ); |
| |
| // Should be able to read call even though it timed out waiting for a response. |
| let mut buf = MessageBuf::new(); |
| assert!(p2.read(&mut buf).is_ok()); |
| assert_eq!(&buf.bytes()[4..], b"call"); |
| assert_eq!(buf.n_handles(), 0); |
| } |
| |
| #[test] |
| fn channel_call() { |
| // Create a pair of channels |
| let (p1, p2) = Channel::create(); |
| |
| // create an mpsc channel for communicating the call data for later assertion |
| let (tx, rx) = ::std::sync::mpsc::channel(); |
| |
| // Start a new thread to respond to the call. |
| thread::spawn(move || { |
| let mut buf = MessageBuf::new(); |
| // if either the read or the write fail, this thread will panic, |
| // resulting in tx being dropped, which will be noticed by the rx. |
| p2.wait_handle(Signals::CHANNEL_READABLE, Time::after(1.seconds())) |
| .expect("callee wait error"); |
| p2.read(&mut buf).expect("callee read error"); |
| |
| let (bytes, handles) = buf.split_mut(); |
| tx.send(bytes.clone()).expect("callee mpsc send error"); |
| assert_eq!(handles.len(), 0); |
| |
| bytes.truncate(4); // Drop the received message, leaving only the txid |
| bytes.extend_from_slice(b"response"); |
| |
| p2.write(bytes, handles).expect("callee write error"); |
| }); |
| |
| // Make the call. |
| let mut buf = MessageBuf::new(); |
| buf.ensure_capacity_bytes(12); |
| // NOTE(raggi): CQ has been seeing some long stalls from channel call, |
| // and it's as yet unclear why. The timeout here has been made much |
| // larger in order to avoid that, as the issues are not issues with this |
| // crate's concerns. The timeout is here just to prevent the tests from |
| // stalling forever if a developer makes a mistake locally in this |
| // crate. Tests of Zircon behavior or virtualization behavior should be |
| // covered elsewhere. See https://fxbug.dev/42106187. |
| p1.call(Time::after(30.seconds()), b"txidcall", &mut vec![], &mut buf) |
| .expect("channel call error"); |
| assert_eq!(&buf.bytes()[4..], b"response"); |
| assert_eq!(buf.n_handles(), 0); |
| |
| let sbuf = rx.recv().expect("mpsc channel recv error"); |
| assert_eq!(&sbuf[4..], b"call"); |
| } |
| |
| #[test] |
| fn channel_call_etc() { |
| // Create a pair of channels |
| let (p1, p2) = Channel::create(); |
| |
| // create an mpsc channel for communicating the call data for later assertion |
| let (tx, rx) = ::std::sync::mpsc::channel(); |
| |
| // Start a new thread to respond to the call. |
| thread::spawn(move || { |
| let mut buf = MessageBuf::new(); |
| // if either the read or the write fail, this thread will panic, |
| // resulting in tx being dropped, which will be noticed by the rx. |
| p2.wait_handle(Signals::CHANNEL_READABLE, Time::after(1.seconds())) |
| .expect("callee wait error"); |
| p2.read(&mut buf).expect("callee read error"); |
| |
| let (bytes, handles) = buf.split_mut(); |
| tx.send(bytes.clone()).expect("callee mpsc send error"); |
| assert_eq!(handles.len(), 1); |
| |
| bytes.truncate(4); // Drop the received message, leaving only the txid |
| bytes.extend_from_slice(b"response"); |
| |
| p2.write(bytes, handles).expect("callee write error"); |
| }); |
| |
| // Make the call. |
| let mut buf = MessageBufEtc::new(); |
| buf.ensure_capacity_bytes(12); |
| buf.ensure_capacity_handle_infos(1); |
| let mut handle_dispositions = [HandleDisposition { |
| handle_op: HandleOp::Move(Port::create().into()), |
| object_type: ObjectType::PORT, |
| rights: Rights::TRANSFER, |
| result: Status::OK, |
| }]; |
| // NOTE(raggi): CQ has been seeing some long stalls from channel call, |
| // and it's as yet unclear why. The timeout here has been made much |
| // larger in order to avoid that, as the issues are not issues with this |
| // crate's concerns. The timeout is here just to prevent the tests from |
| // stalling forever if a developer makes a mistake locally in this |
| // crate. Tests of Zircon behavior or virtualization behavior should be |
| // covered elsewhere. See https://fxbug.dev/42106187. |
| p1.call_etc(Time::after(30.seconds()), b"txidcall", &mut handle_dispositions, &mut buf) |
| .expect("channel call error"); |
| assert_eq!(&buf.bytes()[4..], b"response"); |
| assert_eq!(buf.n_handle_infos(), 1); |
| assert_ne!(buf.handle_infos[0].handle.raw_handle(), 0); |
| assert_eq!(buf.handle_infos[0].object_type, ObjectType::PORT); |
| assert_eq!(buf.handle_infos[0].rights, Rights::TRANSFER); |
| |
| let sbuf = rx.recv().expect("mpsc channel recv error"); |
| assert_eq!(&sbuf[4..], b"call"); |
| } |
| } |