Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 9 additions & 40 deletions src/uucore/src/lib/features/entries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
#[cfg(any(target_os = "freebsd", target_vendor = "apple"))]
use libc::time_t;
use libc::{c_char, c_int, gid_t, uid_t};
use libc::{getgrgid, getgrnam, getgroups};
use libc::{getgrgid, getgrnam};
use libc::{getpwnam, getpwuid, group, passwd};

use std::ffi::{CStr, CString};
Expand All @@ -57,42 +57,6 @@ unsafe extern "C" {
) -> c_int;
}

/// From: `<https://man7.org/linux/man-pages/man2/getgroups.2.html>`
/// > getgroups() returns the supplementary group IDs of the calling
/// > process in list.
/// > If size is zero, list is not modified, but the total number of
/// > supplementary group IDs for the process is returned. This allows
/// > the caller to determine the size of a dynamically allocated list
/// > to be used in a further call to getgroups().
pub fn get_groups() -> IOResult<Vec<gid_t>> {
let mut groups = Vec::new();
loop {
let ngroups = match unsafe { getgroups(0, ptr::null_mut()) } {
-1 => return Err(IOError::last_os_error()),
// Not just optimization; 0 would mess up the next call
0 => return Ok(Vec::new()),
n => n,
};

// This is a small buffer, so we can afford to zero-initialize it and
// use safe Vec operations
groups.resize(ngroups.try_into().unwrap(), 0);
let res = unsafe { getgroups(ngroups, groups.as_mut_ptr()) };
if res == -1 {
let err = IOError::last_os_error();
if err.raw_os_error() == Some(libc::EINVAL) {
// Number of groups has increased, retry
} else {
return Err(err);
}
} else {
// Number of groups may have decreased
groups.truncate(res.try_into().unwrap());
return Ok(groups);
}
}
}

/// The list of group IDs returned from GNU's `groups` and GNU's `id --groups`
/// starts with the effective group ID (egid).
/// This is a wrapper for `get_groups()` to mimic this behavior.
Expand All @@ -116,8 +80,9 @@ pub fn get_groups() -> IOResult<Vec<gid_t>> {
/// > history of a process and its parents could affect the details of
/// > the result.)
#[cfg(all(unix, not(target_os = "redox"), feature = "process"))]
pub fn get_groups_gnu(arg_id: Option<u32>) -> IOResult<Vec<gid_t>> {
let groups = get_groups()?;
pub fn get_groups_gnu(arg_id: Option<u32>) -> IOResult<Vec<rustix::process::RawGid>> {
let groups = rustix::process::getgroups()
.map(|g| g.into_iter().map(rustix::fs::Gid::as_raw).collect())?;
let egid = arg_id.unwrap_or_else(crate::features::process::getegid);
Ok(sort_groups(groups, egid))
}
Expand Down Expand Up @@ -383,7 +348,11 @@ mod test {

#[test]
fn test_entries_get_groups_gnu() {
if let Ok(mut groups) = get_groups() {
if let Ok(mut groups) = rustix::process::getgroups().map(|g| {
g.into_iter()
.map(rustix::fs::Gid::as_raw)
.collect::<Vec<_>>()
}) {
if let Some(last) = groups.pop() {
groups.insert(0, last);
assert_eq!(get_groups_gnu(Some(last)).unwrap(), groups);
Expand Down
Loading