#[cfg(unix)]
pub(crate) fn permission_bits_are_enforced() -> bool {
use std::os::unix::fs::PermissionsExt;
let Ok(directory) = tempfile::tempdir() else {
return false;
};
let path = directory.path().join("unreadable");
if std::fs::write(&path, b"probe").is_err() {
return false;
}
if std::fs::set_permissions(&path, std::fs::Permissions::from_mode(0o000)).is_err() {
return false;
}
std::fs::read(&path).is_err()
}
#[cfg(unix)]
pub(crate) fn require_permission_bits() -> bool {
if permission_bits_are_enforced() {
return true;
}
if std::env::var_os("FDU_TEST_ALLOW_NO_PERMISSION_BITS").as_deref()
== Some(std::ffi::OsStr::new("1"))
{
eprintln!(
"skipped by FDU_TEST_ALLOW_NO_PERMISSION_BITS=1: this host does not enforce Unix \
permission bits for the test process"
);
return false;
}
panic!(
"permission fixture precondition failed: this process can read a mode-000 file; \
run on a host that enforces Unix permission bits, or explicitly opt out with \
FDU_TEST_ALLOW_NO_PERMISSION_BITS=1"
);
}
pub(crate) fn observing_controls() -> crate::ScanScope {
crate::ScanConfig { read_controls: true, ..crate::ScanConfig::default() }.scope()
}
pub(crate) fn not_observing_controls() -> crate::ScanScope {
crate::ScanConfig { read_controls: false, ..crate::ScanConfig::default() }.scope()
}
pub(crate) fn read_of(index: &crate::Index, query: crate::query::Query) -> crate::query::Request {
crate::query::Request::new(crate::query::Basis::held_by(index), query, std::time::UNIX_EPOCH)
}
pub(crate) fn settle_allocations(root: &std::path::Path) {
#[cfg(target_os = "linux")]
{
let status =
std::process::Command::new("sync").arg("-f").arg(root).status().expect("run sync");
assert!(status.success(), "sync -f exited with {status}");
}
#[cfg(not(target_os = "linux"))]
let _ = root;
}
pub(crate) fn resolves_case_insensitively(dir: &std::path::Path) -> bool {
let probe = dir.join("CaseProbe");
std::fs::write(&probe, b"probe").expect("case probe");
let insensitive = std::fs::symlink_metadata(dir.join("caseprobe")).is_ok();
std::fs::remove_file(probe).expect("remove case probe");
insensitive
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum CaseLookups {
Host,
Folded,
}
impl CaseLookups {
pub(crate) fn on_this_host(dir: &std::path::Path) -> Vec<(Self, bool)> {
if resolves_case_insensitively(dir) {
eprintln!(
"the temporary directory is case-insensitive: the host exercises a case-variant \
control for real, and folded lookups are not needed"
);
vec![(Self::Host, true)]
} else {
eprintln!(
"the temporary directory is case-sensitive: a case-variant control is exercised \
through folded lookups, and the host is the negative"
);
vec![(Self::Host, false), (Self::Folded, true)]
}
}
pub(crate) fn install(self, root: &std::path::Path) -> Option<crate::scan::CaseFoldingGuard> {
match self {
Self::Host => None,
Self::Folded => Some(crate::scan::install_case_folding_control_lookup(root)),
}
}
}