1use std::io::{Read, Write};
11use std::path::PathBuf;
12
13use log::*;
14
15use crate::fs::error::ErrorKind::*;
16use crate::fs::error::*;
17
18use crate::fs::{
19 ControllIdentifier, ControllerInternal, Controllers, DeviceResource, DeviceResources,
20 Resources, Subsystem,
21};
22
23#[derive(Debug, Clone)]
28pub struct DevicesController {
29 base: PathBuf,
30 path: PathBuf,
31}
32
33#[derive(Debug, Copy, Clone, PartialEq, Eq)]
35#[cfg_attr(
36 feature = "serde",
37 derive(serde::Serialize, serde::Deserialize),
38 serde(rename_all = "snake_case")
39)]
40pub enum DeviceType {
41 All,
43 Char,
45 Block,
47}
48
49#[allow(clippy::derivable_impls)]
50impl Default for DeviceType {
51 fn default() -> Self {
52 DeviceType::All
53 }
54}
55
56impl DeviceType {
57 #[allow(clippy::should_implement_trait, clippy::wrong_self_convention)]
59 pub fn to_char(&self) -> char {
60 match self {
61 DeviceType::All => 'a',
62 DeviceType::Char => 'c',
63 DeviceType::Block => 'b',
64 }
65 }
66
67 pub fn from_char(c: Option<char>) -> Option<DeviceType> {
69 match c {
70 Some('a') => Some(DeviceType::All),
71 Some('c') => Some(DeviceType::Char),
72 Some('b') => Some(DeviceType::Block),
73 _ => None,
74 }
75 }
76}
77
78#[derive(Debug, Copy, Clone, PartialEq, Eq)]
80#[cfg_attr(
81 feature = "serde",
82 derive(serde::Serialize, serde::Deserialize),
83 serde(rename_all = "snake_case")
84)]
85pub enum DevicePermissions {
86 Read,
88 Write,
90 MkNod,
93}
94
95impl DevicePermissions {
96 #[allow(clippy::should_implement_trait, clippy::wrong_self_convention)]
98 pub fn to_char(&self) -> char {
99 match self {
100 DevicePermissions::Read => 'r',
101 DevicePermissions::Write => 'w',
102 DevicePermissions::MkNod => 'm',
103 }
104 }
105
106 pub fn from_char(c: char) -> Option<DevicePermissions> {
108 match c {
109 'r' => Some(DevicePermissions::Read),
110 'w' => Some(DevicePermissions::Write),
111 'm' => Some(DevicePermissions::MkNod),
112 _ => None,
113 }
114 }
115
116 pub fn is_valid(s: &str) -> bool {
118 if s.is_empty() {
119 return false;
120 }
121 for i in s.chars() {
122 if i != 'r' && i != 'w' && i != 'm' {
123 return false;
124 }
125 }
126 true
127 }
128
129 pub fn all() -> Vec<DevicePermissions> {
131 vec![
132 DevicePermissions::Read,
133 DevicePermissions::Write,
134 DevicePermissions::MkNod,
135 ]
136 }
137
138 #[allow(clippy::should_implement_trait)]
140 pub fn from_str(s: &str) -> Result<Vec<DevicePermissions>> {
141 let mut v = Vec::new();
142 if s.is_empty() {
143 return Ok(v);
144 }
145 for e in s.chars() {
146 let perm = DevicePermissions::from_char(e).ok_or_else(|| Error::new(ParseError))?;
147 v.push(perm);
148 }
149
150 Ok(v)
151 }
152}
153
154impl ControllerInternal for DevicesController {
155 fn control_type(&self) -> Controllers {
156 Controllers::Devices
157 }
158 fn get_path(&self) -> &PathBuf {
159 &self.path
160 }
161 fn get_path_mut(&mut self) -> &mut PathBuf {
162 &mut self.path
163 }
164 fn get_base(&self) -> &PathBuf {
165 &self.base
166 }
167
168 fn apply(&self, res: &Resources) -> Result<()> {
169 let res: &DeviceResources = &res.devices;
171
172 for i in &res.devices {
173 if i.allow {
174 self.allow_device(i.devtype, i.major, i.minor, &i.access)?;
175 } else {
176 self.deny_device(i.devtype, i.major, i.minor, &i.access)?;
177 }
178 }
179
180 Ok(())
181 }
182}
183
184impl ControllIdentifier for DevicesController {
185 fn controller_type() -> Controllers {
186 Controllers::Devices
187 }
188}
189
190impl<'a> From<&'a Subsystem> for &'a DevicesController {
191 fn from(sub: &'a Subsystem) -> &'a DevicesController {
192 unsafe {
193 match sub {
194 Subsystem::Devices(c) => c,
195 _ => {
196 assert_eq!(1, 0);
197 let v = std::mem::MaybeUninit::uninit();
198 v.assume_init()
199 }
200 }
201 }
202 }
203}
204
205impl DevicesController {
206 pub fn new(point: PathBuf, root: PathBuf) -> Self {
208 Self {
209 base: root,
210 path: point,
211 }
212 }
213
214 pub fn allow_device(
219 &self,
220 devtype: DeviceType,
221 major: i64,
222 minor: i64,
223 perm: &[DevicePermissions],
224 ) -> Result<()> {
225 let perms = perm
226 .iter()
227 .map(DevicePermissions::to_char)
228 .collect::<String>();
229 let minor = if minor == -1 {
230 "*".to_string()
231 } else {
232 format!("{}", minor)
233 };
234 let major = if major == -1 {
235 "*".to_string()
236 } else {
237 format!("{}", major)
238 };
239 let final_str = format!("{} {}:{} {}", devtype.to_char(), major, minor, perms);
240 self.open_path("devices.allow", true).and_then(|mut file| {
241 file.write_all(final_str.as_ref()).map_err(|e| {
242 Error::with_cause(
243 WriteFailed(
244 self.get_path().join("devices.allow").display().to_string(),
245 final_str,
246 ),
247 e,
248 )
249 })
250 })
251 }
252
253 pub fn deny_device(
258 &self,
259 devtype: DeviceType,
260 major: i64,
261 minor: i64,
262 perm: &[DevicePermissions],
263 ) -> Result<()> {
264 let perms = perm
265 .iter()
266 .map(DevicePermissions::to_char)
267 .collect::<String>();
268 let minor = if minor == -1 {
269 "*".to_string()
270 } else {
271 format!("{}", minor)
272 };
273 let major = if major == -1 {
274 "*".to_string()
275 } else {
276 format!("{}", major)
277 };
278 let final_str = format!("{} {}:{} {}", devtype.to_char(), major, minor, perms);
279 self.open_path("devices.deny", true).and_then(|mut file| {
280 file.write_all(final_str.as_ref()).map_err(|e| {
281 Error::with_cause(
282 WriteFailed(
283 self.get_path().join("devices.deny").display().to_string(),
284 final_str,
285 ),
286 e,
287 )
288 })
289 })
290 }
291
292 pub fn allowed_devices(&self) -> Result<Vec<DeviceResource>> {
294 self.open_path("devices.list", false).and_then(|mut file| {
295 let mut s = String::new();
296 let res = file.read_to_string(&mut s);
297 match res {
298 Ok(_) => s
299 .lines()
300 .map(|line| parse_device_line(line, true))
301 .collect(),
302 Err(e) => Err(Error::with_cause(ReadFailed("devices.list".to_string()), e)),
303 }
304 })
305 }
306}
307
308fn parse_device_number(s: &str) -> Result<i64> {
309 if s == "*" {
310 Ok(-1)
311 } else {
312 s.parse::<i64>().map_err(|_| Error::new(ParseError))
313 }
314}
315
316fn parse_device_line(line: &str, allow: bool) -> Result<DeviceResource> {
317 let parts: Vec<&str> = line.split([' ', ':']).collect();
318 if parts.len() != 4 {
319 error!("allowed_devices: invalid line format: {:?}", line);
320 return Err(Error::new(ParseError));
321 }
322
323 let devtype = DeviceType::from_char(parts[0].chars().next()).ok_or_else(|| {
324 error!("allowed_devices: invalid device type: {:?}", parts[0]);
325 Error::new(ParseError)
326 })?;
327 let major = parse_device_number(parts[1]).inspect_err(|_| {
328 error!("allowed_devices: invalid major number: {:?}", parts[1]);
329 })?;
330 let minor = parse_device_number(parts[2]).inspect_err(|_| {
331 error!("allowed_devices: invalid minor number: {:?}", parts[2]);
332 })?;
333 let access = DevicePermissions::from_str(parts[3])?;
334
335 Ok(DeviceResource {
336 allow,
337 devtype,
338 major,
339 minor,
340 access,
341 })
342}