Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 086ed558 authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "[libtrusty-rs] Add recv variant that uses Vec<u8>"

parents 3e22bc61 dc2d792a
Loading
Loading
Loading
Loading
+2 −0
Original line number Original line Diff line number Diff line
@@ -24,6 +24,7 @@ rust_library {
    ],
    ],
    rustlibs: [
    rustlibs: [
        "libnix",
        "libnix",
        "liblibc",
    ],
    ],
}
}


@@ -33,5 +34,6 @@ rust_test {
    srcs: ["tests/test.rs"],
    srcs: ["tests/test.rs"],
    rustlibs: [
    rustlibs: [
        "libtrusty-rs",
        "libtrusty-rs",
        "liblibc",
    ]
    ]
}
}
+89 −11
Original line number Original line Diff line number Diff line
@@ -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);
@@ -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;


@@ -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.
@@ -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");
@@ -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
@@ -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)
    }
    }


+63 −3
Original line number Original line Diff line number Diff line
@@ -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");


@@ -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(),
@@ -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");
}