Skip to content

Commit

Permalink
unistd: Add getgrouplist()
Browse files Browse the repository at this point in the history
  • Loading branch information
JayH5 committed Aug 13, 2017
1 parent 7aee80a commit 56c0167
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 1 deletion.
58 changes: 58 additions & 0 deletions src/unistd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1010,6 +1010,64 @@ pub fn setgroups(groups: &[Gid]) -> Result<()> {
Errno::result(res).map(drop)
}

/// Calculate the supplementary group access list. Gets the group IDs of all
/// groups that `user` is a member of. The additional group `group` is also
/// added to the list.
///
/// [Further reading](http://man7.org/linux/man-pages/man3/getgrouplist.3.html)
pub fn getgrouplist(user: &CString, group: Gid) -> Result<Vec<Gid>> {
let ngroups_max = match sysconf(SysconfVar::NGROUPS_MAX) {
Ok(Some(n)) => n as c_int,
Ok(None) | Err(_) => <c_int>::max_value(),
};
use std::cmp::min;
let mut ngroups = min(ngroups_max, 8);
let mut groups = Vec::<Gid>::with_capacity(ngroups as usize);
cfg_if! {
if #[cfg(any(target_os = "ios", target_os = "macos"))] {
type getgrouplist_group_t = c_int;
} else {
type getgrouplist_group_t = gid_t;
}
}
let gid: gid_t = group.into();
loop {
let ret = unsafe {
libc::getgrouplist(user.as_ptr(),
gid as getgrouplist_group_t,
groups.as_mut_ptr() as *mut getgrouplist_group_t,
&mut ngroups)
};
// BSD systems only return 0 or -1, Linux returns ngroups on success.
if ret >= 0 {
unsafe { groups.set_len(ngroups as usize) };
break
}

// Returns -1 if ngroups is too small, but does not set errno.
// BSD systems will still fill the groups buffer with as many groups
// as possible, but Linux manpages do not mention this behavior.
if ret == -1 {
let cap = groups.capacity();
if cap >= ngroups_max as usize {
// We already have the largest capacity we can, give up
// FIXME: What error should be returned?
return Err(Error::invalid_argument())
}

// Trigger buffer resizing
unsafe { groups.set_len(cap) };
groups.reserve(ngroups as usize - cap);

// Even if the buffer gets resized to bigger than ngroups_max,
// don't ever ask for more than ngroups_max groups
ngroups = min(ngroups_max, groups.capacity() as c_int);
}
}

Ok(groups)
}

/// Initialize the supplementary group access list. Sets the supplementary
/// group IDs for the calling process using all groups that `user` is a member
/// of. The additional group `group` is also added to the list.
Expand Down
5 changes: 4 additions & 1 deletion test/test_unistd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,10 +143,13 @@ fn test_initgroups() {
// FIXME: This only tests half of initgroups' functionality.
let user = CString::new("root").unwrap();
let group = Gid::from_raw(123);
let group_list = getgrouplist(&user, group).unwrap();
assert!(group_list.contains(&group));

initgroups(&user, group).unwrap();

let new_groups = getgroups().unwrap();
assert!(new_groups.contains(&group));
assert_eq!(new_groups, group_list);

// Revert back to the old groups
setgroups(&old_groups).unwrap();
Expand Down

0 comments on commit 56c0167

Please sign in to comment.