-
Notifications
You must be signed in to change notification settings - Fork 680
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
Changes from 4 commits
0846f15
072c08d
16f3cf5
a8143fb
de1ed63
51d39cf
3b75439
b3d7716
b361db0
76e33fe
5eda92f
d0b23f3
56b0cd1
cc9f81c
65ba0e5
9111d12
3fe2c01
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2435,8 +2435,8 @@ pub struct User { | |
pub expire: libc::time_t | ||
} | ||
|
||
impl From<*mut libc::passwd> for User { | ||
fn from(pw: *mut libc::passwd) -> User { | ||
impl From<&libc::passwd> for User { | ||
fn from(pw: &libc::passwd) -> User { | ||
unsafe { | ||
User { | ||
name: CStr::from_ptr((*pw).pw_name).to_string_lossy().into_owned(), | ||
|
@@ -2459,6 +2459,45 @@ impl From<*mut libc::passwd> for User { | |
} | ||
|
||
impl User { | ||
fn from_anything(f: impl Fn(*mut libc::passwd, | ||
*mut libc::c_char, | ||
libc::size_t, | ||
*mut *mut libc::passwd) -> libc::c_int) | ||
-> Result<Option<Self>> | ||
{ | ||
let bufsize = match sysconf(SysconfVar::GETPW_R_SIZE_MAX) { | ||
Ok(Some(n)) => n as usize, | ||
Ok(None) | Err(_) => 1024 as usize, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not that it's defined in any publicly visible place, but FreeBSD internally sets this limit to 1MB. |
||
}; | ||
|
||
let mut cbuf = Vec::with_capacity(bufsize); | ||
let mut pwd = mem::MaybeUninit::<libc::passwd>::uninit(); | ||
let mut res = ptr::null_mut(); | ||
|
||
loop { | ||
let error = unsafe { | ||
Errno::clear(); | ||
f(pwd.as_mut_ptr(), cbuf.as_mut_ptr(), cbuf.capacity(), &mut res) | ||
}; | ||
|
||
if error == 0 { | ||
if res.is_null() { | ||
return Ok(None); | ||
} else { | ||
let pwd = unsafe { pwd.assume_init() }; | ||
return Ok(Some(User::from(&pwd))); | ||
} | ||
} else if Errno::last() == Errno::ERANGE { | ||
// Trigger the internal buffer resizing logic of `Vec` by requiring | ||
// more space than the current capacity. | ||
unsafe { cbuf.set_len(cbuf.capacity()); } | ||
cbuf.reserve(1); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Extending by only one byte at a time could take awhile. It would be better to double the storage instead. That's what the C library does. Also, you shouldn't extend the buffer ad infinitum. At some point you should give up and return There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we can add a top limit, however, the same strategy is used inside Also, inside There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The link from inside of Vec is very different. In that case, it really is only 1 additional element's worth of space that is needed. Here, an unknown amount of additional space is needed. That's why I suggest doubling the storage. Nix's getcwd() method should probably do the same. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
From reading the std code, it seems different. See: and So it seems todo exactly this; it seems good to keep the code simple but if really desired, I can rework this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's just the current implementation. Nix shouldn't rely upon it. We should actually try to reserve a reasonable amount of space for what our application needs. |
||
} else { | ||
return Err(Error::Sys(Errno::last())); | ||
} | ||
} | ||
} | ||
|
||
/// Get a user by UID. | ||
/// | ||
/// Internally, this function calls | ||
|
@@ -2468,35 +2507,14 @@ impl User { | |
/// | ||
/// ``` | ||
/// use nix::unistd::{Uid, User}; | ||
/// // Returns an Option<Result<User>>, thus the double unwrap. | ||
/// let res = User::from_uid(Uid::from_raw(0), Some(2048)).unwrap().unwrap(); | ||
/// // Returns an Result<Option<User>>, thus the double unwrap. | ||
/// let res = User::from_uid(Uid::from_raw(0)).unwrap().unwrap(); | ||
/// assert!(res.name == "root"); | ||
/// ``` | ||
pub fn from_uid(uid: Uid, bufsize: Option<usize>) -> Option<Result<Self>> { | ||
let mut cbuf = Vec::with_capacity(bufsize.unwrap_or(PWGRP_BUFSIZE)); | ||
let mut pwd = mem::MaybeUninit::<libc::passwd>::uninit(); | ||
let mut res = ptr::null_mut(); | ||
|
||
let error = unsafe { | ||
Errno::clear(); | ||
libc::getpwuid_r( | ||
uid.0, | ||
pwd.as_mut_ptr(), | ||
cbuf.as_mut_ptr(), | ||
cbuf.capacity(), | ||
&mut res | ||
) | ||
}; | ||
|
||
if error == 0 { | ||
if ! res.is_null() { | ||
Some(Ok(User::from(res))) | ||
} else { | ||
None | ||
} | ||
} else { | ||
Some(Err(Error::Sys(Errno::last()))) | ||
} | ||
pub fn from_uid(uid: Uid) -> Result<Option<Self>> { | ||
User::from_anything(|pwd, cbuf, cap, res| { | ||
unsafe { libc::getpwuid_r(uid.0, pwd, cbuf, cap, res) } | ||
}) | ||
} | ||
|
||
/// Get a user by name. | ||
|
@@ -2508,35 +2526,15 @@ impl User { | |
/// | ||
/// ``` | ||
/// use nix::unistd::User; | ||
/// // Returns an Option<Result<User>>, thus the double unwrap. | ||
/// let res = User::from_name("root", Some(2048)).unwrap().unwrap(); | ||
/// // Returns an Result<Option<User>>, thus the double unwrap. | ||
/// let res = User::from_name("root").unwrap().unwrap(); | ||
/// assert!(res.name == "root"); | ||
/// ``` | ||
pub fn from_name(name: &str, bufsize: Option<usize>) -> Option<Result<Self>> { | ||
let mut cbuf = Vec::with_capacity(bufsize.unwrap_or(PWGRP_BUFSIZE)); | ||
let mut pwd = mem::MaybeUninit::<libc::passwd>::uninit(); | ||
let mut res = ptr::null_mut(); | ||
|
||
let error = unsafe { | ||
Errno::clear(); | ||
libc::getpwnam_r( | ||
CString::new(name).unwrap().as_ptr(), | ||
pwd.as_mut_ptr(), | ||
cbuf.as_mut_ptr(), | ||
cbuf.capacity(), | ||
&mut res | ||
) | ||
}; | ||
|
||
if error == 0 { | ||
if ! res.is_null() { | ||
Some(Ok(User::from(res))) | ||
} else { | ||
None | ||
} | ||
} else { | ||
Some(Err(Error::Sys(Errno::last()))) | ||
} | ||
pub fn from_name(name: &str) -> Result<Option<Self>> { | ||
let name = CString::new(name).unwrap(); | ||
User::from_anything(|pwd, cbuf, cap, res| { | ||
unsafe { libc::getpwnam_r(name.as_ptr(), pwd, cbuf, cap, res) } | ||
}) | ||
} | ||
} | ||
|
||
|
@@ -2551,8 +2549,8 @@ pub struct Group { | |
pub mem: Vec<String> | ||
} | ||
|
||
impl From<*mut libc::group> for Group { | ||
fn from(gr: *mut libc::group) -> Group { | ||
impl From<&libc::group> for Group { | ||
fn from(gr: &libc::group) -> Group { | ||
unsafe { | ||
Group { | ||
name: CStr::from_ptr((*gr).gr_name).to_string_lossy().into_owned(), | ||
|
@@ -2580,6 +2578,45 @@ impl Group { | |
ret | ||
} | ||
|
||
fn from_anything(f: impl Fn(*mut libc::group, | ||
*mut libc::c_char, | ||
libc::size_t, | ||
*mut *mut libc::group) -> libc::c_int) | ||
-> Result<Option<Self>> | ||
{ | ||
let bufsize = match sysconf(SysconfVar::GETGR_R_SIZE_MAX) { | ||
Ok(Some(n)) => n as usize, | ||
Ok(None) | Err(_) => 1024 as usize, | ||
}; | ||
|
||
let mut cbuf = Vec::with_capacity(bufsize); | ||
let mut grp = mem::MaybeUninit::<libc::group>::uninit(); | ||
let mut res = ptr::null_mut(); | ||
|
||
loop { | ||
let error = unsafe { | ||
Errno::clear(); | ||
f(grp.as_mut_ptr(), cbuf.as_mut_ptr(), cbuf.capacity(), &mut res) | ||
}; | ||
|
||
if error == 0 { | ||
if res.is_null() { | ||
return Ok(None); | ||
} else { | ||
let grp = unsafe { grp.assume_init() }; | ||
return Ok(Some(Group::from(&grp))); | ||
} | ||
} else if Errno::last() == Errno::ERANGE { | ||
// Trigger the internal buffer resizing logic of `Vec` by requiring | ||
// more space than the current capacity. | ||
unsafe { cbuf.set_len(cbuf.capacity()); } | ||
cbuf.reserve(1); | ||
} else { | ||
return Err(Error::Sys(Errno::last())); | ||
} | ||
} | ||
} | ||
|
||
/// Get a group by GID. | ||
/// | ||
/// Internally, this function calls | ||
|
@@ -2591,35 +2628,14 @@ impl Group { | |
#[cfg_attr(not(target_os = "linux"), doc = " ```no_run")] | ||
#[cfg_attr(target_os = "linux", doc = " ```")] | ||
/// use nix::unistd::{Gid, Group}; | ||
/// // Returns an Option<Result<Group>>, thus the double unwrap. | ||
/// let res = Group::from_gid(Gid::from_raw(0), Some(2048)).unwrap().unwrap(); | ||
/// // Returns an Result<Option<Group>>, thus the double unwrap. | ||
/// let res = Group::from_gid(Gid::from_raw(0)).unwrap().unwrap(); | ||
/// assert!(res.name == "root"); | ||
/// ``` | ||
pub fn from_gid(gid: Gid, bufsize: Option<usize>) -> Option<Result<Self>> { | ||
let mut cbuf = Vec::with_capacity(bufsize.unwrap_or(PWGRP_BUFSIZE)); | ||
let mut grp = mem::MaybeUninit::<libc::group>::uninit(); | ||
let mut res = ptr::null_mut(); | ||
|
||
let error = unsafe { | ||
Errno::clear(); | ||
libc::getgrgid_r( | ||
gid.0, | ||
grp.as_mut_ptr(), | ||
cbuf.as_mut_ptr(), | ||
cbuf.capacity(), | ||
&mut res | ||
) | ||
}; | ||
|
||
if error == 0 { | ||
if !res.is_null() { | ||
Some(Ok(Group::from(res))) | ||
} else { | ||
None | ||
} | ||
} else { | ||
Some(Err(Error::Sys(Errno::last()))) | ||
} | ||
pub fn from_gid(gid: Gid) -> Result<Option<Self>> { | ||
Group::from_anything(|grp, cbuf, cap, res| { | ||
unsafe { libc::getgrgid_r(gid.0, grp, cbuf, cap, res) } | ||
}) | ||
} | ||
|
||
/// Get a group by name. | ||
|
@@ -2633,35 +2649,15 @@ impl Group { | |
#[cfg_attr(not(target_os = "linux"), doc = " ```no_run")] | ||
#[cfg_attr(target_os = "linux", doc = " ```")] | ||
/// use nix::unistd::Group; | ||
/// // Returns an Option<Result<Group>>, thus the double unwrap. | ||
/// let res = Group::from_name("root", Some(2048)).unwrap().unwrap(); | ||
/// // Returns an Result<Option<Group>>, thus the double unwrap. | ||
/// let res = Group::from_name("root").unwrap().unwrap(); | ||
/// assert!(res.name == "root"); | ||
/// ``` | ||
pub fn from_name(name: &str, bufsize: Option<usize>) -> Option<Result<Self>> { | ||
let mut cbuf = Vec::with_capacity(bufsize.unwrap_or(PWGRP_BUFSIZE)); | ||
let mut grp = mem::MaybeUninit::<libc::group>::uninit(); | ||
let mut res = ptr::null_mut(); | ||
|
||
let error = unsafe { | ||
Errno::clear(); | ||
libc::getgrnam_r( | ||
CString::new(name).unwrap().as_ptr(), | ||
grp.as_mut_ptr(), | ||
cbuf.as_mut_ptr(), | ||
cbuf.capacity(), | ||
&mut res | ||
) | ||
}; | ||
|
||
if error == 0 { | ||
if !res.is_null() { | ||
Some(Ok(Group::from(res))) | ||
} else { | ||
None | ||
} | ||
} else { | ||
Some(Err(Error::Sys(Errno::last()))) | ||
} | ||
pub fn from_name(name: &str) -> Result<Option<Self>> { | ||
let name = CString::new(name).unwrap(); | ||
Group::from_anything(|grp, cbuf, cap, res| { | ||
unsafe { libc::getgrnam_r(name.as_ptr(), grp, cbuf, cap, res) } | ||
}) | ||
} | ||
} | ||
|
||
|
@@ -2719,18 +2715,24 @@ mod usergroupiter { | |
impl Iterator for Users { | ||
type Item = Result<User>; | ||
fn next(&mut self) -> Option<Result<User>> { | ||
|
||
let mut cbuf = vec![0 as c_char; self.0]; | ||
let mut pwd: libc::passwd = unsafe { mem::uninitialized() }; | ||
let mut pwd = mem::MaybeUninit::<libc::passwd>::uninit(); | ||
let mut res = ptr::null_mut(); | ||
|
||
let error = unsafe { | ||
Errno::clear(); | ||
libc::getpwent_r(&mut pwd, cbuf.as_mut_ptr(), self.0, &mut res) | ||
libc::getpwent_r( | ||
pwd.as_mut_ptr(), | ||
cbuf.as_mut_ptr(), | ||
self.0, | ||
&mut res | ||
) | ||
}; | ||
|
||
let pwd = unsafe { pwd.assume_init() }; | ||
|
||
if error == 0 && !res.is_null() { | ||
Some(Ok(User::from(res))) | ||
Some(Ok(User::from(&pwd))) | ||
} else if error == libc::ERANGE { | ||
Some(Err(Error::Sys(Errno::last()))) | ||
} else { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think you can discount the possibility of other errors. POSIX lists |
||
|
@@ -2787,18 +2789,24 @@ mod usergroupiter { | |
impl Iterator for Groups { | ||
type Item = Result<Group>; | ||
fn next(&mut self) -> Option<Result<Group>> { | ||
|
||
let mut cbuf = vec![0 as c_char; self.0]; | ||
let mut grp: libc::group = unsafe { mem::uninitialized() }; | ||
let mut grp = mem::MaybeUninit::<libc::group>::uninit(); | ||
let mut res = ptr::null_mut(); | ||
|
||
let error = unsafe { | ||
Errno::clear(); | ||
libc::getgrent_r(&mut grp, cbuf.as_mut_ptr(), self.0, &mut res) | ||
libc::getgrent_r( | ||
grp.as_mut_ptr(), | ||
cbuf.as_mut_ptr(), | ||
self.0, | ||
&mut res | ||
) | ||
}; | ||
|
||
let grp = unsafe { grp.assume_init() }; | ||
|
||
if error == 0 && !res.is_null() { | ||
Some(Ok(Group::from(res))) | ||
Some(Ok(Group::from(&grp))) | ||
} else if error == libc::ERANGE { | ||
Some(Err(Error::Sys(Errno::last()))) | ||
} else { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As above, don't swallow unexpected errnos. |
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This signature is so big that it would probably be written better with a where clause: