Skip to content

Commit 56c0167

Browse files
committed
unistd: Add getgrouplist()
1 parent 7aee80a commit 56c0167

File tree

2 files changed

+62
-1
lines changed

2 files changed

+62
-1
lines changed

src/unistd.rs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1010,6 +1010,64 @@ pub fn setgroups(groups: &[Gid]) -> Result<()> {
10101010
Errno::result(res).map(drop)
10111011
}
10121012

1013+
/// Calculate the supplementary group access list. Gets the group IDs of all
1014+
/// groups that `user` is a member of. The additional group `group` is also
1015+
/// added to the list.
1016+
///
1017+
/// [Further reading](http://man7.org/linux/man-pages/man3/getgrouplist.3.html)
1018+
pub fn getgrouplist(user: &CString, group: Gid) -> Result<Vec<Gid>> {
1019+
let ngroups_max = match sysconf(SysconfVar::NGROUPS_MAX) {
1020+
Ok(Some(n)) => n as c_int,
1021+
Ok(None) | Err(_) => <c_int>::max_value(),
1022+
};
1023+
use std::cmp::min;
1024+
let mut ngroups = min(ngroups_max, 8);
1025+
let mut groups = Vec::<Gid>::with_capacity(ngroups as usize);
1026+
cfg_if! {
1027+
if #[cfg(any(target_os = "ios", target_os = "macos"))] {
1028+
type getgrouplist_group_t = c_int;
1029+
} else {
1030+
type getgrouplist_group_t = gid_t;
1031+
}
1032+
}
1033+
let gid: gid_t = group.into();
1034+
loop {
1035+
let ret = unsafe {
1036+
libc::getgrouplist(user.as_ptr(),
1037+
gid as getgrouplist_group_t,
1038+
groups.as_mut_ptr() as *mut getgrouplist_group_t,
1039+
&mut ngroups)
1040+
};
1041+
// BSD systems only return 0 or -1, Linux returns ngroups on success.
1042+
if ret >= 0 {
1043+
unsafe { groups.set_len(ngroups as usize) };
1044+
break
1045+
}
1046+
1047+
// Returns -1 if ngroups is too small, but does not set errno.
1048+
// BSD systems will still fill the groups buffer with as many groups
1049+
// as possible, but Linux manpages do not mention this behavior.
1050+
if ret == -1 {
1051+
let cap = groups.capacity();
1052+
if cap >= ngroups_max as usize {
1053+
// We already have the largest capacity we can, give up
1054+
// FIXME: What error should be returned?
1055+
return Err(Error::invalid_argument())
1056+
}
1057+
1058+
// Trigger buffer resizing
1059+
unsafe { groups.set_len(cap) };
1060+
groups.reserve(ngroups as usize - cap);
1061+
1062+
// Even if the buffer gets resized to bigger than ngroups_max,
1063+
// don't ever ask for more than ngroups_max groups
1064+
ngroups = min(ngroups_max, groups.capacity() as c_int);
1065+
}
1066+
}
1067+
1068+
Ok(groups)
1069+
}
1070+
10131071
/// Initialize the supplementary group access list. Sets the supplementary
10141072
/// group IDs for the calling process using all groups that `user` is a member
10151073
/// of. The additional group `group` is also added to the list.

test/test_unistd.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,10 +143,13 @@ fn test_initgroups() {
143143
// FIXME: This only tests half of initgroups' functionality.
144144
let user = CString::new("root").unwrap();
145145
let group = Gid::from_raw(123);
146+
let group_list = getgrouplist(&user, group).unwrap();
147+
assert!(group_list.contains(&group));
148+
146149
initgroups(&user, group).unwrap();
147150

148151
let new_groups = getgroups().unwrap();
149-
assert!(new_groups.contains(&group));
152+
assert_eq!(new_groups, group_list);
150153

151154
// Revert back to the old groups
152155
setgroups(&old_groups).unwrap();

0 commit comments

Comments
 (0)