use libc::{gid_t, uid_t};
use std::cell::{Cell, RefCell};
use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::collections::{HashMap, HashSet};
use std::ffi::OsStr;
use std::hash::Hash;
use std::ops::Deref;
use std::sync::Arc;
use base::{all_groups, all_users, Group, User};
use traits::{AllGroups, AllUsers, Groups, Users};
#[derive(Default)]
pub struct UsersCache {
users: RefCell<IdNameMap<uid_t, Arc<OsStr>, Arc<User>>>,
groups: RefCell<IdNameMap<gid_t, Arc<OsStr>, Arc<Group>>>,
uid: Cell<Option<uid_t>>,
gid: Cell<Option<gid_t>>,
euid: Cell<Option<uid_t>>,
egid: Cell<Option<gid_t>>,
}
struct IdNameMap<I, N, V>
where
I: Eq + Hash + Copy,
N: Eq + Hash,
{
forward: HashMap<I, Option<V>>,
backward: HashMap<N, Option<I>>,
}
impl<I, N, V> IdNameMap<I, N, V>
where
I: Eq + Hash + Copy,
N: Eq + Hash,
{
fn insert(&mut self, id: I, name: N, value: V) {
self.forward.insert(id, Some(value));
self.backward.insert(name, Some(id));
}
}
impl<I, N, V> Default for IdNameMap<I, N, V>
where
I: Eq + Hash + Copy,
N: Eq + Hash,
{
fn default() -> Self {
Self {
forward: HashMap::new(),
backward: HashMap::new(),
}
}
}
impl UsersCache {
pub fn new() -> Self {
Self::default()
}
#[deprecated(since = "0.11.4", note = "consider using `UsersSnapshot::new` instead")]
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.borrow_mut().insert(
uid,
Arc::clone(&user_arc.name_arc),
Arc::clone(&user_arc),
);
}
cache
}
}
impl Users for UsersCache {
fn get_user_by_uid(&self, uid: uid_t) -> Option<Arc<User>> {
let mut users = self.users.borrow_mut();
let entry = match users.forward.entry(uid) {
Vacant(e) => e,
Occupied(e) => return e.get().clone(),
};
if let Some(user) = super::get_user_by_uid(uid) {
let newsername = Arc::clone(&user.name_arc);
let user_arc = Arc::new(user);
entry.insert(Some(Arc::clone(&user_arc)));
users.backward.insert(newsername, Some(uid));
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 = self.users.borrow_mut();
let entry = match users.backward.entry(Arc::from(username.as_ref())) {
Vacant(e) => e,
Occupied(e) => return (*e.get()).and_then(|uid| users.forward[&uid].clone()),
};
if let Some(user) = super::get_user_by_name(username) {
let uid = user.uid();
let user_arc = Arc::new(user);
entry.insert(Some(uid));
users.forward.insert(uid, Some(Arc::clone(&user_arc)));
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 = self.groups.borrow_mut();
let entry = match groups.forward.entry(gid) {
Vacant(e) => e,
Occupied(e) => return e.get().clone(),
};
if let Some(group) = super::get_group_by_gid(gid) {
let new_group_name = Arc::clone(&group.name_arc);
let group_arc = Arc::new(group);
entry.insert(Some(Arc::clone(&group_arc)));
groups.backward.insert(new_group_name, Some(gid));
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 = self.groups.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| 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();
entry.insert(Some(gid));
groups.forward.insert(gid, Some(Arc::clone(&group_arc)));
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))
}
}
#[derive(Default)]
pub struct UsersSnapshot {
users: IdNameMap<uid_t, Arc<OsStr>, Arc<User>>,
groups: IdNameMap<uid_t, Arc<OsStr>, Arc<Group>>,
uid: uid_t,
gid: gid_t,
euid: uid_t,
egid: gid_t,
}
impl UsersSnapshot {
pub(crate) fn from<U, G>(
users: U,
groups: G,
current_uid: uid_t,
current_gid: gid_t,
effective_uid: uid_t,
effective_gid: gid_t,
) -> Self
where
U: Iterator<Item = User>,
G: Iterator<Item = Group>,
{
let mut user_map = IdNameMap::default();
for user in users {
user_map.insert(user.uid(), Arc::clone(&user.name_arc), Arc::from(user));
}
let mut group_map = IdNameMap::default();
for group in groups {
group_map.insert(group.gid(), Arc::clone(&group.name_arc), Arc::from(group));
}
Self {
users: user_map,
groups: group_map,
uid: current_uid,
gid: current_gid,
euid: effective_uid,
egid: effective_gid,
}
}
pub unsafe fn filtered<U, G>(user_filter: U, group_filter: G) -> Self
where
U: FnMut(&User) -> bool,
G: FnMut(&Group) -> bool,
{
Self::from(
all_users().filter(user_filter),
all_groups().filter(group_filter),
super::get_current_uid(),
super::get_current_gid(),
super::get_effective_uid(),
super::get_effective_gid(),
)
}
pub unsafe fn only_users<F>(user_filter: F) -> Self
where
F: FnMut(&User) -> bool,
{
let users = all_users().filter(user_filter).collect::<Vec<_>>();
let primary_groups = users
.iter()
.map(User::primary_group_id)
.collect::<HashSet<_>>();
let groups = all_groups()
.filter(|g| primary_groups.contains(&g.gid()))
.collect::<Vec<_>>();
Self::from(
users.into_iter(),
groups.into_iter(),
super::get_current_uid(),
super::get_current_gid(),
super::get_effective_uid(),
super::get_effective_gid(),
)
}
pub unsafe fn new() -> Self {
Self::filtered(|_| true, |_| true)
}
}
impl AllUsers for UsersSnapshot {
type UserIter<'a> = std::iter::FilterMap<
std::collections::hash_map::Values<'a, uid_t, Option<Arc<User>>>,
for<'b> fn(&'b Option<Arc<User>>) -> Option<&'b User>,
>;
fn get_all_users(&self) -> Self::UserIter<'_> {
fn get_user(x: &Option<Arc<User>>) -> Option<&User> {
x.as_ref().map(Arc::deref)
}
self.users.forward.values().filter_map(get_user)
}
}
impl Users for UsersSnapshot {
fn get_user_by_uid(&self, uid: uid_t) -> Option<Arc<User>> {
self.users.forward.get(&uid)?.as_ref().cloned()
}
fn get_user_by_name<S: AsRef<OsStr> + ?Sized>(&self, username: &S) -> Option<Arc<User>> {
let name_arc = Arc::from(username.as_ref());
let uid = self.users.backward.get(&name_arc)?.as_ref()?;
self.get_user_by_uid(*uid)
}
fn get_current_uid(&self) -> uid_t {
self.uid
}
fn get_current_username(&self) -> Option<Arc<OsStr>> {
self.get_user_by_uid(self.uid)
.map(|u| Arc::clone(&u.name_arc))
}
fn get_effective_uid(&self) -> uid_t {
self.euid
}
fn get_effective_username(&self) -> Option<Arc<OsStr>> {
self.get_user_by_uid(self.euid)
.map(|u| Arc::clone(&u.name_arc))
}
}
impl AllGroups for UsersSnapshot {
type GroupIter<'a> = std::iter::FilterMap<
std::collections::hash_map::Values<'a, gid_t, Option<Arc<Group>>>,
for<'b> fn(&'b Option<Arc<Group>>) -> Option<&'b Group>,
>;
fn get_all_groups(&self) -> Self::GroupIter<'_> {
fn get_group(x: &Option<Arc<Group>>) -> Option<&Group> {
x.as_ref().map(Arc::deref)
}
self.groups.forward.values().filter_map(get_group)
}
}
impl Groups for UsersSnapshot {
fn get_group_by_gid(&self, gid: gid_t) -> Option<Arc<Group>> {
self.groups.forward.get(&gid)?.as_ref().cloned()
}
fn get_group_by_name<S: AsRef<OsStr> + ?Sized>(&self, group_name: &S) -> Option<Arc<Group>> {
let name_arc = Arc::from(group_name.as_ref());
let gid = self.groups.backward.get(&name_arc)?.as_ref()?;
self.get_group_by_gid(*gid)
}
fn get_current_gid(&self) -> gid_t {
self.gid
}
fn get_current_groupname(&self) -> Option<Arc<OsStr>> {
self.get_group_by_gid(self.gid)
.map(|g| Arc::clone(&g.name_arc))
}
fn get_effective_gid(&self) -> gid_t {
self.egid
}
fn get_effective_groupname(&self) -> Option<Arc<OsStr>> {
self.get_group_by_gid(self.egid)
.map(|g| Arc::clone(&g.name_arc))
}
}