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}