cgroups/
devices.rs

1//! This module contains the implementation of the `devices` cgroup subsystem.
2//!
3//! See the Kernel's documentation for more information about this subsystem, found at:
4//!  [Documentation/cgroup-v1/devices.txt](https://www.kernel.org/doc/Documentation/cgroup-v1/devices.txt)
5use std::io::{Read, Write};
6use std::path::PathBuf;
7
8use error::*;
9use error::ErrorKind::*;
10
11use {
12    ControllIdentifier, ControllerInternal, Controllers, DeviceResource, DeviceResources,
13    Resources, Subsystem,
14};
15
16/// A controller that allows controlling the `devices` subsystem of a Cgroup.
17///
18/// In essence, using the devices controller, it is possible to allow or disallow sets of devices to
19/// be used by the control group's tasks.
20#[derive(Debug, Clone)]
21pub struct DevicesController {
22    base: PathBuf,
23    path: PathBuf,
24}
25
26/// An enum holding the different types of devices that can be manipulated using this controller.
27#[derive(Debug, Copy, Clone, PartialEq, Eq)]
28pub enum DeviceType {
29    /// The rule applies to all devices.
30    All,
31    /// The rule only applies to character devices.
32    Char,
33    /// The rule only applies to block devices.
34    Block,
35}
36
37impl Default for DeviceType {
38    fn default() -> Self {
39        DeviceType::All
40    }
41}
42
43impl DeviceType {
44    /// Convert a DeviceType into the character that the kernel recognizes.
45    pub fn to_char(&self) -> char {
46        match self {
47            DeviceType::All => 'a',
48            DeviceType::Char => 'c',
49            DeviceType::Block => 'b',
50        }
51    }
52
53    /// Convert the kenrel's representation into the DeviceType type.
54    pub fn from_char(c: Option<char>) -> Option<DeviceType> {
55        match c {
56            Some('a') => Some(DeviceType::All),
57            Some('c') => Some(DeviceType::Char),
58            Some('b') => Some(DeviceType::Block),
59            _ => None,
60        }
61    }
62}
63
64/// An enum with the permissions that can be allowed/denied to the control group.
65#[derive(Debug, Copy, Clone, PartialEq, Eq)]
66pub enum DevicePermissions {
67    /// Permission to read from the device.
68    Read,
69    /// Permission to write to the device.
70    Write,
71    /// Permission to execute the `mknod(2)` system call with the device's major and minor numbers.
72    /// That is, the permission to create a special file that refers to the device node.
73    MkNod,
74}
75
76impl DevicePermissions {
77    /// Convert a DevicePermissions into the character that the kernel recognizes.
78    pub fn to_char(&self) -> char {
79        match self {
80            DevicePermissions::Read => 'r',
81            DevicePermissions::Write => 'w',
82            DevicePermissions::MkNod => 'm',
83        }
84    }
85
86    /// Convert a char to a DevicePermission if there is such a mapping.
87    pub fn from_char(c: char) -> Option<DevicePermissions> {
88        match c {
89            'r' => Some(DevicePermissions::Read),
90            'w' => Some(DevicePermissions::Write),
91            'm' => Some(DevicePermissions::MkNod),
92            _ => None,
93        }
94    }
95
96    /// Checks whether the string is a valid descriptor of DevicePermissions.
97    pub fn is_valid(s: &str) -> bool {
98        if s == "" {
99            return false;
100        }
101        for i in s.chars() {
102            if i != 'r' && i != 'w' && i != 'm' {
103                return false;
104            }
105        }
106        return true;
107    }
108
109    /// Returns a Vec will all the permissions that a device can have.
110    pub fn all() -> Vec<DevicePermissions> {
111        vec![
112            DevicePermissions::Read,
113            DevicePermissions::Write,
114            DevicePermissions::MkNod,
115        ]
116    }
117
118    /// Convert a string into DevicePermissions.
119    pub fn from_str(s: &str) -> Result<Vec<DevicePermissions>> {
120        let mut v = Vec::new();
121        if s == "" {
122            return Ok(v);
123        }
124        for e in s.chars() {
125            let perm = DevicePermissions::from_char(e)
126                .ok_or_else(|| Error::new(ParseError))?;
127            v.push(perm);
128        }
129
130        Ok(v)
131    }
132}
133
134impl ControllerInternal for DevicesController {
135    fn control_type(&self) -> Controllers {
136        Controllers::Devices
137    }
138    fn get_path(&self) -> &PathBuf {
139        &self.path
140    }
141    fn get_path_mut(&mut self) -> &mut PathBuf {
142        &mut self.path
143    }
144    fn get_base(&self) -> &PathBuf {
145        &self.base
146    }
147
148    fn apply(&self, res: &Resources) -> Result<()> {
149        // get the resources that apply to this controller
150        let res: &DeviceResources = &res.devices;
151
152        if res.update_values {
153            for i in &res.devices {
154                if i.allow {
155                    let _ = self.allow_device(i.devtype, i.major, i.minor, &i.access);
156                } else {
157                    let _ = self.deny_device(i.devtype, i.major, i.minor, &i.access);
158                }
159            }
160        }
161
162        Ok(())
163    }
164}
165
166impl ControllIdentifier for DevicesController {
167    fn controller_type() -> Controllers {
168        Controllers::Devices
169    }
170}
171
172impl<'a> From<&'a Subsystem> for &'a DevicesController {
173    fn from(sub: &'a Subsystem) -> &'a DevicesController {
174        unsafe {
175            match sub {
176                Subsystem::Devices(c) => c,
177                _ => {
178                    assert_eq!(1, 0);
179                    ::std::mem::uninitialized()
180                }
181            }
182        }
183    }
184}
185
186impl DevicesController {
187    /// Constructs a new `DevicesController` with `oroot` serving as the root of the control group.
188    pub fn new(oroot: PathBuf) -> Self {
189        let mut root = oroot;
190        root.push(Self::controller_type().to_string());
191        Self {
192            base: root.clone(),
193            path: root,
194        }
195    }
196
197    /// Allow a (possibly, set of) device(s) to be used by the tasks in the control group.
198    ///
199    /// When `-1` is passed as `major` or `minor`, the kernel interprets that value as "any",
200    /// meaning that it will match any device.
201    pub fn allow_device(
202        &self,
203        devtype: DeviceType,
204        major: i64,
205        minor: i64,
206        perm: &Vec<DevicePermissions>,
207    ) -> Result<()> {
208        let perms = perm
209            .iter()
210            .map(DevicePermissions::to_char)
211            .collect::<String>();
212        let minor = if minor == -1 {
213            "*".to_string()
214        } else {
215            format!("{}", minor)
216        };
217        let major = if major == -1 {
218            "*".to_string()
219        } else {
220            format!("{}", major)
221        };
222        let final_str = format!("{} {}:{} {}", devtype.to_char(), major, minor, perms);
223        self.open_path("devices.allow", true).and_then(|mut file| {
224            file.write_all(final_str.as_ref())
225                .map_err(|e| Error::with_cause(WriteFailed, e))
226        })
227    }
228
229    /// Deny the control group's tasks access to the devices covered by `dev`.
230    ///
231    /// When `-1` is passed as `major` or `minor`, the kernel interprets that value as "any",
232    /// meaning that it will match any device.
233    pub fn deny_device(
234        &self,
235        devtype: DeviceType,
236        major: i64,
237        minor: i64,
238        perm: &Vec<DevicePermissions>,
239    ) -> Result<()> {
240        let perms = perm
241            .iter()
242            .map(DevicePermissions::to_char)
243            .collect::<String>();
244        let minor = if minor == -1 {
245            "*".to_string()
246        } else {
247            format!("{}", minor)
248        };
249        let major = if major == -1 {
250            "*".to_string()
251        } else {
252            format!("{}", major)
253        };
254        let final_str = format!("{} {}:{} {}", devtype.to_char(), major, minor, perms);
255        self.open_path("devices.deny", true).and_then(|mut file| {
256            file.write_all(final_str.as_ref())
257                .map_err(|e| Error::with_cause(WriteFailed, e))
258        })
259    }
260
261    /// Get the current list of allowed devices.
262    pub fn allowed_devices(&self) -> Result<Vec<DeviceResource>> {
263        self.open_path("devices.list", false).and_then(|mut file| {
264            let mut s = String::new();
265            let res = file.read_to_string(&mut s);
266            match res {
267                Ok(_) => {
268                    s.lines().fold(Ok(Vec::new()), |acc, line| {
269                        let ls = line.to_string().split(|c| c == ' ' || c == ':').map(|x| x.to_string()).collect::<Vec<String>>();
270                        if acc.is_err() || ls.len() != 4 {
271                            error!("allowed_devices: acc: {:?}, ls: {:?}", acc, ls);
272                            Err(Error::new(ParseError))
273                        } else {
274                            let devtype = DeviceType::from_char(ls[0].chars().nth(0));
275                            let mut major = ls[1].parse::<i64>();
276                            let mut minor = ls[2].parse::<i64>();
277                            if major.is_err() && ls[1] == "*".to_string() {
278                                major = Ok(-1);
279                            }
280                            if minor.is_err() && ls[2] == "*".to_string() {
281                                minor = Ok(-1);
282                            }
283                            if devtype.is_none() || major.is_err() || minor.is_err() || !DevicePermissions::is_valid(&ls[3]) {
284                                error!("allowed_devices: acc: {:?}, ls: {:?}, devtype: {:?}, major {:?} minor {:?} ls3 {:?}",
285                                         acc, ls, devtype, major, minor, &ls[3]);
286                                Err(Error::new(ParseError))
287                            } else {
288                                let access = DevicePermissions::from_str(&ls[3])?;
289                                let mut acc = acc.unwrap();
290                                acc.push(DeviceResource {
291                                    allow: true,
292                                    devtype: devtype.unwrap(),
293                                    major: major.unwrap(),
294                                    minor: minor.unwrap(),
295                                    access: access,
296                                });
297                                Ok(acc)
298                            }
299                        }
300                    })
301                },
302                Err(e) => Err(Error::with_cause(ReadFailed, e)),
303            }
304        })
305    }
306}