flow_record/artifacts/posix/
file_mode.rs

1use 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}