From bb1299a2884152bc553729d55d625010c1ac7de3 Mon Sep 17 00:00:00 2001 From: Benjamin Sago Date: Sun, 10 Jan 2016 21:14:05 +1300 Subject: [PATCH] Extract OS functions to os module This commit does two things: Firstly, it swaps the locations in the code where the actual, unsafe C library operations are done from being done in the OSUsers struct to in the stand-alone functions. This makes more sense, as previously, the stand-alone functions would instantiate a new cache, run a method on it, and then immediately discard the cache. Now the cache uses the methods, not the other way round. Secondly, it extracts all of the caching methods to its own module, leaving `lib` with a bare-bones users implementation. --- src/lib.rs | 303 ++++++++++------------------------------------------- src/os.rs | 195 ++++++++++++++++++++++++++++++++++ 2 files changed, 251 insertions(+), 247 deletions(-) create mode 100644 src/os.rs diff --git a/src/lib.rs b/src/lib.rs index 61f6a0b..5dfccf0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -108,10 +108,6 @@ //! Use the mocking module to create custom tables to test your code for these //! edge cases. - -use std::borrow::ToOwned; -use std::collections::hash_map::Entry::{Occupied, Vacant}; -use std::collections::HashMap; use std::ffi::{CStr, CString}; use std::io::{Error as IOError, Result as IOResult}; use std::ptr::read; @@ -127,7 +123,8 @@ use libc::{c_char, time_t}; use libc::c_char; pub mod mock; - +pub mod os; +pub use os::OSUsers; /// The trait for the `OSUsers` object. pub trait Users { @@ -261,21 +258,6 @@ pub struct Group { pub members: Vec, } -/// A producer of user and group instances that caches every result. -#[derive(Clone)] -pub struct OSUsers { - users: HashMap>, - users_back: HashMap>, - - groups: HashMap>, - groups_back: HashMap>, - - uid: Option, - gid: Option, - euid: Option, - egid: Option, -} - unsafe fn from_raw_buf(p: *const i8) -> String { from_utf8_unchecked(CStr::from_ptr(p).to_bytes()).to_string() } @@ -328,253 +310,85 @@ unsafe fn members(groups: *const *const c_char) -> Vec { } } -impl Users for OSUsers { - fn get_user_by_uid(&mut self, uid: uid_t) -> Option { - match self.users.entry(uid) { - Vacant(entry) => { - let user = unsafe { passwd_to_user(getpwuid(uid)) }; - match user { - Some(user) => { - entry.insert(Some(user.clone())); - self.users_back.insert(user.name.clone(), Some(user.uid)); - Some(user) - }, - None => { - entry.insert(None); - None - } - } - }, - Occupied(entry) => entry.get().clone(), - } - } - - fn get_user_by_name(&mut self, username: &str) -> Option { - // 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_back.entry(username.to_owned()) { - Vacant(entry) => { - let username_c = CString::new(username); - - if !username_c.is_ok() { - // This usually means the given username contained a '\0' already - // It is debatable what to do here - return None; - } - - let user = unsafe { passwd_to_user(getpwnam(username_c.unwrap().as_ptr())) }; - match user { - Some(user) => { - entry.insert(Some(user.uid)); - self.users.insert(user.uid, Some(user.clone())); - Some(user) - }, - None => { - entry.insert(None); - None - } - } - }, - Occupied(entry) => match entry.get() { - &Some(uid) => self.users[&uid].clone(), - &None => None, - } - } - } - - fn get_group_by_gid(&mut self, gid: gid_t) -> Option { - match self.groups.entry(gid) { - Vacant(entry) => { - let group = unsafe { struct_to_group(getgrgid(gid)) }; - match group { - Some(group) => { - entry.insert(Some(group.clone())); - self.groups_back.insert(group.name.clone(), Some(group.gid)); - Some(group) - }, - None => { - entry.insert(None); - None - } - } - }, - Occupied(entry) => entry.get().clone(), - } - } - - fn get_group_by_name(&mut self, group_name: &str) -> Option { - // 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_back.entry(group_name.to_owned()) { - Vacant(entry) => { - let group_name_c = CString::new(group_name); - - if !group_name_c.is_ok() { - // This usually means the given username contained a '\0' already - // It is debatable what to do here - return None; - } - - let user = unsafe { struct_to_group(getgrnam(group_name_c.unwrap().as_ptr())) }; - match user { - Some(group) => { - entry.insert(Some(group.gid)); - self.groups.insert(group.gid, Some(group.clone())); - Some(group) - }, - None => { - entry.insert(None); - None - } - } - }, - Occupied(entry) => match entry.get() { - &Some(gid) => self.groups[&gid].clone(), - &None => None, - } - } - } - - fn get_current_uid(&mut self) -> uid_t { - match self.uid { - Some(uid) => uid, - None => { - let uid = unsafe { getuid() }; - self.uid = Some(uid); - uid - } - } - } - - /// Return the username of the user running the process. - fn get_current_username(&mut self) -> Option { - let uid = self.get_current_uid(); - self.get_user_by_uid(uid).map(|u| u.name) - } - - fn get_current_gid(&mut self) -> gid_t { - match self.gid { - Some(gid) => gid, - None => { - let gid = unsafe { getgid() }; - self.gid = Some(gid); - gid - } - } - } - - fn get_current_groupname(&mut self) -> Option { - let gid = self.get_current_gid(); - self.get_group_by_gid(gid).map(|g| g.name) - } - - fn get_effective_gid(&mut self) -> gid_t { - match self.egid { - Some(gid) => gid, - None => { - let gid = unsafe { getegid() }; - self.egid = Some(gid); - gid - } - } - } - - fn get_effective_groupname(&mut self) -> Option { - let gid = self.get_effective_gid(); - self.get_group_by_gid(gid).map(|g| g.name) - } - - fn get_effective_uid(&mut self) -> uid_t { - match self.euid { - Some(uid) => uid, - None => { - let uid = unsafe { geteuid() }; - self.euid = Some(uid); - uid - } - } - } - - fn get_effective_username(&mut self) -> Option { - let uid = self.get_effective_uid(); - self.get_user_by_uid(uid).map(|u| u.name) - } -} - -impl OSUsers { - /// Create a new empty OS Users object. - pub fn empty_cache() -> OSUsers { - OSUsers { - users: HashMap::new(), - users_back: HashMap::new(), - groups: HashMap::new(), - groups_back: HashMap::new(), - uid: None, - gid: None, - euid: None, - egid: None, - } - } -} /// Return a User object if one exists for the given user ID; otherwise, return None. pub fn get_user_by_uid(uid: uid_t) -> Option { - OSUsers::empty_cache().get_user_by_uid(uid) + unsafe { passwd_to_user(getpwuid(uid)) } } /// Return a User object if one exists for the given username; otherwise, return None. pub fn get_user_by_name(username: &str) -> Option { - OSUsers::empty_cache().get_user_by_name(username) + let username_c = CString::new(username); + + if !username_c.is_ok() { + // This usually means the given username contained a '\0' already + // It is debatable what to do here + return None; + } + + unsafe { passwd_to_user(getpwnam(username_c.unwrap().as_ptr())) } } /// Return a Group object if one exists for the given group ID; otherwise, return None. pub fn get_group_by_gid(gid: gid_t) -> Option { - OSUsers::empty_cache().get_group_by_gid(gid) + unsafe { struct_to_group(getgrgid(gid)) } } /// Return a Group object if one exists for the given groupname; otherwise, return None. pub fn get_group_by_name(group_name: &str) -> Option { - OSUsers::empty_cache().get_group_by_name(group_name) + let group_name_c = CString::new(group_name); + + if !group_name_c.is_ok() { + // This usually means the given username contained a '\0' already + // It is debatable what to do here + return None; + } + + unsafe { struct_to_group(getgrnam(group_name_c.unwrap().as_ptr())) } } /// Return the user ID for the user running the process. pub fn get_current_uid() -> uid_t { - OSUsers::empty_cache().get_current_uid() + unsafe { getuid() } } /// Return the username of the user running the process. pub fn get_current_username() -> Option { - OSUsers::empty_cache().get_current_username() + let uid = get_current_uid(); + get_user_by_uid(uid).map(|u| u.name) } /// Return the user ID for the effective user running the process. pub fn get_effective_uid() -> uid_t { - OSUsers::empty_cache().get_effective_uid() + unsafe { geteuid() } } /// Return the username of the effective user running the process. pub fn get_effective_username() -> Option { - OSUsers::empty_cache().get_effective_username() + let uid = get_effective_uid(); + get_user_by_uid(uid).map(|u| u.name) } /// Return the group ID for the user running the process. pub fn get_current_gid() -> gid_t { - OSUsers::empty_cache().get_current_gid() + unsafe { getgid() } } /// Return the groupname of the user running the process. pub fn get_current_groupname() -> Option { - OSUsers::empty_cache().get_current_groupname() + let gid = get_current_gid(); + get_group_by_gid(gid).map(|g| g.name) } /// Return the group ID for the effective user running the process. pub fn get_effective_gid() -> gid_t { - OSUsers::empty_cache().get_effective_gid() + unsafe { getegid() } } /// Return the groupname of the effective user running the process. pub fn get_effective_groupname() -> Option { - OSUsers::empty_cache().get_effective_groupname() + let gid = get_effective_gid(); + get_group_by_gid(gid).map(|g| g.name) } /// Set current user for the running process, requires root priviledges. @@ -670,79 +484,74 @@ pub fn switch_user_group(uid: uid_t, gid: gid_t) -> Result>, + users_back: HashMap>, + + groups: HashMap>, + groups_back: HashMap>, + + uid: Option, + gid: Option, + euid: Option, + egid: Option, +} + +impl OSUsers { + /// Create a new empty OS Users object. + pub fn empty_cache() -> OSUsers { + OSUsers { + users: HashMap::new(), + users_back: HashMap::new(), + groups: HashMap::new(), + groups_back: HashMap::new(), + uid: None, + gid: None, + euid: None, + egid: None, + } + } +} + +impl Users for OSUsers { + fn get_user_by_uid(&mut self, uid: uid_t) -> Option { + match self.users.entry(uid) { + Vacant(entry) => { + let user = super::get_user_by_uid(uid); + match user { + Some(user) => { + entry.insert(Some(user.clone())); + self.users_back.insert(user.name.clone(), Some(user.uid)); + Some(user) + }, + None => { + entry.insert(None); + None + } + } + }, + Occupied(entry) => entry.get().clone(), + } + } + + fn get_user_by_name(&mut self, username: &str) -> Option { + // 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_back.entry(username.to_owned()) { + Vacant(entry) => { + let user = super::get_user_by_name(username); + match user { + Some(user) => { + entry.insert(Some(user.uid)); + self.users.insert(user.uid, Some(user.clone())); + Some(user) + }, + None => { + entry.insert(None); + None + } + } + }, + Occupied(entry) => match entry.get() { + &Some(uid) => self.users[&uid].clone(), + &None => None, + } + } + } + + fn get_group_by_gid(&mut self, gid: gid_t) -> Option { + match self.groups.entry(gid) { + Vacant(entry) => { + let group = super::get_group_by_gid(gid); + match group { + Some(group) => { + entry.insert(Some(group.clone())); + self.groups_back.insert(group.name.clone(), Some(group.gid)); + Some(group) + }, + None => { + entry.insert(None); + None + } + } + }, + Occupied(entry) => entry.get().clone(), + } + } + + fn get_group_by_name(&mut self, group_name: &str) -> Option { + // 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_back.entry(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.insert(group.gid, Some(group.clone())); + Some(group) + }, + None => { + entry.insert(None); + None + } + } + }, + Occupied(entry) => match entry.get() { + &Some(gid) => self.groups[&gid].clone(), + &None => None, + } + } + } + + fn get_current_uid(&mut self) -> uid_t { + match self.uid { + Some(uid) => uid, + None => { + let uid = super::get_current_uid(); + self.uid = Some(uid); + uid + } + } + } + + /// Return the username of the user running the process. + fn get_current_username(&mut self) -> Option { + let uid = self.get_current_uid(); + self.get_user_by_uid(uid).map(|u| u.name) + } + + fn get_current_gid(&mut self) -> gid_t { + match self.gid { + Some(gid) => gid, + None => { + let gid = super::get_current_gid(); + self.gid = Some(gid); + gid + } + } + } + + fn get_current_groupname(&mut self) -> Option { + let gid = self.get_current_gid(); + self.get_group_by_gid(gid).map(|g| g.name) + } + + fn get_effective_gid(&mut self) -> gid_t { + match self.egid { + Some(gid) => gid, + None => { + let gid = super::get_effective_gid(); + self.egid = Some(gid); + gid + } + } + } + + fn get_effective_groupname(&mut self) -> Option { + let gid = self.get_effective_gid(); + self.get_group_by_gid(gid).map(|g| g.name) + } + + fn get_effective_uid(&mut self) -> uid_t { + match self.euid { + Some(uid) => uid, + None => { + let uid = super::get_effective_uid(); + self.euid = Some(uid); + uid + } + } + } + + fn get_effective_username(&mut self) -> Option { + let uid = self.get_effective_uid(); + self.get_user_by_uid(uid).map(|u| u.name) + } +}