use std::{ffi::CStr, io::Read};
use nix::{errno::Errno, fcntl::OFlag};
use super::{ambient, CapSet, Capabilities, Capability};
use crate::{
caps::errors::CapsError,
compat::{openat2, ResolveFlag},
err::err2no,
fd::is_empty_file,
lookup::safe_open_how,
path::XPath,
proc::proc_open,
retry::retry_on_eintr,
};
pub fn ambient_set_supported() -> Result<(), CapsError> {
ambient::has_cap(Capability::CAP_CHOWN)?;
Ok(())
}
pub fn procfs_all_supported(proc_mountpoint: Option<&XPath>) -> Result<Capabilities, CapsError> {
const LAST_CAP_FILEPATH: &CStr = c"sys/kernel/cap_last_cap";
let mut fd = proc_open(proc_mountpoint)
.and_then(|fd| {
let how_xdev = safe_open_how(
OFlag::O_RDONLY | OFlag::O_NOCTTY,
ResolveFlag::RESOLVE_NO_XDEV,
);
#[expect(clippy::disallowed_methods)]
retry_on_eintr(|| openat2(&fd, LAST_CAP_FILEPATH, how_xdev))
})
.map_err(CapsError)?;
if !is_empty_file(&fd).unwrap_or(false) {
return Err(CapsError(Errno::EBADFD));
}
let max_cap: u8 = {
let mut buf = String::with_capacity(4);
fd.read_to_string(&mut buf)
.map_err(|err| CapsError(err2no(&err)))?;
buf.trim_end().parse().or(Err(CapsError(Errno::EINVAL)))?
};
drop(fd);
let supported = {
let mask: u64 = if max_cap >= 63 {
u64::MAX
} else {
(1u64 << (u64::from(max_cap) + 1)) - 1
};
super::Capabilities::all() & super::Capabilities::from_bits_truncate(mask)
};
Ok(supported)
}
pub fn thread_all_supported() -> Capabilities {
let mut supported = Capabilities::empty();
for flag in Capabilities::all() {
if let Ok(cap) = Capability::try_from(flag) {
if super::has_cap(None, CapSet::Bounding, cap).unwrap_or(false) {
supported |= flag;
}
}
}
supported
}