mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2025-02-22 19:32:20 +00:00
vhost_user_fs: Add file traits to handle writing volatile memory
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>
This commit is contained in:
parent
e33ccb0c95
commit
5f7935f8e0
409
vhost_user_fs/src/file_traits.rs
Normal file
409
vhost_user_fs/src/file_traits.rs
Normal file
@ -0,0 +1,409 @@
|
||||
// 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);
|
@ -5,6 +5,7 @@
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
pub mod file_traits;
|
||||
pub mod filesystem;
|
||||
pub mod fuse;
|
||||
pub mod multikey;
|
||||
|
Loading…
x
Reference in New Issue
Block a user