1use std::fs::File;
10use std::io::{Error, Result};
11use std::os::unix::io::AsRawFd;
12
13#[cfg(target_env = "musl")]
14use libc::{c_int, lseek64, ENXIO};
15
16#[cfg(target_env = "gnu")]
17use libc::{lseek64, ENXIO, SEEK_DATA, SEEK_HOLE};
18
19#[cfg(all(not(target_env = "musl"), target_os = "android"))]
20use libc::{lseek64, ENXIO, SEEK_DATA, SEEK_HOLE};
21
22pub trait SeekHole {
24 fn seek_hole(&mut self, offset: u64) -> Result<Option<u64>>;
33
34 fn seek_data(&mut self, offset: u64) -> Result<Option<u64>>;
42}
43
44#[cfg(target_env = "musl")]
45const SEEK_DATA: c_int = 3;
46#[cfg(target_env = "musl")]
47const SEEK_HOLE: c_int = 4;
48
49fn lseek(file: &mut File, offset: i64, whence: i32) -> Result<Option<u64>> {
51 let res = unsafe { lseek64(file.as_raw_fd(), offset, whence) };
53
54 if res < 0 {
55 let err = Error::last_os_error();
57 if let Some(errno) = Error::raw_os_error(&err) {
58 if errno == ENXIO {
59 return Ok(None);
60 }
61 }
62 Err(err)
63 } else {
64 Ok(Some(res as u64))
65 }
66}
67
68impl SeekHole for File {
69 fn seek_hole(&mut self, offset: u64) -> Result<Option<u64>> {
70 lseek(self, offset as i64, SEEK_HOLE)
71 }
72
73 fn seek_data(&mut self, offset: u64) -> Result<Option<u64>> {
74 lseek(self, offset as i64, SEEK_DATA)
75 }
76}
77
78#[cfg(test)]
79mod tests {
80 use super::*;
81 use crate::tempdir::TempDir;
82 use std::fs::File;
83 use std::io::{Seek, SeekFrom, Write};
84 use std::path::PathBuf;
85
86 fn seek_cur(file: &mut File) -> u64 {
87 file.stream_position().unwrap()
88 }
89
90 #[test]
91 fn seek_data() {
92 let tempdir = TempDir::new_with_prefix("/tmp/seek_data_test").unwrap();
93 let mut path = PathBuf::from(tempdir.as_path());
94 path.push("test_file");
95 let mut file = File::create(&path).unwrap();
96
97 assert_eq!(file.seek_data(0).unwrap(), None);
99 assert_eq!(seek_cur(&mut file), 0);
100
101 file.set_len(0x10000).unwrap();
103 assert_eq!(file.seek_data(0).unwrap(), None);
104 assert_eq!(seek_cur(&mut file), 0);
105
106 assert_eq!(file.seek_data(0x10000).unwrap(), None);
108 assert_eq!(seek_cur(&mut file), 0);
109 assert_eq!(file.seek_data(0x10001).unwrap(), None);
110 assert_eq!(seek_cur(&mut file), 0);
111
112 let b = [0x55u8; 0x10000];
114 file.seek(SeekFrom::Start(0x10000)).unwrap();
115 file.write_all(&b).unwrap();
116 assert_eq!(file.seek_data(0).unwrap(), Some(0x10000));
117 assert_eq!(seek_cur(&mut file), 0x10000);
118
119 assert_eq!(file.seek_data(0x10000).unwrap(), Some(0x10000));
121 assert_eq!(seek_cur(&mut file), 0x10000);
122 assert_eq!(file.seek_data(0x10001).unwrap(), Some(0x10001));
123 assert_eq!(seek_cur(&mut file), 0x10001);
124 assert_eq!(file.seek_data(0x1FFFF).unwrap(), Some(0x1FFFF));
125 assert_eq!(seek_cur(&mut file), 0x1FFFF);
126
127 file.set_len(0x30000).unwrap();
129 assert_eq!(file.seek_data(0).unwrap(), Some(0x10000));
130 assert_eq!(seek_cur(&mut file), 0x10000);
131 assert_eq!(file.seek_data(0x1FFFF).unwrap(), Some(0x1FFFF));
132 assert_eq!(seek_cur(&mut file), 0x1FFFF);
133 assert_eq!(file.seek_data(0x20000).unwrap(), None);
134 assert_eq!(seek_cur(&mut file), 0x1FFFF);
135 }
136
137 #[test]
138 #[allow(clippy::cognitive_complexity)]
139 fn seek_hole() {
140 let tempdir = TempDir::new_with_prefix("/tmp/seek_hole_test").unwrap();
141 let mut path = PathBuf::from(tempdir.as_path());
142 path.push("test_file");
143 let mut file = File::create(&path).unwrap();
144
145 assert_eq!(file.seek_hole(0).unwrap(), None);
147 assert_eq!(seek_cur(&mut file), 0);
148
149 file.set_len(0x10000).unwrap();
151 assert_eq!(file.seek_hole(0).unwrap(), Some(0));
152 assert_eq!(seek_cur(&mut file), 0);
153 assert_eq!(file.seek_hole(0xFFFF).unwrap(), Some(0xFFFF));
154 assert_eq!(seek_cur(&mut file), 0xFFFF);
155
156 file.rewind().unwrap();
158 assert_eq!(file.seek_hole(0x10000).unwrap(), None);
159 assert_eq!(seek_cur(&mut file), 0);
160 assert_eq!(file.seek_hole(0x10001).unwrap(), None);
161 assert_eq!(seek_cur(&mut file), 0);
162
163 let b = [0x55u8; 0x10000];
165 file.seek(SeekFrom::Start(0x10000)).unwrap();
166 file.write_all(&b).unwrap();
167
168 assert_eq!(file.seek_hole(0).unwrap(), Some(0));
170 assert_eq!(seek_cur(&mut file), 0);
171 assert_eq!(file.seek_hole(0xFFFF).unwrap(), Some(0xFFFF));
172 assert_eq!(seek_cur(&mut file), 0xFFFF);
173
174 file.rewind().unwrap();
176 assert_eq!(file.seek_hole(0x10000).unwrap(), Some(0x20000));
177 assert_eq!(seek_cur(&mut file), 0x20000);
178 file.rewind().unwrap();
179 assert_eq!(file.seek_hole(0x10001).unwrap(), Some(0x20000));
180 assert_eq!(seek_cur(&mut file), 0x20000);
181 file.rewind().unwrap();
182 assert_eq!(file.seek_hole(0x1FFFF).unwrap(), Some(0x20000));
183 assert_eq!(seek_cur(&mut file), 0x20000);
184
185 file.rewind().unwrap();
187 assert_eq!(file.seek_hole(0x20000).unwrap(), None);
188 assert_eq!(seek_cur(&mut file), 0);
189
190 file.set_len(0x30000).unwrap();
192 assert_eq!(file.seek_hole(0).unwrap(), Some(0));
193 assert_eq!(seek_cur(&mut file), 0);
194 assert_eq!(file.seek_hole(0xFFFF).unwrap(), Some(0xFFFF));
195 assert_eq!(seek_cur(&mut file), 0xFFFF);
196 file.rewind().unwrap();
197 assert_eq!(file.seek_hole(0x10000).unwrap(), Some(0x20000));
198 assert_eq!(seek_cur(&mut file), 0x20000);
199 file.rewind().unwrap();
200 assert_eq!(file.seek_hole(0x1FFFF).unwrap(), Some(0x20000));
201 assert_eq!(seek_cur(&mut file), 0x20000);
202 file.rewind().unwrap();
203 assert_eq!(file.seek_hole(0x20000).unwrap(), Some(0x20000));
204 assert_eq!(seek_cur(&mut file), 0x20000);
205 file.rewind().unwrap();
206 assert_eq!(file.seek_hole(0x20001).unwrap(), Some(0x20001));
207 assert_eq!(seek_cur(&mut file), 0x20001);
208
209 file.rewind().unwrap();
211 assert_eq!(file.seek_hole(0x30000).unwrap(), None);
212 assert_eq!(seek_cur(&mut file), 0);
213
214 file.seek(SeekFrom::Start(0x20000)).unwrap();
216 file.write_all(&b).unwrap();
217
218 assert_eq!(file.seek_hole(0x20000).unwrap(), Some(0x30000));
220 assert_eq!(seek_cur(&mut file), 0x30000);
221 file.rewind().unwrap();
222 assert_eq!(file.seek_hole(0x20001).unwrap(), Some(0x30000));
223 assert_eq!(seek_cur(&mut file), 0x30000);
224 file.rewind().unwrap();
225 assert_eq!(file.seek_hole(0x30000).unwrap(), None);
226 assert_eq!(seek_cur(&mut file), 0);
227 }
228}