summary refs log blame commit diff
path: root/sys_util/src/file_traits.rs
blob: bd763c745ceea270f900ca69488e3f16187d57b8 (plain) (tree)
1
2
3
4
5
6
7
8
9
10




                                                                         
                                        
                                        


                              

                                      













                                                        















                                                        
 














                                                            













                                                                                                   





                                                                                             











                                                                                                    









                                                                                                    
                                                      







                                                                                              











                                                                                                   









                                                                                                    
                                                         




              
                                                                                 
                                                                        





































                                                                                                    
                
                 


         






                                                                                                    
                                                     




















                                                                                                    
                
                 

         







                                                                                                  
                                                     







                                                                      
 
 
                                                                                     
























                                                                                                    











                                                                                                   


                                            



                                                               


                                                                                                   
                                                   
                                         
                                                                    





                                              
                                                        


                 



                                                                 
                                                                                      






                                                                                            


                                                    
                                        


                                                            


                                    
                                                        


                 



                                                               


                                                                                                   
                                                    
                                         
                                                                  





                                              
                                                        


                 



                                                                 
                                                                                      






                                                                                            


                                                     
                                        


                                                            


                                    
                                                        


                 

      
 


                               
                                              




                                                               


                                                                                                   
                                                      
                                         
                                                                    
                                              
                                                                    





                                    
                                                        




                                         
                                                                 
                            
                                         
                                                                                      







                                                                                            
                                                       
                                         
                                        

                                                                    




                                    
                                                        


                 




                                                               


                                                                                                   
                                                       
                                         
                                                                  
                                              
                                                                    





                                    
                                                        




                                          
                                                                 
                            
                                         
                                                                                      







                                                                                            
                                                        
                                         
                                        

                                                                    




                                    
                                                        






                     
                        
 












                                                                                      
// 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<u64>;
}

impl FileGetLen for File {
    fn get_len(&self) -> Result<u64> {
        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<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.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).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.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).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.size() > 0 {
            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.size() > 0 {
            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)
    }
}

// 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<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 {
                    $crate::file_traits::lib::read(
                        self.as_raw_fd(),
                        slice.as_mut_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<usize> {
                let iovecs = $crate::file_traits::lib::VolatileSlice::as_iovecs(bufs);

                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.as_ptr(),
                        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<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 {
                    $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<usize> {
                let iovecs = $crate::file_traits::lib::VolatileSlice::as_iovecs(bufs);

                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.as_ptr(),
                        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<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 {
                    $crate::file_traits::lib::pread64(
                        self.as_raw_fd(),
                        slice.as_mut_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<usize> {
                let iovecs = $crate::file_traits::lib::VolatileSlice::as_iovecs(bufs);

                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.as_ptr(),
                        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<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 {
                    $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<usize> {
                let iovecs = $crate::file_traits::lib::VolatileSlice::as_iovecs(bufs);

                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.as_ptr(),
                        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<RawFd>;
}

impl<T> AsRawFds for T
where
    T: AsRawFd,
{
    fn as_raw_fds(&self) -> Vec<RawFd> {
        vec![self.as_raw_fd()]
    }
}