use crate::bitmap::BitmapSlice;
use crate::volatile_memory::copy_slice_impl::{copy_from_volatile_slice, copy_to_volatile_slice};
use crate::{VolatileMemoryError, VolatileSlice};
use std::io::{Cursor, ErrorKind};
#[cfg(feature = "rawfd")]
use std::io::Stdout;
#[cfg(feature = "rawfd")]
use std::os::fd::{AsFd, AsRawFd, BorrowedFd};
macro_rules! retry_eintr {
($io_call: expr) => {
loop {
let r = $io_call;
if let Err(crate::VolatileMemoryError::IOError(ref err)) = r {
if err.kind() == std::io::ErrorKind::Interrupted {
continue;
}
}
break r;
}
};
}
pub(crate) use retry_eintr;
pub trait ReadVolatile {
fn read_volatile<B: BitmapSlice>(
&mut self,
buf: &mut VolatileSlice<B>,
) -> Result<usize, VolatileMemoryError>;
fn read_exact_volatile<B: BitmapSlice>(
&mut self,
buf: &mut VolatileSlice<B>,
) -> Result<(), VolatileMemoryError> {
let mut partial_buf = buf.offset(0)?;
while !partial_buf.is_empty() {
match retry_eintr!(self.read_volatile(&mut partial_buf)) {
Ok(0) => {
return Err(VolatileMemoryError::IOError(std::io::Error::new(
ErrorKind::UnexpectedEof,
"failed to fill whole buffer",
)))
}
Ok(bytes_read) => partial_buf = partial_buf.offset(bytes_read)?,
Err(err) => return Err(err),
}
}
Ok(())
}
}
pub trait WriteVolatile {
fn write_volatile<B: BitmapSlice>(
&mut self,
buf: &VolatileSlice<B>,
) -> Result<usize, VolatileMemoryError>;
fn write_all_volatile<B: BitmapSlice>(
&mut self,
buf: &VolatileSlice<B>,
) -> Result<(), VolatileMemoryError> {
let mut partial_buf = buf.offset(0)?;
while !partial_buf.is_empty() {
match retry_eintr!(self.write_volatile(&partial_buf)) {
Ok(0) => {
return Err(VolatileMemoryError::IOError(std::io::Error::new(
ErrorKind::WriteZero,
"failed to write whole buffer",
)))
}
Ok(bytes_written) => partial_buf = partial_buf.offset(bytes_written)?,
Err(err) => return Err(err),
}
}
Ok(())
}
}
macro_rules! impl_read_write_volatile_for_raw_fd {
($raw_fd_ty:ty) => {
#[cfg(feature = "rawfd")]
impl ReadVolatile for $raw_fd_ty {
fn read_volatile<B: BitmapSlice>(
&mut self,
buf: &mut VolatileSlice<B>,
) -> Result<usize, VolatileMemoryError> {
read_volatile_raw_fd(self.as_fd(), buf)
}
}
#[cfg(feature = "rawfd")]
impl ReadVolatile for &$raw_fd_ty {
fn read_volatile<B: BitmapSlice>(
&mut self,
buf: &mut VolatileSlice<B>,
) -> Result<usize, VolatileMemoryError> {
read_volatile_raw_fd(self.as_fd(), buf)
}
}
#[cfg(feature = "rawfd")]
impl ReadVolatile for &mut $raw_fd_ty {
fn read_volatile<B: BitmapSlice>(
&mut self,
buf: &mut VolatileSlice<B>,
) -> Result<usize, VolatileMemoryError> {
read_volatile_raw_fd(self.as_fd(), buf)
}
}
#[cfg(feature = "rawfd")]
impl WriteVolatile for $raw_fd_ty {
fn write_volatile<B: BitmapSlice>(
&mut self,
buf: &VolatileSlice<B>,
) -> Result<usize, VolatileMemoryError> {
write_volatile_raw_fd(self.as_fd(), buf)
}
}
#[cfg(feature = "rawfd")]
impl WriteVolatile for &$raw_fd_ty {
fn write_volatile<B: BitmapSlice>(
&mut self,
buf: &VolatileSlice<B>,
) -> Result<usize, VolatileMemoryError> {
write_volatile_raw_fd(self.as_fd(), buf)
}
}
#[cfg(feature = "rawfd")]
impl WriteVolatile for &mut $raw_fd_ty {
fn write_volatile<B: BitmapSlice>(
&mut self,
buf: &VolatileSlice<B>,
) -> Result<usize, VolatileMemoryError> {
write_volatile_raw_fd(self.as_fd(), buf)
}
}
};
}
#[cfg(feature = "rawfd")]
impl WriteVolatile for Stdout {
fn write_volatile<B: BitmapSlice>(
&mut self,
buf: &VolatileSlice<B>,
) -> Result<usize, VolatileMemoryError> {
write_volatile_raw_fd(self.as_fd(), buf)
}
}
#[cfg(feature = "rawfd")]
impl WriteVolatile for &Stdout {
fn write_volatile<B: BitmapSlice>(
&mut self,
buf: &VolatileSlice<B>,
) -> Result<usize, VolatileMemoryError> {
write_volatile_raw_fd(self.as_fd(), buf)
}
}
impl_read_write_volatile_for_raw_fd!(std::fs::File);
impl_read_write_volatile_for_raw_fd!(std::net::TcpStream);
impl_read_write_volatile_for_raw_fd!(std::os::unix::net::UnixStream);
impl_read_write_volatile_for_raw_fd!(std::os::fd::OwnedFd);
impl_read_write_volatile_for_raw_fd!(std::os::fd::BorrowedFd<'_>);
#[cfg(feature = "rawfd")]
fn read_volatile_raw_fd(
raw_fd: BorrowedFd<'_>,
buf: &mut VolatileSlice<impl BitmapSlice>,
) -> Result<usize, VolatileMemoryError> {
let fd = raw_fd.as_raw_fd();
let guard = buf.ptr_guard_mut();
let dst = guard.as_ptr().cast::<libc::c_void>();
let bytes_read = unsafe { libc::read(fd, dst, buf.len()) };
if bytes_read < 0 {
buf.bitmap().mark_dirty(0, buf.len());
Err(VolatileMemoryError::IOError(std::io::Error::last_os_error()))
} else {
let bytes_read = bytes_read.try_into().unwrap();
buf.bitmap().mark_dirty(0, bytes_read);
Ok(bytes_read)
}
}
#[cfg(feature = "rawfd")]
fn write_volatile_raw_fd(
raw_fd: BorrowedFd<'_>,
buf: &VolatileSlice<impl BitmapSlice>,
) -> Result<usize, VolatileMemoryError> {
let fd = raw_fd.as_raw_fd();
let guard = buf.ptr_guard();
let src = guard.as_ptr().cast::<libc::c_void>();
let bytes_written = unsafe { libc::write(fd, src, buf.len()) };
if bytes_written < 0 {
Err(VolatileMemoryError::IOError(std::io::Error::last_os_error()))
} else {
Ok(bytes_written.try_into().unwrap())
}
}
impl WriteVolatile for &mut [u8] {
fn write_volatile<B: BitmapSlice>(
&mut self,
buf: &VolatileSlice<B>,
) -> Result<usize, VolatileMemoryError> {
let total = buf.len().min(self.len());
let written = unsafe { copy_from_volatile_slice(self.as_mut_ptr(), buf, total) };
*self = std::mem::take(self).split_at_mut(written).1;
Ok(written)
}
fn write_all_volatile<B: BitmapSlice>(
&mut self,
buf: &VolatileSlice<B>,
) -> Result<(), VolatileMemoryError> {
if self.write_volatile(buf)? == buf.len() {
Ok(())
} else {
Err(VolatileMemoryError::IOError(std::io::Error::new(
ErrorKind::WriteZero,
"failed to write whole buffer",
)))
}
}
}
impl ReadVolatile for &[u8] {
fn read_volatile<B: BitmapSlice>(
&mut self,
buf: &mut VolatileSlice<B>,
) -> Result<usize, VolatileMemoryError> {
let total = buf.len().min(self.len());
let read = unsafe { copy_to_volatile_slice(buf, self.as_ptr(), total) };
*self = self.split_at(read).1;
Ok(read)
}
fn read_exact_volatile<B: BitmapSlice>(
&mut self,
buf: &mut VolatileSlice<B>,
) -> Result<(), VolatileMemoryError> {
if buf.len() > self.len() {
return Err(VolatileMemoryError::IOError(std::io::Error::new(
ErrorKind::UnexpectedEof,
"failed to fill whole buffer",
)));
}
self.read_volatile(buf).map(|_| ())
}
}
impl WriteVolatile for Vec<u8> {
fn write_volatile<B: BitmapSlice>(
&mut self,
buf: &VolatileSlice<B>,
) -> Result<usize, VolatileMemoryError> {
let count = buf.len();
self.reserve(count);
let len = self.len();
unsafe {
let copied_len = copy_from_volatile_slice(self.as_mut_ptr().add(len), buf, count);
assert_eq!(copied_len, count);
self.set_len(len + count);
}
Ok(count)
}
}
impl<T> ReadVolatile for Cursor<T>
where
T: AsRef<[u8]>,
{
fn read_volatile<B: BitmapSlice>(
&mut self,
buf: &mut VolatileSlice<B>,
) -> Result<usize, VolatileMemoryError> {
let inner = self.get_ref().as_ref();
let len = self.position().min(inner.len() as u64);
let n = ReadVolatile::read_volatile(&mut &inner[(len as usize)..], buf)?;
self.set_position(self.position() + n as u64);
Ok(n)
}
fn read_exact_volatile<B: BitmapSlice>(
&mut self,
buf: &mut VolatileSlice<B>,
) -> Result<(), VolatileMemoryError> {
let inner = self.get_ref().as_ref();
let n = buf.len();
let len = self.position().min(inner.len() as u64);
ReadVolatile::read_exact_volatile(&mut &inner[(len as usize)..], buf)?;
self.set_position(self.position() + n as u64);
Ok(())
}
}
impl WriteVolatile for Cursor<&mut [u8]> {
fn write_volatile<B: BitmapSlice>(
&mut self,
buf: &VolatileSlice<B>,
) -> Result<usize, VolatileMemoryError> {
let pos = self.position().min(self.get_ref().len() as u64);
let n = WriteVolatile::write_volatile(&mut &mut self.get_mut()[(pos as usize)..], buf)?;
self.set_position(self.position() + n as u64);
Ok(n)
}
}
#[cfg(test)]
mod tests {
use crate::io::{ReadVolatile, WriteVolatile};
use crate::{VolatileMemoryError, VolatileSlice};
use std::io::{Cursor, ErrorKind};
#[cfg(feature = "rawfd")]
use std::io::{Read, Seek, Write};
#[cfg(feature = "rawfd")]
use vmm_sys_util::tempfile::TempFile;
fn read_4_bytes_to_5_byte_memory(source: Vec<u8>, expected_output: [u8; 5]) {
let mut memory = vec![0u8; 5];
assert_eq!(
(&source[..])
.read_volatile(&mut VolatileSlice::from(&mut memory[..4]))
.unwrap(),
source.len().min(4)
);
assert_eq!(&memory, &expected_output);
let mut memory = vec![0u8; 5];
let result = (&source[..]).read_exact_volatile(&mut VolatileSlice::from(&mut memory[..4]));
if source.len() < 4 {
match result.unwrap_err() {
VolatileMemoryError::IOError(ioe) => {
assert_eq!(ioe.kind(), ErrorKind::UnexpectedEof)
}
err => panic!("{:?}", err),
}
assert_eq!(memory, vec![0u8; 5]);
} else {
result.unwrap();
assert_eq!(&memory, &expected_output);
}
}
#[cfg(all(feature = "rawfd", not(miri)))]
fn read_4_bytes_from_file(source: Vec<u8>, expected_output: [u8; 5]) {
let mut temp_file = TempFile::new().unwrap().into_file();
temp_file.write_all(source.as_ref()).unwrap();
temp_file.rewind().unwrap();
let mut memory = vec![0u8; 5];
assert_eq!(
temp_file
.read_volatile(&mut VolatileSlice::from(&mut memory[..4]))
.unwrap(),
source.len().min(4)
);
assert_eq!(&memory, &expected_output);
temp_file.rewind().unwrap();
let mut memory = vec![0u8; 5];
let read_exact_result =
temp_file.read_exact_volatile(&mut VolatileSlice::from(&mut memory[..4]));
if source.len() < 4 {
read_exact_result.unwrap_err();
} else {
read_exact_result.unwrap();
}
assert_eq!(&memory, &expected_output);
}
#[test]
fn test_read_volatile() {
let test_cases = [
(vec![1u8, 2], [1u8, 2, 0, 0, 0]),
(vec![1, 2, 3, 4], [1, 2, 3, 4, 0]),
(vec![5, 6, 7, 8, 9], [5, 6, 7, 8, 0]),
];
for (input, output) in test_cases {
read_4_bytes_to_5_byte_memory(input.clone(), output);
#[cfg(all(feature = "rawfd", not(miri)))]
read_4_bytes_from_file(input, output);
}
}
fn write_4_bytes_to_5_byte_vec(mut source: Vec<u8>, expected_result: [u8; 5]) {
let mut memory = vec![0u8; 5];
assert_eq!(
(&mut memory[..4])
.write_volatile(&VolatileSlice::from(source.as_mut_slice()))
.unwrap(),
source.len().min(4)
);
assert_eq!(&memory, &expected_result);
let mut memory = vec![0u8; 5];
let result =
(&mut memory[..4]).write_all_volatile(&VolatileSlice::from(source.as_mut_slice()));
if source.len() > 4 {
match result.unwrap_err() {
VolatileMemoryError::IOError(ioe) => {
assert_eq!(ioe.kind(), ErrorKind::WriteZero)
}
err => panic!("{:?}", err),
}
assert_eq!(&memory, &expected_result);
} else {
result.unwrap();
assert_eq!(&memory, &expected_result);
}
}
#[cfg(all(feature = "rawfd", not(miri)))]
fn write_5_bytes_to_file(mut source: Vec<u8>) {
let mut temp_file = TempFile::new().unwrap().into_file();
temp_file
.write_volatile(&VolatileSlice::from(source.as_mut_slice()))
.unwrap();
temp_file.rewind().unwrap();
let mut written = vec![0u8; source.len()];
temp_file.read_exact(written.as_mut_slice()).unwrap();
assert_eq!(source, written);
assert_eq!(temp_file.read(&mut [0u8]).unwrap(), 0);
let mut temp_file = TempFile::new().unwrap().into_file();
temp_file
.write_all_volatile(&VolatileSlice::from(source.as_mut_slice()))
.unwrap();
temp_file.rewind().unwrap();
let mut written = vec![0u8; source.len()];
temp_file.read_exact(written.as_mut_slice()).unwrap();
assert_eq!(source, written);
assert_eq!(temp_file.read(&mut [0u8]).unwrap(), 0);
}
#[test]
fn test_write_volatile() {
let test_cases = [
(vec![1u8, 2], [1u8, 2, 0, 0, 0]),
(vec![1, 2, 3, 4], [1, 2, 3, 4, 0]),
(vec![5, 6, 7, 8, 9], [5, 6, 7, 8, 0]),
];
for (input, output) in test_cases {
write_4_bytes_to_5_byte_vec(input.clone(), output);
#[cfg(all(feature = "rawfd", not(miri)))]
write_5_bytes_to_file(input);
}
}
#[test]
fn test_read_volatile_for_cursor() {
let read_buffer = [1, 2, 3, 4, 5, 6, 7];
let mut output = vec![0u8; 5];
let mut cursor = Cursor::new(read_buffer);
assert_eq!(
cursor
.read_volatile(&mut VolatileSlice::from(&mut output[..4]))
.unwrap(),
4
);
assert_eq!(output, vec![1, 2, 3, 4, 0]);
assert_eq!(
cursor
.read_volatile(&mut VolatileSlice::from(&mut output[..4]))
.unwrap(),
3
);
assert_eq!(output, vec![5, 6, 7, 4, 0]);
cursor.set_position(0);
cursor
.read_exact_volatile(&mut VolatileSlice::from(&mut output[..4]))
.unwrap();
assert_eq!(output, vec![1, 2, 3, 4, 0]);
assert!(cursor
.read_exact_volatile(&mut VolatileSlice::from(&mut output[..4]))
.is_err());
assert_eq!(output, vec![1, 2, 3, 4, 0]);
}
#[test]
fn test_write_volatile_for_cursor() {
let mut write_buffer = vec![0u8; 7];
let mut input = [1, 2, 3, 4];
let mut cursor = Cursor::new(write_buffer.as_mut_slice());
assert_eq!(
cursor
.write_volatile(&VolatileSlice::from(input.as_mut_slice()))
.unwrap(),
4
);
assert_eq!(cursor.get_ref(), &[1, 2, 3, 4, 0, 0, 0]);
assert_eq!(
cursor
.write_volatile(&VolatileSlice::from(input.as_mut_slice()))
.unwrap(),
3
);
assert_eq!(cursor.get_ref(), &[1, 2, 3, 4, 1, 2, 3]);
}
#[test]
fn test_write_volatile_for_vec() {
let mut write_buffer = Vec::new();
let mut input = [1, 2, 3, 4];
assert_eq!(
write_buffer
.write_volatile(&VolatileSlice::from(input.as_mut_slice()))
.unwrap(),
4
);
assert_eq!(&write_buffer, &input);
}
}