1use crate::CoreError;
12use std::os::unix::io::AsRawFd;
13use std::path::Path;
14use std::time::UNIX_EPOCH;
15
16#[derive(Clone, Debug, PartialEq, Eq)]
17pub struct PathFingerprint {
18 pub len: u64,
19 pub modified_ns: u128,
20}
21
22pub fn path_fingerprint(path: &Path) -> Result<PathFingerprint, CoreError> {
28 let metadata = std::fs::metadata(path).map_err(|err| {
29 CoreError::sys(err.raw_os_error().unwrap_or(libc::EIO), "path_fingerprint")
30 })?;
31 let modified_ns = metadata
32 .modified()
33 .ok()
34 .and_then(|modified| modified.duration_since(UNIX_EPOCH).ok())
35 .map(|duration| duration.as_nanos())
36 .unwrap_or_default();
37 Ok(PathFingerprint {
38 len: metadata.len(),
39 modified_ns,
40 })
41}
42
43pub fn path_exists(path: &str) -> bool {
50 match std::ffi::CString::new(path) {
51 Ok(c) => unsafe { libc::access(c.as_ptr(), libc::F_OK) == 0 },
52 Err(_) => false,
53 }
54}
55
56pub fn path_lstat_exists(path: &str) -> bool {
60 match std::ffi::CString::new(path) {
61 Ok(c) => unsafe {
62 let mut stat = std::mem::zeroed();
63 libc::lstat(c.as_ptr(), &mut stat) == 0
64 },
65 Err(_) => false,
66 }
67}
68
69pub fn read_to_string(path: &str) -> Result<String, CoreError> {
80 std::fs::read_to_string(path)
81 .map_err(|err| CoreError::sys(err.raw_os_error().unwrap_or(libc::EIO), "read_to_string"))
82}
83
84pub fn readahead(fd: impl AsRawFd, offset: u64, len: usize) -> Result<(), CoreError> {
99 readahead_raw(fd.as_raw_fd(), offset, len)
100}
101
102pub const FADV_NORMAL: i32 = libc::POSIX_FADV_NORMAL;
105pub const FADV_RANDOM: i32 = libc::POSIX_FADV_RANDOM;
106pub const FADV_SEQUENTIAL: i32 = libc::POSIX_FADV_SEQUENTIAL;
107pub const FADV_WILLNEED: i32 = libc::POSIX_FADV_WILLNEED;
108pub const FADV_DONTNEED: i32 = libc::POSIX_FADV_DONTNEED;
109pub const FADV_NOREUSE: i32 = libc::POSIX_FADV_NOREUSE;
110
111pub fn fadvise(fd: impl AsRawFd, offset: u64, len: usize, advice: i32) -> Result<(), CoreError> {
124 let ret = unsafe {
125 libc::posix_fadvise(
126 fd.as_raw_fd(),
127 offset as libc::off_t,
128 len as libc::off_t,
129 advice,
130 )
131 };
132 if ret == 0 {
133 Ok(())
134 } else {
135 Err(CoreError::sys(ret, "posix_fadvise"))
136 }
137}
138
139pub fn mmap_madvise(
150 fd: impl AsRawFd,
151 offset: u64,
152 len: usize,
153 touch: bool,
154) -> Result<(), CoreError> {
155 mmap_madvise_raw(fd.as_raw_fd(), offset, len, touch)
156}
157
158#[cfg(any(target_os = "linux", target_os = "android"))]
159fn mmap_madvise_raw(
160 fd: libc::c_int,
161 offset: u64,
162 len: usize,
163 touch: bool,
164) -> Result<(), CoreError> {
165 if len == 0 {
166 return Ok(());
167 }
168
169 let page_size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) };
170 if page_size <= 0 {
171 return Err(CoreError::sys(libc::EINVAL, "sysconf(_SC_PAGESIZE)"));
172 }
173 let page_size = page_size as u64;
174 if offset % page_size != 0 || offset > libc::off_t::MAX as u64 {
175 return Err(CoreError::sys(libc::EINVAL, "mmap"));
176 }
177
178 let ptr = unsafe {
179 libc::mmap(
180 std::ptr::null_mut(),
181 len,
182 libc::PROT_READ,
183 libc::MAP_PRIVATE,
184 fd,
185 offset as libc::off_t,
186 )
187 };
188 if ptr == libc::MAP_FAILED {
189 let code = std::io::Error::last_os_error().raw_os_error().unwrap_or(0);
190 return Err(CoreError::sys(code, "mmap"));
191 }
192
193 let result = if unsafe { libc::madvise(ptr, len, libc::MADV_WILLNEED) } == -1 {
194 let code = std::io::Error::last_os_error().raw_os_error().unwrap_or(0);
195 Err(CoreError::sys(code, "madvise"))
196 } else {
197 if touch {
198 let mut pos = 0usize;
199 let page_size = page_size as usize;
200 while pos < len {
201 unsafe {
202 std::ptr::read_volatile((ptr as *const u8).add(pos));
203 }
204 pos = pos.saturating_add(page_size);
205 }
206 }
207 Ok(())
208 };
209
210 if unsafe { libc::munmap(ptr, len) } == -1 {
211 let code = std::io::Error::last_os_error().raw_os_error().unwrap_or(0);
212 return Err(CoreError::sys(code, "munmap"));
213 }
214 result
215}
216
217#[cfg(not(any(target_os = "linux", target_os = "android")))]
218fn mmap_madvise_raw(
219 _fd: libc::c_int,
220 _offset: u64,
221 _len: usize,
222 _touch: bool,
223) -> Result<(), CoreError> {
224 Err(CoreError::sys(libc::ENOSYS, "mmap"))
225}
226
227#[cfg(any(target_os = "linux", target_os = "android"))]
228fn readahead_raw(fd: libc::c_int, offset: u64, len: usize) -> Result<(), CoreError> {
229 if offset > libc::off64_t::MAX as u64 {
230 return Err(CoreError::sys(libc::EINVAL, "readahead"));
231 }
232
233 let count = len as libc::size_t;
234 let offset = offset as libc::off64_t;
235
236 loop {
237 let ret = unsafe { libc::syscall(readahead_syscall_number(), fd, offset, count) };
238 if ret == -1 {
239 let code = std::io::Error::last_os_error().raw_os_error().unwrap_or(0);
240 if code == libc::EINTR {
241 continue;
242 }
243 return Err(CoreError::sys(code, "readahead"));
244 }
245 return Ok(());
246 }
247}
248
249#[cfg(not(any(target_os = "linux", target_os = "android")))]
250fn readahead_raw(_fd: libc::c_int, _offset: u64, _len: usize) -> Result<(), CoreError> {
251 Err(CoreError::sys(libc::ENOSYS, "readahead"))
252}
253
254#[cfg(target_os = "linux")]
255#[inline(always)]
256const fn readahead_syscall_number() -> libc::c_long {
257 libc::SYS_readahead
258}
259
260#[cfg(all(target_os = "android", target_arch = "aarch64"))]
261#[inline(always)]
262const fn readahead_syscall_number() -> libc::c_long {
263 213
264}
265
266#[cfg(all(target_os = "android", target_arch = "arm"))]
267#[inline(always)]
268const fn readahead_syscall_number() -> libc::c_long {
269 225
270}
271
272#[cfg(all(target_os = "android", target_arch = "x86_64"))]
273#[inline(always)]
274const fn readahead_syscall_number() -> libc::c_long {
275 187
276}
277
278#[cfg(all(target_os = "android", target_arch = "x86"))]
279#[inline(always)]
280const fn readahead_syscall_number() -> libc::c_long {
281 225
282}
283
284#[cfg(test)]
285mod tests {
286 #[cfg(target_os = "linux")]
287 #[test]
288 fn test_readahead_syscall_number_linux_matches_libc() {
289 assert_eq!(super::readahead_syscall_number(), libc::SYS_readahead);
290 }
291
292 #[cfg(all(target_os = "android", target_arch = "aarch64"))]
293 #[test]
294 fn test_readahead_syscall_number_android_aarch64() {
295 assert_eq!(super::readahead_syscall_number(), 213);
296 }
297
298 #[cfg(all(target_os = "android", target_arch = "arm"))]
299 #[test]
300 fn test_readahead_syscall_number_android_arm() {
301 assert_eq!(super::readahead_syscall_number(), 225);
302 }
303
304 #[cfg(all(target_os = "android", target_arch = "x86_64"))]
305 #[test]
306 fn test_readahead_syscall_number_android_x86_64() {
307 assert_eq!(super::readahead_syscall_number(), 187);
308 }
309
310 #[cfg(all(target_os = "android", target_arch = "x86"))]
311 #[test]
312 fn test_readahead_syscall_number_android_x86() {
313 assert_eq!(super::readahead_syscall_number(), 225);
314 }
315}