mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2024-09-12 10:04:51 +00:00
The vm_memory implementation for VolatileSlice is able to read and write to a source or destination which implements a Read or Write trait. Unfortunately, this is not enough for this specific use case as we need to be able to write to a file at a specific offset, which is not provided by the Read or Write trait. This code has been ported over from crosvm commit 961461350c0b6824e5f20655031bf6c6bf6b7c30. Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
410 lines
16 KiB
Rust
410 lines
16 KiB
Rust
// Copyright 2018 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::fs::File;
|
|
use std::io::{Error, ErrorKind, Result};
|
|
use std::os::unix::io::AsRawFd;
|
|
|
|
use vm_memory::VolatileSlice;
|
|
|
|
use libc::{
|
|
c_int, c_void, off64_t, pread64, preadv64, pwrite64, pwritev64, read, readv, size_t, write,
|
|
writev,
|
|
};
|
|
|
|
/// A trait for setting the size of a file.
|
|
/// This is equivalent to File's `set_len` method, but
|
|
/// wrapped in a trait so that it can be implemented for
|
|
/// other types.
|
|
pub trait FileSetLen {
|
|
// Set the size of this file.
|
|
// This is the moral equivalent of `ftruncate()`.
|
|
fn set_len(&self, _len: u64) -> Result<()>;
|
|
}
|
|
|
|
impl FileSetLen for File {
|
|
fn set_len(&self, len: u64) -> Result<()> {
|
|
File::set_len(self, len)
|
|
}
|
|
}
|
|
|
|
/// A trait similar to `Read` and `Write`, but uses volatile memory as buffers.
|
|
pub trait FileReadWriteVolatile {
|
|
/// Read bytes from this file into the given slice, returning the number of bytes read on
|
|
/// success.
|
|
fn read_volatile(&mut self, slice: VolatileSlice) -> Result<usize>;
|
|
|
|
/// Like `read_volatile`, except it reads to a slice of buffers. Data is copied to fill each
|
|
/// buffer in order, with the final buffer written to possibly being only partially filled. This
|
|
/// method must behave as a single call to `read_volatile` with the buffers concatenated would.
|
|
/// The default implementation calls `read_volatile` with either the first nonempty buffer
|
|
/// provided, or returns `Ok(0)` if none exists.
|
|
fn read_vectored_volatile(&mut self, bufs: &[VolatileSlice]) -> Result<usize> {
|
|
bufs.iter()
|
|
.find(|b| !b.is_empty())
|
|
.map(|&b| self.read_volatile(b))
|
|
.unwrap_or(Ok(0))
|
|
}
|
|
|
|
/// Reads bytes from this into the given slice until all bytes in the slice are written, or an
|
|
/// error is returned.
|
|
fn read_exact_volatile(&mut self, mut slice: VolatileSlice) -> Result<()> {
|
|
while !slice.is_empty() {
|
|
let bytes_read = self.read_volatile(slice)?;
|
|
if bytes_read == 0 {
|
|
return Err(Error::from(ErrorKind::UnexpectedEof));
|
|
}
|
|
// Will panic if read_volatile read more bytes than we gave it, which would be worthy of
|
|
// a panic.
|
|
slice = slice.offset(bytes_read).unwrap();
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
/// Write bytes from the slice to the given file, returning the number of bytes written on
|
|
/// success.
|
|
fn write_volatile(&mut self, slice: VolatileSlice) -> Result<usize>;
|
|
|
|
/// Like `write_volatile`, except that it writes from a slice of buffers. Data is copied from
|
|
/// each buffer in order, with the final buffer read from possibly being only partially
|
|
/// consumed. This method must behave as a call to `write_volatile` with the buffers
|
|
/// concatenated would. The default implementation calls `write_volatile` with either the first
|
|
/// nonempty buffer provided, or returns `Ok(0)` if none exists.
|
|
fn write_vectored_volatile(&mut self, bufs: &[VolatileSlice]) -> Result<usize> {
|
|
bufs.iter()
|
|
.find(|b| !b.is_empty())
|
|
.map(|&b| self.write_volatile(b))
|
|
.unwrap_or(Ok(0))
|
|
}
|
|
|
|
/// Write bytes from the slice to the given file until all the bytes from the slice have been
|
|
/// written, or an error is returned.
|
|
fn write_all_volatile(&mut self, mut slice: VolatileSlice) -> Result<()> {
|
|
while !slice.is_empty() {
|
|
let bytes_written = self.write_volatile(slice)?;
|
|
if bytes_written == 0 {
|
|
return Err(Error::from(ErrorKind::WriteZero));
|
|
}
|
|
// Will panic if read_volatile read more bytes than we gave it, which would be worthy of
|
|
// a panic.
|
|
slice = slice.offset(bytes_written).unwrap();
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl<'a, T: FileReadWriteVolatile + ?Sized> FileReadWriteVolatile for &'a mut T {
|
|
fn read_volatile(&mut self, slice: VolatileSlice) -> Result<usize> {
|
|
(**self).read_volatile(slice)
|
|
}
|
|
|
|
fn read_vectored_volatile(&mut self, bufs: &[VolatileSlice]) -> Result<usize> {
|
|
(**self).read_vectored_volatile(bufs)
|
|
}
|
|
|
|
fn read_exact_volatile(&mut self, slice: VolatileSlice) -> Result<()> {
|
|
(**self).read_exact_volatile(slice)
|
|
}
|
|
|
|
fn write_volatile(&mut self, slice: VolatileSlice) -> Result<usize> {
|
|
(**self).write_volatile(slice)
|
|
}
|
|
|
|
fn write_vectored_volatile(&mut self, bufs: &[VolatileSlice]) -> Result<usize> {
|
|
(**self).write_vectored_volatile(bufs)
|
|
}
|
|
|
|
fn write_all_volatile(&mut self, slice: VolatileSlice) -> Result<()> {
|
|
(**self).write_all_volatile(slice)
|
|
}
|
|
}
|
|
|
|
/// A trait similar to the unix `ReadExt` and `WriteExt` traits, but for volatile memory.
|
|
pub trait FileReadWriteAtVolatile {
|
|
/// Reads bytes from this file at `offset` into the given slice, returning the number of bytes
|
|
/// read on success.
|
|
fn read_at_volatile(&mut self, slice: VolatileSlice, offset: u64) -> Result<usize>;
|
|
|
|
/// Like `read_at_volatile`, except it reads to a slice of buffers. Data is copied to fill each
|
|
/// buffer in order, with the final buffer written to possibly being only partially filled. This
|
|
/// method must behave as a single call to `read_at_volatile` with the buffers concatenated
|
|
/// would. The default implementation calls `read_at_volatile` with either the first nonempty
|
|
/// buffer provided, or returns `Ok(0)` if none exists.
|
|
fn read_vectored_at_volatile(&mut self, bufs: &[VolatileSlice], offset: u64) -> Result<usize> {
|
|
if let Some(&slice) = bufs.first() {
|
|
self.read_at_volatile(slice, offset)
|
|
} else {
|
|
Ok(0)
|
|
}
|
|
}
|
|
|
|
/// Reads bytes from this file at `offset` into the given slice until all bytes in the slice are
|
|
/// read, or an error is returned.
|
|
fn read_exact_at_volatile(&mut self, mut slice: VolatileSlice, mut offset: u64) -> Result<()> {
|
|
while !slice.is_empty() {
|
|
match self.read_at_volatile(slice, offset) {
|
|
Ok(0) => return Err(Error::from(ErrorKind::UnexpectedEof)),
|
|
Ok(n) => {
|
|
slice = slice.offset(n).unwrap();
|
|
offset = offset.checked_add(n as u64).unwrap();
|
|
}
|
|
Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
|
|
Err(e) => return Err(e),
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
/// Writes bytes from this file at `offset` into the given slice, returning the number of bytes
|
|
/// written on success.
|
|
fn write_at_volatile(&mut self, slice: VolatileSlice, offset: u64) -> Result<usize>;
|
|
|
|
/// Like `write_at_at_volatile`, except that it writes from a slice of buffers. Data is copied
|
|
/// from each buffer in order, with the final buffer read from possibly being only partially
|
|
/// consumed. This method must behave as a call to `write_at_volatile` with the buffers
|
|
/// concatenated would. The default implementation calls `write_at_volatile` with either the
|
|
/// first nonempty buffer provided, or returns `Ok(0)` if none exists.
|
|
fn write_vectored_at_volatile(&mut self, bufs: &[VolatileSlice], offset: u64) -> Result<usize> {
|
|
if let Some(&slice) = bufs.first() {
|
|
self.write_at_volatile(slice, offset)
|
|
} else {
|
|
Ok(0)
|
|
}
|
|
}
|
|
|
|
/// Writes bytes from this file at `offset` into the given slice until all bytes in the slice
|
|
/// are written, or an error is returned.
|
|
fn write_all_at_volatile(&mut self, mut slice: VolatileSlice, mut offset: u64) -> Result<()> {
|
|
while !slice.is_empty() {
|
|
match self.write_at_volatile(slice, offset) {
|
|
Ok(0) => return Err(Error::from(ErrorKind::WriteZero)),
|
|
Ok(n) => {
|
|
slice = slice.offset(n).unwrap();
|
|
offset = offset.checked_add(n as u64).unwrap();
|
|
}
|
|
Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
|
|
Err(e) => return Err(e),
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl<'a, T: FileReadWriteAtVolatile + ?Sized> FileReadWriteAtVolatile for &'a mut T {
|
|
fn read_at_volatile(&mut self, slice: VolatileSlice, offset: u64) -> Result<usize> {
|
|
(**self).read_at_volatile(slice, offset)
|
|
}
|
|
|
|
fn read_vectored_at_volatile(&mut self, bufs: &[VolatileSlice], offset: u64) -> Result<usize> {
|
|
(**self).read_vectored_at_volatile(bufs, offset)
|
|
}
|
|
|
|
fn read_exact_at_volatile(&mut self, slice: VolatileSlice, offset: u64) -> Result<()> {
|
|
(**self).read_exact_at_volatile(slice, offset)
|
|
}
|
|
|
|
fn write_at_volatile(&mut self, slice: VolatileSlice, offset: u64) -> Result<usize> {
|
|
(**self).write_at_volatile(slice, offset)
|
|
}
|
|
|
|
fn write_vectored_at_volatile(&mut self, bufs: &[VolatileSlice], offset: u64) -> Result<usize> {
|
|
(**self).write_vectored_at_volatile(bufs, offset)
|
|
}
|
|
|
|
fn write_all_at_volatile(&mut self, slice: VolatileSlice, offset: u64) -> Result<()> {
|
|
(**self).write_all_at_volatile(slice, offset)
|
|
}
|
|
}
|
|
|
|
macro_rules! volatile_impl {
|
|
($ty:ty) => {
|
|
impl FileReadWriteVolatile for $ty {
|
|
fn read_volatile(&mut self, slice: VolatileSlice) -> Result<usize> {
|
|
// Safe because only bytes inside the slice are accessed and the kernel is expected
|
|
// to handle arbitrary memory for I/O.
|
|
let ret =
|
|
unsafe { read(self.as_raw_fd(), slice.as_ptr() as *mut c_void, slice.len()) };
|
|
if ret >= 0 {
|
|
Ok(ret as usize)
|
|
} else {
|
|
Err(Error::last_os_error())
|
|
}
|
|
}
|
|
|
|
fn read_vectored_volatile(&mut self, bufs: &[VolatileSlice]) -> Result<usize> {
|
|
let iovecs: Vec<libc::iovec> = bufs
|
|
.iter()
|
|
.map(|s| libc::iovec {
|
|
iov_base: s.as_ptr() as *mut c_void,
|
|
iov_len: s.len() as size_t,
|
|
})
|
|
.collect();
|
|
|
|
if iovecs.is_empty() {
|
|
return Ok(0);
|
|
}
|
|
|
|
// Safe because only bytes inside the buffers are accessed and the kernel is
|
|
// expected to handle arbitrary memory for I/O.
|
|
let ret = unsafe { readv(self.as_raw_fd(), &iovecs[0], iovecs.len() as c_int) };
|
|
if ret >= 0 {
|
|
Ok(ret as usize)
|
|
} else {
|
|
Err(Error::last_os_error())
|
|
}
|
|
}
|
|
|
|
fn write_volatile(&mut self, slice: VolatileSlice) -> Result<usize> {
|
|
// Safe because only bytes inside the slice are accessed and the kernel is expected
|
|
// to handle arbitrary memory for I/O.
|
|
let ret = unsafe {
|
|
write(
|
|
self.as_raw_fd(),
|
|
slice.as_ptr() as *const c_void,
|
|
slice.len(),
|
|
)
|
|
};
|
|
if ret >= 0 {
|
|
Ok(ret as usize)
|
|
} else {
|
|
Err(Error::last_os_error())
|
|
}
|
|
}
|
|
|
|
fn write_vectored_volatile(&mut self, bufs: &[VolatileSlice]) -> Result<usize> {
|
|
let iovecs: Vec<libc::iovec> = bufs
|
|
.iter()
|
|
.map(|s| libc::iovec {
|
|
iov_base: s.as_ptr() as *mut c_void,
|
|
iov_len: s.len() as size_t,
|
|
})
|
|
.collect();
|
|
|
|
if iovecs.is_empty() {
|
|
return Ok(0);
|
|
}
|
|
|
|
// Safe because only bytes inside the buffers are accessed and the kernel is
|
|
// expected to handle arbitrary memory for I/O.
|
|
let ret = unsafe { writev(self.as_raw_fd(), &iovecs[0], iovecs.len() as c_int) };
|
|
if ret >= 0 {
|
|
Ok(ret as usize)
|
|
} else {
|
|
Err(Error::last_os_error())
|
|
}
|
|
}
|
|
}
|
|
|
|
impl FileReadWriteAtVolatile for $ty {
|
|
fn read_at_volatile(&mut self, slice: VolatileSlice, offset: u64) -> Result<usize> {
|
|
// Safe because only bytes inside the slice are accessed and the kernel is expected
|
|
// to handle arbitrary memory for I/O.
|
|
let ret = unsafe {
|
|
pread64(
|
|
self.as_raw_fd(),
|
|
slice.as_ptr() as *mut c_void,
|
|
slice.len(),
|
|
offset as off64_t,
|
|
)
|
|
};
|
|
|
|
if ret >= 0 {
|
|
Ok(ret as usize)
|
|
} else {
|
|
Err(Error::last_os_error())
|
|
}
|
|
}
|
|
|
|
fn read_vectored_at_volatile(
|
|
&mut self,
|
|
bufs: &[VolatileSlice],
|
|
offset: u64,
|
|
) -> Result<usize> {
|
|
let iovecs: Vec<libc::iovec> = bufs
|
|
.iter()
|
|
.map(|s| libc::iovec {
|
|
iov_base: s.as_ptr() as *mut c_void,
|
|
iov_len: s.len() as size_t,
|
|
})
|
|
.collect();
|
|
|
|
if iovecs.is_empty() {
|
|
return Ok(0);
|
|
}
|
|
|
|
// Safe because only bytes inside the buffers are accessed and the kernel is
|
|
// expected to handle arbitrary memory for I/O.
|
|
let ret = unsafe {
|
|
preadv64(
|
|
self.as_raw_fd(),
|
|
&iovecs[0],
|
|
iovecs.len() as c_int,
|
|
offset as off64_t,
|
|
)
|
|
};
|
|
if ret >= 0 {
|
|
Ok(ret as usize)
|
|
} else {
|
|
Err(Error::last_os_error())
|
|
}
|
|
}
|
|
|
|
fn write_at_volatile(&mut self, slice: VolatileSlice, offset: u64) -> Result<usize> {
|
|
// Safe because only bytes inside the slice are accessed and the kernel is expected
|
|
// to handle arbitrary memory for I/O.
|
|
let ret = unsafe {
|
|
pwrite64(
|
|
self.as_raw_fd(),
|
|
slice.as_ptr() as *const c_void,
|
|
slice.len(),
|
|
offset as off64_t,
|
|
)
|
|
};
|
|
|
|
if ret >= 0 {
|
|
Ok(ret as usize)
|
|
} else {
|
|
Err(Error::last_os_error())
|
|
}
|
|
}
|
|
|
|
fn write_vectored_at_volatile(
|
|
&mut self,
|
|
bufs: &[VolatileSlice],
|
|
offset: u64,
|
|
) -> Result<usize> {
|
|
let iovecs: Vec<libc::iovec> = bufs
|
|
.iter()
|
|
.map(|s| libc::iovec {
|
|
iov_base: s.as_ptr() as *mut c_void,
|
|
iov_len: s.len() as size_t,
|
|
})
|
|
.collect();
|
|
|
|
if iovecs.is_empty() {
|
|
return Ok(0);
|
|
}
|
|
|
|
// Safe because only bytes inside the buffers are accessed and the kernel is
|
|
// expected to handle arbitrary memory for I/O.
|
|
let ret = unsafe {
|
|
pwritev64(
|
|
self.as_raw_fd(),
|
|
&iovecs[0],
|
|
iovecs.len() as c_int,
|
|
offset as off64_t,
|
|
)
|
|
};
|
|
if ret >= 0 {
|
|
Ok(ret as usize)
|
|
} else {
|
|
Err(Error::last_os_error())
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
volatile_impl!(File);
|