use libc::{uid_t, gid_t};
use std::cell::{Cell, RefCell};
use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::collections::HashMap;
use std::ffi::OsStr;
use std::sync::Arc;
use base::{User, Group, all_users};
use traits::{Users, Groups};
pub struct UsersCache {
users: BiMap<uid_t, User>,
groups: BiMap<gid_t, Group>,
uid: Cell<Option<uid_t>>,
gid: Cell<Option<gid_t>>,
euid: Cell<Option<uid_t>>,
egid: Cell<Option<gid_t>>,
}
struct BiMap<K, V> {
forward: RefCell< HashMap<K, Option<Arc<V>>> >,
backward: RefCell< HashMap<Arc<OsStr>, Option<K>> >,
}
impl Default for UsersCache {
fn default() -> Self {
Self {
users: BiMap {
forward: RefCell::new(HashMap::new()),
backward: RefCell::new(HashMap::new()),
},
groups: BiMap {
forward: RefCell::new(HashMap::new()),
backward: RefCell::new(HashMap::new()),
},
uid: Cell::new(None),
gid: Cell::new(None),
euid: Cell::new(None),
egid: Cell::new(None),
}
}
}
impl UsersCache {
pub fn new() -> Self {
Self::default()
}
pub unsafe fn with_all_users() -> Self {
let cache = Self::new();
for user in all_users() {
let uid = user.uid();
let user_arc = Arc::new(user);
cache.users.forward.borrow_mut().insert(uid, Some(Arc::clone(&user_arc)));
cache.users.backward.borrow_mut().insert(Arc::clone(&user_arc.name_arc), Some(uid));
}
cache
}
}
impl Users for UsersCache {
fn get_user_by_uid(&self, uid: uid_t) -> Option<Arc<User>> {
let mut users_forward = self.users.forward.borrow_mut();
let entry = match users_forward.entry(uid) {
Vacant(e) => e,
Occupied(e) => return e.get().as_ref().map(Arc::clone),
};
if let Some(user) = super::get_user_by_uid(uid) {
let newsername = Arc::clone(&user.name_arc);
let mut users_backward = self.users.backward.borrow_mut();
users_backward.insert(newsername, Some(uid));
let user_arc = Arc::new(user);
entry.insert(Some(Arc::clone(&user_arc)));
Some(user_arc)
}
else {
entry.insert(None);
None
}
}
fn get_user_by_name<S: AsRef<OsStr> + ?Sized>(&self, username: &S) -> Option<Arc<User>> {
let mut users_backward = self.users.backward.borrow_mut();
let entry = match users_backward.entry(Arc::from(username.as_ref())) {
Vacant(e) => e,
Occupied(e) => {
return (*e.get()).and_then(|uid| {
let users_forward = self.users.forward.borrow_mut();
users_forward[&uid].as_ref().map(Arc::clone)
})
}
};
if let Some(user) = super::get_user_by_name(username) {
let uid = user.uid();
let user_arc = Arc::new(user);
let mut users_forward = self.users.forward.borrow_mut();
users_forward.insert(uid, Some(Arc::clone(&user_arc)));
entry.insert(Some(uid));
Some(user_arc)
}
else {
entry.insert(None);
None
}
}
fn get_current_uid(&self) -> uid_t {
self.uid.get().unwrap_or_else(|| {
let uid = super::get_current_uid();
self.uid.set(Some(uid));
uid
})
}
fn get_current_username(&self) -> Option<Arc<OsStr>> {
let uid = self.get_current_uid();
self.get_user_by_uid(uid).map(|u| Arc::clone(&u.name_arc))
}
fn get_effective_uid(&self) -> uid_t {
self.euid.get().unwrap_or_else(|| {
let uid = super::get_effective_uid();
self.euid.set(Some(uid));
uid
})
}
fn get_effective_username(&self) -> Option<Arc<OsStr>> {
let uid = self.get_effective_uid();
self.get_user_by_uid(uid).map(|u| Arc::clone(&u.name_arc))
}
}
impl Groups for UsersCache {
fn get_group_by_gid(&self, gid: gid_t) -> Option<Arc<Group>> {
let mut groups_forward = self.groups.forward.borrow_mut();
let entry = match groups_forward.entry(gid) {
Vacant(e) => e,
Occupied(e) => return e.get().as_ref().map(Arc::clone),
};
if let Some(group) = super::get_group_by_gid(gid) {
let new_group_name = Arc::clone(&group.name_arc);
let mut groups_backward = self.groups.backward.borrow_mut();
groups_backward.insert(new_group_name, Some(gid));
let group_arc = Arc::new(group);
entry.insert(Some(Arc::clone(&group_arc)));
Some(group_arc)
}
else {
entry.insert(None);
None
}
}
fn get_group_by_name<S: AsRef<OsStr> + ?Sized>(&self, group_name: &S) -> Option<Arc<Group>> {
let mut groups_backward = self.groups.backward.borrow_mut();
let entry = match groups_backward.entry(Arc::from(group_name.as_ref())) {
Vacant(e) => e,
Occupied(e) => {
return (*e.get()).and_then(|gid| {
let groups_forward = self.groups.forward.borrow_mut();
groups_forward[&gid].as_ref().cloned()
});
}
};
if let Some(group) = super::get_group_by_name(group_name) {
let group_arc = Arc::new(group.clone());
let gid = group.gid();
let mut groups_forward = self.groups.forward.borrow_mut();
groups_forward.insert(gid, Some(Arc::clone(&group_arc)));
entry.insert(Some(gid));
Some(group_arc)
}
else {
entry.insert(None);
None
}
}
fn get_current_gid(&self) -> gid_t {
self.gid.get().unwrap_or_else(|| {
let gid = super::get_current_gid();
self.gid.set(Some(gid));
gid
})
}
fn get_current_groupname(&self) -> Option<Arc<OsStr>> {
let gid = self.get_current_gid();
self.get_group_by_gid(gid).map(|g| Arc::clone(&g.name_arc))
}
fn get_effective_gid(&self) -> gid_t {
self.egid.get().unwrap_or_else(|| {
let gid = super::get_effective_gid();
self.egid.set(Some(gid));
gid
})
}
fn get_effective_groupname(&self) -> Option<Arc<OsStr>> {
let gid = self.get_effective_gid();
self.get_group_by_gid(gid).map(|g| Arc::clone(&g.name_arc))
}
}