#![allow(clippy::undocumented_unsafe_blocks)]
const MIN_THREADS: usize = 1;
#[cfg(target_os = "macos")]
unsafe fn getdirentries64(
fd: libc::c_int,
buffer_ptr: *mut i8,
nbytes: libc::size_t,
basep: *mut libc::off_t,
) -> i32 {
const SYS_GETDIRENTRIES64: libc::c_int = 344; unsafe { libc::syscall(SYS_GETDIRENTRIES64, fd, buffer_ptr, nbytes, basep) }
}
#[cfg(target_os = "macos")]
#[allow(clippy::expect_used)]
#[allow(clippy::multiple_unsafe_ops_per_block)]
fn test_macos_syscall() {
use std::env::temp_dir;
use std::ffi::CString;
use std::os::unix::ffi::OsStrExt as _;
let tmp = temp_dir();
let test_dir = CString::new(tmp.as_os_str().as_bytes()).expect("CString conversion failed");
unsafe {
let dir_fd = libc::open(test_dir.as_ptr(), libc::O_RDONLY);
assert!(dir_fd >= 0, "Failed to open directory for testing");
let mut buffer: [i8; 1024] = [0; 1024];
let mut basep: libc::off_t = 0;
let result = getdirentries64(dir_fd, buffer.as_mut_ptr(), buffer.len(), &raw mut basep);
libc::close(dir_fd);
assert!(
result >= 0,
"getdirentries64 syscall test failed with result: {result}\n This indicates the syscall number has changed!",
)
}
}
#[cfg(target_os = "linux")]
fn get_supported_filesystems() -> Result<Vec<String>, std::io::Error> {
use std::io::BufRead as _;
let file = std::fs::File::open("/proc/filesystems")?;
let reader = std::io::BufReader::new(file);
let mut filesystems: Vec<String> = Vec::new();
for line in reader.lines().map_while(Result::ok) {
let parts: Vec<&str> = line.split_whitespace().collect();
if let Some(fs_name) = parts.last() {
filesystems.push((*fs_name).to_owned());
}
}
Ok(filesystems)
}
#[allow(clippy::unwrap_used)]
fn check_dirent_has_field(macro_name: &str, cfg_name: &str) {
println!("cargo:rustc-check-cfg=cfg({cfg_name})");
let out = std::env::var("OUT_DIR").unwrap();
let c_file = format!("check_{macro_name}.c");
let src = std::path::PathBuf::from(&out).join(&c_file);
let field_name = cfg_name.strip_prefix("has_").unwrap_or(cfg_name).to_owned();
let code = format!(
"#include <dirent.h>\n#include <stddef.h>\nstatic const size_t off = offsetof(struct dirent, {field_name});\nint main(void) {{ (void)off; return 0; }}\n",
);
std::fs::write(&src, code).unwrap();
let mut build = cc::Build::new();
build.file(&src).cargo_warnings(true).cargo_output(true);
if build.try_compile(&c_file).is_ok() {
println!("cargo:rustc-cfg={cfg_name}")
}
}
fn main() {
#[cfg(target_os = "linux")]
println!("cargo:rerun-if-changed=/proc/filesystems");
let num_threads =
std::thread::available_parallelism().map_or(MIN_THREADS, core::num::NonZeroUsize::get);
println!("cargo:rustc-env=THREAD_COUNT={num_threads}");
let page_size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) };
println!("cargo:rustc-env=FDF_PAGE_SIZE={page_size}");
#[cfg(target_os = "macos")]
test_macos_syscall();
#[cfg(target_os = "linux")]
match get_supported_filesystems() {
Ok(filesystems) => {
let has_reiser = filesystems.iter().any(|fs| fs.starts_with("reiser"));
assert!(!has_reiser, "reiser file systems not supported");
}
Err(e) => {
println!("cargo:warning=Failed to read /proc/filesystems: {e}");
}
}
check_dirent_has_field("_DIRENT_HAVE_D_TYPE", "has_d_type");
check_dirent_has_field("_DIRENT_HAVE_D_RECLEN", "has_d_reclen");
check_dirent_has_field("_DIRENT_HAVE_D_NAMLEN", "has_d_namlen");
check_dirent_has_field("_DIRENT_HAVE_D_INO", "has_d_ino");
}