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> {
23 let metadata = std::fs::metadata(path).map_err(|err| {
24 CoreError::sys(err.raw_os_error().unwrap_or(libc::EIO), "path_fingerprint")
25 })?;
26 let modified_ns = metadata
27 .modified()
28 .ok()
29 .and_then(|modified| modified.duration_since(UNIX_EPOCH).ok())
30 .map(|duration| duration.as_nanos())
31 .unwrap_or_default();
32 Ok(PathFingerprint {
33 len: metadata.len(),
34 modified_ns,
35 })
36}
37
38pub fn path_exists(path: &str) -> bool {
45 match std::ffi::CString::new(path) {
46 Ok(c) => unsafe { libc::access(c.as_ptr(), libc::F_OK) == 0 },
47 Err(_) => false,
48 }
49}
50
51pub fn path_lstat_exists(path: &str) -> bool {
55 match std::ffi::CString::new(path) {
56 Ok(c) => unsafe {
57 let mut stat = std::mem::zeroed();
58 libc::lstat(c.as_ptr(), &mut stat) == 0
59 },
60 Err(_) => false,
61 }
62}
63
64pub fn read_to_string(path: &str) -> Result<String, CoreError> {
69 std::fs::read_to_string(path)
70 .map_err(|err| CoreError::sys(err.raw_os_error().unwrap_or(libc::EIO), "read_to_string"))
71}
72
73pub fn readahead(fd: impl AsRawFd, offset: u64, len: usize) -> Result<(), CoreError> {
83 readahead_raw(fd.as_raw_fd(), offset, len)
84}
85
86pub fn mmap_madvise(
91 fd: impl AsRawFd,
92 offset: u64,
93 len: usize,
94 touch: bool,
95) -> Result<(), CoreError> {
96 mmap_madvise_raw(fd.as_raw_fd(), offset, len, touch)
97}
98
99#[cfg(any(target_os = "linux", target_os = "android"))]
100fn mmap_madvise_raw(
101 fd: libc::c_int,
102 offset: u64,
103 len: usize,
104 touch: bool,
105) -> Result<(), CoreError> {
106 if len == 0 {
107 return Ok(());
108 }
109
110 let page_size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) };
111 if page_size <= 0 {
112 return Err(CoreError::sys(libc::EINVAL, "sysconf(_SC_PAGESIZE)"));
113 }
114 let page_size = page_size as u64;
115 if offset % page_size != 0 || offset > libc::off_t::MAX as u64 {
116 return Err(CoreError::sys(libc::EINVAL, "mmap"));
117 }
118
119 let ptr = unsafe {
120 libc::mmap(
121 std::ptr::null_mut(),
122 len,
123 libc::PROT_READ,
124 libc::MAP_PRIVATE,
125 fd,
126 offset as libc::off_t,
127 )
128 };
129 if ptr == libc::MAP_FAILED {
130 let code = std::io::Error::last_os_error().raw_os_error().unwrap_or(0);
131 return Err(CoreError::sys(code, "mmap"));
132 }
133
134 let result = if unsafe { libc::madvise(ptr, len, libc::MADV_WILLNEED) } == -1 {
135 let code = std::io::Error::last_os_error().raw_os_error().unwrap_or(0);
136 Err(CoreError::sys(code, "madvise"))
137 } else {
138 if touch {
139 let mut pos = 0usize;
140 let page_size = page_size as usize;
141 while pos < len {
142 unsafe {
143 std::ptr::read_volatile((ptr as *const u8).add(pos));
144 }
145 pos = pos.saturating_add(page_size);
146 }
147 }
148 Ok(())
149 };
150
151 if unsafe { libc::munmap(ptr, len) } == -1 {
152 let code = std::io::Error::last_os_error().raw_os_error().unwrap_or(0);
153 return Err(CoreError::sys(code, "munmap"));
154 }
155 result
156}
157
158#[cfg(not(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 Err(CoreError::sys(libc::ENOSYS, "mmap"))
166}
167
168#[cfg(any(target_os = "linux", target_os = "android"))]
169fn readahead_raw(fd: libc::c_int, offset: u64, len: usize) -> Result<(), CoreError> {
170 if offset > libc::off64_t::MAX as u64 {
171 return Err(CoreError::sys(libc::EINVAL, "readahead"));
172 }
173
174 let count = len as libc::size_t;
175 let offset = offset as libc::off64_t;
176
177 loop {
178 let ret = unsafe { libc::syscall(readahead_syscall_number(), fd, offset, count) };
179 if ret == -1 {
180 let code = std::io::Error::last_os_error().raw_os_error().unwrap_or(0);
181 if code == libc::EINTR {
182 continue;
183 }
184 return Err(CoreError::sys(code, "readahead"));
185 }
186 return Ok(());
187 }
188}
189
190#[cfg(not(any(target_os = "linux", target_os = "android")))]
191fn readahead_raw(_fd: libc::c_int, _offset: u64, _len: usize) -> Result<(), CoreError> {
192 Err(CoreError::sys(libc::ENOSYS, "readahead"))
193}
194
195#[cfg(target_os = "linux")]
196#[inline(always)]
197const fn readahead_syscall_number() -> libc::c_long {
198 libc::SYS_readahead
199}
200
201#[cfg(all(target_os = "android", target_arch = "aarch64"))]
202#[inline(always)]
203const fn readahead_syscall_number() -> libc::c_long {
204 213
205}
206
207#[cfg(all(target_os = "android", target_arch = "arm"))]
208#[inline(always)]
209const fn readahead_syscall_number() -> libc::c_long {
210 225
211}
212
213#[cfg(all(target_os = "android", target_arch = "x86_64"))]
214#[inline(always)]
215const fn readahead_syscall_number() -> libc::c_long {
216 187
217}
218
219#[cfg(all(target_os = "android", target_arch = "x86"))]
220#[inline(always)]
221const fn readahead_syscall_number() -> libc::c_long {
222 225
223}
224
225#[cfg(test)]
226mod tests {
227 #[cfg(target_os = "linux")]
228 #[test]
229 fn test_readahead_syscall_number_linux_matches_libc() {
230 assert_eq!(super::readahead_syscall_number(), libc::SYS_readahead);
231 }
232
233 #[cfg(all(target_os = "android", target_arch = "aarch64"))]
234 #[test]
235 fn test_readahead_syscall_number_android_aarch64() {
236 assert_eq!(super::readahead_syscall_number(), 213);
237 }
238
239 #[cfg(all(target_os = "android", target_arch = "arm"))]
240 #[test]
241 fn test_readahead_syscall_number_android_arm() {
242 assert_eq!(super::readahead_syscall_number(), 225);
243 }
244
245 #[cfg(all(target_os = "android", target_arch = "x86_64"))]
246 #[test]
247 fn test_readahead_syscall_number_android_x86_64() {
248 assert_eq!(super::readahead_syscall_number(), 187);
249 }
250
251 #[cfg(all(target_os = "android", target_arch = "x86"))]
252 #[test]
253 fn test_readahead_syscall_number_android_x86() {
254 assert_eq!(super::readahead_syscall_number(), 225);
255 }
256}