// 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; /// 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 { 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; /// 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 { 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 { (**self).read_volatile(slice) } fn read_vectored_volatile(&mut self, bufs: &[VolatileSlice]) -> Result { (**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 { (**self).write_volatile(slice) } fn write_vectored_volatile(&mut self, bufs: &[VolatileSlice]) -> Result { (**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; /// 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 { 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; /// 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 { 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 { (**self).read_at_volatile(slice, offset) } fn read_vectored_at_volatile(&mut self, bufs: &[VolatileSlice], offset: u64) -> Result { (**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 { (**self).write_at_volatile(slice, offset) } fn write_vectored_at_volatile(&mut self, bufs: &[VolatileSlice], offset: u64) -> Result { (**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 { // 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 { let iovecs: Vec = 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 { // 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 { let iovecs: Vec = 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 { // 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 { let iovecs: Vec = 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 { // 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 { let iovecs: Vec = 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);