use std::ffi::CString;
use std::os::unix::ffi::OsStrExt;
use std::path::Path;
use crate::error::WhynoError;
use super::{VFS_CAP_DATA, VFS_CAP_SIZE, XATTR_NAME};
pub fn check_xattr_support(path: &Path) -> Result<(), WhynoError> {
let c_path = path_to_cstring(path)?;
let name_ptr = XATTR_NAME.as_ptr().cast::<libc::c_char>();
let ret = unsafe { libc::getxattr(c_path.as_ptr(), name_ptr, std::ptr::null_mut(), 0) };
if ret < 0 {
let errno = nix::errno::Errno::last();
if errno == nix::errno::Errno::ENOTSUP {
return Err(WhynoError::Caps(
"filesystem does not support extended attributes".to_string(),
));
}
}
Ok(())
}
pub fn write_cap_xattr(path: &Path) -> Result<(), WhynoError> {
let c_path = path_to_cstring(path)?;
let name_ptr = XATTR_NAME.as_ptr().cast::<libc::c_char>();
let ret = unsafe {
libc::setxattr(
c_path.as_ptr(),
name_ptr,
VFS_CAP_DATA.as_ptr().cast::<libc::c_void>(),
VFS_CAP_SIZE,
0, )
};
if ret < 0 {
let errno = nix::errno::Errno::last();
return Err(WhynoError::Caps(format!("setxattr failed: {errno}")));
}
Ok(())
}
pub fn read_cap_xattr(path: &Path) -> Result<Vec<u8>, String> {
let c_path = path_to_cstring(path).map_err(|e| e.to_string())?;
let name_ptr = XATTR_NAME.as_ptr().cast::<libc::c_char>();
let mut buf = [0u8; VFS_CAP_SIZE];
let ret = unsafe {
libc::getxattr(
c_path.as_ptr(),
name_ptr,
buf.as_mut_ptr().cast::<libc::c_void>(),
VFS_CAP_SIZE,
)
};
if ret < 0 {
let errno = nix::errno::Errno::last();
return Err(format!("getxattr failed: {errno}"));
}
#[allow(clippy::cast_sign_loss)]
let len = ret as usize;
Ok(buf[..len].to_vec())
}
pub fn remove_cap_xattr(path: &Path) -> Result<(), WhynoError> {
let c_path = path_to_cstring(path)?;
let name_ptr = XATTR_NAME.as_ptr().cast::<libc::c_char>();
let ret = unsafe { libc::removexattr(c_path.as_ptr(), name_ptr) };
if ret < 0 {
let errno = nix::errno::Errno::last();
return Err(WhynoError::Caps(format!("removexattr failed: {errno}")));
}
Ok(())
}
fn path_to_cstring(path: &Path) -> Result<CString, WhynoError> {
CString::new(path.as_os_str().as_bytes())
.map_err(|_| WhynoError::Caps("path contains interior NUL byte".to_string()))
}