Skip to main content

dolang_vfs/
posix_acl.rs

1use serde::{Deserialize, Deserializer, Serialize, de};
2use std::{collections::HashSet, error, fmt};
3
4use crate::security::Permission;
5
6/// The principal or class selected by a POSIX.1e ACL entry.
7#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
8pub enum PosixAclQualifier {
9    /// Owning user entry.
10    UserObj,
11    /// Named user entry.
12    User(u32),
13    /// Owning group entry.
14    GroupObj,
15    /// Named group entry.
16    Group(u32),
17    /// Maximum permissions for named users and groups.
18    Mask,
19    /// Other users entry.
20    Other,
21}
22
23/// A portable POSIX.1e ACL entry.
24#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
25pub struct PosixAce {
26    /// Principal or class controlled by this entry.
27    pub(crate) qualifier: PosixAclQualifier,
28    /// Permissions granted by this entry.
29    pub(crate) permissions: Permission,
30}
31
32impl PosixAce {
33    /// Creates a POSIX ACL entry.
34    pub const fn new(qualifier: PosixAclQualifier, permissions: Permission) -> Self {
35        Self {
36            qualifier,
37            permissions,
38        }
39    }
40    /// Returns the principal or class to which this entry applies.
41    pub const fn qualifier(self) -> PosixAclQualifier {
42        self.qualifier
43    }
44    /// Returns the permissions granted by this entry.
45    pub const fn permissions(self) -> Permission {
46        self.permissions
47    }
48}
49
50/// A validated, portable POSIX.1e access-control list.
51#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
52pub struct PosixAcl {
53    entries: Vec<PosixAce>,
54}
55
56impl PosixAcl {
57    /// Validates `entries` and constructs an access-control list.
58    pub fn new(entries: Vec<PosixAce>) -> Result<Self, PosixAclError> {
59        validate(&entries)?;
60        Ok(Self { entries })
61    }
62
63    /// Returns the ACL entries in their stored order.
64    pub fn entries(&self) -> &[PosixAce] {
65        &self.entries
66    }
67}
68
69impl<'de> Deserialize<'de> for PosixAcl {
70    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
71    where
72        D: Deserializer<'de>,
73    {
74        #[derive(Deserialize)]
75        struct Wire {
76            entries: Vec<PosixAce>,
77        }
78        let wire = Wire::deserialize(deserializer)?;
79        Self::new(wire.entries).map_err(de::Error::custom)
80    }
81}
82
83/// Validation error returned while constructing a POSIX ACL.
84#[derive(Clone, Copy, Debug, PartialEq, Eq)]
85#[non_exhaustive]
86pub enum PosixAclError {
87    /// The ACL contains no entries.
88    Empty,
89    /// A required entry is absent.
90    Missing(PosixAclQualifier),
91    /// An entry qualifier occurs more than once.
92    Duplicate(PosixAclQualifier),
93    /// Named entries require a mask entry.
94    MissingMask,
95}
96
97impl fmt::Display for PosixAclError {
98    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99        match self {
100            Self::Empty => f.write_str("ACL must contain entries"),
101            Self::Missing(qualifier) => write!(f, "ACL is missing {qualifier} entry"),
102            Self::Duplicate(qualifier) => write!(f, "ACL contains duplicate {qualifier} entries"),
103            Self::MissingMask => f.write_str("ACL with named entries must contain a mask"),
104        }
105    }
106}
107
108impl error::Error for PosixAclError {}
109
110impl fmt::Display for PosixAclQualifier {
111    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
112        match self {
113            Self::UserObj => f.write_str("owner user"),
114            Self::User(id) => write!(f, "user {id}"),
115            Self::GroupObj => f.write_str("owner group"),
116            Self::Group(id) => write!(f, "group {id}"),
117            Self::Mask => f.write_str("mask"),
118            Self::Other => f.write_str("other"),
119        }
120    }
121}
122
123fn validate(entries: &[PosixAce]) -> Result<(), PosixAclError> {
124    if entries.is_empty() {
125        return Err(PosixAclError::Empty);
126    }
127    let mut qualifiers = HashSet::new();
128    let mut named = false;
129    for entry in entries {
130        if !qualifiers.insert(entry.qualifier) {
131            return Err(PosixAclError::Duplicate(entry.qualifier));
132        }
133        named |= matches!(
134            entry.qualifier,
135            PosixAclQualifier::User(_) | PosixAclQualifier::Group(_)
136        );
137    }
138    for required in [
139        PosixAclQualifier::UserObj,
140        PosixAclQualifier::GroupObj,
141        PosixAclQualifier::Other,
142    ] {
143        if !qualifiers.contains(&required) {
144            return Err(PosixAclError::Missing(required));
145        }
146    }
147    if named && !qualifiers.contains(&PosixAclQualifier::Mask) {
148        return Err(PosixAclError::MissingMask);
149    }
150    Ok(())
151}
152
153#[cfg(test)]
154mod tests {
155    use super::*;
156
157    fn ace(qualifier: PosixAclQualifier) -> PosixAce {
158        PosixAce {
159            qualifier,
160            permissions: Permission::empty(),
161        }
162    }
163
164    #[test]
165    fn validates_acl_shape() {
166        assert_eq!(PosixAcl::new(Vec::new()), Err(PosixAclError::Empty));
167        assert_eq!(
168            PosixAcl::new(vec![
169                ace(PosixAclQualifier::UserObj),
170                ace(PosixAclQualifier::GroupObj),
171                ace(PosixAclQualifier::Other),
172                ace(PosixAclQualifier::User(1)),
173            ]),
174            Err(PosixAclError::MissingMask)
175        );
176        PosixAcl::new(vec![
177            ace(PosixAclQualifier::UserObj),
178            ace(PosixAclQualifier::GroupObj),
179            ace(PosixAclQualifier::Other),
180            ace(PosixAclQualifier::User(1)),
181            ace(PosixAclQualifier::Mask),
182        ])
183        .unwrap();
184    }
185
186    #[test]
187    fn serde_round_trip_preserves_entries() {
188        let acl = PosixAcl::new(vec![
189            ace(PosixAclQualifier::Other),
190            ace(PosixAclQualifier::Mask),
191            ace(PosixAclQualifier::GroupObj),
192            ace(PosixAclQualifier::User(42)),
193            ace(PosixAclQualifier::UserObj),
194        ])
195        .unwrap();
196        let bytes = postcard::to_stdvec(&acl).unwrap();
197        assert_eq!(postcard::from_bytes::<PosixAcl>(&bytes).unwrap(), acl);
198    }
199}