use crate::chunk::{Ace, AcePlatform, Acl, Flag, Identifier, OwnerType, Permission};
use std::io;
use std::path::Path;
pub fn set_facl<P: AsRef<Path>>(path: P, acl: Acl) -> io::Result<()> {
let path = path.as_ref();
let mut acl_entries: Vec<exacl::AclEntry> =
acl.entries.into_iter().map(Into::into).collect::<Vec<_>>();
#[cfg(target_os = "macos")]
{
use std::os::unix::fs::MetadataExt;
let meta = std::fs::metadata(path)?;
acl_entries = acl_entries
.into_iter()
.map(|mut it| {
if it.kind == exacl::AclEntryKind::User && it.name.is_empty() {
it.name = meta.uid().to_string();
it
} else if it.kind == exacl::AclEntryKind::Group && it.name.is_empty() {
it.name = meta.gid().to_string();
it
} else {
it
}
})
.collect();
}
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
{
let mut exist_user = false;
let mut exist_group = false;
let mut exist_other = false;
for entry in acl_entries.iter() {
match entry.kind {
exacl::AclEntryKind::User if entry.name.is_empty() => exist_user = true,
exacl::AclEntryKind::Group if entry.name.is_empty() => exist_group = true,
exacl::AclEntryKind::Other => exist_other = true,
_ => (),
}
}
if !exist_user || !exist_group || !exist_other {
let facl = exacl::getfacl(path, None)?;
if !exist_user {
acl_entries.push(
facl.iter()
.find(|it| {
it.allow
&& it.flags.is_empty()
&& it.name.is_empty()
&& it.kind == exacl::AclEntryKind::User
})
.expect("failed to find owner ace")
.clone(),
);
}
if !exist_group {
acl_entries.push(
facl.iter()
.find(|it| {
it.allow
&& it.flags.is_empty()
&& it.name.is_empty()
&& it.kind == exacl::AclEntryKind::Group
})
.expect("failed to find owner group ace")
.clone(),
);
}
if !exist_other {
acl_entries.push(
facl.iter()
.find(|it| {
it.allow
&& it.flags.is_empty()
&& it.name.is_empty()
&& it.kind == exacl::AclEntryKind::Other
})
.expect("failed to find other ace")
.clone(),
);
}
}
}
exacl::setfacl(&[path], &acl_entries, None)
}
pub fn get_facl<P: AsRef<Path>>(path: P) -> io::Result<Acl> {
let ace_list = exacl::getfacl(path.as_ref(), None)?;
Ok(Acl {
platform: AcePlatform::CURRENT,
entries: ace_list.into_iter().map(Into::into).collect(),
})
}
#[allow(clippy::from_over_into)]
impl Into<Ace> for exacl::AclEntry {
fn into(self) -> Ace {
let mut flags = Flag::empty();
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
if self.flags.contains(exacl::Flag::DEFAULT) {
flags.insert(Flag::DEFAULT);
}
#[cfg(any(target_os = "macos", target_os = "freebsd"))]
if self.flags.contains(exacl::Flag::FILE_INHERIT) {
flags.insert(Flag::FILE_INHERIT);
}
#[cfg(any(target_os = "macos", target_os = "freebsd"))]
if self.flags.contains(exacl::Flag::DIRECTORY_INHERIT) {
flags.insert(Flag::DIRECTORY_INHERIT);
}
#[cfg(any(target_os = "macos", target_os = "freebsd"))]
if self.flags.contains(exacl::Flag::ONLY_INHERIT) {
flags.insert(Flag::ONLY_INHERIT);
}
#[cfg(any(target_os = "macos", target_os = "freebsd"))]
if self.flags.contains(exacl::Flag::LIMIT_INHERIT) {
flags.insert(Flag::LIMIT_INHERIT);
}
#[cfg(any(target_os = "macos", target_os = "freebsd"))]
if self.flags.contains(exacl::Flag::INHERITED) {
flags.insert(Flag::INHERITED);
}
let mut permission = Permission::empty();
if self.perms.contains(exacl::Perm::READ) {
permission.insert(Permission::READ);
}
if self.perms.contains(exacl::Perm::WRITE) {
permission.insert(Permission::WRITE);
}
if self.perms.contains(exacl::Perm::EXECUTE) {
permission.insert(Permission::EXECUTE);
}
#[cfg(any(target_os = "macos", target_os = "freebsd"))]
if self.perms.contains(exacl::Perm::DELETE) {
permission.insert(Permission::DELETE);
}
#[cfg(any(target_os = "macos", target_os = "freebsd"))]
if self.perms.contains(exacl::Perm::APPEND) {
permission.insert(Permission::APPEND);
}
#[cfg(any(target_os = "macos", target_os = "freebsd"))]
if self.perms.contains(exacl::Perm::DELETE_CHILD) {
permission.insert(Permission::DELETE_CHILD);
}
#[cfg(any(target_os = "macos", target_os = "freebsd"))]
if self.perms.contains(exacl::Perm::READATTR) {
permission.insert(Permission::READATTR);
}
#[cfg(any(target_os = "macos", target_os = "freebsd"))]
if self.perms.contains(exacl::Perm::WRITEATTR) {
permission.insert(Permission::WRITEATTR);
}
#[cfg(any(target_os = "macos", target_os = "freebsd"))]
if self.perms.contains(exacl::Perm::READEXTATTR) {
permission.insert(Permission::READEXTATTR);
}
#[cfg(any(target_os = "macos", target_os = "freebsd"))]
if self.perms.contains(exacl::Perm::WRITEEXTATTR) {
permission.insert(Permission::WRITEEXTATTR);
}
#[cfg(any(target_os = "macos", target_os = "freebsd"))]
if self.perms.contains(exacl::Perm::READSECURITY) {
permission.insert(Permission::READSECURITY);
}
#[cfg(any(target_os = "macos", target_os = "freebsd"))]
if self.perms.contains(exacl::Perm::WRITESECURITY) {
permission.insert(Permission::WRITESECURITY);
}
#[cfg(any(target_os = "macos", target_os = "freebsd"))]
if self.perms.contains(exacl::Perm::CHOWN) {
permission.insert(Permission::CHOWN);
}
#[cfg(any(target_os = "macos", target_os = "freebsd"))]
if self.perms.contains(exacl::Perm::SYNC) {
permission.insert(Permission::SYNC);
}
#[cfg(target_os = "freebsd")]
if self.perms.contains(exacl::Perm::READ_DATA) {
permission.insert(Permission::READ_DATA);
}
#[cfg(target_os = "freebsd")]
if self.perms.contains(exacl::Perm::WRITE_DATA) {
permission.insert(Permission::WRITE_DATA);
}
Ace {
flags,
owner_type: match self.kind {
exacl::AclEntryKind::User if self.name.is_empty() => OwnerType::Owner,
exacl::AclEntryKind::User => OwnerType::User(Identifier(self.name)),
exacl::AclEntryKind::Group if self.name.is_empty() => OwnerType::OwnerGroup,
exacl::AclEntryKind::Group => OwnerType::Group(Identifier(self.name)),
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
exacl::AclEntryKind::Mask => OwnerType::Mask,
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
exacl::AclEntryKind::Other => OwnerType::Other,
#[cfg(target_os = "freebsd")]
exacl::AclEntryKind::Everyone => OwnerType::Other,
exacl::AclEntryKind::Unknown => panic!("Unknown acl owner"),
},
allow: self.allow,
permission,
}
}
}
#[allow(clippy::from_over_into)]
impl Into<exacl::AclEntry> for Ace {
fn into(self) -> exacl::AclEntry {
let slf = self;
let (kind, name) = match slf.owner_type {
OwnerType::Owner => (exacl::AclEntryKind::User, String::new()),
OwnerType::User(u) => (exacl::AclEntryKind::User, u.0),
OwnerType::OwnerGroup => (exacl::AclEntryKind::Group, String::new()),
OwnerType::Group(u) => {
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
if u.0 == "everyone" {
(exacl::AclEntryKind::Other, String::new())
} else {
(exacl::AclEntryKind::Group, u.0)
}
#[cfg(not(any(target_os = "linux", target_os = "freebsd")))]
(exacl::AclEntryKind::Group, u.0)
}
#[cfg(not(any(target_os = "linux", target_os = "freebsd")))]
OwnerType::Mask => (exacl::AclEntryKind::Unknown, String::new()),
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
OwnerType::Mask => (exacl::AclEntryKind::Mask, String::new()),
#[cfg(not(any(target_os = "linux", target_os = "freebsd")))]
OwnerType::Other => (exacl::AclEntryKind::Group, "everyone".into()),
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
OwnerType::Other => (exacl::AclEntryKind::Other, String::new()),
};
let mut perms = exacl::Perm::empty();
if slf.permission.contains(Permission::READ) {
perms.insert(exacl::Perm::READ);
}
if slf.permission.contains(Permission::WRITE) {
perms.insert(exacl::Perm::WRITE);
}
if slf.permission.contains(Permission::EXECUTE) {
perms.insert(exacl::Perm::EXECUTE);
}
#[cfg(any(target_os = "macos", target_os = "freebsd"))]
if slf.permission.contains(Permission::DELETE) {
perms.insert(exacl::Perm::DELETE);
}
#[cfg(any(target_os = "macos", target_os = "freebsd"))]
if slf.permission.contains(Permission::APPEND) {
perms.insert(exacl::Perm::APPEND);
}
#[cfg(any(target_os = "macos", target_os = "freebsd"))]
if slf.permission.contains(Permission::DELETE_CHILD) {
perms.insert(exacl::Perm::DELETE_CHILD);
}
#[cfg(any(target_os = "macos", target_os = "freebsd"))]
if slf.permission.contains(Permission::READATTR) {
perms.insert(exacl::Perm::READATTR);
}
#[cfg(any(target_os = "macos", target_os = "freebsd"))]
if slf.permission.contains(Permission::WRITEATTR) {
perms.insert(exacl::Perm::WRITEATTR);
}
#[cfg(any(target_os = "macos", target_os = "freebsd"))]
if slf.permission.contains(Permission::READEXTATTR) {
perms.insert(exacl::Perm::READEXTATTR);
}
#[cfg(any(target_os = "macos", target_os = "freebsd"))]
if slf.permission.contains(Permission::WRITEEXTATTR) {
perms.insert(exacl::Perm::WRITEEXTATTR);
}
#[cfg(any(target_os = "macos", target_os = "freebsd"))]
if slf.permission.contains(Permission::READSECURITY) {
perms.insert(exacl::Perm::READSECURITY);
}
#[cfg(any(target_os = "macos", target_os = "freebsd"))]
if slf.permission.contains(Permission::WRITESECURITY) {
perms.insert(exacl::Perm::WRITESECURITY);
}
#[cfg(any(target_os = "macos", target_os = "freebsd"))]
if slf.permission.contains(Permission::CHOWN) {
perms.insert(exacl::Perm::CHOWN);
}
#[cfg(any(target_os = "macos", target_os = "freebsd"))]
if slf.permission.contains(Permission::SYNC) {
perms.insert(exacl::Perm::SYNC);
}
#[cfg(target_os = "freebsd")]
if slf.permission.contains(Permission::READ_DATA) {
perms.insert(exacl::Perm::READ_DATA);
}
#[cfg(target_os = "freebsd")]
if slf.permission.contains(Permission::WRITE_DATA) {
perms.insert(exacl::Perm::WRITE_DATA);
}
let mut flags = exacl::Flag::empty();
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
if slf.flags.contains(Flag::DEFAULT) {
flags.insert(exacl::Flag::DEFAULT);
}
#[cfg(any(target_os = "macos", target_os = "freebsd"))]
if slf.flags.contains(Flag::FILE_INHERIT) {
flags.insert(exacl::Flag::FILE_INHERIT);
}
#[cfg(any(target_os = "macos", target_os = "freebsd"))]
if slf.flags.contains(Flag::DIRECTORY_INHERIT) {
flags.insert(exacl::Flag::DIRECTORY_INHERIT);
}
#[cfg(any(target_os = "macos", target_os = "freebsd"))]
if slf.flags.contains(Flag::LIMIT_INHERIT) {
flags.insert(exacl::Flag::LIMIT_INHERIT);
}
#[cfg(any(target_os = "macos", target_os = "freebsd"))]
if slf.flags.contains(Flag::ONLY_INHERIT) {
flags.insert(exacl::Flag::ONLY_INHERIT);
}
#[cfg(any(target_os = "macos", target_os = "freebsd"))]
if slf.flags.contains(Flag::INHERITED) {
flags.insert(exacl::Flag::INHERITED);
}
exacl::AclEntry {
kind,
name,
perms,
flags,
allow: slf.allow,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ace_mutual_convert() {
let acl_entry = exacl::AclEntry {
kind: exacl::AclEntryKind::User,
name: "name".into(),
perms: exacl::Perm::all(),
flags: exacl::Flag::all(),
allow: false,
};
assert_eq!(
acl_entry.clone(),
<exacl::AclEntry as Into<Ace>>::into(acl_entry).into()
);
}
}