// 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, RawFd}; use data_model::VolatileSlice; use crate::{fallocate, FallocateMode}; /// A trait for flushing the contents of a file to disk. /// This is equivalent to File's `sync_all` method, but /// wrapped in a trait so that it can be implemented for /// other types. pub trait FileSync { // Flush buffers related to this file to disk. fn fsync(&mut self) -> Result<()>; } impl FileSync for File { fn fsync(&mut self) -> Result<()> { self.sync_all() } } /// 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 for getting the size of a file. /// This is equivalent to File's metadata().len() method, /// but wrapped in a trait so that it can be implemented for /// other types. pub trait FileGetLen { /// Get the current length of the file in bytes. fn get_len(&self) -> Result; } impl FileGetLen for File { fn get_len(&self) -> Result { Ok(self.metadata()?.len()) } } /// A trait for allocating disk space in a sparse file. /// This is equivalent to fallocate() with no special flags. pub trait FileAllocate { /// Allocate storage for the region of the file starting at `offset` and extending `len` bytes. fn allocate(&mut self, offset: u64, len: u64) -> Result<()>; } impl FileAllocate for File { fn allocate(&mut self, offset: u64, len: u64) -> Result<()> { fallocate(self, FallocateMode::Allocate, true, offset, len) .map_err(|e| Error::from_raw_os_error(e.errno())) } } /// 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.size() > 0) .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.size() > 0 { 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 as u64).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.size() > 0) .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.size() > 0 { 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 as u64).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.size() > 0 { match self.read_at_volatile(slice, offset) { Ok(0) => return Err(Error::from(ErrorKind::UnexpectedEof)), Ok(n) => { slice = slice.offset(n as u64).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.size() > 0 { match self.write_at_volatile(slice, offset) { Ok(0) => return Err(Error::from(ErrorKind::WriteZero)), Ok(n) => { slice = slice.offset(n as u64).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) } } // This module allows the below macros to refer to $crate::file_traits::lib::X and ensures other // crates don't need to add additional crates to their Cargo.toml. pub mod lib { pub use libc::{ c_int, c_void, iovec, off64_t, pread64, preadv64, pwrite64, pwritev64, read, readv, size_t, write, writev, }; pub use data_model::VolatileSlice; } #[macro_export] macro_rules! volatile_impl { ($ty:ty) => { impl FileReadWriteVolatile for $ty { fn read_volatile( &mut self, slice: $crate::file_traits::lib::VolatileSlice, ) -> std::io::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 { $crate::file_traits::lib::read( self.as_raw_fd(), slice.as_ptr() as *mut std::ffi::c_void, slice.size() as usize, ) }; if ret >= 0 { Ok(ret as usize) } else { Err(std::io::Error::last_os_error()) } } fn read_vectored_volatile( &mut self, bufs: &[$crate::file_traits::lib::VolatileSlice], ) -> std::io::Result { let iovecs: Vec<$crate::file_traits::lib::iovec> = bufs .iter() .map(|s| $crate::file_traits::lib::iovec { iov_base: s.as_ptr() as *mut std::ffi::c_void, iov_len: s.size() as $crate::file_traits::lib::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 { $crate::file_traits::lib::readv( self.as_raw_fd(), &iovecs[0], iovecs.len() as std::os::raw::c_int, ) }; if ret >= 0 { Ok(ret as usize) } else { Err(std::io::Error::last_os_error()) } } fn write_volatile( &mut self, slice: $crate::file_traits::lib::VolatileSlice, ) -> std::io::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 { $crate::file_traits::lib::write( self.as_raw_fd(), slice.as_ptr() as *const std::ffi::c_void, slice.size() as usize, ) }; if ret >= 0 { Ok(ret as usize) } else { Err(std::io::Error::last_os_error()) } } fn write_vectored_volatile( &mut self, bufs: &[$crate::file_traits::lib::VolatileSlice], ) -> std::io::Result { let iovecs: Vec<$crate::file_traits::lib::iovec> = bufs .iter() .map(|s| $crate::file_traits::lib::iovec { iov_base: s.as_ptr() as *mut std::ffi::c_void, iov_len: s.size() as $crate::file_traits::lib::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 { $crate::file_traits::lib::writev( self.as_raw_fd(), &iovecs[0], iovecs.len() as std::os::raw::c_int, ) }; if ret >= 0 { Ok(ret as usize) } else { Err(std::io::Error::last_os_error()) } } } }; } #[macro_export] macro_rules! volatile_at_impl { ($ty:ty) => { impl FileReadWriteAtVolatile for $ty { fn read_at_volatile( &mut self, slice: $crate::file_traits::lib::VolatileSlice, offset: u64, ) -> std::io::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 { $crate::file_traits::lib::pread64( self.as_raw_fd(), slice.as_ptr() as *mut std::ffi::c_void, slice.size() as usize, offset as $crate::file_traits::lib::off64_t, ) }; if ret >= 0 { Ok(ret as usize) } else { Err(std::io::Error::last_os_error()) } } fn read_vectored_at_volatile( &mut self, bufs: &[$crate::file_traits::lib::VolatileSlice], offset: u64, ) -> std::io::Result { let iovecs: Vec<$crate::file_traits::lib::iovec> = bufs .iter() .map(|s| $crate::file_traits::lib::iovec { iov_base: s.as_ptr() as *mut std::ffi::c_void, iov_len: s.size() as $crate::file_traits::lib::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 { $crate::file_traits::lib::preadv64( self.as_raw_fd(), &iovecs[0], iovecs.len() as std::os::raw::c_int, offset as $crate::file_traits::lib::off64_t, ) }; if ret >= 0 { Ok(ret as usize) } else { Err(std::io::Error::last_os_error()) } } fn write_at_volatile( &mut self, slice: $crate::file_traits::lib::VolatileSlice, offset: u64, ) -> std::io::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 { $crate::file_traits::lib::pwrite64( self.as_raw_fd(), slice.as_ptr() as *const std::ffi::c_void, slice.size() as usize, offset as $crate::file_traits::lib::off64_t, ) }; if ret >= 0 { Ok(ret as usize) } else { Err(std::io::Error::last_os_error()) } } fn write_vectored_at_volatile( &mut self, bufs: &[$crate::file_traits::lib::VolatileSlice], offset: u64, ) -> std::io::Result { let iovecs: Vec<$crate::file_traits::lib::iovec> = bufs .iter() .map(|s| $crate::file_traits::lib::iovec { iov_base: s.as_ptr() as *mut std::ffi::c_void, iov_len: s.size() as $crate::file_traits::lib::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 { $crate::file_traits::lib::pwritev64( self.as_raw_fd(), &iovecs[0], iovecs.len() as std::os::raw::c_int, offset as $crate::file_traits::lib::off64_t, ) }; if ret >= 0 { Ok(ret as usize) } else { Err(std::io::Error::last_os_error()) } } } }; } volatile_impl!(File); volatile_at_impl!(File); /// A trait similar to `AsRawFd` but supports an arbitrary number of file descriptors. pub trait AsRawFds { fn as_raw_fds(&self) -> Vec; } impl AsRawFds for T where T: AsRawFd, { fn as_raw_fds(&self) -> Vec { vec![self.as_raw_fd()] } }