lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README

native_metadata.rs (7502B)


      1 //! Native filesystem metadata normalization behind portable generic boundaries.
      2 
      3 pub(crate) fn mode<T>(raw: T) -> u32
      4 where
      5     T: Into<u32>,
      6 {
      7     raw.into()
      8 }
      9 
     10 pub(crate) fn link_count<T>(raw: T) -> u64
     11 where
     12     T: Into<u64>,
     13 {
     14     raw.into()
     15 }
     16 
     17 pub(crate) fn device<T>(raw: T) -> Result<u64, T::Error>
     18 where
     19     T: TryInto<u64>,
     20 {
     21     raw.try_into()
     22 }
     23 
     24 pub(crate) fn secure_directory(
     25     is_directory: bool,
     26     actual_uid: u32,
     27     expected_uid: u32,
     28     raw_mode: u32,
     29 ) -> bool {
     30     crate::all_constraints([
     31         is_directory,
     32         actual_uid == expected_uid,
     33         raw_mode & 0o022 == 0,
     34     ])
     35 }
     36 
     37 pub(crate) fn exact_directory(
     38     is_directory: bool,
     39     actual_uid: u32,
     40     expected_uid: u32,
     41     raw_mode: u32,
     42 ) -> bool {
     43     crate::all_constraints([
     44         is_directory,
     45         actual_uid == expected_uid,
     46         raw_mode & 0o777 == 0o700,
     47     ])
     48 }
     49 
     50 pub(crate) fn restrictive_directory(
     51     is_directory: bool,
     52     actual_uid: u32,
     53     expected_uid: u32,
     54     raw_mode: u32,
     55 ) -> bool {
     56     crate::all_constraints([
     57         is_directory,
     58         actual_uid == expected_uid,
     59         matches!(raw_mode & 0o777, 0o500 | 0o700),
     60     ])
     61 }
     62 
     63 pub(crate) fn exact_regular_file(
     64     is_regular_file: bool,
     65     link_count: u64,
     66     actual_uid: u32,
     67     expected_uid: u32,
     68     raw_mode: u32,
     69 ) -> bool {
     70     crate::all_constraints([
     71         is_regular_file,
     72         link_count == 1,
     73         actual_uid == expected_uid,
     74         raw_mode & 0o777 == 0o600,
     75     ])
     76 }
     77 
     78 pub(crate) fn regular_owner_single_link(
     79     is_regular_file: bool,
     80     link_count: u64,
     81     actual_uid: u32,
     82     expected_uid: u32,
     83 ) -> bool {
     84     crate::all_constraints([is_regular_file, link_count == 1, actual_uid == expected_uid])
     85 }
     86 
     87 pub(crate) fn restrictive_regular_file(
     88     is_regular_file: bool,
     89     link_count: u64,
     90     actual_uid: u32,
     91     expected_uid: u32,
     92     raw_mode: u32,
     93 ) -> bool {
     94     crate::all_constraints([
     95         is_regular_file,
     96         link_count == 1,
     97         actual_uid == expected_uid,
     98         matches!(raw_mode & 0o777, 0o400 | 0o600),
     99     ])
    100 }
    101 
    102 pub(crate) fn valid_artifact_length(length: u64, expected: Option<u64>) -> bool {
    103     let expected_matches = expected.is_none_or(|expected| length == expected);
    104     crate::all_constraints([length != 0, length <= i64::MAX as u64, expected_matches])
    105 }
    106 
    107 pub(crate) fn identity_pair_matches(
    108     held_device: u64,
    109     held_inode: u64,
    110     current_device: u64,
    111     current_inode: u64,
    112     expected_device: u64,
    113     expected_inode: u64,
    114 ) -> bool {
    115     crate::all_constraints([
    116         (held_device, held_inode) == (expected_device, expected_inode),
    117         (current_device, current_inode) == (expected_device, expected_inode),
    118     ])
    119 }
    120 
    121 pub(crate) fn sqlite_wal_header(header: &[u8; 20]) -> bool {
    122     crate::all_constraints([
    123         &header[..16] == b"SQLite format 3\0",
    124         header[18] == 2,
    125         header[19] == 2,
    126     ])
    127 }
    128 
    129 pub(crate) fn sqlite_header(header: &[u8; 20]) -> bool {
    130     crate::all_constraints([
    131         &header[..16] == b"SQLite format 3\0",
    132         matches!(header[18], 1 | 2),
    133         header[19] == header[18],
    134     ])
    135 }
    136 
    137 #[cfg(test)]
    138 mod tests {
    139     use super::*;
    140 
    141     #[test]
    142     fn unsigned_mode_and_link_widths_normalize_without_truncation() {
    143         assert_eq!(mode(0o600_u16), 0o600);
    144         assert_eq!(mode(u32::MAX), u32::MAX);
    145         assert_eq!(link_count(u16::MAX), u64::from(u16::MAX));
    146         assert_eq!(link_count(u32::MAX), u64::from(u32::MAX));
    147         assert_eq!(link_count(u64::MAX), u64::MAX);
    148     }
    149 
    150     #[test]
    151     fn signed_and_unsigned_device_widths_remain_checked() {
    152         assert_eq!(device(7_i32), Ok(7));
    153         assert_eq!(device(u64::MAX), Ok(u64::MAX));
    154         assert!(device(-1_i32).is_err());
    155     }
    156 
    157     #[test]
    158     fn constraint_inventory_requires_every_entry() {
    159         assert!(crate::all_constraints([true; 16]));
    160         for index in 0..16 {
    161             let mut constraints = [true; 16];
    162             constraints[index] = false;
    163             assert!(!crate::all_constraints(constraints));
    164         }
    165         assert!(crate::all_constraints([]));
    166     }
    167 
    168     #[test]
    169     fn directory_predicates_bind_every_independent_fact() {
    170         assert!(secure_directory(true, 7, 7, 0o755));
    171         assert!(!secure_directory(false, 7, 7, 0o755));
    172         assert!(!secure_directory(true, 8, 7, 0o755));
    173         assert!(!secure_directory(true, 7, 7, 0o775));
    174 
    175         assert!(exact_directory(true, 7, 7, 0o700));
    176         assert!(!exact_directory(false, 7, 7, 0o700));
    177         assert!(!exact_directory(true, 8, 7, 0o700));
    178         assert!(!exact_directory(true, 7, 7, 0o500));
    179 
    180         assert!(restrictive_directory(true, 7, 7, 0o500));
    181         assert!(restrictive_directory(true, 7, 7, 0o700));
    182         assert!(!restrictive_directory(false, 7, 7, 0o700));
    183         assert!(!restrictive_directory(true, 8, 7, 0o700));
    184         assert!(!restrictive_directory(true, 7, 7, 0o755));
    185     }
    186 
    187     #[test]
    188     fn regular_file_predicates_bind_every_independent_fact() {
    189         assert!(exact_regular_file(true, 1, 7, 7, 0o600));
    190         assert!(!exact_regular_file(false, 1, 7, 7, 0o600));
    191         assert!(!exact_regular_file(true, 2, 7, 7, 0o600));
    192         assert!(!exact_regular_file(true, 1, 8, 7, 0o600));
    193         assert!(!exact_regular_file(true, 1, 7, 7, 0o400));
    194 
    195         assert!(regular_owner_single_link(true, 1, 7, 7));
    196         assert!(!regular_owner_single_link(false, 1, 7, 7));
    197         assert!(!regular_owner_single_link(true, 2, 7, 7));
    198         assert!(!regular_owner_single_link(true, 1, 8, 7));
    199 
    200         assert!(restrictive_regular_file(true, 1, 7, 7, 0o400));
    201         assert!(restrictive_regular_file(true, 1, 7, 7, 0o600));
    202         assert!(!restrictive_regular_file(false, 1, 7, 7, 0o600));
    203         assert!(!restrictive_regular_file(true, 2, 7, 7, 0o600));
    204         assert!(!restrictive_regular_file(true, 1, 8, 7, 0o600));
    205         assert!(!restrictive_regular_file(true, 1, 7, 7, 0o700));
    206     }
    207 
    208     #[test]
    209     fn artifact_length_and_identity_predicates_bind_boundaries() {
    210         assert!(valid_artifact_length(1, None));
    211         assert!(valid_artifact_length(
    212             i64::MAX as u64,
    213             Some(i64::MAX as u64)
    214         ));
    215         assert!(!valid_artifact_length(0, None));
    216         assert!(!valid_artifact_length(i64::MAX as u64 + 1, None));
    217         assert!(!valid_artifact_length(1, Some(2)));
    218 
    219         assert!(identity_pair_matches(1, 2, 1, 2, 1, 2));
    220         for values in [(0, 2, 1, 2), (1, 0, 1, 2), (1, 2, 0, 2), (1, 2, 1, 0)] {
    221             assert!(!identity_pair_matches(
    222                 values.0, values.1, values.2, values.3, 1, 2
    223             ));
    224         }
    225     }
    226 
    227     #[test]
    228     fn sqlite_header_requires_exact_wal_versions() {
    229         let mut header = [0_u8; 20];
    230         header[..16].copy_from_slice(b"SQLite format 3\0");
    231         header[18] = 2;
    232         header[19] = 2;
    233         assert!(sqlite_wal_header(&header));
    234 
    235         let mut bad_magic = header;
    236         bad_magic[0] = b'X';
    237         assert!(!sqlite_wal_header(&bad_magic));
    238         let mut bad_write = header;
    239         bad_write[18] = 1;
    240         assert!(!sqlite_wal_header(&bad_write));
    241         let mut bad_read = header;
    242         bad_read[19] = 1;
    243         assert!(!sqlite_wal_header(&bad_read));
    244 
    245         let mut rollback = header;
    246         rollback[18] = 1;
    247         rollback[19] = 1;
    248         assert!(sqlite_header(&rollback));
    249         assert!(sqlite_header(&header));
    250         assert!(!sqlite_header(&bad_magic));
    251         assert!(!sqlite_header(&bad_write));
    252         assert!(!sqlite_header(&bad_read));
    253     }
    254 }