use std::ffi::{CStr, CString};
use std::io::{Error, Result};
pub(crate) fn getpwuid_name(uid: u32) -> Result<Option<String>> {
let mut bufsize: usize = 1024;
let mut pwd = std::mem::MaybeUninit::<libc::passwd>::uninit();
let mut result = std::ptr::null_mut::<libc::passwd>();
loop {
let mut buf = vec![0u8; bufsize];
let rc = unsafe {
libc::getpwuid_r(
uid,
pwd.as_mut_ptr(),
buf.as_mut_ptr() as *mut libc::c_char,
buf.len(),
&mut result,
)
};
if rc == libc::ERANGE {
bufsize *= 2;
continue;
}
if rc == libc::ENOENT {
return Ok(None);
}
if rc != 0 {
return Err(Error::last_os_error());
}
if result.is_null() {
return Ok(None);
}
let name = unsafe { core::ffi::CStr::from_ptr((*result).pw_name) };
return Ok(Some(name.to_string_lossy().to_string()));
}
}
pub(crate) fn getgrgid_name(gid: u32) -> Result<Option<String>> {
let mut bufsize: usize = 1024;
let mut group = std::mem::MaybeUninit::<libc::group>::uninit();
let mut result = std::ptr::null_mut::<libc::group>();
loop {
let mut buf = vec![0u8; bufsize];
let rc = unsafe {
libc::getgrgid_r(
gid,
group.as_mut_ptr(),
buf.as_mut_ptr() as *mut libc::c_char,
buf.len(),
&mut result,
)
};
if rc == libc::ERANGE {
bufsize *= 2;
continue;
}
if rc == libc::ENOENT {
return Ok(None);
}
if rc != 0 {
return Err(Error::last_os_error());
}
if result.is_null() {
return Ok(None);
}
let name = unsafe { core::ffi::CStr::from_ptr((*result).gr_name) };
return Ok(Some(name.to_string_lossy().to_string()));
}
}
pub(crate) fn major(dev: u64) -> u32 {
libc::major(dev)
}
pub(crate) fn minor(dev: u64) -> u32 {
libc::minor(dev)
}
pub(crate) fn mknod(pathname: &str, mode: libc::mode_t, major: u32, minor: u32) -> Result<()> {
let p = CString::new(pathname)?;
let rc = unsafe { libc::mknod(p.as_ptr(), mode, libc::makedev(major, minor)) };
if rc != 0 {
return Err(Error::last_os_error());
};
Ok(())
}
pub(crate) fn set_modified(path: &str, mtime: i64) -> Result<()> {
let p = CString::new(path)?;
let mut modified: libc::timespec = unsafe { std::mem::zeroed() };
modified.tv_sec = mtime;
let times = [modified, modified];
let rc = unsafe {
libc::utimensat(
libc::AT_FDCWD,
p.as_ptr(),
times.as_ptr(),
libc::AT_SYMLINK_NOFOLLOW,
)
};
if rc != 0 {
return Err(Error::last_os_error());
};
Ok(())
}
fn strftime(format: &[u8], tm: *mut libc::tm) -> Result<String> {
let mut s = [0u8; 19];
let length = unsafe {
libc::strftime(
s.as_mut_ptr() as *mut libc::c_char,
s.len(),
CStr::from_bytes_with_nul_unchecked(format).as_ptr(),
tm,
)
};
if length == 0 {
return Err(Error::other("strftime returned 0"));
}
Ok(String::from_utf8_lossy(&s[..length]).to_string())
}
pub(crate) fn strftime_local(format: &[u8], timestamp: u32) -> Result<String> {
let mut tm = std::mem::MaybeUninit::<libc::tm>::uninit();
let result = unsafe { libc::localtime_r(×tamp.into(), tm.as_mut_ptr()) };
if result.is_null() {
return Err(Error::last_os_error());
};
strftime(format, result)
}
#[cfg(test)]
pub(crate) mod tests {
use super::*;
use crate::temp_dir::TempDir;
use crate::tests::set_timezone_to_utc;
use std::time::{Duration, SystemTime};
#[test]
fn test_getpwuid_name_root() {
let got = getpwuid_name(0).unwrap();
assert_eq!(got, Some("root".to_string()));
}
#[test]
fn test_getpwuid_name_non_existing() {
let got = getpwuid_name(65520).unwrap();
assert_eq!(got, None);
}
#[test]
fn test_getgrgid_name_root() {
let got = getgrgid_name(0).unwrap();
assert_eq!(got, Some("root".to_string()));
}
#[test]
fn test_getgrgid_name_non_existing() {
let got = getgrgid_name(65520).unwrap();
assert_eq!(got, None);
}
#[test]
fn test_set_modified() {
let dir = TempDir::new().unwrap();
let modified = dir.path.metadata().unwrap().modified().unwrap();
let duration = modified.duration_since(SystemTime::UNIX_EPOCH).unwrap();
let new_modified = SystemTime::UNIX_EPOCH
.checked_add(Duration::new(duration.as_secs() - 10, 0))
.unwrap();
let mtime = new_modified.duration_since(SystemTime::UNIX_EPOCH).unwrap();
let p = dir.path.clone().into_os_string().into_string().unwrap();
set_modified(&p, mtime.as_secs().try_into().unwrap()).unwrap();
assert_eq!(
dir.path.metadata().unwrap().modified().unwrap(),
new_modified
);
}
#[test]
fn test_strftime_local_year() {
let time = strftime_local(b"%b %e %Y\0", 2278410030).unwrap();
assert_eq!(time, "Mar 14 2042");
}
#[test]
fn test_strftime_local_hour() {
set_timezone_to_utc();
let time = strftime_local(b"%b %e %H:%M\0", 1720735264).unwrap();
assert_eq!(time, "Jul 11 22:01");
}
}