libfuse_fs/util/
mapping.rs1use itertools::Itertools;
2use std::{fs, str::FromStr};
3
4#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
8pub struct IdMapEntry {
9 pub host: u32,
10 pub to: u32,
11 pub len: u32,
12}
13
14#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
15pub struct IdMappings {
16 pub uid_map: Vec<IdMapEntry>,
17 pub gid_map: Vec<IdMapEntry>,
18
19 overflow_uid: u32,
22 overflow_gid: u32,
25}
26
27impl IdMappings {
28 pub fn new(uid_map: Vec<IdMapEntry>, gid_map: Vec<IdMapEntry>) -> Self {
29 let overflow_uid = fs::read_to_string("/proc/sys/kernel/overflowuid")
30 .ok()
31 .and_then(|s| s.trim().parse().ok())
32 .unwrap_or_default();
33 let overflow_gid = fs::read_to_string("/proc/sys/kernel/overflowgid")
34 .ok()
35 .and_then(|s| s.trim().parse().ok())
36 .unwrap_or_default();
37 IdMappings {
38 uid_map,
39 gid_map,
40 overflow_uid,
41 overflow_gid,
42 }
43 }
44
45 fn read_mappings(mapping: &str) -> Result<Vec<IdMapEntry>, String> {
57 let parts: Vec<&str> = mapping.split(':').collect();
58 if !parts.len().is_multiple_of(3) {
59 return Err(format!(
60 "Invalid mapping specified: '{mapping}'. The number of fields must be a multiple of 3.",
61 ));
62 }
63 parts
64 .into_iter()
65 .tuples()
66 .map(|(host, to, len)| {
67 let host: u32 = host
68 .parse()
69 .map_err(|e| format!("Invalid host id in mapping: {e}"))?;
70 let to: u32 = to
71 .parse()
72 .map_err(|e| format!("Invalid to id in mapping: {e}"))?;
73 let len: u32 = len
74 .parse()
75 .map_err(|e| format!("Invalid length in mapping: {e}"))?;
76 if len == 0 {
77 return Err("Length in mapping cannot be zero".to_string());
78 }
79 Ok(IdMapEntry { host, to, len })
80 })
81 .collect::<Result<Vec<_>, _>>()
82 }
83
84 pub fn find_mapping(&self, id: u32, direct: bool, uid: bool) -> u32 {
91 let map = if uid { &self.uid_map } else { &self.gid_map };
92 if map.is_empty() {
93 return id;
94 }
95 for entry in map {
96 if direct {
97 if id >= entry.host && id < entry.host + entry.len {
99 return entry.to + (id - entry.host);
100 }
101 } else {
102 if id >= entry.to && id < entry.to + entry.len {
104 return entry.host + (id - entry.to);
105 }
106 }
107 }
108
109 if uid {
110 self.overflow_uid
111 } else {
112 self.overflow_gid
113 }
114 }
115
116 pub fn get_uid(&self, container_id: u32) -> u32 {
118 self.find_mapping(container_id, false, true)
119 }
120
121 pub fn get_gid(&self, container_id: u32) -> u32 {
123 self.find_mapping(container_id, false, false)
124 }
125}
126
127impl FromStr for IdMappings {
128 type Err = String;
129
130 fn from_str(s: &str) -> Result<Self, Self::Err> {
131 let parts: Vec<&str> = s.split(',').collect();
132 if parts.len() != 2 {
133 return Err("Invalid mapping format. Expected 'uidmapping=,gidmapping='".to_string());
134 }
135 let uid_mappings_str = parts[0]
136 .strip_prefix("uidmapping=")
137 .ok_or_else(|| "Invalid uidmapping format: missing 'uidmapping=' prefix".to_string())?;
138
139 let gid_mappings_str = parts[1]
140 .strip_prefix("gidmapping=")
141 .ok_or_else(|| "Invalid gidmapping format: missing 'gidmapping=' prefix".to_string())?;
142
143 let uid_map = IdMappings::read_mappings(uid_mappings_str)?;
144 let gid_map = IdMappings::read_mappings(gid_mappings_str)?;
145 Ok(IdMappings::new(uid_map, gid_map))
146 }
147}
148
149#[cfg(test)]
150mod tests {
151 use nix::unistd::{getgid, getuid};
152
153 use crate::util::mapping::IdMappings;
154
155 #[test]
156 fn test_parse_mappings() {
157 let cur_uid = getuid().as_raw();
158 let cur_gid = getgid().as_raw();
159 let mapping = format!("uidmapping={cur_uid}:1000:1,gidmapping={cur_gid}:1000:1");
160 let id_mappings: IdMappings = mapping.parse().unwrap();
161 assert_eq!(id_mappings.uid_map.len(), 1);
162 assert_eq!(id_mappings.gid_map.len(), 1);
163 assert_eq!(id_mappings.uid_map[0].host, cur_uid);
164 assert_eq!(id_mappings.uid_map[0].to, 1000);
165 assert_eq!(id_mappings.uid_map[0].len, 1);
166 assert_eq!(id_mappings.gid_map[0].host, cur_gid);
167 assert_eq!(id_mappings.gid_map[0].to, 1000);
168 assert_eq!(id_mappings.gid_map[0].len, 1);
169
170 let mapping =
171 format!("uidmapping={cur_uid}:1000:1:0:65534:1,gidmapping={cur_gid}:1000:1:0:65534:1");
172 let id_mappings: IdMappings = mapping.parse().unwrap();
173 assert_eq!(id_mappings.uid_map.len(), 2);
174 assert_eq!(id_mappings.gid_map.len(), 2);
175 assert_eq!(id_mappings.uid_map[0].host, cur_uid);
176 assert_eq!(id_mappings.uid_map[0].to, 1000);
177 assert_eq!(id_mappings.uid_map[0].len, 1);
178 assert_eq!(id_mappings.uid_map[1].host, 0);
179 assert_eq!(id_mappings.uid_map[1].to, 65534);
180 assert_eq!(id_mappings.uid_map[1].len, 1);
181 assert_eq!(id_mappings.gid_map[0].host, cur_gid);
182 assert_eq!(id_mappings.gid_map[0].to, 1000);
183 assert_eq!(id_mappings.gid_map[0].len, 1);
184 assert_eq!(id_mappings.gid_map[1].host, 0);
185 assert_eq!(id_mappings.gid_map[1].to, 65534);
186 assert_eq!(id_mappings.gid_map[1].len, 1);
187 }
188}