use crate::{
errors::InternalError,
file_metadata::{Acl, AclMode, AclQualifier, Gid, Uid},
};
#[derive(Debug, Clone, Copy)]
struct RawAclVersion(u32);
#[derive(Debug, Clone, Copy)]
struct RawAclTag(u16);
#[derive(Debug, Clone, Copy)]
struct RawAclPerm(u16);
#[derive(Debug, Clone, Copy)]
struct RawAclId(u32);
const POSIX_ACL_XATTR_VERSION: RawAclVersion = RawAclVersion(0x0002);
const UNDEFINED_ID: RawAclId = RawAclId(0);
#[derive(Debug, Clone, Copy)]
enum AclTag {
Undefined,
UserObj,
User,
GroupObj,
Group,
Mask,
Other,
}
impl From<AclTag> for RawAclTag {
fn from(tag: AclTag) -> Self {
RawAclTag(match tag {
AclTag::Undefined => 0,
AclTag::UserObj => 1,
AclTag::User => 2,
AclTag::GroupObj => 3,
AclTag::Group => 4,
AclTag::Mask => 5,
AclTag::Other => 6,
})
}
}
#[derive(Debug)]
struct RawAclEntry {
tag: RawAclTag,
perm: RawAclPerm,
id: Option<RawAclId>,
}
impl RawAclEntry {
fn serialize(&self, buf: &mut Vec<u8>) {
buf.extend_from_slice(&self.tag.0.to_le_bytes());
buf.extend_from_slice(&self.perm.0.to_le_bytes());
buf.extend_from_slice(&self.id.unwrap_or(UNDEFINED_ID).0.to_le_bytes());
}
}
#[derive(Debug)]
struct RawAcl(Vec<RawAclEntry>);
impl RawAcl {
fn serialize(&self) -> Vec<u8> {
let mut buf = Vec::new();
buf.extend_from_slice(&POSIX_ACL_XATTR_VERSION.0.to_le_bytes());
for entry in &self.0 {
entry.serialize(&mut buf);
}
buf
}
}
impl From<(AclQualifier, AclMode)> for RawAclEntry {
fn from((qualifier, mode): (AclQualifier, AclMode)) -> Self {
match qualifier {
AclQualifier::User(uid) => RawAclEntry {
tag: AclTag::User.into(),
perm: RawAclPerm(mode.bits()),
id: Some(RawAclId(uid.as_raw())),
},
AclQualifier::Group(gid) => RawAclEntry {
tag: AclTag::Group.into(),
perm: RawAclPerm(mode.bits()),
id: Some(RawAclId(gid.as_raw())),
},
AclQualifier::OwningUser => RawAclEntry {
tag: AclTag::UserObj.into(),
perm: RawAclPerm(mode.bits()),
id: None,
},
AclQualifier::OwningGroup => RawAclEntry {
tag: AclTag::GroupObj.into(),
perm: RawAclPerm(mode.bits()),
id: None,
},
AclQualifier::Other => RawAclEntry {
tag: AclTag::Other.into(),
perm: RawAclPerm(mode.bits()),
id: None,
},
AclQualifier::Mask => RawAclEntry {
tag: AclTag::Mask.into(),
perm: RawAclPerm(mode.bits()),
id: None,
},
}
}
}
impl From<Acl> for RawAcl {
fn from(acl: Acl) -> Self {
RawAcl(acl.into_iter().map(RawAclEntry::from).collect())
}
}
impl TryFrom<RawAclTag> for AclTag {
type Error = ();
fn try_from(tag: RawAclTag) -> Result<Self, Self::Error> {
match tag.0 {
0 => Ok(AclTag::Undefined),
1 => Ok(AclTag::UserObj),
2 => Ok(AclTag::User),
3 => Ok(AclTag::GroupObj),
4 => Ok(AclTag::Group),
5 => Ok(AclTag::Mask),
6 => Ok(AclTag::Other),
_ => Err(()),
}
}
}
impl Acl {
pub(crate) fn serialize(&self) -> Vec<u8> {
RawAcl::from(self.clone()).serialize()
}
pub(crate) fn deserialize(data: &[u8]) -> crate::Result<Self> {
if data.is_empty() {
return Ok(Acl::new());
}
let Some(version_bytes) = data.get(..4) else {
return Err(InternalError::MalformedAcl.into());
};
let Ok(version_array): Result<[u8; 4], _> = version_bytes.try_into() else {
return Err(InternalError::MalformedAcl.into());
};
let version = u32::from_le_bytes(version_array);
if version != POSIX_ACL_XATTR_VERSION.0 {
return Err(InternalError::MalformedAcl.into());
}
let Some(entry_data) = data.get(4..) else {
return Err(InternalError::MalformedAcl.into());
};
if !entry_data.len().is_multiple_of(8) {
return Err(InternalError::MalformedAcl.into());
}
let mut acl = Acl::new();
for chunk in entry_data.chunks_exact(8) {
let Ok(tag_bytes): Result<[u8; 2], _> = chunk.get(..2).unwrap_or_default().try_into()
else {
return Err(InternalError::MalformedAcl.into());
};
let Ok(perm_bytes): Result<[u8; 2], _> = chunk.get(2..4).unwrap_or_default().try_into()
else {
return Err(InternalError::MalformedAcl.into());
};
let Ok(id_bytes): Result<[u8; 4], _> = chunk.get(4..8).unwrap_or_default().try_into()
else {
return Err(InternalError::MalformedAcl.into());
};
let tag = RawAclTag(u16::from_le_bytes(tag_bytes));
let perm = RawAclPerm(u16::from_le_bytes(perm_bytes));
let id = RawAclId(u32::from_le_bytes(id_bytes));
let Ok(acl_tag) = AclTag::try_from(tag) else {
return Err(InternalError::MalformedAcl.into());
};
let mode = AclMode::from_bits_truncate(perm.0);
let qualifier = match acl_tag {
AclTag::Undefined => continue,
AclTag::UserObj => AclQualifier::OwningUser,
AclTag::User => AclQualifier::User(Uid::from_raw(id.0)),
AclTag::GroupObj => AclQualifier::OwningGroup,
AclTag::Group => AclQualifier::Group(Gid::from_raw(id.0)),
AclTag::Mask => AclQualifier::Mask,
AclTag::Other => AclQualifier::Other,
};
acl.set(qualifier, mode);
}
Ok(acl)
}
}