flow_record/artifacts/posix/
file_mode.rs1use bitflags::bitflags;
2use flow_record_common::{FieldType, ToMsgPackValue};
3use lazy_regex::regex_captures;
4
5bitflags! {
6 #[repr(transparent)]
7 #[derive(Debug, PartialEq, Eq)]
8 pub struct FileMode: u16 {
9 const UNSPECIFIED = 0o000000;
10
11 const ISUID = 0o004000;
12 const ISGID = 0o002000;
13 const ISVTX = 0o001000;
14
15 const IRUSR = 0o000400;
16 const IWUSR = 0o000200;
17 const IXUSR = 0o000100;
18
19 const IRGRP = 0o000040;
20 const IWGRP = 0o000020;
21 const IXGRP = 0o000010;
22
23 const IROTH = 0o000004;
24 const IWOTH = 0o000002;
25 const IXOTH = 0o000001;
26 }
27}
28
29impl TryFrom<&str> for FileMode {
30 type Error = flow_record_common::Error;
31
32 fn try_from(value: &str) -> Result<Self, Self::Error> {
33 if let Some((_, rusr, wusr, xusr, rgrp, wgrp, xgrp, roth, woth, xoth)) = regex_captures!(
34 r#"([-r])([-w])([-xsS])([-r])([-w])([-xsS])([-r])([-w])([-xtT])"#,
35 value
36 ) {
37 let mut mode = Self::UNSPECIFIED;
38 if rusr == "r" {
39 mode |= Self::IRUSR
40 }
41 if wusr == "w" {
42 mode |= Self::IWUSR
43 }
44
45 match xusr {
46 "x" => mode |= Self::IXUSR,
47 "s" => mode |= Self::IXUSR | Self::ISUID,
48 "S" => mode |= Self::ISUID,
49 _ => (),
50 }
51
52 if rgrp == "r" {
53 mode |= Self::IRGRP
54 }
55 if wgrp == "w" {
56 mode |= Self::IWGRP
57 }
58
59 match xgrp {
60 "x" => mode |= Self::IXGRP,
61 "s" => mode |= Self::IXGRP | Self::ISGID,
62 "S" => mode |= Self::ISGID,
63 _ => (),
64 }
65
66 if roth == "r" {
67 mode |= Self::IROTH
68 }
69 if woth == "w" {
70 mode |= Self::IWOTH
71 }
72
73 match xoth {
74 "x" => mode |= Self::IXOTH,
75 "t" => mode |= Self::IXOTH | Self::ISVTX,
76 "T" => mode |= Self::ISVTX,
77 _ => (),
78 }
79 Ok(mode)
80 } else {
81 Err(flow_record_common::Error::InvalidModeString(value.into()))
82 }
83 }
84}
85
86impl ToMsgPackValue for FileMode {
87 fn to_msgpack_value(self) -> rmpv::Value {
88 rmpv::Value::Integer(self.bits().into())
89 }
90
91 fn field_type() -> flow_record_common::FieldType {
92 FieldType::UnixFileMode
93 }
94}
95
96#[cfg(test)]
97mod tests {
98 use crate::artifacts::posix::FileMode;
99
100 #[test]
101 fn test_single_flags() {
102 assert_eq!(FileMode::try_from("r--------").unwrap(), FileMode::IRUSR);
103 assert_eq!(FileMode::try_from("-w-------").unwrap(), FileMode::IWUSR);
104 assert_eq!(FileMode::try_from("--x------").unwrap(), FileMode::IXUSR);
105 assert_eq!(FileMode::try_from("---r-----").unwrap(), FileMode::IRGRP);
106 assert_eq!(FileMode::try_from("----w----").unwrap(), FileMode::IWGRP);
107 assert_eq!(FileMode::try_from("-----x---").unwrap(), FileMode::IXGRP);
108 assert_eq!(FileMode::try_from("------r--").unwrap(), FileMode::IROTH);
109 assert_eq!(FileMode::try_from("-------w-").unwrap(), FileMode::IWOTH);
110 assert_eq!(FileMode::try_from("--------x").unwrap(), FileMode::IXOTH);
111
112 assert_eq!(FileMode::try_from("--S------").unwrap(), FileMode::ISUID);
113 assert_eq!(FileMode::try_from("-----S---").unwrap(), FileMode::ISGID);
114 assert_eq!(FileMode::try_from("--------T").unwrap(), FileMode::ISVTX);
115
116 assert_eq!(
117 FileMode::try_from("--s------").unwrap(),
118 FileMode::IXUSR | FileMode::ISUID
119 );
120 assert_eq!(
121 FileMode::try_from("-----s---").unwrap(),
122 FileMode::IXGRP | FileMode::ISGID
123 );
124 assert_eq!(
125 FileMode::try_from("--------t").unwrap(),
126 FileMode::IXOTH | FileMode::ISVTX
127 );
128 }
129
130 #[test]
131 fn test_simple_modes() {
132 assert_eq!(
133 FileMode::try_from("rwxrwxrwx").unwrap(),
134 FileMode::from_bits(0o777).unwrap()
135 );
136 assert_eq!(
137 FileMode::try_from("rwxr-xr-x").unwrap(),
138 FileMode::from_bits(0o755).unwrap()
139 );
140 assert_eq!(
141 FileMode::try_from("rw-r--r--").unwrap(),
142 FileMode::from_bits(0o644).unwrap()
143 );
144 assert_eq!(
145 FileMode::try_from("r--------").unwrap(),
146 FileMode::from_bits(0o400).unwrap()
147 );
148 }
149}