xref: /aosp_15_r20/external/crosvm/base/src/sys/linux/file.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1*bb4ee6a4SAndroid Build Coastguard Worker // Copyright 2022 The ChromiumOS Authors
2*bb4ee6a4SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*bb4ee6a4SAndroid Build Coastguard Worker // found in the LICENSE file.
4*bb4ee6a4SAndroid Build Coastguard Worker 
5*bb4ee6a4SAndroid Build Coastguard Worker #![deny(missing_docs)]
6*bb4ee6a4SAndroid Build Coastguard Worker 
7*bb4ee6a4SAndroid Build Coastguard Worker use std::ops::Range;
8*bb4ee6a4SAndroid Build Coastguard Worker 
9*bb4ee6a4SAndroid Build Coastguard Worker use crate::AsRawDescriptor;
10*bb4ee6a4SAndroid Build Coastguard Worker use crate::Error;
11*bb4ee6a4SAndroid Build Coastguard Worker use crate::Result;
12*bb4ee6a4SAndroid Build Coastguard Worker 
13*bb4ee6a4SAndroid Build Coastguard Worker enum LseekOption {
14*bb4ee6a4SAndroid Build Coastguard Worker     Data,
15*bb4ee6a4SAndroid Build Coastguard Worker     Hole,
16*bb4ee6a4SAndroid Build Coastguard Worker }
17*bb4ee6a4SAndroid Build Coastguard Worker 
lseek(fd: &dyn AsRawDescriptor, offset: u64, option: LseekOption) -> Result<u64>18*bb4ee6a4SAndroid Build Coastguard Worker fn lseek(fd: &dyn AsRawDescriptor, offset: u64, option: LseekOption) -> Result<u64> {
19*bb4ee6a4SAndroid Build Coastguard Worker     let whence = match option {
20*bb4ee6a4SAndroid Build Coastguard Worker         LseekOption::Data => libc::SEEK_DATA,
21*bb4ee6a4SAndroid Build Coastguard Worker         LseekOption::Hole => libc::SEEK_HOLE,
22*bb4ee6a4SAndroid Build Coastguard Worker     };
23*bb4ee6a4SAndroid Build Coastguard Worker     // SAFETY:
24*bb4ee6a4SAndroid Build Coastguard Worker     // safe because this doesn't modify any memory.
25*bb4ee6a4SAndroid Build Coastguard Worker     let ret = unsafe { libc::lseek64(fd.as_raw_descriptor(), offset as i64, whence) };
26*bb4ee6a4SAndroid Build Coastguard Worker     if ret < 0 {
27*bb4ee6a4SAndroid Build Coastguard Worker         return Err(Error::last());
28*bb4ee6a4SAndroid Build Coastguard Worker     }
29*bb4ee6a4SAndroid Build Coastguard Worker     Ok(ret as u64)
30*bb4ee6a4SAndroid Build Coastguard Worker }
31*bb4ee6a4SAndroid Build Coastguard Worker 
32*bb4ee6a4SAndroid Build Coastguard Worker /// Find the offset range of the next data in the file.
33*bb4ee6a4SAndroid Build Coastguard Worker ///
34*bb4ee6a4SAndroid Build Coastguard Worker /// # Arguments
35*bb4ee6a4SAndroid Build Coastguard Worker ///
36*bb4ee6a4SAndroid Build Coastguard Worker /// * `fd` - the [trait@AsRawDescriptor] of the file
37*bb4ee6a4SAndroid Build Coastguard Worker /// * `offset` - the offset to start traversing from
38*bb4ee6a4SAndroid Build Coastguard Worker /// * `len` - the len of the region over which to traverse
find_next_data( fd: &dyn AsRawDescriptor, offset: u64, len: u64, ) -> Result<Option<Range<u64>>>39*bb4ee6a4SAndroid Build Coastguard Worker pub fn find_next_data(
40*bb4ee6a4SAndroid Build Coastguard Worker     fd: &dyn AsRawDescriptor,
41*bb4ee6a4SAndroid Build Coastguard Worker     offset: u64,
42*bb4ee6a4SAndroid Build Coastguard Worker     len: u64,
43*bb4ee6a4SAndroid Build Coastguard Worker ) -> Result<Option<Range<u64>>> {
44*bb4ee6a4SAndroid Build Coastguard Worker     let end = offset + len;
45*bb4ee6a4SAndroid Build Coastguard Worker     let offset_data = match lseek(fd, offset, LseekOption::Data) {
46*bb4ee6a4SAndroid Build Coastguard Worker         Ok(offset) => {
47*bb4ee6a4SAndroid Build Coastguard Worker             if offset >= end {
48*bb4ee6a4SAndroid Build Coastguard Worker                 return Ok(None);
49*bb4ee6a4SAndroid Build Coastguard Worker             } else {
50*bb4ee6a4SAndroid Build Coastguard Worker                 offset
51*bb4ee6a4SAndroid Build Coastguard Worker             }
52*bb4ee6a4SAndroid Build Coastguard Worker         }
53*bb4ee6a4SAndroid Build Coastguard Worker         Err(e) => {
54*bb4ee6a4SAndroid Build Coastguard Worker             return match e.errno() {
55*bb4ee6a4SAndroid Build Coastguard Worker                 libc::ENXIO => Ok(None),
56*bb4ee6a4SAndroid Build Coastguard Worker                 _ => Err(e),
57*bb4ee6a4SAndroid Build Coastguard Worker             }
58*bb4ee6a4SAndroid Build Coastguard Worker         }
59*bb4ee6a4SAndroid Build Coastguard Worker     };
60*bb4ee6a4SAndroid Build Coastguard Worker     let offset_hole = lseek(fd, offset_data, LseekOption::Hole)?;
61*bb4ee6a4SAndroid Build Coastguard Worker 
62*bb4ee6a4SAndroid Build Coastguard Worker     Ok(Some(offset_data..offset_hole.min(end)))
63*bb4ee6a4SAndroid Build Coastguard Worker }
64*bb4ee6a4SAndroid Build Coastguard Worker 
65*bb4ee6a4SAndroid Build Coastguard Worker /// Iterator returning the offset range of data in the file.
66*bb4ee6a4SAndroid Build Coastguard Worker ///
67*bb4ee6a4SAndroid Build Coastguard Worker /// This uses `lseek(2)` internally, and thus it changes the file offset.
68*bb4ee6a4SAndroid Build Coastguard Worker pub struct FileDataIterator<'a> {
69*bb4ee6a4SAndroid Build Coastguard Worker     fd: &'a dyn AsRawDescriptor,
70*bb4ee6a4SAndroid Build Coastguard Worker     offset: u64,
71*bb4ee6a4SAndroid Build Coastguard Worker     end: u64,
72*bb4ee6a4SAndroid Build Coastguard Worker     failed: bool,
73*bb4ee6a4SAndroid Build Coastguard Worker }
74*bb4ee6a4SAndroid Build Coastguard Worker 
75*bb4ee6a4SAndroid Build Coastguard Worker impl<'a> FileDataIterator<'a> {
76*bb4ee6a4SAndroid Build Coastguard Worker     /// Creates the [FileDataIterator]
77*bb4ee6a4SAndroid Build Coastguard Worker     ///
78*bb4ee6a4SAndroid Build Coastguard Worker     /// # Arguments
79*bb4ee6a4SAndroid Build Coastguard Worker     ///
80*bb4ee6a4SAndroid Build Coastguard Worker     /// * `fd` - the [trait@AsRawDescriptor] of the file
81*bb4ee6a4SAndroid Build Coastguard Worker     /// * `offset` - the offset to start traversing from.
82*bb4ee6a4SAndroid Build Coastguard Worker     /// * `len` - the len of the region over which to iterate
new(fd: &'a dyn AsRawDescriptor, offset: u64, len: u64) -> Self83*bb4ee6a4SAndroid Build Coastguard Worker     pub fn new(fd: &'a dyn AsRawDescriptor, offset: u64, len: u64) -> Self {
84*bb4ee6a4SAndroid Build Coastguard Worker         Self {
85*bb4ee6a4SAndroid Build Coastguard Worker             fd,
86*bb4ee6a4SAndroid Build Coastguard Worker             offset,
87*bb4ee6a4SAndroid Build Coastguard Worker             end: offset + len,
88*bb4ee6a4SAndroid Build Coastguard Worker             failed: false,
89*bb4ee6a4SAndroid Build Coastguard Worker         }
90*bb4ee6a4SAndroid Build Coastguard Worker     }
91*bb4ee6a4SAndroid Build Coastguard Worker }
92*bb4ee6a4SAndroid Build Coastguard Worker 
93*bb4ee6a4SAndroid Build Coastguard Worker impl<'a> Iterator for FileDataIterator<'a> {
94*bb4ee6a4SAndroid Build Coastguard Worker     type Item = Result<Range<u64>>;
95*bb4ee6a4SAndroid Build Coastguard Worker 
next(&mut self) -> Option<Self::Item>96*bb4ee6a4SAndroid Build Coastguard Worker     fn next(&mut self) -> Option<Self::Item> {
97*bb4ee6a4SAndroid Build Coastguard Worker         if self.failed {
98*bb4ee6a4SAndroid Build Coastguard Worker             return None;
99*bb4ee6a4SAndroid Build Coastguard Worker         }
100*bb4ee6a4SAndroid Build Coastguard Worker         match find_next_data(self.fd, self.offset, self.end - self.offset) {
101*bb4ee6a4SAndroid Build Coastguard Worker             Ok(data_range) => {
102*bb4ee6a4SAndroid Build Coastguard Worker                 if let Some(ref data_range) = data_range {
103*bb4ee6a4SAndroid Build Coastguard Worker                     self.offset = data_range.end;
104*bb4ee6a4SAndroid Build Coastguard Worker                 }
105*bb4ee6a4SAndroid Build Coastguard Worker                 data_range.map(Ok)
106*bb4ee6a4SAndroid Build Coastguard Worker             }
107*bb4ee6a4SAndroid Build Coastguard Worker             Err(e) => {
108*bb4ee6a4SAndroid Build Coastguard Worker                 self.failed = true;
109*bb4ee6a4SAndroid Build Coastguard Worker                 Some(Err(e))
110*bb4ee6a4SAndroid Build Coastguard Worker             }
111*bb4ee6a4SAndroid Build Coastguard Worker         }
112*bb4ee6a4SAndroid Build Coastguard Worker     }
113*bb4ee6a4SAndroid Build Coastguard Worker }
114*bb4ee6a4SAndroid Build Coastguard Worker 
115*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(test)]
116*bb4ee6a4SAndroid Build Coastguard Worker mod tests {
117*bb4ee6a4SAndroid Build Coastguard Worker     use std::os::unix::fs::FileExt;
118*bb4ee6a4SAndroid Build Coastguard Worker 
119*bb4ee6a4SAndroid Build Coastguard Worker     use super::*;
120*bb4ee6a4SAndroid Build Coastguard Worker     use crate::pagesize;
121*bb4ee6a4SAndroid Build Coastguard Worker 
122*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
file_data_iterator()123*bb4ee6a4SAndroid Build Coastguard Worker     fn file_data_iterator() {
124*bb4ee6a4SAndroid Build Coastguard Worker         let file = tempfile::tempfile().unwrap();
125*bb4ee6a4SAndroid Build Coastguard Worker 
126*bb4ee6a4SAndroid Build Coastguard Worker         file.write_at(&[1_u8], 10).unwrap();
127*bb4ee6a4SAndroid Build Coastguard Worker         file.write_at(&[1_u8], 2 * pagesize() as u64).unwrap();
128*bb4ee6a4SAndroid Build Coastguard Worker         file.write_at(&[1_u8], (4 * pagesize() - 1) as u64).unwrap();
129*bb4ee6a4SAndroid Build Coastguard Worker 
130*bb4ee6a4SAndroid Build Coastguard Worker         let iter = FileDataIterator::new(&file, 0, 4 * pagesize() as u64);
131*bb4ee6a4SAndroid Build Coastguard Worker 
132*bb4ee6a4SAndroid Build Coastguard Worker         let result = iter.collect::<Result<Vec<Range<u64>>>>().unwrap();
133*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(result.len(), 2);
134*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(result[0], 0..(pagesize() as u64));
135*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(result[1], (2 * pagesize() as u64)..(4 * pagesize() as u64));
136*bb4ee6a4SAndroid Build Coastguard Worker     }
137*bb4ee6a4SAndroid Build Coastguard Worker 
138*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
file_data_iterator_subrange()139*bb4ee6a4SAndroid Build Coastguard Worker     fn file_data_iterator_subrange() {
140*bb4ee6a4SAndroid Build Coastguard Worker         let file = tempfile::tempfile().unwrap();
141*bb4ee6a4SAndroid Build Coastguard Worker 
142*bb4ee6a4SAndroid Build Coastguard Worker         file.write_at(&[1_u8], 0).unwrap();
143*bb4ee6a4SAndroid Build Coastguard Worker         file.write_at(&[1_u8], pagesize() as u64).unwrap();
144*bb4ee6a4SAndroid Build Coastguard Worker         file.write_at(&[1_u8], 2 * pagesize() as u64).unwrap();
145*bb4ee6a4SAndroid Build Coastguard Worker         file.write_at(&[1_u8], 4 * pagesize() as u64).unwrap();
146*bb4ee6a4SAndroid Build Coastguard Worker 
147*bb4ee6a4SAndroid Build Coastguard Worker         let iter = FileDataIterator::new(&file, pagesize() as u64, pagesize() as u64);
148*bb4ee6a4SAndroid Build Coastguard Worker 
149*bb4ee6a4SAndroid Build Coastguard Worker         let result = iter.collect::<Result<Vec<Range<u64>>>>().unwrap();
150*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(result.len(), 1);
151*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(result[0], (pagesize() as u64)..(2 * pagesize() as u64));
152*bb4ee6a4SAndroid Build Coastguard Worker     }
153*bb4ee6a4SAndroid Build Coastguard Worker }
154