#![crate_name = "nix"]
#![cfg(unix)]
#![allow(non_camel_case_types)]
#![allow(clippy::too_long_first_doc_paragraph)]
#![recursion_limit = "500"]
#![deny(unused)]
#![deny(unexpected_cfgs)]
#![allow(unused_macros)]
#![cfg_attr(
not(all(
feature = "acct",
feature = "aio",
feature = "dir",
feature = "env",
feature = "event",
feature = "fanotify",
feature = "feature",
feature = "fs",
feature = "hostname",
feature = "inotify",
feature = "ioctl",
feature = "kmod",
feature = "mman",
feature = "mount",
feature = "mqueue",
feature = "net",
feature = "personality",
feature = "poll",
feature = "process",
feature = "pthread",
feature = "ptrace",
feature = "quota",
feature = "reboot",
feature = "resource",
feature = "sched",
feature = "socket",
feature = "signal",
feature = "syslog",
feature = "term",
feature = "time",
feature = "ucontext",
feature = "uio",
feature = "user",
feature = "zerocopy",
)),
allow(unused_imports)
)]
#![deny(unstable_features)]
#![deny(missing_copy_implementations)]
#![deny(missing_debug_implementations)]
#![warn(missing_docs)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![deny(clippy::cast_ptr_alignment)]
#![deny(unsafe_op_in_unsafe_fn)]
#![allow(clippy::unwrap_or_default)]
pub use libc;
#[macro_use]
mod macros;
#[cfg(not(target_os = "redox"))]
feature! {
#![feature = "dir"]
pub mod dir;
}
feature! {
#![feature = "env"]
pub mod env;
}
#[allow(missing_docs)]
pub mod errno;
feature! {
#![feature = "feature"]
#[deny(missing_docs)]
pub mod features;
}
pub mod fcntl;
feature! {
#![feature = "net"]
#[cfg(any(linux_android,
bsd,
solarish,
target_os = "hurd"))]
#[deny(missing_docs)]
pub mod ifaddrs;
#[cfg(not(target_os = "redox"))]
#[deny(missing_docs)]
pub mod net;
}
#[cfg(linux_android)]
feature! {
#![feature = "kmod"]
pub mod kmod;
}
feature! {
#![feature = "mount"]
pub mod mount;
}
#[cfg(any(
freebsdlike,
all(target_os = "linux", not(target_env = "ohos")),
target_os = "netbsd"
))]
feature! {
#![feature = "mqueue"]
pub mod mqueue;
}
feature! {
#![feature = "poll"]
pub mod poll;
}
#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))]
feature! {
#![feature = "term"]
#[deny(missing_docs)]
pub mod pty;
}
feature! {
#![feature = "sched"]
pub mod sched;
}
pub mod sys;
feature! {
#![feature = "time"]
pub mod time;
}
#[cfg(all(
target_os = "linux",
any(
target_arch = "aarch64",
target_arch = "s390x",
target_arch = "x86",
target_arch = "x86_64"
)
))]
feature! {
#![feature = "ucontext"]
#[allow(missing_docs)]
pub mod ucontext;
}
pub mod unistd;
#[cfg(any(feature = "poll", feature = "event"))]
mod poll_timeout;
#[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};
use std::mem::MaybeUninit;
use std::os::unix::ffi::OsStrExt;
use std::path::{Path, PathBuf};
use std::{ptr, result, slice};
use errno::Errno;
pub type Result<T> = result::Result<T, Errno>;
pub type Error = Errno;
pub trait NixPath {
fn is_empty(&self) -> bool;
fn len(&self) -> usize;
fn with_nix_path<T, F>(&self, f: F) -> Result<T>
where
F: FnOnce(&CStr) -> T;
}
impl NixPath for str {
fn is_empty(&self) -> bool {
NixPath::is_empty(OsStr::new(self))
}
fn len(&self) -> usize {
NixPath::len(OsStr::new(self))
}
fn with_nix_path<T, F>(&self, f: F) -> Result<T>
where
F: FnOnce(&CStr) -> T,
{
OsStr::new(self).with_nix_path(f)
}
}
impl NixPath for OsStr {
fn is_empty(&self) -> bool {
self.as_bytes().is_empty()
}
fn len(&self) -> usize {
self.as_bytes().len()
}
fn with_nix_path<T, F>(&self, f: F) -> Result<T>
where
F: FnOnce(&CStr) -> T,
{
self.as_bytes().with_nix_path(f)
}
}
impl NixPath for CStr {
fn is_empty(&self) -> bool {
self.to_bytes().is_empty()
}
fn len(&self) -> usize {
self.to_bytes().len()
}
fn with_nix_path<T, F>(&self, f: F) -> Result<T>
where
F: FnOnce(&CStr) -> T,
{
Ok(f(self))
}
}
impl NixPath for [u8] {
fn is_empty(&self) -> bool {
self.is_empty()
}
fn len(&self) -> usize {
self.len()
}
fn with_nix_path<T, F>(&self, f: F) -> Result<T>
where
F: FnOnce(&CStr) -> T,
{
const MAX_STACK_ALLOCATION: usize = 1024;
if self.len() >= MAX_STACK_ALLOCATION {
return with_nix_path_allocating(self, f);
}
let mut buf = MaybeUninit::<[u8; MAX_STACK_ALLOCATION]>::uninit();
let buf_ptr = buf.as_mut_ptr().cast();
unsafe {
ptr::copy_nonoverlapping(self.as_ptr(), buf_ptr, self.len());
buf_ptr.add(self.len()).write(0);
}
match CStr::from_bytes_with_nul(unsafe {
slice::from_raw_parts(buf_ptr, self.len() + 1)
}) {
Ok(s) => Ok(f(s)),
Err(_) => Err(Errno::EINVAL),
}
}
}
#[cold]
#[inline(never)]
fn with_nix_path_allocating<T, F>(from: &[u8], f: F) -> Result<T>
where
F: FnOnce(&CStr) -> T,
{
match CString::new(from) {
Ok(s) => Ok(f(&s)),
Err(_) => Err(Errno::EINVAL),
}
}
impl NixPath for Path {
fn is_empty(&self) -> bool {
NixPath::is_empty(self.as_os_str())
}
fn len(&self) -> usize {
NixPath::len(self.as_os_str())
}
fn with_nix_path<T, F>(&self, f: F) -> Result<T>
where
F: FnOnce(&CStr) -> T,
{
self.as_os_str().with_nix_path(f)
}
}
impl NixPath for PathBuf {
fn is_empty(&self) -> bool {
NixPath::is_empty(self.as_os_str())
}
fn len(&self) -> usize {
NixPath::len(self.as_os_str())
}
fn with_nix_path<T, F>(&self, f: F) -> Result<T>
where
F: FnOnce(&CStr) -> T,
{
self.as_os_str().with_nix_path(f)
}
}
#[cfg(any(
all(apple_targets, feature = "mount"),
all(linux_android, any(feature = "mount", feature = "fanotify"))
))]
pub(crate) fn with_opt_nix_path<P, T, F>(path: Option<&P>, f: F) -> Result<T>
where
P: ?Sized + NixPath,
F: FnOnce(*const libc::c_char) -> T,
{
match path {
Some(path) => path.with_nix_path(|p_str| f(p_str.as_ptr())),
None => Ok(f(ptr::null())),
}
}