mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2024-08-23 00:11:16 +00:00
vhost_user_fs: Add virtio descriptor helper traits
Introduce helpers to split a virtio descriptor into its readable part on one side, and into its writable part on the other side. This is useful to separate the request from the reply. This code has been ported over from crosvm commit 961461350c0b6824e5f20655031bf6c6bf6b7c30. Two important modifications have been applied to the original code: - GuestMemory is replaced by GuestMemoryMmap from the vm-memory crate, which comes with different ways of accessing the memory regions. - VolatileSlice has different methods, which means the code has been updated accordingly. Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
This commit is contained in:
parent
5f7935f8e0
commit
e1fccc3615
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -1017,6 +1017,7 @@ dependencies = [
|
|||||||
"libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"vm-memory 0.1.0 (git+https://github.com/rust-vmm/vm-memory)",
|
"vm-memory 0.1.0 (git+https://github.com/rust-vmm/vm-memory)",
|
||||||
|
"vm-virtio 0.1.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -9,3 +9,4 @@ bitflags = "1.1.0"
|
|||||||
libc = "0.2.65"
|
libc = "0.2.65"
|
||||||
log = "0.4.8"
|
log = "0.4.8"
|
||||||
vm-memory = { git = "https://github.com/rust-vmm/vm-memory" }
|
vm-memory = { git = "https://github.com/rust-vmm/vm-memory" }
|
||||||
|
vm-virtio = { path = "../vm-virtio" }
|
||||||
|
985
vhost_user_fs/src/descriptor_utils.rs
Normal file
985
vhost_user_fs/src/descriptor_utils.rs
Normal file
@ -0,0 +1,985 @@
|
|||||||
|
// Copyright 2019 The Chromium OS Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
use std::cmp;
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
use std::fmt::{self, Display};
|
||||||
|
use std::io::{self, Read, Write};
|
||||||
|
use std::mem::{size_of, MaybeUninit};
|
||||||
|
use std::ops::Deref;
|
||||||
|
use std::ptr::copy_nonoverlapping;
|
||||||
|
use std::result;
|
||||||
|
|
||||||
|
use vm_memory::{
|
||||||
|
Address, ByteValued, Bytes, GuestAddress, GuestMemory, GuestMemoryError, GuestMemoryMmap,
|
||||||
|
GuestMemoryRegion, Le16, Le32, Le64, VolatileMemory, VolatileMemoryError, VolatileSlice,
|
||||||
|
};
|
||||||
|
use vm_virtio::DescriptorChain;
|
||||||
|
|
||||||
|
use crate::file_traits::{FileReadWriteAtVolatile, FileReadWriteVolatile};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
DescriptorChainOverflow,
|
||||||
|
FindMemoryRegion,
|
||||||
|
GuestMemoryError(GuestMemoryError),
|
||||||
|
InvalidChain,
|
||||||
|
IoError(io::Error),
|
||||||
|
SplitOutOfBounds(usize),
|
||||||
|
VolatileMemoryError(VolatileMemoryError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl 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"),
|
||||||
|
GuestMemoryError(e) => write!(f, "descriptor guest memory error: {}", e),
|
||||||
|
InvalidChain => write!(f, "invalid descriptor chain"),
|
||||||
|
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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Result<T> = result::Result<T, Error>;
|
||||||
|
|
||||||
|
impl std::error::Error for Error {}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct DescriptorChainConsumer<'a> {
|
||||||
|
buffers: VecDeque<VolatileSlice<'a>>,
|
||||||
|
bytes_consumed: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> DescriptorChainConsumer<'a> {
|
||||||
|
fn available_bytes(&self) -> usize {
|
||||||
|
// This is guaranteed not to overflow because the total length of the chain
|
||||||
|
// is checked during all creations of `DescriptorChainConsumer` (see
|
||||||
|
// `Reader::new()` and `Writer::new()`).
|
||||||
|
self.buffers
|
||||||
|
.iter()
|
||||||
|
.fold(0usize, |count, vs| count + vs.len() as usize)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bytes_consumed(&self) -> usize {
|
||||||
|
self.bytes_consumed
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Consumes at most `count` bytes from the `DescriptorChain`. Callers must provide a function
|
||||||
|
/// that takes a `&[VolatileSlice]` and returns the total number of bytes consumed. This
|
||||||
|
/// function guarantees that the combined length of all the slices in the `&[VolatileSlice]` is
|
||||||
|
/// less than or equal to `count`.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// If the provided function returns any error then no bytes are consumed from the buffer and
|
||||||
|
/// the error is returned to the caller.
|
||||||
|
fn consume<F>(&mut self, count: usize, f: F) -> io::Result<usize>
|
||||||
|
where
|
||||||
|
F: FnOnce(&[VolatileSlice]) -> io::Result<usize>,
|
||||||
|
{
|
||||||
|
let mut buflen = 0;
|
||||||
|
let mut bufs = Vec::with_capacity(self.buffers.len());
|
||||||
|
for &vs in &self.buffers {
|
||||||
|
if buflen >= count {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
bufs.push(vs);
|
||||||
|
|
||||||
|
let rem = count - buflen;
|
||||||
|
if rem < vs.len() {
|
||||||
|
buflen += rem;
|
||||||
|
} else {
|
||||||
|
buflen += vs.len() as usize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if bufs.is_empty() {
|
||||||
|
return Ok(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
let bytes_consumed = f(&*bufs)?;
|
||||||
|
|
||||||
|
// This can happen if a driver tricks a device into reading/writing more data than
|
||||||
|
// fits in a `usize`.
|
||||||
|
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(vs) = self.buffers.pop_front() {
|
||||||
|
if rem < vs.len() {
|
||||||
|
// Split the slice and push the remainder back into the buffer list. Safe because we
|
||||||
|
// know that `rem` is not out of bounds due to the check and we checked the bounds
|
||||||
|
// on `vs` when we added it to the buffer list.
|
||||||
|
self.buffers.push_front(vs.offset(rem).unwrap());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No need for checked math because we know that `vs.size() <= rem`.
|
||||||
|
rem -= vs.len();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.bytes_consumed = total_bytes_consumed;
|
||||||
|
|
||||||
|
Ok(bytes_consumed)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn split_at(&mut self, offset: usize) -> Result<DescriptorChainConsumer<'a>> {
|
||||||
|
let mut rem = offset;
|
||||||
|
let pos = self.buffers.iter().position(|vs| {
|
||||||
|
if rem < vs.len() {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
rem -= vs.len();
|
||||||
|
false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(at) = pos {
|
||||||
|
let mut other = self.buffers.split_off(at);
|
||||||
|
|
||||||
|
if rem > 0 {
|
||||||
|
// There must be at least one element in `other` because we checked
|
||||||
|
// its `size` value in the call to `position` above.
|
||||||
|
let front = other.pop_front().expect("empty VecDeque after split");
|
||||||
|
self.buffers
|
||||||
|
.push_back(front.offset(rem).map_err(Error::VolatileMemoryError)?);
|
||||||
|
other.push_front(front.offset(rem).map_err(Error::VolatileMemoryError)?);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(DescriptorChainConsumer {
|
||||||
|
buffers: other,
|
||||||
|
bytes_consumed: 0,
|
||||||
|
})
|
||||||
|
} else if rem == 0 {
|
||||||
|
Ok(DescriptorChainConsumer {
|
||||||
|
buffers: VecDeque::new(),
|
||||||
|
bytes_consumed: 0,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(Error::SplitOutOfBounds(offset))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Provides high-level interface over the sequence of memory regions
|
||||||
|
/// defined by readable descriptors in the descriptor chain.
|
||||||
|
///
|
||||||
|
/// Note that virtio spec requires driver to place any device-writable
|
||||||
|
/// descriptors after any device-readable descriptors (2.6.4.2 in Virtio Spec v1.1).
|
||||||
|
/// Reader will skip iterating over descriptor chain when first writable
|
||||||
|
/// descriptor is encountered.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Reader<'a> {
|
||||||
|
buffer: DescriptorChainConsumer<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Reader<'a> {
|
||||||
|
/// Construct a new Reader wrapper over `desc_chain`.
|
||||||
|
pub fn new(mem: &'a GuestMemoryMmap, desc_chain: DescriptorChain<'a>) -> Result<Reader<'a>> {
|
||||||
|
let mut total_len: usize = 0;
|
||||||
|
let buffers = desc_chain
|
||||||
|
.into_iter()
|
||||||
|
.readable()
|
||||||
|
.map(|desc| {
|
||||||
|
// Verify that summing the descriptor sizes does not overflow.
|
||||||
|
// This can happen if a driver tricks a device into reading more data than
|
||||||
|
// fits in a `usize`.
|
||||||
|
total_len = total_len
|
||||||
|
.checked_add(desc.len as usize)
|
||||||
|
.ok_or(Error::DescriptorChainOverflow)?;
|
||||||
|
|
||||||
|
let region = mem.find_region(desc.addr).ok_or(Error::FindMemoryRegion)?;
|
||||||
|
let offset = desc
|
||||||
|
.addr
|
||||||
|
.checked_sub(region.start_addr().raw_value())
|
||||||
|
.unwrap();
|
||||||
|
region
|
||||||
|
.deref()
|
||||||
|
.get_slice(offset.raw_value() as usize, desc.len as usize)
|
||||||
|
.map_err(Error::VolatileMemoryError)
|
||||||
|
})
|
||||||
|
.collect::<Result<VecDeque<VolatileSlice<'a>>>>()?;
|
||||||
|
Ok(Reader {
|
||||||
|
buffer: DescriptorChainConsumer {
|
||||||
|
buffers,
|
||||||
|
bytes_consumed: 0,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads an object from the descriptor chain buffer.
|
||||||
|
pub fn read_obj<T: ByteValued>(&mut self) -> io::Result<T> {
|
||||||
|
let mut obj = MaybeUninit::<T>::uninit();
|
||||||
|
|
||||||
|
// Safe because `MaybeUninit` guarantees that the pointer is valid for
|
||||||
|
// `size_of::<T>()` bytes.
|
||||||
|
let buf = unsafe {
|
||||||
|
::std::slice::from_raw_parts_mut(obj.as_mut_ptr() as *mut u8, size_of::<T>())
|
||||||
|
};
|
||||||
|
|
||||||
|
self.read_exact(buf)?;
|
||||||
|
|
||||||
|
// Safe because any type that implements `ByteValued` can be considered initialized
|
||||||
|
// even if it is filled with random data.
|
||||||
|
Ok(unsafe { obj.assume_init() })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads data from the descriptor chain buffer into a file descriptor.
|
||||||
|
/// Returns the number of bytes read from the descriptor chain buffer.
|
||||||
|
/// The number of bytes read can be less than `count` if there isn't
|
||||||
|
/// enough data in the descriptor chain buffer.
|
||||||
|
pub fn read_to<F: FileReadWriteVolatile>(
|
||||||
|
&mut self,
|
||||||
|
mut dst: F,
|
||||||
|
count: usize,
|
||||||
|
) -> io::Result<usize> {
|
||||||
|
self.buffer
|
||||||
|
.consume(count, |bufs| dst.write_vectored_volatile(bufs))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads data from the descriptor chain buffer into a File at offset `off`.
|
||||||
|
/// Returns the number of bytes read from the descriptor chain buffer.
|
||||||
|
/// The number of bytes read can be less than `count` if there isn't
|
||||||
|
/// enough data in the descriptor chain buffer.
|
||||||
|
pub fn read_to_at<F: FileReadWriteAtVolatile>(
|
||||||
|
&mut self,
|
||||||
|
mut dst: F,
|
||||||
|
count: usize,
|
||||||
|
off: u64,
|
||||||
|
) -> io::Result<usize> {
|
||||||
|
self.buffer
|
||||||
|
.consume(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(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns number of bytes available for reading. May return an error if the combined
|
||||||
|
/// lengths of all the buffers in the DescriptorChain would cause an integer overflow.
|
||||||
|
pub fn available_bytes(&self) -> usize {
|
||||||
|
self.buffer.available_bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns number of bytes already read from the descriptor chain buffer.
|
||||||
|
pub fn bytes_read(&self) -> usize {
|
||||||
|
self.buffer.bytes_consumed()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Splits this `Reader` into two at the given offset in the `DescriptorChain` buffer.
|
||||||
|
/// After the split, `self` will be able to read up to `offset` bytes while the returned
|
||||||
|
/// `Reader` can read up to `available_bytes() - offset` bytes. Returns an error if
|
||||||
|
/// `offset > self.available_bytes()`.
|
||||||
|
pub fn split_at(&mut self, offset: usize) -> Result<Reader<'a>> {
|
||||||
|
self.buffer.split_at(offset).map(|buffer| Reader { buffer })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> io::Read for Reader<'a> {
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||||
|
self.buffer.consume(buf.len(), |bufs| {
|
||||||
|
let mut rem = buf;
|
||||||
|
let mut total = 0;
|
||||||
|
for vs in bufs {
|
||||||
|
let copy_len = cmp::min(rem.len(), vs.len());
|
||||||
|
|
||||||
|
// Safe because we have already verified that `vs` points to valid memory.
|
||||||
|
unsafe {
|
||||||
|
copy_nonoverlapping(vs.as_ptr() as *const u8, rem.as_mut_ptr(), copy_len);
|
||||||
|
}
|
||||||
|
rem = &mut rem[copy_len..];
|
||||||
|
total += copy_len;
|
||||||
|
}
|
||||||
|
Ok(total)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Provides high-level interface over the sequence of memory regions
|
||||||
|
/// defined by writable descriptors in the descriptor chain.
|
||||||
|
///
|
||||||
|
/// Note that virtio spec requires driver to place any device-writable
|
||||||
|
/// descriptors after any device-readable descriptors (2.6.4.2 in Virtio Spec v1.1).
|
||||||
|
/// Writer will start iterating the descriptors from the first writable one and will
|
||||||
|
/// assume that all following descriptors are writable.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Writer<'a> {
|
||||||
|
buffer: DescriptorChainConsumer<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Writer<'a> {
|
||||||
|
/// Construct a new Writer wrapper over `desc_chain`.
|
||||||
|
pub fn new(mem: &'a GuestMemoryMmap, desc_chain: DescriptorChain<'a>) -> Result<Writer<'a>> {
|
||||||
|
let mut total_len: usize = 0;
|
||||||
|
let buffers = desc_chain
|
||||||
|
.into_iter()
|
||||||
|
.writable()
|
||||||
|
.map(|desc| {
|
||||||
|
// Verify that summing the descriptor sizes does not overflow.
|
||||||
|
// This can happen if a driver tricks a device into writing more data than
|
||||||
|
// fits in a `usize`.
|
||||||
|
total_len = total_len
|
||||||
|
.checked_add(desc.len as usize)
|
||||||
|
.ok_or(Error::DescriptorChainOverflow)?;
|
||||||
|
|
||||||
|
let region = mem.find_region(desc.addr).ok_or(Error::FindMemoryRegion)?;
|
||||||
|
let offset = desc
|
||||||
|
.addr
|
||||||
|
.checked_sub(region.start_addr().raw_value())
|
||||||
|
.unwrap();
|
||||||
|
region
|
||||||
|
.deref()
|
||||||
|
.get_slice(offset.raw_value() as usize, desc.len as usize)
|
||||||
|
.map_err(Error::VolatileMemoryError)
|
||||||
|
})
|
||||||
|
.collect::<Result<VecDeque<VolatileSlice<'a>>>>()?;
|
||||||
|
|
||||||
|
Ok(Writer {
|
||||||
|
buffer: DescriptorChainConsumer {
|
||||||
|
buffers,
|
||||||
|
bytes_consumed: 0,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Writes an object to the descriptor chain buffer.
|
||||||
|
pub fn write_obj<T: ByteValued>(&mut self, val: T) -> io::Result<()> {
|
||||||
|
self.write_all(val.as_slice())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns number of bytes available for writing. May return an error if the combined
|
||||||
|
/// lengths of all the buffers in the DescriptorChain would cause an overflow.
|
||||||
|
pub fn available_bytes(&self) -> usize {
|
||||||
|
self.buffer.available_bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Writes data to the descriptor chain buffer from a file descriptor.
|
||||||
|
/// Returns the number of bytes written to the descriptor chain buffer.
|
||||||
|
/// The number of bytes written can be less than `count` if
|
||||||
|
/// there isn't enough data in the descriptor chain buffer.
|
||||||
|
pub fn write_from<F: FileReadWriteVolatile>(
|
||||||
|
&mut self,
|
||||||
|
mut src: F,
|
||||||
|
count: usize,
|
||||||
|
) -> io::Result<usize> {
|
||||||
|
self.buffer
|
||||||
|
.consume(count, |bufs| src.read_vectored_volatile(bufs))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Writes data to the descriptor chain buffer from a File at offset `off`.
|
||||||
|
/// Returns the number of bytes written to the descriptor chain buffer.
|
||||||
|
/// The number of bytes written can be less than `count` if
|
||||||
|
/// there isn't enough data in the descriptor chain buffer.
|
||||||
|
pub fn write_from_at<F: FileReadWriteAtVolatile>(
|
||||||
|
&mut self,
|
||||||
|
mut src: F,
|
||||||
|
count: usize,
|
||||||
|
off: u64,
|
||||||
|
) -> io::Result<usize> {
|
||||||
|
self.buffer
|
||||||
|
.consume(count, |bufs| src.read_vectored_at_volatile(bufs, off))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_all_from<F: FileReadWriteVolatile>(
|
||||||
|
&mut self,
|
||||||
|
mut src: F,
|
||||||
|
mut count: usize,
|
||||||
|
) -> io::Result<()> {
|
||||||
|
while count > 0 {
|
||||||
|
match self.write_from(&mut src, count) {
|
||||||
|
Ok(0) => {
|
||||||
|
return Err(io::Error::new(
|
||||||
|
io::ErrorKind::WriteZero,
|
||||||
|
"failed to write whole buffer",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
Ok(n) => count -= n,
|
||||||
|
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
|
||||||
|
Err(e) => return Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns number of bytes already written to the descriptor chain buffer.
|
||||||
|
pub fn bytes_written(&self) -> usize {
|
||||||
|
self.buffer.bytes_consumed()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Splits this `Writer` into two at the given offset in the `DescriptorChain` buffer.
|
||||||
|
/// After the split, `self` will be able to write up to `offset` bytes while the returned
|
||||||
|
/// `Writer` can write up to `available_bytes() - offset` bytes. Returns an error if
|
||||||
|
/// `offset > self.available_bytes()`.
|
||||||
|
pub fn split_at(&mut self, offset: usize) -> Result<Writer<'a>> {
|
||||||
|
self.buffer.split_at(offset).map(|buffer| Writer { buffer })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> io::Write for Writer<'a> {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
|
self.buffer.consume(buf.len(), |bufs| {
|
||||||
|
let mut rem = buf;
|
||||||
|
let mut total = 0;
|
||||||
|
for vs in bufs {
|
||||||
|
let copy_len = cmp::min(rem.len(), vs.len());
|
||||||
|
|
||||||
|
// Safe because we have already verified that `vs` points to valid memory.
|
||||||
|
unsafe {
|
||||||
|
copy_nonoverlapping(rem.as_ptr(), vs.as_ptr(), copy_len);
|
||||||
|
}
|
||||||
|
rem = &rem[copy_len..];
|
||||||
|
total += copy_len;
|
||||||
|
}
|
||||||
|
Ok(total)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
// Nothing to flush since the writes go straight into the buffer.
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const VIRTQ_DESC_F_NEXT: u16 = 0x1;
|
||||||
|
const VIRTQ_DESC_F_WRITE: u16 = 0x2;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||||
|
pub enum DescriptorType {
|
||||||
|
Readable,
|
||||||
|
Writable,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Default)]
|
||||||
|
#[repr(C)]
|
||||||
|
struct virtq_desc {
|
||||||
|
addr: Le64,
|
||||||
|
len: Le32,
|
||||||
|
flags: Le16,
|
||||||
|
next: Le16,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Safe because it only has data and has no implicit padding.
|
||||||
|
unsafe impl ByteValued for virtq_desc {}
|
||||||
|
|
||||||
|
/// Test utility function to create a descriptor chain in guest memory.
|
||||||
|
pub fn create_descriptor_chain(
|
||||||
|
memory: &GuestMemoryMmap,
|
||||||
|
descriptor_array_addr: GuestAddress,
|
||||||
|
mut buffers_start_addr: GuestAddress,
|
||||||
|
descriptors: Vec<(DescriptorType, u32)>,
|
||||||
|
spaces_between_regions: u32,
|
||||||
|
) -> Result<DescriptorChain> {
|
||||||
|
let descriptors_len = descriptors.len();
|
||||||
|
for (index, (type_, size)) in descriptors.into_iter().enumerate() {
|
||||||
|
let mut flags = 0;
|
||||||
|
if let DescriptorType::Writable = type_ {
|
||||||
|
flags |= VIRTQ_DESC_F_WRITE;
|
||||||
|
}
|
||||||
|
if index + 1 < descriptors_len {
|
||||||
|
flags |= VIRTQ_DESC_F_NEXT;
|
||||||
|
}
|
||||||
|
|
||||||
|
let index = index as u16;
|
||||||
|
let desc = virtq_desc {
|
||||||
|
addr: buffers_start_addr.raw_value().into(),
|
||||||
|
len: size.into(),
|
||||||
|
flags: flags.into(),
|
||||||
|
next: (index + 1).into(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let offset = size + spaces_between_regions;
|
||||||
|
buffers_start_addr = buffers_start_addr
|
||||||
|
.checked_add(u64::from(offset))
|
||||||
|
.ok_or(Error::InvalidChain)?;
|
||||||
|
|
||||||
|
let _ = memory.write_obj(
|
||||||
|
desc,
|
||||||
|
descriptor_array_addr
|
||||||
|
.checked_add(u64::from(index) * std::mem::size_of::<virtq_desc>() as u64)
|
||||||
|
.ok_or(Error::InvalidChain)?,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
DescriptorChain::checked_new(memory, descriptor_array_addr, 0x100, 0, None)
|
||||||
|
.ok_or(Error::InvalidChain)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reader_test_simple_chain() {
|
||||||
|
use DescriptorType::*;
|
||||||
|
|
||||||
|
let memory_start_addr = GuestAddress(0x0);
|
||||||
|
let memory = GuestMemoryMmap::new(&vec![(memory_start_addr, 0x10000)]).unwrap();
|
||||||
|
|
||||||
|
let chain = create_descriptor_chain(
|
||||||
|
&memory,
|
||||||
|
GuestAddress(0x0),
|
||||||
|
GuestAddress(0x100),
|
||||||
|
vec![
|
||||||
|
(Readable, 8),
|
||||||
|
(Readable, 16),
|
||||||
|
(Readable, 18),
|
||||||
|
(Readable, 64),
|
||||||
|
],
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
.expect("create_descriptor_chain failed");
|
||||||
|
let mut reader = Reader::new(&memory, chain).expect("failed to create Reader");
|
||||||
|
assert_eq!(reader.available_bytes(), 106);
|
||||||
|
assert_eq!(reader.bytes_read(), 0);
|
||||||
|
|
||||||
|
let mut buffer = [0 as u8; 64];
|
||||||
|
if let Err(_) = reader.read_exact(&mut buffer) {
|
||||||
|
panic!("read_exact should not fail here");
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(reader.available_bytes(), 42);
|
||||||
|
assert_eq!(reader.bytes_read(), 64);
|
||||||
|
|
||||||
|
match reader.read(&mut buffer) {
|
||||||
|
Err(_) => panic!("read should not fail here"),
|
||||||
|
Ok(length) => assert_eq!(length, 42),
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(reader.available_bytes(), 0);
|
||||||
|
assert_eq!(reader.bytes_read(), 106);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn writer_test_simple_chain() {
|
||||||
|
use DescriptorType::*;
|
||||||
|
|
||||||
|
let memory_start_addr = GuestAddress(0x0);
|
||||||
|
let memory = GuestMemoryMmap::new(&vec![(memory_start_addr, 0x10000)]).unwrap();
|
||||||
|
|
||||||
|
let chain = create_descriptor_chain(
|
||||||
|
&memory,
|
||||||
|
GuestAddress(0x0),
|
||||||
|
GuestAddress(0x100),
|
||||||
|
vec![
|
||||||
|
(Writable, 8),
|
||||||
|
(Writable, 16),
|
||||||
|
(Writable, 18),
|
||||||
|
(Writable, 64),
|
||||||
|
],
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
.expect("create_descriptor_chain failed");
|
||||||
|
let mut writer = Writer::new(&memory, chain).expect("failed to create Writer");
|
||||||
|
assert_eq!(writer.available_bytes(), 106);
|
||||||
|
assert_eq!(writer.bytes_written(), 0);
|
||||||
|
|
||||||
|
let mut buffer = [0 as u8; 64];
|
||||||
|
if let Err(_) = writer.write_all(&mut buffer) {
|
||||||
|
panic!("write_all should not fail here");
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(writer.available_bytes(), 42);
|
||||||
|
assert_eq!(writer.bytes_written(), 64);
|
||||||
|
|
||||||
|
match writer.write(&mut buffer) {
|
||||||
|
Err(_) => panic!("write should not fail here"),
|
||||||
|
Ok(length) => assert_eq!(length, 42),
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(writer.available_bytes(), 0);
|
||||||
|
assert_eq!(writer.bytes_written(), 106);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reader_test_incompatible_chain() {
|
||||||
|
use DescriptorType::*;
|
||||||
|
|
||||||
|
let memory_start_addr = GuestAddress(0x0);
|
||||||
|
let memory = GuestMemoryMmap::new(&vec![(memory_start_addr, 0x10000)]).unwrap();
|
||||||
|
|
||||||
|
let chain = create_descriptor_chain(
|
||||||
|
&memory,
|
||||||
|
GuestAddress(0x0),
|
||||||
|
GuestAddress(0x100),
|
||||||
|
vec![(Writable, 8)],
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
.expect("create_descriptor_chain failed");
|
||||||
|
let mut reader = Reader::new(&memory, chain).expect("failed to create Reader");
|
||||||
|
assert_eq!(reader.available_bytes(), 0);
|
||||||
|
assert_eq!(reader.bytes_read(), 0);
|
||||||
|
|
||||||
|
assert!(reader.read_obj::<u8>().is_err());
|
||||||
|
|
||||||
|
assert_eq!(reader.available_bytes(), 0);
|
||||||
|
assert_eq!(reader.bytes_read(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn writer_test_incompatible_chain() {
|
||||||
|
use DescriptorType::*;
|
||||||
|
|
||||||
|
let memory_start_addr = GuestAddress(0x0);
|
||||||
|
let memory = GuestMemoryMmap::new(&vec![(memory_start_addr, 0x10000)]).unwrap();
|
||||||
|
|
||||||
|
let chain = create_descriptor_chain(
|
||||||
|
&memory,
|
||||||
|
GuestAddress(0x0),
|
||||||
|
GuestAddress(0x100),
|
||||||
|
vec![(Readable, 8)],
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
.expect("create_descriptor_chain failed");
|
||||||
|
let mut writer = Writer::new(&memory, chain).expect("failed to create Writer");
|
||||||
|
assert_eq!(writer.available_bytes(), 0);
|
||||||
|
assert_eq!(writer.bytes_written(), 0);
|
||||||
|
|
||||||
|
assert!(writer.write_obj(0u8).is_err());
|
||||||
|
|
||||||
|
assert_eq!(writer.available_bytes(), 0);
|
||||||
|
assert_eq!(writer.bytes_written(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reader_writer_shared_chain() {
|
||||||
|
use DescriptorType::*;
|
||||||
|
|
||||||
|
let memory_start_addr = GuestAddress(0x0);
|
||||||
|
let memory = GuestMemoryMmap::new(&vec![(memory_start_addr, 0x10000)]).unwrap();
|
||||||
|
|
||||||
|
let chain = create_descriptor_chain(
|
||||||
|
&memory,
|
||||||
|
GuestAddress(0x0),
|
||||||
|
GuestAddress(0x100),
|
||||||
|
vec![
|
||||||
|
(Readable, 16),
|
||||||
|
(Readable, 16),
|
||||||
|
(Readable, 96),
|
||||||
|
(Writable, 64),
|
||||||
|
(Writable, 1),
|
||||||
|
(Writable, 3),
|
||||||
|
],
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
.expect("create_descriptor_chain failed");
|
||||||
|
let mut reader = Reader::new(&memory, chain.clone()).expect("failed to create Reader");
|
||||||
|
let mut writer = Writer::new(&memory, chain).expect("failed to create Writer");
|
||||||
|
|
||||||
|
assert_eq!(reader.bytes_read(), 0);
|
||||||
|
assert_eq!(writer.bytes_written(), 0);
|
||||||
|
|
||||||
|
let mut buffer = Vec::with_capacity(200);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
reader
|
||||||
|
.read_to_end(&mut buffer)
|
||||||
|
.expect("read should not fail here"),
|
||||||
|
128
|
||||||
|
);
|
||||||
|
|
||||||
|
// The writable descriptors are only 68 bytes long.
|
||||||
|
writer
|
||||||
|
.write_all(&buffer[..68])
|
||||||
|
.expect("write should not fail here");
|
||||||
|
|
||||||
|
assert_eq!(reader.available_bytes(), 0);
|
||||||
|
assert_eq!(reader.bytes_read(), 128);
|
||||||
|
assert_eq!(writer.available_bytes(), 0);
|
||||||
|
assert_eq!(writer.bytes_written(), 68);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reader_writer_shattered_object() {
|
||||||
|
use DescriptorType::*;
|
||||||
|
|
||||||
|
let memory_start_addr = GuestAddress(0x0);
|
||||||
|
let memory = GuestMemoryMmap::new(&vec![(memory_start_addr, 0x10000)]).unwrap();
|
||||||
|
|
||||||
|
let secret: Le32 = 0x12345678.into();
|
||||||
|
|
||||||
|
// Create a descriptor chain with memory regions that are properly separated.
|
||||||
|
let chain_writer = create_descriptor_chain(
|
||||||
|
&memory,
|
||||||
|
GuestAddress(0x0),
|
||||||
|
GuestAddress(0x100),
|
||||||
|
vec![(Writable, 1), (Writable, 1), (Writable, 1), (Writable, 1)],
|
||||||
|
123,
|
||||||
|
)
|
||||||
|
.expect("create_descriptor_chain failed");
|
||||||
|
let mut writer = Writer::new(&memory, chain_writer).expect("failed to create Writer");
|
||||||
|
if let Err(_) = writer.write_obj(secret) {
|
||||||
|
panic!("write_obj should not fail here");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now create new descriptor chain pointing to the same memory and try to read it.
|
||||||
|
let chain_reader = create_descriptor_chain(
|
||||||
|
&memory,
|
||||||
|
GuestAddress(0x0),
|
||||||
|
GuestAddress(0x100),
|
||||||
|
vec![(Readable, 1), (Readable, 1), (Readable, 1), (Readable, 1)],
|
||||||
|
123,
|
||||||
|
)
|
||||||
|
.expect("create_descriptor_chain failed");
|
||||||
|
let mut reader = Reader::new(&memory, chain_reader).expect("failed to create Reader");
|
||||||
|
match reader.read_obj::<Le32>() {
|
||||||
|
Err(_) => panic!("read_obj should not fail here"),
|
||||||
|
Ok(read_secret) => assert_eq!(read_secret, secret),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reader_unexpected_eof() {
|
||||||
|
use DescriptorType::*;
|
||||||
|
|
||||||
|
let memory_start_addr = GuestAddress(0x0);
|
||||||
|
let memory = GuestMemoryMmap::new(&vec![(memory_start_addr, 0x10000)]).unwrap();
|
||||||
|
|
||||||
|
let chain = create_descriptor_chain(
|
||||||
|
&memory,
|
||||||
|
GuestAddress(0x0),
|
||||||
|
GuestAddress(0x100),
|
||||||
|
vec![(Readable, 256), (Readable, 256)],
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
.expect("create_descriptor_chain failed");
|
||||||
|
|
||||||
|
let mut reader = Reader::new(&memory, chain).expect("failed to create Reader");
|
||||||
|
|
||||||
|
let mut buf = Vec::with_capacity(1024);
|
||||||
|
buf.resize(1024, 0);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
reader
|
||||||
|
.read_exact(&mut buf[..])
|
||||||
|
.expect_err("read more bytes than available")
|
||||||
|
.kind(),
|
||||||
|
io::ErrorKind::UnexpectedEof
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn split_border() {
|
||||||
|
use DescriptorType::*;
|
||||||
|
|
||||||
|
let memory_start_addr = GuestAddress(0x0);
|
||||||
|
let memory = GuestMemoryMmap::new(&vec![(memory_start_addr, 0x10000)]).unwrap();
|
||||||
|
|
||||||
|
let chain = create_descriptor_chain(
|
||||||
|
&memory,
|
||||||
|
GuestAddress(0x0),
|
||||||
|
GuestAddress(0x100),
|
||||||
|
vec![
|
||||||
|
(Readable, 16),
|
||||||
|
(Readable, 16),
|
||||||
|
(Readable, 96),
|
||||||
|
(Writable, 64),
|
||||||
|
(Writable, 1),
|
||||||
|
(Writable, 3),
|
||||||
|
],
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
.expect("create_descriptor_chain failed");
|
||||||
|
let mut reader = Reader::new(&memory, chain).expect("failed to create Reader");
|
||||||
|
|
||||||
|
let other = reader.split_at(32).expect("failed to split Reader");
|
||||||
|
assert_eq!(reader.available_bytes(), 32);
|
||||||
|
assert_eq!(other.available_bytes(), 96);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn split_middle() {
|
||||||
|
use DescriptorType::*;
|
||||||
|
|
||||||
|
let memory_start_addr = GuestAddress(0x0);
|
||||||
|
let memory = GuestMemoryMmap::new(&vec![(memory_start_addr, 0x10000)]).unwrap();
|
||||||
|
|
||||||
|
let chain = create_descriptor_chain(
|
||||||
|
&memory,
|
||||||
|
GuestAddress(0x0),
|
||||||
|
GuestAddress(0x100),
|
||||||
|
vec![
|
||||||
|
(Readable, 16),
|
||||||
|
(Readable, 16),
|
||||||
|
(Readable, 96),
|
||||||
|
(Writable, 64),
|
||||||
|
(Writable, 1),
|
||||||
|
(Writable, 3),
|
||||||
|
],
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
.expect("create_descriptor_chain failed");
|
||||||
|
let mut reader = Reader::new(&memory, chain).expect("failed to create Reader");
|
||||||
|
|
||||||
|
let other = reader.split_at(24).expect("failed to split Reader");
|
||||||
|
assert_eq!(reader.available_bytes(), 24);
|
||||||
|
assert_eq!(other.available_bytes(), 104);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn split_end() {
|
||||||
|
use DescriptorType::*;
|
||||||
|
|
||||||
|
let memory_start_addr = GuestAddress(0x0);
|
||||||
|
let memory = GuestMemoryMmap::new(&vec![(memory_start_addr, 0x10000)]).unwrap();
|
||||||
|
|
||||||
|
let chain = create_descriptor_chain(
|
||||||
|
&memory,
|
||||||
|
GuestAddress(0x0),
|
||||||
|
GuestAddress(0x100),
|
||||||
|
vec![
|
||||||
|
(Readable, 16),
|
||||||
|
(Readable, 16),
|
||||||
|
(Readable, 96),
|
||||||
|
(Writable, 64),
|
||||||
|
(Writable, 1),
|
||||||
|
(Writable, 3),
|
||||||
|
],
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
.expect("create_descriptor_chain failed");
|
||||||
|
let mut reader = Reader::new(&memory, chain).expect("failed to create Reader");
|
||||||
|
|
||||||
|
let other = reader.split_at(128).expect("failed to split Reader");
|
||||||
|
assert_eq!(reader.available_bytes(), 128);
|
||||||
|
assert_eq!(other.available_bytes(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn split_beginning() {
|
||||||
|
use DescriptorType::*;
|
||||||
|
|
||||||
|
let memory_start_addr = GuestAddress(0x0);
|
||||||
|
let memory = GuestMemoryMmap::new(&vec![(memory_start_addr, 0x10000)]).unwrap();
|
||||||
|
|
||||||
|
let chain = create_descriptor_chain(
|
||||||
|
&memory,
|
||||||
|
GuestAddress(0x0),
|
||||||
|
GuestAddress(0x100),
|
||||||
|
vec![
|
||||||
|
(Readable, 16),
|
||||||
|
(Readable, 16),
|
||||||
|
(Readable, 96),
|
||||||
|
(Writable, 64),
|
||||||
|
(Writable, 1),
|
||||||
|
(Writable, 3),
|
||||||
|
],
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
.expect("create_descriptor_chain failed");
|
||||||
|
let mut reader = Reader::new(&memory, chain).expect("failed to create Reader");
|
||||||
|
|
||||||
|
let other = reader.split_at(0).expect("failed to split Reader");
|
||||||
|
assert_eq!(reader.available_bytes(), 0);
|
||||||
|
assert_eq!(other.available_bytes(), 128);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn split_outofbounds() {
|
||||||
|
use DescriptorType::*;
|
||||||
|
|
||||||
|
let memory_start_addr = GuestAddress(0x0);
|
||||||
|
let memory = GuestMemoryMmap::new(&vec![(memory_start_addr, 0x10000)]).unwrap();
|
||||||
|
|
||||||
|
let chain = create_descriptor_chain(
|
||||||
|
&memory,
|
||||||
|
GuestAddress(0x0),
|
||||||
|
GuestAddress(0x100),
|
||||||
|
vec![
|
||||||
|
(Readable, 16),
|
||||||
|
(Readable, 16),
|
||||||
|
(Readable, 96),
|
||||||
|
(Writable, 64),
|
||||||
|
(Writable, 1),
|
||||||
|
(Writable, 3),
|
||||||
|
],
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
.expect("create_descriptor_chain failed");
|
||||||
|
let mut reader = Reader::new(&memory, chain).expect("failed to create Reader");
|
||||||
|
|
||||||
|
if let Ok(_) = reader.split_at(256) {
|
||||||
|
panic!("successfully split Reader with out of bounds offset");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn read_full() {
|
||||||
|
use DescriptorType::*;
|
||||||
|
|
||||||
|
let memory_start_addr = GuestAddress(0x0);
|
||||||
|
let memory = GuestMemoryMmap::new(&vec![(memory_start_addr, 0x10000)]).unwrap();
|
||||||
|
|
||||||
|
let chain = create_descriptor_chain(
|
||||||
|
&memory,
|
||||||
|
GuestAddress(0x0),
|
||||||
|
GuestAddress(0x100),
|
||||||
|
vec![(Readable, 16), (Readable, 16), (Readable, 16)],
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
.expect("create_descriptor_chain failed");
|
||||||
|
let mut reader = Reader::new(&memory, chain).expect("failed to create Reader");
|
||||||
|
|
||||||
|
let mut buf = vec![0u8; 64];
|
||||||
|
assert_eq!(
|
||||||
|
reader.read(&mut buf[..]).expect("failed to read to buffer"),
|
||||||
|
48
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn write_full() {
|
||||||
|
use DescriptorType::*;
|
||||||
|
|
||||||
|
let memory_start_addr = GuestAddress(0x0);
|
||||||
|
let memory = GuestMemoryMmap::new(&vec![(memory_start_addr, 0x10000)]).unwrap();
|
||||||
|
|
||||||
|
let chain = create_descriptor_chain(
|
||||||
|
&memory,
|
||||||
|
GuestAddress(0x0),
|
||||||
|
GuestAddress(0x100),
|
||||||
|
vec![(Writable, 16), (Writable, 16), (Writable, 16)],
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
.expect("create_descriptor_chain failed");
|
||||||
|
let mut writer = Writer::new(&memory, chain).expect("failed to create Writer");
|
||||||
|
|
||||||
|
let buf = vec![0xdeu8; 64];
|
||||||
|
assert_eq!(
|
||||||
|
writer.write(&buf[..]).expect("failed to write from buffer"),
|
||||||
|
48
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,7 @@
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate log;
|
extern crate log;
|
||||||
|
|
||||||
|
pub mod descriptor_utils;
|
||||||
pub mod file_traits;
|
pub mod file_traits;
|
||||||
pub mod filesystem;
|
pub mod filesystem;
|
||||||
pub mod fuse;
|
pub mod fuse;
|
||||||
|
Loading…
Reference in New Issue
Block a user