use std::cell::RefCell;
use std::collections::BTreeSet;
use std::path::Path;
use std::os::unix::io;
use nix::{self, Errno};
use nix::fcntl::{open, OFlag};
use nix::sys::stat::{Mode, stat};
use qualia::Illusion;
use ipc::Logind;
pub struct RestrictedOpener {
logind: Logind,
taken_devices: RefCell<BTreeSet<u64>>,
}
impl RestrictedOpener {
pub fn new() -> Self {
RestrictedOpener {
logind: Logind::new(),
taken_devices: RefCell::new(BTreeSet::new()),
}
}
pub fn open(&self, path: &Path, oflag: OFlag, mode: Mode) -> Result<io::RawFd, Illusion> {
match open(path, oflag, mode) {
Ok(fd) => Ok(fd),
Err(nix::Error::Sys(errno)) => {
if (errno == Errno::EPERM) || (errno == Errno::EACCES) {
self.take_device(path)
} else {
Err(Illusion::InvalidArgument(errno.desc().to_owned()))
}
}
Err(nix::Error::InvalidPath) => {
Err(Illusion::InvalidArgument(format!("Path '{:?}' does not exist!", path)))
}
}
}
pub fn initialize_ipc(&mut self) -> Result<(), Illusion> {
self.logind.initialize()
}
}
impl RestrictedOpener {
fn take_device(&self, path: &Path) -> Result<io::RawFd, Illusion> {
match stat(path) {
Ok(st) => {
let rdev = st.st_rdev as u64;
let contains = self.taken_devices.borrow().contains(&rdev);
if contains {
if self.logind.release_device(rdev).is_ok() {
self.taken_devices.borrow_mut().remove(&rdev);
}
}
let result = self.logind.take_device(rdev);
if result.is_ok() {
self.taken_devices.borrow_mut().insert(rdev);
}
result
}
Err(err) => {
Err(Illusion::General(format!("Could not stat file '{:?}': {:?}", path, err)))
}
}
}
}