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) + } +}