#![allow(unsafe_code)]
use core::sync::atomic::{AtomicBool, Ordering};
use std::fs::File;
use std::os::fd::{AsRawFd, FromRawFd, OwnedFd};
use std::os::unix::ffi::OsStrExt;
use std::path::Path;
static CALL: AtomicBool = AtomicBool::new(true);
const ON_STACK: usize = 384;
pub(super) fn open(path: &Path) -> Option<File> {
if !CALL.load(Ordering::Relaxed) {
return None;
}
let bytes = path.as_os_str().as_bytes();
if bytes.contains(&0) {
return None;
}
let mut stack = [0u8; ON_STACK];
let heap;
let name = if bytes.len() < ON_STACK {
stack[..bytes.len()].copy_from_slice(bytes);
stack.as_ptr()
} else {
heap = std::ffi::CString::new(bytes).ok()?;
heap.as_ptr().cast()
};
let place = openat2(name, libc::O_PATH | libc::O_CLOEXEC)?;
regular(&place)?;
local(&place)?;
drop(place);
let file = openat2(name, libc::O_RDONLY | libc::O_CLOEXEC | libc::O_NONBLOCK)?;
regular(&file)?;
#[cfg(test)]
took::note(path);
Some(File::from(file))
}
fn openat2(name: *const u8, flags: i32) -> Option<OwnedFd> {
let mut how: libc::open_how = unsafe { core::mem::zeroed() };
how.flags = flags as u64;
how.resolve = libc::RESOLVE_CACHED;
let opened = unsafe {
crate::io::sys::syscall(
libc::SYS_openat2,
[
libc::AT_FDCWD as usize,
name as usize,
(&how as *const libc::open_how) as usize,
size_of::<libc::open_how>(),
0,
0,
],
)
};
match opened {
Ok(fd) => Some(unsafe { OwnedFd::from_raw_fd(fd as i32) }),
Err(err) => {
if matches!(
err.raw_os_error(),
Some(libc::ENOSYS | libc::EINVAL | libc::E2BIG)
) {
CALL.store(false, Ordering::Relaxed);
}
None
}
}
}
const LOCAL: [u32; 13] = [
0x0000_EF53,
0x5846_5342,
0x9123_683E,
0xF2F5_2010,
0xCA45_1A4E,
0x2FC1_2FC1,
0x0102_1994,
0x8584_58F6,
0x794C_7630,
0x7371_7368,
0xE0F5_E1E2,
0x0000_4D44,
0x2011_BAB0,
];
fn local(fd: &OwnedFd) -> Option<()> {
let mut fs: libc::statfs = unsafe { core::mem::zeroed() };
let r = unsafe { libc::fstatfs(fd.as_raw_fd(), &mut fs) };
(r == 0 && LOCAL.contains(&(fs.f_type as u32))).then_some(())
}
fn regular(fd: &OwnedFd) -> Option<()> {
let mut st: libc::stat = unsafe { core::mem::zeroed() };
let r = unsafe { libc::fstat(fd.as_raw_fd(), &mut st) };
(r == 0 && st.st_mode & libc::S_IFMT == libc::S_IFREG).then_some(())
}
#[cfg(test)]
pub(super) mod took {
use std::path::{Path, PathBuf};
use std::sync::Mutex;
static OPENED: Mutex<Vec<PathBuf>> = Mutex::new(Vec::new());
pub(super) fn note(path: &Path) {
if let Ok(mut opened) = OPENED.lock() {
opened.push(path.to_path_buf());
}
}
pub(in crate::fs) fn on_local(path: &Path) -> bool {
std::fs::File::open(path)
.is_ok_and(|file| super::local(&std::os::fd::OwnedFd::from(file)).is_some())
}
pub(in crate::fs) fn took(path: &Path) -> bool {
let Ok(mut opened) = OPENED.lock() else {
return false;
};
let before = opened.len();
opened.retain(|p| p != path);
opened.len() != before
}
}