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