Return Arcs instead of clones, and take &self

The primary goal of this commit was to avoid having to clone the users and groups it produces. The old implementation was written by me when I hadn't *fully* internalised how lifetimes worked, and just used `clone` to get around the error messages. It was only later that I realised how inefficient this actually is -- the values stored in the cache are never used, only their clones are!

Several attempt at this later, I settled on using `Arc` for everything and cloning *those* values instead.

All the gory details of what happened are written in a comment in the OS module. The short version is that it's now possible to change the `Users` trait's methods from taking `&mut self` to just taking `&self`. This is within the allowed uses of `Cell`: implementation details of a method that does actually return the same thing each time, which this is.

The main casualty is that the user now has to know about `Arc` and initialise a user with a username in such a container, which I hope to solve in a later commit.
This commit is contained in:
Benjamin Sago
2016-01-14 23:06:14 -10:00
parent 6a829c7b7d
commit aa94ee89db
5 changed files with 292 additions and 133 deletions
+24 -23
View File
@@ -93,7 +93,7 @@
//! let mut cache = OSUsers::empty_cache();
//! let group = cache.get_group_by_name("admin").expect("No such group 'admin'!");
//! println!("The '{}' group has the ID {}", group.name, group.gid);
//! for member in group.members.into_iter() {
//! for member in &group.members {
//! println!("{} is a member of the group", member);
//! }
//! ```
@@ -112,6 +112,7 @@ use std::ffi::{CStr, CString};
use std::io::{Error as IOError, Result as IOResult};
use std::ptr::read;
use std::str::from_utf8_unchecked;
use std::sync::Arc;
extern crate libc;
pub use libc::{uid_t, gid_t, c_int};
@@ -130,40 +131,40 @@ pub use os::OSUsers;
pub trait Users {
/// Return a User object if one exists for the given user ID; otherwise, return None.
fn get_user_by_uid(&mut self, uid: uid_t) -> Option<User>;
fn get_user_by_uid(&self, uid: uid_t) -> Option<Arc<User>>;
/// Return a User object if one exists for the given username; otherwise, return None.
fn get_user_by_name(&mut self, username: &str) -> Option<User>;
fn get_user_by_name(&self, username: &str) -> Option<Arc<User>>;
/// Return a Group object if one exists for the given group ID; otherwise, return None.
fn get_group_by_gid(&mut self, gid: gid_t) -> Option<Group>;
fn get_group_by_gid(&self, gid: gid_t) -> Option<Arc<Group>>;
/// Return a Group object if one exists for the given groupname; otherwise, return None.
fn get_group_by_name(&mut self, group_name: &str) -> Option<Group>;
fn get_group_by_name(&self, group_name: &str) -> Option<Arc<Group>>;
/// Return the user ID for the user running the process.
fn get_current_uid(&mut self) -> uid_t;
fn get_current_uid(&self) -> uid_t;
/// Return the username of the user running the process.
fn get_current_username(&mut self) -> Option<String>;
fn get_current_username(&self) -> Option<Arc<String>>;
/// Return the group ID for the user running the process.
fn get_current_gid(&mut self) -> gid_t;
fn get_current_gid(&self) -> gid_t;
/// Return the group name of the user running the process.
fn get_current_groupname(&mut self) -> Option<String>;
fn get_current_groupname(&self) -> Option<Arc<String>>;
/// Return the effective user id.
fn get_effective_uid(&mut self) -> uid_t;
fn get_effective_uid(&self) -> uid_t;
/// Return the effective group id.
fn get_effective_gid(&mut self) -> gid_t;
fn get_effective_gid(&self) -> gid_t;
/// Return the effective username.
fn get_effective_username(&mut self) -> Option<String>;
fn get_effective_username(&self) -> Option<Arc<String>>;
/// Return the effective group name.
fn get_effective_groupname(&mut self) -> Option<String>;
fn get_effective_groupname(&self) -> Option<Arc<String>>;
}
#[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "dragonfly"))]
@@ -232,7 +233,7 @@ pub struct User {
pub uid: uid_t,
/// This user's name
pub name: String,
pub name: Arc<String>,
/// The ID of this user's primary group
pub primary_group: gid_t,
@@ -252,7 +253,7 @@ pub struct Group {
pub gid: uid_t,
/// This group's name
pub name: String,
pub name: Arc<String>,
/// Vector of the names of the users who belong to this group as a non-primary member
pub members: Vec<String>,
@@ -267,7 +268,7 @@ unsafe fn passwd_to_user(pointer: *const c_passwd) -> Option<User> {
let pw = read(pointer);
Some(User {
uid: pw.pw_uid as uid_t,
name: from_raw_buf(pw.pw_name as *const i8),
name: Arc::new(from_raw_buf(pw.pw_name as *const i8)),
primary_group: pw.pw_gid as gid_t,
home_dir: from_raw_buf(pw.pw_dir as *const i8),
shell: from_raw_buf(pw.pw_shell as *const i8)
@@ -283,7 +284,7 @@ unsafe fn struct_to_group(pointer: *const c_group) -> Option<Group> {
let gr = read(pointer);
let name = from_raw_buf(gr.gr_name as *const i8);
let members = members(gr.gr_mem);
Some(Group { gid: gr.gr_gid, name: name, members: members })
Some(Group { gid: gr.gr_gid, name: Arc::new(name), members: members })
}
else {
None
@@ -355,7 +356,7 @@ pub fn get_current_uid() -> uid_t {
/// Return the username of the user running the process.
pub fn get_current_username() -> Option<String> {
let uid = get_current_uid();
get_user_by_uid(uid).map(|u| u.name)
get_user_by_uid(uid).map(|u| Arc::try_unwrap(u.name).unwrap())
}
/// Return the user ID for the effective user running the process.
@@ -366,7 +367,7 @@ pub fn get_effective_uid() -> uid_t {
/// Return the username of the effective user running the process.
pub fn get_effective_username() -> Option<String> {
let uid = get_effective_uid();
get_user_by_uid(uid).map(|u| u.name)
get_user_by_uid(uid).map(|u| Arc::try_unwrap(u.name).unwrap())
}
/// Return the group ID for the user running the process.
@@ -377,7 +378,7 @@ pub fn get_current_gid() -> gid_t {
/// Return the groupname of the user running the process.
pub fn get_current_groupname() -> Option<String> {
let gid = get_current_gid();
get_group_by_gid(gid).map(|g| g.name)
get_group_by_gid(gid).map(|g| Arc::try_unwrap(g.name).unwrap())
}
/// Return the group ID for the effective user running the process.
@@ -388,7 +389,7 @@ pub fn get_effective_gid() -> gid_t {
/// Return the groupname of the effective user running the process.
pub fn get_effective_groupname() -> Option<String> {
let gid = get_effective_gid();
get_group_by_gid(gid).map(|g| g.name)
get_group_by_gid(gid).map(|g| Arc::try_unwrap(g.name).unwrap())
}
/// Set current user for the running process, requires root priviledges.
@@ -497,7 +498,7 @@ mod test {
#[test]
fn username() {
let uid = get_current_uid();
assert_eq!(get_current_username().unwrap(), get_user_by_uid(uid).unwrap().name);
assert_eq!(&*get_current_username().unwrap(), &*get_user_by_uid(uid).unwrap().name);
}
#[test]
@@ -531,7 +532,7 @@ mod test {
let name = get_current_username().unwrap();
let user_by_name = get_user_by_name(&name);
assert!(user_by_name.is_some());
assert_eq!(user_by_name.unwrap().name, name);
assert_eq!(&**user_by_name.unwrap().name, &*name);
// User names containing '\0' cannot be used (for now)
let user = get_user_by_name("user\0");
+43 -38
View File
@@ -18,9 +18,11 @@
//!
//! ```rust
//! use users::mock::{MockUsers, User, Group};
//! use std::sync::Arc;
//!
//! let mut users = MockUsers::with_current_uid(1000);
//! users.add_user(User { uid: 1000, name: "Bobbins".to_string(), primary_group: 100, home_dir: "/home/bobbins".to_string(), shell: "/bin/bash".to_string() });
//! users.add_group(Group { gid: 100, name: "funkyppl".to_string(), members: vec![ "other_person".to_string() ] });
//! users.add_user(User { uid: 1000, name: Arc::new("Bobbins".to_string()), primary_group: 100, home_dir: "/home/bobbins".to_string(), shell: "/bin/bash".to_string() });
//! users.add_group(Group { gid: 100, name: Arc::new("funkyppl".to_string()), members: vec![ "other_person".to_string() ] });
//! ```
//!
//! The exports get re-exported into the mock module, for simpler `use` lines.
@@ -37,13 +39,14 @@
//! ```rust
//! use users::{Users, OSUsers, User};
//! use users::mock::MockUsers;
//! use std::sync::Arc;
//!
//! fn print_current_username<U: Users>(users: &mut U) {
//! println!("Current user: {:?}", users.get_current_username());
//! }
//!
//! let mut users = MockUsers::with_current_uid(1001);
//! users.add_user(User { uid: 1001, name: "fred".to_string(), primary_group: 101 , home_dir: "/home/fred".to_string(), shell: "/bin/bash".to_string()});
//! users.add_user(User { uid: 1001, name: Arc::new("fred".to_string()), primary_group: 101 , home_dir: "/home/fred".to_string(), shell: "/bin/bash".to_string()});
//! print_current_username(&mut users);
//!
//! let mut actual_users = OSUsers::empty_cache();
@@ -52,12 +55,13 @@
pub use super::{Users, User, Group};
use std::collections::HashMap;
use std::sync::Arc;
use libc::{uid_t, gid_t};
/// A mocking users object that you can add your own users and groups to.
pub struct MockUsers {
users: HashMap<uid_t, User>,
groups: HashMap<gid_t, Group>,
users: HashMap<uid_t, Arc<User>>,
groups: HashMap<gid_t, Arc<Group>>,
uid: uid_t,
}
@@ -73,62 +77,62 @@ impl MockUsers {
}
/// Add a user to the users table.
pub fn add_user(&mut self, user: User) -> Option<User> {
self.users.insert(user.uid, user)
pub fn add_user(&mut self, user: User) -> Option<Arc<User>> {
self.users.insert(user.uid, Arc::new(user))
}
/// Add a group to the groups table.
pub fn add_group(&mut self, group: Group) -> Option<Group> {
self.groups.insert(group.gid, group)
pub fn add_group(&mut self, group: Group) -> Option<Arc<Group>> {
self.groups.insert(group.gid, Arc::new(group))
}
}
impl Users for MockUsers {
fn get_user_by_uid(&mut self, uid: uid_t) -> Option<User> {
fn get_user_by_uid(&self, uid: uid_t) -> Option<Arc<User>> {
self.users.get(&uid).cloned()
}
fn get_user_by_name(&mut self, username: &str) -> Option<User> {
self.users.values().find(|u| u.name == username).cloned()
fn get_user_by_name(&self, username: &str) -> Option<Arc<User>> {
self.users.values().find(|u| &*u.name == username).cloned()
}
fn get_group_by_gid(&mut self, gid: gid_t) -> Option<Group> {
fn get_group_by_gid(&self, gid: gid_t) -> Option<Arc<Group>> {
self.groups.get(&gid).cloned()
}
fn get_group_by_name(&mut self, group_name: &str) -> Option<Group> {
self.groups.values().find(|g| g.name == group_name).cloned()
fn get_group_by_name(&self, group_name: &str) -> Option<Arc<Group>> {
self.groups.values().find(|g| &*g.name == group_name).cloned()
}
fn get_current_uid(&mut self) -> uid_t {
fn get_current_uid(&self) -> uid_t {
self.uid
}
fn get_current_username(&mut self) -> Option<String> {
fn get_current_username(&self) -> Option<Arc<String>> {
self.users.get(&self.uid).map(|u| u.name.clone())
}
fn get_current_gid(&mut self) -> uid_t {
fn get_current_gid(&self) -> uid_t {
self.uid
}
fn get_current_groupname(&mut self) -> Option<String> {
fn get_current_groupname(&self) -> Option<Arc<String>> {
self.groups.get(&self.uid).map(|u| u.name.clone())
}
fn get_effective_uid(&mut self) -> uid_t {
fn get_effective_uid(&self) -> uid_t {
self.uid
}
fn get_effective_username(&mut self) -> Option<String> {
fn get_effective_username(&self) -> Option<Arc<String>> {
self.users.get(&self.uid).map(|u| u.name.clone())
}
fn get_effective_gid(&mut self) -> uid_t {
fn get_effective_gid(&self) -> uid_t {
self.uid
}
fn get_effective_groupname(&mut self) -> Option<String> {
fn get_effective_groupname(&self) -> Option<Arc<String>> {
self.groups.get(&self.uid).map(|u| u.name.clone())
}
}
@@ -136,71 +140,72 @@ impl Users for MockUsers {
#[cfg(test)]
mod test {
use super::{Users, User, Group, MockUsers};
use std::sync::Arc;
#[test]
fn current_username() {
let mut users = MockUsers::with_current_uid(1337);
users.add_user(User { uid: 1337, name: "fred".to_string(), primary_group: 101, home_dir: "/home/fred".to_string(), shell: "/bin/bash".to_string() });
assert_eq!(Some("fred".to_string()), users.get_current_username())
users.add_user(User { uid: 1337, name: Arc::new("fred".to_string()), primary_group: 101, home_dir: "/home/fred".to_string(), shell: "/bin/bash".to_string() });
assert_eq!(Some(Arc::new("fred".into())), users.get_current_username())
}
#[test]
fn no_current_username() {
let mut users = MockUsers::with_current_uid(1337);
let users = MockUsers::with_current_uid(1337);
assert_eq!(None, users.get_current_username())
}
#[test]
fn uid() {
let mut users = MockUsers::with_current_uid(0);
users.add_user(User { uid: 1337, name: "fred".to_string(), primary_group: 101, home_dir: "/home/fred".to_string(), shell: "/bin/bash".to_string() });
assert_eq!(Some("fred".to_string()), users.get_user_by_uid(1337).map(|u| u.name))
users.add_user(User { uid: 1337, name: Arc::new("fred".to_string()), primary_group: 101, home_dir: "/home/fred".to_string(), shell: "/bin/bash".to_string() });
assert_eq!(Some(Arc::new("fred".into())), users.get_user_by_uid(1337).map(|u| u.name.clone()))
}
#[test]
fn username() {
let mut users = MockUsers::with_current_uid(1337);
users.add_user(User { uid: 1440, name: "fred".to_string(), primary_group: 101, home_dir: "/home/fred".to_string(), shell: "/bin/bash".to_string() });
users.add_user(User { uid: 1440, name: Arc::new("fred".to_string()), primary_group: 101, home_dir: "/home/fred".to_string(), shell: "/bin/bash".to_string() });
assert_eq!(Some(1440), users.get_user_by_name("fred").map(|u| u.uid))
}
#[test]
fn no_username() {
let mut users = MockUsers::with_current_uid(1337);
users.add_user(User { uid: 1440, name: "fred".to_string(), primary_group: 101, home_dir: "/home/fred".to_string(), shell: "/bin/bash".to_string() });
users.add_user(User { uid: 1440, name: Arc::new("fred".to_string()), primary_group: 101, home_dir: "/home/fred".to_string(), shell: "/bin/bash".to_string() });
assert_eq!(None, users.get_user_by_name("criminy").map(|u| u.uid))
}
#[test]
fn no_uid() {
let mut users = MockUsers::with_current_uid(0);
assert_eq!(None, users.get_user_by_uid(1337).map(|u| u.name))
let users = MockUsers::with_current_uid(0);
assert_eq!(None, users.get_user_by_uid(1337).map(|u| u.name.clone()))
}
#[test]
fn gid() {
let mut users = MockUsers::with_current_uid(0);
users.add_group(Group { gid: 1337, name: "fred".to_string(), members: vec![], });
assert_eq!(Some("fred".to_string()), users.get_group_by_gid(1337).map(|g| g.name))
users.add_group(Group { gid: 1337, name: Arc::new("fred".to_string()), members: vec![], });
assert_eq!(Some(Arc::new("fred".into())), users.get_group_by_gid(1337).map(|g| g.name.clone()))
}
#[test]
fn group_name() {
let mut users = MockUsers::with_current_uid(0);
users.add_group(Group { gid: 1337, name: "fred".to_string(), members: vec![], });
users.add_group(Group { gid: 1337, name: Arc::new("fred".to_string()), members: vec![], });
assert_eq!(Some(1337), users.get_group_by_name("fred").map(|g| g.gid))
}
#[test]
fn no_group_name() {
let mut users = MockUsers::with_current_uid(0);
users.add_group(Group { gid: 1337, name: "fred".to_string(), members: vec![], });
users.add_group(Group { gid: 1337, name: Arc::new("fred".to_string()), members: vec![], });
assert_eq!(None, users.get_group_by_name("santa").map(|g| g.gid))
}
#[test]
fn no_gid() {
let mut users = MockUsers::with_current_uid(0);
assert_eq!(None, users.get_group_by_gid(1337).map(|g| g.name))
let users = MockUsers::with_current_uid(0);
assert_eq!(None, users.get_group_by_gid(1337).map(|g| g.name.clone()))
}
}
+160 -68
View File
@@ -1,21 +1,85 @@
//! A cache for users and groups provided by the OS.
//!
//! ## Caching, multiple threads, and mutability
//!
//! The `OSUsers` type is caught between a rock and a hard place when it comes
//! 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 youll 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!
//!
//! ``norun
//! let mut cache = OSUsers::empty_cache();
//! 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 users entry in the cache. This is a reference to something contained
//! in a mutable value. Then, when we want to get the users primary group, it
//! will return *another* reference to the same mutable value. This is something
//! that Rust explicitly disallows!
//!
//! The compiler wasnt on our side with Option 1, so lets 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
//! were just trying the same trick as earlier. A simplified implementation of
//! a user cache lookup would look something like this:
//!
//! ``norun
//! fn get_user_by_uid(&self, uid: uid_t) -> Option<&User> {
//! let users = self.users.borrow_mut();
//! users.get(uid)
//! }
//! ```
//!
//! Rust wont 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 dont have to worry about further uses of the cache, as
//! the values themselves dont 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.
use libc::{uid_t, gid_t};
use std::borrow::ToOwned;
use std::cell::{Cell, RefCell};
use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::collections::HashMap;
use std::sync::Arc;
use super::{User, Group, Users};
/// A producer of user and group instances that caches every result.
#[derive(Clone)]
pub struct OSUsers {
users: BiMap<uid_t, User>,
users: BiMap<uid_t, User>,
groups: BiMap<gid_t, Group>,
uid: Option<uid_t>,
gid: Option<gid_t>,
euid: Option<uid_t>,
egid: Option<gid_t>,
uid: Cell<Option<uid_t>>,
gid: Cell<Option<gid_t>>,
euid: Cell<Option<uid_t>>,
egid: Cell<Option<gid_t>>,
}
/// A kinda-bi-directional HashMap that associates keys to values, and then
@@ -23,10 +87,9 @@ pub struct OSUsers {
/// *values*-to-keys lookup, because we only want to search based on
/// usernames and group names. There wouldnt be much point offering a “User
/// to uid” map, as the uid is present in the user struct!
#[derive(Clone)]
struct BiMap<K, V> {
forward: HashMap<K, Option<V>>,
backward: HashMap<String, Option<K>>,
forward: RefCell< HashMap<K, Option<Arc<V>>> >,
backward: RefCell< HashMap<Arc<String>, Option<K>> >,
}
impl OSUsers {
@@ -34,33 +97,38 @@ impl OSUsers {
pub fn empty_cache() -> OSUsers {
OSUsers {
users: BiMap {
forward: HashMap::new(),
backward: HashMap::new(),
forward: RefCell::new(HashMap::new()),
backward: RefCell::new(HashMap::new()),
},
groups: BiMap {
forward: HashMap::new(),
backward: HashMap::new(),
forward: RefCell::new(HashMap::new()),
backward: RefCell::new(HashMap::new()),
},
uid: None,
gid: None,
euid: None,
egid: None,
uid: Cell::new(None),
gid: Cell::new(None),
euid: Cell::new(None),
egid: Cell::new(None),
}
}
}
impl Users for OSUsers {
fn get_user_by_uid(&mut self, uid: uid_t) -> Option<User> {
match self.users.forward.entry(uid) {
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) {
Vacant(entry) => {
let user = super::get_user_by_uid(uid);
match user {
match super::get_user_by_uid(uid) {
Some(user) => {
entry.insert(Some(user.clone()));
self.users.backward.insert(user.name.clone(), Some(user.uid));
Some(user)
let newsername = user.name.clone();
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)
},
None => {
entry.insert(None);
@@ -72,17 +140,23 @@ impl Users for OSUsers {
}
}
fn get_user_by_name(&mut self, username: &str) -> Option<User> {
fn get_user_by_name(&self, username: &str) -> Option<Arc<User>> {
let mut users_backward = self.users.backward.borrow_mut();
// 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
match self.users.backward.entry(username.to_owned()) {
match users_backward.entry(Arc::new(username.to_owned())) {
Vacant(entry) => {
let user = super::get_user_by_name(username);
match user {
match super::get_user_by_name(username) {
Some(user) => {
entry.insert(Some(user.uid));
self.users.forward.insert(user.uid, Some(user.clone()));
Some(user)
let uid = user.uid;
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)
},
None => {
entry.insert(None);
@@ -90,22 +164,31 @@ impl Users for OSUsers {
}
}
},
Occupied(entry) => match entry.get() {
&Some(uid) => self.users.forward[&uid].clone(),
&None => None,
Occupied(entry) => match *entry.get() {
Some(uid) => {
let users_forward = self.users.forward.borrow_mut();
users_forward[&uid].clone()
}
None => None,
}
}
}
fn get_group_by_gid(&mut self, gid: gid_t) -> Option<Group> {
match self.groups.forward.entry(gid) {
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) {
Vacant(entry) => {
let group = super::get_group_by_gid(gid);
match group {
Some(group) => {
entry.insert(Some(group.clone()));
self.groups.backward.insert(group.name.clone(), Some(group.gid));
Some(group)
let new_group_name = group.name.clone();
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)
},
None => {
entry.insert(None);
@@ -117,17 +200,24 @@ impl Users for OSUsers {
}
}
fn get_group_by_name(&mut self, group_name: &str) -> Option<Group> {
fn get_group_by_name(&self, group_name: &str) -> Option<Arc<Group>> {
let mut groups_backward = self.groups.backward.borrow_mut();
// 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
match self.groups.backward.entry(group_name.to_owned()) {
match groups_backward.entry(Arc::new(group_name.to_owned())) {
Vacant(entry) => {
let user = super::get_group_by_name(group_name);
match user {
Some(group) => {
entry.insert(Some(group.gid));
self.groups.forward.insert(group.gid, Some(group.clone()));
Some(group)
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(group_arc.clone()));
entry.insert(Some(gid));
Some(group_arc)
},
None => {
entry.insert(None);
@@ -135,75 +225,77 @@ impl Users for OSUsers {
}
}
},
Occupied(entry) => match entry.get() {
&Some(gid) => self.groups.forward[&gid].clone(),
&None => None,
Occupied(entry) => match *entry.get() {
Some(gid) => {
let groups_forward = self.groups.forward.borrow_mut();
groups_forward[&gid].as_ref().cloned()
}
None => None,
}
}
}
fn get_current_uid(&mut self) -> uid_t {
match self.uid {
fn get_current_uid(&self) -> uid_t {
match self.uid.get() {
Some(uid) => uid,
None => {
let uid = super::get_current_uid();
self.uid = Some(uid);
self.uid.set(Some(uid));
uid
}
}
}
/// Return the username of the user running the process.
fn get_current_username(&mut self) -> Option<String> {
fn get_current_username(&self) -> Option<Arc<String>> {
let uid = self.get_current_uid();
self.get_user_by_uid(uid).map(|u| u.name)
self.get_user_by_uid(uid).map(|u| u.name.clone())
}
fn get_current_gid(&mut self) -> gid_t {
match self.gid {
fn get_current_gid(&self) -> gid_t {
match self.gid.get() {
Some(gid) => gid,
None => {
let gid = super::get_current_gid();
self.gid = Some(gid);
self.gid.set(Some(gid));
gid
}
}
}
fn get_current_groupname(&mut self) -> Option<String> {
fn get_current_groupname(&self) -> Option<Arc<String>> {
let gid = self.get_current_gid();
self.get_group_by_gid(gid).map(|g| g.name)
self.get_group_by_gid(gid).map(|g| g.name.clone())
}
fn get_effective_gid(&mut self) -> gid_t {
match self.egid {
fn get_effective_gid(&self) -> gid_t {
match self.egid.get() {
Some(gid) => gid,
None => {
let gid = super::get_effective_gid();
self.egid = Some(gid);
self.egid.set(Some(gid));
gid
}
}
}
fn get_effective_groupname(&mut self) -> Option<String> {
fn get_effective_groupname(&self) -> Option<Arc<String>> {
let gid = self.get_effective_gid();
self.get_group_by_gid(gid).map(|g| g.name)
self.get_group_by_gid(gid).map(|g| g.name.clone())
}
fn get_effective_uid(&mut self) -> uid_t {
match self.euid {
fn get_effective_uid(&self) -> uid_t {
match self.euid.get() {
Some(uid) => uid,
None => {
let uid = super::get_effective_uid();
self.euid = Some(uid);
self.euid.set(Some(uid));
uid
}
}
}
fn get_effective_username(&mut self) -> Option<String> {
fn get_effective_username(&self) -> Option<Arc<String>> {
let uid = self.get_effective_uid();
self.get_user_by_uid(uid).map(|u| u.name)
self.get_user_by_uid(uid).map(|u| u.name.clone())
}
}