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