Skip to content

Commit 4a2d65e

Browse files
committed
unistd: Add getgrouplist()
1 parent 7263c5a commit 4a2d65e

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
@@ -1135,6 +1135,70 @@ pub fn setgroups(groups: &[Gid]) -> Result<()> {
11351135
Errno::result(res).map(|_| ())
11361136
}
11371137

1138+
/// Calculate the supplementary group access list. Gets the group IDs of all
1139+
/// groups that `user` is a member of. The additional group `group` is also
1140+
/// added to the list.
1141+
///
1142+
/// [Further reading](http://man7.org/linux/man-pages/man3/getgrouplist.3.html)
1143+
///
1144+
/// # Errors
1145+
///
1146+
/// Although the `getgrouplist()` call does not return any specific
1147+
/// errors on any known platforms, this implementation will return a system
1148+
/// error of `EINVAL` if the number of groups to be fetched exceeds the
1149+
/// `NGROUPS_MAX` sysconf value. This mimics the behaviour of `getgroups()`
1150+
/// and `setgroups()`. Additionally, while some implementations will return a
1151+
/// partial list of groups when `NGROUPS_MAX` is exceeded, this implementation
1152+
/// will only ever return the complete list or else an error.
1153+
pub fn getgrouplist(user: &CString, group: Gid) -> Result<Vec<Gid>> {
1154+
let ngroups_max = match sysconf(SysconfVar::NGROUPS_MAX) {
1155+
Ok(Some(n)) => n as c_int,
1156+
Ok(None) | Err(_) => <c_int>::max_value(),
1157+
};
1158+
use std::cmp::min;
1159+
let mut ngroups = min(ngroups_max, 8);
1160+
let mut groups = Vec::<Gid>::with_capacity(ngroups as usize);
1161+
cfg_if! {
1162+
if #[cfg(any(target_os = "ios", target_os = "macos"))] {
1163+
type getgrouplist_group_t = c_int;
1164+
} else {
1165+
type getgrouplist_group_t = gid_t;
1166+
}
1167+
}
1168+
let gid: gid_t = group.into();
1169+
loop {
1170+
let ret = unsafe {
1171+
libc::getgrouplist(user.as_ptr(),
1172+
gid as getgrouplist_group_t,
1173+
groups.as_mut_ptr() as *mut getgrouplist_group_t,
1174+
&mut ngroups)
1175+
};
1176+
// BSD systems only return 0 or -1, Linux returns ngroups on success.
1177+
if ret >= 0 {
1178+
unsafe { groups.set_len(ngroups as usize) };
1179+
return Ok(groups);
1180+
} else if ret == -1 {
1181+
// Returns -1 if ngroups is too small, but does not set errno.
1182+
// BSD systems will still fill the groups buffer with as many
1183+
// groups as possible, but Linux manpages do not mention this
1184+
// behavior.
1185+
1186+
let cap = groups.capacity();
1187+
if cap >= ngroups_max as usize {
1188+
// We already have the largest capacity we can, give up
1189+
return Err(Error::invalid_argument());
1190+
}
1191+
1192+
// Reserve space for at least ngroups
1193+
groups.reserve(ngroups as usize);
1194+
1195+
// Even if the buffer gets resized to bigger than ngroups_max,
1196+
// don't ever ask for more than ngroups_max groups
1197+
ngroups = min(ngroups_max, groups.capacity() as c_int);
1198+
}
1199+
}
1200+
}
1201+
11381202
/// Initialize the supplementary group access list. Sets the supplementary
11391203
/// group IDs for the calling process using all groups that `user` is a member
11401204
/// of. The additional group `group` is also added to the list.

0 commit comments

Comments
 (0)