Skip to content

Commit 87c2d8d

Browse files
committed
unistd: Add getgrouplist()
1 parent 5aceb17 commit 87c2d8d

File tree

1 file changed

+64
-0
lines changed

1 file changed

+64
-0
lines changed

src/unistd.rs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1070,6 +1070,70 @@ pub fn setgroups(groups: &[Gid]) -> Result<()> {
10701070
Errno::result(res).map(|_| ())
10711071
}
10721072

1073+
/// Calculate the supplementary group access list. Gets the group IDs of all
1074+
/// groups that `user` is a member of. The additional group `group` is also
1075+
/// added to the list.
1076+
///
1077+
/// [Further reading](http://man7.org/linux/man-pages/man3/getgrouplist.3.html)
1078+
///
1079+
/// # Errors
1080+
///
1081+
/// Although the `getgrouplist()` call does not return any specific
1082+
/// errors on any known platforms, this implementation will return a system
1083+
/// error of `EINVAL` if the number of groups to be fetched exceeds the
1084+
/// `NGROUPS_MAX` sysconf value. This mimics the behaviour of `getgroups()`
1085+
/// and `setgroups()`. Additionally, while some implementations will return a
1086+
/// partial list of groups when `NGROUPS_MAX` is exceeded, this implementation
1087+
/// will only ever return the complete list or else an error.
1088+
pub fn getgrouplist(user: &CString, group: Gid) -> Result<Vec<Gid>> {
1089+
let ngroups_max = match sysconf(SysconfVar::NGROUPS_MAX) {
1090+
Ok(Some(n)) => n as c_int,
1091+
Ok(None) | Err(_) => <c_int>::max_value(),
1092+
};
1093+
use std::cmp::min;
1094+
let mut ngroups = min(ngroups_max, 8);
1095+
let mut groups = Vec::<Gid>::with_capacity(ngroups as usize);
1096+
cfg_if! {
1097+
if #[cfg(any(target_os = "ios", target_os = "macos"))] {
1098+
type getgrouplist_group_t = c_int;
1099+
} else {
1100+
type getgrouplist_group_t = gid_t;
1101+
}
1102+
}
1103+
let gid: gid_t = group.into();
1104+
loop {
1105+
let ret = unsafe {
1106+
libc::getgrouplist(user.as_ptr(),
1107+
gid as getgrouplist_group_t,
1108+
groups.as_mut_ptr() as *mut getgrouplist_group_t,
1109+
&mut ngroups)
1110+
};
1111+
// BSD systems only return 0 or -1, Linux returns ngroups on success.
1112+
if ret >= 0 {
1113+
unsafe { groups.set_len(ngroups as usize) };
1114+
return Ok(groups);
1115+
} else if ret == -1 {
1116+
// Returns -1 if ngroups is too small, but does not set errno.
1117+
// BSD systems will still fill the groups buffer with as many
1118+
// groups as possible, but Linux manpages do not mention this
1119+
// behavior.
1120+
1121+
let cap = groups.capacity();
1122+
if cap >= ngroups_max as usize {
1123+
// We already have the largest capacity we can, give up
1124+
return Err(Error::invalid_argument());
1125+
}
1126+
1127+
// Reserve space for at least ngroups
1128+
groups.reserve(ngroups as usize);
1129+
1130+
// Even if the buffer gets resized to bigger than ngroups_max,
1131+
// don't ever ask for more than ngroups_max groups
1132+
ngroups = min(ngroups_max, groups.capacity() as c_int);
1133+
}
1134+
}
1135+
}
1136+
10731137
/// Initialize the supplementary group access list. Sets the supplementary
10741138
/// group IDs for the calling process using all groups that `user` is a member
10751139
/// of. The additional group `group` is also added to the list.

0 commit comments

Comments
 (0)