1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
//! A cache for users and groups provided by the OS.
//!
//! Because the users table changes so infrequently, it's common for
//! short-running programs to cache the results instead of getting the most
//! up-to-date entries every time. The [`UsersCache`](struct.UsersCache.html)
//! type helps with this, providing methods that have the same name as the
//! others in this crate, only they store the results.
//!
//! ## Example
//!
//! ```no_run
//! use std::sync::Arc;
//! use users::{Users, UsersCache};
//!
//! let mut cache = UsersCache::new();
//! let user      = cache.get_user_by_uid(502).expect("User not found");
//! let same_user = cache.get_user_by_uid(502).unwrap();
//!
//! // The two returned values point to the same User
//! assert!(Arc::ptr_eq(&user, &same_user));
//! ```
//!
//! ## Caching, multiple threads, and mutability
//!
//! The `UsersCache` 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 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!
//!
//! ```norun
//! let mut cache = UsersCache::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 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:
//!
//! ```norun
//! 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.

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, OsString};
use std::sync::Arc;

use base::{User, Group, all_users};
use traits::{Users, Groups};


/// A producer of user and group instances that caches every result.
///
/// For more information, see the [`users::cache` module documentation](index.html).
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>>,
}

/// 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> {
    forward:  RefCell< HashMap<K, Option<Arc<V>>> >,
    backward: RefCell< HashMap<Arc<OsString>, Option<K>> >,
}


// 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...
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 {

    /// Creates a new empty cache.
    ///
    /// # Examples
    ///
    /// ```
    /// use users::cache::UsersCache;
    ///
    /// let cache = UsersCache::new();
    /// ```
    pub fn new() -> Self {
        Self::default()
    }

    /// Creates a new cache that contains all the users present on the system.
    ///
    /// # Safety
    ///
    /// This is `unsafe` because we cannot prevent data races if two caches
    /// were attempted to be initialised on different threads at the same time.
    /// For more information, see the [`all_users` documentation](../fn.all_users.html).
    ///
    /// # Examples
    ///
    /// ```
    /// use users::cache::UsersCache;
    ///
    /// let cache = unsafe { UsersCache::with_all_users() };
    /// ```
    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::new(username.into())) {
            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<OsString>> {
        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<OsString>> {
        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::new(group_name.into())) {
            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<OsString>> {
        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<OsString>> {
        let gid = self.get_effective_gid();
        self.get_group_by_gid(gid).map(|g| Arc::clone(&g.name_arc))
    }
}