Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

nix

Package Overview
Dependencies
Maintainers
1
Versions
79
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

nix - cargo Package Compare versions

Comparing version
0.29.0
to
0.30.0
+431
src/spawn.rs
//! Safe wrappers around posix_spawn* functions found in the libc "spawn.h" header.
use std::{ffi::CStr, mem, os::fd::RawFd};
#[cfg(any(feature = "fs", feature = "term"))]
use crate::fcntl::OFlag;
#[cfg(feature = "signal")]
use crate::sys::signal::SigSet;
#[cfg(feature = "fs")]
use crate::sys::stat::Mode;
use crate::{errno::Errno, unistd::Pid, NixPath, Result};
/// A spawn attributes object. See [posix_spawnattr_t](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_init.html).
#[repr(transparent)]
#[derive(Debug)]
pub struct PosixSpawnAttr {
attr: libc::posix_spawnattr_t,
}
impl PosixSpawnAttr {
/// Initialize the spawn attributes object. See
/// [posix_spawnattr_init](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_init.html).
#[doc(alias("posix_spawnattr_init"))]
pub fn init() -> Result<PosixSpawnAttr> {
let mut attr = mem::MaybeUninit::uninit();
let res = unsafe { libc::posix_spawnattr_init(attr.as_mut_ptr()) };
Errno::result(res)?;
let attr = unsafe { attr.assume_init() };
Ok(PosixSpawnAttr { attr })
}
/// Reinitialize the spawn attributes object.
/// This is a wrapper around
/// [posix_spawnattr_destroy](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_destroy.html)
/// followed by
/// [posix_spawnattr_init](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_init.html).
#[doc(alias("posix_spawnattr_destroy"))]
pub fn reinit(mut self) -> Result<PosixSpawnAttr> {
let res = unsafe {
libc::posix_spawnattr_destroy(
&mut self.attr as *mut libc::posix_spawnattr_t,
)
};
Errno::result(res)?;
let res = unsafe {
libc::posix_spawnattr_init(
&mut self.attr as *mut libc::posix_spawnattr_t,
)
};
Errno::result(res)?;
Ok(self)
}
/// Set spawn flags. See
/// [posix_spawnattr_setflags](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_setflags.html).
#[doc(alias("posix_spawnattr_setflags"))]
pub fn set_flags(&mut self, flags: PosixSpawnFlags) -> Result<()> {
let res = unsafe {
libc::posix_spawnattr_setflags(
&mut self.attr as *mut libc::posix_spawnattr_t,
flags.bits() as libc::c_short,
)
};
Errno::result(res)?;
Ok(())
}
/// Get spawn flags. See
/// [posix_spawnattr_getflags](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_getflags.html).
#[doc(alias("posix_spawnattr_getflags"))]
pub fn flags(&self) -> Result<PosixSpawnFlags> {
let mut flags: libc::c_short = 0;
let res = unsafe {
libc::posix_spawnattr_getflags(
&self.attr as *const libc::posix_spawnattr_t,
&mut flags,
)
};
Errno::result(res)?;
Ok(PosixSpawnFlags::from_bits_truncate(flags.into()))
}
/// Set spawn pgroup. See
/// [posix_spawnattr_setpgroup](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_setpgroup.html).
#[doc(alias("posix_spawnattr_setpgroup"))]
pub fn set_pgroup(&mut self, pgroup: Pid) -> Result<()> {
let res = unsafe {
libc::posix_spawnattr_setpgroup(
&mut self.attr as *mut libc::posix_spawnattr_t,
pgroup.as_raw(),
)
};
Errno::result(res)?;
Ok(())
}
/// Get spawn pgroup. See
/// [posix_spawnattr_getpgroup](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_getpgroup.html).
#[doc(alias("posix_spawnattr_getpgroup"))]
pub fn pgroup(&self) -> Result<Pid> {
let mut pid: libc::pid_t = 0;
let res = unsafe {
libc::posix_spawnattr_getpgroup(
&self.attr as *const libc::posix_spawnattr_t,
&mut pid,
)
};
Errno::result(res)?;
Ok(Pid::from_raw(pid))
}
feature! {
#![feature = "signal"]
/// Set spawn sigdefault. See
/// [posix_spawnattr_setsigdefault](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_setsigdefault.html).
#[doc(alias("posix_spawnattr_setsigdefault"))]
pub fn set_sigdefault(&mut self, sigdefault: &SigSet) -> Result<()> {
let res = unsafe {
libc::posix_spawnattr_setsigdefault(
&mut self.attr as *mut libc::posix_spawnattr_t,
sigdefault.as_ref(),
)
};
Errno::result(res)?;
Ok(())
}
/// Get spawn sigdefault. See
/// [posix_spawnattr_getsigdefault](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_getsigdefault.html).
#[doc(alias("posix_spawnattr_getsigdefault"))]
pub fn sigdefault(&self) -> Result<SigSet> {
let mut sigset = mem::MaybeUninit::uninit();
let res = unsafe {
libc::posix_spawnattr_getsigdefault(
&self.attr as *const libc::posix_spawnattr_t,
sigset.as_mut_ptr(),
)
};
Errno::result(res)?;
let sigdefault =
unsafe { SigSet::from_sigset_t_unchecked(sigset.assume_init()) };
Ok(sigdefault)
}
/// Set spawn sigmask. See
/// [posix_spawnattr_setsigmask](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_setsigmask.html).
#[doc(alias("posix_spawnattr_setsigmask"))]
pub fn set_sigmask(&mut self, sigdefault: &SigSet) -> Result<()> {
let res = unsafe {
libc::posix_spawnattr_setsigmask(
&mut self.attr as *mut libc::posix_spawnattr_t,
sigdefault.as_ref(),
)
};
Errno::result(res)?;
Ok(())
}
/// Get spawn sigmask. See
/// [posix_spawnattr_getsigmask](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_getsigmask.html).
#[doc(alias("posix_spawnattr_getsigmask"))]
pub fn sigmask(&self) -> Result<SigSet> {
let mut sigset = mem::MaybeUninit::uninit();
let res = unsafe {
libc::posix_spawnattr_getsigmask(
&self.attr as *const libc::posix_spawnattr_t,
sigset.as_mut_ptr(),
)
};
Errno::result(res)?;
let sigdefault =
unsafe { SigSet::from_sigset_t_unchecked(sigset.assume_init()) };
Ok(sigdefault)
}
}
}
impl Drop for PosixSpawnAttr {
fn drop(&mut self) {
unsafe {
libc::posix_spawnattr_destroy(
&mut self.attr as *mut libc::posix_spawnattr_t,
);
}
}
}
libc_bitflags!(
/// Process attributes to be changed in the new process image when invoking [`posix_spawn`]
/// or [`posix_spawnp`]. See
/// [posix_spawn](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn.html).
pub struct PosixSpawnFlags: libc::c_int {
/// Reset effective user ID of the child process to parent's real user ID.
POSIX_SPAWN_RESETIDS;
/// Put the child in a process group specified by the spawn-pgroup attribute. See
/// [posix_spawnattr_setpgroup](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_setpgroup.html).
POSIX_SPAWN_SETPGROUP;
/// Force set signals to default signal handling in child process. See
/// [posix_spawnattr_setsigdefault](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_setsigdefault.html).
#[cfg(feature = "signal")]
POSIX_SPAWN_SETSIGDEF;
/// Set signal mask of child process. See
/// [posix_spawnattr_setsigmask](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_setsigmask.html).
#[cfg(feature = "signal")]
POSIX_SPAWN_SETSIGMASK;
// TODO: Add support for the following two flags whenever support for
// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sched.h.html
// is added to nix.
// POSIX_SPAWN_SETSCHEDPARAM;
// POSIX_SPAWN_SETSCHEDULER;
}
);
/// A spawn file actions object. See [posix_spawn_file_actions_t](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn_file_actions_addclose.html).
#[repr(transparent)]
#[derive(Debug)]
pub struct PosixSpawnFileActions {
fa: libc::posix_spawn_file_actions_t,
}
impl PosixSpawnFileActions {
/// Initialize the spawn file actions object. See
/// [posix_spawn_file_actions_init](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn_file_actions_init.html).
#[doc(alias("posix_spawn_file_actions_init"))]
pub fn init() -> Result<PosixSpawnFileActions> {
let mut actions = mem::MaybeUninit::uninit();
let res = unsafe {
libc::posix_spawn_file_actions_init(actions.as_mut_ptr())
};
Errno::result(res)?;
Ok(unsafe {
PosixSpawnFileActions {
fa: actions.assume_init(),
}
})
}
/// Reinitialize the spawn file actions object.
/// This is a wrapper around
/// [posix_spawn_file_actions_destroy](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn_file_actions_destroy.html).
/// followed by
/// [posix_spawn_file_actions_init](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn_file_actions_init.html).
#[doc(alias("posix_spawn_file_actions_destroy"))]
pub fn reinit(mut self) -> Result<PosixSpawnFileActions> {
let res = unsafe {
libc::posix_spawn_file_actions_destroy(
&mut self.fa as *mut libc::posix_spawn_file_actions_t,
)
};
Errno::result(res)?;
let res = unsafe {
libc::posix_spawn_file_actions_init(
&mut self.fa as *mut libc::posix_spawn_file_actions_t,
)
};
Errno::result(res)?;
Ok(self)
}
/// Add a [dup2](https://pubs.opengroup.org/onlinepubs/9699919799/functions/dup2.html) action. See
/// [posix_spawn_file_actions_adddup2](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn_file_actions_adddup2.html).
#[doc(alias("posix_spawn_file_actions_adddup2"))]
pub fn add_dup2(&mut self, fd: RawFd, newfd: RawFd) -> Result<()> {
let res = unsafe {
libc::posix_spawn_file_actions_adddup2(
&mut self.fa as *mut libc::posix_spawn_file_actions_t,
fd,
newfd,
)
};
Errno::result(res)?;
Ok(())
}
feature! {
#![all(feature = "fs", feature = "term")]
/// Add an open action. See
/// [posix_spawn_file_actions_addopen](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn_file_actions_addopen.html).
#[doc(alias("posix_spawn_file_actions_addopen"))]
pub fn add_open<P: ?Sized + NixPath>(
&mut self,
fd: RawFd,
path: &P,
oflag: OFlag,
mode: Mode,
) -> Result<()> {
let res = path.with_nix_path(|cstr| unsafe {
libc::posix_spawn_file_actions_addopen(
&mut self.fa as *mut libc::posix_spawn_file_actions_t,
fd,
cstr.as_ptr(),
oflag.bits(),
mode.bits(),
)
})?;
Errno::result(res)?;
Ok(())
}
}
/// Add a close action. See
/// [posix_spawn_file_actions_addclose](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn_file_actions_addclose.html).
#[doc(alias("posix_spawn_file_actions_addclose"))]
pub fn add_close(&mut self, fd: RawFd) -> Result<()> {
let res = unsafe {
libc::posix_spawn_file_actions_addclose(
&mut self.fa as *mut libc::posix_spawn_file_actions_t,
fd,
)
};
Errno::result(res)?;
Ok(())
}
}
impl Drop for PosixSpawnFileActions {
fn drop(&mut self) {
unsafe {
libc::posix_spawn_file_actions_destroy(
&mut self.fa as *mut libc::posix_spawn_file_actions_t,
);
}
}
}
// The POSIX standard requires those `args` and `envp` to be of type `*const *mut [c_char]`,
// but implementations won't modify them, making the `mut` type redundant. Considering this,
// Nix does not expose this mutability, but we have to change the interface when calling the
// underlying libc interfaces , this helper function does the conversion job.
//
// SAFETY:
// It is safe to add the mutability in types as implementations won't mutable them.
unsafe fn to_exec_array<S: AsRef<CStr>>(args: &[S]) -> Vec<*mut libc::c_char> {
let mut v: Vec<*mut libc::c_char> = args
.iter()
.map(|s| s.as_ref().as_ptr().cast_mut())
.collect();
v.push(std::ptr::null_mut());
v
}
/// Create a new child process from the specified process image. See
/// [posix_spawn](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn.html).
pub fn posix_spawn<P, SA, SE>(
path: &P,
file_actions: &PosixSpawnFileActions,
attr: &PosixSpawnAttr,
args: &[SA],
envp: &[SE],
) -> Result<Pid>
where
P: NixPath + ?Sized,
SA: AsRef<CStr>,
SE: AsRef<CStr>,
{
let mut pid = 0;
let ret = unsafe {
let args_p = to_exec_array(args);
let env_p = to_exec_array(envp);
path.with_nix_path(|c_str| {
libc::posix_spawn(
&mut pid as *mut libc::pid_t,
c_str.as_ptr(),
&file_actions.fa as *const libc::posix_spawn_file_actions_t,
&attr.attr as *const libc::posix_spawnattr_t,
args_p.as_ptr(),
env_p.as_ptr(),
)
})?
};
if ret != 0 {
return Err(Errno::from_raw(ret));
}
Ok(Pid::from_raw(pid))
}
/// Create a new child process from the specified process image. See
/// [posix_spawnp](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnp.html).
pub fn posix_spawnp<SA: AsRef<CStr>, SE: AsRef<CStr>>(
path: &CStr,
file_actions: &PosixSpawnFileActions,
attr: &PosixSpawnAttr,
args: &[SA],
envp: &[SE],
) -> Result<Pid> {
let mut pid = 0;
let ret = unsafe {
let args_p = to_exec_array(args);
let env_p = to_exec_array(envp);
libc::posix_spawnp(
&mut pid as *mut libc::pid_t,
path.as_ptr(),
&file_actions.fa as *const libc::posix_spawn_file_actions_t,
&attr.attr as *const libc::posix_spawnattr_t,
args_p.as_ptr(),
env_p.as_ptr(),
)
};
if ret != 0 {
return Err(Errno::from_raw(ret));
}
Ok(Pid::from_raw(pid))
}
//! Interfaces for controlling system log.
use crate::{NixPath, Result};
use std::ffi::OsStr;
use std::ptr;
/// Logging options of subsequent [`syslog`] calls can be set by calling [`openlog`].
///
/// The parameter `ident` is a string that will be prepended to every message. The `logopt`
/// argument specifies logging options. The `facility` parameter encodes a default facility to be
/// assigned to all messages that do not have an explicit facility encoded.
//
// On Linux, the `ident` argument needs to have static lifetime according to the
// man page:
//
// The argument ident in the call of openlog() is probably stored as-is. Thus,
// if the string it points to is changed, syslog() may start prepending the changed
// string, and if the string it points to ceases to exist, the results are
// undefined. Most portable is to use a string constant.
#[cfg(target_os = "linux")]
pub fn openlog(
ident: Option<&'static std::ffi::CStr>,
logopt: LogFlags,
facility: Facility,
) -> Result<()> {
let logopt = logopt.bits();
let facility = facility as libc::c_int;
match ident {
None => unsafe {
libc::openlog(ptr::null(), logopt, facility);
},
Some(ident) => ident.with_nix_path(|ident| unsafe {
libc::openlog(ident.as_ptr(), logopt, facility);
})?,
}
Ok(())
}
/// Logging options of subsequent [`syslog`] calls can be set by calling [`openlog`].
///
/// The parameter `ident` is a string that will be prepended to every message. The `logopt`
/// argument specifies logging options. The `facility` parameter encodes a default facility to be
/// assigned to all messages that do not have an explicit facility encoded.
#[cfg(not(target_os = "linux"))]
pub fn openlog<S: AsRef<OsStr> + ?Sized>(
ident: Option<&S>,
logopt: LogFlags,
facility: Facility,
) -> Result<()> {
let logopt = logopt.bits();
let facility = facility as libc::c_int;
match ident.map(OsStr::new) {
None => unsafe {
libc::openlog(ptr::null(), logopt, facility);
},
Some(ident) => ident.with_nix_path(|ident| unsafe {
libc::openlog(ident.as_ptr(), logopt, facility);
})?,
}
Ok(())
}
/// Writes message to the system message logger.
///
/// The message is then written to the system console, log files, logged-in users, or forwarded
/// to other machines as appropriate.
///
/// # Examples
///
/// ```rust
/// use nix::syslog::{openlog, syslog, Facility, LogFlags, Severity, Priority};
///
/// let priority = Priority::new(Severity::LOG_EMERG, Facility::LOG_USER);
/// syslog(priority, "Hello, nix!").unwrap();
///
/// // use `format!` to format the message
/// let name = "syslog";
/// syslog(priority, &format!("Hello, {name}!")).unwrap();
/// ```
pub fn syslog<P, S>(priority: P, message: &S) -> Result<()>
where
P: Into<Priority>,
S: AsRef<OsStr> + ?Sized,
{
let priority = priority.into();
let formatter = OsStr::new("%s");
let message = OsStr::new(message);
formatter.with_nix_path(|formatter| {
message.with_nix_path(|message| unsafe {
libc::syslog(priority.0, formatter.as_ptr(), message.as_ptr())
})
})??;
Ok(())
}
/// Set the process-wide priority mask to `mask` and return the previous mask
/// value.
///
/// Calls to `syslog()` with a priority level not set in `mask` are ignored. The
/// default is to log all priorities.
///
/// If the `mask` argument is `None`, the current logmask is not modified, this
/// can be used to query the current log mask.
pub fn setlogmask(mask: Option<LogMask>) -> LogMask {
let mask = match mask {
Some(mask) => mask.0,
None => 0,
};
let prev_mask = unsafe { libc::setlogmask(mask) };
LogMask(prev_mask)
}
/// Closes the log file.
pub fn closelog() {
unsafe { libc::closelog() }
}
/// System log priority mask.
#[derive(Debug, Clone, Copy)]
pub struct LogMask(libc::c_int);
impl LogMask {
/// Creates a mask of all priorities up to and including `priority`.
#[doc(alias("LOG_UPTO"))]
pub fn up_to(priority: Severity) -> Self {
let pri = priority as libc::c_int;
Self((1 << (pri + 1)) - 1)
}
/// Creates a mask for the specified priority.
#[doc(alias("LOG_MASK"))]
pub fn of_priority(priority: Severity) -> Self {
let pri = priority as libc::c_int;
Self(1 << pri)
}
/// Returns if the mask for the specified `priority` is set.
pub fn contains(&self, priority: Severity) -> bool {
let priority = Self::of_priority(priority);
let and_result = *self & priority;
and_result.0 != 0
}
}
impl std::ops::BitOr for LogMask {
type Output = Self;
fn bitor(self, rhs: Self) -> Self::Output {
Self(self.0 | rhs.0)
}
}
impl std::ops::BitAnd for LogMask {
type Output = Self;
fn bitand(self, rhs: Self) -> Self::Output {
Self(self.0 & rhs.0)
}
}
impl std::ops::BitOrAssign for LogMask {
fn bitor_assign(&mut self, rhs: Self) {
self.0 |= rhs.0;
}
}
impl std::ops::BitAndAssign for LogMask {
fn bitand_assign(&mut self, rhs: Self) {
self.0 &= rhs.0;
}
}
impl std::ops::Not for LogMask {
type Output = Self;
fn not(self) -> Self::Output {
Self(!self.0)
}
}
/// The priority for a log message.
#[derive(Debug, Clone, Copy)]
pub struct Priority(libc::c_int);
impl Priority {
/// Create a new priority from a facility and severity level.
pub fn new(severity: Severity, facility: Facility) -> Self {
let priority = (facility as libc::c_int) | (severity as libc::c_int);
Priority(priority)
}
}
impl From<Severity> for Priority {
fn from(severity: Severity) -> Self {
let priority = severity as libc::c_int;
Priority(priority)
}
}
libc_bitflags! {
/// Options for system logging.
pub struct LogFlags: libc::c_int {
/// Log the process id with each message: useful for identifying instantiations of
/// daemons.
LOG_PID;
/// If syslog() cannot pass the message to syslogd(8) it will attempt to write the
/// message to the console ("/dev/console").
LOG_CONS;
/// The converse of [`LOG_NDELAY`][LogFlags::LOG_NDELAY]; opening of the connection is
/// delayed until `syslog` is called.
///
/// This is the default, and need not be specified.
LOG_ODELAY;
/// Open the connection to syslogd(8) immediately. Normally the open is delayed until
/// the first message is logged. Useful for programs that need to manage the order in
/// which file descriptors are allocated.
LOG_NDELAY;
/// Write the message to standard error output as well to the system log.
#[cfg(not(any(solarish, target_os = "redox", target_os = "cygwin")))]
LOG_PERROR;
}
}
libc_enum! {
/// Severity levels for log messages.
#[repr(i32)]
#[non_exhaustive]
pub enum Severity {
/// A panic condition.
///
/// This is normally broadcast to all users.
LOG_EMERG,
/// A condition that should be corrected immediately, such as a corrupted system database.
LOG_ALERT,
/// Critical conditions, e.g., hard device errors.
LOG_CRIT,
/// Errors.
LOG_ERR,
/// Warning messages.
LOG_WARNING,
/// Conditions that are not error conditions, but should possibly be handled specially.
LOG_NOTICE,
/// Informational messages.
LOG_INFO,
/// Messages that contain information normally of use only when debugging a program.
LOG_DEBUG,
}
}
libc_enum! {
/// Facilities for log messages.
#[repr(i32)]
#[non_exhaustive]
pub enum Facility {
/// Messages generated by the kernel.
///
/// These cannot be generated by any user processes.
LOG_KERN,
/// Messages generated by random user processes.
///
/// This is the default facility identifier if none is specified.
LOG_USER,
/// The mail system.
LOG_MAIL,
/// System daemons, such as routed(8), that are not provided for explicitly by other facilities.
LOG_DAEMON,
/// The authorization system: login(1), su(1), getty(8), etc.
LOG_AUTH,
/// Messages generated internally by syslogd(8).
LOG_SYSLOG,
/// The line printer spooling system: cups-lpd(8), cupsd(8), etc.
LOG_LPR,
/// The network news system.
LOG_NEWS,
/// The uucp system.
LOG_UUCP,
/// Reserved for local use.
LOG_LOCAL0,
/// Reserved for local use.
LOG_LOCAL1,
/// Reserved for local use.
LOG_LOCAL2,
/// Reserved for local use.
LOG_LOCAL3,
/// Reserved for local use.
LOG_LOCAL4,
/// Reserved for local use.
LOG_LOCAL5,
/// Reserved for local use.
LOG_LOCAL6,
/// Reserved for local use.
LOG_LOCAL7,
}
}
#[test]
fn test_memfd_create() {
use nix::sys::memfd::memfd_create;
use nix::sys::memfd::MFdFlags;
use nix::unistd::lseek;
use nix::unistd::read;
use nix::unistd::{write, Whence};
let fd =
memfd_create("test_memfd_create_name", MFdFlags::MFD_CLOEXEC).unwrap();
let contents = b"hello";
assert_eq!(write(&fd, contents).unwrap(), 5);
lseek(&fd, 0, Whence::SeekSet).unwrap();
let mut buf = vec![0_u8; contents.len()];
assert_eq!(read(&fd, &mut buf).unwrap(), 5);
assert_eq!(contents, buf.as_slice());
}
use super::FORK_MTX;
use nix::errno::Errno;
use nix::spawn::{self, PosixSpawnAttr, PosixSpawnFileActions};
use nix::sys::signal;
use nix::sys::wait::{waitpid, WaitPidFlag, WaitStatus};
use std::ffi::{CStr, CString};
/// Helper function to find a binary in the $PATH
fn which(exe_name: &str) -> Option<std::path::PathBuf> {
std::env::var_os("PATH").and_then(|paths| {
std::env::split_paths(&paths)
.filter_map(|dir| {
let full_path = dir.join(exe_name);
if full_path.is_file() {
Some(full_path)
} else {
None
}
})
.next()
})
}
#[test]
fn spawn_true() {
let _guard = FORK_MTX.lock();
let bin = which("true").unwrap();
let args = &[
CString::new("true").unwrap(),
CString::new("story").unwrap(),
];
let vars: &[CString] = &[];
let actions = PosixSpawnFileActions::init().unwrap();
let attr = PosixSpawnAttr::init().unwrap();
let pid =
spawn::posix_spawn(bin.as_path(), &actions, &attr, args, vars).unwrap();
let status = waitpid(pid, Some(WaitPidFlag::empty())).unwrap();
match status {
WaitStatus::Exited(wpid, ret) => {
assert_eq!(pid, wpid);
assert_eq!(ret, 0);
}
_ => {
panic!("Invalid WaitStatus");
}
};
}
#[test]
fn spawn_sleep() {
let _guard = FORK_MTX.lock();
let bin = which("sleep").unwrap();
let args = &[CString::new("sleep").unwrap(), CString::new("30").unwrap()];
let vars: &[CString] = &[];
let actions = PosixSpawnFileActions::init().unwrap();
let attr = PosixSpawnAttr::init().unwrap();
let pid =
spawn::posix_spawn(bin.as_path(), &actions, &attr, args, vars).unwrap();
let status =
waitpid(pid, WaitPidFlag::from_bits(WaitPidFlag::WNOHANG.bits()))
.unwrap();
match status {
WaitStatus::StillAlive => {}
_ => {
panic!("Invalid WaitStatus");
}
};
signal::kill(pid, signal::SIGTERM).unwrap();
let status = waitpid(pid, Some(WaitPidFlag::empty())).unwrap();
match status {
WaitStatus::Signaled(wpid, wsignal, _) => {
assert_eq!(pid, wpid);
assert_eq!(wsignal, signal::SIGTERM);
}
_ => {
panic!("Invalid WaitStatus");
}
};
}
#[test]
// `posix_spawn(path_not_exist)` succeeds under QEMU, so ignore the test. No need
// to investigate the root cause, this test still works in native environments, which
// is sufficient to test the binding.
#[cfg_attr(qemu, ignore)]
fn spawn_cmd_does_not_exist() {
let _guard = FORK_MTX.lock();
let args = &[CString::new("buzz").unwrap()];
let envs: &[CString] = &[];
let actions = PosixSpawnFileActions::init().unwrap();
let attr = PosixSpawnAttr::init().unwrap();
let bin = "2b7433c4-523b-470c-abb5-d7ee9fd295d5-fdasf";
let errno =
spawn::posix_spawn(bin, &actions, &attr, args, envs).unwrap_err();
assert_eq!(errno, Errno::ENOENT);
}
#[test]
fn spawnp_true() {
let _guard = FORK_MTX.lock();
let bin = &CString::new("true").unwrap();
let args = &[
CString::new("true").unwrap(),
CString::new("story").unwrap(),
];
let vars: &[CString] = &[];
let actions = PosixSpawnFileActions::init().unwrap();
let attr = PosixSpawnAttr::init().unwrap();
let pid = spawn::posix_spawnp(bin, &actions, &attr, args, vars).unwrap();
let status = waitpid(pid, Some(WaitPidFlag::empty())).unwrap();
match status {
WaitStatus::Exited(wpid, ret) => {
assert_eq!(pid, wpid);
assert_eq!(ret, 0);
}
_ => {
panic!("Invalid WaitStatus");
}
};
}
#[test]
fn spawnp_sleep() {
let _guard = FORK_MTX.lock();
let bin = &CString::new("sleep").unwrap();
let args = &[CString::new("sleep").unwrap(), CString::new("30").unwrap()];
let vars: &[CString] = &[];
let actions = PosixSpawnFileActions::init().unwrap();
let attr = PosixSpawnAttr::init().unwrap();
let pid = spawn::posix_spawnp(bin, &actions, &attr, args, vars).unwrap();
let status =
waitpid(pid, WaitPidFlag::from_bits(WaitPidFlag::WNOHANG.bits()))
.unwrap();
match status {
WaitStatus::StillAlive => {}
_ => {
panic!("Invalid WaitStatus");
}
};
signal::kill(pid, signal::SIGTERM).unwrap();
let status = waitpid(pid, Some(WaitPidFlag::empty())).unwrap();
match status {
WaitStatus::Signaled(wpid, wsignal, _) => {
assert_eq!(pid, wpid);
assert_eq!(wsignal, signal::SIGTERM);
}
_ => {
panic!("Invalid WaitStatus");
}
};
}
#[test]
// `posix_spawnp(bin_not_exist)` succeeds under QEMU, so ignore the test. No need
// to investigate the root cause, this test still works in native environments, which
// is sufficient to test the binding.
#[cfg_attr(qemu, ignore)]
fn spawnp_cmd_does_not_exist() {
let _guard = FORK_MTX.lock();
let args = &[CString::new("buzz").unwrap()];
let envs: &[CString] = &[];
let actions = PosixSpawnFileActions::init().unwrap();
let attr = PosixSpawnAttr::init().unwrap();
let bin = CStr::from_bytes_with_nul(
"2b7433c4-523b-470c-abb5-d7ee9fd295d5-fdasf\0".as_bytes(),
)
.unwrap();
let errno =
spawn::posix_spawnp(bin, &actions, &attr, args, envs).unwrap_err();
assert_eq!(errno, Errno::ENOENT);
}
use nix::syslog::{openlog, syslog, Facility, LogFlags, Severity};
#[test]
fn test_syslog_hello_world() {
let flags = LogFlags::LOG_PID;
#[cfg(not(target_os = "linux"))]
openlog(None::<&str>, flags, Facility::LOG_USER).unwrap();
#[cfg(target_os = "linux")]
openlog(None, flags, Facility::LOG_USER).unwrap();
syslog(Severity::LOG_EMERG, "Hello, nix!").unwrap();
let name = "syslog";
syslog(Severity::LOG_NOTICE, &format!("Hello, {name}!")).unwrap();
}
#[test]
#[cfg(target_os = "linux")]
fn test_openlog_with_ident() {
use std::ffi::CStr;
const IDENT: &CStr = unsafe {
CStr::from_bytes_with_nul_unchecked(b"test_openlog_with_ident\0")
};
let flags = LogFlags::LOG_PID;
openlog(Some(IDENT), flags, Facility::LOG_USER).unwrap();
syslog(Severity::LOG_EMERG, "Hello, ident!").unwrap();
}
#[test]
#[cfg(not(target_os = "linux"))]
fn test_openlog_with_ident() {
let flags = LogFlags::LOG_PID;
openlog(Some("test_openlog_with_ident"), flags, Facility::LOG_USER)
.unwrap();
syslog(Severity::LOG_EMERG, "Hello, ident!").unwrap();
}
+1
-1
{
"git": {
"sha1": "1dad4d8d04a2cd187fae87cb91c4f4e95ff0decd"
"sha1": "989291d5bfb7566bd4415a18607d04d84a0604aa"
},
"path_in_vcs": ""
}

@@ -30,12 +30,7 @@ use cfg_aliases::cfg_aliases;

// Below are Nix's custom cfg values that we need to let the compiler know
println!("cargo:rustc-check-cfg=cfg(apple_targets)");
println!("cargo:rustc-check-cfg=cfg(bsd)");
println!("cargo:rustc-check-cfg=cfg(bsd_without_apple)");
println!("cargo:rustc-check-cfg=cfg(linux_android)");
println!("cargo:rustc-check-cfg=cfg(freebsdlike)");
println!("cargo:rustc-check-cfg=cfg(netbsdlike)");
println!("cargo:rustc-check-cfg=cfg(solarish)");
// Below are custom cfg values set during some CI steps.
println!("cargo:rustc-check-cfg=cfg(fbsd14)");
println!("cargo:rustc-check-cfg=cfg(qemu)");
// Cygwin target, added in 1.86
println!("cargo:rustc-check-cfg=cfg(target_os, values(\"cygwin\"))");
}
+124
-76

@@ -13,5 +13,5 @@ # This file is automatically @generated by Cargo.

name = "autocfg"
version = "1.3.0"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"

@@ -26,5 +26,5 @@ [[package]]

name = "bitflags"
version = "2.5.0"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"

@@ -61,5 +61,5 @@ [[package]]

name = "errno"
version = "0.3.9"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e"
dependencies = [

@@ -72,14 +72,15 @@ "libc",

name = "fastrand"
version = "2.1.0"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a"
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
[[package]]
name = "getrandom"
version = "0.2.15"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0"
dependencies = [
"cfg-if",
"libc",
"r-efi",
"wasi",

@@ -90,11 +91,11 @@ ]

name = "libc"
version = "0.2.155"
version = "0.2.172"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
[[package]]
name = "linux-raw-sys"
version = "0.4.14"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"

@@ -122,6 +123,6 @@ [[package]]

name = "nix"
version = "0.29.0"
version = "0.30.0"
dependencies = [
"assert-impl",
"bitflags 2.5.0",
"bitflags 2.9.0",
"caps",

@@ -141,6 +142,12 @@ "cfg-if",

[[package]]
name = "once_cell"
version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
name = "parking_lot"
version = "0.12.2"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb"
checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
dependencies = [

@@ -172,11 +179,14 @@ "lock_api",

name = "ppv-lite86"
version = "0.2.17"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
dependencies = [
"zerocopy",
]
[[package]]
name = "proc-macro2"
version = "1.0.83"
version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43"
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
dependencies = [

@@ -188,5 +198,5 @@ "unicode-ident",

name = "quote"
version = "1.0.36"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
dependencies = [

@@ -197,8 +207,13 @@ "proc-macro2",

[[package]]
name = "r-efi"
version = "5.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
[[package]]
name = "rand"
version = "0.8.5"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97"
dependencies = [
"libc",
"rand_chacha",

@@ -210,5 +225,5 @@ "rand_core",

name = "rand_chacha"
version = "0.3.1"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
dependencies = [

@@ -221,5 +236,5 @@ "ppv-lite86",

name = "rand_core"
version = "0.6.4"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
dependencies = [

@@ -231,7 +246,7 @@ "getrandom",

name = "redox_syscall"
version = "0.5.1"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e"
checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.9.0",
]

@@ -241,7 +256,7 @@

name = "rustix"
version = "0.38.34"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.9.0",
"errno",

@@ -270,17 +285,17 @@ "libc",

name = "semver"
version = "1.0.23"
version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0"
[[package]]
name = "smallvec"
version = "1.13.2"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9"
[[package]]
name = "syn"
version = "2.0.66"
version = "2.0.101"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5"
checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf"
dependencies = [

@@ -307,8 +322,9 @@ "proc-macro2",

name = "tempfile"
version = "3.10.1"
version = "3.19.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1"
checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf"
dependencies = [
"cfg-if",
"fastrand",
"getrandom",
"once_cell",
"rustix",

@@ -320,5 +336,5 @@ "windows-sys",

name = "thiserror"
version = "1.0.61"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
dependencies = [

@@ -330,5 +346,5 @@ "thiserror-impl",

name = "thiserror-impl"
version = "1.0.61"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
dependencies = [

@@ -342,5 +358,5 @@ "proc-macro2",

name = "unicode-ident"
version = "1.0.12"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"

@@ -359,11 +375,14 @@ [[package]]

name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
version = "0.14.2+wasi-0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
dependencies = [
"wit-bindgen-rt",
]
[[package]]
name = "winapi-util"
version = "0.1.8"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b"
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
dependencies = [

@@ -375,5 +394,5 @@ "windows-sys",

name = "windows-sys"
version = "0.52.0"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [

@@ -385,5 +404,5 @@ "windows-targets",

name = "windows-targets"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [

@@ -402,46 +421,75 @@ "windows_aarch64_gnullvm",

name = "windows_aarch64_gnullvm"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "wit-bindgen-rt"
version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
dependencies = [
"bitflags 2.9.0",
]
[[package]]
name = "zerocopy"
version = "0.8.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef"
dependencies = [
"proc-macro2",
"quote",
"syn",
]

@@ -16,4 +16,5 @@ # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO

name = "nix"
version = "0.29.0"
version = "0.30.0"
authors = ["The nix-rust Project Developers"]
build = "build.rs"
include = [

@@ -27,2 +28,7 @@ "build.rs",

]
autolib = false
autobins = false
autoexamples = false
autotests = false
autobenches = false
description = "Rust friendly bindings to *nix APIs"

@@ -54,2 +60,46 @@ readme = "README.md"

[features]
acct = []
aio = ["pin-utils"]
default = []
dir = ["fs"]
env = []
event = ["poll"]
fanotify = []
feature = []
fs = []
hostname = []
inotify = []
ioctl = []
kmod = []
mman = []
mount = ["uio"]
mqueue = ["fs"]
net = ["socket"]
personality = []
poll = []
process = []
pthread = []
ptrace = ["process"]
quota = []
reboot = []
resource = []
sched = ["process"]
signal = ["process"]
socket = ["memoffset"]
syslog = []
term = []
time = []
ucontext = ["signal"]
uio = []
user = ["feature"]
zerocopy = [
"fs",
"uio",
]
[lib]
name = "nix"
path = "src/lib.rs"
[[test]]

@@ -72,3 +122,3 @@ name = "test"

[dependencies.bitflags]
version = "2.3.1"
version = "2.3.3"

@@ -79,3 +129,3 @@ [dependencies.cfg-if]

[dependencies.libc]
version = "0.2.155"
version = "0.2.171"
features = ["extra_traits"]

@@ -98,3 +148,3 @@

[dev-dependencies.rand]
version = "0.8"
version = "0.9"

@@ -108,47 +158,8 @@ [dev-dependencies.semver]

[build-dependencies.cfg_aliases]
version = "0.2"
version = "0.2.1"
[features]
acct = []
aio = ["pin-utils"]
default = []
dir = ["fs"]
env = []
event = []
fanotify = []
feature = []
fs = []
hostname = []
inotify = []
ioctl = []
kmod = []
mman = []
mount = ["uio"]
mqueue = ["fs"]
net = ["socket"]
personality = []
poll = []
process = []
pthread = []
ptrace = ["process"]
quota = []
reboot = []
resource = []
sched = ["process"]
signal = ["process"]
socket = ["memoffset"]
term = []
time = []
ucontext = ["signal"]
uio = []
user = ["feature"]
zerocopy = [
"fs",
"uio",
]
[target."cfg(any(target_os = \"android\", target_os = \"linux\"))".dev-dependencies.caps]
[target.'cfg(any(target_os = "android", target_os = "linux"))'.dev-dependencies.caps]
version = "0.5.3"
[target."cfg(target_os = \"freebsd\")".dev-dependencies.sysctl]
[target.'cfg(target_os = "freebsd")'.dev-dependencies.sysctl]
version = "0.4"

@@ -5,6 +5,6 @@ # Rust bindings to *nix APIs

[![crates.io](https://img.shields.io/crates/v/nix.svg)](https://crates.io/crates/nix)
[![maintenance-status](https://img.shields.io/badge/maintenance-looking--for--maintainer-orange.svg)](https://github.com/nix-rust/nix/issues/2132)
[![docs.rs](https://img.shields.io/badge/docs.rs-nix-blue?style=flat-square&logo=docs.rs)](https://docs.rs/nix)
![maintenance-status](https://img.shields.io/badge/maintenance-actively--developed-brightgreen.svg)
[![msrv](https://img.shields.io/badge/msrv-1.69-blue?style=flat-square&logo=rust)](https://www.rust-lang.org)
[Documentation (Releases)](https://docs.rs/nix/)
Nix seeks to provide friendly bindings to various *nix platform APIs (Linux, Darwin,

@@ -85,9 +85,13 @@ ...). The goal is to not provide a 100% unified interface, but to unify

<li>aarch64-linux-android</li>
<li>aarch64-unknown-linux-ohos</li>
<li>arm-linux-androideabi</li>
<li>arm-unknown-linux-musleabi</li>
<li>armv7-linux-androideabi</li>
<li>armv7-unknown-linux-ohos</li>
<li>i686-linux-android</li>
<li>loongarch64-unknown-linux-gnu</li>
<li>s390x-unknown-linux-gnu</li>
<li>x86_64-linux-android</li>
<li>x86_64-unknown-illumos</li>
<li>x86_64-unknown-linux-ohos</li>
<li>x86_64-unknown-netbsd</li>

@@ -119,3 +123,3 @@ </td>

Feel free to join us in [the nix-rust/nix](https://gitter.im/nix-rust/nix) channel on Gitter to
Feel free to join us in [the nix-rust/nix](https://discord.com/invite/rkBeJUsmyd) channel on Discord to
discuss `nix` development.

@@ -122,0 +126,0 @@

@@ -6,3 +6,3 @@ //! List directory contents

use crate::sys;
use crate::{Error, NixPath, Result};
use crate::{NixPath, Result};
use cfg_if::cfg_if;

@@ -21,13 +21,34 @@ use std::ffi;

///
/// This is a lower-level interface than `std::fs::ReadDir`. Notable differences:
/// * can be opened from a file descriptor (as returned by `openat`, perhaps before knowing
/// if the path represents a file or directory).
/// * implements `AsRawFd`, so it can be passed to `fstat`, `openat`, etc.
/// The file descriptor continues to be owned by the `Dir`, so callers must not keep a `RawFd`
/// after the `Dir` is dropped.
/// This is a lower-level interface than [`std::fs::ReadDir`]. Notable differences:
/// * can be opened from a file descriptor (as returned by [`openat`][openat],
/// perhaps before knowing if the path represents a file or directory).
/// * implements [`AsFd`][AsFd], so it can be passed to [`fstat`][fstat],
/// [`openat`][openat], etc. The file descriptor continues to be owned by the
/// `Dir`, so callers must not keep a `RawFd` after the `Dir` is dropped.
/// * can be iterated through multiple times without closing and reopening the file
/// descriptor. Each iteration rewinds when finished.
/// * returns entries for `.` (current directory) and `..` (parent directory).
/// * returns entries' names as a `CStr` (no allocation or conversion beyond whatever libc
/// * returns entries' names as a [`CStr`][cstr] (no allocation or conversion beyond whatever libc
/// does).
///
/// [AsFd]: std::os::fd::AsFd
/// [fstat]: crate::sys::stat::fstat
/// [openat]: crate::fcntl::openat
/// [cstr]: std::ffi::CStr
///
/// # Examples
///
/// Traverse the current directory, and print entries' names:
///
/// ```
/// use nix::dir::Dir;
/// use nix::fcntl::OFlag;
/// use nix::sys::stat::Mode;
///
/// let mut cwd = Dir::open(".", OFlag::O_RDONLY | OFlag::O_CLOEXEC, Mode::empty()).unwrap();
/// for res_entry in cwd.iter() {
/// let entry = res_entry.unwrap();
/// println!("File name: {}", entry.file_name().to_string_lossy());
/// }
/// ```
#[derive(Debug, Eq, Hash, PartialEq)]

@@ -48,4 +69,4 @@ pub struct Dir(ptr::NonNull<libc::DIR>);

/// Opens the given path as with `fcntl::openat`.
pub fn openat<P: ?Sized + NixPath>(
dirfd: Option<RawFd>,
pub fn openat<Fd: std::os::fd::AsFd, P: ?Sized + NixPath>(
dirfd: Fd,
path: &P,

@@ -60,17 +81,42 @@ oflag: OFlag,

/// Converts from a descriptor-based object, closing the descriptor on success or failure.
///
/// # Safety
///
/// It is only safe if `fd` is an owned file descriptor.
#[inline]
pub fn from<F: IntoRawFd>(fd: F) -> Result<Self> {
Dir::from_fd(fd.into_raw_fd())
#[deprecated(
since = "0.30.0",
note = "Deprecate this since it is not I/O-safe, use from_fd instead."
)]
pub unsafe fn from<F: IntoRawFd>(fd: F) -> Result<Self> {
use std::os::fd::FromRawFd;
use std::os::fd::OwnedFd;
// SAFETY:
//
// This is indeed unsafe is `fd` it not an owned fd.
let owned_fd = unsafe { OwnedFd::from_raw_fd(fd.into_raw_fd()) };
Dir::from_fd(owned_fd)
}
/// Converts from a file descriptor, closing it on failure.
///
/// # Examples
///
/// `ENOTDIR` would be returned if `fd` does not refer to a directory:
///
/// ```should_panic
/// use std::os::fd::OwnedFd;
/// use nix::dir::Dir;
///
/// let temp_file = tempfile::tempfile().unwrap();
/// let temp_file_fd: OwnedFd = temp_file.into();
/// let never = Dir::from_fd(temp_file_fd).unwrap();
/// ```
#[doc(alias("fdopendir"))]
pub fn from_fd(fd: RawFd) -> Result<Self> {
let d = ptr::NonNull::new(unsafe { libc::fdopendir(fd) }).ok_or_else(
|| {
let e = Error::last();
unsafe { libc::close(fd) };
e
},
)?;
pub fn from_fd(fd: std::os::fd::OwnedFd) -> Result<Self> {
// take the ownership as the constructed `Dir` is now the owner
let raw_fd = fd.into_raw_fd();
let d = ptr::NonNull::new(unsafe { libc::fdopendir(raw_fd) })
.ok_or(Errno::last())?;
Ok(Dir(d))

@@ -93,2 +139,14 @@ }

impl std::os::fd::AsFd for Dir {
fn as_fd(&self) -> std::os::fd::BorrowedFd {
let raw_fd = self.as_raw_fd();
// SAFETY:
//
// `raw_fd` should be open and valid for the lifetime of the returned
// `BorrowedFd` as it is extracted from `&self`.
unsafe { std::os::fd::BorrowedFd::borrow_raw(raw_fd) }
}
}
impl AsRawFd for Dir {

@@ -140,3 +198,3 @@ fn as_raw_fd(&self) -> RawFd {

impl<'d> Iterator for Iter<'d> {
impl Iterator for Iter<'_> {
type Item = Result<Entry>;

@@ -149,3 +207,3 @@

impl<'d> Drop for Iter<'d> {
impl Drop for Iter<'_> {
fn drop(&mut self) {

@@ -239,2 +297,3 @@ unsafe { libc::rewinddir((self.0).0.as_ptr()) }

target_os = "hurd",
target_os = "cygwin",
solarish,

@@ -241,0 +300,0 @@ linux_android,

@@ -1,2 +0,2 @@

//! file control options
//! File control options
use crate::errno::Errno;

@@ -16,8 +16,6 @@ #[cfg(all(target_os = "freebsd", target_arch = "x86_64"))]

use std::ops::{Deref, DerefMut};
use std::os::unix::ffi::OsStringExt;
#[cfg(not(target_os = "redox"))]
use std::os::raw;
use std::os::unix::ffi::OsStringExt;
use std::os::unix::io::OwnedFd;
use std::os::unix::io::RawFd;
#[cfg(not(any(target_os = "redox", target_os = "solaris")))]
use std::os::unix::io::{AsRawFd, OwnedFd};
#[cfg(any(

@@ -31,3 +29,3 @@ target_os = "netbsd",

#[cfg(any(linux_android, target_os = "freebsd"))]
use std::{os::unix::io::AsFd, ptr};
use std::ptr;

@@ -48,2 +46,38 @@ #[cfg(feature = "fs")]

/// A file descriptor referring to the working directory of the current process
/// **that should be ONLY passed to the `dirfd` argument of those `xxat()` functions**.
///
/// # Examples
///
/// Use it in [`openat()`]:
///
/// ```no_run
/// use nix::fcntl::AT_FDCWD;
/// use nix::fcntl::openat;
/// use nix::fcntl::OFlag;
/// use nix::sys::stat::Mode;
///
/// let fd = openat(AT_FDCWD, "foo", OFlag::O_RDONLY | OFlag::O_CLOEXEC, Mode::empty()).unwrap();
/// ```
///
/// # WARNING
///
/// Do NOT pass this symbol to non-`xxat()` functions, it won't work:
///
/// ```should_panic
/// use nix::errno::Errno;
/// use nix::fcntl::AT_FDCWD;
/// use nix::sys::stat::fstat;
///
/// let never = fstat(AT_FDCWD).unwrap();
/// ```
//
// SAFETY:
// 1. `AT_FDCWD` is usable for the whole process life, so it is `'static`.
// 2. It is not a valid file descriptor, but OS will handle it for us when passed
// to `xxat(2)` calls.
#[cfg(not(target_os = "redox"))] // Redox does not have this
pub const AT_FDCWD: std::os::fd::BorrowedFd<'static> =
unsafe { std::os::fd::BorrowedFd::borrow_raw(libc::AT_FDCWD) };
#[cfg(not(target_os = "redox"))]

@@ -100,3 +134,4 @@ #[cfg(any(feature = "fs", feature = "process", feature = "user"))]

target_os = "aix",
target_os = "haiku"
target_os = "haiku",
target_os = "cygwin"
)))]

@@ -114,3 +149,3 @@ O_ASYNC;

linux_android,
solarish,
target_os = "illumos",
target_os = "netbsd"

@@ -147,3 +182,3 @@ ))]

/// Same as `O_NONBLOCK`.
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
#[cfg(not(any(target_os = "redox", target_os = "haiku", target_os = "cygwin")))]
O_NDELAY;

@@ -207,12 +242,2 @@ /// `open()` will fail if the given path is a symbolic link.

/// Computes the raw fd consumed by a function of the form `*at`.
#[cfg(any(
all(feature = "fs", not(target_os = "redox")),
all(feature = "process", linux_android),
all(feature = "fanotify", target_os = "linux")
))]
pub(crate) fn at_rawfd(fd: Option<RawFd>) -> raw::c_int {
fd.unwrap_or(libc::AT_FDCWD)
}
feature! {

@@ -231,8 +256,14 @@ #![feature = "fs"]

mode: Mode,
) -> Result<RawFd> {
) -> Result<std::os::fd::OwnedFd> {
use std::os::fd::FromRawFd;
let fd = path.with_nix_path(|cstr| unsafe {
libc::open(cstr.as_ptr(), oflag.bits(), mode.bits() as c_uint)
})?;
Errno::result(fd)?;
Errno::result(fd)
// SAFETY:
//
// `open(2)` should return a valid owned fd on success
Ok( unsafe { std::os::fd::OwnedFd::from_raw_fd(fd) } )
}

@@ -244,3 +275,3 @@

/// specifies a relative path. In that case, the file to be opened is determined relative to the
/// directory associated with the file descriptor `fd`.
/// directory associated with the file descriptor `dirfd`.
///

@@ -252,12 +283,20 @@ /// # See Also

#[cfg(not(target_os = "redox"))]
pub fn openat<P: ?Sized + NixPath>(
dirfd: Option<RawFd>,
pub fn openat<P: ?Sized + NixPath, Fd: std::os::fd::AsFd>(
dirfd: Fd,
path: &P,
oflag: OFlag,
mode: Mode,
) -> Result<RawFd> {
) -> Result<OwnedFd> {
use std::os::fd::AsRawFd;
use std::os::fd::FromRawFd;
let fd = path.with_nix_path(|cstr| unsafe {
libc::openat(at_rawfd(dirfd), cstr.as_ptr(), oflag.bits(), mode.bits() as c_uint)
libc::openat(dirfd.as_fd().as_raw_fd(), cstr.as_ptr(), oflag.bits(), mode.bits() as c_uint)
})?;
Errno::result(fd)
Errno::result(fd)?;
// SAFETY:
//
// `openat(2)` should return a valid owned fd on success
Ok( unsafe { OwnedFd::from_raw_fd(fd) } )
}

@@ -300,5 +339,7 @@

/// Specifies how [openat2] should open a pathname.
/// Specifies how [`openat2()`] should open a pathname.
///
/// See <https://man7.org/linux/man-pages/man2/open_how.2type.html>
/// # Reference
///
/// * [Linux](https://man7.org/linux/man-pages/man2/open_how.2type.html)
#[repr(transparent)]

@@ -359,11 +400,14 @@ #[derive(Clone, Copy, Debug)]

/// [openat2](https://man7.org/linux/man-pages/man2/openat2.2.html)
pub fn openat2<P: ?Sized + NixPath>(
dirfd: RawFd,
pub fn openat2<P: ?Sized + NixPath, Fd: std::os::fd::AsFd>(
dirfd: Fd,
path: &P,
mut how: OpenHow,
) -> Result<RawFd> {
) -> Result<OwnedFd> {
use std::os::fd::AsRawFd;
use std::os::fd::FromRawFd;
let fd = path.with_nix_path(|cstr| unsafe {
libc::syscall(
libc::SYS_openat2,
dirfd,
dirfd.as_fd().as_raw_fd(),
cstr.as_ptr(),

@@ -373,5 +417,9 @@ &mut how as *mut OpenHow,

)
})?;
})? as RawFd;
Errno::result(fd)?;
Errno::result(fd as RawFd)
// SAFETY:
//
// `openat2(2)` should return a valid owned fd on success
Ok( unsafe { OwnedFd::from_raw_fd(fd) } )
}

@@ -390,14 +438,16 @@ }

#[cfg(not(target_os = "redox"))]
pub fn renameat<P1: ?Sized + NixPath, P2: ?Sized + NixPath>(
old_dirfd: Option<RawFd>,
pub fn renameat<P1: ?Sized + NixPath, P2: ?Sized + NixPath, Fd1: std::os::fd::AsFd, Fd2: std::os::fd::AsFd>(
old_dirfd: Fd1,
old_path: &P1,
new_dirfd: Option<RawFd>,
new_dirfd: Fd2,
new_path: &P2,
) -> Result<()> {
use std::os::fd::AsRawFd;
let res = old_path.with_nix_path(|old_cstr| {
new_path.with_nix_path(|new_cstr| unsafe {
libc::renameat(
at_rawfd(old_dirfd),
old_dirfd.as_fd().as_raw_fd(),
old_cstr.as_ptr(),
at_rawfd(new_dirfd),
new_dirfd.as_fd().as_raw_fd(),
new_cstr.as_ptr(),

@@ -439,15 +489,17 @@ )

#[cfg(all(target_os = "linux", target_env = "gnu"))]
pub fn renameat2<P1: ?Sized + NixPath, P2: ?Sized + NixPath>(
old_dirfd: Option<RawFd>,
pub fn renameat2<P1: ?Sized + NixPath, P2: ?Sized + NixPath, Fd1: std::os::fd::AsFd, Fd2: std::os::fd::AsFd>(
old_dirfd: Fd1,
old_path: &P1,
new_dirfd: Option<RawFd>,
new_dirfd: Fd2,
new_path: &P2,
flags: RenameFlags,
) -> Result<()> {
use std::os::fd::AsRawFd;
let res = old_path.with_nix_path(|old_cstr| {
new_path.with_nix_path(|new_cstr| unsafe {
libc::renameat2(
at_rawfd(old_dirfd),
old_dirfd.as_fd().as_raw_fd(),
old_cstr.as_ptr(),
at_rawfd(new_dirfd),
new_dirfd.as_fd().as_raw_fd(),
new_cstr.as_ptr(),

@@ -467,3 +519,12 @@ flags.bits(),

fn readlink_maybe_at<P: ?Sized + NixPath>(
/// Read the symlink specified by `path` and `dirfd` and put the contents in `v`.
/// Return the number of bytes placed in `v`.
///
/// This function can call `readlink(2)` or `readlinkat(2)` depending on if `dirfd`
/// is some, if it is, then `readlinkat(2)` is called, otherwise, call `readlink(2)`.
///
/// # Safety
///
/// This function is not I/O-safe considering it employs the `RawFd` type.
unsafe fn readlink_maybe_at<P: ?Sized + NixPath>(
dirfd: Option<RawFd>,

@@ -476,3 +537,3 @@ path: &P,

#[cfg(target_os = "redox")]
Some(_) => unreachable!(),
Some(_) => unreachable!("redox does not have readlinkat(2)"),
#[cfg(not(target_os = "redox"))]

@@ -494,3 +555,11 @@ Some(dirfd) => libc::readlinkat(

fn inner_readlink<P: ?Sized + NixPath>(
/// The actual implementation of [`readlink(2)`] or [`readlinkat(2)`].
///
/// This function can call `readlink(2)` or `readlinkat(2)` depending on if `dirfd`
/// is some, if it is, then `readlinkat(2)` is called, otherwise, call `readlink(2)`.
///
/// # Safety
///
/// This function is marked unsafe because it uses `RawFd`.
unsafe fn inner_readlink<P: ?Sized + NixPath>(
dirfd: Option<RawFd>,

@@ -507,3 +576,8 @@ path: &P,

// simple case: result is strictly less than `PATH_MAX`
let res = readlink_maybe_at(dirfd, path, &mut v)?;
// SAFETY:
//
// If this call of `readlink_maybe_at()` is safe or not depends on the
// usage of `unsafe fn inner_readlink()`.
let res = unsafe { readlink_maybe_at(dirfd, path, &mut v)? };
let len = Errno::result(res)?;

@@ -521,5 +595,12 @@ debug_assert!(len >= 0);

#[cfg(target_os = "redox")]
Some(_) => unreachable!(),
Some(_) => unreachable!("redox does not have readlinkat(2)"),
#[cfg(any(linux_android, target_os = "freebsd", target_os = "hurd"))]
Some(dirfd) => {
// SAFETY:
//
// If this call of `borrow_raw()` is safe or not depends on the
// usage of `unsafe fn inner_readlink()`.
let dirfd = unsafe {
std::os::fd::BorrowedFd::borrow_raw(dirfd)
};
let flags = if path.is_empty() {

@@ -531,3 +612,3 @@ AtFlags::AT_EMPTY_PATH

super::sys::stat::fstatat(
Some(dirfd),
dirfd,
path,

@@ -543,7 +624,12 @@ flags | AtFlags::AT_SYMLINK_NOFOLLOW,

)))]
Some(dirfd) => super::sys::stat::fstatat(
Some(dirfd),
path,
AtFlags::AT_SYMLINK_NOFOLLOW,
),
Some(dirfd) => {
// SAFETY:
//
// If this call of `borrow_raw()` is safe or not depends on the
// usage of `unsafe fn inner_readlink()`.
let dirfd = unsafe {
std::os::fd::BorrowedFd::borrow_raw(dirfd)
};
super::sys::stat::fstatat(dirfd, path, AtFlags::AT_SYMLINK_NOFOLLOW)
},
None => super::sys::stat::lstat(path),

@@ -568,3 +654,7 @@ }

v.reserve_exact(try_size);
let res = readlink_maybe_at(dirfd, path, &mut v)?;
// SAFETY:
//
// If this call of `readlink_maybe_at()` is safe or not depends on the
// usage of `unsafe fn inner_readlink()`.
let res = unsafe { readlink_maybe_at(dirfd, path, &mut v)? };
let len = Errno::result(res)?;

@@ -592,3 +682,12 @@ debug_assert!(len >= 0);

pub fn readlink<P: ?Sized + NixPath>(path: &P) -> Result<OsString> {
inner_readlink(None, path)
// argument `dirfd` should be `None` since we call it from `readlink()`
//
// Do NOT call it with `Some(AT_CWD)` as in that way, we are emulating
// `readlink(2)` with `readlinkat(2)`, which will make us lose `readlink(2)`
// on Redox.
//
// SAFETY:
//
// It is definitely safe because the argument involving `RawFd` is `None`
unsafe { inner_readlink(None, path) }
}

@@ -598,4 +697,5 @@

///
/// Equivalent to [`readlink` ] except where `path` specifies a relative path. In that case,
/// interpret `path` relative to open file specified by `dirfd`.
/// Equivalent to [`readlink` ] except for the case where `path` specifies a
/// relative path, `path` will be interpreted relative to the path specified
/// by `dirfd`. (Use [`AT_FDCWD`] to make it relative to the working directory).
///

@@ -605,8 +705,14 @@ /// # See Also

#[cfg(not(target_os = "redox"))]
pub fn readlinkat<P: ?Sized + NixPath>(
dirfd: Option<RawFd>,
pub fn readlinkat<Fd: std::os::fd::AsFd,P: ?Sized + NixPath>(
dirfd: Fd,
path: &P,
) -> Result<OsString> {
let dirfd = at_rawfd(dirfd);
inner_readlink(Some(dirfd), path)
use std::os::fd::AsRawFd;
// argument `dirfd` should be `Some` since we call it from `readlinkat()`
//
// SAFETY:
//
// The passed `RawFd` should be valid since it is borrowed from `Fd: AsFd`.
unsafe { inner_readlink(Some(dirfd.as_fd().as_raw_fd()), path) }
}

@@ -721,2 +827,35 @@ }

F_GETPATH_NOFIRMLINK(&'a mut PathBuf),
/// Issue an advisory read async with no copy to user
#[cfg(apple_targets)]
F_RDADVISE(libc::radvisory),
/// Turn read ahead off/on
#[cfg(apple_targets)]
F_RDAHEAD(bool),
/// Pre-allocate storage with different policies on fd.
/// Note that we want a mutable reference for the OUT
/// fstore_t field fst_bytesalloc.
#[cfg(apple_targets)]
F_PREALLOCATE(&'a mut libc::fstore_t),
#[cfg(apple_targets)]
/// Get disk device information. In practice,
/// only the file offset data is set.
F_LOG2PHYS(&'a mut libc::off_t),
#[cfg(apple_targets)]
/// Get disk device information. In practice,
/// only the file offset data is set.
/// The difference with F_LOG2PHYS is the struct passed
/// is used as both IN/OUT as both its l2p_devoffset and
/// l2p_contigbytes can be used for more specific queries.
F_LOG2PHYS_EXT(&'a mut libc::log2phys),
/// Transfer any extra space in the file past the logical EOF
/// (as previously allocated via F_PREALLOCATE) to another file.
/// The other file is specified via a file descriptor as the lone extra argument.
/// Both descriptors must reference regular files in the same volume.
#[cfg(apple_targets)]
F_TRANSFEREXTENTS(RawFd),
/// Set or clear the read ahead (pre-fetch) amount for sequential access or
/// disable it with 0 or to system default for any value < 0.
/// It manages how the kernel caches file data.
#[cfg(target_os = "freebsd")]
F_READAHEAD(c_int),
// TODO: Rest of flags

@@ -750,3 +889,6 @@ }

// TODO: Figure out how to handle value fcntl returns
pub fn fcntl(fd: RawFd, arg: FcntlArg) -> Result<c_int> {
pub fn fcntl<Fd: std::os::fd::AsFd>(fd: Fd, arg: FcntlArg) -> Result<c_int> {
use std::os::fd::AsRawFd;
let fd = fd.as_fd().as_raw_fd();
let res = unsafe {

@@ -828,2 +970,35 @@ match arg {

},
#[cfg(apple_targets)]
F_RDADVISE(rad) => {
libc::fcntl(fd, libc::F_RDADVISE, &rad)
},
#[cfg(apple_targets)]
F_LOG2PHYS(offset) => {
let mut info: libc::log2phys = std::mem::zeroed();
let res = libc::fcntl(fd, libc::F_LOG2PHYS, &mut info);
let ok_res = Errno::result(res)?;
*offset = info.l2p_devoffset;
return Ok(ok_res)
}
#[cfg(apple_targets)]
F_LOG2PHYS_EXT(info) => {
libc::fcntl(fd, libc::F_LOG2PHYS_EXT, info)
}
#[cfg(apple_targets)]
F_RDAHEAD(on) => {
let val = if on { 1 } else { 0 };
libc::fcntl(fd, libc::F_RDAHEAD, val)
},
#[cfg(apple_targets)]
F_PREALLOCATE(st) => {
libc::fcntl(fd, libc::F_PREALLOCATE, st)
},
#[cfg(apple_targets)]
F_TRANSFEREXTENTS(rawfd) => {
libc::fcntl(fd, libc::F_TRANSFEREXTENTS, rawfd)
},
#[cfg(target_os = "freebsd")]
F_READAHEAD(val) => {
libc::fcntl(fd, libc::F_READAHEAD, val)
},
}

@@ -885,3 +1060,3 @@ };

#[cfg(not(any(target_os = "redox", target_os = "solaris")))]
pub unsafe trait Flockable: AsRawFd {}
pub unsafe trait Flockable: std::os::fd::AsRawFd {}

@@ -1070,3 +1245,3 @@ /// Represents an owned flock, which unlocks on drop.

#[cfg(any(linux_android, target_os = "freebsd"))]
pub fn copy_file_range<Fd1: AsFd, Fd2: AsFd>(
pub fn copy_file_range<Fd1: std::os::fd::AsFd, Fd2: std::os::fd::AsFd>(
fd_in: Fd1,

@@ -1078,2 +1253,4 @@ off_in: Option<&mut i64>,

) -> Result<usize> {
use std::os::fd::AsRawFd;
let off_in = off_in

@@ -1122,3 +1299,3 @@ .map(|offset| offset as *mut i64)

#[cfg(linux_android)]
pub fn splice<Fd1: AsFd, Fd2: AsFd>(
pub fn splice<Fd1: std::os::fd::AsFd, Fd2: std::os::fd::AsFd>(
fd_in: Fd1,

@@ -1131,2 +1308,4 @@ off_in: Option<&mut libc::loff_t>,

) -> Result<usize> {
use std::os::fd::AsRawFd;
let off_in = off_in

@@ -1150,3 +1329,3 @@ .map(|offset| offset as *mut libc::loff_t)

#[cfg(linux_android)]
pub fn tee<Fd1: AsFd, Fd2: AsFd>(
pub fn tee<Fd1: std::os::fd::AsFd, Fd2: std::os::fd::AsFd>(
fd_in: Fd1,

@@ -1157,2 +1336,4 @@ fd_out: Fd2,

) -> Result<usize> {
use std::os::fd::AsRawFd;
let ret = unsafe { libc::tee(fd_in.as_fd().as_raw_fd(), fd_out.as_fd().as_raw_fd(), len, flags.bits()) };

@@ -1167,3 +1348,3 @@ Errno::result(ret).map(|r| r as usize)

#[cfg(linux_android)]
pub fn vmsplice<F: AsFd>(
pub fn vmsplice<F: std::os::fd::AsFd>(
fd: F,

@@ -1173,2 +1354,4 @@ iov: &[std::io::IoSlice<'_>],

) -> Result<usize> {
use std::os::fd::AsRawFd;
let ret = unsafe {

@@ -1214,3 +1397,3 @@ libc::vmsplice(

///
/// Gaurantees that a subsequent write will not fail due to lack of space.
/// Guarantees that a subsequent write will not fail due to lack of space.
FALLOC_FL_UNSHARE_RANGE;

@@ -1229,4 +1412,4 @@ }

#[cfg(feature = "fs")]
pub fn fallocate(
fd: RawFd,
pub fn fallocate<Fd: std::os::fd::AsFd>(
fd: Fd,
mode: FallocateFlags,

@@ -1236,3 +1419,5 @@ offset: libc::off_t,

) -> Result<()> {
let res = unsafe { libc::fallocate(fd, mode.bits(), offset, len) };
use std::os::fd::AsRawFd;
let res = unsafe { libc::fallocate(fd.as_fd().as_raw_fd(), mode.bits(), offset, len) };
Errno::result(res).map(drop)

@@ -1304,3 +1489,3 @@ }

/// while (!range.is_empty()) {
/// range = fspacectl(f.as_raw_fd(), range).unwrap();
/// range = fspacectl(&f, range).unwrap();
/// }

@@ -1313,3 +1498,5 @@ /// let mut buf = vec![0; INITIAL.len()];

#[inline] // Delays codegen, preventing linker errors with dylibs and --no-allow-shlib-undefined
pub fn fspacectl(fd: RawFd, range: SpacectlRange) -> Result<SpacectlRange> {
pub fn fspacectl<Fd: std::os::fd::AsFd>(fd: Fd, range: SpacectlRange) -> Result<SpacectlRange> {
use std::os::fd::AsRawFd;
let mut rqsr = libc::spacectl_range {

@@ -1321,3 +1508,3 @@ r_offset: range.0,

libc::fspacectl(
fd,
fd.as_fd().as_raw_fd(),
libc::SPACECTL_DEALLOC, // Only one command is supported ATM

@@ -1357,3 +1544,3 @@ &rqsr,

/// f.write_all(INITIAL).unwrap();
/// fspacectl_all(f.as_raw_fd(), 3, 6).unwrap();
/// fspacectl_all(&f, 3, 6).unwrap();
/// let mut buf = vec![0; INITIAL.len()];

@@ -1365,7 +1552,9 @@ /// f.read_exact_at(&mut buf, 0).unwrap();

#[inline] // Delays codegen, preventing linker errors with dylibs and --no-allow-shlib-undefined
pub fn fspacectl_all(
fd: RawFd,
pub fn fspacectl_all<Fd: std::os::fd::AsFd>(
fd: Fd,
offset: libc::off_t,
len: libc::off_t,
) -> Result<()> {
use std::os::fd::AsRawFd;
let mut rqsr = libc::spacectl_range {

@@ -1378,3 +1567,3 @@ r_offset: offset,

libc::fspacectl(
fd,
fd.as_fd().as_raw_fd(),
libc::SPACECTL_DEALLOC, // Only one command is supported ATM

@@ -1402,3 +1591,2 @@ &rqsr,

use crate::Result;
use std::os::unix::io::RawFd;

@@ -1435,4 +1623,4 @@ #[cfg(feature = "fs")]

/// * [`posix_fadvise`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_fadvise.html)
pub fn posix_fadvise(
fd: RawFd,
pub fn posix_fadvise<Fd: std::os::fd::AsFd>(
fd: Fd,
offset: libc::off_t,

@@ -1442,4 +1630,6 @@ len: libc::off_t,

) -> Result<()> {
let res = unsafe { libc::posix_fadvise(fd, offset, len, advice as libc::c_int) };
use std::os::fd::AsRawFd;
let res = unsafe { libc::posix_fadvise(fd.as_fd().as_raw_fd(), offset, len, advice as libc::c_int) };
if res == 0 {

@@ -1465,8 +1655,10 @@ Ok(())

))]
pub fn posix_fallocate(
fd: RawFd,
pub fn posix_fallocate<Fd: std::os::fd::AsFd>(
fd: Fd,
offset: libc::off_t,
len: libc::off_t,
) -> Result<()> {
let res = unsafe { libc::posix_fallocate(fd, offset, len) };
use std::os::fd::AsRawFd;
let res = unsafe { libc::posix_fallocate(fd.as_fd().as_raw_fd(), offset, len) };
match Errno::result(res) {

@@ -1473,0 +1665,0 @@ Err(err) => Err(err),

//! Feature tests for OS functionality
pub use self::os::*;
#[cfg(linux_android)]
#[cfg(any(linux_android, target_os = "emscripten"))]
mod os {

@@ -95,3 +95,3 @@ use crate::sys::utsname::uname;

#[test]
pub fn test_parsing_kernel_version() {
fn test_parsing_kernel_version() {
assert!(kernel_version().unwrap() > 0);

@@ -107,2 +107,3 @@ }

target_os = "redox", // Since 1-july-2020
target_os = "cygwin",
))]

@@ -109,0 +110,0 @@ mod os {

@@ -96,3 +96,3 @@ //! Query network interface addresses

let mut addr = InterfaceAddress {
interface_name: ifname.to_string_lossy().to_string(),
interface_name: ifname.to_string_lossy().into_owned(),
flags: InterfaceFlags::from_bits_truncate(

@@ -99,0 +99,0 @@ info.ifa_flags as IflagsType,

@@ -37,2 +37,3 @@ //! Rust friendly bindings to the various *nix system functions.

//! * `signal` - Send and receive signals to processes
//! * `syslog` - System logging
//! * `term` - Terminal control APIs

@@ -47,5 +48,8 @@ //! * `time` - Query the operating system's clocks

#![allow(non_camel_case_types)]
#![cfg_attr(test, deny(warnings))]
// A clear document is a good document no matter if it has a summary in its
// first paragraph or not.
#![allow(clippy::too_long_first_doc_paragraph)]
#![recursion_limit = "500"]
#![deny(unused)]
#![deny(unexpected_cfgs)]
#![allow(unused_macros)]

@@ -81,2 +85,3 @@ #![cfg_attr(

feature = "signal",
feature = "syslog",
feature = "term",

@@ -98,2 +103,7 @@ feature = "time",

#![deny(unsafe_op_in_unsafe_fn)]
// I found the change suggested by this rules could hurt code readability. I cannot
// remeber every type's default value, in such cases, it forces me to open
// the std doc to insepct the Default value, which is unnecessary with
// `.unwrap_or(value)`.
#![allow(clippy::unwrap_or_default)]

@@ -147,3 +157,7 @@ // Re-exported external crates

}
#[cfg(any(freebsdlike, target_os = "linux", target_os = "netbsd"))]
#[cfg(any(
freebsdlike,
all(target_os = "linux", not(target_env = "ohos")),
target_os = "netbsd"
))]
feature! {

@@ -193,2 +207,19 @@ #![feature = "mqueue"]

#[cfg(any(
target_os = "freebsd",
target_os = "haiku",
target_os = "linux",
target_os = "netbsd",
apple_targets
))]
feature! {
#![feature = "process"]
pub mod spawn;
}
feature! {
#![feature = "syslog"]
pub mod syslog;
}
use std::ffi::{CStr, CString, OsStr};

@@ -214,3 +245,3 @@ use std::mem::MaybeUninit;

/// * Represents all of the system's errnos, instead of just the most common
/// ones.
/// ones.
pub type Error = Errno;

@@ -299,3 +330,3 @@

// The real PATH_MAX is typically 4096, but it's statistically unlikely to have a path
// longer than ~300 bytes. See the the PR description to get stats for your own machine.
// longer than ~300 bytes. See the PR description to get stats for your own machine.
// https://github.com/nix-rust/nix/pull/1656

@@ -302,0 +333,0 @@ //

@@ -390,3 +390,3 @@ #[cfg(target_os = "freebsd")]

#[cfg(target_os = "freebsd")]
impl<'a> Drop for Nmount<'a> {
impl Drop for Nmount<'_> {
fn drop(&mut self) {

@@ -393,0 +393,0 @@ for (iov, is_owned) in self.iov.iter().zip(self.is_owned.iter()) {

@@ -263,6 +263,8 @@ //! Posix Message Queue functions

/// Set the attributes of the message queue. Only `O_NONBLOCK` can be set, everything else will be ignored
/// Returns the old attributes
/// It is recommend to use the `mq_set_nonblock()` and `mq_remove_nonblock()` convenience functions as they are easier to use
/// Set the attributes of the message queue. Only `O_NONBLOCK` can be set,
/// everything else will be ignored. Returns the old attributes.
///
/// It is recommend to use the `mq_set_nonblock()` and `mq_remove_nonblock()`
/// convenience functions as they are easier to use.
///
/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_setattr.html)

@@ -269,0 +271,0 @@ pub fn mq_setattr(mqd: &MqdT, newattr: &MqAttr) -> Result<MqAttr> {

@@ -54,3 +54,3 @@ //! Network interface name resolution.

/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
#[cfg(not(target_os = "haiku"))]
#[cfg(not(any(target_os = "haiku", target_os = "cygwin")))]
IFF_DEBUG as IflagsType;

@@ -70,3 +70,4 @@ /// Interface is a loopback interface. (see

target_os = "fuchsia",
target_os = "netbsd"))]
target_os = "netbsd",
target_os = "cygwin"))]
IFF_NOTRAILERS as IflagsType;

@@ -82,3 +83,4 @@ /// Interface manages own routes.

solarish,
target_os = "fuchsia"))]
target_os = "fuchsia",
target_os = "cygwin"))]
IFF_RUNNING as IflagsType;

@@ -93,2 +95,3 @@ /// No arp protocol, L2 destination address not set. (see

/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
#[cfg(not(target_os = "cygwin"))]
IFF_ALLMULTI as IflagsType;

@@ -152,3 +155,3 @@ /// Master of a load balancing bundle. (see

/// Driver signals L1 up. Volatile.
#[cfg(any(target_os = "fuchsia", target_os = "linux"))]
#[cfg(any(target_os = "fuchsia", target_os = "linux", target_os = "cygwin"))]
IFF_LOWER_UP;

@@ -165,3 +168,3 @@ /// Interface is in polling mode.

/// Driver signals dormant. Volatile.
#[cfg(any(target_os = "fuchsia", target_os = "linux"))]
#[cfg(any(target_os = "fuchsia", target_os = "linux", target_os = "cygwin"))]
IFF_DORMANT;

@@ -168,0 +171,0 @@ /// User-requested promisc mode.

@@ -5,3 +5,3 @@ //! Wait for events to trigger on specific file descriptors

use crate::errno::Errno;
pub use crate::poll_timeout::PollTimeout;
pub use crate::poll_timeout::{PollTimeout, PollTimeoutTryFromError};
use crate::Result;

@@ -15,6 +15,6 @@

///
/// After a call to `poll` or `ppoll`, the events that occurred can be
/// retrieved by calling [`revents()`](#method.revents) on the `PollFd`.
/// After a call to `poll` or `ppoll`, the events that occurred can be retrieved by calling
/// [`revents()`](#method.revents) on the `PollFd` object from the array passed to `poll`.
#[repr(transparent)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct PollFd<'fd> {

@@ -38,7 +38,4 @@ pollfd: libc::pollfd,

/// let pfd = PollFd::new(r.as_fd(), PollFlags::POLLIN);
/// let mut fds = [pfd];
/// poll(&mut fds, PollTimeout::NONE).unwrap();
/// let mut buf = [0u8; 80];
/// read(r.as_raw_fd(), &mut buf[..]);
/// ```
/// These are placed in an array and passed to [`poll`] or [`ppoll`](fn.ppoll.html).
// Unlike I/O functions, constructors like this must take `BorrowedFd`

@@ -67,3 +64,3 @@ // instead of AsFd or &AsFd. Otherwise, an `OwnedFd` argument would be

/// `None` if the kernel provides status flags that Nix does not know about.
pub fn revents(self) -> Option<PollFlags> {
pub fn revents(&self) -> Option<PollFlags> {
PollFlags::from_bits(self.pollfd.revents)

@@ -78,3 +75,3 @@ }

/// This is marginally more efficient than [`PollFd::all`].
pub fn any(self) -> Option<bool> {
pub fn any(&self) -> Option<bool> {
Some(self.revents()? != PollFlags::empty())

@@ -89,3 +86,3 @@ }

/// This is marginally less efficient than [`PollFd::any`].
pub fn all(self) -> Option<bool> {
pub fn all(&self) -> Option<bool> {
Some(self.revents()? & self.events() == self.events())

@@ -95,3 +92,3 @@ }

/// The events of interest for this `PollFd`.
pub fn events(self) -> PollFlags {
pub fn events(&self) -> PollFlags {
PollFlags::from_bits(self.pollfd.events).unwrap()

@@ -106,3 +103,3 @@ }

impl<'fd> AsFd for PollFd<'fd> {
impl AsFd for PollFd<'_> {
fn as_fd(&self) -> BorrowedFd<'_> {

@@ -207,2 +204,30 @@ // Safety:

/// descriptors are ready.
///
/// The return value contains the number of `fds` which have selected events ([`PollFd::revents`]).
///
/// # Examples
/// ```no_run
/// # use std::os::unix::io::{AsFd, AsRawFd, FromRawFd};
/// # use nix::{
/// # poll::{PollTimeout, PollFd, PollFlags, poll},
/// # unistd::{pipe, read}
/// # };
/// let (r0, w0) = pipe().unwrap();
/// let (r1, w1) = pipe().unwrap();
///
/// let mut pollfds = [
/// PollFd::new(r0.as_fd(), PollFlags::POLLIN),
/// PollFd::new(r1.as_fd(), PollFlags::POLLIN),
/// ];
///
/// let nready = poll(&mut pollfds, PollTimeout::NONE).unwrap();
/// assert!(nready >= 1); // Since there is no timeout
///
/// let mut buf = [0u8; 80];
/// if pollfds[0].any().unwrap_or_default() {
/// read(&r0, &mut buf[..]);
/// } else if pollfds[1].any().unwrap_or_default() {
/// read(&r1, &mut buf[..]);
/// }
/// ```
pub fn poll<T: Into<PollTimeout>>(

@@ -229,3 +254,3 @@ fds: &mut [PollFd],

///
/// `ppoll` behaves like `poll`, but let you specify what signals may interrupt it
/// `ppoll` behaves like [`poll`], but let you specify what signals may interrupt it
/// with the `sigmask` argument. If you want `ppoll` to block indefinitely,

@@ -232,0 +257,0 @@ /// specify `None` as `timeout` (it is like `timeout = -1` for `poll`).

@@ -55,2 +55,13 @@ //! Create master and slave virtual pseudo-terminals (PTYs)

impl PtyMaster {
/// Constructs a `PytMaster` wrapping an existing `OwnedFd`.
///
/// # Safety
///
/// `OwnedFd` is a valid `PtyMaster`.
pub unsafe fn from_owned_fd(fd: OwnedFd) -> Self {
Self(fd)
}
}
impl AsRawFd for PtyMaster {

@@ -68,2 +79,8 @@ fn as_raw_fd(&self) -> RawFd {

impl From<PtyMaster> for OwnedFd {
fn from(value: PtyMaster) -> Self {
value.0
}
}
impl IntoRawFd for PtyMaster {

@@ -78,3 +95,3 @@ fn into_raw_fd(self) -> RawFd {

fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
unistd::read(self.0.as_raw_fd(), buf).map_err(io::Error::from)
unistd::read(&self.0, buf).map_err(io::Error::from)
}

@@ -94,3 +111,3 @@ }

fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
unistd::read(self.0.as_raw_fd(), buf).map_err(io::Error::from)
unistd::read(&self.0, buf).map_err(io::Error::from)
}

@@ -324,5 +341,5 @@ }

/// In a multithreaded program, only [async-signal-safe] functions like `pause`
/// and `_exit` may be called by the child (the parent isn't restricted). Note
/// that memory allocation may **not** be async-signal-safe and thus must be
/// prevented.
/// and `_exit` may be called by the child (the parent isn't restricted) until
/// a call of `execve(2)`. Note that memory allocation may **not** be
/// async-signal-safe and thus must be prevented.
///

@@ -329,0 +346,0 @@ /// Those functions are only a small subset of your operating system's API, so

@@ -123,3 +123,6 @@ //! Execution scheduling

libc::clone(
mem::transmute(
mem::transmute::<
extern "C" fn(*mut Box<dyn FnMut() -> isize>) -> i32,
extern "C" fn(*mut libc::c_void) -> i32,
>(
callback

@@ -126,0 +129,0 @@ as extern "C" fn(*mut Box<dyn FnMut() -> isize>) -> i32,

@@ -196,3 +196,3 @@ // vim: tw=80

impl<'a> Debug for AioCb<'a> {
impl Debug for AioCb<'_> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {

@@ -206,3 +206,3 @@ fmt.debug_struct("AioCb")

impl<'a> Drop for AioCb<'a> {
impl Drop for AioCb<'_> {
/// If the `AioCb` has no remaining state in the kernel, just drop it.

@@ -460,6 +460,5 @@ /// Otherwise, dropping constitutes a resource leak, which is an error

/// * `prio`: If POSIX Prioritized IO is supported, then the
/// operation will be prioritized at the process's
/// priority level minus `prio`.
/// * `sigev_notify`: Determines how you will be notified of event
/// completion.
/// operation will be prioritized at the process's priority level minus
/// `prio`.
/// * `sigev_notify`: Determines how you will be notified of event completion.
pub fn new(

@@ -505,3 +504,3 @@ fd: BorrowedFd<'a>,

impl<'a> AsRef<libc::aiocb> for AioFsync<'a> {
impl AsRef<libc::aiocb> for AioFsync<'_> {
fn as_ref(&self) -> &libc::aiocb {

@@ -580,7 +579,5 @@ &self.aiocb.aiocb.0

/// * `buf`: A memory buffer. It must outlive the `AioRead`.
/// * `prio`: If POSIX Prioritized IO is supported, then the
/// operation will be prioritized at the process's
/// priority level minus `prio`
/// * `sigev_notify`: Determines how you will be notified of event
/// completion.
/// * `prio`: If POSIX Prioritized IO is supported, then the operation
/// will be prioritized at the process's priority level minus `prio`
/// * `sigev_notify`: Determines how you will be notified of event completion.
pub fn new(

@@ -617,3 +614,3 @@ fd: BorrowedFd<'a>,

impl<'a> AsMut<libc::aiocb> for AioRead<'a> {
impl AsMut<libc::aiocb> for AioRead<'_> {
fn as_mut(&mut self) -> &mut libc::aiocb {

@@ -624,3 +621,3 @@ &mut self.aiocb.aiocb.0

impl<'a> AsRef<libc::aiocb> for AioRead<'a> {
impl AsRef<libc::aiocb> for AioRead<'_> {
fn as_ref(&self) -> &libc::aiocb {

@@ -742,3 +739,3 @@ &self.aiocb.aiocb.0

#[cfg(target_os = "freebsd")]
impl<'a> AsMut<libc::aiocb> for AioReadv<'a> {
impl AsMut<libc::aiocb> for AioReadv<'_> {
fn as_mut(&mut self) -> &mut libc::aiocb {

@@ -750,3 +747,3 @@ &mut self.aiocb.aiocb.0

#[cfg(target_os = "freebsd")]
impl<'a> AsRef<libc::aiocb> for AioReadv<'a> {
impl AsRef<libc::aiocb> for AioReadv<'_> {
fn as_ref(&self) -> &libc::aiocb {

@@ -817,7 +814,5 @@ &self.aiocb.aiocb.0

/// * `buf`: A memory buffer. It must outlive the `AioWrite`.
/// * `prio`: If POSIX Prioritized IO is supported, then the
/// operation will be prioritized at the process's
/// priority level minus `prio`
/// * `sigev_notify`: Determines how you will be notified of event
/// completion.
/// * `prio`: If POSIX Prioritized IO is supported, then the operation
/// will be prioritized at the process's priority level minus `prio`
/// * `sigev_notify`: Determines how you will be notified of event completion.
pub fn new(

@@ -858,3 +853,3 @@ fd: BorrowedFd<'a>,

impl<'a> AsMut<libc::aiocb> for AioWrite<'a> {
impl AsMut<libc::aiocb> for AioWrite<'_> {
fn as_mut(&mut self) -> &mut libc::aiocb {

@@ -865,3 +860,3 @@ &mut self.aiocb.aiocb.0

impl<'a> AsRef<libc::aiocb> for AioWrite<'a> {
impl AsRef<libc::aiocb> for AioWrite<'_> {
fn as_ref(&self) -> &libc::aiocb {

@@ -980,3 +975,3 @@ &self.aiocb.aiocb.0

#[cfg(target_os = "freebsd")]
impl<'a> AsMut<libc::aiocb> for AioWritev<'a> {
impl AsMut<libc::aiocb> for AioWritev<'_> {
fn as_mut(&mut self) -> &mut libc::aiocb {

@@ -988,3 +983,3 @@ &mut self.aiocb.aiocb.0

#[cfg(target_os = "freebsd")]
impl<'a> AsRef<libc::aiocb> for AioWritev<'a> {
impl AsRef<libc::aiocb> for AioWritev<'_> {
fn as_ref(&self) -> &libc::aiocb {

@@ -991,0 +986,0 @@ &self.aiocb.aiocb.0

use crate::errno::Errno;
pub use crate::poll_timeout::PollTimeout as EpollTimeout;
pub use crate::poll_timeout::PollTimeoutTryFromError as EpollTimeoutTryFromError;
use crate::Result;

@@ -91,3 +92,3 @@ use libc::{self, c_int};

/// // Arm eventfd & Time wait
/// eventfd.arm()?;
/// eventfd.write(1)?;
/// let now = Instant::now();

@@ -209,3 +210,6 @@ ///

#[deprecated(since = "0.27.0", note = "Use Epoll::epoll_ctl() instead")]
#[deprecated(
since = "0.27.0",
note = "Use corresponding Epoll methods instead"
)]
#[inline]

@@ -212,0 +216,0 @@ pub fn epoll_ctl<'a, T>(

@@ -176,3 +176,3 @@ //! Kernel event notification mechanism

// that wouldn't simply be repeating the man page.
pub struct EventFlag: type_of_event_flag {
pub struct EvFlags: type_of_event_flag {
#[allow(missing_docs)]

@@ -220,2 +220,6 @@ EV_ADD;

#[deprecated(since = "0.30.0", note = "Use `EvFlags instead`")]
/// The deprecated EventFlag type alias
pub type EventFlag = EvFlags;
libc_bitflags!(

@@ -352,3 +356,3 @@ /// Filter-specific flags. See the man page for details.

filter: EventFilter,
flags: EventFlag,
flags: EvFlags,
fflags: FilterFlag,

@@ -388,4 +392,4 @@ data: intptr_t,

/// [`Kqueue::kevent`].
pub fn flags(&self) -> EventFlag {
EventFlag::from_bits(self.kevent.flags).unwrap()
pub fn flags(&self) -> EvFlags {
EvFlags::from_bits(self.kevent.flags).unwrap()
}

@@ -450,3 +454,3 @@

filter: EventFilter,
flags: EventFlag,
flags: EvFlags,
fflags: FilterFlag,

@@ -453,0 +457,0 @@ udata: intptr_t,

use crate::errno::Errno;
use crate::{Result,unistd};
use std::os::unix::io::{FromRawFd, OwnedFd, AsRawFd, AsFd, RawFd, BorrowedFd};
use crate::{unistd, Result};
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, OwnedFd, RawFd};
libc_bitflags! {
/// Eventfd flags.
pub struct EfdFlags: libc::c_int {
/// Set the close-on-exec (`FD_CLOEXEC`) flag on the new event file descriptor.
EFD_CLOEXEC; // Since Linux 2.6.27/FreeBSD 13.0
/// Set the `O_NONBLOCK` file status flag on the new event file description.
EFD_NONBLOCK; // Since Linux 2.6.27/FreeBSD 13.0
/// Provide semaphore-like semantics for reads from the new event file
/// descriptor.
EFD_SEMAPHORE; // Since Linux 2.6.30/FreeBSD 13.0

@@ -13,3 +18,7 @@ }

#[deprecated(since = "0.28.0", note = "Use EventFd::from_value_and_flags() instead")]
#[deprecated(
since = "0.28.0",
note = "Use EventFd::from_value_and_flags() instead"
)]
#[allow(missing_docs)]
pub fn eventfd(initval: libc::c_uint, flags: EfdFlags) -> Result<OwnedFd> {

@@ -21,5 +30,7 @@ let res = unsafe { libc::eventfd(initval, flags.bits()) };

/// An eventfd file descriptor.
#[derive(Debug)]
#[repr(transparent)]
pub struct EventFd(OwnedFd);
impl EventFd {

@@ -30,9 +41,14 @@ /// [`EventFd::from_value_and_flags`] with `init_val = 0` and `flags = EfdFlags::empty()`.

}
/// Constructs [`EventFd`] with the given `init_val` and `flags`.
///
///
/// Wrapper around [`libc::eventfd`].
pub fn from_value_and_flags(init_val: u32, flags: EfdFlags) -> Result<Self> {
pub fn from_value_and_flags(
init_val: u32,
flags: EfdFlags,
) -> Result<Self> {
let res = unsafe { libc::eventfd(init_val, flags.bits()) };
Errno::result(res).map(|r| Self(unsafe { OwnedFd::from_raw_fd(r) }))
}
/// [`EventFd::from_value_and_flags`] with `init_val = 0` and given `flags`.

@@ -42,2 +58,3 @@ pub fn from_flags(flags: EfdFlags) -> Result<Self> {

}
/// [`EventFd::from_value_and_flags`] with given `init_val` and `flags = EfdFlags::empty()`.

@@ -47,26 +64,42 @@ pub fn from_value(init_val: u32) -> Result<Self> {

}
/// Arms `self`, a following call to `poll`, `select` or `epoll` will return immediately.
///
/// [`EventFd::write`] with `1`.
pub fn arm(&self) -> Result<usize> {
self.write(1)
/// Constructs an `EventFd` wrapping an existing `OwnedFd`.
///
/// # Safety
///
/// `OwnedFd` is a valid eventfd.
pub unsafe fn from_owned_fd(fd: OwnedFd) -> Self {
Self(fd)
}
/// Defuses `self`, a following call to `poll`, `select` or `epoll` will block.
///
/// [`EventFd::write`] with `0`.
pub fn defuse(&self) -> Result<usize> {
self.write(0)
}
/// Enqueues `value` triggers.
///
/// Enqueues `value` triggers, i.e., adds the integer value supplied in `value`
/// to the counter.
///
/// The next `value` calls to `poll`, `select` or `epoll` will return immediately.
///
///
/// [`EventFd::write`] with `value`.
pub fn write(&self, value: u64) -> Result<usize> {
unistd::write(&self.0,&value.to_ne_bytes())
pub fn write(&self, value: u64) -> Result<usize> {
unistd::write(&self.0, &value.to_ne_bytes())
}
// Reads the value from the file descriptor.
/// Reads the value from the file descriptor.
///
/// * If [`EFD_SEMAPHORE`](EfdFlags::EFD_SEMAPHORE) was not specified and
/// the eventfd counter has a nonzero value, then this function returns
/// an `u64` containing that value, and the counter's value is reset to
/// zero.
///
/// * If [`EFD_SEMAPHORE`](EfdFlags::EFD_SEMAPHORE) was specified and the
/// eventfd counter has a nonzero value, then this function returns an
/// `u64` containing the value 1, and the counter's value is decremented
/// by 1.
///
/// * If the eventfd counter is zero at the time of this call, then the
/// call either blocks until the counter becomes nonzero (at which time,
/// this function proceeds as described above) or fails with the error
/// `EAGAIN` if the file descriptor has been made nonblocking with
/// [`EFD_NONBLOCK`](EfdFlags::EFD_NONBLOCK).
pub fn read(&self) -> Result<u64> {
let mut arr = [0; std::mem::size_of::<u64>()];
unistd::read(self.0.as_raw_fd(),&mut arr)?;
unistd::read(&self.0, &mut arr)?;
Ok(u64::from_ne_bytes(arr))

@@ -85,6 +118,7 @@ }

}
impl From<EventFd> for OwnedFd {
fn from(x: EventFd) -> OwnedFd {
x.0
fn from(value: EventFd) -> Self {
value.0
}
}

@@ -14,3 +14,3 @@ //! Monitoring API for filesystem events.

use crate::errno::Errno;
use crate::fcntl::{at_rawfd, OFlag};
use crate::fcntl::OFlag;
use crate::unistd::{close, read, write};

@@ -24,4 +24,4 @@ use crate::{NixPath, Result};

libc_bitflags! {
/// Mask for defining which events shall be listened with
/// [`fanotify_mark`](fn.fanotify_mark.html) and for querying notifications.
/// Mask for defining which events shall be listened with [`Fanotify::mark()`]
/// and for querying notifications.
pub struct MaskFlags: u64 {

@@ -85,3 +85,3 @@ /// File was accessed.

libc_bitflags! {
/// Configuration options for [`fanotify_init`](fn.fanotify_init.html).
/// Configuration options for [`Fanotify::init()`].
pub struct InitFlags: libc::c_uint {

@@ -168,3 +168,3 @@ /// Close-on-exec flag set on the file descriptor.

libc_bitflags! {
/// Configuration options for [`fanotify_mark`](fn.fanotify_mark.html).
/// Configuration options for [`Fanotify::mark()`].
pub struct MarkFlags: libc::c_uint {

@@ -205,4 +205,4 @@ /// Add the events to the marks.

/// Abstract over `libc::fanotify_event_metadata`, which represents an event
/// received via `Fanotify::read_events`.
/// Abstract over [`libc::fanotify_event_metadata`], which represents an event
/// received via [`Fanotify::read_events`].
// Is not Clone due to fd field, to avoid use-after-close scenarios.

@@ -289,3 +289,3 @@ #[derive(Debug, Eq, Hash, PartialEq)]

libc_bitflags! {
/// Response to be wrapped in `FanotifyResponse` and sent to the `Fanotify`
/// Response to be wrapped in [`FanotifyResponse`] and sent to the [`Fanotify`]
/// group to allow or deny an event.

@@ -326,3 +326,2 @@ pub struct Response: u32 {

/// Add, remove, or modify an fanotify mark on a filesystem object.
/// If `dirfd` is `None`, `AT_FDCWD` is used.
///

@@ -332,7 +331,7 @@ /// Returns a Result containing either `()` on success or errno otherwise.

/// For more information, see [fanotify_mark(2)](https://man7.org/linux/man-pages/man7/fanotify_mark.2.html).
pub fn mark<P: ?Sized + NixPath>(
pub fn mark<Fd: std::os::fd::AsFd, P: ?Sized + NixPath>(
&self,
flags: MarkFlags,
mask: MaskFlags,
dirfd: Option<RawFd>,
dirfd: Fd,
path: Option<&P>,

@@ -345,3 +344,3 @@ ) -> Result<()> {

mask.bits(),
at_rawfd(dirfd),
dirfd.as_fd().as_raw_fd(),
p,

@@ -374,3 +373,3 @@ )

let nread = read(self.fd.as_raw_fd(), &mut buffer)?;
let nread = read(&self.fd, &mut buffer)?;

@@ -432,1 +431,27 @@ while (nread - offset) >= metadata_size {

}
impl AsRawFd for Fanotify {
fn as_raw_fd(&self) -> RawFd
{
self.fd.as_raw_fd()
}
}
impl From<Fanotify> for OwnedFd {
fn from(value: Fanotify) -> Self {
value.fd
}
}
impl Fanotify {
/// Constructs a `Fanotify` wrapping an existing `OwnedFd`.
///
/// # Safety
///
/// `OwnedFd` is a valid `Fanotify`.
pub unsafe fn from_owned_fd(fd: OwnedFd) -> Self {
Self {
fd
}
}
}

@@ -204,3 +204,3 @@ //! Monitoring API for filesystem events.

let nread = read(self.fd.as_raw_fd(), &mut buffer)?;
let nread = read(&self.fd, &mut buffer)?;

@@ -243,2 +243,13 @@ while (nread - offset) >= header_size {

}
/// Constructs an `Inotify` wrapping an existing `OwnedFd`.
///
/// # Safety
///
/// `OwnedFd` is a valid `Inotify`.
pub unsafe fn from_owned_fd(fd: OwnedFd) -> Self {
Self {
fd
}
}
}

@@ -259,1 +270,7 @@

}
impl From<Inotify> for OwnedFd {
fn from(value: Inotify) -> Self {
value.fd
}
}
use cfg_if::cfg_if;
/// The datatype used for the ioctl number
#[cfg(any(target_os = "android", target_env = "musl"))]
#[cfg(any(
target_os = "android",
target_os = "fuchsia",
target_env = "musl",
target_env = "ohos"
))]
#[doc(hidden)]
pub type ioctl_num_type = ::libc::c_int;
#[cfg(not(any(target_os = "android", target_env = "musl")))]
#[cfg(not(any(
target_os = "android",
target_os = "fuchsia",
target_env = "musl",
target_env = "ohos"
)))]
#[doc(hidden)]

@@ -9,0 +19,0 @@ pub type ioctl_num_type = ::libc::c_ulong;

@@ -230,7 +230,7 @@ //! Provide helpers for making ioctl system calls.

#[cfg(any(linux_android, target_os = "redox"))]
#[cfg(any(linux_android, target_os = "fuchsia", target_os = "redox"))]
#[macro_use]
mod linux;
#[cfg(any(linux_android, target_os = "redox"))]
#[cfg(any(linux_android, target_os = "fuchsia", target_os = "redox"))]
pub use self::linux::*;

@@ -659,4 +659,6 @@

-> $crate::Result<$crate::libc::c_int> {
let ioty = $ioty;
let nr = $nr;
unsafe {
convert_ioctl_res!($crate::libc::ioctl(fd, request_code_readwrite!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data))
convert_ioctl_res!($crate::libc::ioctl(fd, request_code_readwrite!(ioty, nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data))
}

@@ -663,0 +665,0 @@ }

@@ -7,8 +7,7 @@ //! Interfaces for managing memory-backed files.

use crate::errno::Errno;
use crate::Result;
use std::ffi::CStr;
use crate::{NixPath, Result};
libc_bitflags!(
/// Options that change the behavior of [`memfd_create`].
pub struct MemFdCreateFlag: libc::c_uint {
pub struct MFdFlags: libc::c_uint {
/// Set the close-on-exec ([`FD_CLOEXEC`]) flag on the new file descriptor.

@@ -42,2 +41,14 @@ ///

MFD_HUGETLB;
/// Shift to get the huge page size.
#[cfg(target_env = "ohos")]
MFD_HUGE_SHIFT;
/// Mask to get the huge page size.
#[cfg(target_env = "ohos")]
MFD_HUGE_MASK;
/// hugetlb size of 64KB.
#[cfg(target_env = "ohos")]
MFD_HUGE_64KB;
/// hugetlb size of 512KB.
#[cfg(target_env = "ohos")]
MFD_HUGE_512KB;
/// Following are to be used with [`MFD_HUGETLB`], indicating the desired hugetlb size.

@@ -80,2 +91,6 @@ ///

#[deprecated(since = "0.30.0", note = "Use `MFdFlags instead`")]
/// The deprecated MemFdCreateFlag type alias
pub type MemFdCreateFlag = MFdFlags;
/// Creates an anonymous file that lives in memory, and return a file-descriptor to it.

@@ -90,5 +105,9 @@ ///

#[inline] // Delays codegen, preventing linker errors with dylibs and --no-allow-shlib-undefined
pub fn memfd_create(name: &CStr, flags: MemFdCreateFlag) -> Result<OwnedFd> {
let res = unsafe {
cfg_if! {
pub fn memfd_create<P: NixPath + ?Sized>(
name: &P,
flags: MFdFlags,
) -> Result<OwnedFd> {
let res = name.with_nix_path(|cstr| {
unsafe {
cfg_if! {
if #[cfg(all(

@@ -99,15 +118,17 @@ // Android does not have a memfd_create symbol

target_os = "freebsd",
// If the OS is Linux, gnu and musl expose a memfd_create symbol but not uclibc
// If the OS is Linux, gnu/musl/ohos expose a memfd_create symbol but not uclibc
target_env = "gnu",
target_env = "musl",
target_env = "ohos"
)))]
{
libc::memfd_create(name.as_ptr(), flags.bits())
libc::memfd_create(cstr.as_ptr(), flags.bits())
} else {
libc::syscall(libc::SYS_memfd_create, name.as_ptr(), flags.bits())
libc::syscall(libc::SYS_memfd_create, cstr.as_ptr(), flags.bits())
}
}
};
}
})?;
Errno::result(res).map(|r| unsafe { OwnedFd::from_raw_fd(r as RawFd) })
}

@@ -41,5 +41,9 @@ //! Memory management declarations.

/// Compatibility flag. Ignored.
#[cfg(not(target_os = "solaris"))]
MAP_FILE;
/// Share this mapping. Mutually exclusive with `MAP_PRIVATE`.
MAP_SHARED;
/// Force mmap to check and fail on unknown flags. This also enables `MAP_SYNC`.
#[cfg(target_os = "linux")]
MAP_SHARED_VALIDATE;
/// Create a private copy-on-write mapping. Mutually exclusive with `MAP_SHARED`.

@@ -64,2 +68,3 @@ MAP_PRIVATE;

all(target_os = "linux", target_env = "musl", any(target_arch = "x86", target_arch = "x86_64")),
all(target_os = "linux", target_env = "ohos", target_arch = "x86_64"),
all(target_os = "freebsd", target_pointer_width = "64")))]

@@ -145,4 +150,8 @@ MAP_32BIT;

/// Region grows down, like a stack.
#[cfg(any(linux_android, freebsdlike, target_os = "openbsd"))]
#[cfg(any(linux_android, freebsdlike, netbsdlike))]
MAP_STACK;
/// Do not write through the page caches, write directly to the file. Used for Direct Access (DAX) enabled file systems.
// Available on Linux glibc and musl, MIPS* target excluded.
#[cfg(all(target_os = "linux", not(any(target_arch = "mips", target_arch = "mips64", target_arch = "mips32r6", target_arch = "mips64r6")), not(target_env = "uclibc")))]
MAP_SYNC;
/// Pages in this mapping are not retained in the kernel's memory cache.

@@ -203,2 +212,6 @@ #[cfg(apple_targets)]

MREMAP_FIXED;
/// Works in conjunction with `MREMAP_MAYMOVE` but does not unmap `old_address`.
/// Note that, in this case, `old_size` and `new_size` must be the same.
#[cfg(target_os = "linux")]
MREMAP_DONTUNMAP;
/// Place the mapping at exactly the address specified in `new_address`.

@@ -275,3 +288,3 @@ #[cfg(target_os = "netbsd")]

/// Specify that the application no longer needs the pages in the given range.
#[cfg(not(any(target_os = "aix", target_os = "hurd")))]
#[cfg(not(any(target_os = "aix", target_os = "hurd", target_os = "cygwin")))]
MADV_FREE,

@@ -312,2 +325,21 @@ /// Request that the system not flush the current range to disk unless it needs to.

MADV_CAN_REUSE,
/// Reclaim the address range when applicable.
#[cfg(linux_android)]
MADV_PAGEOUT,
/// Deactivate the address range when applicable.
#[cfg(linux_android)]
MADV_COLD,
/// After fork, the adress range is zero filled.
#[cfg(linux_android)]
MADV_WIPEONFORK,
/// Undo `MADV_WIPEONFORK` when it applied.
#[cfg(linux_android)]
MADV_KEEPONFORK,
/// Pre-load the address range for reading to reduce page-fault latency.
#[cfg(linux_android)]
MADV_POPULATE_READ,
/// Pre-fault the address range for writing to reduce page-fault
/// latency on subsequent writes.
#[cfg(linux_android)]
MADV_POPULATE_WRITE,
}

@@ -334,3 +366,3 @@ }

#[cfg(not(target_os = "haiku"))]
#[cfg(not(any(target_os = "haiku", target_os = "cygwin")))]
libc_bitflags! {

@@ -378,3 +410,3 @@ /// Flags for [`mlockall`].

/// [`mlockall(2)`]: https://man7.org/linux/man-pages/man2/mlockall.2.html
#[cfg(not(target_os = "haiku"))]
#[cfg(not(any(target_os = "haiku", target_os = "cygwin")))]
pub fn mlockall(flags: MlockAllFlags) -> Result<()> {

@@ -389,3 +421,3 @@ unsafe { Errno::result(libc::mlockall(flags.bits())) }.map(drop)

/// [`munlockall(2)`]: https://man7.org/linux/man-pages/man2/munlockall.2.html
#[cfg(not(target_os = "haiku"))]
#[cfg(not(any(target_os = "haiku", target_os = "cygwin")))]
pub fn munlockall() -> Result<()> {

@@ -419,3 +451,3 @@ unsafe { Errno::result(libc::munlockall()) }.map(drop)

if ret == libc::MAP_FAILED {
if std::ptr::eq(ret, libc::MAP_FAILED) {
Err(Errno::last())

@@ -452,3 +484,3 @@ } else {

if ret == libc::MAP_FAILED {
if std::ptr::eq(ret, libc::MAP_FAILED) {
Err(Errno::last())

@@ -502,3 +534,3 @@ } else {

if ret == libc::MAP_FAILED {
if std::ptr::eq(ret, libc::MAP_FAILED) {
Err(Errno::last())

@@ -505,0 +537,0 @@ } else {

//! Mostly platform-specific functionality
#[cfg(any(
freebsdlike,
all(target_os = "linux", not(target_env = "uclibc")),
all(
target_os = "linux",
not(any(target_env = "uclibc", target_env = "ohos"))
),
apple_targets,

@@ -23,4 +26,4 @@ target_os = "netbsd"

/// Event file descriptor.
#[cfg(any(linux_android, target_os = "freebsd"))]
#[allow(missing_docs)]
pub mod eventfd;

@@ -35,3 +38,9 @@ }

#[cfg(any(bsd, linux_android, target_os = "redox", solarish))]
#[cfg(any(
bsd,
linux_android,
solarish,
target_os = "fuchsia",
target_os = "redox",
))]
#[cfg(feature = "ioctl")]

@@ -133,3 +142,9 @@ #[cfg_attr(docsrs, doc(cfg(feature = "ioctl")))]

#[cfg(any(linux_android, freebsdlike, apple_targets, target_os = "openbsd"))]
#[cfg(any(
linux_android,
freebsdlike,
apple_targets,
target_os = "openbsd",
target_os = "cygwin"
))]
feature! {

@@ -136,0 +151,0 @@ #![feature = "fs"]

@@ -23,3 +23,3 @@ //! Process execution domains

/// User-space function pointers to signal handlers point to descriptors.
#[cfg(not(any(target_env = "musl", target_env = "uclibc")))]
#[cfg(not(any(target_env = "musl", target_env = "uclibc", target_env = "ohos")))]
FDPIC_FUNCPTRS;

@@ -45,3 +45,3 @@ /// Map page 0 as read-only.

/// [`uname(2)`]: https://man7.org/linux/man-pages/man2/uname.2.html
#[cfg(not(any(target_env = "musl", target_env = "uclibc")))]
#[cfg(not(any(target_env = "musl", target_env = "uclibc", target_env = "ohos")))]
UNAME26;

@@ -48,0 +48,0 @@ /// No effects.

@@ -169,3 +169,3 @@ //! prctl is a Linux-only API for performing operations on a process or thread.

/// timer expirations and make them the supplied amount of nanoseconds late.
pub fn set_timerslack(ns: u64) -> Result<()> {
pub fn set_timerslack(ns: c_ulong) -> Result<()> {
let res = unsafe { libc::prctl(libc::PR_SET_TIMERSLACK, ns, 0, 0, 0) };

@@ -172,0 +172,0 @@

@@ -17,7 +17,6 @@ //! For detailed description of the ptrace requests, consult `man ptrace`.

all(
target_arch = "x86_64",
any(target_arch = "x86_64", target_arch = "aarch64"),
any(target_env = "gnu", target_env = "musl")
),
all(target_arch = "x86", target_env = "gnu"),
all(target_arch = "aarch64", target_env = "gnu"),
all(target_arch = "riscv64", target_env = "gnu"),

@@ -41,4 +40,4 @@ ),

libc_enum! {
#[cfg_attr(not(any(target_env = "musl", target_env = "uclibc", target_os = "android")), repr(u32))]
#[cfg_attr(any(target_env = "musl", target_env = "uclibc", target_os = "android"), repr(i32))]
#[cfg_attr(not(any(target_env = "musl", target_env = "uclibc", target_os = "android", target_env = "ohos")), repr(u32))]
#[cfg_attr(any(target_env = "musl", target_env = "uclibc", target_os = "android", target_env = "ohos"), repr(i32))]
/// Ptrace Request enum defining the action to be taken.

@@ -59,2 +58,3 @@ #[non_exhaustive]

all(target_os = "linux", any(target_env = "musl",
target_env = "ohos",
target_arch = "mips",

@@ -69,2 +69,3 @@ target_arch = "mips32r6",

all(target_os = "linux", any(target_env = "musl",
target_env = "ohos",
target_arch = "mips",

@@ -79,2 +80,3 @@ target_arch = "mips32r6",

all(target_os = "linux", any(target_env = "musl",
target_env = "ohos",
target_arch = "mips",

@@ -89,2 +91,3 @@ target_arch = "mips32r6",

all(target_os = "linux", any(target_env = "musl",
target_env = "ohos",
target_arch = "mips",

@@ -100,2 +103,3 @@ target_arch = "mips32r6",

#[cfg(all(target_os = "linux", any(target_env = "musl",
target_env = "ohos",
target_arch = "mips",

@@ -109,2 +113,3 @@ target_arch = "mips32r6",

#[cfg(all(target_os = "linux", any(target_env = "musl",
target_env = "ohos",
target_arch = "mips",

@@ -152,2 +157,4 @@ target_arch = "mips32r6",

PTRACE_SYSEMU_SINGLESTEP,
#[cfg(all(target_os = "linux", target_env = "gnu"))]
PTRACE_GET_SYSCALL_INFO,
}

@@ -186,9 +193,17 @@ }

target_os = "linux",
target_env = "gnu",
any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "aarch64",
target_arch = "riscv64",
)
all(
target_env = "gnu",
any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "aarch64",
target_arch = "riscv64",
)
),
all(
target_env = "musl",
target_arch = "aarch64",
)
),
))]

@@ -210,9 +225,17 @@ libc_enum! {

target_os = "linux",
target_env = "gnu",
any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "aarch64",
target_arch = "riscv64",
)
all(
target_env = "gnu",
any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "aarch64",
target_arch = "riscv64",
)
),
all(
target_env = "musl",
target_arch = "aarch64",
)
),
))]

@@ -234,11 +257,20 @@ /// Represents register set areas, such as general-purpose registers or

#[cfg(all(
target_os = "linux",
target_env = "gnu",
any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "aarch64",
target_arch = "riscv64",
)
all(
target_env = "gnu",
any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "aarch64",
target_arch = "riscv64",
)
),
all(
target_env = "musl",
target_arch = "aarch64",
)
),
))]

@@ -349,4 +381,9 @@ /// Register sets used in [`getregset`] and [`setregset`]

target_os = "linux",
target_env = "gnu",
any(target_arch = "aarch64", target_arch = "riscv64")
any(
all(
target_arch = "aarch64",
any(target_env = "gnu", target_env = "musl")
),
all(target_arch = "riscv64", target_env = "gnu")
)
))]

@@ -360,8 +397,13 @@ pub fn getregs(pid: Pid) -> Result<user_regs_struct> {

target_os = "linux",
target_env = "gnu",
any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "aarch64",
target_arch = "riscv64",
all(
target_env = "gnu",
any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "aarch64",
target_arch = "riscv64"
)
),
all(target_env = "musl", target_arch = "aarch64")
)

@@ -425,4 +467,9 @@ ))]

target_os = "linux",
target_env = "gnu",
any(target_arch = "aarch64", target_arch = "riscv64")
any(
all(
target_env = "gnu",
any(target_arch = "aarch64", target_arch = "riscv64")
),
all(target_env = "musl", target_arch = "aarch64")
)
))]

@@ -436,8 +483,13 @@ pub fn setregs(pid: Pid, regs: user_regs_struct) -> Result<()> {

target_os = "linux",
target_env = "gnu",
any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "aarch64",
target_arch = "riscv64",
all(
target_env = "gnu",
any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "aarch64",
target_arch = "riscv64"
)
),
all(target_env = "musl", target_arch = "aarch64")
)

@@ -536,2 +588,9 @@ ))]

/// Get the informations of the syscall that caused the stop, as with
/// `ptrace(PTRACE_GET_SYSCALL_INFO, ...`.
#[cfg(all(target_os = "linux", target_env = "gnu"))]
pub fn syscall_info(pid: Pid) -> Result<libc::ptrace_syscall_info> {
ptrace_get_data::<libc::ptrace_syscall_info>(Request::PTRACE_GET_SYSCALL_INFO, pid)
}
/// Sets the process as traceable, as with `ptrace(PTRACE_TRACEME, ...)`

@@ -538,0 +597,0 @@ ///

@@ -22,3 +22,4 @@ //! Configure the process resource limits.

target_os = "aix",
all(target_os = "linux", not(target_env = "gnu"))
all(target_os = "linux", not(target_env = "gnu")),
target_os = "cygwin"
))]{

@@ -53,3 +54,4 @@ use libc::rlimit;

target_os = "aix",
all(target_os = "linux", not(any(target_env = "gnu", target_env = "uclibc")))
all(target_os = "linux", not(any(target_env = "gnu", target_env = "uclibc"))),
target_os = "cygwin"
), repr(i32))]

@@ -56,0 +58,0 @@ #[non_exhaustive]

@@ -118,3 +118,3 @@ //! Portably monitor a group of file descriptors for readiness.

impl<'fd> Default for FdSet<'fd> {
impl Default for FdSet<'_> {
fn default() -> Self {

@@ -132,3 +132,3 @@ Self::new()

impl<'a, 'fd> Iterator for Fds<'a, 'fd> {
impl<'fd> Iterator for Fds<'_, 'fd> {
type Item = BorrowedFd<'fd>;

@@ -153,3 +153,3 @@

impl<'a, 'fd> DoubleEndedIterator for Fds<'a, 'fd> {
impl<'fd> DoubleEndedIterator for Fds<'_, 'fd> {
#[inline]

@@ -167,3 +167,3 @@ fn next_back(&mut self) -> Option<BorrowedFd<'fd>> {

impl<'a, 'fd> FusedIterator for Fds<'a, 'fd> {}
impl FusedIterator for Fds<'_, '_> {}

@@ -170,0 +170,0 @@ /// Monitors file descriptors for readiness

@@ -1,4 +0,1 @@

// Portions of this file are Copyright 2014 The Rust Project Developers.
// See https://www.rust-lang.org/policies/licenses.
//! Operating system signals.

@@ -13,4 +10,2 @@

use std::ops::BitOr;
#[cfg(freebsdlike)]
use std::os::unix::io::RawFd;
use std::ptr;

@@ -76,2 +71,3 @@ use std::str::FromStr;

target_arch = "mips64r6",
target_arch = "sparc",
target_arch = "sparc64"))))]

@@ -119,3 +115,4 @@ SIGSTKFLT,

target_os = "fuchsia", target_os = "redox",
target_os = "haiku", target_os = "aix")))]
target_os = "haiku", target_os = "aix",
target_os = "solaris", target_os = "cygwin")))]
/// Information request

@@ -158,2 +155,3 @@ SIGINFO,

target_arch = "mips64r6",
target_arch = "sparc",
target_arch = "sparc64"

@@ -198,3 +196,5 @@ ))

target_os = "aix",
target_os = "haiku"
target_os = "haiku",
target_os = "solaris",
target_os = "cygwin"
)))]

@@ -242,2 +242,3 @@ "SIGINFO" => Signal::SIGINFO,

target_arch = "mips64r6",
target_arch = "sparc",
target_arch = "sparc64"

@@ -283,3 +284,5 @@ ))

target_os = "aix",
target_os = "haiku"
target_os = "haiku",
target_os = "solaris",
target_os = "cygwin"
)))]

@@ -331,2 +334,3 @@ Signal::SIGINFO => "SIGINFO",

target_arch = "mips64r6",
target_arch = "sparc",
target_arch = "sparc64"

@@ -349,2 +353,3 @@ ))

target_arch = "mips64r6",
target_arch = "sparc",
target_arch = "sparc64"

@@ -368,2 +373,10 @@ )

];
#[cfg(any(target_os = "solaris", target_os = "cygwin"))]
#[cfg(feature = "signal")]
const SIGNALS: [Signal; 30] = [
SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGTRAP, SIGABRT, SIGBUS, SIGFPE, SIGKILL,
SIGUSR1, SIGSEGV, SIGUSR2, SIGPIPE, SIGALRM, SIGTERM, SIGCHLD, SIGCONT,
SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU, SIGURG, SIGXCPU, SIGXFSZ, SIGVTALRM,
SIGPROF, SIGWINCH, SIGIO, SIGSYS, SIGEMT,
];
#[cfg(not(any(

@@ -375,3 +388,5 @@ linux_android,

target_os = "redox",
target_os = "haiku"
target_os = "haiku",
target_os = "solaris",
target_os = "cygwin"
)))]

@@ -759,7 +774,7 @@ #[cfg(feature = "signal")]

/// Use the given signal-catching function, which takes in the signal.
Handler(extern fn(libc::c_int)),
Handler(extern "C" fn(libc::c_int)),
/// Use the given signal-catching function, which takes in the signal, information about how
/// the signal was generated, and a pointer to the threads `ucontext_t`.
#[cfg(not(target_os = "redox"))]
SigAction(extern fn(libc::c_int, *mut libc::siginfo_t, *mut libc::c_void))
SigAction(extern "C" fn(libc::c_int, *mut libc::siginfo_t, *mut libc::c_void))
}

@@ -787,3 +802,2 @@

pub fn new(handler: SigHandler, flags: SaFlags, mask: SigSet) -> SigAction {
#[cfg(not(target_os = "aix"))]
unsafe fn install_sig(p: *mut libc::sigaction, handler: SigHandler) {

@@ -794,5 +808,5 @@ unsafe {

SigHandler::SigIgn => libc::SIG_IGN,
SigHandler::Handler(f) => f as *const extern fn(libc::c_int) as usize,
SigHandler::Handler(f) => f as *const extern "C" fn(libc::c_int) as usize,
#[cfg(not(target_os = "redox"))]
SigHandler::SigAction(f) => f as *const extern fn(libc::c_int, *mut libc::siginfo_t, *mut libc::c_void) as usize,
SigHandler::SigAction(f) => f as *const extern "C" fn(libc::c_int, *mut libc::siginfo_t, *mut libc::c_void) as usize,
};

@@ -802,14 +816,2 @@ }

#[cfg(target_os = "aix")]
unsafe fn install_sig(p: *mut libc::sigaction, handler: SigHandler) {
unsafe {
(*p).sa_union.__su_sigaction = match handler {
SigHandler::SigDfl => unsafe { mem::transmute::<usize, extern "C" fn(libc::c_int, *mut libc::siginfo_t, *mut libc::c_void)>(libc::SIG_DFL) },
SigHandler::SigIgn => unsafe { mem::transmute::<usize, extern "C" fn(libc::c_int, *mut libc::siginfo_t, *mut libc::c_void)>(libc::SIG_IGN) },
SigHandler::Handler(f) => unsafe { mem::transmute::<extern "C" fn(i32), extern "C" fn(i32, *mut libc::siginfo_t, *mut libc::c_void)>(f) },
SigHandler::SigAction(f) => f,
};
}
}
let mut s = mem::MaybeUninit::<libc::sigaction>::uninit();

@@ -842,3 +844,2 @@ unsafe {

/// Returns the action's handler.
#[cfg(not(target_os = "aix"))]
pub fn handler(&self) -> SigHandler {

@@ -859,5 +860,5 @@ match self.sigaction.sa_sigaction {

*(&p as *const usize
as *const extern fn(_, _, _))
as *const extern "C" fn(_, _, _))
}
as extern fn(_, _, _)),
as extern "C" fn(_, _, _)),
p => SigHandler::Handler(

@@ -872,27 +873,7 @@ // Safe for one of two reasons:

*(&p as *const usize
as *const extern fn(libc::c_int))
as *const extern "C" fn(libc::c_int))
}
as extern fn(libc::c_int)),
as extern "C" fn(libc::c_int)),
}
}
/// Returns the action's handler.
#[cfg(target_os = "aix")]
pub fn handler(&self) -> SigHandler {
unsafe {
match self.sigaction.sa_union.__su_sigaction as usize {
libc::SIG_DFL => SigHandler::SigDfl,
libc::SIG_IGN => SigHandler::SigIgn,
p if self.flags().contains(SaFlags::SA_SIGINFO) =>
SigHandler::SigAction(
*(&p as *const usize
as *const extern fn(_, _, _))
as extern fn(_, _, _)),
p => SigHandler::Handler(
*(&p as *const usize
as *const extern fn(libc::c_int))
as extern fn(libc::c_int)),
}
}
}
}

@@ -956,3 +937,3 @@

///
/// extern fn handle_sigint(signal: libc::c_int) {
/// extern "C" fn handle_sigint(signal: libc::c_int) {
/// let signal = Signal::try_from(signal).unwrap();

@@ -994,3 +975,3 @@ /// SIGNALED.store(signal == Signal::SIGINT, Ordering::Relaxed);

p => SigHandler::Handler(
unsafe { *(&p as *const usize as *const extern fn(libc::c_int)) } as extern fn(libc::c_int)),
unsafe { *(&p as *const usize as *const extern "C" fn(libc::c_int)) } as extern "C" fn(libc::c_int)),
}

@@ -1137,4 +1118,4 @@ })

#[cfg(not(any(target_os = "fuchsia", target_os = "hurd", target_os = "openbsd", target_os = "redox")))]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum SigevNotify {
#[derive(Clone, Copy, Debug)]
pub enum SigevNotify<'fd> {
/// No notification will be delivered

@@ -1155,3 +1136,3 @@ SigevNone,

/// File descriptor of the kqueue to notify.
kq: RawFd,
kq: std::os::fd::BorrowedFd<'fd>,
/// Will be contained in the kevent's `udata` field.

@@ -1165,7 +1146,7 @@ udata: libc::intptr_t

/// File descriptor of the kqueue to notify.
kq: RawFd,
kq: std::os::fd::BorrowedFd<'fd>,
/// Will be contained in the kevent's `udata` field.
udata: libc::intptr_t,
/// Flags that will be set on the delivered event. See `kevent(2)`.
flags: crate::sys::event::EventFlag
flags: crate::sys::event::EvFlags
},

@@ -1187,2 +1168,10 @@ /// Notify by delivering a signal to a thread.

},
/// A helper variant to resolve the unused parameter (`'fd`) problem on
/// platforms other than FreeBSD and DragonFlyBSD.
///
/// This variant can never be constructed due to the usage of an enum with 0
/// variants.
#[doc(hidden)]
#[cfg(not(freebsdlike))]
_Unreachable(&'fd std::convert::Infallible),
}

@@ -1364,4 +1353,6 @@ }

SigevNotify::SigevKevent{kq, udata} => {
use std::os::fd::AsRawFd;
sev.sigev_notify = libc::SIGEV_KEVENT;
sev.sigev_signo = kq;
sev.sigev_signo = kq.as_raw_fd();
sev.sigev_value.sival_ptr = udata as *mut libc::c_void;

@@ -1372,4 +1363,6 @@ },

SigevNotify::SigevKeventFlags{kq, udata, flags} => {
use std::os::fd::AsRawFd;
sev.sigev_notify = libc::SIGEV_KEVENT;
sev.sigev_signo = kq;
sev.sigev_signo = kq.as_raw_fd();
sev.sigev_value.sival_ptr = udata as *mut libc::c_void;

@@ -1392,2 +1385,4 @@ sev._sigev_un._kevent_flags = flags.bits();

}
#[cfg(not(freebsdlike))]
SigevNotify::_Unreachable(_) => unreachable!("This variant could never be constructed")
}

@@ -1428,3 +1423,3 @@ SigEvent{sigevent: sev}

impl<'a> From<&'a libc::sigevent> for SigEvent {
impl From<&'_ libc::sigevent> for SigEvent {
#[cfg(target_os = "freebsd")]

@@ -1431,0 +1426,0 @@ fn from(sigevent: &libc::sigevent) -> Self {

@@ -21,2 +21,4 @@ //! Interface for the `signalfd` syscall.

use crate::Result;
/// Information of a received signal, the return type of [`SignalFd::read_signal()`].
pub use libc::signalfd_siginfo as siginfo;

@@ -129,2 +131,11 @@

/// Constructs a `SignalFd` wrapping an existing `OwnedFd`.
///
/// # Safety
///
/// `OwnedFd` is a valid `SignalFd`.
pub unsafe fn from_owned_fd(fd: OwnedFd) -> Self {
Self(fd)
}
fn update(&self, mask: &SigSet, flags: SfdFlags) -> Result<()> {

@@ -150,2 +161,8 @@ let raw_fd = self.0.as_raw_fd();

impl From<SignalFd> for OwnedFd {
fn from(value: SignalFd) -> Self {
value.0
}
}
impl Iterator for SignalFd {

@@ -152,0 +169,0 @@ type Item = siginfo;

//! Socket options as used by `setsockopt` and `getsockopt`.
use super::{GetSockOpt, SetSockOpt};
use crate::errno::Errno;
#[cfg(any(linux_android, target_os = "illumos"))]
use super::SetSockOpt;
use crate::sys::time::TimeVal;
use crate::Result;
#[cfg(any(linux_android, target_os = "illumos"))]
use crate::{errno::Errno, Result};
use cfg_if::cfg_if;
use libc::{self, c_int, c_void, socklen_t};
use std::ffi::{CStr, CString, OsStr, OsString};
#[cfg(apple_targets)]
use std::ffi::CString;
use std::ffi::{CStr, OsStr, OsString};
use std::mem::{self, MaybeUninit};
use std::os::fd::OwnedFd;
use std::os::unix::ffi::OsStrExt;
#[cfg(any(linux_android, target_os = "illumos"))]
use std::os::unix::io::{AsFd, AsRawFd};

@@ -25,4 +30,4 @@

///
/// Instead of using this macro directly consider using `sockopt_impl!`, especially if the option
/// you are implementing represents a simple type.
/// Instead of using this macro directly consider using [`sockopt_impl!`](crate::sockopt_impl),
/// especially if the option you are implementing represents a simple type.
///

@@ -33,29 +38,38 @@ /// # Arguments

/// * `$level:expr` : socket layer, or a `protocol level`: could be *raw sockets*
/// (`libc::SOL_SOCKET`), *ip protocol* (libc::IPPROTO_IP), *tcp protocol* (`libc::IPPROTO_TCP`),
/// and more. Please refer to your system manual for more options. Will be passed as the second
/// argument (`level`) to the `setsockopt` call.
/// (`libc::SOL_SOCKET`), *ip protocol* (libc::IPPROTO_IP), *tcp protocol* (`libc::IPPROTO_TCP`),
/// and more. Please refer to your system manual for more options. Will be passed as the second
/// argument (`level`) to the `setsockopt` call.
/// * `$flag:path`: a flag name to set. Some examples: `libc::SO_REUSEADDR`, `libc::TCP_NODELAY`,
/// `libc::IP_ADD_MEMBERSHIP` and others. Will be passed as the third argument (`option_name`)
/// to the `setsockopt` call.
/// `libc::IP_ADD_MEMBERSHIP` and others. Will be passed as the third argument (`option_name`)
/// to the `setsockopt` call.
/// * Type of the value that you are going to set.
/// * Type that implements the `Set` trait for the type from the previous item (like `SetBool` for
/// `bool`, `SetUsize` for `usize`, etc.).
/// * Type that implements the `Set` trait for the type from the previous item
/// (like `SetBool` for `bool`, `SetUsize` for `usize`, etc.).
#[macro_export]
macro_rules! setsockopt_impl {
($name:ident, $level:expr, $flag:path, $ty:ty, $setter:ty) => {
impl SetSockOpt for $name {
#[allow(deprecated)] // to allow we have deprecated socket option
impl $crate::sys::socket::SetSockOpt for $name {
type Val = $ty;
fn set<F: AsFd>(&self, fd: &F, val: &$ty) -> Result<()> {
unsafe {
let setter: $setter = Set::new(val);
let res = libc::setsockopt(
fn set<F: std::os::unix::io::AsFd>(
&self,
fd: &F,
val: &$ty,
) -> $crate::Result<()> {
use std::os::fd::AsRawFd;
use $crate::sys::socket::sockopt::Set;
let setter: $setter = Set::new(val);
let level = $level;
let flag = $flag;
let res = unsafe {
libc::setsockopt(
fd.as_fd().as_raw_fd(),
$level,
$flag,
level,
flag,
setter.ffi_ptr(),
setter.ffi_len(),
);
Errno::result(res).map(drop)
}
)
};
$crate::errno::Errno::result(res).map(drop)
}

@@ -72,4 +86,4 @@ }

///
/// Instead of using this macro directly consider using `sockopt_impl!`, especially if the option
/// you are implementing represents a simple type.
/// Instead of using this macro directly consider using [`sockopt_impl!`](crate::sockopt_impl),
/// especially if the option you are implementing represents a simple type.
///

@@ -80,33 +94,57 @@ /// # Arguments

/// * Socket layer, or a `protocol level`: could be *raw sockets* (`lic::SOL_SOCKET`), *ip
/// protocol* (libc::IPPROTO_IP), *tcp protocol* (`libc::IPPROTO_TCP`), and more. Please refer
/// to your system manual for more options. Will be passed as the second argument (`level`) to
/// the `getsockopt` call.
/// protocol* (libc::IPPROTO_IP), *tcp protocol* (`libc::IPPROTO_TCP`), and more. Please refer
/// to your system manual for more options. Will be passed as the second argument (`level`) to
/// the `getsockopt` call.
/// * A flag to set. Some examples: `libc::SO_REUSEADDR`, `libc::TCP_NODELAY`,
/// `libc::SO_ORIGINAL_DST` and others. Will be passed as the third argument (`option_name`) to
/// the `getsockopt` call.
/// `libc::SO_ORIGINAL_DST` and others. Will be passed as the third argument (`option_name`) to
/// the `getsockopt` call.
/// * Type of the value that you are going to get.
/// * Type that implements the `Get` trait for the type from the previous item (`GetBool` for
/// `bool`, `GetUsize` for `usize`, etc.).
/// `bool`, `GetUsize` for `usize`, etc.).
#[macro_export]
macro_rules! getsockopt_impl {
($name:ident, $level:expr, $flag:path, $ty:ty, $getter:ty) => {
impl GetSockOpt for $name {
#[allow(deprecated)] // to allow we have deprecated socket option
impl $crate::sys::socket::GetSockOpt for $name {
type Val = $ty;
fn get<F: AsFd>(&self, fd: &F) -> Result<$ty> {
unsafe {
let mut getter: $getter = Get::uninit();
let res = libc::getsockopt(
fn get<F: std::os::unix::io::AsFd>(
&self,
fd: &F,
) -> $crate::Result<$ty> {
use std::os::fd::AsRawFd;
use $crate::sys::socket::sockopt::Get;
let mut getter: $getter = Get::uninit();
let level = $level;
let flag = $flag;
let res = unsafe {
libc::getsockopt(
fd.as_fd().as_raw_fd(),
$level,
$flag,
level,
flag,
getter.ffi_ptr(),
getter.ffi_len(),
);
Errno::result(res)?;
)
};
$crate::errno::Errno::result(res)?;
match <$ty>::try_from(getter.assume_init()) {
Err(_) => Err(Errno::EINVAL),
Ok(r) => Ok(r),
}
// getter is definitely initialized now
let gotten = unsafe { getter.assume_init() };
match <$ty>::try_from(gotten) {
// In most `getsockopt_impl!` implementations, `assume_init()`
// returns `$ty`, so calling `$ty`::try_from($ty) will always
// succeed. which makes the following `Err(_)` branch
// unreachable.
//
// However, there is indeed one exception, `sockopt::SockType`,
// `assume_init()` returns an `i32`, but `$ty` is `super::SockType`,
// this exception necessitates the use of that `try_from()`,
// and we have to allow the unreachable pattern wraning.
//
// For the reason why we are using `i32` as the underlying
// buffer type for this socket option, see issue:
// https://github.com/nix-rust/nix/issues/1819
#[allow(unreachable_patterns)]
Err(_) => Err($crate::errno::Errno::EINVAL),
Ok(r) => Ok(r),
}

@@ -131,11 +169,11 @@ }

/// * `GetOnly`, `SetOnly` or `Both`: whether you want to implement only getter, only setter or
/// both of them.
/// both of them.
/// * `$name:ident`: name of type `GetSockOpt`/`SetSockOpt` will be implemented for.
/// * `$level:expr` : socket layer, or a `protocol level`: could be *raw sockets*
/// (`libc::SOL_SOCKET`), *ip protocol* (libc::IPPROTO_IP), *tcp protocol* (`libc::IPPROTO_TCP`),
/// and more. Please refer to your system manual for more options. Will be passed as the second
/// argument (`level`) to the `getsockopt`/`setsockopt` call.
/// (`libc::SOL_SOCKET`), *ip protocol* (libc::IPPROTO_IP), *tcp protocol* (`libc::IPPROTO_TCP`),
/// and more. Please refer to your system manual for more options. Will be passed as the second
/// argument (`level`) to the `getsockopt`/`setsockopt` call.
/// * `$flag:path`: a flag name to set. Some examples: `libc::SO_REUSEADDR`, `libc::TCP_NODELAY`,
/// `libc::IP_ADD_MEMBERSHIP` and others. Will be passed as the third argument (`option_name`)
/// to the `setsockopt`/`getsockopt` call.
/// `libc::IP_ADD_MEMBERSHIP` and others. Will be passed as the third argument (`option_name`)
/// to the `setsockopt`/`getsockopt` call.
/// * `$ty:ty`: type of the value that will be get/set.

@@ -146,10 +184,11 @@ /// * `$getter:ty`: `Get` implementation; optional; only for `GetOnly` and `Both`.

#[allow(unused_macro_rules)]
#[macro_export]
macro_rules! sockopt_impl {
($(#[$attr:meta])* $name:ident, GetOnly, $level:expr, $flag:path, bool) => {
sockopt_impl!($(#[$attr])*
$name, GetOnly, $level, $flag, bool, GetBool);
$name, GetOnly, $level, $flag, bool, $crate::sys::socket::sockopt::GetBool);
};
($(#[$attr:meta])* $name:ident, GetOnly, $level:expr, $flag:path, u8) => {
sockopt_impl!($(#[$attr])* $name, GetOnly, $level, $flag, u8, GetU8);
sockopt_impl!($(#[$attr])* $name, GetOnly, $level, $flag, u8, $crate::sys::socket::sockopt::GetU8);
};

@@ -160,12 +199,18 @@

sockopt_impl!($(#[$attr])*
$name, GetOnly, $level, $flag, usize, GetUsize);
$name, GetOnly, $level, $flag, usize, $crate::sys::socket::sockopt::GetUsize);
};
($(#[$attr:meta])* $name:ident, GetOnly, $level:expr, $flag:path, OwnedFd) =>
{
sockopt_impl!($(#[$attr])*
$name, GetOnly, $level, $flag, OwnedFd, $crate::sys::socket::sockopt::GetOwnedFd);
};
($(#[$attr:meta])* $name:ident, SetOnly, $level:expr, $flag:path, bool) => {
sockopt_impl!($(#[$attr])*
$name, SetOnly, $level, $flag, bool, SetBool);
$name, SetOnly, $level, $flag, bool, $crate::sys::socket::sockopt::SetBool);
};
($(#[$attr:meta])* $name:ident, SetOnly, $level:expr, $flag:path, u8) => {
sockopt_impl!($(#[$attr])* $name, SetOnly, $level, $flag, u8, SetU8);
sockopt_impl!($(#[$attr])* $name, SetOnly, $level, $flag, u8, $crate::sys::socket::sockopt::SetU8);
};

@@ -176,8 +221,14 @@

sockopt_impl!($(#[$attr])*
$name, SetOnly, $level, $flag, usize, SetUsize);
$name, SetOnly, $level, $flag, usize, $crate::sys::socket::sockopt::SetUsize);
};
($(#[$attr:meta])* $name:ident, SetOnly, $level:expr, $flag:path, OwnedFd) =>
{
sockopt_impl!($(#[$attr])*
$name, SetOnly, $level, $flag, OwnedFd, $crate::sys::socket::sockopt::SetOwnedFd);
};
($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path, bool) => {
sockopt_impl!($(#[$attr])*
$name, Both, $level, $flag, bool, GetBool, SetBool);
$name, Both, $level, $flag, bool, $crate::sys::socket::sockopt::GetBool, $crate::sys::socket::sockopt::SetBool);
};

@@ -187,3 +238,3 @@

sockopt_impl!($(#[$attr])*
$name, Both, $level, $flag, u8, GetU8, SetU8);
$name, Both, $level, $flag, u8, $crate::sys::socket::sockopt::GetU8, $crate::sys::socket::sockopt::SetU8);
};

@@ -193,5 +244,10 @@

sockopt_impl!($(#[$attr])*
$name, Both, $level, $flag, usize, GetUsize, SetUsize);
$name, Both, $level, $flag, usize, $crate::sys::socket::sockopt::GetUsize, $crate::sys::socket::sockopt::SetUsize);
};
($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path, OwnedFd) => {
sockopt_impl!($(#[$attr])*
$name, Both, $level, $flag, OwnedFd, $crate::sys::socket::sockopt::GetOwnedFd, $crate::sys::socket::sockopt::SetOwnedFd);
};
($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path,

@@ -201,4 +257,4 @@ OsString<$array:ty>) =>

sockopt_impl!($(#[$attr])*
$name, Both, $level, $flag, OsString, GetOsString<$array>,
SetOsString);
$name, Both, $level, $flag, std::ffi::OsString, $crate::sys::socket::sockopt::GetOsString<$array>,
$crate::sys::socket::sockopt::SetOsString);
};

@@ -213,3 +269,3 @@

sockopt_impl!($(#[$attr])*
$name, GetOnly, $level, $flag, $ty, GetStruct<$ty>);
$name, GetOnly, $level, $flag, $ty, $crate::sys::socket::sockopt::GetStruct<$ty>);
};

@@ -230,3 +286,3 @@

sockopt_impl!($(#[$attr])*
$name, SetOnly, $level, $flag, $ty, SetStruct<$ty>);
$name, SetOnly, $level, $flag, $ty, $crate::sys::socket::sockopt::SetStruct<$ty>);
};

@@ -257,4 +313,4 @@

sockopt_impl!($(#[$attr])*
$name, Both, $level, $flag, $ty, GetStruct<$ty>,
SetStruct<$ty>);
$name, Both, $level, $flag, $ty, $crate::sys::socket::sockopt::GetStruct<$ty>,
$crate::sys::socket::sockopt::SetStruct<$ty>);
};

@@ -277,3 +333,3 @@ }

);
#[cfg(not(solarish))]
#[cfg(not(any(solarish, target_os = "cygwin")))]
sockopt_impl!(

@@ -298,5 +354,33 @@ /// Permits multiple AF_INET or AF_INET6 sockets to be bound to an

);
#[cfg(target_os = "freebsd")]
#[cfg(feature = "net")]
sockopt_impl!(
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
/// Select or query the set of functions that TCP will use for this connection. This allows a
/// user to select an alternate TCP stack.
TcpFunctionBlk,
Both,
libc::IPPROTO_TCP,
libc::TCP_FUNCTION_BLK,
libc::tcp_function_set
);
#[cfg(target_os = "freebsd")]
#[cfg(feature = "net")]
sockopt_impl!(
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
/// Query the alias name of the set of function of the socket's TCP stack.
/// Uses the same field for the main name when getting from TCP_FUNCTION_BLK.
/// Empty if no alias.
TcpFunctionAlias,
GetOnly,
libc::IPPROTO_TCP,
libc::TCP_FUNCTION_ALIAS,
libc::tcp_function_set
);
sockopt_impl!(
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
/// Used to disable Nagle's algorithm.
///
/// Nagle's algorithm:
///
/// Under most circumstances, TCP sends data when it is presented; when

@@ -307,4 +391,4 @@ /// outstanding data has not yet been acknowledged, it gathers small amounts

/// send a stream of mouse events which receive no replies, this
/// packetization may cause significant delays. The boolean option
/// TCP_NODELAY defeats this algorithm.
/// packetization may cause significant delays. The boolean option, when
/// enabled, defeats this algorithm.
TcpNoDelay,

@@ -317,3 +401,3 @@ Both,

sockopt_impl!(
/// When enabled, a close(2) or shutdown(2) will not return until all
/// When enabled, a close(2) or shutdown(2) will not return until all
/// queued messages for the socket have been successfully sent or the

@@ -327,2 +411,11 @@ /// linger timeout has been reached.

);
#[cfg(apple_targets)]
sockopt_impl!(
/// Same as `SO_LINGER`, but the duration is in seconds rather than kernel ticks.
LingerSec,
Both,
libc::SOL_SOCKET,
libc::SO_LINGER_SEC,
libc::linger
);
#[cfg(feature = "net")]

@@ -420,5 +513,6 @@ sockopt_impl!(

);
#[cfg(target_os = "linux")]
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
sockopt_impl!(
#[deprecated(since = "0.30.0", note = "Use Ipv4Tos instead")]
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]

@@ -433,7 +527,30 @@ /// Set or receive the Type-Of-Service (TOS) field that is

);
#[cfg(target_os = "linux")]
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
sockopt_impl!(
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
/// Traffic class associated with outgoing packets
/// Set or receive the Type-Of-Service (TOS) field that is
/// sent with every IP packet originating from this socket
Ipv4Tos,
Both,
libc::IPPROTO_IP,
libc::IP_TOS,
libc::c_int
);
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
sockopt_impl!(
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
/// If enabled, the IP_TOS ancillary message is passed with incoming packets.
IpRecvTos,
Both,
libc::IPPROTO_IP,
libc::IP_RECVTOS,
bool
);
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
sockopt_impl!(
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
/// Set the traffic class associated with outgoing packets.
Ipv6TClass,

@@ -445,2 +562,13 @@ Both,

);
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
sockopt_impl!(
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
/// If enabled, the IPV6_TCLASS ancillary message is passed with incoming packets.
Ipv6RecvTClass,
Both,
libc::IPPROTO_IPV6,
libc::IPV6_RECVTCLASS,
bool
);
#[cfg(any(linux_android, target_os = "fuchsia"))]

@@ -529,3 +657,3 @@ #[cfg(feature = "net")]

);
#[cfg(any(freebsdlike, apple_targets))]
#[cfg(freebsdlike)]
sockopt_impl!(

@@ -542,9 +670,29 @@ /// Get the credentials of the peer process of a connected unix domain

sockopt_impl!(
/// Get the credentials of the peer process of a connected unix domain
/// socket.
LocalPeerCred,
GetOnly,
libc::SOL_LOCAL,
libc::LOCAL_PEERCRED,
super::XuCred
);
#[cfg(apple_targets)]
sockopt_impl!(
/// Get the PID of the peer process of a connected unix domain socket.
LocalPeerPid,
GetOnly,
0,
libc::SOL_LOCAL,
libc::LOCAL_PEERPID,
libc::c_int
);
#[cfg(apple_targets)]
sockopt_impl!(
/// Get the audit token of the peer process of a connected unix domain
/// socket.
LocalPeerToken,
GetOnly,
libc::SOL_LOCAL,
libc::LOCAL_PEERTOKEN,
super::audit_token_t
);
#[cfg(linux_android)]

@@ -559,2 +707,11 @@ sockopt_impl!(

);
#[cfg(target_os = "linux")]
sockopt_impl!(
/// Return the pidfd of the foreign process connected to this socket.
PeerPidfd,
GetOnly,
libc::SOL_SOCKET,
libc::SO_PEERPIDFD,
OwnedFd
);
#[cfg(target_os = "freebsd")]

@@ -596,3 +753,3 @@ #[cfg(feature = "net")]

cfg_if! {
if #[cfg(linux_android)] {
if #[cfg(any(linux_android, apple_targets))] {
sockopt_impl!(

@@ -778,3 +935,9 @@ /// The maximum segment size for outgoing TCP packets.

);
#[cfg(not(any(target_os = "aix", target_os = "haiku", target_os = "hurd", target_os = "redox")))]
#[cfg(not(any(
target_os = "aix",
target_os = "haiku",
target_os = "hurd",
target_os = "redox",
target_os = "cygwin"
)))]
sockopt_impl!(

@@ -926,3 +1089,3 @@ /// Enable or disable the receiving of the `SO_TIMESTAMP` control message.

);
#[cfg(any(linux_android, target_os = "freebsd", apple_targets, netbsdlike))]
#[cfg(any(linux_android, bsd))]
#[cfg(feature = "net")]

@@ -939,2 +1102,15 @@ sockopt_impl!(

);
#[cfg(any(linux_android, bsd))]
#[cfg(feature = "net")]
sockopt_impl!(
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
/// Pass an `IPV6_PKTINFO` ancillary message that contains a in6_pktinfo
/// structure that supplies some information about the incoming packet.
Ipv6PacketInfo,
Both,
libc::IPPROTO_IPV6,
libc::IPV6_PKTINFO,
bool
);
#[cfg(bsd)]

@@ -1068,2 +1244,13 @@ #[cfg(feature = "net")]

);
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
sockopt_impl!(
/// Enables a receiving socket to retrieve the Time-to-Live (TTL) field
/// from incoming IPv4 packets.
Ipv4RecvTtl,
Both,
libc::IPPROTO_IP,
libc::IP_RECVTTL,
bool
);
#[cfg(any(apple_targets, linux_android, target_os = "freebsd"))]

@@ -1081,2 +1268,13 @@ sockopt_impl!(

sockopt_impl!(
/// Enables a receiving socket to retrieve the Hop Limit field
/// (similar to TTL in IPv4) from incoming IPv6 packets.
Ipv6RecvHopLimit,
Both,
libc::IPPROTO_IPV6,
libc::IPV6_RECVHOPLIMIT,
bool
);
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
sockopt_impl!(
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]

@@ -1121,2 +1319,26 @@ /// The `recvmsg(2)` call will return the destination IP address for a UDP

#[cfg(solarish)]
sockopt_impl!(
/// Enable/disable exclusive binding.
/// Prevent multiple sockets to bind to the same
/// address:port, neutralizing `SO_REUSEADDR` effect.
ExclBind,
Both,
libc::SOL_SOCKET,
libc::SO_EXCLBIND,
bool
);
#[cfg(target_os = "linux")]
sockopt_impl!(
/// To be used with `ReusePort`,
/// we can then attach a BPF (classic)
/// to set how the packets are assigned
/// to the socket (e.g. cpu distribution).
AttachReusePortCbpf,
SetOnly,
libc::SOL_SOCKET,
libc::SO_ATTACH_REUSEPORT_CBPF,
libc::sock_fprog
);
#[allow(missing_docs)]

@@ -1335,3 +1557,54 @@ // Not documented by Linux!

#[cfg(target_os = "illumos")]
#[derive(Copy, Clone, Debug)]
/// Attach a named filter to this socket to be able to
/// defer when anough byte had been buffered by the kernel
pub struct FilterAttach;
#[cfg(target_os = "illumos")]
impl SetSockOpt for FilterAttach {
type Val = OsStr;
fn set<F: AsFd>(&self, fd: &F, val: &Self::Val) -> Result<()> {
if val.len() > libc::FILNAME_MAX as usize {
return Err(Errno::EINVAL);
}
unsafe {
let res = libc::setsockopt(
fd.as_fd().as_raw_fd(),
libc::SOL_FILTER,
libc::FIL_ATTACH,
val.as_bytes().as_ptr().cast(),
val.len() as libc::socklen_t,
);
Errno::result(res).map(drop)
}
}
}
#[cfg(target_os = "illumos")]
#[derive(Copy, Clone, Debug)]
/// Detach a socket filter previously attached with FIL_ATTACH
pub struct FilterDetach;
#[cfg(target_os = "illumos")]
impl SetSockOpt for FilterDetach {
type Val = OsStr;
fn set<F: AsFd>(&self, fd: &F, val: &Self::Val) -> Result<()> {
if val.len() > libc::FILNAME_MAX as usize {
return Err(Errno::EINVAL);
}
unsafe {
let res = libc::setsockopt(
fd.as_fd().as_raw_fd(),
libc::SOL_FILTER,
libc::FIL_DETACH,
val.as_bytes().as_ptr().cast(),
val.len() as libc::socklen_t,
);
Errno::result(res).map(drop)
}
}
}
/*

@@ -1344,3 +1617,5 @@ *

/// Helper trait that describes what is expected from a `GetSockOpt` getter.
trait Get<T> {
// Hide the docs, because it's an implementation detail of `sockopt_impl!`
#[doc(hidden)]
pub trait Get<T> {
/// Returns an uninitialized value.

@@ -1359,3 +1634,5 @@ fn uninit() -> Self;

/// Helper trait that describes what is expected from a `SetSockOpt` setter.
trait Set<'a, T> {
// Hide the docs, because it's an implementation detail of `sockopt_impl!`
#[doc(hidden)]
pub trait Set<'a, T> {
/// Initialize the setter with a given value.

@@ -1372,3 +1649,6 @@ fn new(val: &'a T) -> Self;

/// Getter for an arbitrary `struct`.
struct GetStruct<T> {
// Hide the docs, because it's an implementation detail of `sockopt_impl!`
#[doc(hidden)]
#[derive(Debug)]
pub struct GetStruct<T> {
len: socklen_t,

@@ -1405,3 +1685,6 @@ val: MaybeUninit<T>,

/// Setter for an arbitrary `struct`.
struct SetStruct<'a, T: 'static> {
// Hide the docs, because it's an implementation detail of `sockopt_impl!`
#[doc(hidden)]
#[derive(Debug)]
pub struct SetStruct<'a, T: 'static> {
ptr: &'a T,

@@ -1425,3 +1708,6 @@ }

/// Getter for a boolean value.
struct GetBool {
// Hide the docs, because it's an implementation detail of `sockopt_impl!`
#[doc(hidden)]
#[derive(Clone, Copy, Debug)]
pub struct GetBool {
len: socklen_t,

@@ -1458,3 +1744,6 @@ val: MaybeUninit<c_int>,

/// Setter for a boolean value.
struct SetBool {
// Hide the docs, because it's an implementation detail of `sockopt_impl!`
#[doc(hidden)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct SetBool {
val: c_int,

@@ -1480,3 +1769,7 @@ }

/// Getter for an `u8` value.
struct GetU8 {
#[cfg(feature = "net")]
// Hide the docs, because it's an implementation detail of `sockopt_impl!`
#[doc(hidden)]
#[derive(Clone, Copy, Debug)]
pub struct GetU8 {
len: socklen_t,

@@ -1486,2 +1779,3 @@ val: MaybeUninit<u8>,

#[cfg(feature = "net")]
impl Get<u8> for GetU8 {

@@ -1514,6 +1808,10 @@ fn uninit() -> Self {

/// Setter for an `u8` value.
struct SetU8 {
// Hide the docs, because it's an implementation detail of `sockopt_impl!`
#[doc(hidden)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct SetU8 {
val: u8,
}
#[cfg(feature = "net")]
impl<'a> Set<'a, u8> for SetU8 {

@@ -1534,3 +1832,6 @@ fn new(val: &'a u8) -> SetU8 {

/// Getter for an `usize` value.
struct GetUsize {
// Hide the docs, because it's an implementation detail of `sockopt_impl!`
#[doc(hidden)]
#[derive(Clone, Copy, Debug)]
pub struct GetUsize {
len: socklen_t,

@@ -1567,3 +1868,6 @@ val: MaybeUninit<c_int>,

/// Setter for an `usize` value.
struct SetUsize {
// Hide the docs, because it's an implementation detail of `sockopt_impl!`
#[doc(hidden)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct SetUsize {
val: c_int,

@@ -1586,4 +1890,69 @@ }

/// Getter for a `OwnedFd` value.
// Hide the docs, because it's an implementation detail of `sockopt_impl!`
#[doc(hidden)]
#[derive(Clone, Copy, Debug)]
pub struct GetOwnedFd {
len: socklen_t,
val: MaybeUninit<c_int>,
}
impl Get<OwnedFd> for GetOwnedFd {
fn uninit() -> Self {
GetOwnedFd {
len: mem::size_of::<c_int>() as socklen_t,
val: MaybeUninit::uninit(),
}
}
fn ffi_ptr(&mut self) -> *mut c_void {
self.val.as_mut_ptr().cast()
}
fn ffi_len(&mut self) -> *mut socklen_t {
&mut self.len
}
unsafe fn assume_init(self) -> OwnedFd {
use std::os::fd::{FromRawFd, RawFd};
assert_eq!(
self.len as usize,
mem::size_of::<c_int>(),
"invalid getsockopt implementation"
);
unsafe { OwnedFd::from_raw_fd(self.val.assume_init() as RawFd) }
}
}
/// Setter for an `OwnedFd` value.
// Hide the docs, because it's an implementation detail of `sockopt_impl!`
#[doc(hidden)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct SetOwnedFd {
val: c_int,
}
impl<'a> Set<'a, OwnedFd> for SetOwnedFd {
fn new(val: &'a OwnedFd) -> SetOwnedFd {
use std::os::fd::AsRawFd;
SetOwnedFd { val: val.as_raw_fd() as c_int }
}
fn ffi_ptr(&self) -> *const c_void {
&self.val as *const c_int as *const c_void
}
fn ffi_len(&self) -> socklen_t {
mem::size_of_val(&self.val) as socklen_t
}
}
/// Getter for a `OsString` value.
struct GetOsString<T: AsMut<[u8]>> {
// Hide the docs, because it's an implementation detail of `sockopt_impl!`
#[doc(hidden)]
#[derive(Debug)]
pub struct GetOsString<T: AsMut<[u8]>> {
len: socklen_t,

@@ -1612,3 +1981,11 @@ val: MaybeUninit<T>,

let mut v = unsafe { self.val.assume_init() };
OsStr::from_bytes(&v.as_mut()[0..len]).to_owned()
if let Ok(cs) = CStr::from_bytes_until_nul(&v.as_mut()[0..len]) {
// It's legal for the kernel to return any number of NULs at the
// end of the string. C applications don't care, after all.
OsStr::from_bytes(cs.to_bytes())
} else {
// Even zero NULs is possible.
OsStr::from_bytes(&v.as_mut()[0..len])
}
.to_owned()
}

@@ -1618,8 +1995,12 @@ }

/// Setter for a `OsString` value.
struct SetOsString<'a> {
// Hide the docs, because it's an implementation detail of `sockopt_impl!`
#[doc(hidden)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct SetOsString<'a> {
val: &'a OsStr,
}
#[cfg(any(target_os = "freebsd", linux_android, target_os = "illumos"))]
impl<'a> Set<'a, OsString> for SetOsString<'a> {
fn new(val: &'a OsString) -> SetOsString {
fn new(val: &OsString) -> SetOsString {
SetOsString {

@@ -1640,2 +2021,3 @@ val: val.as_os_str(),

/// Getter for a `CString` value.
#[cfg(apple_targets)]
struct GetCString<T: AsMut<[u8]>> {

@@ -1646,2 +2028,3 @@ len: socklen_t,

#[cfg(apple_targets)]
impl<T: AsMut<[u8]>> Get<CString> for GetCString<T> {

@@ -1648,0 +2031,0 @@ fn uninit() -> Self {

@@ -9,7 +9,6 @@ #[cfg(any(apple_targets, target_os = "openbsd"))]

#[cfg(not(target_os = "redox"))]
use crate::fcntl::{at_rawfd, AtFlags};
use crate::fcntl::AtFlags;
use crate::sys::time::{TimeSpec, TimeVal};
use crate::{errno::Errno, NixPath, Result};
use std::mem;
use std::os::unix::io::RawFd;

@@ -172,4 +171,4 @@ libc_bitflags!(

#[cfg(not(any(apple_targets, target_os = "redox", target_os = "haiku")))]
pub fn mknodat<P: ?Sized + NixPath>(
dirfd: Option<RawFd>,
pub fn mknodat<Fd: std::os::fd::AsFd, P: ?Sized + NixPath>(
dirfd: Fd,
path: &P,

@@ -180,6 +179,7 @@ kind: SFlag,

) -> Result<()> {
let dirfd = at_rawfd(dirfd);
use std::os::fd::AsRawFd;
let res = path.with_nix_path(|cstr| unsafe {
libc::mknodat(
dirfd,
dirfd.as_fd().as_raw_fd(),
cstr.as_ptr(),

@@ -239,5 +239,7 @@ kind.bits() | perm.bits() as mode_t,

pub fn fstat(fd: RawFd) -> Result<FileStat> {
pub fn fstat<Fd: std::os::fd::AsFd>(fd: Fd) -> Result<FileStat> {
use std::os::fd::AsRawFd;
let mut dst = mem::MaybeUninit::uninit();
let res = unsafe { libc::fstat(fd, dst.as_mut_ptr()) };
let res = unsafe { libc::fstat(fd.as_fd().as_raw_fd(), dst.as_mut_ptr()) };

@@ -250,12 +252,13 @@ Errno::result(res)?;

#[cfg(not(target_os = "redox"))]
pub fn fstatat<P: ?Sized + NixPath>(
dirfd: Option<RawFd>,
pub fn fstatat<Fd: std::os::fd::AsFd, P: ?Sized + NixPath>(
dirfd: Fd,
pathname: &P,
f: AtFlags,
) -> Result<FileStat> {
let dirfd = at_rawfd(dirfd);
use std::os::fd::AsRawFd;
let mut dst = mem::MaybeUninit::uninit();
let res = pathname.with_nix_path(|cstr| unsafe {
libc::fstatat(
dirfd,
dirfd.as_fd().as_raw_fd(),
cstr.as_ptr(),

@@ -277,5 +280,8 @@ dst.as_mut_ptr(),

/// [fchmod(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchmod.html).
pub fn fchmod(fd: RawFd, mode: Mode) -> Result<()> {
let res = unsafe { libc::fchmod(fd, mode.bits() as mode_t) };
pub fn fchmod<Fd: std::os::fd::AsFd>(fd: Fd, mode: Mode) -> Result<()> {
use std::os::fd::AsRawFd;
let res =
unsafe { libc::fchmod(fd.as_fd().as_raw_fd(), mode.bits() as mode_t) };
Errno::result(res).map(drop)

@@ -295,3 +301,3 @@ }

/// with the file descriptor `dirfd` or the current working directory
/// if `dirfd` is `None`.
/// if `dirfd` is [`AT_FDCWD`](crate::fcntl::AT_FDCWD).
///

@@ -301,3 +307,3 @@ /// If `flag` is `FchmodatFlags::NoFollowSymlink` and `path` names a symbolic link,

///
/// `fchmodat(None, path, mode, FchmodatFlags::FollowSymlink)` is identical to
/// `fchmodat(AT_FDCWD, path, mode, FchmodatFlags::FollowSymlink)` is identical to
/// a call `libc::chmod(path, mode)`. That's why `chmod` is unimplemented

@@ -310,4 +316,4 @@ /// in the `nix` crate.

#[cfg(not(target_os = "redox"))]
pub fn fchmodat<P: ?Sized + NixPath>(
dirfd: Option<RawFd>,
pub fn fchmodat<Fd: std::os::fd::AsFd, P: ?Sized + NixPath>(
dirfd: Fd,
path: &P,

@@ -317,2 +323,4 @@ mode: Mode,

) -> Result<()> {
use std::os::fd::AsRawFd;
let atflag = match flag {

@@ -324,3 +332,3 @@ FchmodatFlags::FollowSymlink => AtFlags::empty(),

libc::fchmodat(
at_rawfd(dirfd),
dirfd.as_fd().as_raw_fd(),
cstr.as_ptr(),

@@ -397,5 +405,11 @@ mode.bits() as mode_t,

#[inline]
pub fn futimens(fd: RawFd, atime: &TimeSpec, mtime: &TimeSpec) -> Result<()> {
pub fn futimens<Fd: std::os::fd::AsFd>(
fd: Fd,
atime: &TimeSpec,
mtime: &TimeSpec,
) -> Result<()> {
use std::os::fd::AsRawFd;
let times: [libc::timespec; 2] = [*atime.as_ref(), *mtime.as_ref()];
let res = unsafe { libc::futimens(fd, &times[0]) };
let res = unsafe { libc::futimens(fd.as_fd().as_raw_fd(), &times[0]) };

@@ -417,3 +431,3 @@ Errno::result(res).map(drop)

/// with the file descriptor `dirfd` or the current working directory
/// if `dirfd` is `None`.
/// if `dirfd` is [`AT_FDCWD`](crate::fcntl::AT_FDCWD).
///

@@ -423,3 +437,3 @@ /// If `flag` is `UtimensatFlags::NoFollowSymlink` and `path` names a symbolic link,

///
/// `utimensat(None, path, times, UtimensatFlags::FollowSymlink)` is identical to
/// `utimensat(AT_FDCWD, path, times, UtimensatFlags::FollowSymlink)` is identical to
/// `utimes(path, times)`. The latter is a deprecated API so prefer using the

@@ -435,4 +449,4 @@ /// former if the platforms you care about support it.

#[cfg(not(target_os = "redox"))]
pub fn utimensat<P: ?Sized + NixPath>(
dirfd: Option<RawFd>,
pub fn utimensat<Fd: std::os::fd::AsFd, P: ?Sized + NixPath>(
dirfd: Fd,
path: &P,

@@ -443,2 +457,4 @@ atime: &TimeSpec,

) -> Result<()> {
use std::os::fd::AsRawFd;
let atflag = match flag {

@@ -451,3 +467,3 @@ UtimensatFlags::FollowSymlink => AtFlags::empty(),

libc::utimensat(
at_rawfd(dirfd),
dirfd.as_fd().as_raw_fd(),
cstr.as_ptr(),

@@ -462,11 +478,24 @@ &times[0],

/// Create a directory at the path specified by `dirfd` and `path`.
///
/// If `path` is a relative path, then it is interpreted relative to the directory
/// referred to by the file descriptor `dirfd`. (One can use [`AT_FDCWD`][link] to
/// specify the current working directory in `dirfd`). If `path` is absolute,
/// then `dirfd` is ignored.
///
/// [link]: crate::fcntl::AT_FDCWD
#[cfg(not(target_os = "redox"))]
pub fn mkdirat<P: ?Sized + NixPath>(
fd: Option<RawFd>,
pub fn mkdirat<Fd: std::os::fd::AsFd, P: ?Sized + NixPath>(
dirfd: Fd,
path: &P,
mode: Mode,
) -> Result<()> {
let fd = at_rawfd(fd);
use std::os::fd::AsRawFd;
let res = path.with_nix_path(|cstr| unsafe {
libc::mkdirat(fd, cstr.as_ptr(), mode.bits() as mode_t)
libc::mkdirat(
dirfd.as_fd().as_raw_fd(),
cstr.as_ptr(),
mode.bits() as mode_t,
)
})?;

@@ -473,0 +502,0 @@

//! Get filesystem statistics, non-portably
//!
//! See [`statvfs`](crate::sys::statvfs) for a portable alternative.
#[cfg(not(linux_android))]
#[cfg(not(any(linux_android, target_os = "cygwin")))]
use std::ffi::CStr;

@@ -22,4 +22,7 @@ use std::fmt::{self, Debug};

/// Identifies a mounted file system
#[cfg(not(target_os = "android"))]
#[cfg(not(any(target_os = "android", target_os = "cygwin")))]
pub type fsid_t = libc::fsid_t;
/// Identifies a mounted file system
#[cfg(target_os = "cygwin")]
pub type fsid_t = libc::c_long;

@@ -29,6 +32,6 @@ cfg_if! {

type type_of_statfs = libc::statfs64;
const LIBC_FSTATFS: unsafe extern fn
const LIBC_FSTATFS: unsafe extern "C" fn
(fd: libc::c_int, buf: *mut type_of_statfs) -> libc::c_int
= libc::fstatfs64;
const LIBC_STATFS: unsafe extern fn
const LIBC_STATFS: unsafe extern "C" fn
(path: *const libc::c_char, buf: *mut type_of_statfs) -> libc::c_int

@@ -38,6 +41,6 @@ = libc::statfs64;

type type_of_statfs = libc::statfs;
const LIBC_FSTATFS: unsafe extern fn
const LIBC_FSTATFS: unsafe extern "C" fn
(fd: libc::c_int, buf: *mut type_of_statfs) -> libc::c_int
= libc::fstatfs;
const LIBC_STATFS: unsafe extern fn
const LIBC_STATFS: unsafe extern "C" fn
(path: *const libc::c_char, buf: *mut type_of_statfs) -> libc::c_int

@@ -57,8 +60,10 @@ = libc::statfs;

type fs_type_t = libc::c_ulong;
#[cfg(all(target_os = "linux", target_arch = "s390x", not(target_env = "musl")))]
#[cfg(all(
target_os = "linux",
target_arch = "s390x",
not(target_env = "musl")
))]
type fs_type_t = libc::c_uint;
#[cfg(all(target_os = "linux", target_env = "musl"))]
#[cfg(all(target_os = "linux", any(target_env = "musl", target_env = "ohos")))]
type fs_type_t = libc::c_ulong;
#[cfg(all(target_os = "linux", target_env = "ohos"))]
type fs_type_t = libc::c_ulong;
#[cfg(all(target_os = "linux", target_env = "uclibc"))]

@@ -76,2 +81,4 @@ type fs_type_t = libc::c_int;

type fs_type_t = libc::__fsword_t;
#[cfg(target_os = "cygwin")]
type fs_type_t = libc::c_long;

@@ -89,2 +96,3 @@ /// Describes the file system type as known by the operating system.

),
target_os = "cygwin",
))]

@@ -308,3 +316,3 @@ #[derive(Eq, Copy, Clone, PartialEq, Debug)]

/// Magic code defining system type
#[cfg(not(linux_android))]
#[cfg(not(any(linux_android, target_os = "cygwin")))]
pub fn filesystem_type_name(&self) -> &str {

@@ -328,3 +336,7 @@ let c_str = unsafe { CStr::from_ptr(self.0.f_fstypename.as_ptr()) };

/// Optimal transfer block size
#[cfg(all(target_os = "linux", target_arch = "s390x", not(target_env = "musl")))]
#[cfg(all(
target_os = "linux",
target_arch = "s390x",
not(target_env = "musl")
))]
pub fn optimal_transfer_size(&self) -> u32 {

@@ -384,3 +396,7 @@ self.0.f_bsize

// f_bsize on linux: https://github.com/torvalds/linux/blob/master/fs/nfs/super.c#L471
#[cfg(all(target_os = "linux", target_arch = "s390x", not(target_env = "musl")))]
#[cfg(all(
target_os = "linux",
target_arch = "s390x",
not(target_env = "musl")
))]
pub fn block_size(&self) -> u32 {

@@ -439,3 +455,3 @@ self.0.f_bsize

/// Size of a block
#[cfg(target_os = "dragonfly")]
#[cfg(any(target_os = "dragonfly", target_os = "cygwin"))]
pub fn block_size(&self) -> libc::c_long {

@@ -467,3 +483,7 @@ self.0.f_bsize

/// Maximum length of filenames
#[cfg(all(target_os = "linux", target_arch = "s390x", not(target_env = "musl")))]
#[cfg(all(
target_os = "linux",
target_arch = "s390x",
not(target_env = "musl")
))]
pub fn maximum_name_length(&self) -> u32 {

@@ -518,3 +538,3 @@ self.0.f_namelen

/// Total data blocks in filesystem
#[cfg(target_os = "dragonfly")]
#[cfg(any(target_os = "dragonfly", target_os = "cygwin"))]
pub fn blocks(&self) -> libc::c_long {

@@ -543,3 +563,3 @@ self.0.f_blocks

/// Free blocks in filesystem
#[cfg(target_os = "dragonfly")]
#[cfg(any(target_os = "dragonfly", target_os = "cygwin"))]
pub fn blocks_free(&self) -> libc::c_long {

@@ -562,3 +582,3 @@ self.0.f_bfree

/// Free blocks available to unprivileged user
#[cfg(target_os = "dragonfly")]
#[cfg(any(target_os = "dragonfly", target_os = "cygwin"))]
pub fn blocks_available(&self) -> libc::c_long {

@@ -593,3 +613,3 @@ self.0.f_bavail

/// Total file nodes in filesystem
#[cfg(target_os = "dragonfly")]
#[cfg(any(target_os = "dragonfly", target_os = "cygwin"))]
pub fn files(&self) -> libc::c_long {

@@ -617,3 +637,3 @@ self.0.f_files

/// Free file nodes in filesystem
#[cfg(target_os = "dragonfly")]
#[cfg(any(target_os = "dragonfly", target_os = "cygwin"))]
pub fn files_free(&self) -> libc::c_long {

@@ -644,2 +664,3 @@ self.0.f_ffree

let mut ds = f.debug_struct("Statfs");
#[cfg(not(target_os = "cygwin"))]
ds.field("optimal_transfer_size", &self.optimal_transfer_size());

@@ -646,0 +667,0 @@ ds.field("block_size", &self.block_size());

@@ -421,3 +421,3 @@ //! An interface for controlling asynchronous communication ports

VERASE,
#[cfg(any(freebsdlike, solarish))]
#[cfg(any(freebsdlike, target_os = "illumos"))]
VERASE2,

@@ -435,3 +435,3 @@ VINTR,

VSTART,
#[cfg(any(bsd, solarish))]
#[cfg(any(bsd, target_os = "illumos"))]
VSTATUS,

@@ -466,3 +466,3 @@ VSTOP,

pub use libc::NCCS;
#[cfg(any(linux_android, target_os = "aix", bsd))]
#[cfg(any(bsd, linux_android, target_os = "aix", target_os = "solaris"))]
pub use libc::_POSIX_VDISABLE;

@@ -673,3 +673,3 @@

ECHONL;
#[cfg(not(target_os = "redox"))]
#[cfg(not(any(target_os = "redox", target_os = "cygwin")))]
ECHOPRT;

@@ -683,3 +683,3 @@ #[cfg(not(target_os = "redox"))]

IEXTEN;
#[cfg(not(any(target_os = "redox", target_os = "haiku", target_os = "aix")))]
#[cfg(not(any(target_os = "redox", target_os = "haiku", target_os = "aix", target_os = "cygwin")))]
EXTPROC;

@@ -691,3 +691,3 @@ TOSTOP;

NOKERNINFO;
#[cfg(not(target_os = "redox"))]
#[cfg(not(any(target_os = "redox", target_os = "cygwin")))]
PENDIN;

@@ -694,0 +694,0 @@ NOFLSH;

@@ -1,2 +0,5 @@

#[cfg_attr(target_env = "musl", allow(deprecated))]
#[cfg_attr(
any(target_env = "musl", target_env = "ohos"),
allow(deprecated)
)]
// https://github.com/rust-lang/libc/issues/1848

@@ -256,3 +259,6 @@ pub use libc::{suseconds_t, time_t};

#[inline]
#[cfg_attr(target_env = "musl", allow(deprecated))]
#[cfg_attr(
any(target_env = "musl", target_env = "ohos"),
allow(deprecated)
)]
// https://github.com/rust-lang/libc/issues/1848

@@ -290,3 +296,6 @@ fn seconds(seconds: i64) -> TimeSpec {

#[inline]
#[cfg_attr(target_env = "musl", allow(deprecated))]
#[cfg_attr(
any(target_env = "musl", target_env = "ohos"),
allow(deprecated)
)]
// https://github.com/rust-lang/libc/issues/1848

@@ -345,3 +354,6 @@ fn nanoseconds(nanoseconds: i64) -> TimeSpec {

/// Construct a new `TimeSpec` from its components
#[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848
#[cfg_attr(
any(target_env = "musl", target_env = "ohos"),
allow(deprecated)
)] // https://github.com/rust-lang/libc/issues/1848
pub const fn new(seconds: time_t, nanoseconds: timespec_tv_nsec_t) -> Self {

@@ -362,3 +374,6 @@ let mut ts = zero_init_timespec();

#[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848
#[cfg_attr(
any(target_env = "musl", target_env = "ohos"),
allow(deprecated)
)] // https://github.com/rust-lang/libc/issues/1848
pub const fn tv_sec(&self) -> time_t {

@@ -372,3 +387,6 @@ self.0.tv_sec

#[cfg_attr(target_env = "musl", allow(deprecated))]
#[cfg_attr(
any(target_env = "musl", target_env = "ohos"),
allow(deprecated)
)]
// https://github.com/rust-lang/libc/issues/1848

@@ -514,3 +532,6 @@ pub const fn from_duration(duration: Duration) -> Self {

);
#[cfg_attr(target_env = "musl", allow(deprecated))]
#[cfg_attr(
any(target_env = "musl", target_env = "ohos"),
allow(deprecated)
)]
// https://github.com/rust-lang/libc/issues/1848

@@ -540,3 +561,6 @@ TimeVal(timeval {

);
#[cfg_attr(target_env = "musl", allow(deprecated))]
#[cfg_attr(
any(target_env = "musl", target_env = "ohos"),
allow(deprecated)
)]
// https://github.com/rust-lang/libc/issues/1848

@@ -559,3 +583,6 @@ TimeVal(timeval {

);
#[cfg_attr(target_env = "musl", allow(deprecated))]
#[cfg_attr(
any(target_env = "musl", target_env = "ohos"),
allow(deprecated)
)]
// https://github.com/rust-lang/libc/issues/1848

@@ -597,3 +624,6 @@ TimeVal(timeval {

/// Construct a new `TimeVal` from its components
#[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848
#[cfg_attr(
any(target_env = "musl", target_env = "ohos"),
allow(deprecated)
)] // https://github.com/rust-lang/libc/issues/1848
pub const fn new(seconds: time_t, microseconds: suseconds_t) -> Self {

@@ -614,3 +644,6 @@ Self(timeval {

#[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848
#[cfg_attr(
any(target_env = "musl", target_env = "ohos"),
allow(deprecated)
)] // https://github.com/rust-lang/libc/issues/1848
pub const fn tv_sec(&self) -> time_t {

@@ -617,0 +650,0 @@ self.0.tv_sec

@@ -98,3 +98,3 @@ //! Timer API via signals.

/// - one shot: the alarm will trigger once after the specified amount of
/// time.
/// time.
/// Example: I want an alarm to go off in 60s and then disable itself.

@@ -101,0 +101,0 @@ ///

@@ -61,2 +61,8 @@ //! Timer API via file descriptors.

impl From<TimerFd> for OwnedFd {
fn from(value: TimerFd) -> Self {
value.fd
}
}
libc_enum! {

@@ -117,3 +123,3 @@ /// The type of the clock used to mark the progress of the timer. For more

/// - one shot: the alarm will trigger once after the specified amount of
/// time.
/// time.
/// Example: I want an alarm to go off in 60s and then disable itself.

@@ -213,3 +219,3 @@ ///

pub fn wait(&self) -> Result<()> {
while let Err(e) = read(self.fd.as_fd().as_raw_fd(), &mut [0u8; 8]) {
while let Err(e) = read(&self.fd, &mut [0u8; 8]) {
if e == Errno::ECANCELED {

@@ -225,2 +231,14 @@ break;

}
/// Constructs a `TimerFd` wrapping an existing `OwnedFd`.
///
/// # Safety
///
/// `OwnedFd` is a valid `TimerFd`.
pub unsafe fn from_owned_fd(fd: OwnedFd) -> Self {
Self {
fd
}
}
}

@@ -56,3 +56,3 @@ //! Vectored I/O

/// See also: [`writev`](fn.writev.html) and [`pwrite`](fn.pwrite.html)
#[cfg(not(any(target_os = "redox", target_os = "haiku", target_os = "solaris")))]
#[cfg(not(any(target_os = "redox", target_os = "haiku", target_os = "solaris", target_os = "cygwin")))]
pub fn pwritev<Fd: AsFd>(

@@ -86,3 +86,3 @@ fd: Fd,

/// See also: [`readv`](fn.readv.html) and [`pread`](fn.pread.html)
#[cfg(not(any(target_os = "redox", target_os = "haiku", target_os = "solaris")))]
#[cfg(not(any(target_os = "redox", target_os = "haiku", target_os = "solaris", target_os = "cygwin")))]
// Clippy doesn't know that we need to pass iov mutably only because the

@@ -89,0 +89,0 @@ // mutation happens after converting iov to a pointer

@@ -125,3 +125,6 @@ //! Sleep, query system clocks, and set system clock

target_os = "fuchsia",
all(target_os = "linux", target_env = "musl")
all(
target_os = "linux",
any(target_env = "musl", target_env = "ohos")
)
))]

@@ -128,0 +131,0 @@ pub const CLOCK_SGI_CYCLE: ClockId = ClockId(libc::CLOCK_SGI_CYCLE);

@@ -1,7 +0,7 @@

#[cfg(not(target_env = "musl"))]
#[cfg(not(any(target_env = "musl", target_env = "ohos")))]
use crate::errno::Errno;
use crate::sys::signal::SigSet;
#[cfg(not(target_env = "musl"))]
#[cfg(not(any(target_env = "musl", target_env = "ohos")))]
use crate::Result;
#[cfg(not(target_env = "musl"))]
#[cfg(not(any(target_env = "musl", target_env = "ohos")))]
use std::mem;

@@ -15,3 +15,3 @@

impl UContext {
#[cfg(not(target_env = "musl"))]
#[cfg(not(any(target_env = "musl", target_env = "ohos")))]
pub fn get() -> Result<UContext> {

@@ -27,3 +27,3 @@ let mut context = mem::MaybeUninit::<libc::ucontext_t>::uninit();

#[cfg(not(target_env = "musl"))]
#[cfg(not(any(target_env = "musl", target_env = "ohos")))]
pub fn set(&self) -> Result<()> {

@@ -30,0 +30,0 @@ let res = unsafe {

@@ -11,3 +11,6 @@ mod test_signal;

apple_targets,
all(target_os = "linux", not(target_env = "uclibc")),
all(
target_os = "linux",
not(any(target_env = "uclibc", target_env = "ohos"))
),
target_os = "netbsd"

@@ -20,3 +23,4 @@ ))]

target_os = "haiku",
target_os = "hurd"
target_os = "hurd",
target_os = "cygwin"
)))]

@@ -87,1 +91,11 @@ mod test_ioctl;

mod test_resource;
// This test module should be enabled for both linux_android and freebsd, but
// the `memfd_create(2)` symbol is not available under Linux QEMU,
//
// https://github.com/nix-rust/nix/actions/runs/9427112650/job/25970870477
//
// and I haven't found a way to stop the linker from linking that symbol, so
// only enable this for FreeBSD for now.
#[cfg(target_os = "freebsd")]
mod test_memfd;

@@ -9,2 +9,3 @@ // Test dropping an AioCb that hasn't yet finished.

not(target_env = "uclibc"),
not(target_env = "ohos"),
any(

@@ -11,0 +12,0 @@ target_os = "linux",

use libc::intptr_t;
use nix::sys::event::{EventFilter, EventFlag, FilterFlag, KEvent};
use nix::sys::event::{EvFlags, EventFilter, FilterFlag, KEvent};

@@ -14,3 +14,3 @@ #[test]

EventFilter::EVFILT_READ,
EventFlag::EV_ONESHOT | EventFlag::EV_ADD,
EvFlags::EV_ONESHOT | EvFlags::EV_ADD,
FilterFlag::NOTE_CHILD | FilterFlag::NOTE_EXIT,

@@ -36,3 +36,3 @@ data,

EventFilter::EVFILT_READ,
EventFlag::EV_ONESHOT | EventFlag::EV_ADD,
EvFlags::EV_ONESHOT | EvFlags::EV_ADD,
FilterFlag::NOTE_CHILD | FilterFlag::NOTE_EXIT,

@@ -39,0 +39,0 @@ 0x1337,

use crate::*;
use nix::errno::Errno;
use nix::fcntl::AT_FDCWD;
use nix::sys::fanotify::{

@@ -39,3 +40,3 @@ EventFFlags, Fanotify, FanotifyResponse, InitFlags, MarkFlags, MaskFlags,

MaskFlags::FAN_OPEN | MaskFlags::FAN_MODIFY | MaskFlags::FAN_CLOSE,
None,
AT_FDCWD,
Some(&tempfile),

@@ -103,3 +104,3 @@ )

MaskFlags::FAN_OPEN_PERM,
None,
AT_FDCWD,
Some(&tempfile),

@@ -187,3 +188,3 @@ )

MaskFlags::FAN_OPEN,
None,
AT_FDCWD,
Some(&tempfile),

@@ -190,0 +191,0 @@ )

@@ -122,1 +122,80 @@ #![allow(clippy::redundant_slicing)]

}
#[test]
#[cfg(target_os = "linux")]
fn test_mremap_dontunmap() {
use nix::libc::size_t;
use nix::sys::mman::{mremap, MRemapFlags};
use std::num::NonZeroUsize;
use std::ptr::NonNull;
const ONE_K: size_t = 1024;
let one_k_non_zero = NonZeroUsize::new(ONE_K).unwrap();
let slice: &mut [u8] = unsafe {
let mem = mmap_anonymous(
None,
one_k_non_zero,
ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
MapFlags::MAP_PRIVATE,
)
.unwrap();
std::slice::from_raw_parts_mut(mem.as_ptr().cast(), ONE_K)
};
// because we do not unmap `slice`, `old_size` and `new_size`
// need to be equal or `EINVAL` is set.
let _new_slice: &mut [u8] = unsafe {
let mem = mremap(
NonNull::from(&mut slice[..]).cast(),
ONE_K,
ONE_K,
MRemapFlags::MREMAP_MAYMOVE | MRemapFlags::MREMAP_DONTUNMAP,
None,
)
.unwrap();
std::slice::from_raw_parts_mut(mem.cast().as_ptr(), 10 * ONE_K)
};
}
#[test]
#[cfg(target_os = "linux")]
fn test_madv_wipeonfork() {
use nix::libc::size_t;
use nix::sys::mman::{madvise, MmapAdvise};
use nix::unistd::{fork, ForkResult};
use std::num::NonZeroUsize;
const ONE_K: size_t = 1024;
let ten_one_k = NonZeroUsize::new(10 * ONE_K).unwrap();
let slice: &mut [u8] = unsafe {
let mem = mmap_anonymous(
None,
ten_one_k,
ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
MapFlags::MAP_PRIVATE,
)
.unwrap();
madvise(mem, ONE_K, MmapAdvise::MADV_WIPEONFORK)
.expect("madvise failed");
std::slice::from_raw_parts_mut(mem.as_ptr().cast(), ONE_K)
};
slice[ONE_K - 1] = 0xFF;
let _m = crate::FORK_MTX.lock();
unsafe {
let res = fork().expect("fork failed");
match res {
ForkResult::Child => {
// that s the whole point of MADV_WIPEONFORK
assert_eq!(slice[ONE_K - 1], 0x00);
libc::_exit(0);
}
ForkResult::Parent { child } => {
nix::sys::signal::kill(child, nix::sys::signal::SIGTERM)
.unwrap();
let _ = nix::sys::wait::wait().unwrap();
}
}
}
}

@@ -92,12 +92,16 @@ #[cfg(target_os = "linux")]

fn test_get_set_timerslack() {
let original = prctl::get_timerslack().unwrap();
let original = prctl::get_timerslack().unwrap() as libc::c_ulong;
let slack = 60_000;
prctl::set_timerslack(slack).unwrap();
let res = prctl::get_timerslack().unwrap();
assert_eq!(slack, res as u64);
let res = prctl::get_timerslack().unwrap() as libc::c_ulong;
assert_eq!(slack, res);
prctl::set_timerslack(original as u64).unwrap();
prctl::set_timerslack(original).unwrap();
}
// Loongarch need to use a newer QEMU that disabled these PRCTL subcodes/methods.
// https://github.com/qemu/qemu/commit/220717a6f46a99031a5b1af964bbf4dec1310440
// So we should ignore them when testing in QEMU environments.
#[cfg_attr(all(qemu, target_arch = "loongarch64"), ignore)]
#[test]

@@ -116,2 +120,6 @@ fn test_disable_enable_perf_events() {

// Loongarch need to use a newer QEMU that disabled these PRCTL subcodes/methods
// https://github.com/qemu/qemu/commit/220717a6f46a99031a5b1af964bbf4dec1310440
// So we should ignore them when testing in QEMU environments.
#[cfg_attr(all(qemu, target_arch = "loongarch64"), ignore)]
#[test]

@@ -128,3 +136,8 @@ fn test_get_set_thp_disable() {

// Ignore this test under QEMU, as it started failing after updating the Linux CI
// runner image, for reasons unknown.
//
// See: https://github.com/nix-rust/nix/issues/2418
#[test]
#[cfg_attr(qemu, ignore)]
fn test_set_vma_anon_name() {

@@ -131,0 +144,0 @@ use nix::errno::Errno;

use nix::sys::pthread::*;
#[cfg(any(target_env = "musl", target_os = "redox"))]
#[cfg(any(
target_env = "musl",
target_os = "redox",
target_env = "ohos",
target_os = "cygwin"
))]
#[test]

@@ -10,3 +15,8 @@ fn test_pthread_self() {

#[cfg(not(any(target_env = "musl", target_os = "redox")))]
#[cfg(not(any(
target_env = "musl",
target_os = "redox",
target_env = "ohos",
target_os = "cygwin"
)))]
#[test]

@@ -13,0 +23,0 @@ fn test_pthread_self() {

@@ -182,8 +182,13 @@ #[cfg(all(

target_os = "linux",
target_env = "gnu",
any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "aarch64",
target_arch = "riscv64",
all(
target_env = "gnu",
any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "aarch64",
target_arch = "riscv64"
)
),
all(target_env = "musl", target_arch = "aarch64")
)

@@ -296,8 +301,13 @@ ))]

target_os = "linux",
target_env = "gnu",
any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "aarch64",
target_arch = "riscv64",
all(
target_env = "gnu",
any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "aarch64",
target_arch = "riscv64"
)
),
all(target_env = "musl", target_arch = "aarch64")
)

@@ -376,1 +386,30 @@ ))]

}
#[cfg(all(target_os = "linux", target_env = "gnu"))]
#[test]
fn test_ptrace_syscall_info() {
use nix::sys::ptrace;
use nix::sys::wait::{waitpid, WaitStatus};
use nix::unistd::fork;
use nix::unistd::ForkResult::*;
require_capability!("test_ptrace_syscall_info", CAP_SYS_PTRACE);
let _m = crate::FORK_MTX.lock();
match unsafe { fork() }.expect("Error: Fork Failed") {
Child => {
ptrace::traceme().unwrap();
std::thread::sleep(std::time::Duration::from_millis(1000));
unsafe {
::libc::_exit(0);
}
}
Parent { child } => loop {
if let Ok(WaitStatus::Exited(_, 0)) = waitpid(child, None) {
break;
}
let si = ptrace::syscall_info(child).unwrap();
assert!(si.op >= libc::PTRACE_SYSCALL_INFO_ENTRY);
},
}
}

@@ -13,2 +13,3 @@ use nix::sys::resource::{getrlimit, setrlimit, Resource};

#[test]
#[cfg_attr(target_os = "cygwin", ignore)]
pub fn test_resource_limits_nofile() {

@@ -15,0 +16,0 @@ let (mut soft_limit, hard_limit) =

@@ -7,3 +7,3 @@ #[cfg(linux_android)]

};
use rand::{thread_rng, Rng};
use rand::{rng, Rng};
use std::os::unix::io::{AsRawFd, FromRawFd, OwnedFd};

@@ -70,2 +70,32 @@

#[cfg(apple_targets)]
#[test]
pub fn test_local_peer_token() {
use nix::sys::socket::{audit_token_t, socketpair};
#[link(name = "bsm", kind = "dylib")]
extern "C" {
/// Extract the process ID from an `audit_token_t`, used to identify
/// Mach tasks and senders of Mach messages as subjects of the audit
/// system.
///
/// - `atoken`: The Mach audit token.
/// - Returns: The process ID extracted from the Mach audit token.
fn audit_token_to_pid(atoken: audit_token_t) -> libc::pid_t;
}
let (fd1, _fd2) = socketpair(
AddressFamily::Unix,
SockType::Stream,
None,
SockFlag::empty(),
)
.unwrap();
let audit_token = getsockopt(&fd1, sockopt::LocalPeerToken).unwrap();
assert_eq!(
unsafe { audit_token_to_pid(audit_token) },
std::process::id() as _
);
}
#[cfg(target_os = "linux")]

@@ -99,3 +129,3 @@ #[test]

.unwrap();
let bufsize: usize = thread_rng().gen_range(4096..131_072);
let bufsize: usize = rng().random_range(4096..131_072);
setsockopt(&fd, sockopt::SndBuf, &bufsize).unwrap();

@@ -135,2 +165,3 @@ let actual = getsockopt(&fd, sockopt::SndBuf).unwrap();

#[test]
#[cfg_attr(target_os = "cygwin", ignore)]
fn test_so_tcp_maxseg() {

@@ -140,3 +171,2 @@ use nix::sys::socket::{

};
use nix::unistd::write;
use std::net::SocketAddrV4;

@@ -161,10 +191,8 @@ use std::str::FromStr;

// platforms keep it even lower. This might fail if you've tuned your initial MSS to be larger
// than 700
// than `segsize`
let segsize: u32 = 873;
assert!(initial < segsize);
cfg_if! {
if #[cfg(linux_android)] {
let segsize: u32 = 873;
assert!(initial < segsize);
setsockopt(&rsock, sockopt::TcpMaxSeg, &segsize).unwrap();
} else {
assert!(initial < 700);
}

@@ -181,16 +209,34 @@ }

.unwrap();
connect(ssock.as_raw_fd(), &sock_addr).unwrap();
let rsess = accept(rsock.as_raw_fd()).unwrap();
let rsess = unsafe { OwnedFd::from_raw_fd(rsess) };
write(&rsess, b"hello").unwrap();
let actual = getsockopt(&ssock, sockopt::TcpMaxSeg).unwrap();
// Actual max segment size takes header lengths into account, max IPv4 options (60 bytes) + max
// TCP options (40 bytes) are subtracted from the requested maximum as a lower boundary.
cfg_if! {
if #[cfg(linux_android)] {
assert!((segsize - 100) <= actual);
assert!(actual <= segsize);
if #[cfg(apple_targets)] {
// on apple targets (and unlike linux), we can only set the MSS on a *connected*
// socket. Also, the same MSS can't be read using getsockopt from the other end.
assert_ne!(segsize, getsockopt(&rsess, sockopt::TcpMaxSeg).unwrap());
setsockopt(&rsess, sockopt::TcpMaxSeg, &segsize).unwrap();
assert_eq!(segsize, getsockopt(&rsess, sockopt::TcpMaxSeg).unwrap());
assert_ne!(segsize, getsockopt(&ssock, sockopt::TcpMaxSeg).unwrap());
setsockopt(&ssock, sockopt::TcpMaxSeg, &segsize).unwrap();
assert_eq!(segsize, getsockopt(&ssock, sockopt::TcpMaxSeg).unwrap());
} else {
assert!(initial < actual);
assert!(536 < actual);
use nix::unistd::write;
write(&rsess, b"hello").unwrap();
let actual = getsockopt(&ssock, sockopt::TcpMaxSeg).unwrap();
// Actual max segment size takes header lengths into account, max IPv4 options (60 bytes) + max
// TCP options (40 bytes) are subtracted from the requested maximum as a lower boundary.
if cfg!(linux_android) {
assert!((segsize - 100) <= actual);
assert!(actual <= segsize);
} else {
assert!(initial < actual);
assert!(536 < actual);
}
}

@@ -221,2 +267,4 @@ }

require_capability!("test_so_type", CAP_NET_RAW);
// SOCK_PACKET is deprecated, but since it is used for testing here, we allow it
#[allow(deprecated)]
let raw_fd = unsafe { libc::socket(libc::AF_PACKET, libc::SOCK_PACKET, 0) };

@@ -230,12 +278,12 @@ assert!(raw_fd >= 0, "Error opening socket: {}", nix::Error::last());

// The CI doesn't supported getsockopt and setsockopt on emulated processors.
// It's believed that a QEMU issue, the tests run ok on a fully emulated system.
// Current CI just run the binary with QEMU but the Kernel remains the same as the host.
// It's believed to be a QEMU issue; the tests run ok on a fully emulated
// system. Current CI just runs the binary with QEMU but the kernel remains the
// same as the host.
// So the syscall doesn't work properly unless the kernel is also emulated.
#[test]
#[cfg(all(
any(target_arch = "x86", target_arch = "x86_64"),
any(target_os = "freebsd", target_os = "linux")
))]
#[cfg(any(target_os = "freebsd", target_os = "linux"))]
#[cfg_attr(qemu, ignore)]
fn test_tcp_congestion() {
use std::ffi::OsString;
use std::os::unix::ffi::OsStrExt;

@@ -251,2 +299,10 @@ let fd = socket(

let val = getsockopt(&fd, sockopt::TcpCongestion).unwrap();
let bytes = val.as_os_str().as_bytes();
for b in bytes.iter() {
assert_ne!(
*b, 0,
"OsString should contain no embedded NULs: {:?}",
val
);
}
setsockopt(&fd, sockopt::TcpCongestion, &val).unwrap();

@@ -265,2 +321,31 @@

#[test]
#[cfg(target_os = "freebsd")]
fn test_tcp_function_blk_alias() {
use std::ffi::CStr;
let fd = socket(
AddressFamily::Inet,
SockType::Stream,
SockFlag::empty(),
None,
)
.unwrap();
let tfs = getsockopt(&fd, sockopt::TcpFunctionBlk).unwrap();
let name = unsafe { CStr::from_ptr(tfs.function_set_name.as_ptr()) };
assert!(!name.to_bytes().is_empty());
let aliastfs = getsockopt(&fd, sockopt::TcpFunctionAlias).unwrap();
let aliasname =
unsafe { CStr::from_ptr(aliastfs.function_set_name.as_ptr()) };
// freebsd default tcp stack has no alias.
assert!(aliasname.to_bytes().is_empty());
// We can't know at compile time what options are available. So just test the setter by a
// no-op set.
// TODO: test if we can load for example BBR tcp stack kernel module.
setsockopt(&fd, sockopt::TcpFunctionBlk, &tfs).unwrap();
}
#[test]
#[cfg(linux_android)]

@@ -483,3 +568,3 @@ fn test_bindtodevice() {

#[test]
#[cfg(target_os = "linux")]
#[cfg(any(linux_android, target_os = "freebsd"))]
fn test_ip_tos() {

@@ -494,8 +579,8 @@ let fd = socket(

let tos = 0x80; // CS4
setsockopt(&fd, sockopt::IpTos, &tos).unwrap();
assert_eq!(getsockopt(&fd, sockopt::IpTos).unwrap(), tos);
setsockopt(&fd, sockopt::Ipv4Tos, &tos).unwrap();
assert_eq!(getsockopt(&fd, sockopt::Ipv4Tos).unwrap(), tos);
}
#[test]
#[cfg(target_os = "linux")]
#[cfg(any(linux_android, target_os = "freebsd"))]
// Disable the test under emulation because it fails in Cirrus-CI. Lack

@@ -699,3 +784,43 @@ // of QEMU support is suspected.

#[cfg(target_os = "linux")]
fn pid_from_pidfd(pidfd: OwnedFd) -> u32 {
use std::fs::read_to_string;
let fd = pidfd.as_raw_fd();
let fdinfo = read_to_string(format!("/proc/self/fdinfo/{fd}")).unwrap();
let pidline = fdinfo.split('\n').find(|s| s.starts_with("Pid:")).unwrap();
pidline.split('\t').next_back().unwrap().parse().unwrap()
}
#[cfg(target_os = "linux")]
#[test]
fn can_get_peerpidfd_on_unix_socket() {
use nix::sys::socket::{socketpair, sockopt, SockFlag, SockType};
let (a, b) = socketpair(
AddressFamily::Unix,
SockType::Stream,
None,
SockFlag::empty(),
)
.unwrap();
match (
getsockopt(&a, sockopt::PeerPidfd),
getsockopt(&b, sockopt::PeerPidfd),
) {
(Ok(a_pidfd), Ok(b_pidfd)) => {
let a_pid = pid_from_pidfd(a_pidfd);
let b_pid = pid_from_pidfd(b_pidfd);
assert_eq!(a_pid, b_pid);
assert_ne!(a_pid, 0);
}
(Err(nix::Error::ENOPROTOOPT), Err(nix::Error::ENOPROTOOPT)) => {
// Pidfd can still be unsupported on some CI runners
}
(Err(err), _) | (_, Err(err)) => panic!("{err:?}"),
};
}
#[test]
fn is_socket_type_unix() {

@@ -894,1 +1019,273 @@ use nix::sys::socket::{socketpair, sockopt, SockFlag, SockType};

}
#[test]
#[cfg(any(linux_android, target_os = "freebsd"))]
fn test_ipv4_recv_ttl_opts() {
let fd = socket(
AddressFamily::Inet,
SockType::Stream,
SockFlag::empty(),
SockProtocol::Tcp,
)
.unwrap();
setsockopt(&fd, sockopt::Ipv4RecvTtl, &true)
.expect("setting IP_RECVTTL on an inet stream socket should succeed");
setsockopt(&fd, sockopt::Ipv4RecvTtl, &false)
.expect("unsetting IP_RECVTTL on an inet stream socket should succeed");
let fdd = socket(
AddressFamily::Inet,
SockType::Datagram,
SockFlag::empty(),
None,
)
.unwrap();
setsockopt(&fdd, sockopt::Ipv4RecvTtl, &true)
.expect("setting IP_RECVTTL on an inet datagram socket should succeed");
setsockopt(&fdd, sockopt::Ipv4RecvTtl, &false).expect(
"unsetting IP_RECVTTL on an inet datagram socket should succeed",
);
}
#[test]
#[cfg(any(linux_android, target_os = "freebsd"))]
fn test_ipv6_recv_hop_limit_opts() {
let fd = socket(
AddressFamily::Inet6,
SockType::Stream,
SockFlag::empty(),
SockProtocol::Tcp,
)
.unwrap();
setsockopt(&fd, sockopt::Ipv6RecvHopLimit, &true).expect(
"setting IPV6_RECVHOPLIMIT on an inet6 stream socket should succeed",
);
setsockopt(&fd, sockopt::Ipv6RecvHopLimit, &false).expect(
"unsetting IPV6_RECVHOPLIMIT on an inet6 stream socket should succeed",
);
let fdd = socket(
AddressFamily::Inet6,
SockType::Datagram,
SockFlag::empty(),
None,
)
.unwrap();
setsockopt(&fdd, sockopt::Ipv6RecvHopLimit, &true).expect(
"setting IPV6_RECVHOPLIMIT on an inet6 datagram socket should succeed",
);
setsockopt(&fdd, sockopt::Ipv6RecvHopLimit, &false).expect(
"unsetting IPV6_RECVHOPLIMIT on an inet6 datagram socket should succeed",
);
}
#[test]
#[cfg(any(linux_android, target_os = "freebsd"))]
fn test_ipv4_recv_tos_opts() {
let fd = socket(
AddressFamily::Inet,
SockType::Stream,
SockFlag::empty(),
SockProtocol::Tcp,
)
.unwrap();
setsockopt(&fd, sockopt::IpRecvTos, &true)
.expect("setting IP_RECVTOS on an inet stream socket should succeed");
setsockopt(&fd, sockopt::IpRecvTos, &false)
.expect("unsetting IP_RECVTOS on an inet stream socket should succeed");
let fdd = socket(
AddressFamily::Inet,
SockType::Datagram,
SockFlag::empty(),
None,
)
.unwrap();
setsockopt(&fdd, sockopt::IpRecvTos, &true)
.expect("setting IP_RECVTOS on an inet datagram socket should succeed");
setsockopt(&fdd, sockopt::IpRecvTos, &false).expect(
"unsetting IP_RECVTOS on an inet datagram socket should succeed",
);
}
#[test]
#[cfg(any(linux_android, target_os = "freebsd"))]
fn test_ipv6_recv_traffic_class_opts() {
let fd = socket(
AddressFamily::Inet6,
SockType::Stream,
SockFlag::empty(),
SockProtocol::Tcp,
)
.unwrap();
setsockopt(&fd, sockopt::Ipv6RecvTClass, &true).expect(
"setting IPV6_RECVTCLASS on an inet6 stream socket should succeed",
);
setsockopt(&fd, sockopt::Ipv6RecvTClass, &false).expect(
"unsetting IPV6_RECVTCLASS on an inet6 stream socket should succeed",
);
let fdd = socket(
AddressFamily::Inet6,
SockType::Datagram,
SockFlag::empty(),
None,
)
.unwrap();
setsockopt(&fdd, sockopt::Ipv6RecvTClass, &true).expect(
"setting IPV6_RECVTCLASS on an inet6 datagram socket should succeed",
);
setsockopt(&fdd, sockopt::Ipv6RecvTClass, &false).expect(
"unsetting IPV6_RECVTCLASS on an inet6 datagram socket should succeed",
);
}
#[cfg(apple_targets)]
#[test]
fn test_linger_sec() {
let fd = socket(
AddressFamily::Inet,
SockType::Stream,
SockFlag::empty(),
None,
)
.unwrap();
let set_linger = libc::linger {
l_onoff: 1,
l_linger: 1,
};
setsockopt(&fd, sockopt::LingerSec, &set_linger).unwrap();
let get_linger = getsockopt(&fd, sockopt::Linger).unwrap();
assert_eq!(get_linger.l_linger, set_linger.l_linger * 100);
}
/// Users should be able to define their own sockopts.
mod sockopt_impl {
use nix::sys::socket::{
getsockopt, setsockopt, socket, AddressFamily, SockFlag, SockProtocol,
SockType,
};
sockopt_impl!(KeepAlive, Both, libc::SOL_SOCKET, libc::SO_KEEPALIVE, bool);
#[test]
fn test_so_tcp_keepalive() {
let fd = socket(
AddressFamily::Inet,
SockType::Stream,
SockFlag::empty(),
SockProtocol::Tcp,
)
.unwrap();
setsockopt(&fd, KeepAlive, &true).unwrap();
assert!(getsockopt(&fd, KeepAlive).unwrap());
}
sockopt_impl!(
Linger,
Both,
libc::SOL_SOCKET,
libc::SO_LINGER,
libc::linger
);
#[test]
fn test_linger() {
let fd = socket(
AddressFamily::Inet,
SockType::Stream,
SockFlag::empty(),
None,
)
.unwrap();
let set_linger = libc::linger {
l_onoff: 1,
l_linger: 42,
};
setsockopt(&fd, Linger, &set_linger).unwrap();
let get_linger = getsockopt(&fd, Linger).unwrap();
assert_eq!(get_linger.l_linger, set_linger.l_linger);
}
}
#[cfg(solarish)]
#[test]
fn test_exclbind() {
use nix::errno::Errno;
use nix::sys::socket::{
bind, socket, AddressFamily, SockFlag, SockType, SockaddrIn,
};
use std::net::SocketAddrV4;
use std::str::FromStr;
let fd1 = socket(
AddressFamily::Inet,
SockType::Stream,
SockFlag::empty(),
None,
)
.unwrap();
let addr = SocketAddrV4::from_str("127.0.0.1:8081").unwrap();
let excl = true;
setsockopt(&fd1, sockopt::ExclBind, &excl).unwrap();
bind(fd1.as_raw_fd(), &SockaddrIn::from(addr)).unwrap();
assert_eq!(getsockopt(&fd1, sockopt::ExclBind), Ok(true));
let fd2 = socket(
AddressFamily::Inet,
SockType::Stream,
SockFlag::empty(),
None,
)
.unwrap();
assert_eq!(
bind(fd2.as_raw_fd(), &SockaddrIn::from(addr)),
Err(Errno::EADDRINUSE)
);
}
#[cfg(target_os = "illumos")]
#[test]
fn test_solfilter() {
use nix::errno::Errno;
let s = socket(
AddressFamily::Inet,
SockType::Stream,
SockFlag::empty(),
SockProtocol::Tcp,
)
.unwrap();
let data = std::ffi::OsStr::new("httpf");
let attach = sockopt::FilterAttach;
let detach = sockopt::FilterDetach;
// These 2 options won't work unless the needed kernel module is installed:
// https://github.com/nix-rust/nix/pull/2611#issuecomment-2750237782
//
// So we only test the binding here
assert_eq!(Err(Errno::ENOENT), setsockopt(&s, attach, data));
assert_eq!(Err(Errno::ENOENT), setsockopt(&s, detach, data));
}
#[cfg(target_os = "linux")]
#[test]
pub fn test_so_attach_reuseport_cbpf() {
let fd = socket(
AddressFamily::Inet6,
SockType::Datagram,
SockFlag::empty(),
None,
)
.unwrap();
setsockopt(&fd, sockopt::ReusePort, &true).unwrap();
setsockopt(&fd, sockopt::ReuseAddr, &true).unwrap();
let mut flt: [libc::sock_filter; 2] = unsafe { std::mem::zeroed() };
flt[0].code = (libc::BPF_LD | libc::BPF_W | libc::BPF_ABS) as u16;
flt[0].k = (libc::SKF_AD_OFF + libc::SKF_AD_CPU) as u32;
flt[1].code = (libc::BPF_RET | 0x10) as u16;
let fp = libc::sock_fprog {
len: flt.len() as u16,
filter: flt.as_mut_ptr(),
};
setsockopt(&fd, sockopt::AttachReusePortCbpf, &fp).unwrap_or_else(|e| {
assert_eq!(e, nix::errno::Errno::ENOPROTOOPT);
});
}

@@ -0,1 +1,483 @@

#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
use std::fs;
use std::fs::File;
#[cfg(not(target_os = "redox"))]
use std::os::unix::fs::symlink;
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
use std::os::unix::fs::PermissionsExt;
#[cfg(not(target_os = "redox"))]
use std::path::Path;
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
use std::time::{Duration, UNIX_EPOCH};
use libc::mode_t;
#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
use libc::{S_IFLNK, S_IFMT};
#[cfg(not(target_os = "redox"))]
use nix::errno::Errno;
#[cfg(not(target_os = "redox"))]
use nix::fcntl;
#[cfg(any(
target_os = "linux",
apple_targets,
target_os = "freebsd",
target_os = "netbsd"
))]
use nix::sys::stat::lutimes;
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
use nix::sys::stat::utimensat;
#[cfg(not(target_os = "redox"))]
use nix::sys::stat::FchmodatFlags;
use nix::sys::stat::Mode;
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
use nix::sys::stat::UtimensatFlags;
#[cfg(not(target_os = "redox"))]
use nix::sys::stat::{self};
use nix::sys::stat::{fchmod, stat};
#[cfg(not(target_os = "redox"))]
use nix::sys::stat::{fchmodat, mkdirat};
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
use nix::sys::stat::{futimens, utimes};
#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
use nix::sys::stat::FileStat;
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
use nix::sys::time::{TimeSpec, TimeVal, TimeValLike};
#[cfg(not(target_os = "redox"))]
use nix::unistd::chdir;
#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
use nix::Result;
#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
fn assert_stat_results(stat_result: Result<FileStat>) {
let stats = stat_result.expect("stat call failed");
assert!(stats.st_dev > 0); // must be positive integer, exact number machine dependent
assert!(stats.st_ino > 0); // inode is positive integer, exact number machine dependent
assert!(stats.st_mode > 0); // must be positive integer
assert_eq!(stats.st_nlink, 1); // there links created, must be 1
assert_eq!(stats.st_size, 0); // size is 0 because we did not write anything to the file
assert!(stats.st_blksize > 0); // must be positive integer, exact number machine dependent
assert!(stats.st_blocks <= 16); // Up to 16 blocks can be allocated for a blank file
}
#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
// (Android's st_blocks is ulonglong which is always non-negative.)
#[cfg_attr(target_os = "android", allow(unused_comparisons))]
#[allow(clippy::absurd_extreme_comparisons)] // Not absurd on all OSes
fn assert_lstat_results(stat_result: Result<FileStat>) {
let stats = stat_result.expect("stat call failed");
assert!(stats.st_dev > 0); // must be positive integer, exact number machine dependent
assert!(stats.st_ino > 0); // inode is positive integer, exact number machine dependent
assert!(stats.st_mode > 0); // must be positive integer
// st_mode is c_uint (u32 on Android) while S_IFMT is mode_t
// (u16 on Android), and that will be a compile error.
// On other platforms they are the same (either both are u16 or u32).
assert_eq!(
(stats.st_mode as usize) & (S_IFMT as usize),
S_IFLNK as usize
); // should be a link
assert_eq!(stats.st_nlink, 1); // there links created, must be 1
assert!(stats.st_size > 0); // size is > 0 because it points to another file
assert!(stats.st_blksize > 0); // must be positive integer, exact number machine dependent
// st_blocks depends on whether the machine's file system uses fast
// or slow symlinks, so just make sure it's not negative
assert!(stats.st_blocks >= 0);
}
#[test]
#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
fn test_stat_and_fstat() {
use nix::sys::stat::fstat;
let tempdir = tempfile::tempdir().unwrap();
let filename = tempdir.path().join("foo.txt");
let file = File::create(&filename).unwrap();
let stat_result = stat(&filename);
assert_stat_results(stat_result);
let fstat_result = fstat(&file);
assert_stat_results(fstat_result);
}
#[test]
#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
fn test_fstatat() {
let tempdir = tempfile::tempdir().unwrap();
let filename = tempdir.path().join("foo.txt");
File::create(&filename).unwrap();
let dirfd =
fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
.unwrap();
let result = stat::fstatat(&dirfd, &filename, fcntl::AtFlags::empty());
assert_stat_results(result);
}
#[test]
#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
fn test_stat_fstat_lstat() {
use nix::sys::stat::{fstat, lstat};
let tempdir = tempfile::tempdir().unwrap();
let filename = tempdir.path().join("bar.txt");
let linkname = tempdir.path().join("barlink");
File::create(&filename).unwrap();
symlink("bar.txt", &linkname).unwrap();
let link = File::open(&linkname).unwrap();
// should be the same result as calling stat,
// since it's a regular file
let stat_result = stat(&filename);
assert_stat_results(stat_result);
let lstat_result = lstat(&linkname);
assert_lstat_results(lstat_result);
let fstat_result = fstat(&link);
assert_stat_results(fstat_result);
}
#[test]
fn test_fchmod() {
let tempdir = tempfile::tempdir().unwrap();
let filename = tempdir.path().join("foo.txt");
let file = File::create(&filename).unwrap();
let mut mode1 = Mode::empty();
mode1.insert(Mode::S_IRUSR);
mode1.insert(Mode::S_IWUSR);
fchmod(&file, mode1).unwrap();
let file_stat1 = stat(&filename).unwrap();
assert_eq!(file_stat1.st_mode as mode_t & 0o7777, mode1.bits());
let mut mode2 = Mode::empty();
mode2.insert(Mode::S_IROTH);
fchmod(&file, mode2).unwrap();
let file_stat2 = stat(&filename).unwrap();
assert_eq!(file_stat2.st_mode as mode_t & 0o7777, mode2.bits());
}
#[test]
#[cfg(not(target_os = "redox"))]
fn test_fchmodat() {
let _dr = crate::DirRestore::new();
let tempdir = tempfile::tempdir().unwrap();
let filename = "foo.txt";
let fullpath = tempdir.path().join(filename);
File::create(&fullpath).unwrap();
let dirfd =
fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
.unwrap();
let mut mode1 = Mode::empty();
mode1.insert(Mode::S_IRUSR);
mode1.insert(Mode::S_IWUSR);
fchmodat(&dirfd, filename, mode1, FchmodatFlags::FollowSymlink).unwrap();
let file_stat1 = stat(&fullpath).unwrap();
assert_eq!(file_stat1.st_mode as mode_t & 0o7777, mode1.bits());
chdir(tempdir.path()).unwrap();
let mut mode2 = Mode::empty();
mode2.insert(Mode::S_IROTH);
fchmodat(
fcntl::AT_FDCWD,
filename,
mode2,
FchmodatFlags::FollowSymlink,
)
.unwrap();
let file_stat2 = stat(&fullpath).unwrap();
assert_eq!(file_stat2.st_mode as mode_t & 0o7777, mode2.bits());
}
/// Asserts that the atime and mtime in a file's metadata match expected values.
///
/// The atime and mtime are expressed with a resolution of seconds because some file systems
/// (like macOS's HFS+) do not have higher granularity.
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
fn assert_times_eq(
exp_atime_sec: u64,
exp_mtime_sec: u64,
attr: &fs::Metadata,
) {
assert_eq!(
Duration::new(exp_atime_sec, 0),
attr.accessed().unwrap().duration_since(UNIX_EPOCH).unwrap()
);
assert_eq!(
Duration::new(exp_mtime_sec, 0),
attr.modified().unwrap().duration_since(UNIX_EPOCH).unwrap()
);
}
#[test]
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
fn test_utimes() {
let tempdir = tempfile::tempdir().unwrap();
let fullpath = tempdir.path().join("file");
drop(File::create(&fullpath).unwrap());
utimes(&fullpath, &TimeVal::seconds(9990), &TimeVal::seconds(5550))
.unwrap();
assert_times_eq(9990, 5550, &fs::metadata(&fullpath).unwrap());
}
#[test]
#[cfg(any(
target_os = "linux",
apple_targets,
target_os = "freebsd",
target_os = "netbsd"
))]
fn test_lutimes() {
let tempdir = tempfile::tempdir().unwrap();
let target = tempdir.path().join("target");
let fullpath = tempdir.path().join("symlink");
drop(File::create(&target).unwrap());
symlink(&target, &fullpath).unwrap();
let exp_target_metadata = fs::symlink_metadata(&target).unwrap();
lutimes(&fullpath, &TimeVal::seconds(4560), &TimeVal::seconds(1230))
.unwrap();
assert_times_eq(4560, 1230, &fs::symlink_metadata(&fullpath).unwrap());
let target_metadata = fs::symlink_metadata(&target).unwrap();
assert_eq!(
exp_target_metadata.accessed().unwrap(),
target_metadata.accessed().unwrap(),
"atime of symlink target was unexpectedly modified"
);
assert_eq!(
exp_target_metadata.modified().unwrap(),
target_metadata.modified().unwrap(),
"mtime of symlink target was unexpectedly modified"
);
}
#[test]
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
fn test_futimens() {
let tempdir = tempfile::tempdir().unwrap();
let fullpath = tempdir.path().join("file");
drop(File::create(&fullpath).unwrap());
let fd = fcntl::open(&fullpath, fcntl::OFlag::empty(), stat::Mode::empty())
.unwrap();
futimens(&fd, &TimeSpec::seconds(10), &TimeSpec::seconds(20)).unwrap();
assert_times_eq(10, 20, &fs::metadata(&fullpath).unwrap());
}
#[test]
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
fn test_utimensat() {
let _dr = crate::DirRestore::new();
let tempdir = tempfile::tempdir().unwrap();
let filename = "foo.txt";
let fullpath = tempdir.path().join(filename);
drop(File::create(&fullpath).unwrap());
let dirfd =
fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
.unwrap();
utimensat(
&dirfd,
filename,
&TimeSpec::seconds(12345),
&TimeSpec::seconds(678),
UtimensatFlags::FollowSymlink,
)
.unwrap();
assert_times_eq(12345, 678, &fs::metadata(&fullpath).unwrap());
chdir(tempdir.path()).unwrap();
utimensat(
fcntl::AT_FDCWD,
filename,
&TimeSpec::seconds(500),
&TimeSpec::seconds(800),
UtimensatFlags::FollowSymlink,
)
.unwrap();
assert_times_eq(500, 800, &fs::metadata(&fullpath).unwrap());
}
#[test]
#[cfg(not(target_os = "redox"))]
fn test_mkdirat_success_path() {
let tempdir = tempfile::tempdir().unwrap();
let filename = "example_subdir";
let dirfd =
fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
.unwrap();
mkdirat(&dirfd, filename, Mode::S_IRWXU).expect("mkdirat failed");
assert!(Path::exists(&tempdir.path().join(filename)));
}
#[test]
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
fn test_mkdirat_success_mode() {
let expected_bits =
stat::SFlag::S_IFDIR.bits() | stat::Mode::S_IRWXU.bits();
let tempdir = tempfile::tempdir().unwrap();
let filename = "example_subdir";
let dirfd =
fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
.unwrap();
mkdirat(&dirfd, filename, Mode::S_IRWXU).expect("mkdirat failed");
let permissions = fs::metadata(tempdir.path().join(filename))
.unwrap()
.permissions();
let mode = permissions.mode();
assert_eq!(mode as mode_t, expected_bits)
}
#[test]
#[cfg(not(target_os = "redox"))]
fn test_mkdirat_fail() {
let tempdir = tempfile::tempdir().unwrap();
let not_dir_filename = "example_not_dir";
let filename = "example_subdir_dir";
let dirfd = fcntl::open(
&tempdir.path().join(not_dir_filename),
fcntl::OFlag::O_CREAT,
stat::Mode::empty(),
)
.unwrap();
let result = mkdirat(dirfd, filename, Mode::S_IRWXU).unwrap_err();
assert_eq!(result, Errno::ENOTDIR);
}
#[test]
#[cfg(not(any(
freebsdlike,
apple_targets,
target_os = "haiku",
target_os = "redox",
target_os = "solaris"
)))]
fn test_mknod() {
use stat::{lstat, mknod, SFlag};
let file_name = "test_file";
let tempdir = tempfile::tempdir().unwrap();
let target = tempdir.path().join(file_name);
mknod(&target, SFlag::S_IFREG, Mode::S_IRWXU, 0).unwrap();
let mode = lstat(&target).unwrap().st_mode as mode_t;
assert_eq!(mode & libc::S_IFREG, libc::S_IFREG);
assert_eq!(mode & libc::S_IRWXU, libc::S_IRWXU);
}
#[test]
#[cfg(not(any(
solarish,
freebsdlike,
apple_targets,
target_os = "haiku",
target_os = "redox"
)))]
fn test_mknodat() {
use fcntl::{AtFlags, OFlag};
use nix::dir::Dir;
use stat::{fstatat, mknodat, SFlag};
let file_name = "test_file";
let tempdir = tempfile::tempdir().unwrap();
let target_dir =
Dir::open(tempdir.path(), OFlag::O_DIRECTORY, Mode::S_IRWXU).unwrap();
mknodat(&target_dir, file_name, SFlag::S_IFREG, Mode::S_IRWXU, 0).unwrap();
let mode = fstatat(&target_dir, file_name, AtFlags::AT_SYMLINK_NOFOLLOW)
.unwrap()
.st_mode as mode_t;
assert_eq!(mode & libc::S_IFREG, libc::S_IFREG);
assert_eq!(mode & libc::S_IRWXU, libc::S_IRWXU);
}
#[test]
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
fn test_futimens_unchanged() {
let tempdir = tempfile::tempdir().unwrap();
let fullpath = tempdir.path().join("file");
drop(File::create(&fullpath).unwrap());
let fd = fcntl::open(&fullpath, fcntl::OFlag::empty(), stat::Mode::empty())
.unwrap();
let old_atime = fs::metadata(fullpath.as_path())
.unwrap()
.accessed()
.unwrap();
let old_mtime = fs::metadata(fullpath.as_path())
.unwrap()
.modified()
.unwrap();
futimens(&fd, &TimeSpec::UTIME_OMIT, &TimeSpec::UTIME_OMIT).unwrap();
let new_atime = fs::metadata(fullpath.as_path())
.unwrap()
.accessed()
.unwrap();
let new_mtime = fs::metadata(fullpath.as_path())
.unwrap()
.modified()
.unwrap();
assert_eq!(old_atime, new_atime);
assert_eq!(old_mtime, new_mtime);
}
#[test]
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
fn test_utimensat_unchanged() {
let _dr = crate::DirRestore::new();
let tempdir = tempfile::tempdir().unwrap();
let filename = "foo.txt";
let fullpath = tempdir.path().join(filename);
drop(File::create(&fullpath).unwrap());
let dirfd =
fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
.unwrap();
let old_atime = fs::metadata(fullpath.as_path())
.unwrap()
.accessed()
.unwrap();
let old_mtime = fs::metadata(fullpath.as_path())
.unwrap()
.modified()
.unwrap();
utimensat(
&dirfd,
filename,
&TimeSpec::UTIME_OMIT,
&TimeSpec::UTIME_OMIT,
UtimensatFlags::NoFollowSymlink,
)
.unwrap();
let new_atime = fs::metadata(fullpath.as_path())
.unwrap()
.accessed()
.unwrap();
let new_mtime = fs::metadata(fullpath.as_path())
.unwrap()
.modified()
.unwrap();
assert_eq!(old_atime, new_atime);
assert_eq!(old_mtime, new_mtime);
}
// The conversion is not useless on all platforms.

@@ -10,3 +492,2 @@ #[allow(clippy::useless_conversion)]

};
use std::os::unix::io::AsRawFd;
use tempfile::NamedTempFile;

@@ -16,5 +497,4 @@

let initial = FileFlag::from_bits_truncate(
fstat(f.as_raw_fd()).unwrap().st_flags.into(),
);
let initial =
FileFlag::from_bits_truncate(fstat(&f).unwrap().st_flags.into());
// UF_OFFLINE is preserved by all FreeBSD file systems, but not interpreted

@@ -26,7 +506,6 @@ // in any way, so it's handy for testing.

let changed = FileFlag::from_bits_truncate(
fstat(f.as_raw_fd()).unwrap().st_flags.into(),
);
let changed =
FileFlag::from_bits_truncate(fstat(&f).unwrap().st_flags.into());
assert_eq!(commanded, changed);
}

@@ -1,2 +0,2 @@

use std::os::unix::io::{AsFd, AsRawFd};
use std::os::unix::io::AsFd;
use tempfile::tempfile;

@@ -83,2 +83,3 @@

#[test]
#[cfg(not(target_os = "solaris"))]
fn test_local_flags() {

@@ -104,6 +105,6 @@ // openpty uses ptname(3) internally

// Set the master is in nonblocking mode or reading will never return.
let flags = fcntl::fcntl(pty.master.as_raw_fd(), fcntl::F_GETFL).unwrap();
let flags = fcntl::fcntl(&pty.master, fcntl::F_GETFL).unwrap();
let new_flags =
fcntl::OFlag::from_bits_truncate(flags) | fcntl::OFlag::O_NONBLOCK;
fcntl::fcntl(pty.master.as_raw_fd(), fcntl::F_SETFL(new_flags)).unwrap();
fcntl::fcntl(pty.master.as_fd(), fcntl::F_SETFL(new_flags)).unwrap();

@@ -116,4 +117,4 @@ // Write into the master

let mut buf = [0u8; 10];
let read = read(pty.master.as_raw_fd(), &mut buf).unwrap_err();
let read = read(&pty.master, &mut buf).unwrap_err();
assert_eq!(read, Errno::EAGAIN);
}
use nix::sys::uio::*;
use nix::unistd::*;
use rand::distributions::Alphanumeric;
use rand::{thread_rng, Rng};
use rand::distr::Alphanumeric;
use rand::{rng, Rng};
use std::fs::OpenOptions;
use std::io::IoSlice;
use std::os::unix::io::AsRawFd;
use std::{cmp, iter};

@@ -18,6 +17,8 @@

#[test]
// On Solaris sometimes wrtitev() returns EINVAL.
#[cfg(not(target_os = "solaris"))]
fn test_writev() {
let mut to_write = Vec::with_capacity(16 * 128);
for _ in 0..16 {
let s: String = thread_rng()
let s: String = rng()
.sample_iter(&Alphanumeric)

@@ -38,3 +39,3 @@ .map(char::from)

} else {
thread_rng().gen_range(64..cmp::min(256, left))
rng().random_range(64..cmp::min(256, left))
};

@@ -54,3 +55,3 @@ let b = &to_write[consumed..consumed + slice_len];

assert_eq!(to_write.len(), written);
let read_res = read(reader.as_raw_fd(), &mut read_buf[..]);
let read_res = read(&reader, &mut read_buf[..]);
let read = read_res.expect("couldn't read");

@@ -66,3 +67,3 @@ // Check we have read as much as we written

fn test_readv() {
let s: String = thread_rng()
let s: String = rng()
.sample_iter(&Alphanumeric)

@@ -80,3 +81,3 @@ .map(char::from)

} else {
thread_rng().gen_range(64..cmp::min(256, left))
rng().random_range(64..cmp::min(256, left))
};

@@ -151,3 +152,4 @@ let v: Vec<u8> = iter::repeat(0u8).take(vec_len).collect();

target_os = "haiku",
target_os = "solaris"
target_os = "solaris",
target_os = "cygwin"
)))]

@@ -191,3 +193,4 @@ fn test_pwritev() {

target_os = "haiku",
target_os = "solaris"
target_os = "solaris",
target_os = "cygwin"
)))]

@@ -238,3 +241,2 @@ fn test_preadv() {

use nix::unistd::ForkResult::*;
use std::os::unix::io::AsRawFd;

@@ -253,3 +255,3 @@ require_capability!("test_process_vm_readv", CAP_SYS_PTRACE);

// wait for child
read(r.as_raw_fd(), &mut [0u8]).unwrap();
read(&r, &mut [0u8]).unwrap();
drop(r);

@@ -256,0 +258,0 @@

@@ -19,3 +19,2 @@ use nix::dir::{Dir, Type};

#[test]
#[allow(clippy::unnecessary_sort_by)] // False positive
fn read() {

@@ -61,7 +60,1 @@ let tmp = tempdir().unwrap();

}
#[cfg(not(target_os = "haiku"))]
#[test]
fn ebadf() {
assert_eq!(Dir::from_fd(-1).unwrap_err(), nix::Error::EBADF);
}

@@ -24,3 +24,3 @@ #[cfg(not(target_os = "redox"))]

#[cfg(not(target_os = "redox"))]
use nix::unistd::{close, read};
use nix::unistd::read;
#[cfg(not(target_os = "redox"))]

@@ -49,3 +49,3 @@ use std::fs::File;

let fd = openat(
Some(dirfd),
dirfd,
tmp.path().file_name().unwrap(),

@@ -58,7 +58,4 @@ OFlag::O_RDONLY,

let mut buf = [0u8; 1024];
assert_eq!(4, read(fd, &mut buf).unwrap());
assert_eq!(4, read(&fd, &mut buf).unwrap());
assert_eq!(CONTENTS, &buf[0..4]);
close(fd).unwrap();
close(dirfd).unwrap();
}

@@ -91,7 +88,4 @@

let mut buf = [0u8; 1024];
assert_eq!(4, read(fd, &mut buf).unwrap());
assert_eq!(4, read(&fd, &mut buf).unwrap());
assert_eq!(CONTENTS, &buf[0..4]);
close(fd).unwrap();
close(dirfd).unwrap();
}

@@ -122,3 +116,3 @@

);
assert_eq!(Err(Errno::EXDEV), res);
assert_eq!(res.unwrap_err(), Errno::EXDEV);
}

@@ -137,9 +131,7 @@

open(new_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
renameat(Some(old_dirfd), "old", Some(new_dirfd), "new").unwrap();
renameat(&old_dirfd, "old", &new_dirfd, "new").unwrap();
assert_eq!(
renameat(Some(old_dirfd), "old", Some(new_dirfd), "new").unwrap_err(),
renameat(&old_dirfd, "old", &new_dirfd, "new").unwrap_err(),
Errno::ENOENT
);
close(old_dirfd).unwrap();
close(new_dirfd).unwrap();
assert!(new_dir.path().join("new").exists());

@@ -167,23 +159,9 @@ }

open(new_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
renameat2(
Some(old_dirfd),
"old",
Some(new_dirfd),
"new",
RenameFlags::empty(),
)
.unwrap();
renameat2(&old_dirfd, "old", &new_dirfd, "new", RenameFlags::empty())
.unwrap();
assert_eq!(
renameat2(
Some(old_dirfd),
"old",
Some(new_dirfd),
"new",
RenameFlags::empty()
)
.unwrap_err(),
renameat2(&old_dirfd, "old", &new_dirfd, "new", RenameFlags::empty())
.unwrap_err(),
Errno::ENOENT
);
close(old_dirfd).unwrap();
close(new_dirfd).unwrap();
assert!(new_dir.path().join("new").exists());

@@ -220,5 +198,5 @@ }

renameat2(
Some(old_dirfd),
&old_dirfd,
"old",
Some(new_dirfd),
&new_dirfd,
"new",

@@ -236,4 +214,2 @@ RenameFlags::RENAME_EXCHANGE,

assert_eq!(buf, "new");
close(old_dirfd).unwrap();
close(new_dirfd).unwrap();
}

@@ -264,5 +240,5 @@

renameat2(
Some(old_dirfd),
&old_dirfd,
"old",
Some(new_dirfd),
&new_dirfd,
"new",

@@ -274,4 +250,2 @@ RenameFlags::RENAME_NOREPLACE

);
close(old_dirfd).unwrap();
close(new_dirfd).unwrap();
assert!(new_dir.path().join("new").exists());

@@ -294,3 +268,3 @@ assert!(old_dir.path().join("old").exists());

assert_eq!(
readlinkat(Some(dirfd), "b").unwrap().to_str().unwrap(),
readlinkat(dirfd, "b").unwrap().to_str().unwrap(),
expected_dir

@@ -316,3 +290,2 @@ );

use nix::fcntl::copy_file_range;
use std::os::unix::io::AsFd;

@@ -328,10 +301,3 @@ const CONTENTS: &[u8] = b"foobarbaz";

let mut from_offset: i64 = 3;
copy_file_range(
tmp1.as_fd(),
Some(&mut from_offset),
tmp2.as_fd(),
None,
3,
)
.unwrap();
copy_file_range(&tmp1, Some(&mut from_offset), &tmp2, None, 3).unwrap();

@@ -351,3 +317,2 @@ let mut res: String = String::new();

use std::io::IoSlice;
use std::os::unix::prelude::*;

@@ -378,3 +343,3 @@ use nix::fcntl::*;

let mut buf = [0u8; 1024];
assert_eq!(2, read(rd.as_raw_fd(), &mut buf).unwrap());
assert_eq!(2, read(&rd, &mut buf).unwrap());
assert_eq!(b"f1", &buf[0..2]);

@@ -398,7 +363,7 @@ assert_eq!(7, offset);

// Check the tee'd bytes are at rd2.
assert_eq!(2, read(rd2.as_raw_fd(), &mut buf).unwrap());
assert_eq!(2, read(&rd2, &mut buf).unwrap());
assert_eq!(b"ab", &buf[0..2]);
// Check all the bytes are still at rd1.
assert_eq!(3, read(rd1.as_raw_fd(), &mut buf).unwrap());
assert_eq!(3, read(&rd1, &mut buf).unwrap());
assert_eq!(b"abc", &buf[0..3]);

@@ -421,3 +386,3 @@ }

let mut buf = [0u8; 32];
assert_eq!(6, read(rd.as_raw_fd(), &mut buf).unwrap());
assert_eq!(6, read(&rd, &mut buf).unwrap());
assert_eq!(b"abcdef", &buf[0..6]);

@@ -431,8 +396,7 @@ }

let fd = tmp.as_raw_fd();
fallocate(fd, FallocateFlags::empty(), 0, 100).unwrap();
fallocate(&tmp, FallocateFlags::empty(), 0, 100).unwrap();
// Check if we read exactly 100 bytes
let mut buf = [0u8; 200];
assert_eq!(100, read(fd, &mut buf).unwrap());
assert_eq!(100, read(&tmp, &mut buf).unwrap());
}

@@ -454,3 +418,2 @@

let fd = tmp.as_raw_fd();
let statfs = nix::sys::statfs::fstatfs(tmp.as_file()).unwrap();

@@ -463,3 +426,3 @@ if statfs.filesystem_type() == nix::sys::statfs::OVERLAYFS_SUPER_MAGIC {

}
let inode = fstat(fd).expect("fstat failed").st_ino as usize;
let inode = fstat(&tmp).expect("fstat failed").st_ino as usize;

@@ -474,3 +437,3 @@ let mut flock: libc::flock = unsafe {

flock.l_pid = 0;
fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).expect("write lock failed");
fcntl(&tmp, FcntlArg::F_OFD_SETLKW(&flock)).expect("write lock failed");
assert_eq!(

@@ -482,3 +445,4 @@ Some(("OFDLCK".to_string(), "WRITE".to_string())),

flock.l_type = libc::F_UNLCK as libc::c_short;
fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).expect("write unlock failed");
fcntl(&tmp, FcntlArg::F_OFD_SETLKW(&flock))
.expect("write unlock failed");
assert_eq!(None, lock_info(inode));

@@ -496,3 +460,2 @@ }

let fd = tmp.as_raw_fd();
let statfs = nix::sys::statfs::fstatfs(tmp.as_file()).unwrap();

@@ -505,3 +468,3 @@ if statfs.filesystem_type() == nix::sys::statfs::OVERLAYFS_SUPER_MAGIC {

}
let inode = fstat(fd).expect("fstat failed").st_ino as usize;
let inode = fstat(&tmp).expect("fstat failed").st_ino as usize;

@@ -516,3 +479,3 @@ let mut flock: libc::flock = unsafe {

flock.l_pid = 0;
fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).expect("read lock failed");
fcntl(&tmp, FcntlArg::F_OFD_SETLKW(&flock)).expect("read lock failed");
assert_eq!(

@@ -524,3 +487,4 @@ Some(("OFDLCK".to_string(), "READ".to_string())),

flock.l_type = libc::F_UNLCK as libc::c_short;
fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).expect("read unlock failed");
fcntl(&tmp, FcntlArg::F_OFD_SETLKW(&flock))
.expect("read unlock failed");
assert_eq!(None, lock_info(inode));

@@ -560,7 +524,5 @@ }

mod test_posix_fadvise {
use nix::errno::Errno;
use nix::fcntl::*;
use nix::unistd::pipe;
use std::os::unix::io::AsRawFd;
use tempfile::NamedTempFile;

@@ -571,4 +533,3 @@

let tmp = NamedTempFile::new().unwrap();
let fd = tmp.as_raw_fd();
posix_fadvise(fd, 0, 100, PosixFadviseAdvice::POSIX_FADV_WILLNEED)
posix_fadvise(&tmp, 0, 100, PosixFadviseAdvice::POSIX_FADV_WILLNEED)
.expect("posix_fadvise failed");

@@ -580,8 +541,4 @@ }

let (rd, _wr) = pipe().unwrap();
let res = posix_fadvise(
rd.as_raw_fd(),
0,
100,
PosixFadviseAdvice::POSIX_FADV_WILLNEED,
);
let res =
posix_fadvise(&rd, 0, 100, PosixFadviseAdvice::POSIX_FADV_WILLNEED);
assert_eq!(res, Err(Errno::ESPIPE));

@@ -603,3 +560,3 @@ }

use nix::unistd::pipe;
use std::{io::Read, os::unix::io::AsRawFd};
use std::io::Read;
use tempfile::NamedTempFile;

@@ -611,4 +568,3 @@

let mut tmp = NamedTempFile::new().unwrap();
let fd = tmp.as_raw_fd();
let res = posix_fallocate(fd, 0, LEN as libc::off_t);
let res = posix_fallocate(&tmp, 0, LEN as libc::off_t);
match res {

@@ -635,3 +591,3 @@ Ok(_) => {

let (rd, _wr) = pipe().unwrap();
let err = posix_fallocate(rd.as_raw_fd(), 0, 100).unwrap_err();
let err = posix_fallocate(&rd, 0, 100).unwrap_err();
match err {

@@ -648,9 +604,8 @@ Errno::EINVAL | Errno::ENODEV | Errno::ESPIPE | Errno::EBADF => (),

use nix::fcntl::*;
use std::{os::unix::io::AsRawFd, path::PathBuf};
use std::path::PathBuf;
let tmp = NamedTempFile::new().unwrap();
let fd = tmp.as_raw_fd();
let mut path = PathBuf::new();
let res =
fcntl(fd, FcntlArg::F_GETPATH(&mut path)).expect("get path failed");
fcntl(&tmp, FcntlArg::F_GETPATH(&mut path)).expect("get path failed");
assert_ne!(res, -1);

@@ -665,10 +620,27 @@ assert_eq!(

#[test]
fn test_f_preallocate() {
use nix::fcntl::*;
let tmp = NamedTempFile::new().unwrap();
let mut st: libc::fstore_t = unsafe { std::mem::zeroed() };
st.fst_flags = libc::F_ALLOCATECONTIG as libc::c_uint;
st.fst_posmode = libc::F_PEOFPOSMODE;
st.fst_length = 1024;
let res = fcntl(tmp, FcntlArg::F_PREALLOCATE(&mut st))
.expect("preallocation failed");
assert_eq!(res, 0);
assert!(st.fst_bytesalloc > 0);
}
#[cfg(apple_targets)]
#[test]
fn test_f_get_path_nofirmlink() {
use nix::fcntl::*;
use std::{os::unix::io::AsRawFd, path::PathBuf};
use std::path::PathBuf;
let tmp = NamedTempFile::new().unwrap();
let fd = tmp.as_raw_fd();
let mut path = PathBuf::new();
let res = fcntl(fd, FcntlArg::F_GETPATH_NOFIRMLINK(&mut path))
let res = fcntl(&tmp, FcntlArg::F_GETPATH_NOFIRMLINK(&mut path))
.expect("get path failed");

@@ -700,3 +672,3 @@ let mut tmpstr = String::from("/System/Volumes/Data");

use nix::fcntl::*;
use std::{os::unix::io::AsRawFd, path::PathBuf};
use std::path::PathBuf;

@@ -709,5 +681,5 @@ let tmp = NamedTempFile::new().unwrap();

let tmp2 = File::open(tmp.path()).unwrap();
let fd = tmp2.as_raw_fd();
let mut path = PathBuf::new();
let res = fcntl(fd, FcntlArg::F_KINFO(&mut path)).expect("get path failed");
let res =
fcntl(&tmp2, FcntlArg::F_KINFO(&mut path)).expect("get path failed");
assert_ne!(res, -1);

@@ -818,1 +790,69 @@ assert_eq!(path, tmp.path());

}
#[cfg(apple_targets)]
#[test]
fn test_f_rdadvise() {
use nix::fcntl::*;
let contents = vec![1; 1024];
let mut buf = [0; 1024];
let mut tmp = NamedTempFile::new().unwrap();
tmp.write_all(&contents).unwrap();
let fd = open(tmp.path(), OFlag::empty(), Mode::empty()).unwrap();
let rad = libc::radvisory {
ra_offset: 0,
ra_count: contents.len() as _,
};
let res = fcntl(&tmp, FcntlArg::F_RDADVISE(rad)).expect("rdadivse failed");
assert_ne!(res, -1);
assert_eq!(contents.len(), read(&fd, &mut buf).unwrap());
assert_eq!(contents, &buf[0..contents.len()]);
}
#[cfg(apple_targets)]
#[test]
fn test_f_log2phys() {
use nix::fcntl::*;
const CONTENTS: &[u8] = b"abcd";
let mut tmp = NamedTempFile::new().unwrap();
tmp.write_all(CONTENTS).unwrap();
let mut offset: libc::off_t = 0;
let mut res = fcntl(&tmp, FcntlArg::F_LOG2PHYS(&mut offset))
.expect("log2phys failed");
assert_ne!(res, -1);
assert_ne!(offset, 0);
let mut info: libc::log2phys = unsafe { std::mem::zeroed() };
info.l2p_contigbytes = CONTENTS.len() as _;
info.l2p_devoffset = 3;
res = fcntl(&tmp, FcntlArg::F_LOG2PHYS_EXT(&mut info))
.expect("log2phys failed");
assert_ne!(res, -1);
assert_ne!({ info.l2p_devoffset }, 3);
}
#[cfg(apple_targets)]
#[test]
fn test_f_transferextents() {
use nix::fcntl::*;
use std::os::fd::AsRawFd;
let tmp1 = NamedTempFile::new().unwrap();
let tmp2 = NamedTempFile::new().unwrap();
let res = fcntl(&tmp1, FcntlArg::F_TRANSFEREXTENTS(tmp2.as_raw_fd()))
.expect("transferextents failed");
assert_ne!(res, -1);
}
#[cfg(target_os = "freebsd")]
#[test]
fn test_f_readahead() {
use nix::fcntl::*;
let tmp = NamedTempFile::new().unwrap();
let mut res = fcntl(&tmp, FcntlArg::F_READAHEAD(1_000_000))
.expect("read ahead failed");
assert_ne!(res, -1);
res = fcntl(&tmp, FcntlArg::F_READAHEAD(-1024)).expect("read ahead failed");
assert_ne!(res, -1);
}

@@ -13,2 +13,3 @@ use nix::net::if_::*;

#[test]
#[cfg_attr(target_os = "cygwin", ignore)]
fn test_if_nametoindex() {

@@ -19,2 +20,3 @@ if_nametoindex(LOOPBACK).expect("assertion failed");

#[test]
#[cfg_attr(target_os = "cygwin", ignore)]
fn test_if_indextoname() {

@@ -21,0 +23,0 @@ let loopback_index = if_nametoindex(LOOPBACK).expect("assertion failed");

@@ -111,10 +111,13 @@ use std::fs::File;

let ldterm = b"ldterm\0";
let r = unsafe { ioctl(slave_fd, I_FIND, ldterm.as_ptr()) };
let r = unsafe { ioctl(slave_fd.as_raw_fd(), I_FIND, ldterm.as_ptr()) };
if r < 0 {
panic!("I_FIND failure");
} else if r == 0 {
if unsafe { ioctl(slave_fd, I_PUSH, ptem.as_ptr()) } < 0 {
if unsafe { ioctl(slave_fd.as_raw_fd(), I_PUSH, ptem.as_ptr()) } < 0
{
panic!("I_PUSH ptem failure");
}
if unsafe { ioctl(slave_fd, I_PUSH, ldterm.as_ptr()) } < 0 {
if unsafe { ioctl(slave_fd.as_raw_fd(), I_PUSH, ldterm.as_ptr()) }
< 0
{
panic!("I_PUSH ldterm failure");

@@ -125,3 +128,3 @@ }

let slave = unsafe { File::from_raw_fd(slave_fd) };
let slave = File::from(slave_fd);

@@ -150,2 +153,3 @@ (master, slave)

#[test]
#[cfg(not(target_os = "solaris"))]
fn test_read_ptty_pair() {

@@ -152,0 +156,0 @@ let (mut master, mut slave) = open_ptty_pair();

@@ -10,6 +10,8 @@ use std::io::prelude::*;

use nix::unistd::{pipe, read};
use std::os::unix::io::AsRawFd;
} else if #[cfg(any(freebsdlike, apple_targets, solarish))] {
} else if #[cfg(any(freebsdlike, apple_targets))] {
use std::net::Shutdown;
use std::os::unix::net::UnixStream;
} else if #[cfg(solarish)] {
use std::net::Shutdown;
use std::net::{TcpListener, TcpStream};
}

@@ -32,3 +34,3 @@ }

let mut buf = [0u8; 1024];
assert_eq!(2, read(rd.as_raw_fd(), &mut buf).unwrap());
assert_eq!(2, read(&rd, &mut buf).unwrap());
assert_eq!(b"f1", &buf[0..2]);

@@ -52,3 +54,3 @@ assert_eq!(7, offset);

let mut buf = [0u8; 1024];
assert_eq!(2, read(rd.as_raw_fd(), &mut buf).unwrap());
assert_eq!(2, read(&rd, &mut buf).unwrap());
assert_eq!(b"f1", &buf[0..2]);

@@ -101,3 +103,3 @@ assert_eq!(7, offset);

// Verify the message that was sent
assert_eq!(bytes_written as usize, expected_string.as_bytes().len());
assert_eq!(bytes_written as usize, expected_string.len());

@@ -151,3 +153,3 @@ let mut read_string = String::new();

// Verify the message that was sent
assert_eq!(bytes_written as usize, expected_string.as_bytes().len());
assert_eq!(bytes_written as usize, expected_string.len());

@@ -231,3 +233,7 @@ let mut read_string = String::new();

.unwrap();
let (mut rd, wr) = UnixStream::pair().unwrap();
// Create a TCP socket pair (listener and client)
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
let addr = listener.local_addr().unwrap();
let mut rd = TcpStream::connect(addr).unwrap();
let (wr, _) = listener.accept().unwrap();
let vec: &[SendfileVec] = &[

@@ -253,3 +259,3 @@ SendfileVec::new(

assert!(res.is_ok());
wr.shutdown(Shutdown::Both).unwrap();
wr.shutdown(Shutdown::Write).unwrap();

@@ -256,0 +262,0 @@ // Prepare the expected result

@@ -29,3 +29,2 @@ use libc::{_exit, mode_t, off_t};

use std::io::Write;
use std::os::unix::prelude::*;
#[cfg(not(any(

@@ -125,4 +124,3 @@ target_os = "fuchsia",

match result {
Ok((fd, path)) => {
close(fd).unwrap();
Ok((_, path)) => {
unlink(path.as_path()).unwrap();

@@ -168,2 +166,4 @@ }

fn test_mkfifoat_none() {
use nix::fcntl::AT_FDCWD;
let _m = crate::CWD_LOCK.read();

@@ -174,3 +174,3 @@

mkfifoat(None, &mkfifoat_fifo, Mode::S_IRUSR).unwrap();
mkfifoat(AT_FDCWD, &mkfifoat_fifo, Mode::S_IRUSR).unwrap();

@@ -196,7 +196,6 @@ let stats = stat::stat(&mkfifoat_fifo).unwrap();

mkfifoat(Some(dirfd), mkfifoat_name, Mode::S_IRUSR).unwrap();
mkfifoat(&dirfd, mkfifoat_name, Mode::S_IRUSR).unwrap();
let stats =
stat::fstatat(Some(dirfd), mkfifoat_name, fcntl::AtFlags::empty())
.unwrap();
stat::fstatat(&dirfd, mkfifoat_name, fcntl::AtFlags::empty()).unwrap();
let typ = stat::SFlag::from_bits_truncate(stats.st_mode);

@@ -214,6 +213,8 @@ assert_eq!(typ, SFlag::S_IFIFO);

fn test_mkfifoat_directory_none() {
use nix::fcntl::AT_FDCWD;
let _m = crate::CWD_LOCK.read();
// mkfifoat should fail if a directory is given
mkfifoat(None, &env::temp_dir(), Mode::S_IRUSR)
mkfifoat(AT_FDCWD, &env::temp_dir(), Mode::S_IRUSR)
.expect_err("assertion failed");

@@ -234,5 +235,5 @@ }

let mkfifoat_dir = "mkfifoat_dir";
stat::mkdirat(Some(dirfd), mkfifoat_dir, Mode::S_IRUSR).unwrap();
stat::mkdirat(&dirfd, mkfifoat_dir, Mode::S_IRUSR).unwrap();
mkfifoat(Some(dirfd), mkfifoat_dir, Mode::S_IRUSR)
mkfifoat(&dirfd, mkfifoat_dir, Mode::S_IRUSR)
.expect_err("assertion failed");

@@ -395,3 +396,3 @@ }

// Make `writer` be the stdout of the new process.
dup2(writer.as_raw_fd(), 1).unwrap();
nix::unistd::dup2_stdout(&writer).unwrap();
let r = syscall();

@@ -410,3 +411,3 @@ let _ = std::io::stderr()

let mut buf = [0u8; 1024];
read(reader.as_raw_fd(), &mut buf).unwrap();
read(&reader, &mut buf).unwrap();
// It should contain the things we printed using `/bin/sh`.

@@ -444,3 +445,3 @@ let string = String::from_utf8_lossy(&buf);

execve_test_factory!(test_execve, execve, CString::new("/system/bin/sh").unwrap().as_c_str());
execve_test_factory!(test_fexecve, fexecve, File::open("/system/bin/sh").unwrap().into_raw_fd());
execve_test_factory!(test_fexecve, fexecve, &File::open("/system/bin/sh").unwrap());
} else if #[cfg(any(freebsdlike, target_os = "linux", target_os = "hurd"))] {

@@ -450,3 +451,3 @@ // These tests frequently fail on musl, probably due to

execve_test_factory!(test_execve, execve, CString::new("/bin/sh").unwrap().as_c_str());
execve_test_factory!(test_fexecve, fexecve, File::open("/bin/sh").unwrap().into_raw_fd());
execve_test_factory!(test_fexecve, fexecve, &File::open("/bin/sh").unwrap());
} else if #[cfg(any(solarish, apple_targets, netbsdlike))] {

@@ -470,17 +471,17 @@ execve_test_factory!(test_execve, execve, CString::new("/bin/sh").unwrap().as_c_str());

execve_test_factory!(test_execveat_empty, execveat,
Some(File::open("/system/bin/sh").unwrap().into_raw_fd()),
&File::open("/system/bin/sh").unwrap(),
"", AtFlags::AT_EMPTY_PATH);
execve_test_factory!(test_execveat_relative, execveat,
Some(File::open("/system/bin/").unwrap().into_raw_fd()),
&File::open("/system/bin/").unwrap(),
"./sh", AtFlags::empty());
execve_test_factory!(test_execveat_absolute, execveat,
Some(File::open("/").unwrap().into_raw_fd()),
&File::open("/").unwrap(),
"/system/bin/sh", AtFlags::empty());
} else if #[cfg(all(target_os = "linux", any(target_arch ="x86_64", target_arch ="x86")))] {
use nix::fcntl::AtFlags;
execve_test_factory!(test_execveat_empty, execveat, Some(File::open("/bin/sh").unwrap().into_raw_fd()),
execve_test_factory!(test_execveat_empty, execveat, &File::open("/bin/sh").unwrap(),
"", AtFlags::AT_EMPTY_PATH);
execve_test_factory!(test_execveat_relative, execveat, Some(File::open("/bin/").unwrap().into_raw_fd()),
execve_test_factory!(test_execveat_relative, execveat, &File::open("/bin/").unwrap(),
"./sh", AtFlags::empty());
execve_test_factory!(test_execveat_absolute, execveat, Some(File::open("/").unwrap().into_raw_fd()),
execve_test_factory!(test_execveat_absolute, execveat, &File::open("/").unwrap(),
"/bin/sh", AtFlags::empty());

@@ -498,8 +499,6 @@ }

let tmpdir_path = tmpdir.path().canonicalize().unwrap();
let tmpdir_fd = File::open(&tmpdir_path).unwrap().into_raw_fd();
let tmpdir_fd = File::open(&tmpdir_path).unwrap();
fchdir(tmpdir_fd).expect("assertion failed");
fchdir(&tmpdir_fd).expect("assertion failed");
assert_eq!(getcwd().unwrap(), tmpdir_path);
close(tmpdir_fd).expect("assertion failed");
}

@@ -558,9 +557,7 @@

let path = tempfile().unwrap();
let fd = path.as_raw_fd();
let file = tempfile().unwrap();
fchown(fd, uid, gid).unwrap();
fchown(fd, uid, None).unwrap();
fchown(fd, None, gid).unwrap();
fchown(999999999, uid, gid).unwrap_err();
fchown(&file, uid, gid).unwrap();
fchown(&file, uid, None).unwrap();
fchown(&file, None, gid).unwrap();
}

@@ -572,2 +569,3 @@

use nix::fcntl::AtFlags;
use nix::fcntl::AT_FDCWD;

@@ -587,9 +585,9 @@ let _dr = crate::DirRestore::new();

fchownat(Some(dirfd), "file", uid, gid, AtFlags::empty()).unwrap();
fchownat(&dirfd, "file", uid, gid, AtFlags::empty()).unwrap();
chdir(tempdir.path()).unwrap();
fchownat(None, "file", uid, gid, AtFlags::empty()).unwrap();
fchownat(AT_FDCWD, "file", uid, gid, AtFlags::empty()).unwrap();
fs::remove_file(&path).unwrap();
fchownat(None, "file", uid, gid, AtFlags::empty()).unwrap_err();
fchownat(AT_FDCWD, "file", uid, gid, AtFlags::empty()).unwrap_err();
}

@@ -604,3 +602,3 @@

let offset: off_t = 5;
lseek(tmp.as_raw_fd(), offset, Whence::SeekSet).unwrap();
lseek(&tmp, offset, Whence::SeekSet).unwrap();

@@ -619,3 +617,3 @@ let mut buf = [0u8; 7];

lseek64(tmp.as_raw_fd(), 5, Whence::SeekSet).unwrap();
lseek64(&tmp, 5, Whence::SeekSet).unwrap();

@@ -654,3 +652,4 @@ let mut buf = [0u8; 7];

target_os = "fuchsia",
target_os = "haiku"
target_os = "haiku",
target_os = "cygwin"
)))]

@@ -755,3 +754,3 @@ fn test_acct() {

let m0 = stat::SFlag::from_bits_truncate(
stat::fstat(fd0.as_raw_fd()).unwrap().st_mode as mode_t,
stat::fstat(&fd0).unwrap().st_mode as mode_t,
);

@@ -761,3 +760,3 @@ // S_IFIFO means it's a pipe

let m1 = stat::SFlag::from_bits_truncate(
stat::fstat(fd1.as_raw_fd()).unwrap().st_mode as mode_t,
stat::fstat(&fd1).unwrap().st_mode as mode_t,
);

@@ -782,9 +781,7 @@ assert_eq!(m1, SFlag::S_IFIFO);

let (fd0, fd1) = pipe2(OFlag::O_CLOEXEC).unwrap();
let f0 = FdFlag::from_bits_truncate(
fcntl(fd0.as_raw_fd(), FcntlArg::F_GETFD).unwrap(),
);
let f0 =
FdFlag::from_bits_truncate(fcntl(&fd0, FcntlArg::F_GETFD).unwrap());
assert!(f0.contains(FdFlag::FD_CLOEXEC));
let f1 = FdFlag::from_bits_truncate(
fcntl(fd1.as_raw_fd(), FcntlArg::F_GETFD).unwrap(),
);
let f1 =
FdFlag::from_bits_truncate(fcntl(&fd1, FcntlArg::F_GETFD).unwrap());
assert!(f1.contains(FdFlag::FD_CLOEXEC));

@@ -897,2 +894,4 @@ }

fn test_symlinkat() {
use nix::fcntl::AT_FDCWD;
let _m = crate::CWD_LOCK.read();

@@ -904,3 +903,3 @@

let linkpath = tempdir.path().join("b");
symlinkat(&target, None, &linkpath).unwrap();
symlinkat(&target, AT_FDCWD, &linkpath).unwrap();
assert_eq!(

@@ -914,3 +913,3 @@ readlink(&linkpath).unwrap().to_str().unwrap(),

let linkpath = "d";
symlinkat(target, Some(dirfd), linkpath).unwrap();
symlinkat(target, &dirfd, linkpath).unwrap();
assert_eq!(

@@ -947,5 +946,5 @@ readlink(&tempdir.path().join(linkpath))

linkat(
Some(dirfd),
&dirfd,
oldfilename,
Some(dirfd),
&dirfd,
newfilename,

@@ -960,4 +959,39 @@ AtFlags::AT_SYMLINK_FOLLOW,

#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
/// This test is the same as [test_linkat_file], but ensures that two different types can be used
/// as the path arguments.
fn test_linkat_pathtypes() {
use nix::fcntl::AtFlags;
let tempdir = tempdir().unwrap();
let oldfilename = "foo.txt";
let oldfilepath = tempdir.path().join(oldfilename);
let newfilename = "bar.txt";
let newfilepath = tempdir.path().join(newfilename);
// Create file
File::create(oldfilepath).unwrap();
// Get file descriptor for base directory
let dirfd =
fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
.unwrap();
// Attempt hard link file at relative path
linkat(
&dirfd,
PathBuf::from(oldfilename).as_path(),
&dirfd,
newfilename,
AtFlags::AT_SYMLINK_FOLLOW,
)
.unwrap();
assert!(newfilepath.exists());
}
#[test]
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
fn test_linkat_olddirfd_none() {
use nix::fcntl::AtFlags;
use nix::fcntl::AT_FDCWD;

@@ -988,5 +1022,5 @@ let _dr = crate::DirRestore::new();

linkat(
None,
AT_FDCWD,
oldfilename,
Some(dirfd),
&dirfd,
newfilename,

@@ -1003,2 +1037,3 @@ AtFlags::AT_SYMLINK_FOLLOW,

use nix::fcntl::AtFlags;
use nix::fcntl::AT_FDCWD;

@@ -1029,5 +1064,5 @@ let _dr = crate::DirRestore::new();

linkat(
Some(dirfd),
&dirfd,
oldfilename,
None,
AT_FDCWD,
newfilename,

@@ -1044,2 +1079,3 @@ AtFlags::AT_SYMLINK_FOLLOW,

use nix::fcntl::AtFlags;
use nix::fcntl::AT_FDCWD;

@@ -1062,3 +1098,3 @@ let _m = crate::CWD_LOCK.read();

// Create symlink to file
symlinkat(&oldfilepath, None, &symoldfilepath).unwrap();
symlinkat(&oldfilepath, AT_FDCWD, &symoldfilepath).unwrap();

@@ -1072,5 +1108,5 @@ // Get file descriptor for base directory

linkat(
Some(dirfd),
&dirfd,
symoldfilename,
Some(dirfd),
&dirfd,
newfilename,

@@ -1092,2 +1128,3 @@ AtFlags::empty(),

use nix::fcntl::AtFlags;
use nix::fcntl::AT_FDCWD;

@@ -1110,3 +1147,3 @@ let _m = crate::CWD_LOCK.read();

// Create symlink to file
symlinkat(&oldfilepath, None, &symoldfilepath).unwrap();
symlinkat(&oldfilepath, AT_FDCWD, &symoldfilepath).unwrap();

@@ -1120,5 +1157,5 @@ // Get file descriptor for base directory

linkat(
Some(dirfd),
&dirfd,
symoldfilename,
Some(dirfd),
&dirfd,
newfilename,

@@ -1159,3 +1196,3 @@ AtFlags::AT_SYMLINK_FOLLOW,

let err_result =
unlinkat(Some(dirfd), dirname, UnlinkatFlags::NoRemoveDir).unwrap_err();
unlinkat(&dirfd, dirname, UnlinkatFlags::NoRemoveDir).unwrap_err();
assert!(err_result == Errno::EISDIR || err_result == Errno::EPERM);

@@ -1180,3 +1217,3 @@ }

// Attempt unlink dir at relative path with proper flag
unlinkat(Some(dirfd), dirname, UnlinkatFlags::RemoveDir).unwrap();
unlinkat(&dirfd, dirname, UnlinkatFlags::RemoveDir).unwrap();
assert!(!dirpath.exists());

@@ -1201,3 +1238,3 @@ }

// Attempt unlink file at relative path
unlinkat(Some(dirfd), filename, UnlinkatFlags::NoRemoveDir).unwrap();
unlinkat(&dirfd, filename, UnlinkatFlags::NoRemoveDir).unwrap();
assert!(!filepath.exists());

@@ -1228,8 +1265,12 @@ }

fn test_user_into_passwd() {
// get the UID of the "nobody" user
#[cfg(not(target_os = "haiku"))]
let test_username = "nobody";
// "nobody" unavailable on haiku
#[cfg(target_os = "haiku")]
let test_username = "user";
let test_username = if cfg!(target_os = "haiku") {
// "nobody" unavailable on haiku
"user"
} else if cfg!(target_os = "cygwin") {
// the Windows admin user
"Administrator"
} else {
// get the UID of the "nobody" user
"nobody"
};

@@ -1286,2 +1327,4 @@ let nobody = User::from_name(test_username).unwrap().unwrap();

fn test_ttyname() {
use std::os::fd::AsRawFd;
let fd = posix_openpt(OFlag::O_RDWR).expect("posix_openpt failed");

@@ -1335,6 +1378,8 @@ assert!(fd.as_raw_fd() > 0);

use nix::fcntl::AtFlags;
use nix::fcntl::AT_FDCWD;
let tempdir = tempfile::tempdir().unwrap();
let dir = tempdir.path().join("does_not_exist.txt");
assert_eq!(
faccessat(None, &dir, AccessFlags::F_OK, AtFlags::empty())
faccessat(AT_FDCWD, &dir, AccessFlags::F_OK, AtFlags::empty())
.err()

@@ -1350,2 +1395,3 @@ .unwrap(),

use nix::fcntl::AtFlags;
let tempdir = tempfile::tempdir().unwrap();

@@ -1355,10 +1401,5 @@ let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap();

assert_eq!(
faccessat(
Some(dirfd),
not_exist_file,
AccessFlags::F_OK,
AtFlags::empty(),
)
.err()
.unwrap(),
faccessat(&dirfd, not_exist_file, AccessFlags::F_OK, AtFlags::empty(),)
.err()
.unwrap(),
Errno::ENOENT

@@ -1372,2 +1413,4 @@ );

use nix::fcntl::AtFlags;
use nix::fcntl::AT_FDCWD;
let tempdir = tempfile::tempdir().unwrap();

@@ -1377,3 +1420,3 @@ let path = tempdir.path().join("does_exist.txt");

assert!(faccessat(
None,
AT_FDCWD,
&path,

@@ -1390,2 +1433,3 @@ AccessFlags::R_OK | AccessFlags::W_OK,

use nix::fcntl::AtFlags;
let tempdir = tempfile::tempdir().unwrap();

@@ -1397,3 +1441,3 @@ let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap();

assert!(faccessat(
Some(dirfd),
&dirfd,
&path,

@@ -1400,0 +1444,0 @@ AccessFlags::R_OK | AccessFlags::W_OK,

#[macro_use]
extern crate cfg_if;
#[cfg_attr(not(any(target_os = "redox", target_os = "haiku")), macro_use)]
#[cfg_attr(not(any(target_os = "redox")), macro_use)]
extern crate nix;

@@ -16,3 +16,7 @@

mod test_kmod;
#[cfg(any(freebsdlike, target_os = "linux", target_os = "netbsd"))]
#[cfg(any(
freebsdlike,
all(target_os = "linux", not(target_env = "ohos")),
target_os = "netbsd"
))]
mod test_mq;

@@ -37,3 +41,13 @@ #[cfg(not(target_os = "redox"))]

mod test_sendfile;
mod test_stat;
#[cfg(any(
target_os = "freebsd",
target_os = "haiku",
target_os = "linux",
target_os = "netbsd",
apple_targets
))]
mod test_spawn;
mod test_syslog;
mod test_time;

@@ -44,3 +58,3 @@ mod test_unistd;

use parking_lot::{Mutex, RwLock, RwLockWriteGuard};
use std::os::unix::io::{AsFd, AsRawFd};
use std::os::unix::io::AsFd;
use std::path::PathBuf;

@@ -54,9 +68,10 @@

let (_, remaining) = buf.split_at_mut(len);
len += read(f.as_fd().as_raw_fd(), remaining).unwrap();
len += read(&f, remaining).unwrap();
}
}
/// Any test that creates child processes or can be affected by child processes must grab this mutex, regardless
/// of what it does with those children. It must hold the mutex until the
/// child processes are waited upon.
/// Any test that creates child processes or can be affected by child processes
/// must grab this mutex, regardless of what it does with those children.
///
/// It must hold the mutex until the child processes are waited upon.
pub static FORK_MTX: Mutex<()> = Mutex::new(());

@@ -83,3 +98,3 @@ /// Any test that changes the process's current working directory must grab

impl<'a> DirRestore<'a> {
impl DirRestore<'_> {
fn new() -> Self {

@@ -94,3 +109,3 @@ let guard = crate::CWD_LOCK.write();

impl<'a> Drop for DirRestore<'a> {
impl Drop for DirRestore<'_> {
fn drop(&mut self) {

@@ -97,0 +112,0 @@ let r = chdir(&self.d);

#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
use std::fs;
use std::fs::File;
#[cfg(not(target_os = "redox"))]
use std::os::unix::fs::symlink;
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
use std::os::unix::fs::PermissionsExt;
use std::os::unix::prelude::AsRawFd;
#[cfg(not(target_os = "redox"))]
use std::path::Path;
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
use std::time::{Duration, UNIX_EPOCH};
use libc::mode_t;
#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
use libc::{S_IFLNK, S_IFMT};
#[cfg(not(target_os = "redox"))]
use nix::errno::Errno;
#[cfg(not(target_os = "redox"))]
use nix::fcntl;
#[cfg(any(
target_os = "linux",
apple_targets,
target_os = "freebsd",
target_os = "netbsd"
))]
use nix::sys::stat::lutimes;
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
use nix::sys::stat::utimensat;
#[cfg(not(target_os = "redox"))]
use nix::sys::stat::FchmodatFlags;
use nix::sys::stat::Mode;
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
use nix::sys::stat::UtimensatFlags;
#[cfg(not(target_os = "redox"))]
use nix::sys::stat::{self};
use nix::sys::stat::{fchmod, stat};
#[cfg(not(target_os = "redox"))]
use nix::sys::stat::{fchmodat, mkdirat};
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
use nix::sys::stat::{futimens, utimes};
#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
use nix::sys::stat::FileStat;
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
use nix::sys::time::{TimeSpec, TimeVal, TimeValLike};
#[cfg(not(target_os = "redox"))]
use nix::unistd::chdir;
#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
use nix::Result;
#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
fn assert_stat_results(stat_result: Result<FileStat>) {
let stats = stat_result.expect("stat call failed");
assert!(stats.st_dev > 0); // must be positive integer, exact number machine dependent
assert!(stats.st_ino > 0); // inode is positive integer, exact number machine dependent
assert!(stats.st_mode > 0); // must be positive integer
assert_eq!(stats.st_nlink, 1); // there links created, must be 1
assert_eq!(stats.st_size, 0); // size is 0 because we did not write anything to the file
assert!(stats.st_blksize > 0); // must be positive integer, exact number machine dependent
assert!(stats.st_blocks <= 16); // Up to 16 blocks can be allocated for a blank file
}
#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
// (Android's st_blocks is ulonglong which is always non-negative.)
#[cfg_attr(target_os = "android", allow(unused_comparisons))]
#[allow(clippy::absurd_extreme_comparisons)] // Not absurd on all OSes
fn assert_lstat_results(stat_result: Result<FileStat>) {
let stats = stat_result.expect("stat call failed");
assert!(stats.st_dev > 0); // must be positive integer, exact number machine dependent
assert!(stats.st_ino > 0); // inode is positive integer, exact number machine dependent
assert!(stats.st_mode > 0); // must be positive integer
// st_mode is c_uint (u32 on Android) while S_IFMT is mode_t
// (u16 on Android), and that will be a compile error.
// On other platforms they are the same (either both are u16 or u32).
assert_eq!(
(stats.st_mode as usize) & (S_IFMT as usize),
S_IFLNK as usize
); // should be a link
assert_eq!(stats.st_nlink, 1); // there links created, must be 1
assert!(stats.st_size > 0); // size is > 0 because it points to another file
assert!(stats.st_blksize > 0); // must be positive integer, exact number machine dependent
// st_blocks depends on whether the machine's file system uses fast
// or slow symlinks, so just make sure it's not negative
assert!(stats.st_blocks >= 0);
}
#[test]
#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
fn test_stat_and_fstat() {
use nix::sys::stat::fstat;
let tempdir = tempfile::tempdir().unwrap();
let filename = tempdir.path().join("foo.txt");
let file = File::create(&filename).unwrap();
let stat_result = stat(&filename);
assert_stat_results(stat_result);
let fstat_result = fstat(file.as_raw_fd());
assert_stat_results(fstat_result);
}
#[test]
#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
fn test_fstatat() {
let tempdir = tempfile::tempdir().unwrap();
let filename = tempdir.path().join("foo.txt");
File::create(&filename).unwrap();
let dirfd =
fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty());
let result =
stat::fstatat(Some(dirfd.unwrap()), &filename, fcntl::AtFlags::empty());
assert_stat_results(result);
}
#[test]
#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
fn test_stat_fstat_lstat() {
use nix::sys::stat::{fstat, lstat};
let tempdir = tempfile::tempdir().unwrap();
let filename = tempdir.path().join("bar.txt");
let linkname = tempdir.path().join("barlink");
File::create(&filename).unwrap();
symlink("bar.txt", &linkname).unwrap();
let link = File::open(&linkname).unwrap();
// should be the same result as calling stat,
// since it's a regular file
let stat_result = stat(&filename);
assert_stat_results(stat_result);
let lstat_result = lstat(&linkname);
assert_lstat_results(lstat_result);
let fstat_result = fstat(link.as_raw_fd());
assert_stat_results(fstat_result);
}
#[test]
fn test_fchmod() {
let tempdir = tempfile::tempdir().unwrap();
let filename = tempdir.path().join("foo.txt");
let file = File::create(&filename).unwrap();
let mut mode1 = Mode::empty();
mode1.insert(Mode::S_IRUSR);
mode1.insert(Mode::S_IWUSR);
fchmod(file.as_raw_fd(), mode1).unwrap();
let file_stat1 = stat(&filename).unwrap();
assert_eq!(file_stat1.st_mode as mode_t & 0o7777, mode1.bits());
let mut mode2 = Mode::empty();
mode2.insert(Mode::S_IROTH);
fchmod(file.as_raw_fd(), mode2).unwrap();
let file_stat2 = stat(&filename).unwrap();
assert_eq!(file_stat2.st_mode as mode_t & 0o7777, mode2.bits());
}
#[test]
#[cfg(not(target_os = "redox"))]
fn test_fchmodat() {
let _dr = crate::DirRestore::new();
let tempdir = tempfile::tempdir().unwrap();
let filename = "foo.txt";
let fullpath = tempdir.path().join(filename);
File::create(&fullpath).unwrap();
let dirfd =
fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
.unwrap();
let mut mode1 = Mode::empty();
mode1.insert(Mode::S_IRUSR);
mode1.insert(Mode::S_IWUSR);
fchmodat(Some(dirfd), filename, mode1, FchmodatFlags::FollowSymlink)
.unwrap();
let file_stat1 = stat(&fullpath).unwrap();
assert_eq!(file_stat1.st_mode as mode_t & 0o7777, mode1.bits());
chdir(tempdir.path()).unwrap();
let mut mode2 = Mode::empty();
mode2.insert(Mode::S_IROTH);
fchmodat(None, filename, mode2, FchmodatFlags::FollowSymlink).unwrap();
let file_stat2 = stat(&fullpath).unwrap();
assert_eq!(file_stat2.st_mode as mode_t & 0o7777, mode2.bits());
}
/// Asserts that the atime and mtime in a file's metadata match expected values.
///
/// The atime and mtime are expressed with a resolution of seconds because some file systems
/// (like macOS's HFS+) do not have higher granularity.
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
fn assert_times_eq(
exp_atime_sec: u64,
exp_mtime_sec: u64,
attr: &fs::Metadata,
) {
assert_eq!(
Duration::new(exp_atime_sec, 0),
attr.accessed().unwrap().duration_since(UNIX_EPOCH).unwrap()
);
assert_eq!(
Duration::new(exp_mtime_sec, 0),
attr.modified().unwrap().duration_since(UNIX_EPOCH).unwrap()
);
}
#[test]
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
fn test_utimes() {
let tempdir = tempfile::tempdir().unwrap();
let fullpath = tempdir.path().join("file");
drop(File::create(&fullpath).unwrap());
utimes(&fullpath, &TimeVal::seconds(9990), &TimeVal::seconds(5550))
.unwrap();
assert_times_eq(9990, 5550, &fs::metadata(&fullpath).unwrap());
}
#[test]
#[cfg(any(
target_os = "linux",
apple_targets,
target_os = "freebsd",
target_os = "netbsd"
))]
fn test_lutimes() {
let tempdir = tempfile::tempdir().unwrap();
let target = tempdir.path().join("target");
let fullpath = tempdir.path().join("symlink");
drop(File::create(&target).unwrap());
symlink(&target, &fullpath).unwrap();
let exp_target_metadata = fs::symlink_metadata(&target).unwrap();
lutimes(&fullpath, &TimeVal::seconds(4560), &TimeVal::seconds(1230))
.unwrap();
assert_times_eq(4560, 1230, &fs::symlink_metadata(&fullpath).unwrap());
let target_metadata = fs::symlink_metadata(&target).unwrap();
assert_eq!(
exp_target_metadata.accessed().unwrap(),
target_metadata.accessed().unwrap(),
"atime of symlink target was unexpectedly modified"
);
assert_eq!(
exp_target_metadata.modified().unwrap(),
target_metadata.modified().unwrap(),
"mtime of symlink target was unexpectedly modified"
);
}
#[test]
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
fn test_futimens() {
let tempdir = tempfile::tempdir().unwrap();
let fullpath = tempdir.path().join("file");
drop(File::create(&fullpath).unwrap());
let fd = fcntl::open(&fullpath, fcntl::OFlag::empty(), stat::Mode::empty())
.unwrap();
futimens(fd, &TimeSpec::seconds(10), &TimeSpec::seconds(20)).unwrap();
assert_times_eq(10, 20, &fs::metadata(&fullpath).unwrap());
}
#[test]
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
fn test_utimensat() {
let _dr = crate::DirRestore::new();
let tempdir = tempfile::tempdir().unwrap();
let filename = "foo.txt";
let fullpath = tempdir.path().join(filename);
drop(File::create(&fullpath).unwrap());
let dirfd =
fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
.unwrap();
utimensat(
Some(dirfd),
filename,
&TimeSpec::seconds(12345),
&TimeSpec::seconds(678),
UtimensatFlags::FollowSymlink,
)
.unwrap();
assert_times_eq(12345, 678, &fs::metadata(&fullpath).unwrap());
chdir(tempdir.path()).unwrap();
utimensat(
None,
filename,
&TimeSpec::seconds(500),
&TimeSpec::seconds(800),
UtimensatFlags::FollowSymlink,
)
.unwrap();
assert_times_eq(500, 800, &fs::metadata(&fullpath).unwrap());
}
#[test]
#[cfg(not(target_os = "redox"))]
fn test_mkdirat_success_path() {
let tempdir = tempfile::tempdir().unwrap();
let filename = "example_subdir";
let dirfd =
fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
.unwrap();
mkdirat(Some(dirfd), filename, Mode::S_IRWXU).expect("mkdirat failed");
assert!(Path::exists(&tempdir.path().join(filename)));
}
#[test]
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
fn test_mkdirat_success_mode() {
let expected_bits =
stat::SFlag::S_IFDIR.bits() | stat::Mode::S_IRWXU.bits();
let tempdir = tempfile::tempdir().unwrap();
let filename = "example_subdir";
let dirfd =
fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
.unwrap();
mkdirat(Some(dirfd), filename, Mode::S_IRWXU).expect("mkdirat failed");
let permissions = fs::metadata(tempdir.path().join(filename))
.unwrap()
.permissions();
let mode = permissions.mode();
assert_eq!(mode as mode_t, expected_bits)
}
#[test]
#[cfg(not(target_os = "redox"))]
fn test_mkdirat_fail() {
let tempdir = tempfile::tempdir().unwrap();
let not_dir_filename = "example_not_dir";
let filename = "example_subdir_dir";
let dirfd = fcntl::open(
&tempdir.path().join(not_dir_filename),
fcntl::OFlag::O_CREAT,
stat::Mode::empty(),
)
.unwrap();
let result = mkdirat(Some(dirfd), filename, Mode::S_IRWXU).unwrap_err();
assert_eq!(result, Errno::ENOTDIR);
}
#[test]
#[cfg(not(any(
freebsdlike,
apple_targets,
target_os = "haiku",
target_os = "redox"
)))]
fn test_mknod() {
use stat::{lstat, mknod, SFlag};
let file_name = "test_file";
let tempdir = tempfile::tempdir().unwrap();
let target = tempdir.path().join(file_name);
mknod(&target, SFlag::S_IFREG, Mode::S_IRWXU, 0).unwrap();
let mode = lstat(&target).unwrap().st_mode as mode_t;
assert_eq!(mode & libc::S_IFREG, libc::S_IFREG);
assert_eq!(mode & libc::S_IRWXU, libc::S_IRWXU);
}
#[test]
#[cfg(not(any(
solarish,
freebsdlike,
apple_targets,
target_os = "haiku",
target_os = "redox"
)))]
fn test_mknodat() {
use fcntl::{AtFlags, OFlag};
use nix::dir::Dir;
use stat::{fstatat, mknodat, SFlag};
let file_name = "test_file";
let tempdir = tempfile::tempdir().unwrap();
let target_dir =
Dir::open(tempdir.path(), OFlag::O_DIRECTORY, Mode::S_IRWXU).unwrap();
mknodat(
Some(target_dir.as_raw_fd()),
file_name,
SFlag::S_IFREG,
Mode::S_IRWXU,
0,
)
.unwrap();
let mode = fstatat(
Some(target_dir.as_raw_fd()),
file_name,
AtFlags::AT_SYMLINK_NOFOLLOW,
)
.unwrap()
.st_mode as mode_t;
assert_eq!(mode & libc::S_IFREG, libc::S_IFREG);
assert_eq!(mode & libc::S_IRWXU, libc::S_IRWXU);
}
#[test]
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
fn test_futimens_unchanged() {
let tempdir = tempfile::tempdir().unwrap();
let fullpath = tempdir.path().join("file");
drop(File::create(&fullpath).unwrap());
let fd = fcntl::open(&fullpath, fcntl::OFlag::empty(), stat::Mode::empty())
.unwrap();
let old_atime = fs::metadata(fullpath.as_path())
.unwrap()
.accessed()
.unwrap();
let old_mtime = fs::metadata(fullpath.as_path())
.unwrap()
.modified()
.unwrap();
futimens(fd, &TimeSpec::UTIME_OMIT, &TimeSpec::UTIME_OMIT).unwrap();
let new_atime = fs::metadata(fullpath.as_path())
.unwrap()
.accessed()
.unwrap();
let new_mtime = fs::metadata(fullpath.as_path())
.unwrap()
.modified()
.unwrap();
assert_eq!(old_atime, new_atime);
assert_eq!(old_mtime, new_mtime);
}
#[test]
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
fn test_utimensat_unchanged() {
let _dr = crate::DirRestore::new();
let tempdir = tempfile::tempdir().unwrap();
let filename = "foo.txt";
let fullpath = tempdir.path().join(filename);
drop(File::create(&fullpath).unwrap());
let dirfd =
fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
.unwrap();
let old_atime = fs::metadata(fullpath.as_path())
.unwrap()
.accessed()
.unwrap();
let old_mtime = fs::metadata(fullpath.as_path())
.unwrap()
.modified()
.unwrap();
utimensat(
Some(dirfd),
filename,
&TimeSpec::UTIME_OMIT,
&TimeSpec::UTIME_OMIT,
UtimensatFlags::NoFollowSymlink,
)
.unwrap();
let new_atime = fs::metadata(fullpath.as_path())
.unwrap()
.accessed()
.unwrap();
let new_mtime = fs::metadata(fullpath.as_path())
.unwrap()
.modified()
.unwrap();
assert_eq!(old_atime, new_atime);
assert_eq!(old_mtime, new_mtime);
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display