1use crate::{
4 private, AccessError, AddRuleError, AddRulesError, BitFlags, CompatError, CompatResult,
5 HandleAccessError, HandleAccessesError, Ruleset, TailoredCompatLevel, TryCompat, ABI,
6};
7use enumflags2::BitFlag;
8
9#[cfg(test)]
10use crate::{make_bitflags, AccessFs, CompatLevel, CompatState, Compatibility};
11
12pub trait Access: BitFlag + private::Sealed {
13 fn from_all(abi: ABI) -> BitFlags<Self>;
15}
16
17pub trait HandledAccess: Access {}
19
20pub trait PrivateHandledAccess: HandledAccess {
21 fn ruleset_handle_access(
22 ruleset: &mut Ruleset,
23 access: BitFlags<Self>,
24 ) -> Result<(), HandleAccessesError>
25 where
26 Self: Access;
27
28 fn into_add_rules_error(error: AddRuleError<Self>) -> AddRulesError
29 where
30 Self: Access;
31
32 fn into_handle_accesses_error(error: HandleAccessError<Self>) -> HandleAccessesError
33 where
34 Self: Access;
35}
36
37fn full_negation<T>(flags: BitFlags<T>) -> BitFlags<T>
39where
40 T: Access,
41{
42 unsafe { BitFlags::<T>::from_bits_unchecked(!flags.bits()) }
43}
44
45#[test]
46fn bit_flags_full_negation() {
47 let scoped_negation = !BitFlags::<AccessFs>::all();
48 assert_eq!(scoped_negation, BitFlags::<AccessFs>::empty());
49 assert_ne!(scoped_negation, full_negation(BitFlags::<AccessFs>::all()));
52}
53
54impl<A> TailoredCompatLevel for BitFlags<A> where A: Access {}
55
56impl<A> TryCompat<A> for BitFlags<A>
57where
58 A: Access,
59{
60 fn try_compat_inner(&mut self, abi: ABI) -> Result<CompatResult<A>, CompatError<A>> {
61 if self.is_empty() {
62 Err(AccessError::Empty.into())
64 } else if !Self::all().contains(*self) {
65 Err(AccessError::Unknown {
68 access: *self,
69 unknown: *self & full_negation(Self::all()),
70 }
71 .into())
72 } else {
73 let compat = *self & A::from_all(abi);
74 let ret = if compat.is_empty() {
75 Ok(CompatResult::No(
76 AccessError::Incompatible { access: *self }.into(),
77 ))
78 } else if compat != *self {
79 let error = AccessError::PartiallyCompatible {
80 access: *self,
81 incompatible: *self & full_negation(compat),
82 }
83 .into();
84 Ok(CompatResult::Partial(error))
85 } else {
86 Ok(CompatResult::Full)
87 };
88 *self = compat;
89 ret
90 }
91 }
92}
93
94#[test]
95fn compat_bit_flags() {
96 use crate::ABI;
97
98 let mut compat: Compatibility = ABI::V1.into();
99 assert!(compat.state == CompatState::Init);
100
101 let ro_access = make_bitflags!(AccessFs::{Execute | ReadFile | ReadDir});
102 assert_eq!(
103 ro_access,
104 ro_access
105 .try_compat(compat.abi(), compat.level, &mut compat.state)
106 .unwrap()
107 .unwrap()
108 );
109 assert!(compat.state == CompatState::Full);
110
111 let empty_access = BitFlags::<AccessFs>::empty();
112 assert!(matches!(
113 empty_access
114 .try_compat(compat.abi(), compat.level, &mut compat.state)
115 .unwrap_err(),
116 CompatError::Access(AccessError::Empty)
117 ));
118
119 let all_unknown_access = unsafe { BitFlags::<AccessFs>::from_bits_unchecked(1 << 63) };
120 assert!(matches!(
121 all_unknown_access.try_compat(compat.abi(), compat.level, &mut compat.state).unwrap_err(),
122 CompatError::Access(AccessError::Unknown { access, unknown }) if access == all_unknown_access && unknown == all_unknown_access
123 ));
124 assert!(compat.state == CompatState::Dummy);
126
127 let some_unknown_access = unsafe { BitFlags::<AccessFs>::from_bits_unchecked(1 << 63 | 1) };
128 assert!(matches!(
129 some_unknown_access.try_compat(compat.abi(), compat.level, &mut compat.state).unwrap_err(),
130 CompatError::Access(AccessError::Unknown { access, unknown }) if access == some_unknown_access && unknown == all_unknown_access
131 ));
132 assert!(compat.state == CompatState::Dummy);
133
134 compat = ABI::Unsupported.into();
135
136 assert!(compat.state == CompatState::Init);
138
139 assert_eq!(
141 None,
142 ro_access
143 .try_compat(compat.abi(), compat.level, &mut compat.state)
144 .unwrap()
145 );
146
147 assert!(compat.state == CompatState::No);
148
149 compat.level = Some(CompatLevel::HardRequirement);
151 assert!(matches!(
152 ro_access.try_compat(compat.abi(), compat.level, &mut compat.state).unwrap_err(),
153 CompatError::Access(AccessError::Incompatible { access }) if access == ro_access
154 ));
155
156 compat = ABI::V1.into();
157
158 assert!(compat.state == CompatState::Init);
160
161 assert_eq!(
163 ro_access,
164 ro_access
165 .try_compat(compat.abi(), compat.level, &mut compat.state)
166 .unwrap()
167 .unwrap()
168 );
169
170 assert!(compat.state == CompatState::Full);
173
174 let v2_access = ro_access | AccessFs::Refer;
175
176 compat.level = Some(CompatLevel::HardRequirement);
178 assert!(matches!(
179 v2_access.try_compat(compat.abi(), compat.level, &mut compat.state).unwrap_err(),
180 CompatError::Access(AccessError::PartiallyCompatible { access, incompatible })
181 if access == v2_access && incompatible == AccessFs::Refer
182 ));
183}