diff options
Diffstat (limited to 'third_party/rust/nix/src/lib.rs')
-rw-r--r-- | third_party/rust/nix/src/lib.rs | 333 |
1 files changed, 333 insertions, 0 deletions
diff --git a/third_party/rust/nix/src/lib.rs b/third_party/rust/nix/src/lib.rs new file mode 100644 index 0000000000..6b82125761 --- /dev/null +++ b/third_party/rust/nix/src/lib.rs @@ -0,0 +1,333 @@ +//! Rust friendly bindings to the various *nix system functions. +//! +//! Modules are structured according to the C header file that they would be +//! defined in. +//! +//! # Features +//! +//! Nix uses the following Cargo features to enable optional functionality. +//! They may be enabled in any combination. +//! * `acct` - Process accounting +//! * `aio` - POSIX AIO +//! * `dir` - Stuff relating to directory iteration +//! * `env` - Manipulate environment variables +//! * `event` - Event-driven APIs, like `kqueue` and `epoll` +//! * `feature` - Query characteristics of the OS at runtime +//! * `fs` - File system functionality +//! * `hostname` - Get and set the system's hostname +//! * `inotify` - Linux's `inotify` file system notification API +//! * `ioctl` - The `ioctl` syscall, and wrappers for my specific instances +//! * `kmod` - Load and unload kernel modules +//! * `mman` - Stuff relating to memory management +//! * `mount` - Mount and unmount file systems +//! * `mqueue` - POSIX message queues +//! * `net` - Networking-related functionality +//! * `personality` - Set the process execution domain +//! * `poll` - APIs like `poll` and `select` +//! * `process` - Stuff relating to running processes +//! * `pthread` - POSIX threads +//! * `ptrace` - Process tracing and debugging +//! * `quota` - File system quotas +//! * `reboot` - Reboot the system +//! * `resource` - Process resource limits +//! * `sched` - Manipulate process's scheduling +//! * `socket` - Sockets, whether for networking or local use +//! * `signal` - Send and receive signals to processes +//! * `term` - Terminal control APIs +//! * `time` - Query the operating system's clocks +//! * `ucontext` - User thread context +//! * `uio` - Vectored I/O +//! * `user` - Stuff relating to users and groups +//! * `zerocopy` - APIs like `sendfile` and `copy_file_range` +#![crate_name = "nix"] +#![cfg(unix)] +#![cfg_attr(docsrs, doc(cfg(all())))] +#![allow(non_camel_case_types)] +#![cfg_attr(test, deny(warnings))] +#![recursion_limit = "500"] +#![deny(unused)] +#![allow(unused_macros)] +#![cfg_attr(not(feature = "default"), 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)] + +// Re-exported external crates +pub use libc; + +// Private internal modules +#[macro_use] +mod macros; + +// Public crates +#[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; +} +#[allow(missing_docs)] +pub mod fcntl; +feature! { + #![feature = "net"] + + #[cfg(any(target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "illumos", + target_os = "openbsd"))] + #[deny(missing_docs)] + pub mod ifaddrs; + #[cfg(not(target_os = "redox"))] + #[deny(missing_docs)] + pub mod net; +} +#[cfg(any(target_os = "android", target_os = "linux"))] +feature! { + #![feature = "kmod"] + #[allow(missing_docs)] + pub mod kmod; +} +feature! { + #![feature = "mount"] + pub mod mount; +} +#[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + 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"] + #[allow(missing_docs)] + pub mod time; +} +// This can be implemented for other platforms as soon as libc +// provides bindings for them. +#[cfg(all( + target_os = "linux", + any(target_arch = "s390x", target_arch = "x86", target_arch = "x86_64") +))] +feature! { + #![feature = "ucontext"] + #[allow(missing_docs)] + pub mod ucontext; +} +#[allow(missing_docs)] +pub mod unistd; + +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; + +/// Nix Result Type +pub type Result<T> = result::Result<T, Errno>; + +/// Nix's main error type. +/// +/// It's a wrapper around Errno. As such, it's very interoperable with +/// [`std::io::Error`], but it has the advantages of: +/// * `Clone` +/// * `Copy` +/// * `Eq` +/// * Small size +/// * Represents all of the system's errnos, instead of just the most common +/// ones. +pub type Error = Errno; + +/// Common trait used to represent file system paths by many Nix functions. +pub trait NixPath { + /// Is the path empty? + fn is_empty(&self) -> bool; + + /// Length of the path in bytes + fn len(&self) -> usize; + + /// Execute a function with this path as a `CStr`. + /// + /// Mostly used internally by Nix. + 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, + { + // 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. + // https://github.com/nix-rust/nix/pull/1656 + // + // By being smaller than a memory page, we also avoid the compiler inserting a probe frame: + // https://docs.rs/compiler_builtins/latest/compiler_builtins/probestack/index.html + 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() as *mut u8; + + 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) + } +} |