Skip to main content

dolang_vfs/
macos_acl.rs

1use serde::{Deserialize, Serialize};
2use uuid::Uuid;
3
4bitflags::bitflags! {
5    /// Permission bits used by macOS extended ACL entries.
6    #[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
7    pub struct MacosAceMask: u32 {
8        /// Read the data of a file, or list the entries of a directory.
9        const READ_DATA = 0x00000002;
10        /// Write the data of a file, or add a new file to a directory.
11        const WRITE_DATA = 0x00000004;
12        /// Execute a file, or search a directory.
13        const EXECUTE = 0x00000008;
14        /// Delete the file or directory.
15        const DELETE = 0x00000010;
16        /// Append data to a file, or add a new subdirectory to a directory.
17        const APPEND_DATA = 0x00000020;
18        /// Delete a file or directory within a directory.
19        const DELETE_CHILD = 0x00000040;
20        /// Read the (non-ACL) attributes of a file or directory.
21        const READ_ATTRIBUTES = 0x00000080;
22        /// Write the (non-ACL) attributes of a file or directory.
23        const WRITE_ATTRIBUTES = 0x00000100;
24        /// Read the extended attributes of a file or directory.
25        const READ_EXTATTRIBUTES = 0x00000200;
26        /// Write the extended attributes of a file or directory.
27        const WRITE_EXTATTRIBUTES = 0x00000400;
28        /// Read the ACL/security information.
29        const READ_SECURITY = 0x00000800;
30        /// Write the ACL/security information.
31        const WRITE_SECURITY = 0x00001000;
32        /// Change the owner.
33        const CHANGE_OWNER = 0x00002000;
34        /// Synchronize I/O.
35        const SYNCHRONIZE = 0x00100000;
36    }
37}
38
39bitflags::bitflags! {
40    /// Inheritance flags used by macOS extended ACL entries.
41    #[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
42    pub struct MacosAceFlags: u32 {
43        /// The entry was created by inheritance from a parent directory.
44        const INHERITED = 0x0010;
45        /// Inherited by files created within a directory.
46        const FILE_INHERIT = 0x0020;
47        /// Inherited by subdirectories created within a directory.
48        const DIRECTORY_INHERIT = 0x0040;
49        /// Inherited only by direct children, not further descendants.
50        const LIMIT_INHERIT = 0x0080;
51        /// Present only to be inherited; does not apply to the entry's own object.
52        const ONLY_INHERIT = 0x0100;
53    }
54}
55
56/// The kind of a macOS extended ACL entry.
57///
58/// Unlike NFSv4 ACLs, macOS extended ACLs have no audit/alarm entry kinds.
59#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
60pub enum MacosAceType {
61    /// Grants the permissions in the entry's mask.
62    Allow,
63    /// Denies the permissions in the entry's mask.
64    Deny,
65}
66
67/// A portable macOS extended ACL entry.
68///
69/// macOS resolves every principal (owning user, owning group, well-known
70/// accounts, or an arbitrary user/group) to a `guid_t` before it reaches the
71/// kernel ACL, so unlike [`Nfs4Ace`](crate::security::Nfs4Ace)'s qualifier,
72/// there is no separate enum here: the qualifier is simply the UUID.
73#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
74pub struct MacosAce {
75    /// Whether this entry allows or denies.
76    pub(crate) ace_type: MacosAceType,
77    /// Principal controlled by this entry, as a macOS `guid_t`.
78    pub(crate) qualifier: Uuid,
79    /// Permissions named by this entry.
80    pub(crate) mask: MacosAceMask,
81    /// Inheritance flags.
82    pub(crate) flags: MacosAceFlags,
83}
84
85impl MacosAce {
86    /// Creates a macOS ACL entry.
87    pub const fn new(
88        ace_type: MacosAceType,
89        qualifier: Uuid,
90        mask: MacosAceMask,
91        flags: MacosAceFlags,
92    ) -> Self {
93        Self {
94            ace_type,
95            qualifier,
96            mask,
97            flags,
98        }
99    }
100    /// Returns whether this entry allows or denies access.
101    pub const fn ace_type(self) -> MacosAceType {
102        self.ace_type
103    }
104    /// Returns the principal UUID.
105    pub const fn qualifier(self) -> Uuid {
106        self.qualifier
107    }
108    /// Returns the access-rights mask.
109    pub const fn mask(self) -> MacosAceMask {
110        self.mask
111    }
112    /// Returns the inheritance flags.
113    pub const fn flags(self) -> MacosAceFlags {
114        self.flags
115    }
116}
117
118/// A portable macOS extended access-control list.
119///
120/// Like [`Nfs4Acl`](crate::security::Nfs4Acl), this is an ordered,
121/// first-match list with no completeness requirement, so there is nothing to
122/// validate beyond the shape of the individual entries.
123#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
124pub struct MacosAcl {
125    entries: Vec<MacosAce>,
126}
127
128impl MacosAcl {
129    /// Constructs an access-control list from `entries`, in evaluation order.
130    pub fn new(entries: Vec<MacosAce>) -> Self {
131        Self { entries }
132    }
133
134    /// Returns the ACL entries in their stored (evaluation) order.
135    pub fn entries(&self) -> &[MacosAce] {
136        &self.entries
137    }
138}
139
140#[cfg(test)]
141mod tests {
142    use super::*;
143
144    #[test]
145    fn serde_round_trip_preserves_entries() {
146        let acl = MacosAcl::new(vec![
147            MacosAce {
148                ace_type: MacosAceType::Allow,
149                qualifier: Uuid::nil(),
150                mask: MacosAceMask::READ_DATA | MacosAceMask::WRITE_DATA,
151                flags: MacosAceFlags::empty(),
152            },
153            MacosAce {
154                ace_type: MacosAceType::Deny,
155                qualifier: Uuid::max(),
156                mask: MacosAceMask::WRITE_DATA,
157                flags: MacosAceFlags::FILE_INHERIT | MacosAceFlags::DIRECTORY_INHERIT,
158            },
159        ]);
160        let bytes = postcard::to_stdvec(&acl).unwrap();
161        assert_eq!(postcard::from_bytes::<MacosAcl>(&bytes).unwrap(), acl);
162    }
163}