#![cfg(unix)]
use std::fs;
use std::os::unix::fs::PermissionsExt;
use std::os::unix::fs::MetadataExt;
use std::os::unix::io::FromRawFd;
use std::os::unix::ffi::OsStrExt;
use std::path::Path;
use std::io;
#[cfg(target_os = "linux")]
use nix::unistd;
use users::{get_user_by_name, get_group_by_name};
pub type PosixResult<T> = Result<T, PosixError>;
#[derive(Debug)]
#[allow(dead_code)]
pub enum PosixError {
Io(io::Error),
InvalidArgument(String),
NotFound,
}
impl From<io::Error> for PosixError {
fn from(err: io::Error) -> Self {
PosixError::Io(err)
}
}
impl From<nix::Error> for PosixError {
fn from(err: nix::Error) -> Self {
PosixError::Io(io::Error::from_raw_os_error(err as i32))
}
}
#[cfg(target_os = "linux")]
pub fn posix_access(path: &str, mode: &str) -> PosixResult<bool> {
let path = Path::new(path);
let mut access_mode = 0;
for ch in mode.chars() {
match ch {
'r' => access_mode |= libc::R_OK,
'w' => access_mode |= libc::W_OK,
'x' => access_mode |= libc::X_OK,
'f' => access_mode |= libc::F_OK,
' ' => continue,
_ => return Err(PosixError::InvalidArgument(format!("unknown mode: {}", ch))),
}
}
if access_mode == 0 {
access_mode = libc::F_OK;
}
use std::ffi::CString;
let path_cstr = CString::new(path.as_os_str().as_bytes())
.map_err(|_| PosixError::InvalidArgument("path contains null byte".to_string()))?;
match unsafe { libc::access(path_cstr.as_ptr(), access_mode) } {
0 => Ok(true),
_ => Ok(false),
}
}
fn mode_munch_parse_who_bits(p: &mut std::iter::Peekable<std::str::Chars<'_>>) -> u32 {
let mut affected_bits = 0u32;
loop {
match p.peek() {
Some(&'u') => {
affected_bits |= 0o4700;
p.next();
}
Some(&'g') => {
affected_bits |= 0o2070;
p.next();
}
Some(&'o') => {
affected_bits |= 0o1007;
p.next();
}
Some(&'a') => {
affected_bits |= 0o7777;
p.next();
}
Some(&' ') => {
p.next();
}
_ => break,
}
}
if affected_bits == 0 {
0o7777
} else {
affected_bits
}
}
fn mode_munch_try_octal_prefix(
mode: &mut u32,
p: &mut std::iter::Peekable<std::str::Chars<'_>>,
) -> Result<bool, PosixError> {
if !matches!(p.peek(), Some(&c) if c >= '0' && c <= '7') {
return Ok(false);
}
let mut octal_str = String::new();
while let Some(&c) = p.peek() {
if c >= '0' && c <= '7' {
octal_str.push(p.next().unwrap());
} else {
break;
}
}
if p.peek().is_some() {
return Err(PosixError::InvalidArgument(
"invalid octal mode: non-octal character found".to_string(),
));
}
if octal_str.is_empty() {
return Err(PosixError::InvalidArgument("invalid octal mode: empty string".to_string()));
}
let mode_num = u32::from_str_radix(&octal_str, 8)
.map_err(|_| PosixError::InvalidArgument(format!("invalid octal mode: {}", octal_str)))?;
*mode = mode_num;
Ok(true)
}
pub fn mode_munch(mode: &mut u32, mode_str: &str) -> Result<(), PosixError> {
let mut p = mode_str.chars().peekable();
let mut done = false;
while !done {
let mut ch_mode = 0u32;
let op;
let affected_bits = mode_munch_parse_who_bits(&mut p);
if let Some(&ch) = p.peek() {
if ch == 'r' || ch == '-' {
return rwxrwxrwx(mode, mode_str);
}
}
if mode_munch_try_octal_prefix(mode, &mut p)? {
return Ok(());
}
match p.next() {
Some('+') => op = Some('+'),
Some('-') => op = Some('-'),
Some('=') => op = Some('='),
Some(' ') => continue,
None => {
break;
}
_ => return Err(PosixError::InvalidArgument("bad operator".to_string())),
}
loop {
match p.peek() {
Some(&'r') => {
ch_mode |= 0o0444;
p.next();
}
Some(&'w') => {
ch_mode |= 0o0222;
p.next();
}
Some(&'x') => {
ch_mode |= 0o0111;
p.next();
}
Some(&'s') => {
ch_mode |= 0o6000;
p.next();
}
Some(&' ') => {
p.next();
}
_ => break,
}
}
if ch_mode != 0 {
match op {
Some('+') => *mode |= ch_mode & affected_bits,
Some('-') => *mode &= !(ch_mode & affected_bits),
Some('=') => *mode = (*mode & !affected_bits) | (ch_mode & affected_bits),
_ => return Err(PosixError::InvalidArgument("bad mode change".to_string())),
}
}
match p.peek() {
Some(&',') => {
p.next();
}
Some(&' ') => {
p.next();
}
None => done = true,
_ => done = true,
}
}
Ok(())
}
fn rwxrwxrwx(mode: &mut u32, mode_str: &str) -> Result<(), PosixError> {
let mut tmp_mode = *mode;
tmp_mode &= !((libc::S_ISUID as u32) | (libc::S_ISGID as u32));
let chars: Vec<char> = mode_str.chars().take(9).collect();
if chars.len() != 9 {
return Err(PosixError::InvalidArgument("rwxrwxrwx format requires 9 characters".to_string()));
}
let modesel = [
('r', libc::S_IRUSR as u32, 0),
('w', libc::S_IWUSR as u32, 1),
('x', libc::S_IXUSR as u32, 2),
('r', libc::S_IRGRP as u32, 3),
('w', libc::S_IWGRP as u32, 4),
('x', libc::S_IXGRP as u32, 5),
('r', libc::S_IROTH as u32, 6),
('w', libc::S_IWOTH as u32, 7),
('x', libc::S_IXOTH as u32, 8),
];
for (i, ch) in chars.iter().enumerate() {
let (expected_ch, bit, pos) = modesel[i];
match *ch {
c if c == expected_ch => tmp_mode |= bit,
'-' => tmp_mode &= !bit,
's' if pos == 2 => {
tmp_mode |= (libc::S_ISUID as u32) | (libc::S_IXUSR as u32);
}
's' if pos == 5 => {
tmp_mode |= (libc::S_ISGID as u32) | (libc::S_IXGRP as u32);
}
_ => return Err(PosixError::InvalidArgument(format!("bad rwxrwxrwx mode change at position {}", i))),
}
}
*mode = tmp_mode;
Ok(())
}
pub fn posix_chmod(path: &str, mode_str: &str) -> PosixResult<()> {
let path = Path::new(path);
let metadata = fs::metadata(path)?;
let mut mode = metadata.mode();
mode_munch(&mut mode, mode_str)?;
mode &= 0o7777;
fs::set_permissions(path, fs::Permissions::from_mode(mode))?;
Ok(())
}
pub fn resolve_user_group_ids(user: Option<&str>, group: Option<&str>) -> (Option<u32>, Option<u32>) {
let uid = if let Some(u) = user {
if let Ok(uid_num) = u.parse::<u32>() {
Some(uid_num)
} else if let Some(user) = get_user_by_name(u) {
Some(user.uid())
} else {
None
}
} else {
None
};
let gid = if let Some(g) = group {
if let Ok(gid_num) = g.parse::<u32>() {
Some(gid_num)
} else if let Some(group) = get_group_by_name(g) {
Some(group.gid())
} else {
None
}
} else {
None
};
(uid, gid)
}
#[cfg(target_os = "linux")]
pub fn posix_chown(path: &str, user: Option<&str>, group: Option<&str>) -> PosixResult<()> {
let (uid, gid) = resolve_user_group_ids(user, group);
let path = Path::new(path);
unistd::chown(path, uid.map(unistd::Uid::from_raw), gid.map(unistd::Gid::from_raw))?;
Ok(())
}
#[derive(Debug, Clone)]
pub struct PosixStat {
pub mode: u32,
pub mode_str: String,
pub ino: u64,
pub dev: u64,
pub nlink: u64,
pub uid: u32,
pub gid: u32,
pub size: u64,
pub atime: u64,
pub mtime: u64,
pub ctime: u64,
pub file_type: String,
}
fn modechopper(mode: u32) -> String {
let mut result = String::with_capacity(9);
let modesel = [
(libc::S_IRUSR as u32, 'r'),
(libc::S_IWUSR as u32, 'w'),
(libc::S_IXUSR as u32, 'x'),
(libc::S_IRGRP as u32, 'r'),
(libc::S_IWGRP as u32, 'w'),
(libc::S_IXGRP as u32, 'x'),
(libc::S_IROTH as u32, 'r'),
(libc::S_IWOTH as u32, 'w'),
(libc::S_IXOTH as u32, 'x'),
];
for (bit, ch) in modesel.iter() {
if mode & *bit != 0 {
result.push(*ch);
} else {
result.push('-');
}
}
let mut chars: Vec<char> = result.chars().collect();
if mode & libc::S_ISUID as u32 != 0 {
chars[2] = if mode & libc::S_IXUSR as u32 != 0 { 's' } else { 'S' };
}
if mode & libc::S_ISGID as u32 != 0 {
chars[5] = if mode & libc::S_IXGRP as u32 != 0 { 's' } else { 'S' };
}
chars.into_iter().collect()
}
fn filetype(mode: u32) -> &'static str {
use libc::{S_IFMT, S_IFREG, S_IFLNK, S_IFDIR, S_IFCHR, S_IFBLK, S_IFIFO, S_IFSOCK};
let file_type = mode & (S_IFMT as u32);
if file_type == S_IFREG as u32 {
"regular"
} else if file_type == S_IFLNK as u32 {
"link"
} else if file_type == S_IFDIR as u32 {
"directory"
} else if file_type == S_IFCHR as u32 {
"character device"
} else if file_type == S_IFBLK as u32 {
"block device"
} else if file_type == S_IFIFO as u32 {
"fifo"
} else if file_type == S_IFSOCK as u32 {
"socket"
} else {
"?"
}
}
pub fn posix_stat(path: &str) -> PosixResult<PosixStat> {
use std::os::unix::fs::MetadataExt;
let metadata = fs::symlink_metadata(path)?;
let mode = metadata.mode();
let file_type = filetype(mode);
Ok(PosixStat {
mode: mode,
mode_str: modechopper(mode),
ino: metadata.ino(),
dev: metadata.dev(),
nlink: metadata.nlink(),
uid: metadata.uid(),
gid: metadata.gid(),
size: metadata.len(),
atime: metadata.accessed()?
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs(),
mtime: metadata.modified()?
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs(),
ctime: metadata.ctime() as u64,
file_type: file_type.to_string(),
})
}
#[cfg(target_os = "linux")]
pub fn posix_umask(mask: Option<&str>) -> PosixResult<String> {
let current_umask = unsafe { libc::umask(0) };
let mode = (!current_umask) & 0o777;
if let Some(mask_str) = mask {
let mut new_mode = mode as u32;
mode_munch(&mut new_mode, mask_str)?;
let new_mode = (new_mode & 0o777) as libc::mode_t;
unsafe { libc::umask(!new_mode) };
Ok(modechopper(new_mode as u32))
} else {
unsafe { libc::umask(current_umask) };
Ok(modechopper(mode as u32))
}
}
#[cfg(target_os = "linux")]
fn call_libc_path_mode<F>(path: &str, mode: libc::mode_t, f: F) -> io::Result<()>
where
F: FnOnce(*const libc::c_char, libc::mode_t) -> libc::c_int,
{
let path_cstr = std::ffi::CString::new(path)
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "path contains null byte"))?;
let result = f(path_cstr.as_ptr(), mode);
if result == 0 {
Ok(())
} else {
Err(io::Error::last_os_error())
}
}
#[cfg(target_os = "linux")]
pub fn posix_mkdir(path: &str) -> io::Result<()> {
call_libc_path_mode(path, 0o777, |p, m| unsafe { libc::mkdir(p, m) })
}
#[cfg(target_os = "linux")]
pub fn posix_mkfifo(path: &str) -> io::Result<()> {
call_libc_path_mode(path, 0o777, |p, m| unsafe { libc::mkfifo(p, m) })
}
#[derive(Debug, Clone)]
pub struct PosixUname {
pub sysname: String,
pub nodename: String,
pub release: String,
pub version: String,
pub machine: String,
}
pub fn posix_uname() -> PosixResult<PosixUname> {
let mut uts = std::mem::MaybeUninit::<libc::utsname>::uninit();
match unsafe { libc::uname(uts.as_mut_ptr()) } {
0 => {
let uts = unsafe { uts.assume_init() };
Ok(PosixUname {
sysname: unsafe { std::ffi::CStr::from_ptr(uts.sysname.as_ptr()).to_string_lossy().to_string() },
nodename: unsafe { std::ffi::CStr::from_ptr(uts.nodename.as_ptr()).to_string_lossy().to_string() },
release: unsafe { std::ffi::CStr::from_ptr(uts.release.as_ptr()).to_string_lossy().to_string() },
version: unsafe { std::ffi::CStr::from_ptr(uts.version.as_ptr()).to_string_lossy().to_string() },
machine: unsafe { std::ffi::CStr::from_ptr(uts.machine.as_ptr()).to_string_lossy().to_string() },
})
}
_ => Err(PosixError::Io(io::Error::last_os_error())),
}
}
#[cfg(target_os = "linux")]
pub fn posix_ttyname(fd: i32) -> Option<String> {
let tty_name = unsafe { libc::ttyname(fd) };
if tty_name.is_null() {
None
} else {
Some(unsafe { std::ffi::CStr::from_ptr(tty_name).to_string_lossy().to_string() })
}
}
#[cfg(target_os = "linux")]
pub fn posix_ctermid() -> String {
let mut buf = vec![0u8; 1024];
let ctermid_str = unsafe { libc::ctermid(buf.as_mut_ptr() as *mut libc::c_char) };
if ctermid_str.is_null() {
String::new()
} else {
unsafe { std::ffi::CStr::from_ptr(ctermid_str).to_string_lossy().to_string() }
}
}
#[cfg(target_os = "linux")]
pub fn posix_getlogin() -> Option<String> {
let login_name = unsafe { libc::getlogin() };
if login_name.is_null() {
None
} else {
Some(unsafe { std::ffi::CStr::from_ptr(login_name).to_string_lossy().to_string() })
}
}
#[cfg(target_os = "linux")]
pub fn posix_dir(path: &str) -> PosixResult<Vec<String>> {
let dir = fs::read_dir(path)?;
let mut entries = Vec::new();
for entry in dir {
let entry = entry?;
entries.push(entry.file_name().to_string_lossy().to_string());
}
Ok(entries)
}
#[derive(Debug, Clone)]
pub struct PosixStatFs {
pub f_type: i64,
pub f_bsize: u64,
pub f_blocks: u64,
pub f_bfree: u64,
pub f_bavail: u64,
pub f_files: u64,
pub f_ffree: u64,
pub f_namelen: u64,
pub f_fsid: u64,
}
#[allow(clippy::unnecessary_transmutes)]
pub fn posix_statfs(path: &str) -> PosixResult<PosixStatFs> {
use std::ffi::CString;
let path_cstr = CString::new(path)
.map_err(|_| PosixError::InvalidArgument("path contains null byte".to_string()))?;
let mut st: libc::statfs = unsafe { std::mem::zeroed() };
let ret = unsafe { libc::statfs(path_cstr.as_ptr(), &mut st) };
if ret != 0 {
return Err(PosixError::Io(io::Error::last_os_error()));
}
Ok(PosixStatFs {
f_type: st.f_type as i64,
f_bsize: st.f_bsize as u64,
f_blocks: st.f_blocks as u64,
f_bfree: st.f_bfree as u64,
f_bavail: st.f_bavail as u64,
f_files: st.f_files as u64,
f_ffree: st.f_ffree as u64,
#[cfg(target_os = "linux")]
f_namelen: st.f_namelen as u64,
#[cfg(not(target_os = "linux"))]
f_namelen: 0,
f_fsid: {
#[cfg(target_os = "linux")]
{
use std::convert::TryInto;
let bytes = unsafe { std::mem::transmute::<_, [u8; 8]>(st.f_fsid) };
let val0 = u32::from_ne_bytes(bytes[0..4].try_into().unwrap()) as u64;
let val1 = u32::from_ne_bytes(bytes[4..8].try_into().unwrap()) as u64;
(val0 << 32) | val1
}
#[cfg(not(target_os = "linux"))]
{
st.f_type as u64
}
},
})
}
pub fn posix_mkstemp(template: &str) -> PosixResult<(String, std::fs::File)> {
let template_bytes = template.as_bytes();
let mut template_vec = template_bytes.to_vec();
template_vec.push(0);
let fd = unsafe { libc::mkstemp(template_vec.as_mut_ptr() as *mut libc::c_char) };
if fd == -1 {
return Err(PosixError::Io(io::Error::last_os_error()));
}
template_vec.pop();
let path = String::from_utf8(template_vec)
.map_err(|_| PosixError::InvalidArgument("invalid UTF-8 in template".to_string()))?;
let file = unsafe { std::fs::File::from_raw_fd(fd) };
Ok((path, file))
}
pub fn posix_utime(path: impl AsRef<Path>, mtime: Option<u64>, atime: Option<u64>) -> PosixResult<()> {
let path = path.as_ref();
let path_cstr = std::ffi::CString::new(path.as_os_str().as_bytes().to_vec())
.map_err(|_| PosixError::InvalidArgument("path contains null byte".to_string()))?;
let currtime = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs() as i64;
let times = libc::utimbuf {
modtime: mtime.map(|t| t as i64).unwrap_or(currtime),
actime: atime.map(|t| t as i64).unwrap_or(currtime),
};
match unsafe { libc::utime(path_cstr.as_ptr(), ×) } {
0 => Ok(()),
_ => Err(PosixError::Io(io::Error::last_os_error())),
}
}
#[derive(Debug, Clone)]
#[cfg(target_os = "linux")]
pub struct PosixPasswd {
#[allow(dead_code)]
pub name: String,
pub uid: u32,
pub gid: u32,
#[allow(dead_code)]
pub dir: String,
#[allow(dead_code)]
pub shell: String,
#[allow(dead_code)]
pub gecos: String,
#[allow(dead_code)]
pub passwd: String,
}
#[cfg(target_os = "linux")]
pub fn posix_getpasswd(name: Option<&str>, uid: Option<u32>) -> PosixResult<PosixPasswd> {
use std::ffi::CString;
use libc::{getpwnam, getpwuid, geteuid};
let pwd = if let Some(n) = name {
let name_cstr = CString::new(n)
.map_err(|_| PosixError::InvalidArgument("name contains null byte".to_string()))?;
unsafe { getpwnam(name_cstr.as_ptr()) }
} else if let Some(u) = uid {
unsafe { getpwuid(u) }
} else {
let euid = unsafe { geteuid() };
unsafe { getpwuid(euid) }
};
if pwd.is_null() {
return Err(PosixError::NotFound);
}
let pwd = unsafe { *pwd };
Ok(PosixPasswd {
name: unsafe { std::ffi::CStr::from_ptr(pwd.pw_name).to_string_lossy().to_string() },
uid: pwd.pw_uid,
gid: pwd.pw_gid,
dir: unsafe { std::ffi::CStr::from_ptr(pwd.pw_dir).to_string_lossy().to_string() },
shell: unsafe { std::ffi::CStr::from_ptr(pwd.pw_shell).to_string_lossy().to_string() },
gecos: unsafe { std::ffi::CStr::from_ptr(pwd.pw_gecos).to_string_lossy().to_string() },
passwd: unsafe { std::ffi::CStr::from_ptr(pwd.pw_passwd).to_string_lossy().to_string() },
})
}
#[derive(Debug, Clone)]
#[cfg(target_os = "linux")]
pub struct PosixGroup {
#[allow(dead_code)]
pub name: String,
pub gid: u32,
#[allow(dead_code)]
pub members: Vec<String>,
}
#[cfg(target_os = "linux")]
pub fn posix_getgroup(name: Option<&str>, gid: Option<u32>) -> PosixResult<PosixGroup> {
use std::ffi::CString;
use libc::{getgrnam, getgrgid};
let grp = if let Some(n) = name {
let name_cstr = CString::new(n)
.map_err(|_| PosixError::InvalidArgument("name contains null byte".to_string()))?;
unsafe { getgrnam(name_cstr.as_ptr()) }
} else if let Some(g) = gid {
unsafe { getgrgid(g) }
} else {
return Err(PosixError::InvalidArgument("name or gid required".to_string()));
};
if grp.is_null() {
return Err(PosixError::NotFound);
}
let grp = unsafe { *grp };
let mut members = Vec::new();
let mut mem_ptr = grp.gr_mem;
while !mem_ptr.is_null() && !unsafe { *mem_ptr }.is_null() {
let member = unsafe { std::ffi::CStr::from_ptr(*mem_ptr).to_string_lossy().to_string() };
members.push(member);
mem_ptr = unsafe { mem_ptr.add(1) };
}
Ok(PosixGroup {
name: unsafe { std::ffi::CStr::from_ptr(grp.gr_name).to_string_lossy().to_string() },
gid: grp.gr_gid,
members,
})
}
#[cfg(target_os = "linux")]
#[derive(Debug, Clone)]
pub struct PosixTimes {
pub utime: f64,
pub stime: f64,
pub cutime: f64,
pub cstime: f64,
pub elapsed: f64,
}
#[cfg(target_os = "linux")]
pub fn posix_times() -> PosixResult<PosixTimes> {
let mut tms = std::mem::MaybeUninit::<libc::tms>::uninit();
let elapsed = unsafe { libc::times(tms.as_mut_ptr()) };
if elapsed == (!0 as libc::clock_t) {
return Err(PosixError::Io(io::Error::last_os_error()));
}
let tms = unsafe { tms.assume_init() };
const CLOCKS_PER_SEC: f64 = 1_000_000.0;
let clk_tck = CLOCKS_PER_SEC;
Ok(PosixTimes {
utime: tms.tms_utime as f64 / clk_tck,
stime: tms.tms_stime as f64 / clk_tck,
cutime: tms.tms_cutime as f64 / clk_tck,
cstime: tms.tms_cstime as f64 / clk_tck,
elapsed: elapsed as f64 / clk_tck,
})
}
#[cfg(target_os = "linux")]
#[derive(Debug, Clone)]
pub struct PosixPathconf {
pub link_max: i64,
pub max_canon: i64,
pub max_input: i64,
pub name_max: i64,
pub path_max: i64,
pub pipe_buf: i64,
pub chown_restricted: i64,
pub no_trunc: i64,
pub vdisable: i64,
}
#[cfg(target_os = "linux")]
pub fn posix_pathconf(path: &str) -> PosixResult<PosixPathconf> {
use std::ffi::CString;
let path_cstr = CString::new(path)
.map_err(|_| PosixError::InvalidArgument("path contains null byte".to_string()))?;
Ok(PosixPathconf {
link_max: unsafe { libc::pathconf(path_cstr.as_ptr(), libc::_PC_LINK_MAX) },
max_canon: unsafe { libc::pathconf(path_cstr.as_ptr(), libc::_PC_MAX_CANON) },
max_input: unsafe { libc::pathconf(path_cstr.as_ptr(), libc::_PC_MAX_INPUT) },
name_max: unsafe { libc::pathconf(path_cstr.as_ptr(), libc::_PC_NAME_MAX) },
path_max: unsafe { libc::pathconf(path_cstr.as_ptr(), libc::_PC_PATH_MAX) },
pipe_buf: unsafe { libc::pathconf(path_cstr.as_ptr(), libc::_PC_PIPE_BUF) },
chown_restricted: unsafe { libc::pathconf(path_cstr.as_ptr(), libc::_PC_CHOWN_RESTRICTED) },
no_trunc: unsafe { libc::pathconf(path_cstr.as_ptr(), libc::_PC_NO_TRUNC) },
vdisable: unsafe { libc::pathconf(path_cstr.as_ptr(), libc::_PC_VDISABLE) },
})
}
#[cfg(target_os = "linux")]
#[derive(Debug, Clone)]
pub struct PosixSysconf {
pub arg_max: i64,
pub child_max: i64,
pub clk_tck: i64,
pub ngroups_max: i64,
pub stream_max: i64,
pub tzname_max: i64,
pub open_max: i64,
pub job_control: i64,
pub saved_ids: i64,
pub version: i64,
}
#[cfg(target_os = "linux")]
pub fn posix_sysconf() -> PosixResult<PosixSysconf> {
Ok(PosixSysconf {
arg_max: unsafe { libc::sysconf(libc::_SC_ARG_MAX) },
child_max: unsafe { libc::sysconf(libc::_SC_CHILD_MAX) },
clk_tck: unsafe { libc::sysconf(libc::_SC_CLK_TCK) },
ngroups_max: unsafe { libc::sysconf(libc::_SC_NGROUPS_MAX) },
stream_max: unsafe { libc::sysconf(libc::_SC_STREAM_MAX) },
tzname_max: unsafe { libc::sysconf(libc::_SC_TZNAME_MAX) },
open_max: unsafe { libc::sysconf(libc::_SC_OPEN_MAX) },
job_control: unsafe { libc::sysconf(libc::_SC_JOB_CONTROL) },
saved_ids: unsafe { libc::sysconf(libc::_SC_SAVED_IDS) },
version: unsafe { libc::sysconf(libc::_SC_VERSION) },
})
}
#[cfg(target_os = "linux")]
pub fn posix_getuid_by_name(name: &str) -> Option<u32> {
get_user_by_name(name).map(|u| u.uid())
}
#[cfg(target_os = "linux")]
pub fn posix_getgid_by_name(name: &str) -> Option<u32> {
get_group_by_name(name).map(|g| g.gid())
}
#[cfg(target_os = "linux")]
pub fn posix_setuid(uid: u32) -> PosixResult<()> {
match unsafe { libc::setuid(uid) } {
0 => Ok(()),
_ => Err(PosixError::Io(io::Error::last_os_error())),
}
}
#[cfg(target_os = "linux")]
pub fn posix_setgid(gid: u32) -> PosixResult<()> {
match unsafe { libc::setgid(gid) } {
0 => Ok(()),
_ => Err(PosixError::Io(io::Error::last_os_error())),
}
}
#[cfg(test)]
mod tests {
#[test]
#[allow(clippy::unnecessary_transmutes)]
fn test_statfs_fsid() {
use std::ffi::CString;
use std::convert::TryInto;
let path = CString::new(".").unwrap();
let mut st: libc::statfs = unsafe { std::mem::zeroed() };
let ret = unsafe { libc::statfs(path.as_ptr(), &mut st) };
if ret != 0 {
panic!("statfs failed");
}
println!("f_type: 0x{:x}", st.f_type);
let bytes = unsafe { std::mem::transmute::<_, [u8; 8]>(st.f_fsid) };
println!("f_fsid bytes: {:02x?}", bytes);
let val0 = u32::from_ne_bytes(bytes[0..4].try_into().unwrap()) as u64;
let val1 = u32::from_ne_bytes(bytes[4..8].try_into().unwrap()) as u64;
println!("val0: 0x{:x}, val1: 0x{:x}", val0, val1);
let gnu_id = (val0 << 32) | val1;
println!("GNU stat ID: 0x{:x}", gnu_id);
let le = u64::from_le_bytes(bytes);
println!("le: 0x{:x}", le);
let be = u64::from_be_bytes(bytes);
println!("be: 0x{:x}", be);
if le != 0 {
println!("non-zero fsid");
}
}
}