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(fd.as_raw_fd(), offset as libc::off_t, len as libc::off_t, advice)
126 };
127 if ret == 0 { Ok(()) } else { Err(CoreError::sys(ret, "posix_fadvise")) }
128}
129
130pub fn mmap_madvise(
141 fd: impl AsRawFd,
142 offset: u64,
143 len: usize,
144 touch: bool,
145) -> Result<(), CoreError> {
146 mmap_madvise_raw(fd.as_raw_fd(), offset, len, touch)
147}
148
149#[cfg(any(target_os = "linux", target_os = "android"))]
150fn mmap_madvise_raw(
151 fd: libc::c_int,
152 offset: u64,
153 len: usize,
154 touch: bool,
155) -> Result<(), CoreError> {
156 if len == 0 {
157 return Ok(());
158 }
159
160 let page_size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) };
161 if page_size <= 0 {
162 return Err(CoreError::sys(libc::EINVAL, "sysconf(_SC_PAGESIZE)"));
163 }
164 let page_size = page_size as u64;
165 if offset % page_size != 0 || offset > libc::off_t::MAX as u64 {
166 return Err(CoreError::sys(libc::EINVAL, "mmap"));
167 }
168
169 let ptr = unsafe {
170 libc::mmap(
171 std::ptr::null_mut(),
172 len,
173 libc::PROT_READ,
174 libc::MAP_PRIVATE,
175 fd,
176 offset as libc::off_t,
177 )
178 };
179 if ptr == libc::MAP_FAILED {
180 let code = std::io::Error::last_os_error().raw_os_error().unwrap_or(0);
181 return Err(CoreError::sys(code, "mmap"));
182 }
183
184 let result = if unsafe { libc::madvise(ptr, len, libc::MADV_WILLNEED) } == -1 {
185 let code = std::io::Error::last_os_error().raw_os_error().unwrap_or(0);
186 Err(CoreError::sys(code, "madvise"))
187 } else {
188 if touch {
189 let mut pos = 0usize;
190 let page_size = page_size as usize;
191 while pos < len {
192 unsafe {
193 std::ptr::read_volatile((ptr as *const u8).add(pos));
194 }
195 pos = pos.saturating_add(page_size);
196 }
197 }
198 Ok(())
199 };
200
201 if unsafe { libc::munmap(ptr, len) } == -1 {
202 let code = std::io::Error::last_os_error().raw_os_error().unwrap_or(0);
203 return Err(CoreError::sys(code, "munmap"));
204 }
205 result
206}
207
208#[cfg(not(any(target_os = "linux", target_os = "android")))]
209fn mmap_madvise_raw(
210 _fd: libc::c_int,
211 _offset: u64,
212 _len: usize,
213 _touch: bool,
214) -> Result<(), CoreError> {
215 Err(CoreError::sys(libc::ENOSYS, "mmap"))
216}
217
218#[cfg(any(target_os = "linux", target_os = "android"))]
219fn readahead_raw(fd: libc::c_int, offset: u64, len: usize) -> Result<(), CoreError> {
220 if offset > libc::off64_t::MAX as u64 {
221 return Err(CoreError::sys(libc::EINVAL, "readahead"));
222 }
223
224 let count = len as libc::size_t;
225 let offset = offset as libc::off64_t;
226
227 loop {
228 let ret = unsafe { libc::syscall(readahead_syscall_number(), fd, offset, count) };
229 if ret == -1 {
230 let code = std::io::Error::last_os_error().raw_os_error().unwrap_or(0);
231 if code == libc::EINTR {
232 continue;
233 }
234 return Err(CoreError::sys(code, "readahead"));
235 }
236 return Ok(());
237 }
238}
239
240#[cfg(not(any(target_os = "linux", target_os = "android")))]
241fn readahead_raw(_fd: libc::c_int, _offset: u64, _len: usize) -> Result<(), CoreError> {
242 Err(CoreError::sys(libc::ENOSYS, "readahead"))
243}
244
245#[cfg(target_os = "linux")]
246#[inline(always)]
247const fn readahead_syscall_number() -> libc::c_long {
248 libc::SYS_readahead
249}
250
251#[cfg(all(target_os = "android", target_arch = "aarch64"))]
252#[inline(always)]
253const fn readahead_syscall_number() -> libc::c_long {
254 213
255}
256
257#[cfg(all(target_os = "android", target_arch = "arm"))]
258#[inline(always)]
259const fn readahead_syscall_number() -> libc::c_long {
260 225
261}
262
263#[cfg(all(target_os = "android", target_arch = "x86_64"))]
264#[inline(always)]
265const fn readahead_syscall_number() -> libc::c_long {
266 187
267}
268
269#[cfg(all(target_os = "android", target_arch = "x86"))]
270#[inline(always)]
271const fn readahead_syscall_number() -> libc::c_long {
272 225
273}
274
275#[cfg(test)]
276mod tests {
277 #[cfg(target_os = "linux")]
278 #[test]
279 fn test_readahead_syscall_number_linux_matches_libc() {
280 assert_eq!(super::readahead_syscall_number(), libc::SYS_readahead);
281 }
282
283 #[cfg(all(target_os = "android", target_arch = "aarch64"))]
284 #[test]
285 fn test_readahead_syscall_number_android_aarch64() {
286 assert_eq!(super::readahead_syscall_number(), 213);
287 }
288
289 #[cfg(all(target_os = "android", target_arch = "arm"))]
290 #[test]
291 fn test_readahead_syscall_number_android_arm() {
292 assert_eq!(super::readahead_syscall_number(), 225);
293 }
294
295 #[cfg(all(target_os = "android", target_arch = "x86_64"))]
296 #[test]
297 fn test_readahead_syscall_number_android_x86_64() {
298 assert_eq!(super::readahead_syscall_number(), 187);
299 }
300
301 #[cfg(all(target_os = "android", target_arch = "x86"))]
302 #[test]
303 fn test_readahead_syscall_number_android_x86() {
304 assert_eq!(super::readahead_syscall_number(), 225);
305 }
306}