use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct MapEntry {
pub inside_start: u32,
pub outside_start: u32,
pub count: u32,
}
impl MapEntry {
pub fn new(inside_start: u32, outside_start: u32, count: u32) -> Self {
Self {
inside_start,
outside_start,
count,
}
}
pub fn identity_single(id: u32) -> Self {
Self {
inside_start: id,
outside_start: id,
count: 1,
}
}
pub fn contains_inside(&self, id: u32) -> bool {
id >= self.inside_start && id < self.inside_start.saturating_add(self.count)
}
pub fn contains_outside(&self, id: u32) -> bool {
id >= self.outside_start && id < self.outside_start.saturating_add(self.count)
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct NsConfig {
pub uid_map: Vec<MapEntry>,
pub gid_map: Vec<MapEntry>,
}
impl NsConfig {
pub fn identity() -> Self {
Self {
uid_map: vec![MapEntry::new(0, 0, u32::MAX)],
gid_map: vec![MapEntry::new(0, 0, u32::MAX)],
}
}
pub fn is_identity(&self) -> bool {
self.uid_map.len() == 1
&& self.uid_map[0].inside_start == 0
&& self.uid_map[0].outside_start == 0
&& self.uid_map[0].count == u32::MAX
&& self.gid_map.len() == 1
&& self.gid_map[0].inside_start == 0
&& self.gid_map[0].outside_start == 0
&& self.gid_map[0].count == u32::MAX
}
}
pub fn outside_to_inside(outside: u32, map: &[MapEntry]) -> Option<u32> {
for entry in map {
if entry.contains_outside(outside) {
return Some(entry.inside_start + (outside - entry.outside_start));
}
}
None
}
pub fn inside_to_outside(inside: u32, map: &[MapEntry]) -> Option<u32> {
for entry in map {
if entry.contains_inside(inside) {
return Some(entry.outside_start + (inside - entry.inside_start));
}
}
None
}
pub fn remap(old_outside: u32, old_map: &[MapEntry], new_map: &[MapEntry]) -> Option<u32> {
let inside = outside_to_inside(old_outside, old_map)?;
inside_to_outside(inside, new_map)
}
pub fn mappings_equal(a: &NsConfig, b: &NsConfig) -> bool {
a.uid_map == b.uid_map && a.gid_map == b.gid_map
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_map_entry_contains() {
let entry = MapEntry::new(1, 100000, 65536);
assert!(entry.contains_inside(1));
assert!(entry.contains_inside(100));
assert!(entry.contains_inside(65536));
assert!(!entry.contains_inside(0));
assert!(!entry.contains_inside(65537));
assert!(entry.contains_outside(100000));
assert!(entry.contains_outside(100100));
assert!(entry.contains_outside(165535));
assert!(!entry.contains_outside(99999));
assert!(!entry.contains_outside(165536));
}
#[test]
fn test_outside_to_inside_simple() {
let map = vec![MapEntry::new(0, 1000, 1), MapEntry::new(1, 100000, 65536)];
assert_eq!(outside_to_inside(1000, &map), Some(0));
assert_eq!(outside_to_inside(100000, &map), Some(1));
assert_eq!(outside_to_inside(100189, &map), Some(190));
assert_eq!(outside_to_inside(999, &map), None);
assert_eq!(outside_to_inside(200000, &map), None);
}
#[test]
fn test_inside_to_outside_simple() {
let map = vec![MapEntry::new(0, 1000, 1), MapEntry::new(1, 100000, 65536)];
assert_eq!(inside_to_outside(0, &map), Some(1000));
assert_eq!(inside_to_outside(1, &map), Some(100000));
assert_eq!(inside_to_outside(190, &map), Some(100189));
assert_eq!(inside_to_outside(70000, &map), None);
}
#[test]
fn test_remap_between_namespaces() {
let map_a = vec![MapEntry::new(0, 1000, 1), MapEntry::new(1, 100000, 65536)];
let map_b = vec![MapEntry::new(0, 2000, 1), MapEntry::new(1, 200000, 65536)];
assert_eq!(remap(100189, &map_a, &map_b), Some(200189));
assert_eq!(remap(1000, &map_a, &map_b), Some(2000));
}
#[test]
fn test_identity_mapping() {
let id = NsConfig::identity();
assert!(id.is_identity());
assert_eq!(outside_to_inside(0, &id.uid_map), Some(0));
assert_eq!(outside_to_inside(1000, &id.uid_map), Some(1000));
assert_eq!(
outside_to_inside(u32::MAX - 1, &id.uid_map),
Some(u32::MAX - 1)
);
assert_eq!(inside_to_outside(0, &id.uid_map), Some(0));
assert_eq!(inside_to_outside(65535, &id.uid_map), Some(65535));
}
#[test]
fn test_non_identity() {
let ns = NsConfig {
uid_map: vec![MapEntry::new(0, 1000, 1)],
gid_map: vec![MapEntry::new(0, 1000, 1)],
};
assert!(!ns.is_identity());
}
#[test]
fn test_mappings_equal() {
let a = NsConfig {
uid_map: vec![MapEntry::new(0, 1000, 1)],
gid_map: vec![MapEntry::new(0, 1000, 1)],
};
let b = NsConfig {
uid_map: vec![MapEntry::new(0, 1000, 1)],
gid_map: vec![MapEntry::new(0, 1000, 1)],
};
let c = NsConfig {
uid_map: vec![MapEntry::new(0, 2000, 1)],
gid_map: vec![MapEntry::new(0, 1000, 1)],
};
assert!(mappings_equal(&a, &b));
assert!(!mappings_equal(&a, &c));
}
#[test]
fn test_overflow_safety() {
let entry = MapEntry::new(u32::MAX - 10, 0, 20);
assert!(entry.contains_inside(u32::MAX - 10));
assert!(entry.contains_inside(u32::MAX - 1));
assert!(!entry.contains_inside(u32::MAX));
let entry2 = MapEntry::new(0, u32::MAX - 10, 20);
assert!(entry2.contains_outside(u32::MAX - 1));
assert!(!entry2.contains_outside(u32::MAX));
}
}