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 fn user_access(&self) -> inner_access::Access {
108 inner_access::Access::try_from((self.bits() & 0o700) >> 6).unwrap()
109 }
110
111 pub fn group_access(&self) -> inner_access::Access {
112 inner_access::Access::try_from((self.bits() & 0o070) >> 3).unwrap()
113 }
114
115 pub fn other_access(&self) -> inner_access::Access {
116 inner_access::Access::try_from(self.bits() & 0o007).unwrap()
117 }
118}
119
120impl ops::BitOr for FileMode {
121 type Output = Self;
122
123 fn bitor(self, rhs: Self) -> Self::Output {
124 Self(self.0 | rhs.0)
125 }
126}
127
128impl ops::BitAnd for FileMode {
129 type Output = Self;
130
131 fn bitand(self, rhs: Self) -> Self::Output {
132 Self(self.0 & rhs.0)
133 }
134}
135
136impl ops::BitOrAssign for FileMode {
137 fn bitor_assign(&mut self, rhs: Self) {
138 self.0 |= rhs.0;
139 }
140}
141
142impl ops::BitAndAssign for FileMode {
143 fn bitand_assign(&mut self, rhs: Self) {
144 self.0 &= rhs.0;
145 }
146}
147
148impl ops::Not for FileMode {
149 type Output = Self;
150
151 fn not(self) -> Self::Output {
152 Self(!self.0)
153 }
154}
155
156#[macro_export]
157macro_rules! mode {
158 ($type:ident, $mode:expr) => {
159 $crate::file_mode::FileMode::from_bits($mode | $crate::file_mode::FileMode::$type.bits())
160 };
161}
162
163mod inner_access {
166 #![allow(clippy::bad_bit_mask)] use super::OpenFlags;
170 use crate::errors::{Errno, errno};
171
172 bitflags::bitflags! {
173 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
174 pub struct Access: u8 {
175 const EXIST = 0;
176 const EXEC = 1;
177 const WRITE = 2;
178 const READ = 4;
179
180 const ACCESS_MASK = 1 | 2 | 4;
183 }
184 }
185
186 impl Access {
187 pub fn from_open_flags(flags: OpenFlags) -> Self {
188 match flags & OpenFlags::ACCESS_MASK {
189 OpenFlags::RDONLY => Self::READ,
190 OpenFlags::WRONLY => Self::WRITE,
191 OpenFlags::RDWR => Self::READ | Self::WRITE,
192 _ => Self::EXIST, }
194 }
195
196 pub fn rwx() -> Self {
197 Access::EXEC | Access::WRITE | Access::READ
198 }
199
200 pub fn is_nontrivial(&self) -> bool {
201 *self != Self::EXIST
202 }
203
204 pub fn rwx_bits(&self) -> u8 {
205 self.bits() & Self::ACCESS_MASK.bits()
206 }
207 }
208
209 impl std::convert::TryFrom<u32> for Access {
210 type Error = Errno;
211
212 fn try_from(value: u32) -> Result<Self, Self::Error> {
213 Self::from_bits(value.try_into().map_err(|_| errno!(EINVAL))?)
214 .ok_or_else(|| errno!(EINVAL))
215 }
216 }
217}
218
219pub use inner_access::Access;
220
221pub struct AccessCheck(Option<Access>);
222
223impl Default for AccessCheck {
224 fn default() -> Self {
226 Self(None)
227 }
228}
229
230impl AccessCheck {
231 pub fn skip() -> Self {
233 Self(Some(Access::EXIST))
234 }
235
236 pub fn check_for(access: Access) -> Self {
238 Self(Some(access))
239 }
240
241 pub fn resolve(&self, flags: OpenFlags) -> Access {
247 self.0.unwrap_or_else(|| Access::from_open_flags(flags))
248 }
249}
250
251pub use mode;
253
254#[cfg(test)]
255mod test {
256 use super::*;
257
258 #[::fuchsia::test]
259 fn test_file_mode_from_string() {
260 assert_eq!(FileMode::from_string(b"0123".into()), Ok(FileMode(0o123)));
261 assert!(FileMode::from_string(b"123".into()).is_err());
262 assert!(FileMode::from_string(b"\x80".into()).is_err());
263 assert!(FileMode::from_string(b"0999".into()).is_err());
264 }
265}