selinux/policy/
parser.rs

1// Copyright 2023 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use super::error::ParseError;
6
7use std::fmt::Debug;
8use std::sync::Arc;
9use zerocopy::{FromBytes, Immutable, KnownLayout, Unaligned};
10
11pub type PolicyData = Arc<Vec<u8>>;
12pub type PolicyOffset = u32;
13
14#[derive(Clone, Debug, PartialEq)]
15pub struct PolicyCursor {
16    data: PolicyData,
17    offset: PolicyOffset,
18}
19
20impl PolicyCursor {
21    /// Returns a new [`PolicyCursor`] that wraps `data` in a [`Cursor`] for parsing.
22    pub fn new(data: PolicyData) -> Self {
23        Self { data, offset: 0 }
24    }
25
26    /// Returns a new [`PolicyCursor`] that wraps `data` in a [`Cursor`] for parsing at `offset`.
27    pub fn new_at(data: PolicyData, offset: PolicyOffset) -> Self {
28        Self { data, offset }
29    }
30
31    /// Returns an `P` as the parsed output of the next bytes in the underlying [`Cursor`] data.
32    pub fn parse<P: Clone + Debug + FromBytes + KnownLayout + Immutable + PartialEq + Unaligned>(
33        mut self,
34    ) -> Result<(P, Self), ParseError> {
35        let remaining_slice = &(self.data.as_ref()[self.offset as usize..]);
36        let (output, _) =
37            P::read_from_prefix(remaining_slice).map_err(|_| ParseError::MissingData {
38                type_name: std::any::type_name::<P>(),
39                type_size: std::mem::size_of::<P>(),
40                num_bytes: self.data.len() - self.offset as usize,
41            })?;
42        self.offset += std::mem::size_of::<P>() as PolicyOffset;
43        Ok((output, self))
44    }
45
46    pub fn offset(&self) -> PolicyOffset {
47        self.offset
48    }
49
50    pub fn data(&self) -> &PolicyData {
51        &self.data
52    }
53}
54
55#[cfg(test)]
56mod tests {
57    use super::*;
58    use zerocopy::little_endian as le;
59
60    #[derive(Clone, Debug, KnownLayout, FromBytes, Immutable, PartialEq, Unaligned)]
61    #[repr(C, packed)]
62    struct SomeNumbers {
63        a: u8,
64        b: le::U32,
65        c: le::U16,
66        d: u8,
67    }
68
69    #[test]
70    fn entire_vector() {
71        let bytes: Vec<u8> = (0..8).collect();
72        let data = Arc::new(bytes);
73
74        let tail = PolicyCursor::new(data);
75        let (some_numbers, tail) = tail.parse::<SomeNumbers>().expect("some numbers");
76
77        assert_eq!(0, some_numbers.a);
78        assert_eq!((1 << 0) + (2 << 8) + (3 << 16) + (4 << 24), some_numbers.b.get());
79        assert_eq!((5 << 0) + (6 << 8), some_numbers.c.get());
80        assert_eq!(7, some_numbers.d);
81        assert_eq!(8, tail.offset());
82        assert_eq!(8, tail.data().len());
83    }
84
85    #[test]
86    fn range_within_vector() {
87        let bytes: Vec<u8> = (0..40).collect();
88        let data = Arc::new(bytes);
89
90        let tail = PolicyCursor::new_at(data, 8);
91        let (first_some_numbers, tail) = tail.parse::<SomeNumbers>().expect("some numbers");
92        let (second_some_numbers, tail) = tail.parse::<SomeNumbers>().expect("some numbers");
93        let (third_some_numbers, tail) = tail.parse::<SomeNumbers>().expect("some numbers");
94
95        assert_eq!(8, first_some_numbers.a);
96        assert_eq!((9 << 0) + (10 << 8) + (11 << 16) + (12 << 24), first_some_numbers.b.get());
97        assert_eq!((13 << 0) + (14 << 8), first_some_numbers.c.get());
98        assert_eq!(15, first_some_numbers.d);
99        assert_eq!(16, second_some_numbers.a);
100        assert_eq!((17 << 0) + (18 << 8) + (19 << 16) + (20 << 24), second_some_numbers.b.get());
101        assert_eq!((21 << 0) + (22 << 8), second_some_numbers.c.get());
102        assert_eq!(23, second_some_numbers.d);
103        assert_eq!(24, third_some_numbers.a);
104        assert_eq!((25 << 0) + (26 << 8) + (27 << 16) + (28 << 24), third_some_numbers.b.get());
105        assert_eq!((29 << 0) + (30 << 8), third_some_numbers.c.get());
106        assert_eq!(31, third_some_numbers.d);
107        assert_eq!(32, tail.offset());
108        assert_eq!(40, tail.data().len());
109    }
110}