1 // Copyright 2021 The Chromium OS Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 use std::{cmp::min, convert::TryFrom, fs::File as StdFile, io, path::Path, sync::Arc}; 6 7 use sys_util::{AsRawDescriptor, SafeDescriptor}; 8 9 use super::io_driver; 10 use crate::{AsIoBufs, OwnedIoBuf}; 11 12 #[derive(Debug)] 13 pub struct File { 14 fd: Arc<SafeDescriptor>, 15 } 16 17 impl File { open<P: AsRef<Path>>(p: P) -> anyhow::Result<File>18 pub fn open<P: AsRef<Path>>(p: P) -> anyhow::Result<File> { 19 let f = StdFile::open(p)?; 20 File::try_from(f) 21 } 22 create<P: AsRef<Path>>(p: P) -> anyhow::Result<File>23 pub fn create<P: AsRef<Path>>(p: P) -> anyhow::Result<File> { 24 let f = StdFile::create(p)?; 25 File::try_from(f) 26 } 27 read(&self, buf: &mut [u8], offset: Option<u64>) -> anyhow::Result<usize>28 pub async fn read(&self, buf: &mut [u8], offset: Option<u64>) -> anyhow::Result<usize> { 29 io_driver::read(&self.fd, buf, offset).await 30 } 31 read_iobuf<B: AsIoBufs + Unpin + 'static>( &self, buf: B, offset: Option<u64>, ) -> (anyhow::Result<usize>, B)32 pub async fn read_iobuf<B: AsIoBufs + Unpin + 'static>( 33 &self, 34 buf: B, 35 offset: Option<u64>, 36 ) -> (anyhow::Result<usize>, B) { 37 io_driver::read_iobuf(&self.fd, buf, offset).await 38 } 39 write(&self, buf: &[u8], offset: Option<u64>) -> anyhow::Result<usize>40 pub async fn write(&self, buf: &[u8], offset: Option<u64>) -> anyhow::Result<usize> { 41 io_driver::write(&self.fd, buf, offset).await 42 } 43 write_iobuf<B: AsIoBufs + Unpin + 'static>( &self, buf: B, offset: Option<u64>, ) -> (anyhow::Result<usize>, B)44 pub async fn write_iobuf<B: AsIoBufs + Unpin + 'static>( 45 &self, 46 buf: B, 47 offset: Option<u64>, 48 ) -> (anyhow::Result<usize>, B) { 49 io_driver::write_iobuf(&self.fd, buf, offset).await 50 } 51 punch_hole(&self, offset: u64, len: u64) -> anyhow::Result<()>52 pub async fn punch_hole(&self, offset: u64, len: u64) -> anyhow::Result<()> { 53 io_driver::fallocate( 54 &self.fd, 55 offset, 56 len, 57 (libc::FALLOC_FL_PUNCH_HOLE | libc::FALLOC_FL_KEEP_SIZE) as u32, 58 ) 59 .await 60 } 61 write_zeroes(&self, offset: u64, len: u64) -> anyhow::Result<()>62 pub async fn write_zeroes(&self, offset: u64, len: u64) -> anyhow::Result<()> { 63 let res = io_driver::fallocate( 64 &self.fd, 65 offset, 66 len, 67 (libc::FALLOC_FL_ZERO_RANGE | libc::FALLOC_FL_KEEP_SIZE) as u32, 68 ) 69 .await; 70 match res { 71 Ok(()) => Ok(()), 72 Err(_) => { 73 // Fall back to writing zeros if fallocate doesn't work. 74 let buf_size = min(len, 0x10000); 75 let mut buf = OwnedIoBuf::new(vec![0u8; buf_size as usize]); 76 let mut nwritten = 0; 77 while nwritten < len { 78 buf.reset(); 79 let remaining = len - nwritten; 80 let write_size = min(remaining, buf_size) as usize; 81 buf.truncate(write_size); 82 83 let (res, b) = self.write_iobuf(buf, Some(offset + nwritten as u64)).await; 84 nwritten += res? as u64; 85 buf = b; 86 } 87 Ok(()) 88 } 89 } 90 } 91 allocate(&self, offset: u64, len: u64) -> anyhow::Result<()>92 pub async fn allocate(&self, offset: u64, len: u64) -> anyhow::Result<()> { 93 io_driver::fallocate(&self.fd, offset, len, 0).await 94 } 95 get_len(&self) -> anyhow::Result<u64>96 pub async fn get_len(&self) -> anyhow::Result<u64> { 97 io_driver::stat(&self.fd).await.map(|st| st.st_size as u64) 98 } 99 set_len(&self, len: u64) -> anyhow::Result<()>100 pub async fn set_len(&self, len: u64) -> anyhow::Result<()> { 101 io_driver::ftruncate(&self.fd, len).await 102 } 103 sync_all(&self) -> anyhow::Result<()>104 pub async fn sync_all(&self) -> anyhow::Result<()> { 105 io_driver::fsync(&self.fd, false).await 106 } 107 sync_data(&self) -> anyhow::Result<()>108 pub async fn sync_data(&self) -> anyhow::Result<()> { 109 io_driver::fsync(&self.fd, true).await 110 } 111 try_clone(&self) -> anyhow::Result<File>112 pub fn try_clone(&self) -> anyhow::Result<File> { 113 self.fd 114 .try_clone() 115 .map(|fd| File { fd: Arc::new(fd) }) 116 .map_err(io::Error::from) 117 .map_err(From::from) 118 } 119 } 120 121 impl TryFrom<StdFile> for File { 122 type Error = anyhow::Error; 123 try_from(f: StdFile) -> anyhow::Result<Self>124 fn try_from(f: StdFile) -> anyhow::Result<Self> { 125 io_driver::prepare(&f)?; 126 Ok(File { 127 fd: Arc::new(f.into()), 128 }) 129 } 130 } 131 132 impl TryFrom<File> for StdFile { 133 type Error = File; try_from(f: File) -> Result<StdFile, File>134 fn try_from(f: File) -> Result<StdFile, File> { 135 Arc::try_unwrap(f.fd) 136 .map(From::from) 137 .map_err(|fd| File { fd }) 138 } 139 } 140 141 impl AsRawDescriptor for File { as_raw_descriptor(&self) -> sys_util::RawDescriptor142 fn as_raw_descriptor(&self) -> sys_util::RawDescriptor { 143 self.fd.as_raw_descriptor() 144 } 145 } 146