use std::collections::HashMap;
use std::ffi::OsString;
use std::path::Path;
pub fn copy_xattrs<P: AsRef<Path>>(source: P, dest: P) -> std::io::Result<()> {
for attr_name in xattr::list(&source)? {
if let Some(value) = xattr::get(&source, &attr_name)? {
xattr::set(&dest, &attr_name, &value)?;
}
}
Ok(())
}
pub fn retrieve_xattrs<P: AsRef<Path>>(source: P) -> std::io::Result<HashMap<OsString, Vec<u8>>> {
let mut attrs = HashMap::new();
for attr_name in xattr::list(&source)? {
if let Some(value) = xattr::get(&source, &attr_name)? {
attrs.insert(attr_name, value);
}
}
Ok(attrs)
}
pub fn apply_xattrs<P: AsRef<Path>>(
dest: P,
xattrs: HashMap<OsString, Vec<u8>>,
) -> std::io::Result<()> {
for (attr, value) in xattrs {
xattr::set(&dest, &attr, &value)?;
}
Ok(())
}
pub fn has_acl<P: AsRef<Path>>(file: P) -> bool {
xattr::list_deref(file).is_ok_and(|acl| {
acl.count() > 0
})
}
pub fn get_acl_perm_bits_from_xattr<P: AsRef<Path>>(source: P) -> u32 {
if let Ok(entries) = retrieve_xattrs(source) {
let mut perm: u32 = 0;
if let Some(value) = entries.get(&OsString::from("system.posix_acl_default")) {
let acl_entries = value
.split_at(3)
.1
.iter()
.filter(|&x| *x != 255)
.copied()
.collect::<Vec<u8>>();
for entry in acl_entries.chunks_exact(4) {
perm = (perm << 3) | u32::from(entry[2]) | u32::from(entry[3]);
}
return perm;
}
}
0
}
#[cfg(not(target_os = "openbsd"))]
#[cfg(test)]
mod tests {
use super::*;
use std::fs::File;
use tempfile::tempdir;
#[test]
fn test_copy_xattrs() {
let temp_dir = tempdir().unwrap();
let source_path = temp_dir.path().join("source.txt");
let dest_path = temp_dir.path().join("dest.txt");
File::create(&source_path).unwrap();
File::create(&dest_path).unwrap();
let test_attr = "user.test";
let test_value = b"test value";
xattr::set(&source_path, test_attr, test_value).unwrap();
copy_xattrs(&source_path, &dest_path).unwrap();
let copied_value = xattr::get(&dest_path, test_attr).unwrap().unwrap();
assert_eq!(copied_value, test_value);
}
#[test]
fn test_apply_and_retrieve_xattrs() {
let temp_dir = tempdir().unwrap();
let file_path = temp_dir.path().join("test_file.txt");
File::create(&file_path).unwrap();
let mut test_xattrs = HashMap::new();
let test_attr = "user.test_attr";
let test_value = b"test value";
test_xattrs.insert(OsString::from(test_attr), test_value.to_vec());
apply_xattrs(&file_path, test_xattrs).unwrap();
let retrieved_xattrs = retrieve_xattrs(&file_path).unwrap();
assert!(retrieved_xattrs.contains_key(OsString::from(test_attr).as_os_str()));
assert_eq!(
retrieved_xattrs
.get(OsString::from(test_attr).as_os_str())
.unwrap(),
test_value
);
}
#[test]
#[cfg(target_os = "linux")]
fn test_get_perm_bits_from_xattrs() {
let temp_dir = tempdir().unwrap();
let source_path = temp_dir.path().join("source_dir");
std::fs::create_dir(&source_path).unwrap();
let test_attr = "system.posix_acl_default";
let test_value = vec![
2, 0, 0, 0, 1, 0, 7, 0, 255, 255, 255, 255, 4, 0, 0, 0, 255, 255, 255, 255, 32, 0, 0,
0, 255, 255, 255, 255,
];
xattr::set(&source_path, test_attr, test_value.as_slice()).unwrap();
let perm_bits = get_acl_perm_bits_from_xattr(source_path);
assert_eq!(0o700, perm_bits);
}
#[test]
fn test_file_has_acl() {
let temp_dir = tempdir().unwrap();
let file_path = temp_dir.path().join("test_file.txt");
File::create(&file_path).unwrap();
assert!(!has_acl(&file_path));
let test_attr = "user.test_acl";
let test_value = b"test value";
xattr::set(&file_path, test_attr, test_value).unwrap();
assert!(has_acl(&file_path));
}
}