starnix_uapi/
file_mode.rs1use crate::errors::{Errno, errno, error};
6use crate::open_flags::OpenFlags;
7use crate::uapi;
8use bstr::BStr;
9use std::ops;
10
11#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
12pub struct FileMode(u32);
13
14impl FileMode {
15 pub const IFLNK: FileMode = FileMode(uapi::S_IFLNK);
16 pub const IFREG: FileMode = FileMode(uapi::S_IFREG);
17 pub const IFDIR: FileMode = FileMode(uapi::S_IFDIR);
18 pub const IFCHR: FileMode = FileMode(uapi::S_IFCHR);
19 pub const IFBLK: FileMode = FileMode(uapi::S_IFBLK);
20 pub const IFIFO: FileMode = FileMode(uapi::S_IFIFO);
21 pub const IFSOCK: FileMode = FileMode(uapi::S_IFSOCK);
22
23 pub const ISUID: FileMode = FileMode(uapi::S_ISUID);
24 pub const ISGID: FileMode = FileMode(uapi::S_ISGID);
25 pub const ISVTX: FileMode = FileMode(uapi::S_ISVTX);
26 pub const IRWXU: FileMode = FileMode(uapi::S_IRWXU);
27 pub const IRUSR: FileMode = FileMode(uapi::S_IRUSR);
28 pub const IWUSR: FileMode = FileMode(uapi::S_IWUSR);
29 pub const IXUSR: FileMode = FileMode(uapi::S_IXUSR);
30 pub const IRWXG: FileMode = FileMode(uapi::S_IRWXG);
31 pub const IRGRP: FileMode = FileMode(uapi::S_IRGRP);
32 pub const IWGRP: FileMode = FileMode(uapi::S_IWGRP);
33 pub const IXGRP: FileMode = FileMode(uapi::S_IXGRP);
34 pub const IRWXO: FileMode = FileMode(uapi::S_IRWXO);
35 pub const IROTH: FileMode = FileMode(uapi::S_IROTH);
36 pub const IWOTH: FileMode = FileMode(uapi::S_IWOTH);
37 pub const IXOTH: FileMode = FileMode(uapi::S_IXOTH);
38
39 pub const IFMT: FileMode = FileMode(uapi::S_IFMT);
40
41 pub const DEFAULT_UMASK: FileMode = FileMode(0o022);
42 pub const ALLOW_ALL: FileMode = FileMode(0o777);
43 pub const PERMISSIONS: FileMode = FileMode(0o7777);
44 pub const EMPTY: FileMode = FileMode(0);
45
46 pub const fn from_bits(mask: u32) -> FileMode {
47 FileMode(mask)
48 }
49
50 pub fn from_string(mask: &BStr) -> Result<FileMode, Errno> {
51 if !mask.starts_with(b"0") {
52 return error!(EINVAL);
53 }
54 let mask = std::str::from_utf8(mask).map_err(|_| errno!(EINVAL))?;
55 let mask = u32::from_str_radix(mask, 8).map_err(|_| errno!(EINVAL))?;
56 Ok(Self::from_bits(mask))
57 }
58
59 pub const fn bits(&self) -> u32 {
60 self.0
61 }
62
63 pub const fn contains(&self, other: FileMode) -> bool {
64 self.0 & other.0 == other.bits()
65 }
66
67 pub const fn intersects(&self, other: FileMode) -> bool {
68 self.0 & other.0 != 0
69 }
70
71 pub fn fmt(&self) -> FileMode {
72 FileMode(self.bits() & uapi::S_IFMT)
73 }
74
75 pub const fn with_type(&self, file_type: FileMode) -> FileMode {
76 FileMode((self.bits() & Self::PERMISSIONS.bits()) | (file_type.bits() & uapi::S_IFMT))
77 }
78
79 pub const fn is_lnk(&self) -> bool {
80 (self.bits() & uapi::S_IFMT) == uapi::S_IFLNK
81 }
82
83 pub const fn is_reg(&self) -> bool {
84 (self.bits() & uapi::S_IFMT) == uapi::S_IFREG
85 }
86
87 pub const fn is_dir(&self) -> bool {
88 (self.bits() & uapi::S_IFMT) == uapi::S_IFDIR
89 }
90
91 pub const fn is_chr(&self) -> bool {
92 (self.bits() & uapi::S_IFMT) == uapi::S_IFCHR
93 }
94
95 pub const fn is_blk(&self) -> bool {
96 (self.bits() & uapi::S_IFMT) == uapi::S_IFBLK
97 }
98
99 pub const fn is_fifo(&self) -> bool {
100 (self.bits() & uapi::S_IFMT) == uapi::S_IFIFO
101 }
102
103 pub const fn is_sock(&self) -> bool {
104 (self.bits() & uapi::S_IFMT) == uapi::S_IFSOCK
105 }
106
107 pub const fn is_special(&self) -> bool {
110 self.is_chr() | self.is_blk() | self.is_fifo() | self.is_sock()
111 }
112
113 pub fn user_access(&self) -> inner_access::Access {
114 inner_access::Access::try_from((self.bits() & 0o700) >> 6).unwrap()
115 }
116
117 pub fn group_access(&self) -> inner_access::Access {
118 inner_access::Access::try_from((self.bits() & 0o070) >> 3).unwrap()
119 }
120
121 pub fn other_access(&self) -> inner_access::Access {
122 inner_access::Access::try_from(self.bits() & 0o007).unwrap()
123 }
124}
125
126impl ops::BitOr for FileMode {
127 type Output = Self;
128
129 fn bitor(self, rhs: Self) -> Self::Output {
130 Self(self.0 | rhs.0)
131 }
132}
133
134impl ops::BitAnd for FileMode {
135 type Output = Self;
136
137 fn bitand(self, rhs: Self) -> Self::Output {
138 Self(self.0 & rhs.0)
139 }
140}
141
142impl ops::BitOrAssign for FileMode {
143 fn bitor_assign(&mut self, rhs: Self) {
144 self.0 |= rhs.0;
145 }
146}
147
148impl ops::BitAndAssign for FileMode {
149 fn bitand_assign(&mut self, rhs: Self) {
150 self.0 &= rhs.0;
151 }
152}
153
154impl ops::Not for FileMode {
155 type Output = Self;
156
157 fn not(self) -> Self::Output {
158 Self(!self.0)
159 }
160}
161
162#[macro_export]
163macro_rules! mode {
164 ($type:ident, $mode:expr) => {
165 $crate::file_mode::FileMode::from_bits($mode | $crate::file_mode::FileMode::$type.bits())
166 };
167}
168
169mod inner_access {
172 #![allow(clippy::bad_bit_mask)] use super::{FileMode, OpenFlags};
176 use crate::errors::{Errno, errno};
177
178 bitflags::bitflags! {
179 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
180 pub struct Access: u8 {
181 const EXIST = 0;
182 const EXEC = 1 << 0;
183 const WRITE = 1 << 1;
184 const READ = 1 << 2;
185
186 const ACCESS_MASK = Self::EXEC.bits() | Self::WRITE.bits() | Self::READ.bits();
189 }
190 }
191
192 impl Access {
193 pub fn from_open_flags(flags: OpenFlags) -> Self {
194 match flags & OpenFlags::ACCESS_MASK {
195 OpenFlags::RDONLY => Self::READ,
196 OpenFlags::WRONLY => Self::WRITE,
197 OpenFlags::RDWR => Self::READ | Self::WRITE,
198 _ => Self::EXIST, }
200 }
201
202 pub fn rwx() -> Self {
203 Access::EXEC | Access::WRITE | Access::READ
204 }
205
206 pub fn is_nontrivial(&self) -> bool {
207 *self != Self::EXIST
208 }
209
210 pub fn rwx_bits(&self) -> u8 {
211 self.bits() & Self::ACCESS_MASK.bits()
212 }
213
214 pub fn user_mode(&self) -> FileMode {
215 FileMode::from_bits(u32::from(self.rwx_bits()) << 6)
216 }
217
218 pub fn group_mode(&self) -> FileMode {
219 FileMode::from_bits(u32::from(self.rwx_bits()) << 3)
220 }
221
222 pub fn other_mode(&self) -> FileMode {
223 FileMode::from_bits(u32::from(self.rwx_bits()))
224 }
225 }
226
227 impl std::convert::TryFrom<u32> for Access {
228 type Error = Errno;
229
230 fn try_from(value: u32) -> Result<Self, Self::Error> {
231 Self::from_bits(value.try_into().map_err(|_| errno!(EINVAL))?)
232 .ok_or_else(|| errno!(EINVAL))
233 }
234 }
235}
236
237pub use inner_access::Access;
238
239pub struct AccessCheck(Option<Access>);
240
241impl Default for AccessCheck {
242 fn default() -> Self {
244 Self(None)
245 }
246}
247
248impl AccessCheck {
249 pub fn skip() -> Self {
251 Self(Some(Access::EXIST))
252 }
253
254 pub fn check_for(access: Access) -> Self {
256 Self(Some(access))
257 }
258
259 pub fn resolve(&self, flags: OpenFlags) -> Access {
265 self.0.unwrap_or_else(|| Access::from_open_flags(flags))
266 }
267}
268
269pub use mode;
271
272#[cfg(test)]
273mod test {
274 use super::*;
275
276 #[::fuchsia::test]
277 fn test_file_mode_from_string() {
278 assert_eq!(FileMode::from_string(b"0123".into()), Ok(FileMode(0o123)));
279 assert!(FileMode::from_string(b"123".into()).is_err());
280 assert!(FileMode::from_string(b"\x80".into()).is_err());
281 assert!(FileMode::from_string(b"0999".into()).is_err());
282 }
283}