2016-01-14 23:06:14 -10:00
|
|
|
|
//! A cache for users and groups provided by the OS.
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! ## Caching, multiple threads, and mutability
|
|
|
|
|
|
//!
|
2016-01-26 19:17:24 +00:00
|
|
|
|
//! The `UsersCache` type is caught between a rock and a hard place when it comes
|
2016-01-14 23:06:14 -10:00
|
|
|
|
//! to providing references to users and groups.
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! Instead of returning a fresh `User` struct each time, for example, it will
|
|
|
|
|
|
//! return a reference to the version it currently has in its cache. So you can
|
|
|
|
|
|
//! ask for User #501 twice, and you’ll get a reference to the same value both
|
|
|
|
|
|
//! time. Its methods are *idempotent* -- calling one multiple times has the
|
|
|
|
|
|
//! same effect as calling one once.
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! This works fine in theory, but in practice, the cache has to update its own
|
|
|
|
|
|
//! state somehow: it contains several `HashMap`s that hold the result of user
|
|
|
|
|
|
//! and group lookups. Rust provides mutability in two ways:
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! 1. Have its methods take `&mut self`, instead of `&self`, allowing the
|
|
|
|
|
|
//! internal maps to be mutated (“inherited mutability”)
|
|
|
|
|
|
//! 2. Wrap the internal maps in a `RefCell`, allowing them to be modified
|
|
|
|
|
|
//! (“interior mutability”).
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! Unfortunately, Rust is also very protective of references to a mutable
|
|
|
|
|
|
//! value. In this case, switching to `&mut self` would only allow for one user
|
|
|
|
|
|
//! to be read at a time!
|
|
|
|
|
|
//!
|
2018-03-06 02:05:31 -08:00
|
|
|
|
//! ```norun
|
2016-01-26 19:17:24 +00:00
|
|
|
|
//! let mut cache = UsersCache::empty_cache();
|
2016-01-14 23:06:14 -10:00
|
|
|
|
//! let uid = cache.get_current_uid(); // OK...
|
|
|
|
|
|
//! let user = cache.get_user_by_uid(uid).unwrap() // OK...
|
|
|
|
|
|
//! let group = cache.get_group_by_gid(user.primary_group); // No!
|
|
|
|
|
|
//! ```
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! When we get the `user`, it returns an optional reference (which we unwrap)
|
|
|
|
|
|
//! to the user’s entry in the cache. This is a reference to something contained
|
|
|
|
|
|
//! in a mutable value. Then, when we want to get the user’s primary group, it
|
|
|
|
|
|
//! will return *another* reference to the same mutable value. This is something
|
|
|
|
|
|
//! that Rust explicitly disallows!
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! The compiler wasn’t on our side with Option 1, so let’s try Option 2:
|
|
|
|
|
|
//! changing the methods back to `&self` instead of `&mut self`, and using
|
|
|
|
|
|
//! `RefCell`s internally. However, Rust is smarter than this, and knows that
|
|
|
|
|
|
//! we’re just trying the same trick as earlier. A simplified implementation of
|
|
|
|
|
|
//! a user cache lookup would look something like this:
|
|
|
|
|
|
//!
|
2018-03-06 02:05:31 -08:00
|
|
|
|
//! ```norun
|
2016-01-14 23:06:14 -10:00
|
|
|
|
//! fn get_user_by_uid(&self, uid: uid_t) -> Option<&User> {
|
|
|
|
|
|
//! let users = self.users.borrow_mut();
|
|
|
|
|
|
//! users.get(uid)
|
|
|
|
|
|
//! }
|
|
|
|
|
|
//! ```
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! Rust won’t allow us to return a reference like this because the `Ref` of the
|
|
|
|
|
|
//! `RefCell` just gets dropped at the end of the method, meaning that our
|
|
|
|
|
|
//! reference does not live long enough.
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! So instead of doing any of that, we use `Arc` everywhere in order to get
|
|
|
|
|
|
//! around all the lifetime restrictions. Returning reference-counted users and
|
|
|
|
|
|
//! groups mean that we don’t have to worry about further uses of the cache, as
|
|
|
|
|
|
//! the values themselves don’t count as being stored *in* the cache anymore. So
|
|
|
|
|
|
//! it can be queried multiple times or go out of scope and the values it
|
|
|
|
|
|
//! produces are not affected.
|
|
|
|
|
|
|
2016-01-10 21:14:05 +13:00
|
|
|
|
use libc::{uid_t, gid_t};
|
|
|
|
|
|
use std::borrow::ToOwned;
|
2016-01-14 23:06:14 -10:00
|
|
|
|
use std::cell::{Cell, RefCell};
|
2016-01-10 21:14:05 +13:00
|
|
|
|
use std::collections::hash_map::Entry::{Occupied, Vacant};
|
|
|
|
|
|
use std::collections::HashMap;
|
2016-01-14 23:06:14 -10:00
|
|
|
|
use std::sync::Arc;
|
2016-01-10 21:14:05 +13:00
|
|
|
|
|
2016-01-29 13:30:48 +00:00
|
|
|
|
use base::{User, Group, AllUsers};
|
2016-01-26 19:05:10 +00:00
|
|
|
|
use traits::{Users, Groups};
|
2016-01-10 21:14:05 +13:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// A producer of user and group instances that caches every result.
|
2016-01-26 19:17:24 +00:00
|
|
|
|
pub struct UsersCache {
|
2016-01-14 23:06:14 -10:00
|
|
|
|
users: BiMap<uid_t, User>,
|
2016-01-13 18:00:20 +13:00
|
|
|
|
groups: BiMap<gid_t, Group>,
|
2016-01-10 21:14:05 +13:00
|
|
|
|
|
2016-01-14 23:06:14 -10:00
|
|
|
|
uid: Cell<Option<uid_t>>,
|
|
|
|
|
|
gid: Cell<Option<gid_t>>,
|
|
|
|
|
|
euid: Cell<Option<uid_t>>,
|
|
|
|
|
|
egid: Cell<Option<gid_t>>,
|
2016-01-10 21:14:05 +13:00
|
|
|
|
}
|
|
|
|
|
|
|
2016-01-13 18:00:20 +13:00
|
|
|
|
/// A kinda-bi-directional HashMap that associates keys to values, and then
|
|
|
|
|
|
/// strings back to keys. It doesn’t go the full route and offer
|
|
|
|
|
|
/// *values*-to-keys lookup, because we only want to search based on
|
|
|
|
|
|
/// usernames and group names. There wouldn’t be much point offering a “User
|
|
|
|
|
|
/// to uid” map, as the uid is present in the user struct!
|
|
|
|
|
|
struct BiMap<K, V> {
|
2016-01-14 23:06:14 -10:00
|
|
|
|
forward: RefCell< HashMap<K, Option<Arc<V>>> >,
|
|
|
|
|
|
backward: RefCell< HashMap<Arc<String>, Option<K>> >,
|
2016-01-13 18:00:20 +13:00
|
|
|
|
}
|
|
|
|
|
|
|
2016-01-16 11:27:18 -10:00
|
|
|
|
// Default has to be impl'd manually here, because there's no
|
|
|
|
|
|
// Default impl on User or Group, even though those types aren't
|
|
|
|
|
|
// needed to produce a default instance of any HashMaps...
|
|
|
|
|
|
|
2016-01-26 19:17:24 +00:00
|
|
|
|
impl Default for UsersCache {
|
|
|
|
|
|
fn default() -> UsersCache {
|
|
|
|
|
|
UsersCache {
|
2016-01-13 18:00:20 +13:00
|
|
|
|
users: BiMap {
|
2016-01-14 23:06:14 -10:00
|
|
|
|
forward: RefCell::new(HashMap::new()),
|
|
|
|
|
|
backward: RefCell::new(HashMap::new()),
|
2016-01-13 18:00:20 +13:00
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
groups: BiMap {
|
2016-01-14 23:06:14 -10:00
|
|
|
|
forward: RefCell::new(HashMap::new()),
|
|
|
|
|
|
backward: RefCell::new(HashMap::new()),
|
2016-01-13 18:00:20 +13:00
|
|
|
|
},
|
|
|
|
|
|
|
2016-01-14 23:06:14 -10:00
|
|
|
|
uid: Cell::new(None),
|
|
|
|
|
|
gid: Cell::new(None),
|
|
|
|
|
|
euid: Cell::new(None),
|
|
|
|
|
|
egid: Cell::new(None),
|
2016-01-10 21:14:05 +13:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2016-01-26 19:17:24 +00:00
|
|
|
|
impl UsersCache {
|
2016-01-16 11:27:18 -10:00
|
|
|
|
|
2016-01-29 13:52:13 +00:00
|
|
|
|
/// Creates a new empty cache.
|
2016-01-26 19:17:24 +00:00
|
|
|
|
pub fn new() -> UsersCache {
|
|
|
|
|
|
UsersCache::default()
|
2016-01-16 11:27:18 -10:00
|
|
|
|
}
|
2016-01-29 13:30:48 +00:00
|
|
|
|
|
2016-01-29 13:52:13 +00:00
|
|
|
|
/// Creates a new cache that contains all the users present on the system.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// This is `unsafe` because we cannot prevent data races if two caches
|
|
|
|
|
|
/// were attempted to be initialised on different threads at the same time.
|
2016-01-29 13:30:48 +00:00
|
|
|
|
pub unsafe fn with_all_users() -> UsersCache {
|
|
|
|
|
|
let cache = UsersCache::new();
|
|
|
|
|
|
|
2016-01-29 13:52:13 +00:00
|
|
|
|
for user in AllUsers::new() {
|
2016-01-29 13:30:48 +00:00
|
|
|
|
let uid = user.uid();
|
|
|
|
|
|
let user_arc = Arc::new(user);
|
|
|
|
|
|
cache.users.forward.borrow_mut().insert(uid, Some(user_arc.clone()));
|
|
|
|
|
|
cache.users.backward.borrow_mut().insert(user_arc.name_arc.clone(), Some(uid));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
cache
|
|
|
|
|
|
}
|
2016-01-16 11:27:18 -10:00
|
|
|
|
}
|
|
|
|
|
|
|
2016-01-26 19:17:24 +00:00
|
|
|
|
impl Users for UsersCache {
|
2016-01-14 23:06:14 -10:00
|
|
|
|
fn get_user_by_uid(&self, uid: uid_t) -> Option<Arc<User>> {
|
|
|
|
|
|
let mut users_forward = self.users.forward.borrow_mut();
|
|
|
|
|
|
|
|
|
|
|
|
match users_forward.entry(uid) {
|
2016-01-10 21:14:05 +13:00
|
|
|
|
Vacant(entry) => {
|
2016-01-14 23:06:14 -10:00
|
|
|
|
match super::get_user_by_uid(uid) {
|
2016-01-10 21:14:05 +13:00
|
|
|
|
Some(user) => {
|
2016-01-28 15:04:22 +00:00
|
|
|
|
let newsername = user.name_arc.clone();
|
2016-01-14 23:06:14 -10:00
|
|
|
|
let mut users_backward = self.users.backward.borrow_mut();
|
|
|
|
|
|
users_backward.insert(newsername, Some(uid));
|
|
|
|
|
|
|
|
|
|
|
|
let user_arc = Arc::new(user);
|
|
|
|
|
|
entry.insert(Some(user_arc.clone()));
|
|
|
|
|
|
Some(user_arc)
|
2016-01-10 21:14:05 +13:00
|
|
|
|
},
|
|
|
|
|
|
None => {
|
|
|
|
|
|
entry.insert(None);
|
|
|
|
|
|
None
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
Occupied(entry) => entry.get().clone(),
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2016-01-14 23:06:14 -10:00
|
|
|
|
fn get_user_by_name(&self, username: &str) -> Option<Arc<User>> {
|
|
|
|
|
|
let mut users_backward = self.users.backward.borrow_mut();
|
|
|
|
|
|
|
2016-01-10 21:14:05 +13:00
|
|
|
|
// to_owned() could change here:
|
|
|
|
|
|
// https://github.com/rust-lang/rfcs/blob/master/text/0509-collections-reform-part-2.md#alternatives-to-toowned-on-entries
|
2016-01-14 23:06:14 -10:00
|
|
|
|
match users_backward.entry(Arc::new(username.to_owned())) {
|
2016-01-10 21:14:05 +13:00
|
|
|
|
Vacant(entry) => {
|
2016-01-14 23:06:14 -10:00
|
|
|
|
match super::get_user_by_name(username) {
|
2016-01-10 21:14:05 +13:00
|
|
|
|
Some(user) => {
|
2016-01-28 15:04:22 +00:00
|
|
|
|
let uid = user.uid();
|
2016-01-14 23:06:14 -10:00
|
|
|
|
let user_arc = Arc::new(user);
|
|
|
|
|
|
|
|
|
|
|
|
let mut users_forward = self.users.forward.borrow_mut();
|
|
|
|
|
|
users_forward.insert(uid, Some(user_arc.clone()));
|
|
|
|
|
|
entry.insert(Some(uid));
|
|
|
|
|
|
|
|
|
|
|
|
Some(user_arc)
|
2016-01-10 21:14:05 +13:00
|
|
|
|
},
|
|
|
|
|
|
None => {
|
|
|
|
|
|
entry.insert(None);
|
|
|
|
|
|
None
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
2016-01-14 23:06:14 -10:00
|
|
|
|
Occupied(entry) => match *entry.get() {
|
|
|
|
|
|
Some(uid) => {
|
|
|
|
|
|
let users_forward = self.users.forward.borrow_mut();
|
|
|
|
|
|
users_forward[&uid].clone()
|
|
|
|
|
|
}
|
|
|
|
|
|
None => None,
|
2016-01-10 21:14:05 +13:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2016-01-14 23:15:53 -10:00
|
|
|
|
fn get_current_uid(&self) -> uid_t {
|
|
|
|
|
|
match self.uid.get() {
|
|
|
|
|
|
Some(uid) => uid,
|
|
|
|
|
|
None => {
|
|
|
|
|
|
let uid = super::get_current_uid();
|
|
|
|
|
|
self.uid.set(Some(uid));
|
|
|
|
|
|
uid
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn get_current_username(&self) -> Option<Arc<String>> {
|
|
|
|
|
|
let uid = self.get_current_uid();
|
2016-01-28 15:04:22 +00:00
|
|
|
|
self.get_user_by_uid(uid).map(|u| u.name_arc.clone())
|
2016-01-14 23:15:53 -10:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn get_effective_uid(&self) -> uid_t {
|
|
|
|
|
|
match self.euid.get() {
|
|
|
|
|
|
Some(uid) => uid,
|
|
|
|
|
|
None => {
|
|
|
|
|
|
let uid = super::get_effective_uid();
|
|
|
|
|
|
self.euid.set(Some(uid));
|
|
|
|
|
|
uid
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn get_effective_username(&self) -> Option<Arc<String>> {
|
|
|
|
|
|
let uid = self.get_effective_uid();
|
2016-01-28 15:04:22 +00:00
|
|
|
|
self.get_user_by_uid(uid).map(|u| u.name_arc.clone())
|
2016-01-14 23:15:53 -10:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2016-01-26 19:17:24 +00:00
|
|
|
|
impl Groups for UsersCache {
|
2016-01-14 23:06:14 -10:00
|
|
|
|
fn get_group_by_gid(&self, gid: gid_t) -> Option<Arc<Group>> {
|
|
|
|
|
|
let mut groups_forward = self.groups.forward.borrow_mut();
|
|
|
|
|
|
|
|
|
|
|
|
match groups_forward.entry(gid) {
|
2016-01-10 21:14:05 +13:00
|
|
|
|
Vacant(entry) => {
|
|
|
|
|
|
let group = super::get_group_by_gid(gid);
|
|
|
|
|
|
match group {
|
|
|
|
|
|
Some(group) => {
|
2016-01-28 15:39:04 +00:00
|
|
|
|
let new_group_name = group.name_arc.clone();
|
2016-01-14 23:06:14 -10:00
|
|
|
|
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(group_arc.clone()));
|
|
|
|
|
|
Some(group_arc)
|
2016-01-10 21:14:05 +13:00
|
|
|
|
},
|
|
|
|
|
|
None => {
|
|
|
|
|
|
entry.insert(None);
|
|
|
|
|
|
None
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
Occupied(entry) => entry.get().clone(),
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2016-01-14 23:06:14 -10:00
|
|
|
|
fn get_group_by_name(&self, group_name: &str) -> Option<Arc<Group>> {
|
|
|
|
|
|
let mut groups_backward = self.groups.backward.borrow_mut();
|
|
|
|
|
|
|
2016-01-10 21:14:05 +13:00
|
|
|
|
// to_owned() could change here:
|
|
|
|
|
|
// https://github.com/rust-lang/rfcs/blob/master/text/0509-collections-reform-part-2.md#alternatives-to-toowned-on-entries
|
2016-01-14 23:06:14 -10:00
|
|
|
|
match groups_backward.entry(Arc::new(group_name.to_owned())) {
|
2016-01-10 21:14:05 +13:00
|
|
|
|
Vacant(entry) => {
|
|
|
|
|
|
let user = super::get_group_by_name(group_name);
|
|
|
|
|
|
match user {
|
|
|
|
|
|
Some(group) => {
|
2016-01-14 23:06:14 -10:00
|
|
|
|
let group_arc = Arc::new(group.clone());
|
2016-01-28 15:39:04 +00:00
|
|
|
|
let gid = group.gid();
|
2016-01-14 23:06:14 -10:00
|
|
|
|
|
|
|
|
|
|
let mut groups_forward = self.groups.forward.borrow_mut();
|
|
|
|
|
|
groups_forward.insert(gid, Some(group_arc.clone()));
|
|
|
|
|
|
entry.insert(Some(gid));
|
|
|
|
|
|
|
|
|
|
|
|
Some(group_arc)
|
2016-01-10 21:14:05 +13:00
|
|
|
|
},
|
|
|
|
|
|
None => {
|
|
|
|
|
|
entry.insert(None);
|
|
|
|
|
|
None
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
2016-01-14 23:06:14 -10:00
|
|
|
|
Occupied(entry) => match *entry.get() {
|
|
|
|
|
|
Some(gid) => {
|
|
|
|
|
|
let groups_forward = self.groups.forward.borrow_mut();
|
|
|
|
|
|
groups_forward[&gid].as_ref().cloned()
|
|
|
|
|
|
}
|
|
|
|
|
|
None => None,
|
2016-01-10 21:14:05 +13:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2016-01-14 23:06:14 -10:00
|
|
|
|
fn get_current_gid(&self) -> gid_t {
|
|
|
|
|
|
match self.gid.get() {
|
2016-01-10 21:14:05 +13:00
|
|
|
|
Some(gid) => gid,
|
|
|
|
|
|
None => {
|
|
|
|
|
|
let gid = super::get_current_gid();
|
2016-01-14 23:06:14 -10:00
|
|
|
|
self.gid.set(Some(gid));
|
2016-01-10 21:14:05 +13:00
|
|
|
|
gid
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2016-01-14 23:06:14 -10:00
|
|
|
|
fn get_current_groupname(&self) -> Option<Arc<String>> {
|
2016-01-10 21:14:05 +13:00
|
|
|
|
let gid = self.get_current_gid();
|
2016-01-28 15:39:04 +00:00
|
|
|
|
self.get_group_by_gid(gid).map(|g| g.name_arc.clone())
|
2016-01-10 21:14:05 +13:00
|
|
|
|
}
|
|
|
|
|
|
|
2016-01-14 23:06:14 -10:00
|
|
|
|
fn get_effective_gid(&self) -> gid_t {
|
|
|
|
|
|
match self.egid.get() {
|
2016-01-10 21:14:05 +13:00
|
|
|
|
Some(gid) => gid,
|
|
|
|
|
|
None => {
|
|
|
|
|
|
let gid = super::get_effective_gid();
|
2016-01-14 23:06:14 -10:00
|
|
|
|
self.egid.set(Some(gid));
|
2016-01-10 21:14:05 +13:00
|
|
|
|
gid
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2016-01-14 23:06:14 -10:00
|
|
|
|
fn get_effective_groupname(&self) -> Option<Arc<String>> {
|
2016-01-10 21:14:05 +13:00
|
|
|
|
let gid = self.get_effective_gid();
|
2016-01-28 15:39:04 +00:00
|
|
|
|
self.get_group_by_gid(gid).map(|g| g.name_arc.clone())
|
2016-01-10 21:14:05 +13:00
|
|
|
|
}
|
|
|
|
|
|
}
|