1use serde::{Deserialize, Serialize};
2
3#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
5pub struct MapEntry {
6 pub inside_start: u32,
8 pub outside_start: u32,
10 pub count: u32,
12}
13
14impl MapEntry {
15 pub fn new(inside_start: u32, outside_start: u32, count: u32) -> Self {
16 Self {
17 inside_start,
18 outside_start,
19 count,
20 }
21 }
22
23 pub fn identity_single(id: u32) -> Self {
25 Self {
26 inside_start: id,
27 outside_start: id,
28 count: 1,
29 }
30 }
31
32 pub fn contains_inside(&self, id: u32) -> bool {
34 id >= self.inside_start && id < self.inside_start.saturating_add(self.count)
35 }
36
37 pub fn contains_outside(&self, id: u32) -> bool {
39 id >= self.outside_start && id < self.outside_start.saturating_add(self.count)
40 }
41}
42
43#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
45pub struct NsConfig {
46 pub uid_map: Vec<MapEntry>,
47 pub gid_map: Vec<MapEntry>,
48}
49
50impl NsConfig {
51 pub fn identity() -> Self {
54 Self {
55 uid_map: vec![MapEntry::new(0, 0, u32::MAX)],
56 gid_map: vec![MapEntry::new(0, 0, u32::MAX)],
57 }
58 }
59
60 pub fn is_identity(&self) -> bool {
62 self.uid_map.len() == 1
63 && self.uid_map[0].inside_start == 0
64 && self.uid_map[0].outside_start == 0
65 && self.uid_map[0].count == u32::MAX
66 && self.gid_map.len() == 1
67 && self.gid_map[0].inside_start == 0
68 && self.gid_map[0].outside_start == 0
69 && self.gid_map[0].count == u32::MAX
70 }
71}
72
73pub fn outside_to_inside(outside: u32, map: &[MapEntry]) -> Option<u32> {
75 for entry in map {
76 if entry.contains_outside(outside) {
77 return Some(entry.inside_start + (outside - entry.outside_start));
78 }
79 }
80 None
81}
82
83pub fn inside_to_outside(inside: u32, map: &[MapEntry]) -> Option<u32> {
85 for entry in map {
86 if entry.contains_inside(inside) {
87 return Some(entry.outside_start + (inside - entry.inside_start));
88 }
89 }
90 None
91}
92
93pub fn remap(old_outside: u32, old_map: &[MapEntry], new_map: &[MapEntry]) -> Option<u32> {
96 let inside = outside_to_inside(old_outside, old_map)?;
97 inside_to_outside(inside, new_map)
98}
99
100pub fn mappings_equal(a: &NsConfig, b: &NsConfig) -> bool {
102 a.uid_map == b.uid_map && a.gid_map == b.gid_map
103}
104
105#[cfg(test)]
106mod tests {
107 use super::*;
108
109 #[test]
110 fn test_map_entry_contains() {
111 let entry = MapEntry::new(1, 100000, 65536);
112
113 assert!(entry.contains_inside(1));
115 assert!(entry.contains_inside(100));
116 assert!(entry.contains_inside(65536));
117 assert!(!entry.contains_inside(0));
118 assert!(!entry.contains_inside(65537));
119
120 assert!(entry.contains_outside(100000));
122 assert!(entry.contains_outside(100100));
123 assert!(entry.contains_outside(165535));
124 assert!(!entry.contains_outside(99999));
125 assert!(!entry.contains_outside(165536));
126 }
127
128 #[test]
129 fn test_outside_to_inside_simple() {
130 let map = vec![MapEntry::new(0, 1000, 1), MapEntry::new(1, 100000, 65536)];
134
135 assert_eq!(outside_to_inside(1000, &map), Some(0));
137
138 assert_eq!(outside_to_inside(100000, &map), Some(1));
140 assert_eq!(outside_to_inside(100189, &map), Some(190));
141
142 assert_eq!(outside_to_inside(999, &map), None);
144 assert_eq!(outside_to_inside(200000, &map), None);
145 }
146
147 #[test]
148 fn test_inside_to_outside_simple() {
149 let map = vec![MapEntry::new(0, 1000, 1), MapEntry::new(1, 100000, 65536)];
150
151 assert_eq!(inside_to_outside(0, &map), Some(1000));
152 assert_eq!(inside_to_outside(1, &map), Some(100000));
153 assert_eq!(inside_to_outside(190, &map), Some(100189));
154
155 assert_eq!(inside_to_outside(70000, &map), None);
157 }
158
159 #[test]
160 fn test_remap_between_namespaces() {
161 let map_a = vec![MapEntry::new(0, 1000, 1), MapEntry::new(1, 100000, 65536)];
163
164 let map_b = vec![MapEntry::new(0, 2000, 1), MapEntry::new(1, 200000, 65536)];
166
167 assert_eq!(remap(100189, &map_a, &map_b), Some(200189));
170
171 assert_eq!(remap(1000, &map_a, &map_b), Some(2000));
173 }
174
175 #[test]
176 fn test_identity_mapping() {
177 let id = NsConfig::identity();
178 assert!(id.is_identity());
179
180 assert_eq!(outside_to_inside(0, &id.uid_map), Some(0));
182 assert_eq!(outside_to_inside(1000, &id.uid_map), Some(1000));
183 assert_eq!(
184 outside_to_inside(u32::MAX - 1, &id.uid_map),
185 Some(u32::MAX - 1)
186 );
187
188 assert_eq!(inside_to_outside(0, &id.uid_map), Some(0));
189 assert_eq!(inside_to_outside(65535, &id.uid_map), Some(65535));
190 }
191
192 #[test]
193 fn test_non_identity() {
194 let ns = NsConfig {
195 uid_map: vec![MapEntry::new(0, 1000, 1)],
196 gid_map: vec![MapEntry::new(0, 1000, 1)],
197 };
198 assert!(!ns.is_identity());
199 }
200
201 #[test]
202 fn test_mappings_equal() {
203 let a = NsConfig {
204 uid_map: vec![MapEntry::new(0, 1000, 1)],
205 gid_map: vec![MapEntry::new(0, 1000, 1)],
206 };
207 let b = NsConfig {
208 uid_map: vec![MapEntry::new(0, 1000, 1)],
209 gid_map: vec![MapEntry::new(0, 1000, 1)],
210 };
211 let c = NsConfig {
212 uid_map: vec![MapEntry::new(0, 2000, 1)],
213 gid_map: vec![MapEntry::new(0, 1000, 1)],
214 };
215
216 assert!(mappings_equal(&a, &b));
217 assert!(!mappings_equal(&a, &c));
218 }
219
220 #[test]
221 fn test_overflow_safety() {
222 let entry = MapEntry::new(u32::MAX - 10, 0, 20);
224 assert!(entry.contains_inside(u32::MAX - 10));
225 assert!(entry.contains_inside(u32::MAX - 1));
228 assert!(!entry.contains_inside(u32::MAX));
230
231 let entry2 = MapEntry::new(0, u32::MAX - 10, 20);
232 assert!(entry2.contains_outside(u32::MAX - 1));
233 assert!(!entry2.contains_outside(u32::MAX));
234 }
235}