use std::any::Any;
use std::collections::VecDeque;
use std::io::{self, IoSlice, Read};
use std::marker::PhantomData;
use std::mem::{size_of, MaybeUninit};
use std::ptr::copy_nonoverlapping;
use std::{cmp, fmt};
use lazy_static::lazy_static;
use libc::{sysconf, _SC_PAGESIZE};
use vm_memory::{ByteValued, VolatileSlice};
#[cfg(feature = "async-io")]
use crate::file_buf::FileVolatileBuf;
use crate::file_buf::FileVolatileSlice;
#[cfg(feature = "async-io")]
use crate::file_traits::AsyncFileReadWriteVolatile;
use crate::file_traits::FileReadWriteVolatile;
use crate::BitmapSlice;
mod fs_cache_req_handler;
#[cfg(feature = "fusedev")]
mod fusedev;
#[cfg(feature = "virtiofs")]
mod virtiofs;
pub use self::fs_cache_req_handler::FsCacheReqHandler;
#[cfg(feature = "fusedev")]
pub use self::fusedev::{FuseBuf, FuseChannel, FuseDevWriter, FuseSession, FuseSessionExt};
#[cfg(feature = "virtiofs")]
pub use self::virtiofs::VirtioFsWriter;
#[derive(Debug)]
pub enum Error {
DescriptorChainOverflow,
FindMemoryRegion,
InvalidChain,
InvalidParameter,
IoError(io::Error),
SplitOutOfBounds(usize),
VolatileMemoryError(vm_memory::VolatileMemoryError),
#[cfg(feature = "fusedev")]
SessionFailure(String),
#[cfg(feature = "virtiofs")]
GuestMemoryError(vm_memory::GuestMemoryError),
#[cfg(feature = "virtiofs")]
ConvertIndirectDescriptor(virtio_queue::Error),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::Error::*;
match self {
DescriptorChainOverflow => write!(
f,
"the combined length of all the buffers in a `DescriptorChain` would overflow"
),
FindMemoryRegion => write!(f, "no memory region for this address range"),
InvalidChain => write!(f, "invalid descriptor chain"),
InvalidParameter => write!(f, "invalid parameter"),
IoError(e) => write!(f, "descriptor I/O error: {e}"),
SplitOutOfBounds(off) => write!(f, "`DescriptorChain` split is out of bounds: {off}"),
VolatileMemoryError(e) => write!(f, "volatile memory error: {e}"),
#[cfg(feature = "fusedev")]
SessionFailure(e) => write!(f, "fuse session failure: {e}"),
#[cfg(feature = "virtiofs")]
ConvertIndirectDescriptor(e) => write!(f, "invalid indirect descriptor: {e}"),
#[cfg(feature = "virtiofs")]
GuestMemoryError(e) => write!(f, "descriptor guest memory error: {e}"),
}
}
}
impl From<Box<dyn Any + Send>> for Error {
fn from(value: Box<dyn Any + Send>) -> Self {
let err = value.downcast::<Error>().unwrap();
*err
}
}
pub type Result<T> = std::result::Result<T, Error>;
impl std::error::Error for Error {}
#[derive(Clone)]
struct IoBuffers<'a, S> {
buffers: VecDeque<VolatileSlice<'a, S>>,
bytes_consumed: usize,
}
impl<S: BitmapSlice> Default for IoBuffers<'_, S> {
fn default() -> Self {
IoBuffers {
buffers: VecDeque::new(),
bytes_consumed: 0,
}
}
}
impl<S: BitmapSlice> IoBuffers<'_, S> {
fn available_bytes(&self) -> usize {
self.buffers
.iter()
.fold(0usize, |count, buf| count + buf.len())
}
fn bytes_consumed(&self) -> usize {
self.bytes_consumed
}
fn allocate_file_volatile_slice(&self, count: usize) -> Vec<FileVolatileSlice<'_>> {
let mut rem = count;
let mut bufs: Vec<FileVolatileSlice> = Vec::with_capacity(self.buffers.len());
for buf in &self.buffers {
if rem == 0 {
break;
}
let local_buf = if buf.len() > rem {
FileVolatileSlice::from_volatile_slice(&buf.subslice(0, rem).unwrap())
} else {
FileVolatileSlice::from_volatile_slice(buf)
};
bufs.push(local_buf);
rem -= local_buf.len();
}
bufs
}
#[cfg(feature = "async-io")]
unsafe fn prepare_io_buf(&self, count: usize) -> Vec<FileVolatileBuf> {
let mut rem = count;
let mut bufs = Vec::with_capacity(self.buffers.len());
for buf in &self.buffers {
if rem == 0 {
break;
}
let local_buf = if buf.len() > rem {
buf.subslice(0, rem).unwrap()
} else {
buf.clone()
};
bufs.push(FileVolatileBuf::from_raw_ptr(
local_buf.ptr_guard_mut().as_ptr(),
local_buf.len(),
local_buf.len(),
));
rem -= local_buf.len() as usize;
}
bufs
}
#[cfg(all(feature = "async-io", feature = "virtiofs"))]
unsafe fn prepare_mut_io_buf(&self, count: usize) -> Vec<FileVolatileBuf> {
let mut rem = count;
let mut bufs = Vec::with_capacity(self.buffers.len());
for buf in &self.buffers {
if rem == 0 {
break;
}
let local_buf = if buf.len() > rem {
buf.subslice(0, rem).unwrap()
} else {
buf.clone()
};
bufs.push(FileVolatileBuf::from_raw_ptr(
local_buf.ptr_guard_mut().as_ptr(),
0,
local_buf.len(),
));
rem -= local_buf.len() as usize;
}
bufs
}
fn mark_dirty(&self, count: usize) {
let mut rem = count;
for buf in &self.buffers {
if rem == 0 {
break;
}
let local_buf = if buf.len() > rem {
buf.subslice(0, rem).unwrap()
} else {
buf.clone()
};
local_buf.bitmap().mark_dirty(0, local_buf.len());
rem -= local_buf.len();
}
}
fn mark_used(&mut self, bytes_consumed: usize) -> io::Result<()> {
let total_bytes_consumed =
self.bytes_consumed
.checked_add(bytes_consumed)
.ok_or_else(|| {
io::Error::new(io::ErrorKind::InvalidData, Error::DescriptorChainOverflow)
})?;
let mut rem = bytes_consumed;
while let Some(buf) = self.buffers.pop_front() {
if rem < buf.len() {
self.buffers.push_front(buf.offset(rem).unwrap());
break;
}
rem -= buf.len();
}
self.bytes_consumed = total_bytes_consumed;
Ok(())
}
fn consume<F>(&mut self, mark_dirty: bool, count: usize, f: F) -> io::Result<usize>
where
F: FnOnce(&[FileVolatileSlice]) -> io::Result<usize>,
{
let bufs = self.allocate_file_volatile_slice(count);
if bufs.is_empty() {
Ok(0)
} else {
let bytes_consumed = f(&bufs)?;
if mark_dirty {
self.mark_dirty(bytes_consumed);
}
self.mark_used(bytes_consumed)?;
Ok(bytes_consumed)
}
}
fn consume_for_read<F>(&mut self, count: usize, f: F) -> io::Result<usize>
where
F: FnOnce(&[FileVolatileSlice]) -> io::Result<usize>,
{
self.consume(false, count, f)
}
fn split_at(&mut self, offset: usize) -> Result<Self> {
let mut rem = offset;
let pos = self.buffers.iter().position(|buf| {
if rem < buf.len() {
true
} else {
rem -= buf.len();
false
}
});
if let Some(at) = pos {
let mut other = self.buffers.split_off(at);
if rem > 0 {
let front = other.pop_front().expect("empty VecDeque after split");
self.buffers
.push_back(front.subslice(0, rem).map_err(Error::VolatileMemoryError)?);
other.push_front(front.offset(rem).map_err(Error::VolatileMemoryError)?);
}
Ok(IoBuffers {
buffers: other,
bytes_consumed: 0,
})
} else if rem == 0 {
Ok(IoBuffers {
buffers: VecDeque::new(),
bytes_consumed: 0,
})
} else {
Err(Error::SplitOutOfBounds(offset))
}
}
}
#[derive(Clone)]
pub struct Reader<'a, S = ()> {
buffers: IoBuffers<'a, S>,
}
impl<S: BitmapSlice> Default for Reader<'_, S> {
fn default() -> Self {
Reader {
buffers: IoBuffers::default(),
}
}
}
impl<S: BitmapSlice> Reader<'_, S> {
pub fn read_obj<T: ByteValued>(&mut self) -> io::Result<T> {
let mut obj = MaybeUninit::<T>::uninit();
let buf = unsafe {
::std::slice::from_raw_parts_mut(obj.as_mut_ptr() as *mut u8, size_of::<T>())
};
self.read_exact(buf)?;
Ok(unsafe { obj.assume_init() })
}
pub fn read_to<F: FileReadWriteVolatile>(
&mut self,
mut dst: F,
count: usize,
) -> io::Result<usize> {
self.buffers
.consume_for_read(count, |bufs| dst.write_vectored_volatile(bufs))
}
pub fn read_to_at<F: FileReadWriteVolatile>(
&mut self,
mut dst: F,
count: usize,
off: u64,
) -> io::Result<usize> {
self.buffers
.consume_for_read(count, |bufs| dst.write_vectored_at_volatile(bufs, off))
}
pub fn read_exact_to<F: FileReadWriteVolatile>(
&mut self,
mut dst: F,
mut count: usize,
) -> io::Result<()> {
while count > 0 {
match self.read_to(&mut dst, count) {
Ok(0) => {
return Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
"failed to fill whole buffer",
))
}
Ok(n) => count -= n,
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
Err(e) => return Err(e),
}
}
Ok(())
}
pub fn available_bytes(&self) -> usize {
self.buffers.available_bytes()
}
pub fn bytes_read(&self) -> usize {
self.buffers.bytes_consumed()
}
pub fn split_at(&mut self, offset: usize) -> Result<Self> {
self.buffers
.split_at(offset)
.map(|buffers| Reader { buffers })
}
}
impl<S: BitmapSlice> io::Read for Reader<'_, S> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.buffers.consume_for_read(buf.len(), |bufs| {
let mut rem = buf;
let mut total = 0;
for buf in bufs {
let copy_len = cmp::min(rem.len(), buf.len());
unsafe {
copy_nonoverlapping(buf.as_ptr() as *const u8, rem.as_mut_ptr(), copy_len);
}
rem = &mut rem[copy_len..];
total += copy_len;
}
Ok(total)
})
}
}
#[cfg(feature = "async-io")]
mod async_io {
use super::*;
impl<'a, S: BitmapSlice> Reader<'a, S> {
pub async fn async_read_to_at<F: AsyncFileReadWriteVolatile>(
&mut self,
dst: &F,
count: usize,
off: u64,
) -> io::Result<usize> {
let bufs = unsafe { self.buffers.prepare_io_buf(count) };
if bufs.is_empty() {
Ok(0)
} else {
let (res, _) = dst.async_write_vectored_at_volatile(bufs, off).await;
match res {
Ok(cnt) => {
self.buffers.mark_used(cnt)?;
Ok(cnt)
}
Err(e) => Err(e),
}
}
}
}
}
pub enum Writer<'a, S: BitmapSlice = ()> {
#[cfg(feature = "fusedev")]
FuseDev(FuseDevWriter<'a, S>),
#[cfg(feature = "virtiofs")]
VirtioFs(VirtioFsWriter<'a, S>),
Noop(PhantomData<&'a S>),
}
impl<S: BitmapSlice> Writer<'_, S> {
pub fn write_from_at<F: FileReadWriteVolatile>(
&mut self,
src: F,
count: usize,
off: u64,
) -> io::Result<usize> {
match self {
#[cfg(feature = "fusedev")]
Writer::FuseDev(w) => w.write_from_at(src, count, off),
#[cfg(feature = "virtiofs")]
Writer::VirtioFs(w) => w.write_from_at(src, count, off),
_ => Err(std::io::Error::from_raw_os_error(libc::EINVAL)),
}
}
pub fn split_at(&mut self, offset: usize) -> Result<Self> {
match self {
#[cfg(feature = "fusedev")]
Writer::FuseDev(w) => w.split_at(offset).map(|w| w.into()),
#[cfg(feature = "virtiofs")]
Writer::VirtioFs(w) => w.split_at(offset).map(|w| w.into()),
_ => Err(Error::InvalidParameter),
}
}
pub fn available_bytes(&self) -> usize {
match self {
#[cfg(feature = "fusedev")]
Writer::FuseDev(w) => w.available_bytes(),
#[cfg(feature = "virtiofs")]
Writer::VirtioFs(w) => w.available_bytes(),
_ => 0,
}
}
pub fn bytes_written(&self) -> usize {
match self {
#[cfg(feature = "fusedev")]
Writer::FuseDev(w) => w.bytes_written(),
#[cfg(feature = "virtiofs")]
Writer::VirtioFs(w) => w.bytes_written(),
_ => 0,
}
}
pub fn commit(&mut self, other: Option<&Self>) -> io::Result<usize> {
match self {
#[cfg(feature = "fusedev")]
Writer::FuseDev(w) => w.commit(other),
#[cfg(feature = "virtiofs")]
Writer::VirtioFs(w) => w.commit(other),
_ => Ok(0),
}
}
}
impl<S: BitmapSlice> io::Write for Writer<'_, S> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
match self {
#[cfg(feature = "fusedev")]
Writer::FuseDev(w) => w.write(buf),
#[cfg(feature = "virtiofs")]
Writer::VirtioFs(w) => w.write(buf),
_ => Err(std::io::Error::from_raw_os_error(libc::EINVAL)),
}
}
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
match self {
#[cfg(feature = "fusedev")]
Writer::FuseDev(w) => w.write_vectored(bufs),
#[cfg(feature = "virtiofs")]
Writer::VirtioFs(w) => w.write_vectored(bufs),
_ => Err(std::io::Error::from_raw_os_error(libc::EINVAL)),
}
}
fn flush(&mut self) -> io::Result<()> {
match self {
#[cfg(feature = "fusedev")]
Writer::FuseDev(w) => w.flush(),
#[cfg(feature = "virtiofs")]
Writer::VirtioFs(w) => w.flush(),
_ => Ok(()),
}
}
}
#[cfg(feature = "async-io")]
impl<'a, S: BitmapSlice> Writer<'a, S> {
pub async fn async_write(&mut self, data: &[u8]) -> io::Result<usize> {
match self {
#[cfg(feature = "fusedev")]
Writer::FuseDev(w) => w.async_write(data).await,
#[cfg(feature = "virtiofs")]
Writer::VirtioFs(w) => w.async_write(data).await,
_ => Err(std::io::Error::from_raw_os_error(libc::EINVAL)),
}
}
pub async fn async_write2(&mut self, data: &[u8], data2: &[u8]) -> io::Result<usize> {
match self {
#[cfg(feature = "fusedev")]
Writer::FuseDev(w) => w.async_write2(data, data2).await,
#[cfg(feature = "virtiofs")]
Writer::VirtioFs(w) => w.async_write2(data, data2).await,
_ => Err(std::io::Error::from_raw_os_error(libc::EINVAL)),
}
}
pub async fn async_write3(
&mut self,
data: &[u8],
data2: &[u8],
data3: &[u8],
) -> io::Result<usize> {
match self {
#[cfg(feature = "fusedev")]
Writer::FuseDev(w) => w.async_write3(data, data2, data3).await,
#[cfg(feature = "virtiofs")]
Writer::VirtioFs(w) => w.async_write3(data, data2, data3).await,
_ => Err(std::io::Error::from_raw_os_error(libc::EINVAL)),
}
}
pub async fn async_write_all(&mut self, buf: &[u8]) -> io::Result<()> {
match self {
#[cfg(feature = "fusedev")]
Writer::FuseDev(w) => w.async_write_all(buf).await,
#[cfg(feature = "virtiofs")]
Writer::VirtioFs(w) => w.async_write_all(buf).await,
_ => Err(std::io::Error::from_raw_os_error(libc::EINVAL)),
}
}
pub async fn async_write_from_at<F: AsyncFileReadWriteVolatile>(
&mut self,
src: &F,
count: usize,
off: u64,
) -> io::Result<usize> {
match self {
#[cfg(feature = "fusedev")]
Writer::FuseDev(w) => w.async_write_from_at(src, count, off).await,
#[cfg(feature = "virtiofs")]
Writer::VirtioFs(w) => w.async_write_from_at(src, count, off).await,
_ => Err(std::io::Error::from_raw_os_error(libc::EINVAL)),
}
}
pub async fn async_commit(&mut self, other: Option<&Writer<'a, S>>) -> io::Result<usize> {
match self {
#[cfg(feature = "fusedev")]
Writer::FuseDev(w) => w.async_commit(other).await,
#[cfg(feature = "virtiofs")]
Writer::VirtioFs(w) => w.async_commit(other).await,
_ => Err(std::io::Error::from_raw_os_error(libc::EINVAL)),
}
}
}
#[cfg(feature = "fusedev")]
impl<'a, S: BitmapSlice> From<FuseDevWriter<'a, S>> for Writer<'a, S> {
fn from(w: FuseDevWriter<'a, S>) -> Self {
Writer::FuseDev(w)
}
}
#[cfg(feature = "virtiofs")]
impl<'a, S: BitmapSlice> From<VirtioFsWriter<'a, S>> for Writer<'a, S> {
fn from(w: VirtioFsWriter<'a, S>) -> Self {
Writer::VirtioFs(w)
}
}
lazy_static! {
static ref PAGESIZE: usize = unsafe { sysconf(_SC_PAGESIZE) as usize };
}
#[inline(always)]
pub fn pagesize() -> usize {
*PAGESIZE
}
#[cfg(test)]
mod tests {
use crate::transport::IoBuffers;
use std::collections::VecDeque;
use vm_memory::{
bitmap::{AtomicBitmap, Bitmap},
VolatileSlice,
};
#[test]
fn test_io_buffers() {
let mut buf1 = vec![0x0u8; 16];
let mut buf2 = vec![0x0u8; 16];
let mut bufs = VecDeque::new();
unsafe {
bufs.push_back(VolatileSlice::new(buf1.as_mut_ptr(), buf1.len()));
bufs.push_back(VolatileSlice::new(buf2.as_mut_ptr(), buf2.len()));
}
let mut buffers = IoBuffers {
buffers: bufs,
bytes_consumed: 0,
};
assert_eq!(buffers.available_bytes(), 32);
assert_eq!(buffers.bytes_consumed(), 0);
assert_eq!(
buffers.consume_for_read(2, |buf| Ok(buf[0].len())).unwrap(),
2
);
assert_eq!(buffers.available_bytes(), 30);
assert_eq!(buffers.bytes_consumed(), 2);
let mut buffers2 = buffers.split_at(10).unwrap();
assert_eq!(buffers.available_bytes(), 10);
assert_eq!(buffers.bytes_consumed(), 2);
assert_eq!(buffers2.available_bytes(), 20);
assert_eq!(buffers2.bytes_consumed(), 0);
assert_eq!(
buffers2
.consume_for_read(10, |buf| Ok(buf[0].len() + buf[1].len()))
.unwrap(),
10
);
assert_eq!(
buffers2
.consume_for_read(20, |buf| Ok(buf[0].len()))
.unwrap(),
10
);
let _buffers3 = buffers2.split_at(0).unwrap();
assert!(buffers2.split_at(1).is_err());
}
#[test]
fn test_mark_dirty() {
let mut buf1 = vec![0x0u8; 16];
let bitmap1 = AtomicBitmap::new(16, std::num::NonZero::new(2).unwrap());
assert_eq!(bitmap1.len(), 8);
for i in 0..8 {
assert_eq!(bitmap1.is_bit_set(i), false);
}
let mut buf2 = vec![0x0u8; 16];
let bitmap2 = AtomicBitmap::new(16, std::num::NonZero::new(2).unwrap());
let mut bufs = VecDeque::new();
unsafe {
bufs.push_back(VolatileSlice::with_bitmap(
buf1.as_mut_ptr(),
buf1.len(),
bitmap1.slice_at(0),
None,
));
bufs.push_back(VolatileSlice::with_bitmap(
buf2.as_mut_ptr(),
buf2.len(),
bitmap2.slice_at(0),
None,
));
}
let mut buffers = IoBuffers {
buffers: bufs,
bytes_consumed: 0,
};
assert_eq!(buffers.available_bytes(), 32);
assert_eq!(buffers.bytes_consumed(), 0);
assert_eq!(
buffers.consume_for_read(8, |buf| Ok(buf[0].len())).unwrap(),
8
);
assert_eq!(buffers.available_bytes(), 24);
assert_eq!(buffers.bytes_consumed(), 8);
for i in 0..8 {
assert_eq!(bitmap1.is_bit_set(i), false);
}
assert_eq!(
buffers
.consume(true, 16, |buf| Ok(buf[0].len() + buf[1].len()))
.unwrap(),
16
);
assert_eq!(buffers.available_bytes(), 8);
assert_eq!(buffers.bytes_consumed(), 24);
for i in 0..8 {
if i >= 4 {
assert_eq!(bitmap1.is_bit_set(i), true);
continue;
} else {
assert_eq!(bitmap1.is_bit_set(i), false);
}
}
for i in 0..8 {
if i < 4 {
assert_eq!(bitmap2.is_bit_set(i), true);
} else {
assert_eq!(bitmap2.is_bit_set(i), false);
}
}
}
}