• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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