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 let mut access = Self::EXIST;
195 if flags.can_read() {
196 access |= Self::READ;
197 }
198 if flags.can_write() {
199 access |= Self::WRITE;
200 }
201 access
202 }
203
204 pub fn rwx() -> Self {
205 Access::EXEC | Access::WRITE | Access::READ
206 }
207
208 pub fn is_nontrivial(&self) -> bool {
209 *self != Self::EXIST
210 }
211
212 pub fn rwx_bits(&self) -> u8 {
213 self.bits() & Self::ACCESS_MASK.bits()
214 }
215
216 pub fn user_mode(&self) -> FileMode {
217 FileMode::from_bits(u32::from(self.rwx_bits()) << 6)
218 }
219
220 pub fn group_mode(&self) -> FileMode {
221 FileMode::from_bits(u32::from(self.rwx_bits()) << 3)
222 }
223
224 pub fn other_mode(&self) -> FileMode {
225 FileMode::from_bits(u32::from(self.rwx_bits()))
226 }
227 }
228
229 impl std::convert::TryFrom<u32> for Access {
230 type Error = Errno;
231
232 fn try_from(value: u32) -> Result<Self, Self::Error> {
233 Self::from_bits(value.try_into().map_err(|_| errno!(EINVAL))?)
234 .ok_or_else(|| errno!(EINVAL))
235 }
236 }
237}
238
239pub use inner_access::Access;
240
241pub use mode;
243
244#[cfg(test)]
245mod test {
246 use super::*;
247
248 #[::fuchsia::test]
249 fn test_file_mode_from_string() {
250 assert_eq!(FileMode::from_string(b"0123".into()), Ok(FileMode(0o123)));
251 assert!(FileMode::from_string(b"123".into()).is_err());
252 assert!(FileMode::from_string(b"\x80".into()).is_err());
253 assert!(FileMode::from_string(b"0999".into()).is_err());
254 }
255
256 #[::fuchsia::test]
257 fn test_access_from_open_flags() {
258 assert_eq!(Access::from_open_flags(OpenFlags::RDONLY), Access::READ);
259 assert_eq!(Access::from_open_flags(OpenFlags::WRONLY), Access::WRITE);
260 assert_eq!(Access::from_open_flags(OpenFlags::RDWR), Access::READ | Access::WRITE);
261 assert_eq!(Access::from_open_flags(OpenFlags::ACCESS_MASK), Access::EXIST);
262 assert_eq!(Access::from_open_flags(OpenFlags::PATH), Access::EXIST);
263 assert_eq!(Access::from_open_flags(OpenFlags::PATH | OpenFlags::RDONLY), Access::EXIST);
264 assert_eq!(Access::from_open_flags(OpenFlags::PATH | OpenFlags::WRONLY), Access::EXIST);
265 assert_eq!(Access::from_open_flags(OpenFlags::PATH | OpenFlags::RDWR), Access::EXIST);
266 }
267}