Loading trusty/libtrusty-rs/Android.bp +2 −0 Original line number Original line Diff line number Diff line Loading @@ -24,6 +24,7 @@ rust_library { ], ], rustlibs: [ rustlibs: [ "libnix", "libnix", "liblibc", ], ], } } Loading @@ -33,5 +34,6 @@ rust_test { srcs: ["tests/test.rs"], srcs: ["tests/test.rs"], rustlibs: [ rustlibs: [ "libtrusty-rs", "libtrusty-rs", "liblibc", ] ] } } trusty/libtrusty-rs/src/lib.rs +89 −11 Original line number Original line Diff line number Diff line Loading @@ -51,8 +51,8 @@ //! //! //! chann.send("Hello, world!".as_bytes()).unwrap(); //! chann.send("Hello, world!".as_bytes()).unwrap(); //! //! //! let mut read_buf = [0u8; 1024]; //! let mut read_buf = Vec::new(); //! let read_len = stream.read(&mut read_buf[..]).unwrap(); //! let read_len = stream.recv(&mut read_buf).unwrap(); //! //! //! let response = std::str::from_utf8(&read_buf[..read_len]).unwrap(); //! let response = std::str::from_utf8(&read_buf[..read_len]).unwrap(); //! assert_eq!("Hello, world!", response); //! assert_eq!("Hello, world!", response); Loading @@ -63,8 +63,8 @@ use crate::sys::tipc_connect; use crate::sys::tipc_connect; use std::ffi::CString; use std::ffi::CString; use std::fs::File; use std::fs::File; use std::io; use std::io::prelude::*; use std::io::prelude::*; use std::io::{ErrorKind, Result}; use std::os::unix::prelude::AsRawFd; use std::os::unix::prelude::AsRawFd; use std::path::Path; use std::path::Path; Loading @@ -73,6 +73,12 @@ mod sys; /// The default filesystem path for the Trusty IPC device. /// The default filesystem path for the Trusty IPC device. pub const DEFAULT_DEVICE: &str = "/dev/trusty-ipc-dev0"; pub const DEFAULT_DEVICE: &str = "/dev/trusty-ipc-dev0"; /// The maximum size an incoming TIPC message can be. /// /// This can be used to pre-allocate buffer space in order to ensure that your /// read buffer can always hold an incoming message. pub const MAX_MESSAGE_SIZE: usize = 4096; /// A channel for communicating with a Trusty service. /// A channel for communicating with a Trusty service. /// /// /// See the [crate-level documentation][crate] for usage details and examples. /// See the [crate-level documentation][crate] for usage details and examples. Loading @@ -92,7 +98,7 @@ impl TipcChannel { /// bytes. This is handled with a panic because the service names are all /// bytes. This is handled with a panic because the service names are all /// hard-coded constants, and so such an error should always be indicative of a /// hard-coded constants, and so such an error should always be indicative of a /// bug in the calling code. /// bug in the calling code. pub fn connect(device: impl AsRef<Path>, service: &str) -> io::Result<Self> { pub fn connect(device: impl AsRef<Path>, service: &str) -> Result<Self> { let file = File::options().read(true).write(true).open(device)?; let file = File::options().read(true).write(true).open(device)?; let srv_name = CString::new(service).expect("Service name contained null bytes"); let srv_name = CString::new(service).expect("Service name contained null bytes"); Loading @@ -107,7 +113,7 @@ impl TipcChannel { /// /// /// The entire contents of `buf` will be sent as a single message to the /// The entire contents of `buf` will be sent as a single message to the /// connected service. /// connected service. pub fn send(&mut self, buf: &[u8]) -> io::Result<()> { pub fn send(&mut self, buf: &[u8]) -> Result<()> { let write_len = self.0.write(buf)?; let write_len = self.0.write(buf)?; // Verify that the expected number of bytes were written. The entire message // Verify that the expected number of bytes were written. The entire message Loading @@ -125,19 +131,91 @@ impl TipcChannel { Ok(()) Ok(()) } } /// Receives a message from the connected service. /// Reads the next incoming message. /// /// Attempts to read the next incoming message from the connected service if any /// exist. If the initial capacity of `buf` is not enough to hold the incoming /// message the function repeatedly attempts to reserve additional space until /// it is able to fully read the message. /// /// Blocks until there is an incoming message if there is not already a message /// ready to be received. /// /// # Errors /// /// If this function encounters an error of the kind [`ErrorKind::Interrupted`] /// then the error is ignored and the operation will be tried again. /// /// If this function encounters an error with the error code `EMSGSIZE` then /// additional space will be reserved in `buf` and the operation will be tried /// again. /// /// If any other read error is encountered then this function immediately /// returns the error to the caller, and the length of `buf` is set to 0. pub fn recv(&mut self, buf: &mut Vec<u8>) -> Result<()> { // If no space has been allocated in the buffer reserve enough space to hold any // incoming message. if buf.capacity() == 0 { buf.reserve(MAX_MESSAGE_SIZE); } loop { // Resize the vec to make its full capacity available to write into. buf.resize(buf.capacity(), 0); match self.0.read(buf.as_mut_slice()) { Ok(len) => { buf.truncate(len); return Ok(()); } Err(err) => { if let Some(libc::EMSGSIZE) = err.raw_os_error() { // Ensure that we didn't get `EMSGSIZE` when we already had enough capacity // to contain the maximum message size. This should never happen, but if it // does we don't want to hang by looping infinitely. assert!( buf.capacity() < MAX_MESSAGE_SIZE, "Received `EMSGSIZE` error when buffer capacity was already at maximum", ); // If we didn't have enough space to hold the incoming message, reserve // enough space to fit the maximum message size regardless of how much // capacity the buffer already had. buf.reserve(MAX_MESSAGE_SIZE - buf.capacity()); } else if err.kind() == ErrorKind::Interrupted { // If we get an interrupted error the operation can be retried as-is, i.e. // we don't need to allocate additional space. continue; } else { buf.truncate(0); return Err(err); } } } } } /// Reads the next incoming message without allocating. /// /// /// Returns the number of bytes in the received message, or any error that /// Returns the number of bytes in the received message, or any error that /// occurred when reading the message. Blocks until there is a message to /// occurred when reading the message. /// receive if none is already ready to read. /// /// Blocks until there is an incoming message if there is not already a message /// ready to be received. /// /// /// # Errors /// # Errors /// /// /// Returns an error with native error code 90 (`EMSGSIZE`) if `buf` isn't large /// Returns an error with native error code `EMSGSIZE` if `buf` isn't large /// enough to contain the incoming message. Use /// enough to contain the incoming message. Use /// [`raw_os_error`][std::io::Error::raw_os_error] to check the error code to /// [`raw_os_error`][std::io::Error::raw_os_error] to check the error code to /// determine if you need to increase the size of `buf`. /// determine if you need to increase the size of `buf`. If error code pub fn recv(&mut self, buf: &mut [u8]) -> io::Result<usize> { /// `EMSGSIZE` is returned the incoming message will not be dropped, and a /// subsequent call to `recv_no_alloc` can still read it. /// /// An error of the [`ErrorKind::Interrupted`] kind is non-fatal and the read /// operation should be retried if there is nothing else to do. pub fn recv_no_alloc(&mut self, buf: &mut [u8]) -> Result<usize> { self.0.read(buf) self.0.read(buf) } } Loading trusty/libtrusty-rs/tests/test.rs +63 −3 Original line number Original line Diff line number Diff line Loading @@ -3,7 +3,7 @@ use trusty::{TipcChannel, DEFAULT_DEVICE}; const ECHO_NAME: &str = "com.android.ipc-unittest.srv.echo"; const ECHO_NAME: &str = "com.android.ipc-unittest.srv.echo"; #[test] #[test] fn echo() { fn recv_no_alloc() { let mut connection = TipcChannel::connect(DEFAULT_DEVICE, ECHO_NAME) let mut connection = TipcChannel::connect(DEFAULT_DEVICE, ECHO_NAME) .expect("Failed to connect to Trusty service"); .expect("Failed to connect to Trusty service"); Loading @@ -11,9 +11,10 @@ fn echo() { let send_buf = [7u8; 32]; let send_buf = [7u8; 32]; connection.send(send_buf.as_slice()).unwrap(); connection.send(send_buf.as_slice()).unwrap(); // Receive the response message from the TA. // Receive the response message from the TA. The response message will be the // same as the message we just sent. let mut recv_buf = [0u8; 32]; let mut recv_buf = [0u8; 32]; let read_len = connection.recv(&mut recv_buf).expect("Failed to read from connection"); let read_len = connection.recv_no_alloc(recv_buf.as_mut_slice()).unwrap(); assert_eq!( assert_eq!( send_buf.len(), send_buf.len(), Loading @@ -24,3 +25,62 @@ fn echo() { ); ); assert_eq!(send_buf, recv_buf, "Received data does not match sent data"); assert_eq!(send_buf, recv_buf, "Received data does not match sent data"); } } #[test] fn recv_small_buf() { let mut connection = TipcChannel::connect(DEFAULT_DEVICE, ECHO_NAME) .expect("Failed to connect to Trusty service"); // Send a long message to the echo service so that we can test receiving a long // message. let send_buf = [7u8; 2048]; connection.send(send_buf.as_slice()).unwrap(); // Attempt to receive the response message with a buffer that is too small to // contain the message. let mut recv_buf = [0u8; 32]; let err = connection.recv_no_alloc(recv_buf.as_mut_slice()).unwrap_err(); assert_eq!( Some(libc::EMSGSIZE), err.raw_os_error(), "Unexpected error err when receiving incoming message: {:?}", err, ); } #[test] fn recv_empty_vec() { let mut connection = TipcChannel::connect(DEFAULT_DEVICE, ECHO_NAME) .expect("Failed to connect to Trusty service"); // Send a message to the echo TA. let send_buf = [7u8; 2048]; connection.send(send_buf.as_slice()).unwrap(); // Receive the response message. `recv_buf` is initially empty, and `recv` is // responsible for allocating enough space to hold the message. let mut recv_buf = Vec::new(); connection.recv(&mut recv_buf).unwrap(); assert_eq!(send_buf.as_slice(), recv_buf, "Received data does not match sent data"); } #[test] fn recv_vec_existing_capacity() { let mut connection = TipcChannel::connect(DEFAULT_DEVICE, ECHO_NAME) .expect("Failed to connect to Trusty service"); // Send a message to the echo TA. let send_buf = [7u8; 2048]; connection.send(send_buf.as_slice()).unwrap(); // Receive the response message into a buffer that already has enough capacity // to hold the message. No additional capacity should be allocated when // receiving the message. let mut recv_buf = Vec::with_capacity(2048); connection.recv(&mut recv_buf).unwrap(); assert_eq!(send_buf.as_slice(), recv_buf, "Received data does not match sent data"); assert_eq!(2048, recv_buf.capacity(), "Additional capacity was allocated when not needed"); } Loading
trusty/libtrusty-rs/Android.bp +2 −0 Original line number Original line Diff line number Diff line Loading @@ -24,6 +24,7 @@ rust_library { ], ], rustlibs: [ rustlibs: [ "libnix", "libnix", "liblibc", ], ], } } Loading @@ -33,5 +34,6 @@ rust_test { srcs: ["tests/test.rs"], srcs: ["tests/test.rs"], rustlibs: [ rustlibs: [ "libtrusty-rs", "libtrusty-rs", "liblibc", ] ] } }
trusty/libtrusty-rs/src/lib.rs +89 −11 Original line number Original line Diff line number Diff line Loading @@ -51,8 +51,8 @@ //! //! //! chann.send("Hello, world!".as_bytes()).unwrap(); //! chann.send("Hello, world!".as_bytes()).unwrap(); //! //! //! let mut read_buf = [0u8; 1024]; //! let mut read_buf = Vec::new(); //! let read_len = stream.read(&mut read_buf[..]).unwrap(); //! let read_len = stream.recv(&mut read_buf).unwrap(); //! //! //! let response = std::str::from_utf8(&read_buf[..read_len]).unwrap(); //! let response = std::str::from_utf8(&read_buf[..read_len]).unwrap(); //! assert_eq!("Hello, world!", response); //! assert_eq!("Hello, world!", response); Loading @@ -63,8 +63,8 @@ use crate::sys::tipc_connect; use crate::sys::tipc_connect; use std::ffi::CString; use std::ffi::CString; use std::fs::File; use std::fs::File; use std::io; use std::io::prelude::*; use std::io::prelude::*; use std::io::{ErrorKind, Result}; use std::os::unix::prelude::AsRawFd; use std::os::unix::prelude::AsRawFd; use std::path::Path; use std::path::Path; Loading @@ -73,6 +73,12 @@ mod sys; /// The default filesystem path for the Trusty IPC device. /// The default filesystem path for the Trusty IPC device. pub const DEFAULT_DEVICE: &str = "/dev/trusty-ipc-dev0"; pub const DEFAULT_DEVICE: &str = "/dev/trusty-ipc-dev0"; /// The maximum size an incoming TIPC message can be. /// /// This can be used to pre-allocate buffer space in order to ensure that your /// read buffer can always hold an incoming message. pub const MAX_MESSAGE_SIZE: usize = 4096; /// A channel for communicating with a Trusty service. /// A channel for communicating with a Trusty service. /// /// /// See the [crate-level documentation][crate] for usage details and examples. /// See the [crate-level documentation][crate] for usage details and examples. Loading @@ -92,7 +98,7 @@ impl TipcChannel { /// bytes. This is handled with a panic because the service names are all /// bytes. This is handled with a panic because the service names are all /// hard-coded constants, and so such an error should always be indicative of a /// hard-coded constants, and so such an error should always be indicative of a /// bug in the calling code. /// bug in the calling code. pub fn connect(device: impl AsRef<Path>, service: &str) -> io::Result<Self> { pub fn connect(device: impl AsRef<Path>, service: &str) -> Result<Self> { let file = File::options().read(true).write(true).open(device)?; let file = File::options().read(true).write(true).open(device)?; let srv_name = CString::new(service).expect("Service name contained null bytes"); let srv_name = CString::new(service).expect("Service name contained null bytes"); Loading @@ -107,7 +113,7 @@ impl TipcChannel { /// /// /// The entire contents of `buf` will be sent as a single message to the /// The entire contents of `buf` will be sent as a single message to the /// connected service. /// connected service. pub fn send(&mut self, buf: &[u8]) -> io::Result<()> { pub fn send(&mut self, buf: &[u8]) -> Result<()> { let write_len = self.0.write(buf)?; let write_len = self.0.write(buf)?; // Verify that the expected number of bytes were written. The entire message // Verify that the expected number of bytes were written. The entire message Loading @@ -125,19 +131,91 @@ impl TipcChannel { Ok(()) Ok(()) } } /// Receives a message from the connected service. /// Reads the next incoming message. /// /// Attempts to read the next incoming message from the connected service if any /// exist. If the initial capacity of `buf` is not enough to hold the incoming /// message the function repeatedly attempts to reserve additional space until /// it is able to fully read the message. /// /// Blocks until there is an incoming message if there is not already a message /// ready to be received. /// /// # Errors /// /// If this function encounters an error of the kind [`ErrorKind::Interrupted`] /// then the error is ignored and the operation will be tried again. /// /// If this function encounters an error with the error code `EMSGSIZE` then /// additional space will be reserved in `buf` and the operation will be tried /// again. /// /// If any other read error is encountered then this function immediately /// returns the error to the caller, and the length of `buf` is set to 0. pub fn recv(&mut self, buf: &mut Vec<u8>) -> Result<()> { // If no space has been allocated in the buffer reserve enough space to hold any // incoming message. if buf.capacity() == 0 { buf.reserve(MAX_MESSAGE_SIZE); } loop { // Resize the vec to make its full capacity available to write into. buf.resize(buf.capacity(), 0); match self.0.read(buf.as_mut_slice()) { Ok(len) => { buf.truncate(len); return Ok(()); } Err(err) => { if let Some(libc::EMSGSIZE) = err.raw_os_error() { // Ensure that we didn't get `EMSGSIZE` when we already had enough capacity // to contain the maximum message size. This should never happen, but if it // does we don't want to hang by looping infinitely. assert!( buf.capacity() < MAX_MESSAGE_SIZE, "Received `EMSGSIZE` error when buffer capacity was already at maximum", ); // If we didn't have enough space to hold the incoming message, reserve // enough space to fit the maximum message size regardless of how much // capacity the buffer already had. buf.reserve(MAX_MESSAGE_SIZE - buf.capacity()); } else if err.kind() == ErrorKind::Interrupted { // If we get an interrupted error the operation can be retried as-is, i.e. // we don't need to allocate additional space. continue; } else { buf.truncate(0); return Err(err); } } } } } /// Reads the next incoming message without allocating. /// /// /// Returns the number of bytes in the received message, or any error that /// Returns the number of bytes in the received message, or any error that /// occurred when reading the message. Blocks until there is a message to /// occurred when reading the message. /// receive if none is already ready to read. /// /// Blocks until there is an incoming message if there is not already a message /// ready to be received. /// /// /// # Errors /// # Errors /// /// /// Returns an error with native error code 90 (`EMSGSIZE`) if `buf` isn't large /// Returns an error with native error code `EMSGSIZE` if `buf` isn't large /// enough to contain the incoming message. Use /// enough to contain the incoming message. Use /// [`raw_os_error`][std::io::Error::raw_os_error] to check the error code to /// [`raw_os_error`][std::io::Error::raw_os_error] to check the error code to /// determine if you need to increase the size of `buf`. /// determine if you need to increase the size of `buf`. If error code pub fn recv(&mut self, buf: &mut [u8]) -> io::Result<usize> { /// `EMSGSIZE` is returned the incoming message will not be dropped, and a /// subsequent call to `recv_no_alloc` can still read it. /// /// An error of the [`ErrorKind::Interrupted`] kind is non-fatal and the read /// operation should be retried if there is nothing else to do. pub fn recv_no_alloc(&mut self, buf: &mut [u8]) -> Result<usize> { self.0.read(buf) self.0.read(buf) } } Loading
trusty/libtrusty-rs/tests/test.rs +63 −3 Original line number Original line Diff line number Diff line Loading @@ -3,7 +3,7 @@ use trusty::{TipcChannel, DEFAULT_DEVICE}; const ECHO_NAME: &str = "com.android.ipc-unittest.srv.echo"; const ECHO_NAME: &str = "com.android.ipc-unittest.srv.echo"; #[test] #[test] fn echo() { fn recv_no_alloc() { let mut connection = TipcChannel::connect(DEFAULT_DEVICE, ECHO_NAME) let mut connection = TipcChannel::connect(DEFAULT_DEVICE, ECHO_NAME) .expect("Failed to connect to Trusty service"); .expect("Failed to connect to Trusty service"); Loading @@ -11,9 +11,10 @@ fn echo() { let send_buf = [7u8; 32]; let send_buf = [7u8; 32]; connection.send(send_buf.as_slice()).unwrap(); connection.send(send_buf.as_slice()).unwrap(); // Receive the response message from the TA. // Receive the response message from the TA. The response message will be the // same as the message we just sent. let mut recv_buf = [0u8; 32]; let mut recv_buf = [0u8; 32]; let read_len = connection.recv(&mut recv_buf).expect("Failed to read from connection"); let read_len = connection.recv_no_alloc(recv_buf.as_mut_slice()).unwrap(); assert_eq!( assert_eq!( send_buf.len(), send_buf.len(), Loading @@ -24,3 +25,62 @@ fn echo() { ); ); assert_eq!(send_buf, recv_buf, "Received data does not match sent data"); assert_eq!(send_buf, recv_buf, "Received data does not match sent data"); } } #[test] fn recv_small_buf() { let mut connection = TipcChannel::connect(DEFAULT_DEVICE, ECHO_NAME) .expect("Failed to connect to Trusty service"); // Send a long message to the echo service so that we can test receiving a long // message. let send_buf = [7u8; 2048]; connection.send(send_buf.as_slice()).unwrap(); // Attempt to receive the response message with a buffer that is too small to // contain the message. let mut recv_buf = [0u8; 32]; let err = connection.recv_no_alloc(recv_buf.as_mut_slice()).unwrap_err(); assert_eq!( Some(libc::EMSGSIZE), err.raw_os_error(), "Unexpected error err when receiving incoming message: {:?}", err, ); } #[test] fn recv_empty_vec() { let mut connection = TipcChannel::connect(DEFAULT_DEVICE, ECHO_NAME) .expect("Failed to connect to Trusty service"); // Send a message to the echo TA. let send_buf = [7u8; 2048]; connection.send(send_buf.as_slice()).unwrap(); // Receive the response message. `recv_buf` is initially empty, and `recv` is // responsible for allocating enough space to hold the message. let mut recv_buf = Vec::new(); connection.recv(&mut recv_buf).unwrap(); assert_eq!(send_buf.as_slice(), recv_buf, "Received data does not match sent data"); } #[test] fn recv_vec_existing_capacity() { let mut connection = TipcChannel::connect(DEFAULT_DEVICE, ECHO_NAME) .expect("Failed to connect to Trusty service"); // Send a message to the echo TA. let send_buf = [7u8; 2048]; connection.send(send_buf.as_slice()).unwrap(); // Receive the response message into a buffer that already has enough capacity // to hold the message. No additional capacity should be allocated when // receiving the message. let mut recv_buf = Vec::with_capacity(2048); connection.recv(&mut recv_buf).unwrap(); assert_eq!(send_buf.as_slice(), recv_buf, "Received data does not match sent data"); assert_eq!(2048, recv_buf.capacity(), "Additional capacity was allocated when not needed"); }