Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add getpwnam and related functions #864

Closed
wants to merge 17 commits into from
Closed
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
53 changes: 23 additions & 30 deletions src/unistd.rs
Expand Up @@ -30,7 +30,7 @@ pub use self::usergroupiter::*;
///
/// Newtype pattern around `uid_t` (which is just alias). It prevents bugs caused by accidentally
/// passing wrong value.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Default)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct Uid(uid_t);

impl Uid {
Expand Down Expand Up @@ -79,7 +79,7 @@ pub const ROOT: Uid = Uid(0);
///
/// Newtype pattern around `gid_t` (which is just alias). It prevents bugs caused by accidentally
/// passing wrong value.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Default)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct Gid(gid_t);

impl Gid {
Expand Down Expand Up @@ -1336,32 +1336,34 @@ pub fn setgid(gid: Gid) -> Result<()> {
#[cfg(not(any(target_os = "ios", target_os = "macos")))]
pub fn getgroups() -> Result<Vec<Gid>> {
// First get the number of groups so we can size our Vec
let ret = unsafe { libc::getgroups(0, ptr::null_mut()) };
let ngroups = unsafe { libc::getgroups(0, ptr::null_mut()) };
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I meant is that it would be more idiomatic to write it like this:

let ngroups = unsafe { libc::getgroups(0, ptr::null_mut()) }? as usize;

let ngroups_max = match sysconf(SysconfVar::NGROUPS_MAX) {
Ok(Some(n)) => n as usize,
Ok(None) | Err(_) => <usize>::max_value(),
};

// Now actually get the groups. We try multiple times in case the number of
// groups has changed since the first call to getgroups() and the buffer is
// now too small.
let mut groups = Vec::<Gid>::with_capacity(Errno::result(ret)? as usize);
let mut groups = Vec::<Gid>::with_capacity(Errno::result(ngroups)? as usize);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the code would be more readable if you moved Errno::result(...)? closer to the call site on line 1339.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reworked. Also, the 16Kb was taken from https://linux.die.net/man/3/getpwuid_r example code.

loop {
// FIXME: On the platforms we currently support, the `Gid` struct has
// the same representation in memory as a bare `gid_t`. This is not
// necessarily the case on all Rust platforms, though. See RFC 1785.
let ret = unsafe {
let ngroups = unsafe {
libc::getgroups(groups.capacity() as c_int, groups.as_mut_ptr() as *mut gid_t)
};

match Errno::result(ret) {
match Errno::result(ngroups) {
Ok(s) => {
unsafe { groups.set_len(s as usize) };
return Ok(groups);
},
Err(Error::Sys(Errno::EINVAL)) => {
// EINVAL indicates that the buffer size was too small. Trigger
// the internal buffer resizing logic of `Vec` by requiring
// more space than the current capacity.
let cap = groups.capacity();
unsafe { groups.set_len(cap) };
groups.reserve(1);
// EINVAL indicates that the buffer size was too
// small. Trigger the internal buffer resizing logic
reserve_double_buffer_size(&mut groups, ngroups_max)
.or(Err(Error::Sys(Errno::EINVAL)))?;
},
Err(e) => return Err(e)
}
Expand Down Expand Up @@ -1479,19 +1481,8 @@ pub fn getgrouplist(user: &CStr, group: Gid) -> Result<Vec<Gid>> {
// BSD systems will still fill the groups buffer with as many
// groups as possible, but Linux manpages do not mention this
// behavior.

let cap = groups.capacity();
if cap >= ngroups_max as usize {
// We already have the largest capacity we can, give up
return Err(Error::invalid_argument());
}

// Reserve space for at least ngroups
groups.reserve(ngroups as usize);

// 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);
reserve_double_buffer_size(&mut groups, ngroups_max as usize)
.or(Err(Error::invalid_argument()))?;
}
}
}
Expand Down Expand Up @@ -2423,7 +2414,7 @@ const PWGRP_BUFSIZE: usize = 1024;
/// fields are based on the user's locale, which could be non-UTF8, while other fields are
/// guaranteed to conform to [`NAME_REGEX`](https://serverfault.com/a/73101/407341), which only
/// contains ASCII.
#[derive(Debug, Clone, PartialEq, Default)]
#[derive(Debug, Clone, PartialEq)]
pub struct User {
/// Username
pub name: String,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why make some fields String if others must be CString? The inconsistency is weird.

Copy link
Author

@ctrlcctrlv ctrlcctrlv Mar 8, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because it's guaranteed that the fields I made String will conform to NAME_REGEX. gecos ("real name") has no such guarantee.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh, makes sense. Could you add a comment to that effect?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this seems done as well since d4d5a00.

Expand Down Expand Up @@ -2482,9 +2473,10 @@ impl User {
libc::size_t,
*mut *mut libc::passwd) -> libc::c_int
{
let buflimit = 16384;
let bufsize = match sysconf(SysconfVar::GETPW_R_SIZE_MAX) {
Ok(Some(n)) => n as usize,
Ok(None) | Err(_) => 1024 as usize,
Ok(None) | Err(_) => buflimit as usize,
};

let mut cbuf = Vec::with_capacity(bufsize);
Expand All @@ -2506,7 +2498,7 @@ impl User {
}
} else if Errno::last() == Errno::ERANGE {
// Trigger the internal buffer resizing logic.
reserve_double_buffer_size(&mut cbuf, bufsize)?;
reserve_double_buffer_size(&mut cbuf, buflimit)?;
} else {
return Err(Error::Sys(Errno::last()));
}
Expand Down Expand Up @@ -2600,9 +2592,10 @@ impl Group {
libc::size_t,
*mut *mut libc::group) -> libc::c_int
{
let buflimit = 16384;
let bufsize = match sysconf(SysconfVar::GETGR_R_SIZE_MAX) {
Ok(Some(n)) => n as usize,
Ok(None) | Err(_) => 1024 as usize,
Ok(None) | Err(_) => buflimit as usize,
};

let mut cbuf = Vec::with_capacity(bufsize);
Expand All @@ -2624,7 +2617,7 @@ impl Group {
}
} else if Errno::last() == Errno::ERANGE {
// Trigger the internal buffer resizing logic.
reserve_double_buffer_size(&mut cbuf, bufsize)?;
reserve_double_buffer_size(&mut cbuf, buflimit)?;
} else {
return Err(Error::Sys(Errno::last()));
}
Expand Down