diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 257f89a66..cbf79ab18 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -219,7 +219,8 @@ jobs: # Omit --all-targets on haiku because not all the tests build yet. # Temporarily disable this because the target appears to have breakage on nightly. #- run: cargo check -Z build-std --target x86_64-unknown-haiku --features=all-apis - - run: cargo check -Z build-std --target x86_64-uwp-windows-msvc --all-targets --features=all-apis + # Temporarily disable this because the target appears to have breakage on nightly. + #- run: cargo check -Z build-std --target x86_64-uwp-windows-msvc --all-targets --features=all-apis # Temporarily disable riscv32imc-esp-espidf due to std using SOMAXCONN. #- run: cargo check -Z build-std --target=riscv32imc-esp-espidf --features=all-apis - run: cargo check -Z build-std --target=aarch64-unknown-nto-qnx710 --features=all-apis @@ -246,7 +247,7 @@ jobs: RUSTFLAGS: --cfg rustix_use_experimental_features strategy: matrix: - build: [ubuntu, ubuntu-20.04, i686-linux, aarch64-linux, powerpc64le-linux, riscv64-linux, s390x-linux, arm-linux, ubuntu-stable, i686-linux-stable, aarch64-linux-stable, riscv64-linux-stable, s390x-linux-stable, powerpc64le-linux-stable, arm-linux-stable, ubuntu-1.63, i686-linux-1.63, aarch64-linux-1.63, riscv64-linux-1.63, s390x-linux-1.63, powerpc64le-linux-1.63, arm-linux-1.63, macos-latest, macos-11, windows, windows-2019, musl] + build: [ubuntu, ubuntu-20.04, i686-linux, aarch64-linux, powerpc64le-linux, riscv64-linux, s390x-linux, arm-linux, ubuntu-stable, i686-linux-stable, aarch64-linux-stable, riscv64-linux-stable, s390x-linux-stable, powerpc64le-linux-stable, arm-linux-stable, ubuntu-1.63, i686-linux-1.63, aarch64-linux-1.63, riscv64-linux-1.63, s390x-linux-1.63, powerpc64le-linux-1.63, arm-linux-1.63, macos-latest, macos-12, windows, windows-2019, musl] include: - build: ubuntu os: ubuntu-latest @@ -419,8 +420,8 @@ jobs: - build: macos-latest os: macos-latest rust: stable - - build: macos-11 - os: macos-11 + - build: macos-12 + os: macos-12 rust: stable - build: windows os: windows-latest diff --git a/Cargo.toml b/Cargo.toml index ab413e710..85cc2f130 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rustix" -version = "0.38.34" +version = "0.38.35" authors = [ "Dan Gohman ", "Jakub Konka ", @@ -21,7 +21,7 @@ itoa = { version = "1.0.1", default-features = false, optional = true } # Special dependencies used in rustc-dep-of-std mode. core = { version = "1.0.0", optional = true, package = "rustc-std-workspace-core" } -alloc = { version = "1.0.0", optional = true, package = "rustc-std-workspace-alloc" } +rustc-std-workspace-alloc = { version = "1.0.0", optional = true } # not aliased here but in lib.rs casuse of name collision with the alloc feature compiler_builtins = { version = '0.1.49', optional = true } # The procfs feature needs once_cell. @@ -36,9 +36,9 @@ once_cell = { version = "1.5.2", optional = true } # libc backend can be selected via adding `--cfg=rustix_use_libc` to # `RUSTFLAGS` or enabling the `use-libc` cargo feature. [target.'cfg(all(not(rustix_use_libc), not(miri), target_os = "linux", target_endian = "little", any(target_arch = "arm", all(target_arch = "aarch64", target_pointer_width = "64"), target_arch = "riscv64", all(rustix_use_experimental_asm, target_arch = "powerpc64"), all(rustix_use_experimental_asm, target_arch = "mips"), all(rustix_use_experimental_asm, target_arch = "mips32r6"), all(rustix_use_experimental_asm, target_arch = "mips64"), all(rustix_use_experimental_asm, target_arch = "mips64r6"), target_arch = "x86", all(target_arch = "x86_64", target_pointer_width = "64"))))'.dependencies] -linux-raw-sys = { version = "0.4.12", default-features = false, features = ["general", "errno", "ioctl", "no_std", "elf"] } +linux-raw-sys = { version = "0.4.14", default-features = false, features = ["general", "errno", "ioctl", "no_std", "elf"] } libc_errno = { package = "errno", version = "0.3.8", default-features = false, optional = true } -libc = { version = "0.2.153", default-features = false, optional = true } +libc = { version = "0.2.156", default-features = false, optional = true } # Dependencies for platforms where only libc is supported: # @@ -46,14 +46,14 @@ libc = { version = "0.2.153", default-features = false, optional = true } # backend, so enable its dependencies unconditionally. [target.'cfg(all(not(windows), any(rustix_use_libc, miri, not(all(target_os = "linux", target_endian = "little", any(target_arch = "arm", all(target_arch = "aarch64", target_pointer_width = "64"), target_arch = "riscv64", all(rustix_use_experimental_asm, target_arch = "powerpc64"), all(rustix_use_experimental_asm, target_arch = "mips"), all(rustix_use_experimental_asm, target_arch = "mips32r6"), all(rustix_use_experimental_asm, target_arch = "mips64"), all(rustix_use_experimental_asm, target_arch = "mips64r6"), target_arch = "x86", all(target_arch = "x86_64", target_pointer_width = "64")))))))'.dependencies] libc_errno = { package = "errno", version = "0.3.8", default-features = false } -libc = { version = "0.2.153", default-features = false } +libc = { version = "0.2.156", default-features = false } # Additional dependencies for Linux with the libc backend: # # Some syscalls do not have libc wrappers, such as in `io_uring`. For these, # the libc backend uses the linux-raw-sys ABI and `libc::syscall`. [target.'cfg(all(any(target_os = "android", target_os = "linux"), any(rustix_use_libc, miri, not(all(target_os = "linux", target_endian = "little", any(target_arch = "arm", all(target_arch = "aarch64", target_pointer_width = "64"), target_arch = "riscv64", all(rustix_use_experimental_asm, target_arch = "powerpc64"), all(rustix_use_experimental_asm, target_arch = "mips"), all(rustix_use_experimental_asm, target_arch = "mips32r6"), all(rustix_use_experimental_asm, target_arch = "mips64"), all(rustix_use_experimental_asm, target_arch = "mips64r6"), target_arch = "x86", all(target_arch = "x86_64", target_pointer_width = "64")))))))'.dependencies] -linux-raw-sys = { version = "0.4.12", default-features = false, features = ["general", "ioctl", "no_std"] } +linux-raw-sys = { version = "0.4.14", default-features = false, features = ["general", "ioctl", "no_std"] } # For the libc backend on Windows, use the Winsock API in windows-sys. [target.'cfg(windows)'.dependencies.windows-sys] @@ -74,7 +74,7 @@ default-features = false [dev-dependencies] tempfile = "3.5.0" -libc = "0.2.153" +libc = "0.2.156" libc_errno = { package = "errno", version = "0.3.8", default-features = false } serial_test = "2.0.0" memoffset = "0.9.0" @@ -84,9 +84,6 @@ static_assertions = "1.1.0" [target.'cfg(all(criterion, not(any(target_os = "emscripten", target_os = "wasi"))))'.dev-dependencies] criterion = "0.4" -[target.'cfg(windows)'.dev-dependencies] -ctor = "0.2.0" - # Add Criterion configuration, as described here: # [[bench]] @@ -95,7 +92,6 @@ harness = false [package.metadata.docs.rs] features = ["all-apis"] -rustdoc-args = ["--cfg", "doc_cfg"] targets = [ "x86_64-unknown-linux-gnu", "i686-unknown-linux-gnu", @@ -186,8 +182,8 @@ stdio = [] # Enable `rustix::system::*`. system = ["linux-raw-sys/system"] -# Enable `rustix::runtime::*`. This API is undocumented and unstable and -# experimental and not intended for general-purpose use. +# Enable `rustix::runtime::*`. ⚠ This API is undocumented and unstable and +# experimental and not intended for general-purpose use. ⚠ runtime = ["linux-raw-sys/prctl"] # Enable all API features. @@ -238,9 +234,9 @@ alloc = [] # This is used in the port of std to rustix. This is experimental and not meant # for regular use. rustc-dep-of-std = [ - "dep:core", - "dep:alloc", - "dep:compiler_builtins", + "core", + "rustc-std-workspace-alloc", + "compiler_builtins", "linux-raw-sys/rustc-dep-of-std", "bitflags/rustc-dep-of-std", "compiler_builtins?/rustc-dep-of-std", @@ -248,3 +244,37 @@ rustc-dep-of-std = [ # Obsolete and deprecated. cc = [] + +# Enable `rustix::io::try_close`. The rustix developers do not intend the +# existence of this feature to imply that anyone should use it. +try_close = [] + +[lints.rust.unexpected_cfgs] +level = "warn" +check-cfg = [ + 'cfg(alloc_c_string)', + 'cfg(alloc_ffi)', + 'cfg(apple)', + 'cfg(asm_experimental_arch)', + 'cfg(bsd)', + 'cfg(core_c_str)', + 'cfg(core_ffi_c)', + 'cfg(core_intrinsics)', + 'cfg(criterion)', + 'cfg(document_experimental_runtime_api)', + 'cfg(fix_y2038)', + 'cfg(freebsdlike)', + 'cfg(libc)', + 'cfg(linux_kernel)', + 'cfg(linux_like)', + 'cfg(linux_raw)', + 'cfg(netbsdlike)', + 'cfg(rustc_attrs)', + 'cfg(solarish)', + 'cfg(staged_api)', + 'cfg(static_assertions)', + 'cfg(thumb_mode)', + 'cfg(wasi)', + 'cfg(wasi_ext)', + 'cfg(target_arch, values("xtensa"))', +] diff --git a/examples/pivot_root.rs b/examples/pivot_root.rs new file mode 100644 index 000000000..bf62fa735 --- /dev/null +++ b/examples/pivot_root.rs @@ -0,0 +1,27 @@ +//! A wrapper around `rustix::fs::pivot_root`. + +#[cfg(all(target_os = "linux", feature = "fs", feature = "process"))] +fn main() -> rustix::io::Result<()> { + let mut args = std::env::args(); + if args.len() != 3 { + eprintln!("Usage: {} new_root put_old", args.next().unwrap()); + std::process::exit(1); + } + + let _argv0 = args.next().unwrap(); + let new_root = args.next().unwrap(); + let put_old = args.next().unwrap(); + + rustix::process::pivot_root(new_root, put_old)?; + + Ok(()) +} + +#[cfg(any( + not(target_os = "linux"), + not(feature = "fs"), + not(feature = "process") +))] +fn main() -> Result<(), &'static str> { + Err("This example requires --features=fs,process and is only supported on Linux.") +} diff --git a/src/backend/libc/c.rs b/src/backend/libc/c.rs index 02958698c..1a5dbf79d 100644 --- a/src/backend/libc/c.rs +++ b/src/backend/libc/c.rs @@ -86,21 +86,21 @@ pub(crate) const XCASE: tcflag_t = linux_raw_sys::general::XCASE as _; #[cfg(target_os = "aix")] pub(crate) const MSG_DONTWAIT: c_int = libc::MSG_NONBLOCK; -// TODO: Remove once https://github.com/rust-lang/libc/pull/3377 is merged and released. -#[cfg(target_os = "netbsd")] -#[cfg(feature = "net")] -pub(crate) const SO_NOSIGPIPE: c_int = 0x0800; - -// It is defined as 0 in libc under 64-bit platforms, but is automatically set by kernel. -// https://github.com/torvalds/linux/blob/v6.7/fs/open.c#L1458-L1459 +// `O_LARGEFILE` can be automatically set by the kernel on Linux: +// +// so libc implementations may leave it undefined or defined to zero. #[cfg(linux_kernel)] pub(crate) const O_LARGEFILE: c_int = linux_raw_sys::general::O_LARGEFILE as _; // Gated under `_LARGEFILE_SOURCE` but automatically set by the kernel. -// https://github.com/illumos/illumos-gate/blob/fb2cb638e5604b214d8ea8d4f01ad2e77b437c17/usr/src/ucbhead/sys/fcntl.h#L64 +// #[cfg(target_os = "illumos")] pub(crate) const O_LARGEFILE: c_int = 0x2000; +// TODO: This is new in Linux 6.11; remove when linux-raw-sys is updated. +#[cfg(linux_kernel)] +pub(crate) const MAP_DROPPABLE: u32 = 0x8; + // On PowerPC, the regular `termios` has the `termios2` fields and there is no // `termios2`. linux-raw-sys has aliases `termios2` to `termios` to cover this // difference, but we still need to manually import it since `libc` doesn't diff --git a/src/backend/libc/conv.rs b/src/backend/libc/conv.rs index 171fc10d8..5052b010f 100644 --- a/src/backend/libc/conv.rs +++ b/src/backend/libc/conv.rs @@ -13,7 +13,7 @@ use crate::io; #[cfg(not(windows))] #[inline] pub(super) fn c_str(c: &CStr) -> *const c::c_char { - c.as_ptr() + c.as_ptr().cast() } #[cfg(not(any(windows, target_os = "espidf", target_os = "vita", target_os = "wasi")))] @@ -226,6 +226,7 @@ pub(crate) fn msg_iov_len(len: usize) -> c::c_int { target_os = "emscripten", target_os = "fuchsia", target_os = "haiku", + target_os = "hurd", target_os = "nto", ))] #[inline] @@ -244,6 +245,7 @@ pub(crate) fn msg_control_len(len: usize) -> c::socklen_t { target_os = "espidf", target_os = "fuchsia", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "redox", target_os = "vita", diff --git a/src/backend/libc/event/epoll.rs b/src/backend/libc/event/epoll.rs index 6e2ba3c12..7473f6857 100644 --- a/src/backend/libc/event/epoll.rs +++ b/src/backend/libc/event/epoll.rs @@ -1,91 +1,10 @@ -//! Linux `epoll` support. -//! -//! # Examples -//! -//! ```no_run -//! # #[cfg(feature = "net")] -//! # fn main() -> std::io::Result<()> { -//! use rustix::event::epoll; -//! use rustix::fd::AsFd; -//! use rustix::io::{ioctl_fionbio, read, write}; -//! use rustix::net::{ -//! accept, bind_v4, listen, socket, AddressFamily, Ipv4Addr, SocketAddrV4, SocketType, -//! }; -//! use std::collections::HashMap; -//! use std::os::unix::io::AsRawFd; -//! -//! // Create a socket and listen on it. -//! let listen_sock = socket(AddressFamily::INET, SocketType::STREAM, None)?; -//! bind_v4(&listen_sock, &SocketAddrV4::new(Ipv4Addr::LOCALHOST, 0))?; -//! listen(&listen_sock, 1)?; -//! -//! // Create an epoll object. Using `Owning` here means the epoll object will -//! // take ownership of the file descriptors registered with it. -//! let epoll = epoll::create(epoll::CreateFlags::CLOEXEC)?; -//! -//! // Register the socket with the epoll object. -//! epoll::add( -//! &epoll, -//! &listen_sock, -//! epoll::EventData::new_u64(1), -//! epoll::EventFlags::IN, -//! )?; -//! -//! // Keep track of the sockets we've opened. -//! let mut next_id = epoll::EventData::new_u64(2); -//! let mut sockets = HashMap::new(); -//! -//! // Process events. -//! let mut event_list = epoll::EventVec::with_capacity(4); -//! loop { -//! epoll::wait(&epoll, &mut event_list, -1)?; -//! for event in &event_list { -//! let target = event.data; -//! if target.u64() == 1 { -//! // Accept a new connection, set it to non-blocking, and -//! // register to be notified when it's ready to write to. -//! let conn_sock = accept(&listen_sock)?; -//! ioctl_fionbio(&conn_sock, true)?; -//! epoll::add( -//! &epoll, -//! &conn_sock, -//! next_id, -//! epoll::EventFlags::OUT | epoll::EventFlags::ET, -//! )?; -//! -//! // Keep track of the socket. -//! sockets.insert(next_id, conn_sock); -//! next_id = epoll::EventData::new_u64(next_id.u64() + 1); -//! } else { -//! // Write a message to the stream and then unregister it. -//! let target = sockets.remove(&target).unwrap(); -//! write(&target, b"hello\n")?; -//! let _ = epoll::delete(&epoll, &target)?; -//! } -//! } -//! } -//! # } -//! # #[cfg(not(feature = "net"))] -//! # fn main() {} -//! ``` - use crate::backend::c; -#[cfg(feature = "alloc")] -use crate::backend::conv::ret_u32; -use crate::backend::conv::{ret, ret_owned_fd}; -use crate::fd::{AsFd, AsRawFd, OwnedFd}; -use crate::io; -use crate::utils::as_mut_ptr; -#[cfg(feature = "alloc")] -use alloc::vec::Vec; use bitflags::bitflags; -use core::ffi::c_void; -use core::hash::{Hash, Hasher}; -use core::ptr::null_mut; -use core::slice; bitflags! { - /// `EPOLL_*` for use with [`new`]. + /// `EPOLL_*` for use with [`epoll::create`]. + /// + /// [`epoll::create`]: crate::event::epoll::create #[repr(transparent)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct CreateFlags: u32 { @@ -98,7 +17,9 @@ bitflags! { } bitflags! { - /// `EPOLL*` for use with [`add`]. + /// `EPOLL*` for use with [`epoll::add`]. + /// + /// [`epoll::add`]: crate::event::epoll::add #[repr(transparent)] #[derive(Default, Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct EventFlags: u32 { @@ -152,355 +73,3 @@ bitflags! { const _ = !0; } } - -/// `epoll_create1(flags)`—Creates a new epoll object. -/// -/// Use the [`CreateFlags::CLOEXEC`] flag to prevent the resulting file -/// descriptor from being implicitly passed across `exec` boundaries. -#[inline] -#[doc(alias = "epoll_create1")] -pub fn create(flags: CreateFlags) -> io::Result { - // SAFETY: We're calling `epoll_create1` via FFI and we know how it - // behaves. - unsafe { ret_owned_fd(c::epoll_create1(bitflags_bits!(flags))) } -} - -/// `epoll_ctl(self, EPOLL_CTL_ADD, data, event)`—Adds an element to an epoll -/// object. -/// -/// This registers interest in any of the events set in `events` occurring on -/// the file descriptor associated with `data`. -/// -/// If [`delete`] is not called on the I/O source passed into this function -/// before the I/O source is `close`d, then the `epoll` will act as if the I/O -/// source is still registered with it. This can lead to spurious events being -/// returned from [`wait`]. If a file descriptor is an -/// `Arc`, then `epoll` can be thought to maintain a -/// `Weak` to the file descriptor. -#[doc(alias = "epoll_ctl")] -pub fn add( - epoll: impl AsFd, - source: impl AsFd, - data: EventData, - event_flags: EventFlags, -) -> io::Result<()> { - // SAFETY: We're calling `epoll_ctl` via FFI and we know how it - // behaves. We use our own `Event` struct instead of libc's because - // ours preserves pointer provenance instead of just using a `u64`, - // and we have tests elsewhere for layout equivalence. - unsafe { - let raw_fd = source.as_fd().as_raw_fd(); - ret(c::epoll_ctl( - epoll.as_fd().as_raw_fd(), - c::EPOLL_CTL_ADD, - raw_fd, - as_mut_ptr(&mut Event { - flags: event_flags, - data, - #[cfg(target_os = "redox")] - _pad: 0, - }) - .cast(), - )) - } -} - -/// `epoll_ctl(self, EPOLL_CTL_MOD, target, event)`—Modifies an element in a -/// given epoll object. -/// -/// This sets the events of interest with `target` to `events`. -#[doc(alias = "epoll_ctl")] -pub fn modify( - epoll: impl AsFd, - source: impl AsFd, - data: EventData, - event_flags: EventFlags, -) -> io::Result<()> { - let raw_fd = source.as_fd().as_raw_fd(); - - // SAFETY: We're calling `epoll_ctl` via FFI and we know how it - // behaves. We use our own `Event` struct instead of libc's because - // ours preserves pointer provenance instead of just using a `u64`, - // and we have tests elsewhere for layout equivalence. - unsafe { - ret(c::epoll_ctl( - epoll.as_fd().as_raw_fd(), - c::EPOLL_CTL_MOD, - raw_fd, - as_mut_ptr(&mut Event { - flags: event_flags, - data, - #[cfg(target_os = "redox")] - _pad: 0, - }) - .cast(), - )) - } -} - -/// `epoll_ctl(self, EPOLL_CTL_DEL, target, NULL)`—Removes an element in a -/// given epoll object. -#[doc(alias = "epoll_ctl")] -pub fn delete(epoll: impl AsFd, source: impl AsFd) -> io::Result<()> { - // SAFETY: We're calling `epoll_ctl` via FFI and we know how it - // behaves. - unsafe { - let raw_fd = source.as_fd().as_raw_fd(); - ret(c::epoll_ctl( - epoll.as_fd().as_raw_fd(), - c::EPOLL_CTL_DEL, - raw_fd, - null_mut(), - )) - } -} - -/// `epoll_wait(self, events, timeout)`—Waits for registered events of -/// interest. -/// -/// For each event of interest, an element is written to `events`. On -/// success, this returns the number of written elements. -#[cfg(feature = "alloc")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] -pub fn wait(epoll: impl AsFd, event_list: &mut EventVec, timeout: c::c_int) -> io::Result<()> { - // SAFETY: We're calling `epoll_wait` via FFI and we know how it - // behaves. - unsafe { - event_list.events.set_len(0); - let nfds = ret_u32(c::epoll_wait( - epoll.as_fd().as_raw_fd(), - event_list.events.as_mut_ptr().cast::(), - event_list.events.capacity().try_into().unwrap_or(i32::MAX), - timeout, - ))?; - event_list.events.set_len(nfds as usize); - } - - Ok(()) -} - -/// An iterator over the `Event`s in an `EventVec`. -pub struct Iter<'a> { - /// Use `Copied` to copy the struct, since `Event` is `packed` on some - /// platforms, and it's common for users to directly destructure it, which - /// would lead to errors about forming references to packed fields. - iter: core::iter::Copied>, -} - -impl<'a> Iterator for Iter<'a> { - type Item = Event; - - #[inline] - fn next(&mut self) -> Option { - self.iter.next() - } -} - -/// A record of an event that occurred. -#[repr(C)] -#[cfg_attr( - all( - linux_kernel, - any( - all( - target_arch = "x86", - not(target_env = "musl"), - not(target_os = "android"), - ), - target_arch = "x86_64", - ) - ), - repr(packed) -)] -#[derive(Copy, Clone, Eq, PartialEq, Hash)] -pub struct Event { - /// Which specific event(s) occurred. - pub flags: EventFlags, - /// User data. - pub data: EventData, - - #[cfg(target_os = "redox")] - _pad: u64, -} - -/// Data associated with an [`Event`]. This can either be a 64-bit integer -/// value or a pointer which preserves pointer provenance. -#[repr(C)] -#[derive(Copy, Clone)] -pub union EventData { - /// A 64-bit integer value. - as_u64: u64, - - /// A `*mut c_void` which preserves pointer provenance, extended to be - /// 64-bit so that if we read the value as a `u64` union field, we don't - /// get uninitialized memory. - sixty_four_bit_pointer: SixtyFourBitPointer, -} - -impl EventData { - /// Construct a new value containing a `u64`. - #[inline] - pub const fn new_u64(value: u64) -> Self { - Self { as_u64: value } - } - - /// Construct a new value containing a `*mut c_void`. - #[inline] - pub const fn new_ptr(value: *mut c_void) -> Self { - Self { - sixty_four_bit_pointer: SixtyFourBitPointer { - pointer: value, - #[cfg(target_pointer_width = "32")] - _padding: 0, - }, - } - } - - /// Return the value as a `u64`. - /// - /// If the stored value was a pointer, the pointer is zero-extended to a - /// `u64`. - #[inline] - pub fn u64(self) -> u64 { - unsafe { self.as_u64 } - } - - /// Return the value as a `*mut c_void`. - /// - /// If the stored value was a `u64`, the least-significant bits of the - /// `u64` are returned as a pointer value. - #[inline] - pub fn ptr(self) -> *mut c_void { - unsafe { self.sixty_four_bit_pointer.pointer } - } -} - -impl PartialEq for EventData { - #[inline] - fn eq(&self, other: &Self) -> bool { - self.u64() == other.u64() - } -} - -impl Eq for EventData {} - -impl Hash for EventData { - #[inline] - fn hash(&self, state: &mut H) { - self.u64().hash(state) - } -} - -#[repr(C)] -#[derive(Copy, Clone)] -struct SixtyFourBitPointer { - #[cfg(target_endian = "big")] - #[cfg(target_pointer_width = "32")] - _padding: u32, - - pointer: *mut c_void, - - #[cfg(target_endian = "little")] - #[cfg(target_pointer_width = "32")] - _padding: u32, -} - -/// A vector of `Event`s, plus context for interpreting them. -#[cfg(feature = "alloc")] -pub struct EventVec { - events: Vec, -} - -#[cfg(feature = "alloc")] -impl EventVec { - /// Constructs an `EventVec` from raw pointer, length, and capacity. - /// - /// # Safety - /// - /// This function calls [`Vec::from_raw_parts`] with its arguments. - /// - /// [`Vec::from_raw_parts`]: https://doc.rust-lang.org/stable/std/vec/struct.Vec.html#method.from_raw_parts - #[inline] - pub unsafe fn from_raw_parts(ptr: *mut Event, len: usize, capacity: usize) -> Self { - Self { - events: Vec::from_raw_parts(ptr, len, capacity), - } - } - - /// Constructs an `EventVec` with memory for `capacity` `Event`s. - #[inline] - pub fn with_capacity(capacity: usize) -> Self { - Self { - events: Vec::with_capacity(capacity), - } - } - - /// Returns the current `Event` capacity of this `EventVec`. - #[inline] - pub fn capacity(&self) -> usize { - self.events.capacity() - } - - /// Reserves enough memory for at least `additional` more `Event`s. - #[inline] - pub fn reserve(&mut self, additional: usize) { - self.events.reserve(additional); - } - - /// Reserves enough memory for exactly `additional` more `Event`s. - #[inline] - pub fn reserve_exact(&mut self, additional: usize) { - self.events.reserve_exact(additional); - } - - /// Clears all the `Events` out of this `EventVec`. - #[inline] - pub fn clear(&mut self) { - self.events.clear(); - } - - /// Shrinks the capacity of this `EventVec` as much as possible. - #[inline] - pub fn shrink_to_fit(&mut self) { - self.events.shrink_to_fit(); - } - - /// Returns an iterator over the `Event`s in this `EventVec`. - #[inline] - pub fn iter(&self) -> Iter<'_> { - Iter { - iter: self.events.iter().copied(), - } - } - - /// Returns the number of `Event`s logically contained in this `EventVec`. - #[inline] - pub fn len(&mut self) -> usize { - self.events.len() - } - - /// Tests whether this `EventVec` is logically empty. - #[inline] - pub fn is_empty(&mut self) -> bool { - self.events.is_empty() - } -} - -#[cfg(feature = "alloc")] -impl<'a> IntoIterator for &'a EventVec { - type IntoIter = Iter<'a>; - type Item = Event; - - #[inline] - fn into_iter(self) -> Self::IntoIter { - self.iter() - } -} - -#[test] -fn test_epoll_layouts() { - check_renamed_type!(Event, epoll_event); - check_renamed_type!(Event, epoll_event); - check_renamed_struct_renamed_field!(Event, epoll_event, flags, events); - check_renamed_struct_renamed_field!(Event, epoll_event, data, u64); -} diff --git a/src/backend/libc/event/poll_fd.rs b/src/backend/libc/event/poll_fd.rs index 26df15b2c..ba1bab0b5 100644 --- a/src/backend/libc/event/poll_fd.rs +++ b/src/backend/libc/event/poll_fd.rs @@ -63,8 +63,8 @@ pub struct PollFd<'fd> { } impl<'fd> fmt::Debug for PollFd<'fd> { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt.debug_struct("PollFd") + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("PollFd") .field("fd", &self.pollfd.fd) .field("events", &self.pollfd.events) .field("revents", &self.pollfd.revents) diff --git a/src/backend/libc/event/syscalls.rs b/src/backend/libc/event/syscalls.rs index 725ec8250..626ddb524 100644 --- a/src/backend/libc/event/syscalls.rs +++ b/src/backend/libc/event/syscalls.rs @@ -1,27 +1,27 @@ //! libc syscalls supporting `rustix::event`. use crate::backend::c; -use crate::backend::conv::ret_c_int; -#[cfg(any(apple, netbsdlike, target_os = "dragonfly", target_os = "solaris"))] -use crate::backend::conv::ret_owned_fd; -use crate::event::PollFd; -#[cfg(any(linux_kernel, bsd, solarish, target_os = "espidf"))] -use crate::fd::OwnedFd; -use crate::io; -#[cfg(any(bsd, solarish))] -use {crate::backend::conv::borrowed_fd, crate::fd::BorrowedFd, core::mem::MaybeUninit}; +#[cfg(any(linux_kernel, target_os = "redox"))] +use crate::backend::conv::ret_u32; +use crate::backend::conv::{borrowed_fd, ret, ret_c_int, ret_owned_fd}; #[cfg(solarish)] -use { - crate::backend::conv::ret, crate::event::port::Event, crate::utils::as_mut_ptr, - core::ptr::null_mut, -}; +use crate::event::port::Event; #[cfg(any( linux_kernel, target_os = "freebsd", target_os = "illumos", target_os = "espidf" ))] -use {crate::backend::conv::ret_owned_fd, crate::event::EventfdFlags}; +use crate::event::EventfdFlags; +use crate::event::PollFd; +use crate::fd::{BorrowedFd, OwnedFd}; +use crate::io; +#[cfg(solarish)] +use crate::utils::as_mut_ptr; +#[cfg(any(linux_kernel, target_os = "redox"))] +use crate::utils::as_ptr; +use core::mem::MaybeUninit; +use core::ptr::null_mut; #[cfg(all(feature = "alloc", bsd))] use {crate::event::kqueue::Event, crate::utils::as_ptr, core::ptr::null}; @@ -184,8 +184,84 @@ pub(crate) fn port_send( #[cfg(not(any(windows, target_os = "redox", target_os = "wasi")))] pub(crate) fn pause() { - let r = unsafe { libc::pause() }; + let r = unsafe { c::pause() }; let errno = libc_errno::errno().0; debug_assert_eq!(r, -1); - debug_assert_eq!(errno, libc::EINTR); + debug_assert_eq!(errno, c::EINTR); +} + +#[inline] +#[cfg(any(linux_kernel, target_os = "redox"))] +pub(crate) fn epoll_create(flags: super::epoll::CreateFlags) -> io::Result { + unsafe { ret_owned_fd(c::epoll_create1(bitflags_bits!(flags))) } +} + +#[inline] +#[cfg(any(linux_kernel, target_os = "redox"))] +pub(crate) fn epoll_add( + epoll: BorrowedFd<'_>, + source: BorrowedFd<'_>, + event: &crate::event::epoll::Event, +) -> io::Result<()> { + // We use our own `Event` struct instead of libc's because + // ours preserves pointer provenance instead of just using a `u64`, + // and we have tests elsewhere for layout equivalence. + unsafe { + ret(c::epoll_ctl( + borrowed_fd(epoll), + c::EPOLL_CTL_ADD, + borrowed_fd(source), + // The event is read-only even though libc has a non-const pointer. + as_ptr(event) as *mut c::epoll_event, + )) + } +} + +#[inline] +#[cfg(any(linux_kernel, target_os = "redox"))] +pub(crate) fn epoll_mod( + epoll: BorrowedFd<'_>, + source: BorrowedFd<'_>, + event: &crate::event::epoll::Event, +) -> io::Result<()> { + unsafe { + ret(c::epoll_ctl( + borrowed_fd(epoll), + c::EPOLL_CTL_MOD, + borrowed_fd(source), + // The event is read-only even though libc has a non-const pointer. + as_ptr(event) as *mut c::epoll_event, + )) + } +} + +#[inline] +#[cfg(any(linux_kernel, target_os = "redox"))] +pub(crate) fn epoll_del(epoll: BorrowedFd<'_>, source: BorrowedFd<'_>) -> io::Result<()> { + unsafe { + ret(c::epoll_ctl( + borrowed_fd(epoll), + c::EPOLL_CTL_DEL, + borrowed_fd(source), + null_mut(), + )) + } +} + +#[inline] +#[cfg(any(linux_kernel, target_os = "redox"))] +pub(crate) fn epoll_wait( + epoll: BorrowedFd<'_>, + events: &mut [MaybeUninit], + timeout: c::c_int, +) -> io::Result { + unsafe { + ret_u32(c::epoll_wait( + borrowed_fd(epoll), + events.as_mut_ptr().cast::(), + events.len().try_into().unwrap_or(i32::MAX), + timeout, + )) + .map(|i| i.try_into().unwrap_or(usize::MAX)) + } } diff --git a/src/backend/libc/fs/dir.rs b/src/backend/libc/fs/dir.rs index 6c8274323..4b4676872 100644 --- a/src/backend/libc/fs/dir.rs +++ b/src/backend/libc/fs/dir.rs @@ -220,7 +220,7 @@ impl Dir { /// `fchdir(self)` #[cfg(feature = "process")] #[cfg(not(any(target_os = "fuchsia", target_os = "vita", target_os = "wasi")))] - #[cfg_attr(doc_cfg, doc(cfg(feature = "process")))] + #[cfg_attr(docsrs, doc(cfg(feature = "process")))] #[inline] pub fn chdir(&self) -> io::Result<()> { fchdir(unsafe { BorrowedFd::borrow_raw(c::dirfd(self.libc_dir.as_ptr())) }) diff --git a/src/backend/libc/fs/inotify.rs b/src/backend/libc/fs/inotify.rs index 2044bd945..c89702564 100644 --- a/src/backend/libc/fs/inotify.rs +++ b/src/backend/libc/fs/inotify.rs @@ -1,15 +1,12 @@ //! inotify support for working with inotifies use crate::backend::c; -use crate::backend::conv::{borrowed_fd, c_str, ret, ret_c_int, ret_owned_fd}; -use crate::fd::{BorrowedFd, OwnedFd}; -use crate::io; use bitflags::bitflags; bitflags! { - /// `IN_*` for use with [`inotify_init`]. + /// `IN_*` for use with [`inotify::init`]. /// - /// [`inotify_init`]: crate::fs::inotify::inotify_init + /// [`inotify::init`]: crate::fs::inotify::init #[repr(transparent)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct CreateFlags: u32 { @@ -24,9 +21,9 @@ bitflags! { } bitflags! { - /// `IN*` for use with [`inotify_add_watch`]. + /// `IN*` for use with [`inotify::add_watch`]. /// - /// [`inotify_add_watch`]: crate::fs::inotify::inotify_add_watch + /// [`inotify::add_watch`]: crate::fs::inotify::add_watch #[repr(transparent)] #[derive(Default, Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct WatchFlags: u32 { @@ -80,52 +77,48 @@ bitflags! { } } -/// `inotify_init1(flags)`—Creates a new inotify object. -/// -/// Use the [`CreateFlags::CLOEXEC`] flag to prevent the resulting file -/// descriptor from being implicitly passed across `exec` boundaries. -#[doc(alias = "inotify_init1")] -pub fn inotify_init(flags: CreateFlags) -> io::Result { - // SAFETY: `inotify_init1` has no safety preconditions. - unsafe { ret_owned_fd(c::inotify_init1(bitflags_bits!(flags))) } -} +bitflags! { + /// `IN*` for use with [`inotify::Reader`]. + /// + /// [`inotify::Reader`]: crate::fs::inotify::Reader + #[repr(transparent)] + #[derive(Default, Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct ReadFlags: u32 { + /// `IN_ACCESS` + const ACCESS = c::IN_ACCESS; + /// `IN_ATTRIB` + const ATTRIB = c::IN_ATTRIB; + /// `IN_CLOSE_NOWRITE` + const CLOSE_NOWRITE = c::IN_CLOSE_NOWRITE; + /// `IN_CLOSE_WRITE` + const CLOSE_WRITE = c::IN_CLOSE_WRITE; + /// `IN_CREATE` + const CREATE = c::IN_CREATE; + /// `IN_DELETE` + const DELETE = c::IN_DELETE; + /// `IN_DELETE_SELF` + const DELETE_SELF = c::IN_DELETE_SELF; + /// `IN_MODIFY` + const MODIFY = c::IN_MODIFY; + /// `IN_MOVE_SELF` + const MOVE_SELF = c::IN_MOVE_SELF; + /// `IN_MOVED_FROM` + const MOVED_FROM = c::IN_MOVED_FROM; + /// `IN_MOVED_TO` + const MOVED_TO = c::IN_MOVED_TO; + /// `IN_OPEN` + const OPEN = c::IN_OPEN; -/// `inotify_add_watch(self, path, flags)`—Adds a watch to inotify. -/// -/// This registers or updates a watch for the filesystem path `path` and -/// returns a watch descriptor corresponding to this watch. -/// -/// Note: Due to the existence of hardlinks, providing two different paths to -/// this method may result in it returning the same watch descriptor. An -/// application should keep track of this externally to avoid logic errors. -pub fn inotify_add_watch( - inot: BorrowedFd<'_>, - path: P, - flags: WatchFlags, -) -> io::Result { - path.into_with_c_str(|path| { - // SAFETY: The fd and path we are passing is guaranteed valid by the - // type system. - unsafe { - ret_c_int(c::inotify_add_watch( - borrowed_fd(inot), - c_str(path), - flags.bits(), - )) - } - }) -} + /// `IN_IGNORED` + const IGNORED = c::IN_IGNORED; + /// `IN_ISDIR` + const ISDIR = c::IN_ISDIR; + /// `IN_Q_OVERFLOW` + const QUEUE_OVERFLOW = c::IN_Q_OVERFLOW; + /// `IN_UNMOUNT` + const UNMOUNT = c::IN_UNMOUNT; -/// `inotify_rm_watch(self, wd)`—Removes a watch from this inotify. -/// -/// The watch descriptor provided should have previously been returned by -/// [`inotify_add_watch`] and not previously have been removed. -#[doc(alias = "inotify_rm_watch")] -pub fn inotify_remove_watch(inot: BorrowedFd<'_>, wd: i32) -> io::Result<()> { - // Android's `inotify_rm_watch` takes `u32` despite that - // `inotify_add_watch` expects a `i32`. - #[cfg(target_os = "android")] - let wd = wd as u32; - // SAFETY: The fd is valid and closing an arbitrary wd is valid. - unsafe { ret(c::inotify_rm_watch(borrowed_fd(inot), wd)) } + /// + const _ = !0; + } } diff --git a/src/backend/libc/fs/syscalls.rs b/src/backend/libc/fs/syscalls.rs index b87f1ccde..205dc0094 100644 --- a/src/backend/libc/fs/syscalls.rs +++ b/src/backend/libc/fs/syscalls.rs @@ -80,7 +80,7 @@ use { crate::backend::conv::nonnegative_ret, crate::fs::{copyfile_state_t, CloneFlags, CopyfileFlags}, }; -#[cfg(any(apple, linux_kernel))] +#[cfg(any(apple, linux_kernel, target_os = "hurd"))] use {crate::fs::XattrFlags, core::mem::size_of, core::ptr::null_mut}; #[cfg(linux_kernel)] use { @@ -348,7 +348,7 @@ pub(crate) fn linkat( new_path: &CStr, flags: AtFlags, ) -> io::Result<()> { - // macOS <= 10.9 lacks `linkat`. + // macOS ≤ 10.9 lacks `linkat`. #[cfg(target_os = "macos")] unsafe { weak! { @@ -405,7 +405,7 @@ pub(crate) fn unlink(path: &CStr) -> io::Result<()> { #[cfg(not(any(target_os = "espidf", target_os = "redox")))] pub(crate) fn unlinkat(dirfd: BorrowedFd<'_>, path: &CStr, flags: AtFlags) -> io::Result<()> { - // macOS <= 10.9 lacks `unlinkat`. + // macOS ≤ 10.9 lacks `unlinkat`. #[cfg(target_os = "macos")] unsafe { weak! { @@ -458,7 +458,7 @@ pub(crate) fn renameat( new_dirfd: BorrowedFd<'_>, new_path: &CStr, ) -> io::Result<()> { - // macOS <= 10.9 lacks `renameat`. + // macOS ≤ 10.9 lacks `renameat`. #[cfg(target_os = "macos")] unsafe { weak! { @@ -744,7 +744,7 @@ pub(crate) fn accessat( access: Access, flags: AtFlags, ) -> io::Result<()> { - // macOS <= 10.9 lacks `faccessat`. + // macOS ≤ 10.9 lacks `faccessat`. #[cfg(target_os = "macos")] unsafe { weak! { @@ -2239,7 +2239,7 @@ struct Attrlist { forkattr: Attrgroup, } -#[cfg(any(apple, linux_kernel))] +#[cfg(any(apple, linux_kernel, target_os = "hurd"))] pub(crate) fn getxattr(path: &CStr, name: &CStr, value: &mut [u8]) -> io::Result { let value_ptr = value.as_mut_ptr(); @@ -2255,8 +2255,8 @@ pub(crate) fn getxattr(path: &CStr, name: &CStr, value: &mut [u8]) -> io::Result #[cfg(apple)] { - // Passing an empty to slice to getxattr leads to ERANGE on macOS. Pass null - // instead. + // Passing an empty to slice to `getxattr` leads to `ERANGE` on macOS. + // Pass null instead. let ptr = if value.is_empty() { core::ptr::null_mut() } else { @@ -2275,7 +2275,7 @@ pub(crate) fn getxattr(path: &CStr, name: &CStr, value: &mut [u8]) -> io::Result } } -#[cfg(any(apple, linux_kernel))] +#[cfg(any(apple, linux_kernel, target_os = "hurd"))] pub(crate) fn lgetxattr(path: &CStr, name: &CStr, value: &mut [u8]) -> io::Result { let value_ptr = value.as_mut_ptr(); @@ -2291,8 +2291,8 @@ pub(crate) fn lgetxattr(path: &CStr, name: &CStr, value: &mut [u8]) -> io::Resul #[cfg(apple)] { - // Passing an empty to slice to getxattr leads to ERANGE on macOS. Pass null - // instead. + // Passing an empty to slice to `getxattr` leads to `ERANGE` on macOS. + // Pass null instead. let ptr = if value.is_empty() { core::ptr::null_mut() } else { @@ -2312,7 +2312,7 @@ pub(crate) fn lgetxattr(path: &CStr, name: &CStr, value: &mut [u8]) -> io::Resul } } -#[cfg(any(apple, linux_kernel))] +#[cfg(any(apple, linux_kernel, target_os = "hurd"))] pub(crate) fn fgetxattr(fd: BorrowedFd<'_>, name: &CStr, value: &mut [u8]) -> io::Result { let value_ptr = value.as_mut_ptr(); @@ -2328,8 +2328,8 @@ pub(crate) fn fgetxattr(fd: BorrowedFd<'_>, name: &CStr, value: &mut [u8]) -> io #[cfg(apple)] { - // Passing an empty to slice to getxattr leads to ERANGE on macOS. Pass null - // instead. + // Passing an empty to slice to `getxattr` leads to `ERANGE` on macOS. + // Pass null instead. let ptr = if value.is_empty() { core::ptr::null_mut() } else { @@ -2348,7 +2348,7 @@ pub(crate) fn fgetxattr(fd: BorrowedFd<'_>, name: &CStr, value: &mut [u8]) -> io } } -#[cfg(any(apple, linux_kernel))] +#[cfg(any(apple, linux_kernel, target_os = "hurd"))] pub(crate) fn setxattr( path: &CStr, name: &CStr, @@ -2379,7 +2379,7 @@ pub(crate) fn setxattr( } } -#[cfg(any(apple, linux_kernel))] +#[cfg(any(apple, linux_kernel, target_os = "hurd"))] pub(crate) fn lsetxattr( path: &CStr, name: &CStr, @@ -2410,7 +2410,7 @@ pub(crate) fn lsetxattr( } } -#[cfg(any(apple, linux_kernel))] +#[cfg(any(apple, linux_kernel, target_os = "hurd"))] pub(crate) fn fsetxattr( fd: BorrowedFd<'_>, name: &CStr, @@ -2441,7 +2441,7 @@ pub(crate) fn fsetxattr( } } -#[cfg(any(apple, linux_kernel))] +#[cfg(any(apple, linux_kernel, target_os = "hurd"))] pub(crate) fn listxattr(path: &CStr, list: &mut [c::c_char]) -> io::Result { #[cfg(not(apple))] unsafe { @@ -2459,7 +2459,7 @@ pub(crate) fn listxattr(path: &CStr, list: &mut [c::c_char]) -> io::Result io::Result { #[cfg(not(apple))] unsafe { @@ -2477,7 +2477,7 @@ pub(crate) fn llistxattr(path: &CStr, list: &mut [c::c_char]) -> io::Result, list: &mut [c::c_char]) -> io::Result { let fd = borrowed_fd(fd); @@ -2492,7 +2492,7 @@ pub(crate) fn flistxattr(fd: BorrowedFd<'_>, list: &mut [c::c_char]) -> io::Resu } } -#[cfg(any(apple, linux_kernel))] +#[cfg(any(apple, linux_kernel, target_os = "hurd"))] pub(crate) fn removexattr(path: &CStr, name: &CStr) -> io::Result<()> { #[cfg(not(apple))] unsafe { @@ -2505,7 +2505,7 @@ pub(crate) fn removexattr(path: &CStr, name: &CStr) -> io::Result<()> { } } -#[cfg(any(apple, linux_kernel))] +#[cfg(any(apple, linux_kernel, target_os = "hurd"))] pub(crate) fn lremovexattr(path: &CStr, name: &CStr) -> io::Result<()> { #[cfg(not(apple))] unsafe { @@ -2522,7 +2522,7 @@ pub(crate) fn lremovexattr(path: &CStr, name: &CStr) -> io::Result<()> { } } -#[cfg(any(apple, linux_kernel))] +#[cfg(any(apple, linux_kernel, target_os = "hurd"))] pub(crate) fn fremovexattr(fd: BorrowedFd<'_>, name: &CStr) -> io::Result<()> { let fd = borrowed_fd(fd); @@ -2549,3 +2549,39 @@ fn test_sizes() { #[cfg(fix_y2038)] assert!(core::mem::size_of::<[c::timespec; 2]>() < core::mem::size_of::()); } + +#[inline] +#[cfg(linux_kernel)] +pub(crate) fn inotify_init1(flags: super::inotify::CreateFlags) -> io::Result { + // SAFETY: `inotify_init1` has no safety preconditions. + unsafe { ret_owned_fd(c::inotify_init1(bitflags_bits!(flags))) } +} + +#[inline] +#[cfg(linux_kernel)] +pub(crate) fn inotify_add_watch( + inot: BorrowedFd<'_>, + path: &CStr, + flags: super::inotify::WatchFlags, +) -> io::Result { + // SAFETY: The fd and path we are passing is guaranteed valid by the + // type system. + unsafe { + ret_c_int(c::inotify_add_watch( + borrowed_fd(inot), + c_str(path), + flags.bits(), + )) + } +} + +#[inline] +#[cfg(linux_kernel)] +pub(crate) fn inotify_rm_watch(inot: BorrowedFd<'_>, wd: i32) -> io::Result<()> { + // Android's `inotify_rm_watch` takes `u32` despite that + // `inotify_add_watch` expects a `i32`. + #[cfg(target_os = "android")] + let wd = wd as u32; + // SAFETY: The fd is valid and closing an arbitrary wd is valid. + unsafe { ret(c::inotify_rm_watch(borrowed_fd(inot), wd)) } +} diff --git a/src/backend/libc/fs/types.rs b/src/backend/libc/fs/types.rs index 0eb122fb0..922d20e3f 100644 --- a/src/backend/libc/fs/types.rs +++ b/src/backend/libc/fs/types.rs @@ -162,7 +162,7 @@ impl Mode { /// `Mode`. #[inline] pub const fn from_raw_mode(st_mode: RawMode) -> Self { - Self::from_bits_truncate(st_mode) + Self::from_bits_truncate(st_mode & !c::S_IFMT as RawMode) } /// Construct an `st_mode` value from a `Mode`. diff --git a/src/backend/libc/io/errno.rs b/src/backend/libc/io/errno.rs index fb604a882..70613c41d 100644 --- a/src/backend/libc/io/errno.rs +++ b/src/backend/libc/io/errno.rs @@ -31,7 +31,6 @@ use libc_errno::errno; /// [DragonFly BSD]: https://man.dragonflybsd.org/?command=errno§ion=2 /// [illumos]: https://illumos.org/man/3C/errno /// [glibc]: https://www.gnu.org/software/libc/manual/html_node/Error-Codes.html -/// [`std::io::Error`]: Result #[repr(transparent)] #[doc(alias = "errno")] #[derive(Eq, PartialEq, Hash, Copy, Clone)] diff --git a/src/backend/libc/io/syscalls.rs b/src/backend/libc/io/syscalls.rs index 9c8638f75..270aefd32 100644 --- a/src/backend/libc/io/syscalls.rs +++ b/src/backend/libc/io/syscalls.rs @@ -201,6 +201,11 @@ pub(crate) unsafe fn close(raw_fd: RawFd) { let _ = c::close(raw_fd as c::c_int); } +#[cfg(feature = "try_close")] +pub(crate) unsafe fn try_close(raw_fd: RawFd) -> io::Result<()> { + ret(c::close(raw_fd as c::c_int)) +} + #[inline] pub(crate) unsafe fn ioctl( fd: BorrowedFd<'_>, diff --git a/src/backend/libc/io/windows_syscalls.rs b/src/backend/libc/io/windows_syscalls.rs index 049221d2f..6e451a9f1 100644 --- a/src/backend/libc/io/windows_syscalls.rs +++ b/src/backend/libc/io/windows_syscalls.rs @@ -1,6 +1,8 @@ //! Windows system calls in the `io` module. use crate::backend::c; +#[cfg(feature = "try_close")] +use crate::backend::conv::ret; use crate::backend::conv::{borrowed_fd, ret_c_int}; use crate::backend::fd::LibcFd; use crate::fd::{BorrowedFd, RawFd}; @@ -11,6 +13,11 @@ pub(crate) unsafe fn close(raw_fd: RawFd) { let _ = c::close(raw_fd as LibcFd); } +#[cfg(feature = "try_close")] +pub(crate) unsafe fn try_close(raw_fd: RawFd) -> io::Result<()> { + ret(c::close(raw_fd as LibcFd)) +} + #[inline] pub(crate) unsafe fn ioctl( fd: BorrowedFd<'_>, diff --git a/src/backend/libc/io_uring/syscalls.rs b/src/backend/libc/io_uring/syscalls.rs index 8e8182401..8bf1e987a 100644 --- a/src/backend/libc/io_uring/syscalls.rs +++ b/src/backend/libc/io_uring/syscalls.rs @@ -4,7 +4,7 @@ use crate::backend::c; use crate::backend::conv::{borrowed_fd, ret_owned_fd, ret_u32}; use crate::fd::{BorrowedFd, OwnedFd}; use crate::io; -use crate::io_uring::{io_uring_params, IoringEnterFlags, IoringRegisterOp}; +use crate::io_uring::{io_uring_params, IoringEnterFlags, IoringRegisterFlags, IoringRegisterOp}; #[inline] pub(crate) fn io_uring_setup(entries: u32, params: &mut io_uring_params) -> io::Result { @@ -40,6 +40,30 @@ pub(crate) unsafe fn io_uring_register( )) } +#[inline] +pub(crate) unsafe fn io_uring_register_with( + fd: BorrowedFd<'_>, + opcode: IoringRegisterOp, + flags: IoringRegisterFlags, + arg: *const c::c_void, + nr_args: u32, +) -> io::Result { + syscall! { + fn io_uring_register( + fd: c::c_uint, + opcode: c::c_uint, + arg: *const c::c_void, + nr_args: c::c_uint + ) via SYS_io_uring_register -> c::c_int + } + ret_u32(io_uring_register( + borrowed_fd(fd) as _, + (opcode as u32) | bitflags_bits!(flags), + arg, + nr_args, + )) +} + #[inline] pub(crate) unsafe fn io_uring_enter( fd: BorrowedFd<'_>, diff --git a/src/backend/libc/mm/types.rs b/src/backend/libc/mm/types.rs index a4aa3e232..46150982e 100644 --- a/src/backend/libc/mm/types.rs +++ b/src/backend/libc/mm/types.rs @@ -85,6 +85,7 @@ bitflags! { target_os = "emscripten", target_os = "fuchsia", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "redox", )))] @@ -97,6 +98,7 @@ bitflags! { solarish, target_os = "aix", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "redox", )))] @@ -112,6 +114,7 @@ bitflags! { target_os = "emscripten", target_os = "fuchsia", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "redox", )))] @@ -122,6 +125,7 @@ bitflags! { solarish, target_os = "aix", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "redox", )))] @@ -132,6 +136,7 @@ bitflags! { solarish, target_os = "aix", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "redox", )))] @@ -145,6 +150,7 @@ bitflags! { target_os = "emscripten", target_os = "fuchsia", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "redox", )))] @@ -158,6 +164,7 @@ bitflags! { target_os = "emscripten", target_os = "fuchsia", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "redox", )))] @@ -168,6 +175,7 @@ bitflags! { solarish, target_os = "aix", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "redox", )))] @@ -179,6 +187,7 @@ bitflags! { #[cfg(not(any( freebsdlike, target_os = "aix", + target_os = "hurd", target_os = "nto", target_os = "redox", )))] @@ -192,6 +201,7 @@ bitflags! { solarish, target_os = "aix", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "redox", )))] @@ -201,9 +211,8 @@ bitflags! { apple, solarish, target_os = "aix", - target_os = "dragonfly", target_os = "haiku", - target_os = "netbsd", + target_os = "hurd", target_os = "redox", )))] const STACK = bitcast!(c::MAP_STACK); @@ -219,6 +228,7 @@ bitflags! { target_os = "emscripten", target_os = "fuchsia", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "redox", all( @@ -230,6 +240,9 @@ bitflags! { /// `MAP_UNINITIALIZED` #[cfg(any())] const UNINITIALIZED = bitcast!(c::MAP_UNINITIALIZED); + /// `MAP_DROPPABLE` + #[cfg(linux_kernel)] + const DROPPABLE = bitcast!(c::MAP_DROPPABLE); /// const _ = !0; @@ -334,7 +347,12 @@ pub enum Advice { WillNeed = bitcast!(c::MADV_WILLNEED), /// `POSIX_MADV_DONTNEED` - #[cfg(not(any(target_os = "android", target_os = "emscripten", target_os = "haiku")))] + #[cfg(not(any( + target_os = "android", + target_os = "emscripten", + target_os = "haiku", + target_os = "hurd", + )))] DontNeed = bitcast!(c::POSIX_MADV_DONTNEED), /// `POSIX_MADV_DONTNEED` diff --git a/src/backend/libc/mount/types.rs b/src/backend/libc/mount/types.rs index 10236866c..4df8fbc35 100644 --- a/src/backend/libc/mount/types.rs +++ b/src/backend/libc/mount/types.rs @@ -227,9 +227,8 @@ bitflags! { /// `MOVE_MOUNT__MASK` const MOVE_MOUNT_SET_GROUP = 0x0000_0100; - // TODO: add when Linux 6.5 is released - // /// `MOVE_MOUNT_BENEATH` - // const MOVE_MOUNT_BENEATH = 0x0000_0200; + /// `MOVE_MOUNT_BENEATH` (since Linux 6.5) + const MOVE_MOUNT_BENEATH = 0x0000_0200; /// `MOVE_MOUNT__MASK` const MOVE_MOUNT__MASK = 0x0000_0377; diff --git a/src/backend/libc/net/addr.rs b/src/backend/libc/net/addr.rs index 719a549b1..bef45e44e 100644 --- a/src/backend/libc/net/addr.rs +++ b/src/backend/libc/net/addr.rs @@ -76,7 +76,13 @@ impl SocketAddrUnix { fn init() -> c::sockaddr_un { c::sockaddr_un { - #[cfg(any(bsd, target_os = "aix", target_os = "haiku", target_os = "nto"))] + #[cfg(any( + bsd, + target_os = "aix", + target_os = "haiku", + target_os = "nto", + target_os = "hurd", + ))] sun_len: 0, #[cfg(target_os = "vita")] ss_len: 0, @@ -188,16 +194,16 @@ impl Hash for SocketAddrUnix { #[cfg(unix)] impl fmt::Debug for SocketAddrUnix { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if let Some(path) = self.path() { - path.fmt(fmt) + path.fmt(f) } else { #[cfg(linux_kernel)] if let Some(name) = self.abstract_name() { - return name.fmt(fmt); + return name.fmt(f); } - "(unnamed)".fmt(fmt) + "(unnamed)".fmt(f) } } } @@ -210,7 +216,13 @@ pub type SocketAddrStorage = c::sockaddr_storage; #[inline] pub(crate) fn offsetof_sun_path() -> usize { let z = c::sockaddr_un { - #[cfg(any(bsd, target_os = "aix", target_os = "haiku", target_os = "nto"))] + #[cfg(any( + bsd, + target_os = "aix", + target_os = "haiku", + target_os = "hurd", + target_os = "nto", + ))] sun_len: 0_u8, #[cfg(target_os = "vita")] ss_len: 0, @@ -219,6 +231,7 @@ pub(crate) fn offsetof_sun_path() -> usize { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita" ))] @@ -228,6 +241,7 @@ pub(crate) fn offsetof_sun_path() -> usize { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita" )))] diff --git a/src/backend/libc/net/ext.rs b/src/backend/libc/net/ext.rs index 2e11c051d..efd2b31c8 100644 --- a/src/backend/libc/net/ext.rs +++ b/src/backend/libc/net/ext.rs @@ -83,6 +83,7 @@ pub(crate) const fn sockaddr_in6_new( target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita" ))] @@ -99,6 +100,7 @@ pub(crate) const fn sockaddr_in6_new( target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita" ))] diff --git a/src/backend/libc/net/netdevice.rs b/src/backend/libc/net/netdevice.rs index 2fbb55b81..6ba80eca7 100644 --- a/src/backend/libc/net/netdevice.rs +++ b/src/backend/libc/net/netdevice.rs @@ -1,3 +1,5 @@ +//! Wrappers for netdevice ioctls. + #![allow(unsafe_code)] #[cfg(feature = "alloc")] diff --git a/src/backend/libc/net/read_sockaddr.rs b/src/backend/libc/net/read_sockaddr.rs index 08939d4f8..d2c9b69e6 100644 --- a/src/backend/libc/net/read_sockaddr.rs +++ b/src/backend/libc/net/read_sockaddr.rs @@ -59,6 +59,7 @@ unsafe fn read_ss_family(storage: *const c::sockaddr_storage) -> u16 { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita" ))] @@ -68,6 +69,7 @@ unsafe fn read_ss_family(storage: *const c::sockaddr_storage) -> u16 { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita" ))] @@ -77,6 +79,7 @@ unsafe fn read_ss_family(storage: *const c::sockaddr_storage) -> u16 { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita" )))] @@ -185,7 +188,9 @@ pub(crate) unsafe fn read_sockaddr( return Err(io::Errno::INVAL); } debug_assert_eq!( - CStr::from_ptr(decode.sun_path.as_ptr()).to_bytes().len(), + CStr::from_ptr(decode.sun_path.as_ptr().cast()) + .to_bytes() + .len(), provided_len ); &decode.sun_path[..provided_len] diff --git a/src/backend/libc/net/send_recv.rs b/src/backend/libc/net/send_recv.rs index 5dc60ddcd..3dbe92598 100644 --- a/src/backend/libc/net/send_recv.rs +++ b/src/backend/libc/net/send_recv.rs @@ -2,7 +2,7 @@ use crate::backend::c; use bitflags::bitflags; bitflags! { - /// `MSG_*` flags for use with [`send`], [`send_to`], and related + /// `MSG_*` flags for use with [`send`], [`sendto`], and related /// functions. /// /// [`send`]: crate::net::send @@ -19,6 +19,7 @@ bitflags! { target_os = "espidf", target_os = "nto", target_os = "haiku", + target_os = "hurd", target_os = "vita", )))] const CONFIRM = bitcast!(c::MSG_CONFIRM); @@ -27,9 +28,15 @@ bitflags! { /// `MSG_DONTWAIT` #[cfg(not(windows))] const DONTWAIT = bitcast!(c::MSG_DONTWAIT); - /// `MSG_EOR` + /// Deprecated alias for [`EOR`]. + /// + /// [`EOR`]: Self::EOR #[cfg(not(windows))] + #[deprecated(note = "`rustix::net::SendFlags::EOT` is renamed to `rustix::net::SendFlags::EOR`.")] const EOT = bitcast!(c::MSG_EOR); + /// `MSG_EOR` + #[cfg(not(windows))] + const EOR = bitcast!(c::MSG_EOR); /// `MSG_MORE` #[cfg(not(any( bsd, @@ -37,6 +44,7 @@ bitflags! { windows, target_os = "aix", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", )))] @@ -84,6 +92,7 @@ bitflags! { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", )))] diff --git a/src/backend/libc/net/sockopt.rs b/src/backend/libc/net/sockopt.rs index 26c9df300..7824e69a1 100644 --- a/src/backend/libc/net/sockopt.rs +++ b/src/backend/libc/net/sockopt.rs @@ -369,6 +369,7 @@ pub(crate) fn get_socket_send_buffer_size(fd: BorrowedFd<'_>) -> io::Result, value: &str) -> io::Result< ))] #[inline] pub(crate) fn get_tcp_congestion(fd: BorrowedFd<'_>) -> io::Result { + const OPTLEN: c::socklen_t = 16; + let level = c::IPPROTO_TCP; let optname = c::TCP_CONGESTION; - const OPTLEN: c::socklen_t = 16; let mut value = MaybeUninit::<[MaybeUninit; OPTLEN as usize]>::uninit(); let mut optlen = OPTLEN; getsockopt_raw(fd, level, optname, &mut value, &mut optlen)?; diff --git a/src/backend/libc/net/syscalls.rs b/src/backend/libc/net/syscalls.rs index 3fdb7766b..3013f9922 100644 --- a/src/backend/libc/net/syscalls.rs +++ b/src/backend/libc/net/syscalls.rs @@ -526,8 +526,8 @@ pub(crate) fn acceptfrom_with( } } -/// Darwin lacks `accept4`, but does have `accept`. We define -/// `SocketFlags` to have no flags, so we can discard it here. +/// Darwin lacks `accept4`, but does have `accept`. We define `SocketFlags` to +/// have no flags, so we can discard it here. #[cfg(any( apple, windows, @@ -541,8 +541,8 @@ pub(crate) fn accept_with(sockfd: BorrowedFd<'_>, _flags: SocketFlags) -> io::Re accept(sockfd) } -/// Darwin lacks `accept4`, but does have `accept`. We define -/// `SocketFlags` to have no flags, so we can discard it here. +/// Darwin lacks `accept4`, but does have `accept`. We define `SocketFlags` to +/// have no flags, so we can discard it here. #[cfg(any( apple, windows, diff --git a/src/backend/libc/net/write_sockaddr.rs b/src/backend/libc/net/write_sockaddr.rs index fdc5dbb13..29e69e141 100644 --- a/src/backend/libc/net/write_sockaddr.rs +++ b/src/backend/libc/net/write_sockaddr.rs @@ -32,6 +32,7 @@ pub(crate) fn encode_sockaddr_v4(v4: &SocketAddrV4) -> c::sockaddr_in { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", ))] @@ -62,6 +63,7 @@ pub(crate) fn encode_sockaddr_v6(v6: &SocketAddrV6) -> c::sockaddr_in6 { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita" ))] @@ -80,6 +82,7 @@ pub(crate) fn encode_sockaddr_v6(v6: &SocketAddrV6) -> c::sockaddr_in6 { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita" )))] diff --git a/src/backend/libc/param/auxv.rs b/src/backend/libc/param/auxv.rs index e53767583..55890c24a 100644 --- a/src/backend/libc/param/auxv.rs +++ b/src/backend/libc/param/auxv.rs @@ -46,10 +46,8 @@ pub(crate) fn linux_hwcap() -> (usize, usize) { ))] #[inline] pub(crate) fn linux_minsigstksz() -> usize { - // FIXME: reuse const from libc when available? - const AT_MINSIGSTKSZ: c::c_ulong = 51; if let Some(libc_getauxval) = getauxval.get() { - unsafe { libc_getauxval(AT_MINSIGSTKSZ) as usize } + unsafe { libc_getauxval(c::AT_MINSIGSTKSZ) as usize } } else { 0 } diff --git a/src/backend/libc/pipe/syscalls.rs b/src/backend/libc/pipe/syscalls.rs index cff932d55..b36a18141 100644 --- a/src/backend/libc/pipe/syscalls.rs +++ b/src/backend/libc/pipe/syscalls.rs @@ -54,7 +54,7 @@ pub(crate) fn pipe_with(flags: PipeFlags) -> io::Result<(OwnedFd, OwnedFd)> { #[cfg(linux_kernel)] #[inline] -pub fn splice( +pub(crate) fn splice( fd_in: BorrowedFd<'_>, off_in: Option<&mut u64>, fd_out: BorrowedFd<'_>, @@ -79,7 +79,7 @@ pub fn splice( #[cfg(linux_kernel)] #[inline] -pub unsafe fn vmsplice( +pub(crate) unsafe fn vmsplice( fd: BorrowedFd<'_>, bufs: &[IoSliceRaw<'_>], flags: SpliceFlags, @@ -94,7 +94,7 @@ pub unsafe fn vmsplice( #[cfg(linux_kernel)] #[inline] -pub fn tee( +pub(crate) fn tee( fd_in: BorrowedFd<'_>, fd_out: BorrowedFd<'_>, len: usize, diff --git a/src/backend/libc/pipe/types.rs b/src/backend/libc/pipe/types.rs index 78fc2fcad..19c3e982c 100644 --- a/src/backend/libc/pipe/types.rs +++ b/src/backend/libc/pipe/types.rs @@ -18,6 +18,7 @@ bitflags! { solarish, target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "openbsd", target_os = "redox", @@ -36,6 +37,10 @@ bitflags! { bitflags! { /// `SPLICE_F_*` constants for use with [`splice`], [`vmsplice`], and /// [`tee`]. + /// + /// [`splice`]: crate::pipe::splice + /// [`vmsplice`]: crate::pipe::splice + /// [`tee`]: crate::pipe::tee #[repr(transparent)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct SpliceFlags: c::c_uint { diff --git a/src/backend/libc/process/syscalls.rs b/src/backend/libc/process/syscalls.rs index fc00071de..efb5a77f0 100644 --- a/src/backend/libc/process/syscalls.rs +++ b/src/backend/libc/process/syscalls.rs @@ -5,7 +5,7 @@ use super::types::RawCpuSet; use crate::backend::c; #[cfg(not(any(target_os = "wasi", target_os = "fuchsia")))] use crate::backend::conv::borrowed_fd; -#[cfg(feature = "fs")] +#[cfg(any(target_os = "linux", feature = "fs"))] use crate::backend::conv::c_str; #[cfg(all(feature = "alloc", feature = "fs", not(target_os = "wasi")))] use crate::backend::conv::ret_discarded_char_ptr; @@ -28,7 +28,7 @@ use crate::backend::conv::{ret, ret_c_int}; use crate::fd::BorrowedFd; #[cfg(target_os = "linux")] use crate::fd::{AsRawFd, OwnedFd, RawFd}; -#[cfg(feature = "fs")] +#[cfg(any(target_os = "linux", feature = "fs"))] use crate::ffi::CStr; #[cfg(feature = "fs")] use crate::fs::Mode; @@ -380,7 +380,7 @@ pub(crate) fn prlimit(pid: Option, limit: Resource, new: Rlimit) -> io::Res } } -/// Convert a Rust [`Rlimit`] to a C `c::rlimit`. +/// Convert a C `c::rlimit` to a Rust `Rlimit`. #[cfg(not(any( target_os = "espidf", target_os = "fuchsia", @@ -402,7 +402,7 @@ fn rlimit_from_libc(lim: c::rlimit) -> Rlimit { Rlimit { current, maximum } } -/// Convert a C `c::rlimit` to a Rust `Rlimit`. +/// Convert a Rust [`Rlimit`] to a C `c::rlimit`. #[cfg(not(any( target_os = "espidf", target_os = "fuchsia", @@ -585,8 +585,8 @@ fn _waitid_pidfd(fd: BorrowedFd<'_>, options: WaitidOptions) -> io::Result) -> Option { let status = status.assume_init(); // `si_pid` is supposedly the better way to check that the struct has been - // filled, e.g. the Linux manpage says about the `WNOHANG` case “zero out - // the si_pid field before the call and check for a nonzero value”. + // filled, e.g. the Linux manual page says about the `WNOHANG` case “zero + // out the si_pid field before the call and check for a nonzero value”. // But e.g. NetBSD/OpenBSD don't have it exposed in the libc crate for now, // and some platforms don't have it at all. For simplicity, always check // `si_signo`. We have zero-initialized the whole struct, and all kernels @@ -725,6 +725,17 @@ pub(crate) fn pidfd_getfd( } } +#[cfg(target_os = "linux")] +pub(crate) fn pivot_root(new_root: &CStr, put_old: &CStr) -> io::Result<()> { + syscall! { + fn pivot_root( + new_root: *const c::c_char, + put_old: *const c::c_char + ) via SYS_pivot_root -> c::c_int + } + unsafe { ret(pivot_root(c_str(new_root), c_str(put_old))) } +} + #[cfg(all(feature = "alloc", not(target_os = "wasi")))] pub(crate) fn getgroups(buf: &mut [Gid]) -> io::Result { let len = buf.len().try_into().map_err(|_| io::Errno::NOMEM)?; diff --git a/src/backend/libc/shm/types.rs b/src/backend/libc/shm/types.rs index 6575ef523..59f19b38f 100644 --- a/src/backend/libc/shm/types.rs +++ b/src/backend/libc/shm/types.rs @@ -2,9 +2,9 @@ use crate::backend::c; use bitflags::bitflags; bitflags! { - /// `O_*` constants for use with [`shm_open`]. + /// `O_*` constants for use with [`shm::open`]. /// - /// [`shm_open`]: crate:shm::shm_open + /// [`shm::open`]: crate:shm::open #[repr(transparent)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct ShmOFlags: u32 { diff --git a/src/backend/libc/termios/syscalls.rs b/src/backend/libc/termios/syscalls.rs index a833aea8d..9eba5637f 100644 --- a/src/backend/libc/termios/syscalls.rs +++ b/src/backend/libc/termios/syscalls.rs @@ -117,7 +117,7 @@ pub(crate) fn tcgetpgrp(fd: BorrowedFd<'_>) -> io::Result { let pid = ret_pid_t(c::tcgetpgrp(borrowed_fd(fd)))?; // This doesn't appear to be documented, but on Linux, it appears - // `tcsetpgrp` can succceed and set the pid to 0 if we pass it a + // `tcsetpgrp` can succeed and set the pid to 0 if we pass it a // pseudo-terminal device fd. For now, translate it into `OPNOTSUPP`. #[cfg(linux_kernel)] if pid == 0 { diff --git a/src/backend/libc/thread/futex.rs b/src/backend/libc/thread/futex.rs index 44d96f0f6..cc8465fad 100644 --- a/src/backend/libc/thread/futex.rs +++ b/src/backend/libc/thread/futex.rs @@ -1,12 +1,12 @@ use crate::backend::c; bitflags::bitflags! { - /// `FUTEX_*` flags for use with [`futex`]. + /// `FUTEX_*` flags for use with the functions in [`futex`]. /// - /// [`futex`]: crate::thread::futex + /// [`futex`]: mod@crate::thread::futex #[repr(transparent)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] - pub struct FutexFlags: u32 { + pub struct Flags: u32 { /// `FUTEX_PRIVATE_FLAG` const PRIVATE = bitcast!(c::FUTEX_PRIVATE_FLAG); /// `FUTEX_CLOCK_REALTIME` @@ -14,9 +14,53 @@ bitflags::bitflags! { } } -/// `FUTEX_*` operations for use with [`futex`]. +/// `FUTEX_*` operations for use with the futex syscall wrappers. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[repr(u32)] +pub(crate) enum Operation { + /// `FUTEX_WAIT` + Wait = bitcast!(c::FUTEX_WAIT), + /// `FUTEX_WAKE` + Wake = bitcast!(c::FUTEX_WAKE), + /// `FUTEX_FD` + Fd = bitcast!(c::FUTEX_FD), + /// `FUTEX_REQUEUE` + Requeue = bitcast!(c::FUTEX_REQUEUE), + /// `FUTEX_CMP_REQUEUE` + CmpRequeue = bitcast!(c::FUTEX_CMP_REQUEUE), + /// `FUTEX_WAKE_OP` + WakeOp = bitcast!(c::FUTEX_WAKE_OP), + /// `FUTEX_LOCK_PI` + LockPi = bitcast!(c::FUTEX_LOCK_PI), + /// `FUTEX_UNLOCK_PI` + UnlockPi = bitcast!(c::FUTEX_UNLOCK_PI), + /// `FUTEX_TRYLOCK_PI` + TrylockPi = bitcast!(c::FUTEX_TRYLOCK_PI), + /// `FUTEX_WAIT_BITSET` + WaitBitset = bitcast!(c::FUTEX_WAIT_BITSET), + /// `FUTEX_WAKE_BITSET` + WakeBitset = bitcast!(c::FUTEX_WAKE_BITSET), + /// `FUTEX_WAIT_REQUEUE_PI` + WaitRequeuePi = bitcast!(c::FUTEX_WAIT_REQUEUE_PI), + /// `FUTEX_CMP_REQUEUE_PI` + CmpRequeuePi = bitcast!(c::FUTEX_CMP_REQUEUE_PI), + /// `FUTEX_LOCK_PI2` + LockPi2 = bitcast!(c::FUTEX_LOCK_PI2), +} + +/// `FUTEX_*` operations for use with the [`futex`] function. /// -/// [`futex`]: crate::thread::futex +/// [`futex`]: fn@crate::thread::futex +// TODO: Deprecate this now that we have a new typed API. +/* +#[deprecated( + since = "0.38.35", + note = " + The `futex` function and `FutexOperation` enum are deprecated. There are + individual functions available to perform futex operations with improved + type safety. See the `rustix::thread::futex` module." +)] +*/ #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[repr(u32)] pub enum FutexOperation { @@ -41,3 +85,9 @@ pub enum FutexOperation { /// `FUTEX_WAIT_BITSET` WaitBitset = bitcast!(c::FUTEX_WAIT_BITSET), } + +/// `FUTEX_WAITERS` +pub const WAITERS: u32 = linux_raw_sys::general::FUTEX_WAITERS; + +/// `FUTEX_OWNER_DIED` +pub const OWNER_DIED: u32 = linux_raw_sys::general::FUTEX_OWNER_DIED; diff --git a/src/backend/libc/thread/syscalls.rs b/src/backend/libc/thread/syscalls.rs index df2378690..27836ebe9 100644 --- a/src/backend/libc/thread/syscalls.rs +++ b/src/backend/libc/thread/syscalls.rs @@ -7,13 +7,21 @@ use crate::io; use crate::thread::{NanosleepRelativeResult, Timespec}; #[cfg(all(target_env = "gnu", fix_y2038))] use crate::timespec::LibcTimespec; +#[cfg(all( + linux_kernel, + target_pointer_width = "32", + not(any(target_arch = "aarch64", target_arch = "x86_64")) +))] +use crate::utils::option_as_ptr; use core::mem::MaybeUninit; #[cfg(linux_kernel)] +use core::sync::atomic::AtomicU32; +#[cfg(linux_kernel)] use { crate::backend::conv::{borrowed_fd, ret_c_int, ret_usize}, crate::fd::BorrowedFd, crate::pid::Pid, - crate::thread::{FutexFlags, FutexOperation}, + crate::thread::futex, crate::utils::as_mut_ptr, }; #[cfg(not(any( @@ -415,17 +423,27 @@ pub(crate) fn setresgid_thread( unsafe { ret(setresgid(rgid.as_raw(), egid.as_raw(), sgid.as_raw())) } } -// TODO: This could be de-multiplexed. +/// # Safety +/// +/// The raw pointers must point to valid aligned memory. #[cfg(linux_kernel)] -pub(crate) unsafe fn futex( - uaddr: *mut u32, - op: FutexOperation, - flags: FutexFlags, +pub(crate) unsafe fn futex_val2( + uaddr: *const AtomicU32, + op: super::futex::Operation, + flags: futex::Flags, val: u32, - utime: *const Timespec, - uaddr2: *mut u32, + val2: u32, + uaddr2: *const AtomicU32, val3: u32, ) -> io::Result { + // Pass `val2` in the least-significant bytes of the `timeout` argument. + // [“the kernel casts the timeout value first to unsigned long, then to + // uint32_t”], so we perform that exact conversion in reverse to create + // the pointer. + // + // [“the kernel casts the timeout value first to unsigned long, then to uint32_t”]: https://man7.org/linux/man-pages/man2/futex.2.html + let timeout = val2 as usize as *const Timespec; + #[cfg(all( target_pointer_width = "32", not(any(target_arch = "aarch64", target_arch = "x86_64")) @@ -437,11 +455,11 @@ pub(crate) unsafe fn futex( syscall! { fn futex_time64( - uaddr: *mut u32, + uaddr: *const AtomicU32, futex_op: c::c_int, val: u32, timeout: *const Timespec, - uaddr2: *mut u32, + uaddr2: *const AtomicU32, val3: u32 ) via SYS_futex_time64 -> c::ssize_t } @@ -450,7 +468,78 @@ pub(crate) unsafe fn futex( uaddr, op as i32 | flags.bits() as i32, val, - utime, + timeout, + uaddr2, + val3, + )) + } + + #[cfg(any( + target_pointer_width = "64", + target_arch = "aarch64", + target_arch = "x86_64" + ))] + { + syscall! { + fn futex( + uaddr: *const AtomicU32, + futex_op: c::c_int, + val: u32, + timeout: *const linux_raw_sys::general::__kernel_timespec, + uaddr2: *const AtomicU32, + val3: u32 + ) via SYS_futex -> c::c_long + } + + ret_usize(futex( + uaddr, + op as i32 | flags.bits() as i32, + val, + timeout.cast(), + uaddr2, + val3, + ) as isize) + } +} + +/// # Safety +/// +/// The raw pointers must point to valid aligned memory. +#[cfg(linux_kernel)] +pub(crate) unsafe fn futex_timeout( + uaddr: *const AtomicU32, + op: super::futex::Operation, + flags: futex::Flags, + val: u32, + timeout: *const Timespec, + uaddr2: *const AtomicU32, + val3: u32, +) -> io::Result { + #[cfg(all( + target_pointer_width = "32", + not(any(target_arch = "aarch64", target_arch = "x86_64")) + ))] + { + // TODO: Upstream this to the libc crate. + #[allow(non_upper_case_globals)] + const SYS_futex_time64: i32 = linux_raw_sys::general::__NR_futex_time64 as i32; + + syscall! { + fn futex_time64( + uaddr: *const AtomicU32, + futex_op: c::c_int, + val: u32, + timeout: *const Timespec, + uaddr2: *const AtomicU32, + val3: u32 + ) via SYS_futex_time64 -> c::ssize_t + } + + ret_usize(futex_time64( + uaddr, + op as i32 | flags.bits() as i32, + val, + timeout, uaddr2, val3, )) @@ -458,7 +547,7 @@ pub(crate) unsafe fn futex( // See the comments in `rustix_clock_gettime_via_syscall` about // emulation. if err == io::Errno::NOSYS { - futex_old(uaddr, op, flags, val, utime, uaddr2, val3) + futex_old_timespec(uaddr, op, flags, val, timeout, uaddr2, val3) } else { Err(err) } @@ -473,11 +562,11 @@ pub(crate) unsafe fn futex( { syscall! { fn futex( - uaddr: *mut u32, + uaddr: *const AtomicU32, futex_op: c::c_int, val: u32, timeout: *const linux_raw_sys::general::__kernel_timespec, - uaddr2: *mut u32, + uaddr2: *const AtomicU32, val3: u32 ) via SYS_futex -> c::c_long } @@ -486,47 +575,57 @@ pub(crate) unsafe fn futex( uaddr, op as i32 | flags.bits() as i32, val, - utime.cast(), + timeout.cast(), uaddr2, val3, ) as isize) } } +/// # Safety +/// +/// The raw pointers must point to valid aligned memory. #[cfg(linux_kernel)] #[cfg(all( target_pointer_width = "32", not(any(target_arch = "aarch64", target_arch = "x86_64")) ))] -unsafe fn futex_old( - uaddr: *mut u32, - op: FutexOperation, - flags: FutexFlags, +unsafe fn futex_old_timespec( + uaddr: *const AtomicU32, + op: super::futex::Operation, + flags: futex::Flags, val: u32, - utime: *const Timespec, - uaddr2: *mut u32, + timeout: *const Timespec, + uaddr2: *const AtomicU32, val3: u32, ) -> io::Result { syscall! { fn futex( - uaddr: *mut u32, + uaddr: *const AtomicU32, futex_op: c::c_int, val: u32, timeout: *const linux_raw_sys::general::__kernel_old_timespec, - uaddr2: *mut u32, + uaddr2: *const AtomicU32, val3: u32 ) via SYS_futex -> c::c_long } - let old_utime = linux_raw_sys::general::__kernel_old_timespec { - tv_sec: (*utime).tv_sec.try_into().map_err(|_| io::Errno::INVAL)?, - tv_nsec: (*utime).tv_nsec.try_into().map_err(|_| io::Errno::INVAL)?, + let old_timeout = if timeout.is_null() { + None + } else { + Some(linux_raw_sys::general::__kernel_old_timespec { + tv_sec: (*timeout).tv_sec.try_into().map_err(|_| io::Errno::INVAL)?, + tv_nsec: (*timeout) + .tv_nsec + .try_into() + .map_err(|_| io::Errno::INVAL)?, + }) }; ret_usize(futex( uaddr, op as i32 | flags.bits() as i32, val, - &old_utime, + option_as_ptr(old_timeout.as_ref()), uaddr2, val3, ) as isize) diff --git a/src/backend/linux_raw/arch/x86.rs b/src/backend/linux_raw/arch/x86.rs index e789181cc..a5e651567 100644 --- a/src/backend/linux_raw/arch/x86.rs +++ b/src/backend/linux_raw/arch/x86.rs @@ -357,6 +357,7 @@ pub(in crate::backend) unsafe fn syscall4_readonly( a3: ArgReg<'_, A3>, ) -> RetReg { let r0; + // See the comments in `syscall4`. asm!( "xchg esi, {a3}", "int $$0x80", diff --git a/src/backend/linux_raw/c.rs b/src/backend/linux_raw/c.rs index b2cd5bdcb..70bc46a8c 100644 --- a/src/backend/linux_raw/c.rs +++ b/src/backend/linux_raw/c.rs @@ -317,3 +317,9 @@ mod reboot_symbols { } #[cfg(feature = "system")] pub(crate) use reboot_symbols::*; + +// TODO: This is new in Linux 6.11; remove when linux-raw-sys is updated. +pub(crate) const MAP_DROPPABLE: u32 = 0x8; + +// TODO: This is new in Linux 6.5; remove when linux-raw-sys is updated. +pub(crate) const MOVE_MOUNT_BENEATH: u32 = 0x200; diff --git a/src/backend/linux_raw/conv.rs b/src/backend/linux_raw/conv.rs index ab85c130f..62f6868e0 100644 --- a/src/backend/linux_raw/conv.rs +++ b/src/backend/linux_raw/conv.rs @@ -107,14 +107,14 @@ pub(super) fn pass_usize<'a, Num: ArgNumber>(t: usize) -> ArgReg<'a, Num> { impl<'a, Num: ArgNumber, T> From<*mut T> for ArgReg<'a, Num> { #[inline] - fn from(c: *mut T) -> ArgReg<'a, Num> { + fn from(c: *mut T) -> Self { raw_arg(c.cast()) } } impl<'a, Num: ArgNumber, T> From<*const T> for ArgReg<'a, Num> { #[inline] - fn from(c: *const T) -> ArgReg<'a, Num> { + fn from(c: *const T) -> Self { let mut_ptr = c as *mut T; raw_arg(mut_ptr.cast()) } @@ -790,11 +790,19 @@ impl<'a, Num: ArgNumber> From<(crate::net::SocketType, crate::net::SocketFlags)> } #[cfg(feature = "thread")] -impl<'a, Num: ArgNumber> From<(crate::thread::FutexOperation, crate::thread::FutexFlags)> - for ArgReg<'a, Num> +impl<'a, Num: ArgNumber> + From<( + crate::backend::thread::futex::Operation, + crate::thread::futex::Flags, + )> for ArgReg<'a, Num> { #[inline] - fn from(pair: (crate::thread::FutexOperation, crate::thread::FutexFlags)) -> Self { + fn from( + pair: ( + crate::backend::thread::futex::Operation, + crate::thread::futex::Flags, + ), + ) -> Self { c_uint(pair.0 as u32 | pair.1.bits()) } } diff --git a/src/backend/linux_raw/event/epoll.rs b/src/backend/linux_raw/event/epoll.rs index 3d5787b8f..fa3a46660 100644 --- a/src/backend/linux_raw/event/epoll.rs +++ b/src/backend/linux_raw/event/epoll.rs @@ -1,89 +1,10 @@ -//! Linux `epoll` support. -//! -//! # Examples -//! -//! ```no_run -//! # #[cfg(feature = "net")] -//! # fn main() -> std::io::Result<()> { -//! use rustix::event::epoll; -//! use rustix::fd::AsFd; -//! use rustix::io::{ioctl_fionbio, read, write}; -//! use rustix::net::{ -//! accept, bind_v4, listen, socket, AddressFamily, Ipv4Addr, SocketAddrV4, SocketType, -//! }; -//! use std::collections::HashMap; -//! use std::os::unix::io::AsRawFd; -//! -//! // Create a socket and listen on it. -//! let listen_sock = socket(AddressFamily::INET, SocketType::STREAM, None)?; -//! bind_v4(&listen_sock, &SocketAddrV4::new(Ipv4Addr::LOCALHOST, 0))?; -//! listen(&listen_sock, 1)?; -//! -//! // Create an epoll object. Using `Owning` here means the epoll object will -//! // take ownership of the file descriptors registered with it. -//! let epoll = epoll::create(epoll::CreateFlags::CLOEXEC)?; -//! -//! // Register the socket with the epoll object. -//! epoll::add( -//! &epoll, -//! &listen_sock, -//! epoll::EventData::new_u64(1), -//! epoll::EventFlags::IN, -//! )?; -//! -//! // Keep track of the sockets we've opened. -//! let mut next_id = epoll::EventData::new_u64(2); -//! let mut sockets = HashMap::new(); -//! -//! // Process events. -//! let mut event_list = epoll::EventVec::with_capacity(4); -//! loop { -//! epoll::wait(&epoll, &mut event_list, -1)?; -//! for event in &event_list { -//! let target = event.data; -//! if target.u64() == 1 { -//! // Accept a new connection, set it to non-blocking, and -//! // register to be notified when it's ready to write to. -//! let conn_sock = accept(&listen_sock)?; -//! ioctl_fionbio(&conn_sock, true)?; -//! epoll::add( -//! &epoll, -//! &conn_sock, -//! next_id, -//! epoll::EventFlags::OUT | epoll::EventFlags::ET, -//! )?; -//! -//! // Keep track of the socket. -//! sockets.insert(next_id, conn_sock); -//! next_id = epoll::EventData::new_u64(next_id.u64() + 1); -//! } else { -//! // Write a message to the stream and then unregister it. -//! let target = sockets.remove(&target).unwrap(); -//! write(&target, b"hello\n")?; -//! let _ = epoll::delete(&epoll, &target)?; -//! } -//! } -//! } -//! # } -//! # #[cfg(not(feature = "net"))] -//! # fn main() {} -//! ``` - -#![allow(unsafe_code)] - use crate::backend::c; -use crate::backend::event::syscalls; -use crate::fd::{AsFd, AsRawFd, OwnedFd}; -use crate::io; -#[cfg(feature = "alloc")] -use alloc::vec::Vec; use bitflags::bitflags; -use core::ffi::c_void; -use core::hash::{Hash, Hasher}; -use core::slice; bitflags! { - /// `EPOLL_*` for use with [`new`]. + /// `EPOLL_*` for use with [`epoll::create`]. + /// + /// [`epoll::create`]: crate::event::epoll::create #[repr(transparent)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct CreateFlags: c::c_uint { @@ -96,7 +17,9 @@ bitflags! { } bitflags! { - /// `EPOLL*` for use with [`add`]. + /// `EPOLL*` for use with [`epoll::add`]. + /// + /// [`epoll::add`]: crate::event::epoll::add #[repr(transparent)] #[derive(Default, Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct EventFlags: u32 { @@ -149,322 +72,3 @@ bitflags! { const _ = !0; } } - -/// `epoll_create1(flags)`—Creates a new epoll object. -/// -/// Use the [`CreateFlags::CLOEXEC`] flag to prevent the resulting file -/// descriptor from being implicitly passed across `exec` boundaries. -#[inline] -#[doc(alias = "epoll_create1")] -pub fn create(flags: CreateFlags) -> io::Result { - syscalls::epoll_create(flags) -} - -/// `epoll_ctl(self, EPOLL_CTL_ADD, data, event)`—Adds an element to an epoll -/// object. -/// -/// This registers interest in any of the events set in `events` occurring on -/// the file descriptor associated with `data`. -/// -/// If [`delete`] is not called on the I/O source passed into this function -/// before the I/O source is `close`d, then the `epoll` will act as if the I/O -/// source is still registered with it. This can lead to spurious events being -/// returned from [`wait`]. If a file descriptor is an -/// `Arc`, then `epoll` can be thought to maintain a -/// `Weak` to the file descriptor. -#[doc(alias = "epoll_ctl")] -#[inline] -pub fn add( - epoll: impl AsFd, - source: impl AsFd, - data: EventData, - event_flags: EventFlags, -) -> io::Result<()> { - // SAFETY: We're calling `epoll_ctl` via FFI and we know how it - // behaves. - unsafe { - syscalls::epoll_add( - epoll.as_fd(), - source.as_fd().as_raw_fd(), - &Event { - flags: event_flags, - data, - }, - ) - } -} - -/// `epoll_ctl(self, EPOLL_CTL_MOD, target, event)`—Modifies an element in a -/// given epoll object. -/// -/// This sets the events of interest with `target` to `events`. -#[doc(alias = "epoll_ctl")] -#[inline] -pub fn modify( - epoll: impl AsFd, - source: impl AsFd, - data: EventData, - event_flags: EventFlags, -) -> io::Result<()> { - // SAFETY: We're calling `epoll_ctl` via FFI and we know how it - // behaves. - unsafe { - let raw_fd = source.as_fd().as_raw_fd(); - syscalls::epoll_mod( - epoll.as_fd(), - raw_fd, - &Event { - flags: event_flags, - data, - }, - ) - } -} - -/// `epoll_ctl(self, EPOLL_CTL_DEL, target, NULL)`—Removes an element in a -/// given epoll object. -#[doc(alias = "epoll_ctl")] -#[inline] -pub fn delete(epoll: impl AsFd, source: impl AsFd) -> io::Result<()> { - // SAFETY: We're calling `epoll_ctl` via FFI and we know how it - // behaves. - unsafe { - let raw_fd = source.as_fd().as_raw_fd(); - syscalls::epoll_del(epoll.as_fd(), raw_fd) - } -} - -/// `epoll_wait(self, events, timeout)`—Waits for registered events of -/// interest. -/// -/// For each event of interest, an element is written to `events`. On -/// success, this returns the number of written elements. -#[cfg(feature = "alloc")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] -#[inline] -pub fn wait(epoll: impl AsFd, event_list: &mut EventVec, timeout: c::c_int) -> io::Result<()> { - // SAFETY: We're calling `epoll_wait` via FFI and we know how it - // behaves. - unsafe { - event_list.events.set_len(0); - let nfds = syscalls::epoll_wait( - epoll.as_fd(), - event_list.events[..].as_mut_ptr().cast(), - event_list.events.capacity(), - timeout, - )?; - event_list.events.set_len(nfds); - } - - Ok(()) -} - -/// An iterator over the `Event`s in an `EventVec`. -pub struct Iter<'a> { - /// Use `Copied` to copy the struct, since `Event` is `packed` on some - /// platforms, and it's common for users to directly destructure it, which - /// would lead to errors about forming references to packed fields. - iter: core::iter::Copied>, -} - -impl<'a> Iterator for Iter<'a> { - type Item = Event; - - #[inline] - fn next(&mut self) -> Option { - self.iter.next() - } -} - -/// A record of an event that occurred. -#[repr(C)] -#[cfg_attr(target_arch = "x86_64", repr(packed))] -#[derive(Copy, Clone, Eq, PartialEq, Hash)] -pub struct Event { - /// Which specific event(s) occurred. - pub flags: EventFlags, - /// User data. - pub data: EventData, -} - -/// Data associated with an [`Event`]. This can either be a 64-bit integer -/// value or a pointer which preserves pointer provenance. -#[repr(C)] -#[derive(Copy, Clone)] -pub union EventData { - /// A 64-bit integer value. - as_u64: u64, - - /// A `*mut c_void` which preserves pointer provenance, extended to be - /// 64-bit so that if we read the value as a `u64` union field, we don't - /// get uninitialized memory. - sixty_four_bit_pointer: SixtyFourBitPointer, -} - -impl EventData { - /// Construct a new value containing a `u64`. - #[inline] - pub const fn new_u64(value: u64) -> Self { - Self { as_u64: value } - } - - /// Construct a new value containing a `*mut c_void`. - #[inline] - pub const fn new_ptr(value: *mut c_void) -> Self { - Self { - sixty_four_bit_pointer: SixtyFourBitPointer { - pointer: value, - #[cfg(target_pointer_width = "32")] - _padding: 0, - }, - } - } - - /// Return the value as a `u64`. - /// - /// If the stored value was a pointer, the pointer is zero-extended to a - /// `u64`. - #[inline] - pub fn u64(self) -> u64 { - unsafe { self.as_u64 } - } - - /// Return the value as a `*mut c_void`. - /// - /// If the stored value was a `u64`, the least-significant bits of the - /// `u64` are returned as a pointer value. - #[inline] - pub fn ptr(self) -> *mut c_void { - unsafe { self.sixty_four_bit_pointer.pointer } - } -} - -impl PartialEq for EventData { - #[inline] - fn eq(&self, other: &Self) -> bool { - self.u64() == other.u64() - } -} - -impl Eq for EventData {} - -impl Hash for EventData { - #[inline] - fn hash(&self, state: &mut H) { - self.u64().hash(state) - } -} - -#[repr(C)] -#[derive(Copy, Clone)] -struct SixtyFourBitPointer { - #[cfg(target_endian = "big")] - #[cfg(target_pointer_width = "32")] - _padding: u32, - - pointer: *mut c_void, - - #[cfg(target_endian = "little")] - #[cfg(target_pointer_width = "32")] - _padding: u32, -} - -/// A vector of `Event`s, plus context for interpreting them. -#[cfg(feature = "alloc")] -pub struct EventVec { - events: Vec, -} - -#[cfg(feature = "alloc")] -impl EventVec { - /// Constructs an `EventVec` from raw pointer, length, and capacity. - /// - /// # Safety - /// - /// This function calls [`Vec::from_raw_parts`] with its arguments. - /// - /// [`Vec::from_raw_parts`]: https://doc.rust-lang.org/stable/std/vec/struct.Vec.html#method.from_raw_parts - #[inline] - pub unsafe fn from_raw_parts(ptr: *mut Event, len: usize, capacity: usize) -> Self { - Self { - events: Vec::from_raw_parts(ptr, len, capacity), - } - } - - /// Constructs an `EventVec` with memory for `capacity` `Event`s. - #[inline] - pub fn with_capacity(capacity: usize) -> Self { - Self { - events: Vec::with_capacity(capacity), - } - } - - /// Returns the current `Event` capacity of this `EventVec`. - #[inline] - pub fn capacity(&self) -> usize { - self.events.capacity() - } - - /// Reserves enough memory for at least `additional` more `Event`s. - #[inline] - pub fn reserve(&mut self, additional: usize) { - self.events.reserve(additional); - } - - /// Reserves enough memory for exactly `additional` more `Event`s. - #[inline] - pub fn reserve_exact(&mut self, additional: usize) { - self.events.reserve_exact(additional); - } - - /// Clears all the `Events` out of this `EventVec`. - #[inline] - pub fn clear(&mut self) { - self.events.clear(); - } - - /// Shrinks the capacity of this `EventVec` as much as possible. - #[inline] - pub fn shrink_to_fit(&mut self) { - self.events.shrink_to_fit(); - } - - /// Returns an iterator over the `Event`s in this `EventVec`. - #[inline] - pub fn iter(&self) -> Iter<'_> { - Iter { - iter: self.events.iter().copied(), - } - } - - /// Returns the number of `Event`s logically contained in this `EventVec`. - #[inline] - pub fn len(&mut self) -> usize { - self.events.len() - } - - /// Tests whether this `EventVec` is logically empty. - #[inline] - pub fn is_empty(&mut self) -> bool { - self.events.is_empty() - } -} - -#[cfg(feature = "alloc")] -impl<'a> IntoIterator for &'a EventVec { - type IntoIter = Iter<'a>; - type Item = Event; - - #[inline] - fn into_iter(self) -> Self::IntoIter { - self.iter() - } -} - -#[test] -fn test_epoll_layouts() { - check_renamed_type!(Event, epoll_event); - check_renamed_type!(Event, epoll_event); - check_renamed_struct_renamed_field!(Event, epoll_event, flags, events); - check_renamed_struct_renamed_field!(Event, epoll_event, data, data); -} diff --git a/src/backend/linux_raw/event/syscalls.rs b/src/backend/linux_raw/event/syscalls.rs index 0ae775339..ac199adfa 100644 --- a/src/backend/linux_raw/event/syscalls.rs +++ b/src/backend/linux_raw/event/syscalls.rs @@ -6,14 +6,14 @@ #![allow(unsafe_code, clippy::undocumented_unsafe_blocks)] use crate::backend::c; -#[cfg(feature = "alloc")] -use crate::backend::conv::pass_usize; use crate::backend::conv::{ - by_ref, c_int, c_uint, raw_fd, ret, ret_error, ret_owned_fd, ret_usize, slice_mut, zero, + by_ref, c_int, c_uint, ret, ret_error, ret_owned_fd, ret_usize, slice_mut, zero, }; use crate::event::{epoll, EventfdFlags, PollFd}; use crate::fd::{BorrowedFd, OwnedFd}; use crate::io; +#[cfg(feature = "alloc")] +use core::mem::MaybeUninit; use linux_raw_sys::general::{EPOLL_CTL_ADD, EPOLL_CTL_DEL, EPOLL_CTL_MOD}; #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] use { @@ -52,75 +52,92 @@ pub(crate) fn poll(fds: &mut [PollFd<'_>], timeout: c::c_int) -> io::Result io::Result { + // SAFETY: `__NR_epoll_create1` doesn't access any user memory. unsafe { ret_owned_fd(syscall_readonly!(__NR_epoll_create1, flags)) } } #[inline] -pub(crate) unsafe fn epoll_add( +pub(crate) fn epoll_add( epfd: BorrowedFd<'_>, - fd: c::c_int, + fd: BorrowedFd<'_>, event: &epoll::Event, ) -> io::Result<()> { - ret(syscall_readonly!( - __NR_epoll_ctl, - epfd, - c_uint(EPOLL_CTL_ADD), - raw_fd(fd), - by_ref(event) - )) + // SAFETY: `__NR_epoll_ctl` with `EPOLL_CTL_ADD` doesn't modify any user + // memory, and it only reads from `event`. + unsafe { + ret(syscall_readonly!( + __NR_epoll_ctl, + epfd, + c_uint(EPOLL_CTL_ADD), + fd, + by_ref(event) + )) + } } #[inline] -pub(crate) unsafe fn epoll_mod( +pub(crate) fn epoll_mod( epfd: BorrowedFd<'_>, - fd: c::c_int, + fd: BorrowedFd<'_>, event: &epoll::Event, ) -> io::Result<()> { - ret(syscall_readonly!( - __NR_epoll_ctl, - epfd, - c_uint(EPOLL_CTL_MOD), - raw_fd(fd), - by_ref(event) - )) + // SAFETY: `__NR_epoll_ctl` with `EPOLL_CTL_MOD` doesn't modify any user + // memory, and it only reads from `event`. + unsafe { + ret(syscall_readonly!( + __NR_epoll_ctl, + epfd, + c_uint(EPOLL_CTL_MOD), + fd, + by_ref(event) + )) + } } #[inline] -pub(crate) unsafe fn epoll_del(epfd: BorrowedFd<'_>, fd: c::c_int) -> io::Result<()> { - ret(syscall_readonly!( - __NR_epoll_ctl, - epfd, - c_uint(EPOLL_CTL_DEL), - raw_fd(fd), - zero() - )) +pub(crate) fn epoll_del(epfd: BorrowedFd<'_>, fd: BorrowedFd<'_>) -> io::Result<()> { + // SAFETY: `__NR_epoll_ctl` with `EPOLL_CTL_DEL` doesn't access any user + // memory. + unsafe { + ret(syscall_readonly!( + __NR_epoll_ctl, + epfd, + c_uint(EPOLL_CTL_DEL), + fd, + zero() + )) + } } #[cfg(feature = "alloc")] #[inline] pub(crate) fn epoll_wait( epfd: BorrowedFd<'_>, - events: *mut epoll::Event, - num_events: usize, + events: &mut [MaybeUninit], timeout: c::c_int, ) -> io::Result { + let (buf_addr_mut, buf_len) = slice_mut(events); + // SAFETY: `__NR_epoll_wait` doesn't access any user memory outside of + // the `events` array. #[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))] unsafe { ret_usize(syscall!( __NR_epoll_wait, epfd, - events, - pass_usize(num_events), + buf_addr_mut, + buf_len, c_int(timeout) )) } + // SAFETY: `__NR_epoll_pwait` doesn't access any user memory outside of + // the `events` array, as we don't pass it a `sigmask`. #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] unsafe { ret_usize(syscall!( __NR_epoll_pwait, epfd, - events, - pass_usize(num_events), + buf_addr_mut, + buf_len, c_int(timeout), zero() )) diff --git a/src/backend/linux_raw/fs/dir.rs b/src/backend/linux_raw/fs/dir.rs index a8c9a5576..429dd023d 100644 --- a/src/backend/linux_raw/fs/dir.rs +++ b/src/backend/linux_raw/fs/dir.rs @@ -234,7 +234,7 @@ impl Dir { /// `fchdir(self)` #[cfg(feature = "process")] - #[cfg_attr(doc_cfg, doc(cfg(feature = "process")))] + #[cfg_attr(docsrs, doc(cfg(feature = "process")))] #[inline] pub fn chdir(&self) -> io::Result<()> { fchdir(&self.fd) diff --git a/src/backend/linux_raw/fs/inotify.rs b/src/backend/linux_raw/fs/inotify.rs index 851335ba8..fbcfddb18 100644 --- a/src/backend/linux_raw/fs/inotify.rs +++ b/src/backend/linux_raw/fs/inotify.rs @@ -1,15 +1,12 @@ //! inotify support for working with inotifies use crate::backend::c; -use crate::backend::fs::syscalls; -use crate::fd::{BorrowedFd, OwnedFd}; -use crate::io; use bitflags::bitflags; bitflags! { - /// `IN_*` for use with [`inotify_init`]. + /// `IN_*` for use with [`inotify::init`]. /// - /// [`inotify_init`]: crate::fs::inotify::inotify_init + /// [`inotify::init`]: crate::fs::inotify::init #[repr(transparent)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct CreateFlags: c::c_uint { @@ -24,9 +21,9 @@ bitflags! { } bitflags! { - /// `IN*` for use with [`inotify_add_watch`]. + /// `IN*` for use with [`inotify::add_watch`]. /// - /// [`inotify_add_watch`]: crate::fs::inotify::inotify_add_watch + /// [`inotify::add_watch`]: crate::fs::inotify::add_watch #[repr(transparent)] #[derive(Default, Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct WatchFlags: c::c_uint { @@ -80,39 +77,48 @@ bitflags! { } } -/// `inotify_init1(flags)`—Creates a new inotify object. -/// -/// Use the [`CreateFlags::CLOEXEC`] flag to prevent the resulting file -/// descriptor from being implicitly passed across `exec` boundaries. -#[doc(alias = "inotify_init1")] -#[inline] -pub fn inotify_init(flags: CreateFlags) -> io::Result { - syscalls::inotify_init1(flags) -} +bitflags! { + /// `IN*` for use with [`inotify::Reader`]. + /// + /// [`inotify::Reader`]: crate::fs::inotify::InotifyReader + #[repr(transparent)] + #[derive(Default, Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct ReadFlags: c::c_uint { + /// `IN_ACCESS` + const ACCESS = linux_raw_sys::general::IN_ACCESS; + /// `IN_ATTRIB` + const ATTRIB = linux_raw_sys::general::IN_ATTRIB; + /// `IN_CLOSE_NOWRITE` + const CLOSE_NOWRITE = linux_raw_sys::general::IN_CLOSE_NOWRITE; + /// `IN_CLOSE_WRITE` + const CLOSE_WRITE = linux_raw_sys::general::IN_CLOSE_WRITE; + /// `IN_CREATE` + const CREATE = linux_raw_sys::general::IN_CREATE; + /// `IN_DELETE` + const DELETE = linux_raw_sys::general::IN_DELETE; + /// `IN_DELETE_SELF` + const DELETE_SELF = linux_raw_sys::general::IN_DELETE_SELF; + /// `IN_MODIFY` + const MODIFY = linux_raw_sys::general::IN_MODIFY; + /// `IN_MOVE_SELF` + const MOVE_SELF = linux_raw_sys::general::IN_MOVE_SELF; + /// `IN_MOVED_FROM` + const MOVED_FROM = linux_raw_sys::general::IN_MOVED_FROM; + /// `IN_MOVED_TO` + const MOVED_TO = linux_raw_sys::general::IN_MOVED_TO; + /// `IN_OPEN` + const OPEN = linux_raw_sys::general::IN_OPEN; -/// `inotify_add_watch(self, path, flags)`—Adds a watch to inotify. -/// -/// This registers or updates a watch for the filesystem path `path` and -/// returns a watch descriptor corresponding to this watch. -/// -/// Note: Due to the existence of hardlinks, providing two different paths to -/// this method may result in it returning the same watch descriptor. An -/// application should keep track of this externally to avoid logic errors. -#[inline] -pub fn inotify_add_watch( - inot: BorrowedFd<'_>, - path: P, - flags: WatchFlags, -) -> io::Result { - path.into_with_c_str(|path| syscalls::inotify_add_watch(inot, path, flags)) -} + /// `IN_IGNORED` + const IGNORED = linux_raw_sys::general::IN_IGNORED; + /// `IN_ISDIR` + const ISDIR = linux_raw_sys::general::IN_ISDIR; + /// `IN_Q_OVERFLOW` + const QUEUE_OVERFLOW = linux_raw_sys::general::IN_Q_OVERFLOW; + /// `IN_UNMOUNT` + const UNMOUNT = linux_raw_sys::general::IN_UNMOUNT; -/// `inotify_rm_watch(self, wd)`—Removes a watch from this inotify. -/// -/// The watch descriptor provided should have previously been returned by -/// [`inotify_add_watch`] and not previously have been removed. -#[doc(alias = "inotify_rm_watch")] -#[inline] -pub fn inotify_remove_watch(inot: BorrowedFd<'_>, wd: i32) -> io::Result<()> { - syscalls::inotify_rm_watch(inot, wd) + /// + const _ = !0; + } } diff --git a/src/backend/linux_raw/fs/syscalls.rs b/src/backend/linux_raw/fs/syscalls.rs index b6cf6b7b4..0c4b03ba1 100644 --- a/src/backend/linux_raw/fs/syscalls.rs +++ b/src/backend/linux_raw/fs/syscalls.rs @@ -358,7 +358,7 @@ pub(crate) fn fallocate( #[inline] pub(crate) fn fadvise(fd: BorrowedFd<'_>, pos: u64, len: u64, advice: Advice) -> io::Result<()> { - // On ARM, the arguments are reordered so that the len and pos argument + // On ARM, the arguments are reordered so that the `len` and `pos` argument // pairs are aligned. And ARM has a custom syscall code for this. #[cfg(target_arch = "arm")] unsafe { diff --git a/src/backend/linux_raw/fs/types.rs b/src/backend/linux_raw/fs/types.rs index 201ed9169..ecadde06d 100644 --- a/src/backend/linux_raw/fs/types.rs +++ b/src/backend/linux_raw/fs/types.rs @@ -130,7 +130,7 @@ impl Mode { /// `Mode`. #[inline] pub const fn from_raw_mode(st_mode: RawMode) -> Self { - Self::from_bits_truncate(st_mode) + Self::from_bits_truncate(st_mode & !linux_raw_sys::general::S_IFMT) } /// Construct an `st_mode` value from a `Mode`. @@ -742,7 +742,7 @@ pub type RawMode = linux_raw_sys::general::__kernel_mode_t; pub type RawMode = c::c_uint; /// `dev_t` -// Within the kernel the dev_t is 32-bit, but userspace uses a 64-bit field. +// Within the kernel the `dev_t` is 32-bit, but userspace uses a 64-bit field. pub type Dev = u64; /// `__fsword_t` diff --git a/src/backend/linux_raw/io/errno.rs b/src/backend/linux_raw/io/errno.rs index 4b13ae1b0..1af1936c1 100644 --- a/src/backend/linux_raw/io/errno.rs +++ b/src/backend/linux_raw/io/errno.rs @@ -41,7 +41,6 @@ use linux_raw_sys::errno; /// [DragonFly BSD]: https://man.dragonflybsd.org/?command=errno§ion=2 /// [illumos]: https://illumos.org/man/3C/errno /// [glibc]: https://www.gnu.org/software/libc/manual/html_node/Error-Codes.html -/// [`std::io::Error`]: Result #[repr(transparent)] #[doc(alias = "errno")] #[derive(Eq, PartialEq, Hash, Copy, Clone)] diff --git a/src/backend/linux_raw/io/syscalls.rs b/src/backend/linux_raw/io/syscalls.rs index c38f28fce..059ad7abd 100644 --- a/src/backend/linux_raw/io/syscalls.rs +++ b/src/backend/linux_raw/io/syscalls.rs @@ -241,6 +241,12 @@ pub(crate) unsafe fn close(fd: RawFd) { syscall_readonly!(__NR_close, raw_fd(fd)).decode_void(); } +#[cfg(feature = "try_close")] +#[inline] +pub(crate) unsafe fn try_close(fd: RawFd) -> io::Result<()> { + ret(syscall_readonly!(__NR_close, raw_fd(fd))) +} + #[inline] pub(crate) unsafe fn ioctl( fd: BorrowedFd<'_>, @@ -271,7 +277,7 @@ pub(crate) fn is_read_write(fd: BorrowedFd<'_>) -> io::Result<(bool, bool)> { match unsafe { crate::backend::net::syscalls::recv( fd, - buf.as_mut_ptr() as *mut u8, + buf.as_mut_ptr().cast::(), 1, RecvFlags::PEEK | RecvFlags::DONTWAIT, ) diff --git a/src/backend/linux_raw/io_uring/syscalls.rs b/src/backend/linux_raw/io_uring/syscalls.rs index d10cd1395..3e61b7aa6 100644 --- a/src/backend/linux_raw/io_uring/syscalls.rs +++ b/src/backend/linux_raw/io_uring/syscalls.rs @@ -8,7 +8,7 @@ use crate::backend::conv::{by_mut, c_uint, pass_usize, ret_c_uint, ret_owned_fd}; use crate::fd::{BorrowedFd, OwnedFd}; use crate::io; -use crate::io_uring::{io_uring_params, IoringEnterFlags, IoringRegisterOp}; +use crate::io_uring::{io_uring_params, IoringEnterFlags, IoringRegisterFlags, IoringRegisterOp}; use core::ffi::c_void; #[inline] @@ -38,6 +38,23 @@ pub(crate) unsafe fn io_uring_register( )) } +#[inline] +pub(crate) unsafe fn io_uring_register_with( + fd: BorrowedFd<'_>, + opcode: IoringRegisterOp, + flags: IoringRegisterFlags, + arg: *const c_void, + nr_args: u32, +) -> io::Result { + ret_c_uint(syscall_readonly!( + __NR_io_uring_register, + fd, + c_uint((opcode as u32) | bitflags_bits!(flags)), + arg, + c_uint(nr_args) + )) +} + #[inline] pub(crate) unsafe fn io_uring_enter( fd: BorrowedFd<'_>, diff --git a/src/backend/linux_raw/mm/syscalls.rs b/src/backend/linux_raw/mm/syscalls.rs index 361f11157..0aff9ab7e 100644 --- a/src/backend/linux_raw/mm/syscalls.rs +++ b/src/backend/linux_raw/mm/syscalls.rs @@ -16,7 +16,7 @@ use crate::backend::conv::loff_t_from_u64; use crate::backend::conv::{c_uint, no_fd, pass_usize, ret, ret_owned_fd, ret_void_star}; use crate::fd::{BorrowedFd, OwnedFd}; use crate::io; -use linux_raw_sys::general::MAP_ANONYMOUS; +use linux_raw_sys::general::{MAP_ANONYMOUS, MREMAP_FIXED}; #[inline] pub(crate) fn madvise(addr: *mut c::c_void, len: usize, advice: Advice) -> io::Result<()> { @@ -170,7 +170,7 @@ pub(crate) unsafe fn mremap_fixed( old_address, pass_usize(old_size), pass_usize(new_size), - flags, + c_uint(flags.bits() | MREMAP_FIXED), new_address )) } diff --git a/src/backend/linux_raw/mm/types.rs b/src/backend/linux_raw/mm/types.rs index 68898f58b..6960cf7f3 100644 --- a/src/backend/linux_raw/mm/types.rs +++ b/src/backend/linux_raw/mm/types.rs @@ -105,6 +105,8 @@ bitflags! { /// `MAP_UNINITIALIZED` #[cfg(not(any(target_arch = "mips", target_arch = "mips32r6", target_arch = "mips64", target_arch = "mips64r6")))] const UNINITIALIZED = linux_raw_sys::general::MAP_UNINITIALIZED; + /// `MAP_DROPPABLE` + const DROPPABLE = c::MAP_DROPPABLE; /// const _ = !0; diff --git a/src/backend/linux_raw/mount/types.rs b/src/backend/linux_raw/mount/types.rs index 43cb8c0d5..4ca38c0ee 100644 --- a/src/backend/linux_raw/mount/types.rs +++ b/src/backend/linux_raw/mount/types.rs @@ -223,9 +223,8 @@ bitflags! { /// `MOVE_MOUNT__MASK` const MOVE_MOUNT_SET_GROUP = linux_raw_sys::general::MOVE_MOUNT_SET_GROUP; - // TODO: add when Linux 6.5 is released - // /// `MOVE_MOUNT_BENEATH` - // const MOVE_MOUNT_BENEATH = linux_raw_sys::general::MOVE_MOUNT_BENEATH; + /// `MOVE_MOUNT_BENEATH` (since Linux 6.5) + const MOVE_MOUNT_BENEATH = c::MOVE_MOUNT_BENEATH; /// `MOVE_MOUNT__MASK` const MOVE_MOUNT__MASK = linux_raw_sys::general::MOVE_MOUNT__MASK; diff --git a/src/backend/linux_raw/net/addr.rs b/src/backend/linux_raw/net/addr.rs index 3c6af46b7..5bc84ed69 100644 --- a/src/backend/linux_raw/net/addr.rs +++ b/src/backend/linux_raw/net/addr.rs @@ -152,13 +152,13 @@ impl Hash for SocketAddrUnix { } impl fmt::Debug for SocketAddrUnix { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if let Some(path) = self.path() { - path.fmt(fmt) + path.fmt(f) } else if let Some(name) = self.abstract_name() { - name.fmt(fmt) + name.fmt(f) } else { - "(unnamed)".fmt(fmt) + "(unnamed)".fmt(f) } } } diff --git a/src/backend/linux_raw/net/netdevice.rs b/src/backend/linux_raw/net/netdevice.rs index c312dfb5c..2f7e5c1ef 100644 --- a/src/backend/linux_raw/net/netdevice.rs +++ b/src/backend/linux_raw/net/netdevice.rs @@ -1,8 +1,11 @@ +//! Wrappers for netdevice ioctls. + #![allow(unsafe_code)] use crate::backend::io::syscalls::ioctl; use crate::fd::AsFd; use crate::io; +use core::ptr::addr_of_mut; use core::{slice, str}; use linux_raw_sys::ctypes::c_char; use linux_raw_sys::ioctl::SIOCGIFINDEX; @@ -32,7 +35,7 @@ pub(crate) fn name_to_index(fd: impl AsFd, if_name: &str) -> io::Result { }; unsafe { ifreq.ifr_ifrn.ifrn_name[..if_name_bytes.len()].copy_from_slice(if_name_bytes) }; - unsafe { ioctl(fd.as_fd(), SIOCGIFINDEX, &mut ifreq as *mut ifreq as _) }?; + unsafe { ioctl(fd.as_fd(), SIOCGIFINDEX, addr_of_mut!(ifreq).cast()) }?; let index = unsafe { ifreq.ifr_ifru.ifru_ivalue }; Ok(index as u32) } @@ -46,7 +49,7 @@ pub(crate) fn index_to_name(fd: impl AsFd, index: u32) -> io::Result { }, }; - unsafe { ioctl(fd.as_fd(), SIOCGIFNAME, &mut ifreq as *mut ifreq as _) }?; + unsafe { ioctl(fd.as_fd(), SIOCGIFNAME, addr_of_mut!(ifreq).cast()) }?; if let Some(nul_byte) = unsafe { ifreq.ifr_ifrn.ifrn_name } .iter() diff --git a/src/backend/linux_raw/net/send_recv.rs b/src/backend/linux_raw/net/send_recv.rs index d5cdd075e..ef4609cbf 100644 --- a/src/backend/linux_raw/net/send_recv.rs +++ b/src/backend/linux_raw/net/send_recv.rs @@ -2,7 +2,7 @@ use crate::backend::c; use bitflags::bitflags; bitflags! { - /// `MSG_*` flags for use with [`send`], [`send_to`], and related + /// `MSG_*` flags for use with [`send`], [`sendto`], and related /// functions. /// /// [`send`]: crate::net::send @@ -16,8 +16,13 @@ bitflags! { const DONTROUTE = c::MSG_DONTROUTE; /// `MSG_DONTWAIT` const DONTWAIT = c::MSG_DONTWAIT; - /// `MSG_EOT` + /// Deprecated alias for [`EOR`]. + /// + /// [`EOR`]: Self::EOR + #[deprecated(note = "`rustix::net::SendFlags::EOT` is renamed to `rustix::net::SendFlags::EOR`.")] const EOT = c::MSG_EOR; + /// `MSG_EOR` + const EOR = c::MSG_EOR; /// `MSG_MORE` const MORE = c::MSG_MORE; /// `MSG_NOSIGNAL` diff --git a/src/backend/linux_raw/net/sockopt.rs b/src/backend/linux_raw/net/sockopt.rs index 1cc3b744c..642f9f3eb 100644 --- a/src/backend/linux_raw/net/sockopt.rs +++ b/src/backend/linux_raw/net/sockopt.rs @@ -709,7 +709,7 @@ pub(crate) fn set_tcp_keepidle(fd: BorrowedFd<'_>, duration: Duration) -> io::Re #[inline] pub(crate) fn get_tcp_keepidle(fd: BorrowedFd<'_>) -> io::Result { let secs: c::c_uint = getsockopt(fd, c::IPPROTO_TCP, c::TCP_KEEPIDLE)?; - Ok(Duration::from_secs(secs as u64)) + Ok(Duration::from_secs(secs.into())) } #[inline] @@ -721,7 +721,7 @@ pub(crate) fn set_tcp_keepintvl(fd: BorrowedFd<'_>, duration: Duration) -> io::R #[inline] pub(crate) fn get_tcp_keepintvl(fd: BorrowedFd<'_>) -> io::Result { let secs: c::c_uint = getsockopt(fd, c::IPPROTO_TCP, c::TCP_KEEPINTVL)?; - Ok(Duration::from_secs(secs as u64)) + Ok(Duration::from_secs(secs.into())) } #[inline] @@ -755,9 +755,10 @@ pub(crate) fn set_tcp_congestion(fd: BorrowedFd<'_>, value: &str) -> io::Result< #[cfg(feature = "alloc")] #[inline] pub(crate) fn get_tcp_congestion(fd: BorrowedFd<'_>) -> io::Result { + const OPTLEN: c::socklen_t = 16; + let level = c::IPPROTO_TCP; let optname = c::TCP_CONGESTION; - const OPTLEN: c::socklen_t = 16; let mut value = MaybeUninit::<[MaybeUninit; OPTLEN as usize]>::uninit(); let mut optlen = OPTLEN; getsockopt_raw(fd, level, optname, &mut value, &mut optlen)?; diff --git a/src/backend/linux_raw/param/auxv.rs b/src/backend/linux_raw/param/auxv.rs index 59c7a17ad..676fac687 100644 --- a/src/backend/linux_raw/param/auxv.rs +++ b/src/backend/linux_raw/param/auxv.rs @@ -145,7 +145,7 @@ pub(in super::super) fn sysinfo_ehdr() -> *const Elf_Ehdr { let mut ehdr = SYSINFO_EHDR.load(Relaxed); if ehdr.is_null() { - // Use `maybe_init_auxv` to to read the aux vectors if it can, but do + // Use `maybe_init_auxv` to read the aux vectors if it can, but do // nothing if it can't. If it can't, then we'll get a null pointer // here, which our callers are prepared to deal with. maybe_init_auxv(); @@ -204,9 +204,9 @@ static RANDOM: AtomicPtr<[u8; 16]> = AtomicPtr::new(null_mut()); const PR_GET_AUXV: c::c_int = 0x4155_5856; -/// Use Linux >= 6.4's `PR_GET_AUXV` to read the aux records, into a provided +/// Use Linux ≥ 6.4's `PR_GET_AUXV` to read the aux records, into a provided /// statically-sized buffer. Return: -/// - `Ok(...)` if the buffer is big enough. +/// - `Ok(…)` if the buffer is big enough. /// - `Err(Ok(len))` if we need a buffer of length `len`. /// - `Err(Err(err))` if we failed with `err`. #[cold] @@ -228,10 +228,10 @@ fn pr_get_auxv_static(buffer: &mut [u8; 512]) -> Result<&mut [u8], crate::io::Re Err(Ok(len)) } -/// Use Linux >= 6.4's `PR_GET_AUXV` to read the aux records, using a provided +/// Use Linux ≥ 6.4's `PR_GET_AUXV` to read the aux records, using a provided /// statically-sized buffer if possible, or a dynamically allocated buffer /// otherwise. Return: -/// - Ok(...) on success. +/// - Ok(…) on success. /// - Err(err) on failure. #[cfg(feature = "alloc")] #[cold] @@ -270,9 +270,7 @@ fn init_auxv() { /// must be prepared for initialization to be skipped. #[cold] fn maybe_init_auxv() { - if let Ok(()) = init_auxv_impl() { - return; - } + let _ = init_auxv_impl(); } /// If we don't have "use-explicitly-provided-auxv" or "use-libc-auxv", we diff --git a/src/backend/linux_raw/param/libc_auxv.rs b/src/backend/linux_raw/param/libc_auxv.rs index c92e62a28..9f6a06724 100644 --- a/src/backend/linux_raw/param/libc_auxv.rs +++ b/src/backend/linux_raw/param/libc_auxv.rs @@ -39,6 +39,7 @@ const AT_HWCAP2: c::c_ulong = 26; const AT_SECURE: c::c_ulong = 23; const AT_EXECFN: c::c_ulong = 31; const AT_SYSINFO_EHDR: c::c_ulong = 33; +const AT_MINSIGSTKSZ: c::c_ulong = 51; // Declare `sysconf` ourselves so that we don't depend on all of libc just for // this. @@ -64,6 +65,7 @@ fn test_abi() { const_assert_eq!(self::AT_EXECFN, ::libc::AT_EXECFN); const_assert_eq!(self::AT_SECURE, ::libc::AT_SECURE); const_assert_eq!(self::AT_SYSINFO_EHDR, ::libc::AT_SYSINFO_EHDR); + const_assert_eq!(self::AT_MINSIGSTKSZ, ::libc::AT_MINSIGSTKSZ); #[cfg(feature = "runtime")] const_assert_eq!(self::AT_PHDR, ::libc::AT_PHDR); #[cfg(feature = "runtime")] @@ -111,9 +113,6 @@ pub(crate) fn linux_hwcap() -> (usize, usize) { #[cfg(feature = "param")] #[inline] pub(crate) fn linux_minsigstksz() -> usize { - // FIXME: reuse const from libc when available? - const AT_MINSIGSTKSZ: c::c_ulong = 51; - #[cfg(not(feature = "runtime"))] if let Some(libc_getauxval) = getauxval.get() { unsafe { libc_getauxval(AT_MINSIGSTKSZ) as usize } diff --git a/src/backend/linux_raw/pipe/syscalls.rs b/src/backend/linux_raw/pipe/syscalls.rs index ec3e459be..19039b42f 100644 --- a/src/backend/linux_raw/pipe/syscalls.rs +++ b/src/backend/linux_raw/pipe/syscalls.rs @@ -57,7 +57,7 @@ pub(crate) fn pipe_with(flags: PipeFlags) -> io::Result<(OwnedFd, OwnedFd)> { } #[inline] -pub fn splice( +pub(crate) fn splice( fd_in: BorrowedFd<'_>, off_in: Option<&mut u64>, fd_out: BorrowedFd<'_>, @@ -79,7 +79,7 @@ pub fn splice( } #[inline] -pub unsafe fn vmsplice( +pub(crate) unsafe fn vmsplice( fd: BorrowedFd<'_>, bufs: &[IoSliceRaw<'_>], flags: SpliceFlags, @@ -89,7 +89,7 @@ pub unsafe fn vmsplice( } #[inline] -pub fn tee( +pub(crate) fn tee( fd_in: BorrowedFd<'_>, fd_out: BorrowedFd<'_>, len: usize, diff --git a/src/backend/linux_raw/pipe/types.rs b/src/backend/linux_raw/pipe/types.rs index 2d1ed9ab9..a2a9e1cb1 100644 --- a/src/backend/linux_raw/pipe/types.rs +++ b/src/backend/linux_raw/pipe/types.rs @@ -24,6 +24,10 @@ bitflags! { bitflags! { /// `SPLICE_F_*` constants for use with [`splice`], [`vmsplice`], and /// [`tee`]. + /// + /// [`splice`]: crate::pipe::splice + /// [`vmsplice`]: crate::pipe::splice + /// [`tee`]: crate::pipe::tee #[repr(transparent)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct SpliceFlags: c::c_uint { diff --git a/src/backend/linux_raw/process/syscalls.rs b/src/backend/linux_raw/process/syscalls.rs index fb900a4d3..d562aab33 100644 --- a/src/backend/linux_raw/process/syscalls.rs +++ b/src/backend/linux_raw/process/syscalls.rs @@ -313,7 +313,7 @@ pub(crate) fn getrlimit(limit: Resource) -> Rlimit { #[inline] pub(crate) fn setrlimit(limit: Resource, new: Rlimit) -> io::Result<()> { unsafe { - let lim = rlimit_to_linux(new.clone()); + let lim = rlimit_to_linux(new); match ret(syscall_readonly!( __NR_prlimit64, c_uint(0), @@ -345,7 +345,7 @@ pub(crate) fn prlimit(pid: Option, limit: Resource, new: Rlimit) -> io::Res } } -/// Convert a Rust [`Rlimit`] to a C `rlimit64`. +/// Convert a C `rlimit64` to a Rust `Rlimit`. #[inline] fn rlimit_from_linux(lim: rlimit64) -> Rlimit { let current = if lim.rlim_cur == RLIM64_INFINITY as _ { @@ -361,7 +361,7 @@ fn rlimit_from_linux(lim: rlimit64) -> Rlimit { Rlimit { current, maximum } } -/// Convert a C `rlimit64` to a Rust `Rlimit`. +/// Convert a Rust [`Rlimit`] to a C `rlimit64`. #[inline] fn rlimit_to_linux(lim: Rlimit) -> rlimit64 { let rlim_cur = match lim.current { @@ -601,6 +601,12 @@ pub(crate) fn pidfd_send_signal(fd: BorrowedFd<'_>, sig: Signal) -> io::Result<( } } +#[cfg(feature = "fs")] +#[inline] +pub(crate) fn pivot_root(new_root: &CStr, put_old: &CStr) -> io::Result<()> { + unsafe { ret(syscall_readonly!(__NR_pivot_root, new_root, put_old)) } +} + #[cfg(feature = "alloc")] #[inline] pub(crate) fn getgroups(buf: &mut [Gid]) -> io::Result { diff --git a/src/backend/linux_raw/runtime/syscalls.rs b/src/backend/linux_raw/runtime/syscalls.rs index e00acc6fc..4b16ff5ba 100644 --- a/src/backend/linux_raw/runtime/syscalls.rs +++ b/src/backend/linux_raw/runtime/syscalls.rs @@ -38,8 +38,8 @@ pub(crate) unsafe fn fork() -> io::Result { let mut child_pid = MaybeUninit::::uninit(); // Unix `fork` only returns the child PID in the parent; we'd like it in - // the child too, so set `CLONE_CHILD_SETTID` and pass in the address of - // a memory location to store it to in the child. + // the child too, so set `CLONE_CHILD_SETTID` and pass in the address of a + // memory location to store it to in the child. // // Architectures differ on the order of the parameters. #[cfg(target_arch = "x86_64")] diff --git a/src/backend/linux_raw/shm/types.rs b/src/backend/linux_raw/shm/types.rs index 3343d4424..866e8b73b 100644 --- a/src/backend/linux_raw/shm/types.rs +++ b/src/backend/linux_raw/shm/types.rs @@ -2,9 +2,9 @@ use crate::backend::c; use bitflags::bitflags; bitflags! { - /// `O_*` constants for use with [`shm_open`]. + /// `O_*` constants for use with [`shm::open`]. /// - /// [`shm_open`]: crate:shm::shm_open + /// [`shm::open`]: crate:shm::open #[repr(transparent)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct ShmOFlags: c::c_uint { diff --git a/src/backend/linux_raw/termios/syscalls.rs b/src/backend/linux_raw/termios/syscalls.rs index 5a24c0a74..75eeb5edf 100644 --- a/src/backend/linux_raw/termios/syscalls.rs +++ b/src/backend/linux_raw/termios/syscalls.rs @@ -88,7 +88,7 @@ pub(crate) fn tcgetpgrp(fd: BorrowedFd<'_>) -> io::Result { let pid = result.assume_init(); // This doesn't appear to be documented, but it appears `tcsetpgrp` can - // succceed and set the pid to 0 if we pass it a pseudo-terminal device + // succeed and set the pid to 0 if we pass it a pseudo-terminal device // fd. For now, fail with `OPNOTSUPP`. if pid == 0 { return Err(io::Errno::OPNOTSUPP); diff --git a/src/backend/linux_raw/thread/futex.rs b/src/backend/linux_raw/thread/futex.rs index 263e98070..232096e75 100644 --- a/src/backend/linux_raw/thread/futex.rs +++ b/src/backend/linux_raw/thread/futex.rs @@ -1,10 +1,10 @@ bitflags::bitflags! { - /// `FUTEX_*` flags for use with [`futex`]. + /// `FUTEX_*` flags for use with the functions in [`futex`]. /// - /// [`futex`]: crate::thread::futex + /// [`futex`]: mod@crate::thread::futex #[repr(transparent)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] - pub struct FutexFlags: u32 { + pub struct Flags: u32 { /// `FUTEX_PRIVATE_FLAG` const PRIVATE = linux_raw_sys::general::FUTEX_PRIVATE_FLAG; /// `FUTEX_CLOCK_REALTIME` @@ -16,9 +16,53 @@ bitflags::bitflags! { } } -/// `FUTEX_*` operations for use with [`futex`]. +/// `FUTEX_*` operations for use with the futex syscall wrappers. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[repr(u32)] +pub(crate) enum Operation { + /// `FUTEX_WAIT` + Wait = linux_raw_sys::general::FUTEX_WAIT, + /// `FUTEX_WAKE` + Wake = linux_raw_sys::general::FUTEX_WAKE, + /// `FUTEX_FD` + Fd = linux_raw_sys::general::FUTEX_FD, + /// `FUTEX_REQUEUE` + Requeue = linux_raw_sys::general::FUTEX_REQUEUE, + /// `FUTEX_CMP_REQUEUE` + CmpRequeue = linux_raw_sys::general::FUTEX_CMP_REQUEUE, + /// `FUTEX_WAKE_OP` + WakeOp = linux_raw_sys::general::FUTEX_WAKE_OP, + /// `FUTEX_LOCK_PI` + LockPi = linux_raw_sys::general::FUTEX_LOCK_PI, + /// `FUTEX_UNLOCK_PI` + UnlockPi = linux_raw_sys::general::FUTEX_UNLOCK_PI, + /// `FUTEX_TRYLOCK_PI` + TrylockPi = linux_raw_sys::general::FUTEX_TRYLOCK_PI, + /// `FUTEX_WAIT_BITSET` + WaitBitset = linux_raw_sys::general::FUTEX_WAIT_BITSET, + /// `FUTEX_WAKE_BITSET` + WakeBitset = linux_raw_sys::general::FUTEX_WAKE_BITSET, + /// `FUTEX_WAIT_REQUEUE_PI` + WaitRequeuePi = linux_raw_sys::general::FUTEX_WAIT_REQUEUE_PI, + /// `FUTEX_CMP_REQUEUE_PI` + CmpRequeuePi = linux_raw_sys::general::FUTEX_CMP_REQUEUE_PI, + /// `FUTEX_LOCK_PI2` + LockPi2 = linux_raw_sys::general::FUTEX_LOCK_PI2, +} + +/// `FUTEX_*` operations for use with the [`futex`] function. /// -/// [`futex`]: crate::thread::futex +/// [`futex`]: fn@crate::thread::futex +// TODO: Deprecate this now that we have a new typed API. +/* +#[deprecated( + since = "0.38.35", + note = " + The `futex` function and `FutexOperation` enum are deprecated. There are + individual functions available to perform futex operations with improved + type safety. See the `rustix::thread::futex` module." +)] +*/ #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[repr(u32)] pub enum FutexOperation { @@ -43,3 +87,9 @@ pub enum FutexOperation { /// `FUTEX_WAIT_BITSET` WaitBitset = linux_raw_sys::general::FUTEX_WAIT_BITSET, } + +/// `FUTEX_WAITERS` +pub const WAITERS: u32 = linux_raw_sys::general::FUTEX_WAITERS; + +/// `FUTEX_OWNER_DIED` +pub const OWNER_DIED: u32 = linux_raw_sys::general::FUTEX_OWNER_DIED; diff --git a/src/backend/linux_raw/thread/syscalls.rs b/src/backend/linux_raw/thread/syscalls.rs index f1a8f6e71..a88a8e51e 100644 --- a/src/backend/linux_raw/thread/syscalls.rs +++ b/src/backend/linux_raw/thread/syscalls.rs @@ -13,11 +13,12 @@ use crate::backend::conv::{ use crate::fd::BorrowedFd; use crate::io; use crate::pid::Pid; -use crate::thread::{ClockId, FutexFlags, FutexOperation, NanosleepRelativeResult, Timespec}; +use crate::thread::{futex, ClockId, NanosleepRelativeResult, Timespec}; use core::mem::MaybeUninit; -#[cfg(target_pointer_width = "32")] -use linux_raw_sys::general::timespec as __kernel_old_timespec; +use core::sync::atomic::AtomicU32; use linux_raw_sys::general::{__kernel_timespec, TIMER_ABSTIME}; +#[cfg(target_pointer_width = "32")] +use {crate::utils::option_as_ptr, linux_raw_sys::general::timespec as __kernel_old_timespec}; #[inline] pub(crate) fn clock_nanosleep_relative( @@ -203,17 +204,27 @@ pub(crate) fn gettid() -> Pid { } } -// TODO: This could be de-multiplexed. +/// # Safety +/// +/// The raw pointers must point to valid aligned memory. #[inline] -pub(crate) unsafe fn futex( - uaddr: *mut u32, - op: FutexOperation, - flags: FutexFlags, +pub(crate) unsafe fn futex_val2( + uaddr: *const AtomicU32, + op: super::futex::Operation, + flags: futex::Flags, val: u32, - utime: *const Timespec, - uaddr2: *mut u32, + val2: u32, + uaddr2: *const AtomicU32, val3: u32, ) -> io::Result { + // Pass `val2` in the least-significant bytes of the `timeout` argument. + // [“the kernel casts the timeout value first to unsigned long, then to + // uint32_t”], so we perform that exact conversion in reverse to create + // the pointer. + // + // [“the kernel casts the timeout value first to unsigned long, then to uint32_t”]: https://man7.org/linux/man-pages/man2/futex.2.html + let timeout = val2 as usize as *const Timespec; + #[cfg(target_pointer_width = "32")] { ret_usize(syscall!( @@ -221,7 +232,44 @@ pub(crate) unsafe fn futex( uaddr, (op, flags), c_uint(val), - utime, + timeout, + uaddr2, + c_uint(val3) + )) + } + #[cfg(target_pointer_width = "64")] + ret_usize(syscall!( + __NR_futex, + uaddr, + (op, flags), + c_uint(val), + timeout, + uaddr2, + c_uint(val3) + )) +} + +/// # Safety +/// +/// The raw pointers must point to valid aligned memory. +#[inline] +pub(crate) unsafe fn futex_timeout( + uaddr: *const AtomicU32, + op: super::futex::Operation, + flags: futex::Flags, + val: u32, + timeout: *const Timespec, + uaddr2: *const AtomicU32, + val3: u32, +) -> io::Result { + #[cfg(target_pointer_width = "32")] + { + ret_usize(syscall!( + __NR_futex_time64, + uaddr, + (op, flags), + c_uint(val), + timeout, uaddr2, c_uint(val3) )) @@ -229,7 +277,7 @@ pub(crate) unsafe fn futex( // See the comments in `rustix_clock_gettime_via_syscall` about // emulation. if err == io::Errno::NOSYS { - futex_old(uaddr, op, flags, val, utime, uaddr2, val3) + futex_old_timespec(uaddr, op, flags, val, timeout, uaddr2, val3) } else { Err(err) } @@ -241,37 +289,46 @@ pub(crate) unsafe fn futex( uaddr, (op, flags), c_uint(val), - utime, + timeout, uaddr2, c_uint(val3) )) } +/// # Safety +/// +/// The raw pointers must point to valid aligned memory. #[cfg(target_pointer_width = "32")] -unsafe fn futex_old( - uaddr: *mut u32, - op: FutexOperation, - flags: FutexFlags, +unsafe fn futex_old_timespec( + uaddr: *const AtomicU32, + op: super::futex::Operation, + flags: futex::Flags, val: u32, - utime: *const Timespec, - uaddr2: *mut u32, + timeout: *const Timespec, + uaddr2: *const AtomicU32, val3: u32, ) -> io::Result { - let old_utime = __kernel_old_timespec { - tv_sec: (*utime).tv_sec.try_into().map_err(|_| io::Errno::INVAL)?, - tv_nsec: (*utime).tv_nsec.try_into().map_err(|_| io::Errno::INVAL)?, + let old_timeout = if timeout.is_null() { + None + } else { + Some(__kernel_old_timespec { + tv_sec: (*timeout).tv_sec.try_into().map_err(|_| io::Errno::INVAL)?, + tv_nsec: (*timeout) + .tv_nsec + .try_into() + .map_err(|_| io::Errno::INVAL)?, + }) }; ret_usize(syscall!( __NR_futex, uaddr, (op, flags), c_uint(val), - by_ref(&old_utime), + option_as_ptr(old_timeout.as_ref()), uaddr2, c_uint(val3) )) } - #[inline] pub(crate) fn setns(fd: BorrowedFd<'_>, nstype: c::c_int) -> io::Result { unsafe { ret_c_int(syscall_readonly!(__NR_setns, fd, c_int(nstype))) } diff --git a/src/backend/linux_raw/vdso_wrappers.rs b/src/backend/linux_raw/vdso_wrappers.rs index 441738f8d..69309a101 100644 --- a/src/backend/linux_raw/vdso_wrappers.rs +++ b/src/backend/linux_raw/vdso_wrappers.rs @@ -8,6 +8,7 @@ //! passes them uninitialized memory buffers. This file also calls vDSO //! functions. #![allow(unsafe_code)] +#![allow(clippy::missing_transmute_annotations)] #[cfg(target_arch = "x86")] use super::reg::{ArgReg, RetReg, SyscallNumber, A0, A1, A2, A3, A4, A5, R0}; diff --git a/src/bitcast.rs b/src/bitcast.rs index 77e0e6338..11ae24513 100644 --- a/src/bitcast.rs +++ b/src/bitcast.rs @@ -14,7 +14,12 @@ macro_rules! bitcast { } else if false { // Ensure that the source and destinations are the same size. // SAFETY: This code is under an `if false`. - #[allow(unsafe_code, unused_unsafe, clippy::useless_transmute)] + #[allow( + unsafe_code, + unused_unsafe, + clippy::useless_transmute, + clippy::missing_transmute_annotations + )] unsafe { ::core::mem::transmute($x) } diff --git a/src/buffer.rs b/src/buffer.rs index e4b40c739..d7c0924ce 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -16,6 +16,6 @@ pub(super) unsafe fn split_init( init: usize, ) -> (&mut [u8], &mut [MaybeUninit]) { let (init, uninit) = buf.split_at_mut(init); - let init = slice::from_raw_parts_mut(init.as_mut_ptr() as *mut u8, init.len()); + let init = slice::from_raw_parts_mut(init.as_mut_ptr().cast::(), init.len()); (init, uninit) } diff --git a/src/clockid.rs b/src/clockid.rs index 1c7deeb9e..7f986ad1d 100644 --- a/src/clockid.rs +++ b/src/clockid.rs @@ -69,7 +69,7 @@ pub enum ClockId { #[doc(alias = "CLOCK_REALTIME_ALARM")] RealtimeAlarm = bitcast!(c::CLOCK_REALTIME_ALARM), - /// `CLOCK_TAI`, available on Linux >= 3.10 + /// `CLOCK_TAI`, available on Linux ≥ 3.10 #[cfg(all(linux_kernel, feature = "linux_4_11"))] #[doc(alias = "CLOCK_TAI")] Tai = bitcast!(c::CLOCK_TAI), @@ -142,7 +142,7 @@ pub enum DynamicClockId<'a> { #[doc(alias = "CLOCK_REALTIME_ALARM")] RealtimeAlarm, - /// `CLOCK_TAI`, available on Linux >= 3.10 + /// `CLOCK_TAI`, available on Linux ≥ 3.10 #[cfg(linux_kernel)] #[doc(alias = "CLOCK_TAI")] Tai, diff --git a/src/event/epoll.rs b/src/event/epoll.rs new file mode 100644 index 000000000..0990407b9 --- /dev/null +++ b/src/event/epoll.rs @@ -0,0 +1,447 @@ +//! Linux `epoll` support. +//! +//! # Examples +//! +//! ```no_run +//! # #[cfg(feature = "net")] +//! # fn main() -> std::io::Result<()> { +//! use rustix::event::epoll; +//! use rustix::fd::AsFd; +//! use rustix::io::{ioctl_fionbio, read, write}; +//! use rustix::net::{ +//! accept, bind_v4, listen, socket, AddressFamily, Ipv4Addr, SocketAddrV4, SocketType, +//! }; +//! use std::collections::HashMap; +//! use std::os::unix::io::AsRawFd; +//! +//! // Create a socket and listen on it. +//! let listen_sock = socket(AddressFamily::INET, SocketType::STREAM, None)?; +//! bind_v4(&listen_sock, &SocketAddrV4::new(Ipv4Addr::LOCALHOST, 0))?; +//! listen(&listen_sock, 1)?; +//! +//! // Create an epoll object. Using `Owning` here means the epoll object will +//! // take ownership of the file descriptors registered with it. +//! let epoll = epoll::create(epoll::CreateFlags::CLOEXEC)?; +//! +//! // Register the socket with the epoll object. +//! epoll::add( +//! &epoll, +//! &listen_sock, +//! epoll::EventData::new_u64(1), +//! epoll::EventFlags::IN, +//! )?; +//! +//! // Keep track of the sockets we've opened. +//! let mut next_id = epoll::EventData::new_u64(2); +//! let mut sockets = HashMap::new(); +//! +//! // Process events. +//! let mut event_list = epoll::EventVec::with_capacity(4); +//! loop { +//! epoll::wait(&epoll, &mut event_list, -1)?; +//! for event in &event_list { +//! let target = event.data; +//! if target.u64() == 1 { +//! // Accept a new connection, set it to non-blocking, and +//! // register to be notified when it's ready to write to. +//! let conn_sock = accept(&listen_sock)?; +//! ioctl_fionbio(&conn_sock, true)?; +//! epoll::add( +//! &epoll, +//! &conn_sock, +//! next_id, +//! epoll::EventFlags::OUT | epoll::EventFlags::ET, +//! )?; +//! +//! // Keep track of the socket. +//! sockets.insert(next_id, conn_sock); +//! next_id = epoll::EventData::new_u64(next_id.u64() + 1); +//! } else { +//! // Write a message to the stream and then unregister it. +//! let target = sockets.remove(&target).unwrap(); +//! write(&target, b"hello\n")?; +//! let _ = epoll::delete(&epoll, &target)?; +//! } +//! } +//! } +//! # } +//! # #[cfg(not(feature = "net"))] +//! # fn main() {} +//! ``` + +#![allow(unsafe_code)] +#![allow(unused_qualifications)] + +use super::epoll; +#[cfg(feature = "alloc")] +use crate::backend::c; +pub use crate::backend::event::epoll::*; +use crate::backend::event::syscalls; +use crate::fd::{AsFd, OwnedFd}; +use crate::io; +#[cfg(feature = "alloc")] +use alloc::vec::Vec; +use core::ffi::c_void; +use core::hash::{Hash, Hasher}; +use core::slice; + +/// `epoll_create1(flags)`—Creates a new epoll object. +/// +/// Use the [`epoll::CreateFlags::CLOEXEC`] flag to prevent the resulting file +/// descriptor from being implicitly passed across `exec` boundaries. +/// +/// # References +/// - [Linux] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/epoll_create.2.html +#[inline] +#[doc(alias = "epoll_create1")] +pub fn create(flags: epoll::CreateFlags) -> io::Result { + syscalls::epoll_create(flags) +} + +/// `epoll_ctl(self, EPOLL_CTL_ADD, data, event)`—Adds an element to an epoll +/// object. +/// +/// This registers interest in any of the events set in `event_flags` occurring +/// on the file descriptor associated with `data`. +/// +/// Note that `close`ing a file descriptor does not necessarily unregister +/// interest which can lead to spurious events being returned from +/// [`epoll::wait`]. If a file descriptor is an `Arc`, then +/// `epoll` can be thought to maintain a `Weak` to the file +/// descriptor. Check the [faq] for details. +/// +/// # References +/// - [Linux] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/epoll_ctl.2.html +/// [faq]: https://man7.org/linux/man-pages/man7/epoll.7.html#:~:text=Will%20closing%20a%20file%20descriptor%20cause%20it%20to%20be%20removed%20from%20all%0A%20%20%20%20%20%20%20%20%20%20epoll%20interest%20lists%3F +#[doc(alias = "epoll_ctl")] +#[inline] +pub fn add( + epoll: impl AsFd, + source: impl AsFd, + data: epoll::EventData, + event_flags: epoll::EventFlags, +) -> io::Result<()> { + syscalls::epoll_add( + epoll.as_fd(), + source.as_fd(), + &Event { + flags: event_flags, + data, + #[cfg(all(libc, target_os = "redox"))] + _pad: 0, + }, + ) +} + +/// `epoll_ctl(self, EPOLL_CTL_MOD, target, event)`—Modifies an element in a +/// given epoll object. +/// +/// This sets the events of interest with `target` to `events`. +/// +/// # References +/// - [Linux] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/epoll_ctl.2.html +#[doc(alias = "epoll_ctl")] +#[inline] +pub fn modify( + epoll: impl AsFd, + source: impl AsFd, + data: epoll::EventData, + event_flags: epoll::EventFlags, +) -> io::Result<()> { + syscalls::epoll_mod( + epoll.as_fd(), + source.as_fd(), + &Event { + flags: event_flags, + data, + #[cfg(all(libc, target_os = "redox"))] + _pad: 0, + }, + ) +} + +/// `epoll_ctl(self, EPOLL_CTL_DEL, target, NULL)`—Removes an element in a +/// given epoll object. +/// +/// # References +/// - [Linux] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/epoll_ctl.2.html +#[doc(alias = "epoll_ctl")] +#[inline] +pub fn delete(epoll: impl AsFd, source: impl AsFd) -> io::Result<()> { + syscalls::epoll_del(epoll.as_fd(), source.as_fd()) +} + +/// `epoll_wait(self, events, timeout)`—Waits for registered events of +/// interest. +/// +/// For each event of interest, an element is written to `events`. On +/// success, this returns the number of written elements. +/// +/// # References +/// - [Linux] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/epoll_wait.2.html +#[cfg(feature = "alloc")] +#[cfg_attr(docsrs, doc(cfg(feature = "alloc"), alias = "epoll_wait"))] +#[inline] +pub fn wait(epoll: impl AsFd, event_list: &mut EventVec, timeout: c::c_int) -> io::Result<()> { + // SAFETY: We're calling `epoll_wait` via FFI and we know how it + // behaves. + unsafe { + event_list.events.clear(); + let nfds = syscalls::epoll_wait( + epoll.as_fd(), + event_list.events.spare_capacity_mut(), + timeout, + )?; + event_list.events.set_len(nfds); + } + + Ok(()) +} + +/// An iterator over the [`epoll::Event`]s in an [`epoll::EventVec`]. +pub struct Iter<'a> { + /// Use `Copied` to copy the struct, since `Event` is `packed` on some + /// platforms, and it's common for users to directly destructure it, which + /// would lead to errors about forming references to packed fields. + iter: core::iter::Copied>, +} + +impl<'a> Iterator for Iter<'a> { + type Item = epoll::Event; + + #[inline] + fn next(&mut self) -> Option { + self.iter.next() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +/// A record of an event that occurred. +#[repr(C)] +#[cfg_attr(all(not(libc), target_arch = "x86_64"), repr(packed))] +#[cfg_attr( + all( + libc, + linux_kernel, + any( + all( + target_arch = "x86", + not(target_env = "musl"), + not(target_os = "android"), + ), + target_arch = "x86_64", + ) + ), + repr(packed) +)] +#[derive(Copy, Clone, Eq, PartialEq, Hash)] +pub struct Event { + /// Which specific event(s) occurred. + pub flags: EventFlags, + /// User data. + pub data: EventData, + + #[cfg(all(libc, target_os = "redox"))] + _pad: u64, +} + +/// Data associated with an [`epoll::Event`]. This can either be a 64-bit +/// integer value or a pointer which preserves pointer provenance. +#[repr(C)] +#[derive(Copy, Clone)] +pub union EventData { + /// A 64-bit integer value. + as_u64: u64, + + /// A `*mut c_void` which preserves pointer provenance, extended to be + /// 64-bit so that if we read the value as a `u64` union field, we don't + /// get uninitialized memory. + sixty_four_bit_pointer: SixtyFourBitPointer, +} + +impl EventData { + /// Construct a new value containing a `u64`. + #[inline] + pub const fn new_u64(value: u64) -> Self { + Self { as_u64: value } + } + + /// Construct a new value containing a `*mut c_void`. + #[inline] + pub const fn new_ptr(value: *mut c_void) -> Self { + Self { + sixty_four_bit_pointer: SixtyFourBitPointer { + pointer: value, + #[cfg(target_pointer_width = "32")] + _padding: 0, + }, + } + } + + /// Return the value as a `u64`. + /// + /// If the stored value was a pointer, the pointer is zero-extended to a + /// `u64`. + #[inline] + pub fn u64(self) -> u64 { + unsafe { self.as_u64 } + } + + /// Return the value as a `*mut c_void`. + /// + /// If the stored value was a `u64`, the least-significant bits of the + /// `u64` are returned as a pointer value. + #[inline] + pub fn ptr(self) -> *mut c_void { + unsafe { self.sixty_four_bit_pointer.pointer } + } +} + +impl PartialEq for EventData { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.u64() == other.u64() + } +} + +impl Eq for EventData {} + +impl Hash for EventData { + #[inline] + fn hash(&self, state: &mut H) { + self.u64().hash(state) + } +} + +#[repr(C)] +#[derive(Copy, Clone)] +struct SixtyFourBitPointer { + #[cfg(target_endian = "big")] + #[cfg(target_pointer_width = "32")] + _padding: u32, + + pointer: *mut c_void, + + #[cfg(target_endian = "little")] + #[cfg(target_pointer_width = "32")] + _padding: u32, +} + +/// A vector of `epoll::Event`s, plus context for interpreting them. +#[cfg(feature = "alloc")] +pub struct EventVec { + events: Vec, +} + +#[cfg(feature = "alloc")] +impl EventVec { + /// Constructs an `epoll::EventVec` from raw pointer, length, and capacity. + /// + /// # Safety + /// + /// This function calls [`Vec::from_raw_parts`] with its arguments. + /// + /// [`Vec::from_raw_parts`]: https://doc.rust-lang.org/stable/std/vec/struct.Vec.html#method.from_raw_parts + #[inline] + pub unsafe fn from_raw_parts(ptr: *mut Event, len: usize, capacity: usize) -> Self { + Self { + events: Vec::from_raw_parts(ptr, len, capacity), + } + } + + /// Constructs an `epoll::EventVec` with memory for `capacity` + /// `epoll::Event`s. + #[inline] + pub fn with_capacity(capacity: usize) -> Self { + Self { + events: Vec::with_capacity(capacity), + } + } + + /// Returns the current `epoll::Event` capacity of this `epoll::EventVec`. + #[inline] + pub fn capacity(&self) -> usize { + self.events.capacity() + } + + /// Reserves enough memory for at least `additional` more `epoll::Event`s. + #[inline] + pub fn reserve(&mut self, additional: usize) { + self.events.reserve(additional); + } + + /// Reserves enough memory for exactly `additional` more `epoll::Event`s. + #[inline] + pub fn reserve_exact(&mut self, additional: usize) { + self.events.reserve_exact(additional); + } + + /// Clears all the `epoll::Events` out of this `epoll::EventVec`. + #[inline] + pub fn clear(&mut self) { + self.events.clear(); + } + + /// Shrinks the capacity of this `epoll::EventVec` as much as possible. + #[inline] + pub fn shrink_to_fit(&mut self) { + self.events.shrink_to_fit(); + } + + /// Returns an iterator over the `epoll::Event`s in this `epoll::EventVec`. + #[inline] + pub fn iter(&self) -> Iter<'_> { + Iter { + iter: self.events.iter().copied(), + } + } + + /// Returns the number of `epoll::Event`s logically contained in this + /// `epoll::EventVec`. + #[inline] + pub fn len(&mut self) -> usize { + self.events.len() + } + + /// Tests whether this `epoll::EventVec` is logically empty. + #[inline] + pub fn is_empty(&mut self) -> bool { + self.events.is_empty() + } +} + +#[cfg(feature = "alloc")] +impl<'a> IntoIterator for &'a EventVec { + type IntoIter = Iter<'a>; + type Item = epoll::Event; + + #[inline] + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +#[test] +fn test_epoll_layouts() { + check_renamed_type!(Event, epoll_event); + check_renamed_type!(Event, epoll_event); + check_renamed_struct_renamed_field!(Event, epoll_event, flags, events); + #[cfg(libc)] + check_renamed_struct_renamed_field!(Event, epoll_event, data, u64); + #[cfg(not(libc))] + check_renamed_struct_renamed_field!(Event, epoll_event, data, data); +} diff --git a/src/event/kqueue.rs b/src/event/kqueue.rs index d6b7cdecf..56064999c 100644 --- a/src/event/kqueue.rs +++ b/src/event/kqueue.rs @@ -196,8 +196,8 @@ pub enum EventFilter { /// The signal number we waited on. signal: Signal, - /// The number of times the signal has been - /// received since the last call to kevent. + /// The number of times the signal has been received since the last + /// call to kevent. times: usize, }, diff --git a/src/event/mod.rs b/src/event/mod.rs index c497febe0..dab9c6932 100644 --- a/src/event/mod.rs +++ b/src/event/mod.rs @@ -1,5 +1,7 @@ //! Event operations. +#[cfg(any(linux_kernel, target_os = "redox"))] +pub mod epoll; #[cfg(any( linux_kernel, target_os = "freebsd", @@ -15,8 +17,6 @@ mod poll; #[cfg(solarish)] pub mod port; -#[cfg(any(linux_kernel, target_os = "redox"))] -pub use crate::backend::event::epoll; #[cfg(any( linux_kernel, target_os = "freebsd", diff --git a/src/fs/fd.rs b/src/fs/fd.rs index 1aa0e6352..8dc35d1a5 100644 --- a/src/fs/fd.rs +++ b/src/fs/fd.rs @@ -59,8 +59,8 @@ pub struct Timestamps { } impl fmt::Debug for Timestamps { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt.debug_struct("Timestamps") + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Timestamps") .field("last_access.tv_sec", &self.last_access.tv_sec) .field("last_access.tv_nsec", &self.last_access.tv_nsec) .field("last_modification.tv_sec", &self.last_modification.tv_sec) diff --git a/src/fs/inotify.rs b/src/fs/inotify.rs new file mode 100644 index 000000000..f169e0470 --- /dev/null +++ b/src/fs/inotify.rs @@ -0,0 +1,228 @@ +//! inotify support for working with inotifies +//! +//! # Examples +//! +//! ``` +//! use rustix::fs::inotify; +//! use rustix::io; +//! use std::mem::MaybeUninit; +//! +//! # fn test() -> io::Result<()> { +//! // Create an inotify object. In this example, we use `NONBLOCK` so that the +//! // reader fails with `WOULDBLOCk` when no events are ready. Otherwise it +//! // will block until at least one event is ready. +//! let inotify = inotify::init(inotify::CreateFlags::NONBLOCK)?; +//! +//! // Add a directory to watch. +//! inotify::add_watch( +//! &inotify, +//! "/path/to/some/directory/to/watch", +//! inotify::WatchFlags::ALL_EVENTS, +//! )?; +//! +//! // Generate some events in the watched directory… +//! +//! // Loop over pending events. +//! let mut buf = [MaybeUninit::uninit(); 512]; +//! let mut iter = inotify::Reader::new(inotify, &mut buf); +//! loop { +//! let entry = match iter.next() { +//! // Stop iterating if there are no more events for now. +//! Err(io::Errno::WOULDBLOCK) => break, +//! Err(e) => return Err(e), +//! Ok(entry) => entry, +//! }; +//! +//! // Use `entry`… +//! } +//! +//! # Ok(()) +//! # } + +#![allow(unused_qualifications)] + +use super::inotify; +pub use crate::backend::fs::inotify::{CreateFlags, ReadFlags, WatchFlags}; +use crate::backend::fs::syscalls; +use crate::fd::{AsFd, OwnedFd}; +use crate::ffi::CStr; +use crate::io; +use crate::io::{read_uninit, Errno}; +use core::mem::{align_of, size_of, MaybeUninit}; +use linux_raw_sys::general::inotify_event; + +#[deprecated(note = "Use `inotify::add_watch`.")] +#[doc(hidden)] +pub use add_watch as inotify_add_watch; +#[deprecated(note = "Use `inotify::init`.")] +#[doc(hidden)] +pub use init as inotify_init; +#[deprecated(note = "Use `inotify::remove_watch`.")] +#[doc(hidden)] +pub use remove_watch as inotify_remove_watch; + +/// `inotify_init1(flags)`—Creates a new inotify object. +/// +/// Use the [`CreateFlags::CLOEXEC`] flag to prevent the resulting file +/// descriptor from being implicitly passed across `exec` boundaries. +#[doc(alias = "inotify_init1")] +#[inline] +pub fn init(flags: inotify::CreateFlags) -> io::Result { + syscalls::inotify_init1(flags) +} + +/// `inotify_add_watch(self, path, flags)`—Adds a watch to inotify. +/// +/// This registers or updates a watch for the filesystem path `path` and +/// returns a watch descriptor corresponding to this watch. +/// +/// Note: Due to the existence of hardlinks, providing two different paths to +/// this method may result in it returning the same watch descriptor. An +/// application should keep track of this externally to avoid logic errors. +#[doc(alias = "inotify_add_watch")] +#[inline] +pub fn add_watch( + inot: impl AsFd, + path: P, + flags: inotify::WatchFlags, +) -> io::Result { + path.into_with_c_str(|path| syscalls::inotify_add_watch(inot.as_fd(), path, flags)) +} + +/// `inotify_rm_watch(self, wd)`—Removes a watch from this inotify. +/// +/// The watch descriptor provided should have previously been returned by +/// [`inotify::add_watch`] and not previously have been removed. +#[doc(alias = "inotify_rm_watch")] +#[inline] +pub fn remove_watch(inot: impl AsFd, wd: i32) -> io::Result<()> { + syscalls::inotify_rm_watch(inot.as_fd(), wd) +} + +/// An inotify event iterator implemented with the read syscall. +/// +/// See the [`RawDir`] API for more details and usage examples as this API is +/// based on it. +/// +/// [`RawDir`]: crate::fs::raw_dir::RawDir +pub struct Reader<'buf, Fd: AsFd> { + fd: Fd, + buf: &'buf mut [MaybeUninit], + initialized: usize, + offset: usize, +} + +impl<'buf, Fd: AsFd> Reader<'buf, Fd> { + /// Create a new iterator from the given file descriptor and buffer. + pub fn new(fd: Fd, buf: &'buf mut [MaybeUninit]) -> Self { + Self { + fd, + buf: { + let offset = buf.as_ptr().align_offset(align_of::()); + if offset < buf.len() { + &mut buf[offset..] + } else { + &mut [] + } + }, + initialized: 0, + offset: 0, + } + } +} + +/// An inotify event. +#[derive(Debug)] +pub struct InotifyEvent<'a> { + wd: i32, + events: ReadFlags, + cookie: u32, + file_name: Option<&'a CStr>, +} + +impl<'a> InotifyEvent<'a> { + /// Returns the watch for which this event occurs. + #[inline] + pub fn wd(&self) -> i32 { + self.wd + } + + /// Returns a description of the events. + #[inline] + #[doc(alias = "mask")] + pub fn events(&self) -> ReadFlags { + self.events + } + + /// Returns the unique cookie associating related events. + #[inline] + pub fn cookie(&self) -> u32 { + self.cookie + } + + /// Returns the file name of this event, if any. + #[inline] + pub fn file_name(&self) -> Option<&CStr> { + self.file_name + } +} + +impl<'buf, Fd: AsFd> Reader<'buf, Fd> { + /// Read the next inotify event. + /// + /// This is similar to `[Iterator::next`] except that it doesn't return an + /// `Option`, because the stream doesn't have an ending. It always returns + /// events or errors. + /// + /// If there are no events in the buffer and none ready to be read: + /// - If the file descriptor was opened with + /// [`inotify::CreateFlags::NONBLOCK`], this will fail with + /// [`Errno::AGAIN`]. + /// - Otherwise this will block until at least one event is ready or an + /// error occurs. + #[allow(unsafe_code)] + #[allow(clippy::should_implement_trait)] + pub fn next(&mut self) -> io::Result> { + if self.is_buffer_empty() { + match read_uninit(self.fd.as_fd(), self.buf).map(|(init, _)| init.len()) { + Ok(0) => return Err(Errno::INVAL), + Ok(bytes_read) => { + self.initialized = bytes_read; + self.offset = 0; + } + Err(e) => return Err(e), + } + } + + let ptr = self.buf[self.offset..].as_ptr(); + + // SAFETY: + // - This data is initialized by the check above. + // - Assumption: the kernel will not give us partial structs. + // - Assumption: the kernel uses proper alignment between structs. + // - The starting pointer is aligned (performed in RawDir::new) + let event = unsafe { &*ptr.cast::() }; + + self.offset += size_of::() + usize::try_from(event.len).unwrap(); + + Ok(InotifyEvent { + wd: event.wd, + events: ReadFlags::from_bits_retain(event.mask), + cookie: event.cookie, + file_name: if event.len > 0 { + // SAFETY: The kernel guarantees a NUL-terminated string. + Some(unsafe { CStr::from_ptr(event.name.as_ptr().cast()) }) + } else { + None + }, + }) + } + + /// Returns true if the internal buffer is empty and will be refilled when + /// calling [`next`]. This is useful to avoid further blocking reads. + /// + /// [`next`]: Self::next + pub fn is_buffer_empty(&self) -> bool { + self.offset >= self.initialized + } +} diff --git a/src/fs/ioctl.rs b/src/fs/ioctl.rs index 490f183ff..b44bd174e 100644 --- a/src/fs/ioctl.rs +++ b/src/fs/ioctl.rs @@ -23,7 +23,7 @@ use crate::fd::{AsRawFd, BorrowedFd}; #[inline] #[doc(alias = "BLKSSZGET")] pub fn ioctl_blksszget(fd: Fd) -> io::Result { - // SAFETY: BLZSSZGET is a getter opcode that gets a u32. + // SAFETY: `BLZSSZGET` is a getter opcode that gets a u32. unsafe { let ctl = ioctl::Getter::, c::c_uint>::new(); ioctl::ioctl(fd, ctl) @@ -35,7 +35,7 @@ pub fn ioctl_blksszget(fd: Fd) -> io::Result { #[inline] #[doc(alias = "BLKPBSZGET")] pub fn ioctl_blkpbszget(fd: Fd) -> io::Result { - // SAFETY: BLKPBSZGET is a getter opcode that gets a u32. + // SAFETY: `BLKPBSZGET` is a getter opcode that gets a u32. unsafe { let ctl = ioctl::Getter::, c::c_uint>::new(); ioctl::ioctl(fd, ctl) @@ -44,7 +44,7 @@ pub fn ioctl_blkpbszget(fd: Fd) -> io::Result { /// `ioctl(fd, FICLONE, src_fd)`—Share data between open files. /// -/// This ioctl is not available on Sparc platforms +/// This ioctl is not available on Sparc platforms. /// /// # References /// - [Linux] @@ -62,7 +62,7 @@ pub fn ioctl_ficlone(fd: Fd, src_fd: SrcFd) -> io::Result #[inline] #[doc(alias = "EXT4_IOC_RESIZE_FS")] pub fn ext4_ioc_resize_fs(fd: Fd, blocks: u64) -> io::Result<()> { - // SAFETY: EXT4_IOC_RESIZE_FS is a pointer setter opcode. + // SAFETY: `EXT4_IOC_RESIZE_FS` is a pointer setter opcode. unsafe { let ctl = ioctl::Setter::, u64>::new( blocks, @@ -95,7 +95,9 @@ unsafe impl ioctl::Ioctl for Ficlone<'_> { #[cfg(linux_kernel)] bitflags! { - /// `FS_*` constants for use with [`ioctl_getflags`][crate::io::ioctl::ioctl_getflags]. + /// `FS_*` constants for use with [`ioctl_getflags`]. + /// + /// [`ioctl_getflags`]: crate::fs::ioctl::ioctl_getflags pub struct IFlags: c::c_uint { /// `FS_APPEND_FL` const APPEND = linux_raw_sys::general::FS_APPEND_FL; diff --git a/src/fs/mod.rs b/src/fs/mod.rs index a4282088e..ed32b0162 100644 --- a/src/fs/mod.rs +++ b/src/fs/mod.rs @@ -7,7 +7,8 @@ mod constants; #[cfg(linux_kernel)] mod copy_file_range; #[cfg(not(any(target_os = "espidf", target_os = "redox")))] -#[cfg(not(target_os = "haiku"))] // Haiku needs +#[cfg(not(target_os = "haiku"))] +// Haiku needs mod cwd; #[cfg(all(feature = "alloc", not(any(target_os = "espidf", target_os = "redox"))))] mod dir; @@ -33,6 +34,8 @@ mod getpath; #[cfg(not(target_os = "wasi"))] // WASI doesn't have get[gpu]id. mod id; #[cfg(linux_kernel)] +pub mod inotify; +#[cfg(linux_kernel)] mod ioctl; #[cfg(not(any( target_os = "espidf", @@ -63,11 +66,9 @@ mod statx; target_os = "wasi" )))] mod sync; -#[cfg(any(apple, linux_kernel))] +#[cfg(any(apple, linux_kernel, target_os = "hurd"))] mod xattr; -#[cfg(linux_kernel)] -pub use crate::backend::fs::inotify; pub use abs::*; #[cfg(not(target_os = "redox"))] pub use at::*; @@ -75,7 +76,8 @@ pub use constants::*; #[cfg(linux_kernel)] pub use copy_file_range::copy_file_range; #[cfg(not(any(target_os = "espidf", target_os = "redox")))] -#[cfg(not(target_os = "haiku"))] // Haiku needs +#[cfg(not(target_os = "haiku"))] +// Haiku needs pub use cwd::*; #[cfg(all(feature = "alloc", not(any(target_os = "espidf", target_os = "redox"))))] pub use dir::{Dir, DirEntry}; @@ -131,7 +133,7 @@ pub use statx::statx; target_os = "wasi" )))] pub use sync::sync; -#[cfg(any(apple, linux_kernel))] +#[cfg(any(apple, linux_kernel, target_os = "hurd"))] pub use xattr::*; /// Re-export types common to POSIX-ish platforms. @@ -150,7 +152,7 @@ pub use std::os::wasi::fs::{DirEntryExt, FileExt, FileTypeExt, MetadataExt, Open /// the Unix epoch. Until the next semver bump, these unsigned fields are /// deprecated, and this trait provides accessors which return their values /// as signed integers. -#[cfg(all(unix, not(any(target_os = "aix", target_os = "nto"))))] +#[cfg(unix)] pub trait StatExt { /// Return the value of the `st_atime` field, casted to the correct type. fn atime(&self) -> i64; @@ -160,7 +162,10 @@ pub trait StatExt { fn ctime(&self) -> i64; } -#[cfg(all(unix, not(any(target_os = "aix", target_os = "nto"))))] +#[cfg(all( + unix, + not(any(target_os = "aix", target_os = "hurd", target_os = "nto")) +))] #[allow(deprecated)] impl StatExt for Stat { #[inline] @@ -178,3 +183,22 @@ impl StatExt for Stat { self.st_ctime as i64 } } + +#[cfg(any(target_os = "aix", target_os = "hurd", target_os = "nto"))] +#[allow(deprecated)] +impl StatExt for Stat { + #[inline] + fn atime(&self) -> i64 { + self.st_atim.tv_sec as i64 + } + + #[inline] + fn mtime(&self) -> i64 { + self.st_mtim.tv_sec as i64 + } + + #[inline] + fn ctime(&self) -> i64 { + self.st_ctim.tv_sec as i64 + } +} diff --git a/src/fs/raw_dir.rs b/src/fs/raw_dir.rs index 93686b19a..963df7d6f 100644 --- a/src/fs/raw_dir.rs +++ b/src/fs/raw_dir.rs @@ -89,8 +89,8 @@ impl<'buf, Fd: AsFd> RawDir<'buf, Fd> { /// Heap allocated growing buffer for supporting directory entries with /// arbitrarily large file names: /// - /// ```notrust - /// # // The `notrust` above can be removed when we can depend on Rust 1.65. + /// ```ignore + /// # // The `ignore` above can be removed when we can depend on Rust 1.65. /// # use std::mem::MaybeUninit; /// # use rustix::fs::{CWD, Mode, OFlags, openat, RawDir}; /// # use rustix::io::Errno; diff --git a/src/io/close.rs b/src/io/close.rs index 474d252f4..b09267f30 100644 --- a/src/io/close.rs +++ b/src/io/close.rs @@ -53,3 +53,13 @@ use backend::fd::RawFd; pub unsafe fn close(raw_fd: RawFd) { backend::io::syscalls::close(raw_fd) } + +/// `close(raw_fd)`—Closes a `RawFd` directly, and report any errors +/// returned by the OS. +/// +/// The rustix developers do not intend the existence of this feature to imply +/// that anyone should use it. +#[cfg(feature = "try_close")] +pub unsafe fn try_close(raw_fd: RawFd) -> crate::io::Result<()> { + backend::io::syscalls::try_close(raw_fd) +} diff --git a/src/io/dup.rs b/src/io/dup.rs index 5d959c125..fab5357db 100644 --- a/src/io/dup.rs +++ b/src/io/dup.rs @@ -54,7 +54,7 @@ pub fn dup(fd: Fd) -> io::Result { /// /// This function does not set the `O_CLOEXEC` flag. To do a `dup2` that does /// set `O_CLOEXEC`, use [`dup3`] with [`DupFlags::CLOEXEC`] on platforms which -/// support it, or [`fcntl_dupfd_cloexec`] +/// support it, or [`fcntl_dupfd_cloexec`]. /// /// For `dup2` to stdin, stdout, and stderr, see [`stdio::dup2_stdin`], /// [`stdio::dup2_stdout`], and [`stdio::dup2_stderr`]. diff --git a/src/io/errno.rs b/src/io/errno.rs index 2b72de005..2cfaef70d 100644 --- a/src/io/errno.rs +++ b/src/io/errno.rs @@ -24,27 +24,27 @@ impl Errno { } impl fmt::Display for Errno { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { #[cfg(feature = "std")] { - std::io::Error::from(*self).fmt(fmt) + std::io::Error::from(*self).fmt(f) } #[cfg(not(feature = "std"))] { - write!(fmt, "os error {}", self.raw_os_error()) + write!(f, "os error {}", self.raw_os_error()) } } } impl fmt::Debug for Errno { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { #[cfg(feature = "std")] { - std::io::Error::from(*self).fmt(fmt) + std::io::Error::from(*self).fmt(f) } #[cfg(not(feature = "std"))] { - write!(fmt, "os error {}", self.raw_os_error()) + write!(f, "os error {}", self.raw_os_error()) } } } diff --git a/src/io/fcntl.rs b/src/io/fcntl.rs index 31eb84cef..f06b30ffa 100644 --- a/src/io/fcntl.rs +++ b/src/io/fcntl.rs @@ -98,6 +98,7 @@ pub fn fcntl_setfd(fd: Fd, flags: FdFlags) -> io::Result<()> { /// [DragonFly BSD]: https://man.dragonflybsd.org/?command=fcntl§ion=2 /// [illumos]: https://illumos.org/man/2/fcntl /// [glibc]: https://www.gnu.org/software/libc/manual/html_node/Control-Operations.html#index-fcntl-function +/// [file description]: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_258 #[cfg(not(any(target_os = "espidf", target_os = "wasi")))] #[inline] #[doc(alias = "F_DUPFD_CLOEXEC")] @@ -133,6 +134,7 @@ pub fn fcntl_dupfd_cloexec(fd: Fd, min: RawFd) -> io::Result /// [DragonFly BSD]: https://man.dragonflybsd.org/?command=fcntl§ion=2 /// [illumos]: https://illumos.org/man/2/fcntl /// [glibc]: https://www.gnu.org/software/libc/manual/html_node/Control-Operations.html#index-fcntl-function +/// [file description]: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_258 #[cfg(target_os = "espidf")] #[inline] #[doc(alias = "F_DUPFD")] diff --git a/src/io/ioctl.rs b/src/io/ioctl.rs index e83e692a3..596877f7b 100644 --- a/src/io/ioctl.rs +++ b/src/io/ioctl.rs @@ -21,7 +21,7 @@ use backend::fd::AsFd; #[doc(alias = "FIOCLEX")] #[doc(alias = "FD_CLOEXEC")] pub fn ioctl_fioclex(fd: Fd) -> io::Result<()> { - // SAFETY: FIOCLEX is a no-argument setter opcode. + // SAFETY: `FIOCLEX` is a no-argument setter opcode. unsafe { let ctl = ioctl::NoArg::>::new(); ioctl::ioctl(fd, ctl) @@ -41,7 +41,7 @@ pub fn ioctl_fioclex(fd: Fd) -> io::Result<()> { #[inline] #[doc(alias = "FIONBIO")] pub fn ioctl_fionbio(fd: Fd, value: bool) -> io::Result<()> { - // SAFETY: FIONBIO is a pointer setter opcode. + // SAFETY: `FIONBIO` is a pointer setter opcode. unsafe { let ctl = ioctl::Setter::, c::c_int>::new(value.into()); ioctl::ioctl(fd, ctl) @@ -69,7 +69,7 @@ pub fn ioctl_fionbio(fd: Fd, value: bool) -> io::Result<()> { #[inline] #[doc(alias = "FIONREAD")] pub fn ioctl_fionread(fd: Fd) -> io::Result { - // SAFETY: FIONREAD is a getter opcode that gets a c_int. + // SAFETY: `FIONREAD` is a getter opcode that gets a `c_int`. unsafe { let ctl = ioctl::Getter::, c::c_int>::new(); ioctl::ioctl(fd, ctl).map(|n| n as u64) diff --git a/src/io/is_read_write.rs b/src/io/is_read_write.rs index af33806cb..392ec9ba2 100644 --- a/src/io/is_read_write.rs +++ b/src/io/is_read_write.rs @@ -13,7 +13,7 @@ use backend::fd::AsFd; /// /// [`is_file_read_write`]: crate::fs::is_file_read_write #[inline] -#[cfg_attr(doc_cfg, doc(cfg(all(feature = "fs", feature = "net"))))] +#[cfg_attr(docsrs, doc(cfg(all(feature = "fs", feature = "net"))))] pub fn is_read_write(fd: Fd) -> io::Result<(bool, bool)> { backend::io::syscalls::is_read_write(fd.as_fd()) } diff --git a/src/io/mod.rs b/src/io/mod.rs index bddd12acd..c47eb8d4e 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -18,7 +18,7 @@ mod is_read_write; #[cfg(not(windows))] mod read_write; -pub use close::close; +pub use close::*; #[cfg(not(windows))] pub use dup::*; pub use errno::{retry_on_intr, Errno, Result}; diff --git a/src/io/read_write.rs b/src/io/read_write.rs index 9ebbd7a12..c2cc1122d 100644 --- a/src/io/read_write.rs +++ b/src/io/read_write.rs @@ -16,6 +16,9 @@ pub use backend::io::types::ReadWriteFlags; /// `read(fd, buf)`—Reads from a stream. /// +/// This takes a `&mut [u8]` which Rust requires to contain initialized memory. +/// To use an uninitialized buffer, use [`read_uninit`]. +/// /// # References /// - [POSIX] /// - [Linux] @@ -52,8 +55,9 @@ pub fn read_uninit( buf: &mut [MaybeUninit], ) -> io::Result<(&mut [u8], &mut [MaybeUninit])> { // Get number of initialized bytes. - let length = - unsafe { backend::io::syscalls::read(fd.as_fd(), buf.as_mut_ptr() as *mut u8, buf.len()) }; + let length = unsafe { + backend::io::syscalls::read(fd.as_fd(), buf.as_mut_ptr().cast::(), buf.len()) + }; // Split into the initialized and uninitialized portions. Ok(unsafe { split_init(buf, length?) }) @@ -88,6 +92,9 @@ pub fn write(fd: Fd, buf: &[u8]) -> io::Result { /// `pread(fd, buf, offset)`—Reads from a file at a given position. /// +/// This takes a `&mut [u8]` which Rust requires to contain initialized memory. +/// To use an uninitialized buffer, use [`pread_uninit`]. +/// /// # References /// - [POSIX] /// - [Linux] @@ -123,7 +130,7 @@ pub fn pread_uninit( offset: u64, ) -> io::Result<(&mut [u8], &mut [MaybeUninit])> { let length = unsafe { - backend::io::syscalls::pread(fd.as_fd(), buf.as_mut_ptr() as *mut u8, buf.len(), offset) + backend::io::syscalls::pread(fd.as_fd(), buf.as_mut_ptr().cast::(), buf.len(), offset) }; Ok(unsafe { split_init(buf, length?) }) } diff --git a/src/io_uring.rs b/src/io_uring.rs index 0f4d7d566..e1b13d3c6 100644 --- a/src/io_uring.rs +++ b/src/io_uring.rs @@ -35,7 +35,9 @@ use linux_raw_sys::net; pub use crate::event::epoll::{ Event as EpollEvent, EventData as EpollEventData, EventFlags as EpollEventFlags, }; -pub use crate::fs::{Advice, AtFlags, Mode, OFlags, RenameFlags, ResolveFlags, Statx, StatxFlags}; +pub use crate::fs::{ + Advice, AtFlags, Mode, OFlags, RenameFlags, ResolveFlags, Statx, StatxFlags, XattrFlags, +}; pub use crate::io::ReadWriteFlags; pub use crate::net::{RecvFlags, SendFlags, SocketFlags}; pub use crate::timespec::Timespec; @@ -115,6 +117,30 @@ pub unsafe fn io_uring_register( backend::io_uring::syscalls::io_uring_register(fd.as_fd(), opcode, arg, nr_args) } +/// `io_uring_register_with(fd, opcode, flags, arg, nr_args)`—Register files or +/// user buffers for asynchronous I/O. +/// +/// # Safety +/// +/// io_uring operates on raw pointers and raw file descriptors. Users are +/// responsible for ensuring that memory and resources are only accessed in +/// valid ways. +/// +/// # References +/// - [Linux] +/// +/// [Linux]: https://man.archlinux.org/man/io_uring_register.2.en +#[inline] +pub unsafe fn io_uring_register_with( + fd: Fd, + opcode: IoringRegisterOp, + flags: IoringRegisterFlags, + arg: *const c_void, + nr_args: u32, +) -> io::Result { + backend::io_uring::syscalls::io_uring_register_with(fd.as_fd(), opcode, flags, arg, nr_args) +} + /// `io_uring_enter(fd, to_submit, min_complete, flags, arg, size)`—Initiate /// and/or complete asynchronous I/O. /// @@ -260,6 +286,19 @@ pub enum IoringRegisterOp { RegisterFileAllocRange = sys::IORING_REGISTER_FILE_ALLOC_RANGE as _, } +bitflags::bitflags! { + /// `IORING_REGISTER_*` flags for use with [`io_uring_register_with`]. + #[repr(transparent)] + #[derive(Default, Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct IoringRegisterFlags: u32 { + /// `IORING_REGISTER_USE_REGISTERED_RING` + const USE_REGISTERED_RING = sys::IORING_REGISTER_USE_REGISTERED_RING as u32; + + /// + const _ = !0; + } +} + /// `IORING_OP_*` constants for use with [`io_uring_sqe`]. #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] #[repr(u8)] @@ -505,6 +544,15 @@ bitflags::bitflags! { /// `IORING_SETUP_DEFER_TASKRUN` const DEFER_TASKRUN = sys::IORING_SETUP_DEFER_TASKRUN; + /// `IORING_SETUP_NO_MMAP` + const NO_MMAP = sys::IORING_SETUP_NO_MMAP; + + /// `IORING_SETUP_REGISTERED_FD_ONLY` + const REGISTERED_FD_ONLY = sys::IORING_SETUP_REGISTERED_FD_ONLY; + + /// `IORING_SETUP_NO_SQARRAY` + const NO_SQARRAY = sys::IORING_SETUP_NO_SQARRAY; + /// const _ = !0; } @@ -706,6 +754,9 @@ bitflags::bitflags! { /// `IORING_FEAT_LINKED_FILE` const LINKED_FILE = sys::IORING_FEAT_LINKED_FILE; + /// `IORING_FEAT_REG_REG_RING` + const REG_REG_RING = sys::IORING_FEAT_REG_REG_RING; + /// const _ = !0; } @@ -1008,10 +1059,10 @@ impl Default for io_uring_user_data { } impl core::fmt::Debug for io_uring_user_data { - fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { // SAFETY: Just format as a `u64`, since formatting doesn't preserve // provenance, and we don't have a discriminant. - unsafe { self.u64_.fmt(fmt) } + unsafe { self.u64_.fmt(f) } } } @@ -1122,6 +1173,7 @@ pub union op_flags_union { pub rename_flags: RenameFlags, pub unlink_flags: AtFlags, pub hardlink_flags: AtFlags, + pub xattr_flags: XattrFlags, pub msg_ring_flags: IoringMsgringFlags, } diff --git a/src/ioctl/bsd.rs b/src/ioctl/bsd.rs index 2639d81fc..814d93d66 100644 --- a/src/ioctl/bsd.rs +++ b/src/ioctl/bsd.rs @@ -20,8 +20,8 @@ pub(super) const fn compose_opcode( // `IOC_VOID` pub const NONE: RawOpcode = 0x2000_0000; -// `IOC_OUT` ("out" is from the perspective of the kernel) +// `IOC_OUT` (“out” is from the perspective of the kernel) pub const READ: RawOpcode = 0x4000_0000; -// `IOC_IN` +// `IOC_IN` (“in” is from the perspective of the kernel) pub const WRITE: RawOpcode = 0x8000_0000; pub const IOCPARAM_MASK: RawOpcode = 0x1FFF; diff --git a/src/ioctl/linux.rs b/src/ioctl/linux.rs index 2f3599fc2..552625f2d 100644 --- a/src/ioctl/linux.rs +++ b/src/ioctl/linux.rs @@ -84,8 +84,8 @@ mod consts { } #[cfg(not(any( - // These have no ioctl opcodes defined in linux_raw_sys - // so can't use that as a known-good value for this test. + // These have no ioctl opcodes defined in linux_raw_sys so we can't use + // that as a known-good value for this test. target_arch = "sparc", target_arch = "sparc64" )))] diff --git a/src/ioctl/mod.rs b/src/ioctl/mod.rs index 30646efe9..2f0c95188 100644 --- a/src/ioctl/mod.rs +++ b/src/ioctl/mod.rs @@ -217,9 +217,10 @@ impl Opcode { number: u8, data_size: usize, ) -> Self { - if data_size > RawOpcode::MAX as usize { - panic!("data size is too large"); - } + assert!( + data_size <= RawOpcode::MAX as usize, + "data size is too large" + ); Self::old(platform::compose_opcode( direction, diff --git a/src/lib.rs b/src/lib.rs index 51fbdbaf7..28b77a96f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -100,7 +100,7 @@ #![allow(stable_features)] #![cfg_attr(linux_raw, deny(unsafe_code))] #![cfg_attr(rustc_attrs, feature(rustc_attrs))] -#![cfg_attr(doc_cfg, feature(doc_cfg))] +#![cfg_attr(docsrs, feature(doc_cfg))] #![cfg_attr(all(wasi_ext, target_os = "wasi", feature = "std"), feature(wasi_ext))] #![cfg_attr(core_ffi_c, feature(core_ffi_c))] #![cfg_attr(core_c_str, feature(core_c_str))] @@ -127,6 +127,9 @@ allow(unused_imports) )] +#[cfg(all(feature = "rustc-dep-of-std", feature = "alloc"))] +extern crate rustc_std_workspace_alloc as alloc; + #[cfg(all(feature = "alloc", not(feature = "rustc-dep-of-std")))] extern crate alloc; @@ -193,63 +196,63 @@ pub mod fd { // The public API modules. #[cfg(feature = "event")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "event")))] +#[cfg_attr(docsrs, doc(cfg(feature = "event")))] pub mod event; #[cfg(not(windows))] pub mod ffi; #[cfg(not(windows))] #[cfg(feature = "fs")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "fs")))] +#[cfg_attr(docsrs, doc(cfg(feature = "fs")))] pub mod fs; pub mod io; #[cfg(linux_kernel)] #[cfg(feature = "io_uring")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "io_uring")))] +#[cfg_attr(docsrs, doc(cfg(feature = "io_uring")))] pub mod io_uring; pub mod ioctl; #[cfg(not(any(windows, target_os = "espidf", target_os = "vita", target_os = "wasi")))] #[cfg(feature = "mm")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "mm")))] +#[cfg_attr(docsrs, doc(cfg(feature = "mm")))] pub mod mm; #[cfg(linux_kernel)] #[cfg(feature = "mount")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "mount")))] +#[cfg_attr(docsrs, doc(cfg(feature = "mount")))] pub mod mount; #[cfg(not(any(target_os = "redox", target_os = "wasi")))] #[cfg(feature = "net")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "net")))] +#[cfg_attr(docsrs, doc(cfg(feature = "net")))] pub mod net; #[cfg(not(any(windows, target_os = "espidf")))] #[cfg(feature = "param")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "param")))] +#[cfg_attr(docsrs, doc(cfg(feature = "param")))] pub mod param; #[cfg(not(windows))] #[cfg(any(feature = "fs", feature = "mount", feature = "net"))] #[cfg_attr( - doc_cfg, + docsrs, doc(cfg(any(feature = "fs", feature = "mount", feature = "net"))) )] pub mod path; #[cfg(feature = "pipe")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "pipe")))] +#[cfg_attr(docsrs, doc(cfg(feature = "pipe")))] #[cfg(not(any(windows, target_os = "wasi")))] pub mod pipe; #[cfg(not(windows))] #[cfg(feature = "process")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "process")))] +#[cfg_attr(docsrs, doc(cfg(feature = "process")))] pub mod process; #[cfg(feature = "procfs")] #[cfg(linux_kernel)] -#[cfg_attr(doc_cfg, doc(cfg(feature = "procfs")))] +#[cfg_attr(docsrs, doc(cfg(feature = "procfs")))] pub mod procfs; #[cfg(not(windows))] #[cfg(not(target_os = "wasi"))] #[cfg(feature = "pty")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "pty")))] +#[cfg_attr(docsrs, doc(cfg(feature = "pty")))] pub mod pty; #[cfg(not(windows))] #[cfg(feature = "rand")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "rand")))] +#[cfg_attr(docsrs, doc(cfg(feature = "rand")))] pub mod rand; #[cfg(not(any( windows, @@ -259,27 +262,27 @@ pub mod rand; target_os = "wasi" )))] #[cfg(feature = "shm")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "shm")))] +#[cfg_attr(docsrs, doc(cfg(feature = "shm")))] pub mod shm; #[cfg(not(windows))] #[cfg(feature = "stdio")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "stdio")))] +#[cfg_attr(docsrs, doc(cfg(feature = "stdio")))] pub mod stdio; #[cfg(feature = "system")] #[cfg(not(any(windows, target_os = "wasi")))] -#[cfg_attr(doc_cfg, doc(cfg(feature = "system")))] +#[cfg_attr(docsrs, doc(cfg(feature = "system")))] pub mod system; #[cfg(not(any(windows, target_os = "vita")))] #[cfg(feature = "termios")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "termios")))] +#[cfg_attr(docsrs, doc(cfg(feature = "termios")))] pub mod termios; #[cfg(not(windows))] #[cfg(feature = "thread")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "thread")))] +#[cfg_attr(docsrs, doc(cfg(feature = "thread")))] pub mod thread; #[cfg(not(any(windows, target_os = "espidf")))] #[cfg(feature = "time")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "time")))] +#[cfg_attr(docsrs, doc(cfg(feature = "time")))] pub mod time; // "runtime" is also a public API module, but it's only for libc-like users. @@ -287,7 +290,7 @@ pub mod time; #[cfg(feature = "runtime")] #[cfg(linux_raw)] #[cfg_attr(not(document_experimental_runtime_api), doc(hidden))] -#[cfg_attr(doc_cfg, doc(cfg(feature = "runtime")))] +#[cfg_attr(docsrs, doc(cfg(feature = "runtime")))] pub mod runtime; // Temporarily provide some mount functions for use in the fs module for @@ -312,7 +315,7 @@ pub(crate) mod mount; target_arch = "x86", ) ))] -#[cfg_attr(doc_cfg, doc(cfg(feature = "fs")))] +#[cfg_attr(docsrs, doc(cfg(feature = "fs")))] pub(crate) mod fs; // Similarly, declare `path` as a non-public module if needed. diff --git a/src/mount/mount_unmount.rs b/src/mount/mount_unmount.rs index 651a18338..feda8eec1 100644 --- a/src/mount/mount_unmount.rs +++ b/src/mount/mount_unmount.rs @@ -40,9 +40,9 @@ pub fn mount( flags: RecvFlags, ) -> io::Result<(&mut [u8], &mut [MaybeUninit])> { let length = unsafe { - backend::net::syscalls::recv(fd.as_fd(), buf.as_mut_ptr() as *mut u8, buf.len(), flags) + backend::net::syscalls::recv(fd.as_fd(), buf.as_mut_ptr().cast::(), buf.len(), flags) }; Ok(unsafe { split_init(buf, length?) }) @@ -115,6 +118,9 @@ pub fn send(fd: Fd, buf: &[u8], flags: SendFlags) -> io::Result /// `recvfrom(fd, buf, flags, addr, len)`—Reads data from a socket and /// returns the sender address. /// +/// This takes a `&mut [u8]` which Rust requires to contain initialized memory. +/// To use an uninitialized buffer, use [`recvfrom_uninit`]. +/// /// # References /// - [Beej's Guide to Network Programming] /// - [POSIX] @@ -162,7 +168,12 @@ pub fn recvfrom_uninit( flags: RecvFlags, ) -> io::Result<(&mut [u8], &mut [MaybeUninit], Option)> { let (length, addr) = unsafe { - backend::net::syscalls::recvfrom(fd.as_fd(), buf.as_mut_ptr() as *mut u8, buf.len(), flags)? + backend::net::syscalls::recvfrom( + fd.as_fd(), + buf.as_mut_ptr().cast::(), + buf.len(), + flags, + )? }; let (init, uninit) = unsafe { split_init(buf, length) }; Ok((init, uninit, addr)) diff --git a/src/net/send_recv/msg.rs b/src/net/send_recv/msg.rs index 8b81c8d8f..646018ed8 100644 --- a/src/net/send_recv/msg.rs +++ b/src/net/send_recv/msg.rs @@ -97,6 +97,7 @@ macro_rules! cmsg_aligned_space { }}; } +/// Helper function for [`cmsg_space`]. #[doc(hidden)] pub const fn __cmsg_space(len: usize) -> usize { // Add `align_of::()` so that we can align the user-provided @@ -106,6 +107,7 @@ pub const fn __cmsg_space(len: usize) -> usize { __cmsg_aligned_space(len) } +/// Helper function for [`cmsg_aligned_space`]. #[doc(hidden)] pub const fn __cmsg_aligned_space(len: usize) -> usize { // Convert `len` to `u32` for `CMSG_SPACE`. This would be `try_into()` if @@ -942,7 +944,7 @@ mod messages { let msghdr = { let mut h = msghdr::zero_msghdr(); h.msg_control = buf.as_mut_ptr().cast(); - h.msg_controllen = buf.len().try_into().expect("buffer too large for msghdr"); + h.msg_controllen = buf.len().try_into().unwrap(); h }; diff --git a/src/net/socket.rs b/src/net/socket.rs index 1a2157776..ac40b2672 100644 --- a/src/net/socket.rs +++ b/src/net/socket.rs @@ -473,8 +473,8 @@ pub fn connect_unix(sockfd: Fd, addr: &SocketAddrUnix) -> io::Result<( backend::net::syscalls::connect_unix(sockfd.as_fd(), addr) } -/// `connect(sockfd, {.sa_family = AF_UNSPEC}, sizeof(struct sockaddr))` -/// — Dissolve the socket's association. +/// `connect(sockfd, {.sa_family = AF_UNSPEC}, sizeof(struct sockaddr))`— +/// Dissolve the socket's association. /// /// On UDP sockets, BSD platforms report [`Errno::AFNOSUPPORT`] or /// [`Errno::INVAL`] even if the disconnect was successful. diff --git a/src/net/socket_addr_any.rs b/src/net/socket_addr_any.rs index 3be80a3ad..b43d09667 100644 --- a/src/net/socket_addr_any.rs +++ b/src/net/socket_addr_any.rs @@ -109,14 +109,14 @@ impl SocketAddrAny { #[cfg(feature = "std")] impl fmt::Debug for SocketAddrAny { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::V4(v4) => v4.fmt(fmt), - Self::V6(v6) => v6.fmt(fmt), + Self::V4(v4) => v4.fmt(f), + Self::V6(v6) => v6.fmt(f), #[cfg(unix)] - Self::Unix(unix) => unix.fmt(fmt), + Self::Unix(unix) => unix.fmt(f), #[cfg(target_os = "linux")] - Self::Xdp(xdp) => xdp.fmt(fmt), + Self::Xdp(xdp) => xdp.fmt(f), } } } diff --git a/src/net/sockopt.rs b/src/net/sockopt.rs index 47ab1a5be..1ed3ce594 100644 --- a/src/net/sockopt.rs +++ b/src/net/sockopt.rs @@ -442,6 +442,7 @@ pub fn get_socket_send_buffer_size(fd: Fd) -> io::Result { target_os = "emscripten", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "netbsd", target_os = "nto", target_os = "vita", @@ -1069,7 +1070,7 @@ pub fn get_ipv6_freebind(fd: Fd) -> io::Result { /// `getsockopt(fd, IPPROTO_IP, SO_ORIGINAL_DST)` /// -/// Even though this corresponnds to a `SO_*` constant, it is an `IPPROTO_IP` +/// Even though this corresponds to a `SO_*` constant, it is an `IPPROTO_IP` /// option. /// /// See the [module-level documentation] for more. @@ -1084,7 +1085,7 @@ pub fn get_ip_original_dst(fd: Fd) -> io::Result { /// `getsockopt(fd, IPPROTO_IPV6, IP6T_SO_ORIGINAL_DST)` /// -/// Even though this corresponnds to a `IP6T_*` constant, it is an +/// Even though this corresponds to a `IP6T_*` constant, it is an /// `IPPROTO_IPV6` option. /// /// See the [module-level documentation] for more. @@ -1362,7 +1363,8 @@ pub fn get_tcp_cork(fd: Fd) -> io::Result { backend::net::sockopt::get_tcp_cork(fd.as_fd()) } -/// Get credentials of Unix domain socket peer process +/// `getsockopt(fd, SOL_SOCKET, SO_PEERCRED)`—Get credentials of Unix domain +/// socket peer process. /// /// # References /// - [Linux `unix`] diff --git a/src/net/types.rs b/src/net/types.rs index 14345a213..0d4c40ea8 100644 --- a/src/net/types.rs +++ b/src/net/types.rs @@ -91,6 +91,7 @@ impl AddressFamily { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", )))] @@ -106,6 +107,7 @@ impl AddressFamily { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", )))] @@ -128,6 +130,7 @@ impl AddressFamily { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", )))] @@ -140,6 +143,7 @@ impl AddressFamily { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", )))] @@ -152,6 +156,7 @@ impl AddressFamily { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", )))] @@ -163,6 +168,7 @@ impl AddressFamily { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", )))] @@ -175,6 +181,7 @@ impl AddressFamily { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", )))] @@ -190,6 +197,7 @@ impl AddressFamily { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", )))] @@ -202,6 +210,7 @@ impl AddressFamily { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", )))] @@ -213,6 +222,7 @@ impl AddressFamily { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", )))] @@ -229,6 +239,7 @@ impl AddressFamily { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", )))] @@ -241,6 +252,7 @@ impl AddressFamily { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", )))] @@ -253,6 +265,7 @@ impl AddressFamily { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", )))] @@ -265,6 +278,7 @@ impl AddressFamily { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", )))] @@ -277,6 +291,7 @@ impl AddressFamily { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", )))] @@ -291,6 +306,7 @@ impl AddressFamily { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", )))] @@ -303,6 +319,7 @@ impl AddressFamily { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", )))] @@ -315,6 +332,7 @@ impl AddressFamily { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", )))] @@ -327,6 +345,7 @@ impl AddressFamily { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", )))] @@ -339,6 +358,7 @@ impl AddressFamily { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", )))] @@ -351,6 +371,7 @@ impl AddressFamily { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", )))] @@ -362,6 +383,7 @@ impl AddressFamily { windows, target_os = "aix", target_os = "espidf", + target_os = "hurd", target_os = "vita", )))] pub const BLUETOOTH: Self = Self(c::AF_BLUETOOTH as _); @@ -373,6 +395,7 @@ impl AddressFamily { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", )))] @@ -385,6 +408,7 @@ impl AddressFamily { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", )))] @@ -396,6 +420,7 @@ impl AddressFamily { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "vita", )))] pub const ISDN: Self = Self(c::AF_ISDN as _); @@ -407,6 +432,7 @@ impl AddressFamily { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", )))] @@ -419,6 +445,7 @@ impl AddressFamily { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", )))] @@ -616,8 +643,10 @@ const fn new_raw_protocol(u: u32) -> RawProtocol { } /// `IPPROTO_*` and other constants for use with [`socket`], [`socket_with`], -/// and [`socketpair`] when a nondefault value is desired. See the [`ipproto`], -/// [`sysproto`], and [`netlink`] modules for possible values. +/// and [`socketpair`] when a nondefault value is desired. +/// +/// See the [`ipproto`], [`sysproto`], and [`netlink`] modules for possible +/// values. /// /// For the default values, such as `IPPROTO_IP` or `NETLINK_ROUTE`, pass /// `None` as the `protocol` argument in these functions. @@ -1468,7 +1497,7 @@ pub mod xdp { // Constant needs to be cast because bindgen does generate a u32 but the struct // expects a u16. https://github.com/torvalds/linux/blob/v6.6/include/uapi/linux/if_xdp.h#L15-L44 bitflags! { - /// `XDP_*` constants for use in [`SockaddrXdp`]. + /// `XDP_*` constants for use in [`SocketAddrXdp`]. #[repr(transparent)] #[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Debug)] pub struct SockaddrXdpFlags: u16 { diff --git a/src/path/arg.rs b/src/path/arg.rs index fc2910291..435d727a1 100644 --- a/src/path/arg.rs +++ b/src/path/arg.rs @@ -3,7 +3,8 @@ //! This module defines the `Arg` trait and implements it for several common //! string types. This allows users to pass any of these string types directly //! to rustix APIs with string arguments, and it allows rustix to implement -//! NUL-termination without the need for copying where possible. +//! NUL-termination without the need for copying or dynamic allocation where +//! possible. use crate::ffi::CStr; use crate::io; @@ -90,9 +91,9 @@ pub trait Arg { } /// Runs a closure on `arg` where `A` is mapped to a `&CStr` -pub fn option_into_with_c_str(arg: Option, f: F) -> io::Result +pub fn option_into_with_c_str(arg: Option, f: F) -> io::Result where - A: Sized, + A: Arg + Sized, F: FnOnce(Option<&CStr>) -> io::Result, { if let Some(arg) = arg { @@ -1032,7 +1033,7 @@ where // `openat` to open the files under it, which will avoid this, and is often // faster in the OS as well. - // Test with >= so that we have room for the trailing NUL. + // Test with `>=` so that we have room for the trailing NUL. if bytes.len() >= SMALL_PATH_BUFFER_SIZE { return with_c_str_slow_path(bytes, f); } @@ -1076,11 +1077,11 @@ where #[cfg(not(feature = "alloc"))] { - #[cfg(all(libc, not(target_os = "wasi")))] + #[cfg(all(libc, not(any(target_os = "hurd", target_os = "wasi"))))] const LARGE_PATH_BUFFER_SIZE: usize = libc::PATH_MAX as usize; #[cfg(linux_raw)] const LARGE_PATH_BUFFER_SIZE: usize = linux_raw_sys::general::PATH_MAX as usize; - #[cfg(target_os = "wasi")] + #[cfg(any(target_os = "hurd", target_os = "wasi"))] const LARGE_PATH_BUFFER_SIZE: usize = 4096 as usize; // TODO: upstream this // Taken from diff --git a/src/path/dec_int.rs b/src/path/dec_int.rs index e9c46f49a..73907c5b2 100644 --- a/src/path/dec_int.rs +++ b/src/path/dec_int.rs @@ -87,6 +87,8 @@ impl DecInt { } } +/// A wrapper around `DecInt` that implements `Write` without exposing this +/// implementation to `DecInt`'s public API. struct DecIntWriter(DecInt); impl core::fmt::Write for DecIntWriter { @@ -114,7 +116,7 @@ impl AsRef for DecInt { #[cfg(feature = "std")] impl fmt::Debug for DecInt { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - self.as_str().fmt(fmt) + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.as_str().fmt(f) } } diff --git a/src/path/mod.rs b/src/path/mod.rs index ae1cbc14b..54ca1886b 100644 --- a/src/path/mod.rs +++ b/src/path/mod.rs @@ -6,7 +6,7 @@ mod dec_int; pub use arg::{option_into_with_c_str, Arg}; #[cfg(feature = "itoa")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "itoa")))] +#[cfg_attr(docsrs, doc(cfg(feature = "itoa")))] pub use dec_int::DecInt; pub(crate) const SMALL_PATH_BUFFER_SIZE: usize = 256; diff --git a/src/process/chdir.rs b/src/process/chdir.rs index a68352f0e..16785bea8 100644 --- a/src/process/chdir.rs +++ b/src/process/chdir.rs @@ -21,7 +21,7 @@ use { /// [Linux]: https://man7.org/linux/man-pages/man2/chdir.2.html #[inline] #[cfg(feature = "fs")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "fs")))] +#[cfg_attr(docsrs, doc(cfg(feature = "fs")))] pub fn chdir(path: P) -> io::Result<()> { path.into_with_c_str(backend::process::syscalls::chdir) } @@ -52,7 +52,7 @@ pub fn fchdir(fd: Fd) -> io::Result<()> { /// [Linux]: https://man7.org/linux/man-pages/man3/getcwd.3.html #[cfg(all(feature = "alloc", feature = "fs"))] #[cfg(not(target_os = "wasi"))] -#[cfg_attr(doc_cfg, doc(cfg(feature = "fs")))] +#[cfg_attr(docsrs, doc(cfg(feature = "fs")))] #[inline] pub fn getcwd>>(reuse: B) -> io::Result { _getcwd(reuse.into()) diff --git a/src/process/chroot.rs b/src/process/chroot.rs index a4fd8d852..7c2476db3 100644 --- a/src/process/chroot.rs +++ b/src/process/chroot.rs @@ -1,5 +1,5 @@ #[cfg(feature = "fs")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "fs")))] +#[cfg_attr(docsrs, doc(cfg(feature = "fs")))] use crate::{backend, io, path}; /// `chroot(path)`—Change the process root directory. @@ -9,7 +9,7 @@ use crate::{backend, io, path}; /// /// [Linux]: https://man7.org/linux/man-pages/man2/chroot.2.html #[cfg(feature = "fs")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "fs")))] +#[cfg_attr(docsrs, doc(cfg(feature = "fs")))] #[inline] pub fn chroot(path: P) -> io::Result<()> { path.into_with_c_str(backend::process::syscalls::chroot) diff --git a/src/process/ioctl.rs b/src/process/ioctl.rs index 5afc76609..1b617035f 100644 --- a/src/process/ioctl.rs +++ b/src/process/ioctl.rs @@ -40,7 +40,7 @@ unsafe impl ioctl::Ioctl for Tiocsctty { const OPCODE: ioctl::Opcode = ioctl::Opcode::old(c::TIOCSCTTY as ioctl::RawOpcode); fn as_ptr(&mut self) -> *mut c::c_void { - (&0u32) as *const u32 as *mut c::c_void + (&0_u32) as *const u32 as *mut c::c_void } unsafe fn output_from_ptr( diff --git a/src/process/mod.rs b/src/process/mod.rs index 5fbc1f3b7..5d69e023f 100644 --- a/src/process/mod.rs +++ b/src/process/mod.rs @@ -17,6 +17,8 @@ mod membarrier; mod pidfd; #[cfg(target_os = "linux")] mod pidfd_getfd; +#[cfg(target_os = "linux")] +mod pivot_root; #[cfg(linux_kernel)] mod prctl; #[cfg(not(any(target_os = "fuchsia", target_os = "vita", target_os = "wasi")))] @@ -57,6 +59,8 @@ pub use membarrier::*; pub use pidfd::*; #[cfg(target_os = "linux")] pub use pidfd_getfd::*; +#[cfg(target_os = "linux")] +pub use pivot_root::*; #[cfg(linux_kernel)] pub use prctl::*; #[cfg(not(any(target_os = "fuchsia", target_os = "vita", target_os = "wasi")))] diff --git a/src/process/pivot_root.rs b/src/process/pivot_root.rs new file mode 100644 index 000000000..91672774d --- /dev/null +++ b/src/process/pivot_root.rs @@ -0,0 +1,18 @@ +#[cfg(feature = "fs")] +#[cfg_attr(docsrs, doc(cfg(feature = "fs")))] +use crate::{backend, io, path}; + +/// `pivot_root(new_root, put_old)`—Change the root mount. +/// +/// # References +/// - [Linux] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/pivot_root.2.html +#[cfg(feature = "fs")] +#[cfg_attr(docsrs, doc(cfg(feature = "fs")))] +#[inline] +pub fn pivot_root(new_root: P, put_old: Q) -> io::Result<()> { + new_root.into_with_c_str(|new_root| { + put_old.into_with_c_str(|put_old| backend::process::syscalls::pivot_root(new_root, put_old)) + }) +} diff --git a/src/process/prctl.rs b/src/process/prctl.rs index 830abc13d..6a2465b6b 100644 --- a/src/process/prctl.rs +++ b/src/process/prctl.rs @@ -29,11 +29,11 @@ const PR_GET_PDEATHSIG: c_int = 2; /// Get the current value of the parent process death signal. /// /// # References -/// - [Linux: `prctl(PR_GET_PDEATHSIG,...)`] -/// - [FreeBSD: `procctl(PROC_PDEATHSIG_STATUS,...)`] +/// - [Linux: `prctl(PR_GET_PDEATHSIG,…)`] +/// - [FreeBSD: `procctl(PROC_PDEATHSIG_STATUS,…)`] /// -/// [Linux: `prctl(PR_GET_PDEATHSIG,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html -/// [FreeBSD: `procctl(PROC_PDEATHSIG_STATUS,...)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 +/// [Linux: `prctl(PR_GET_PDEATHSIG,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [FreeBSD: `procctl(PROC_PDEATHSIG_STATUS,…)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 #[inline] #[doc(alias = "PR_GET_PDEATHSIG")] pub fn parent_process_death_signal() -> io::Result> { @@ -45,11 +45,11 @@ const PR_SET_PDEATHSIG: c_int = 1; /// Set the parent-death signal of the calling process. /// /// # References -/// - [Linux: `prctl(PR_SET_PDEATHSIG,...)`] -/// - [FreeBSD: `procctl(PROC_PDEATHSIG_CTL,...)`] +/// - [Linux: `prctl(PR_SET_PDEATHSIG,…)`] +/// - [FreeBSD: `procctl(PROC_PDEATHSIG_CTL,…)`] /// -/// [Linux: `prctl(PR_SET_PDEATHSIG,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html -/// [FreeBSD: `procctl(PROC_PDEATHSIG_CTL,...)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 +/// [Linux: `prctl(PR_SET_PDEATHSIG,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [FreeBSD: `procctl(PROC_PDEATHSIG_CTL,…)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 #[inline] #[doc(alias = "PR_SET_PDEATHSIG")] pub fn set_parent_process_death_signal(signal: Option) -> io::Result<()> { @@ -99,9 +99,9 @@ impl TryFrom for DumpableBehavior { /// Get the current state of the calling process' `dumpable` attribute. /// /// # References -/// - [`prctl(PR_GET_DUMPABLE,...)`] +/// - [`prctl(PR_GET_DUMPABLE,…)`] /// -/// [`prctl(PR_GET_DUMPABLE,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_GET_DUMPABLE,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_GET_DUMPABLE")] pub fn dumpable_behavior() -> io::Result { @@ -110,19 +110,20 @@ pub fn dumpable_behavior() -> io::Result { const PR_SET_DUMPABLE: c_int = 4; -/// Set the state of the `dumpable` attribute, which determines whether the -/// process can be traced and whether core dumps are produced for the calling -/// process upon delivery of a signal whose default behavior is to produce a -/// core dump. +/// Set the state of the `dumpable` attribute. +/// +/// This attribute determines whether the process can be traced and whether +/// core dumps are produced for the calling process upon delivery of a signal +/// whose default behavior is to produce a core dump. /// /// A similar function with the same name is available on FreeBSD (as part of /// the `procctl` interface), but it has an extra argument which allows to /// select a process other then the current process. /// /// # References -/// - [`prctl(PR_SET_DUMPABLE,...)`] +/// - [`prctl(PR_SET_DUMPABLE,…)`] /// -/// [`prctl(PR_SET_DUMPABLE,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_SET_DUMPABLE,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_SET_DUMPABLE")] pub fn set_dumpable_behavior(config: DumpableBehavior) -> io::Result<()> { @@ -157,9 +158,9 @@ bitflags! { /// Get unaligned access control bits. /// /// # References -/// - [`prctl(PR_GET_UNALIGN,...)`] +/// - [`prctl(PR_GET_UNALIGN,…)`] /// -/// [`prctl(PR_GET_UNALIGN,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_GET_UNALIGN,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_GET_UNALIGN")] pub fn unaligned_access_control() -> io::Result { @@ -172,9 +173,9 @@ const PR_SET_UNALIGN: c_int = 6; /// Set unaligned access control bits. /// /// # References -/// - [`prctl(PR_SET_UNALIGN,...)`] +/// - [`prctl(PR_SET_UNALIGN,…)`] /// -/// [`prctl(PR_SET_UNALIGN,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_SET_UNALIGN,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_SET_UNALIGN")] pub fn set_unaligned_access_control(config: UnalignedAccessControl) -> io::Result<()> { @@ -206,9 +207,9 @@ bitflags! { /// Get floating point emulation control bits. /// /// # References -/// - [`prctl(PR_GET_FPEMU,...)`] +/// - [`prctl(PR_GET_FPEMU,…)`] /// -/// [`prctl(PR_GET_FPEMU,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_GET_FPEMU,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_GET_FPEMU")] pub fn floating_point_emulation_control() -> io::Result { @@ -221,9 +222,9 @@ const PR_SET_FPEMU: c_int = 10; /// Set floating point emulation control bits. /// /// # References -/// - [`prctl(PR_SET_FPEMU,...)`] +/// - [`prctl(PR_SET_FPEMU,…)`] /// -/// [`prctl(PR_SET_FPEMU,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_SET_FPEMU,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_SET_FPEMU")] pub fn set_floating_point_emulation_control( @@ -268,9 +269,9 @@ bitflags! { /// Get floating point exception mode. /// /// # References -/// - [`prctl(PR_GET_FPEXC,...)`] +/// - [`prctl(PR_GET_FPEXC,…)`] /// -/// [`prctl(PR_GET_FPEXC,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_GET_FPEXC,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_GET_FPEXEC")] pub fn floating_point_exception_mode() -> io::Result> { @@ -283,9 +284,9 @@ const PR_SET_FPEXC: c_int = 12; /// Set floating point exception mode. /// /// # References -/// - [`prctl(PR_SET_FPEXC,...)`] +/// - [`prctl(PR_SET_FPEXC,…)`] /// -/// [`prctl(PR_SET_FPEXC,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_SET_FPEXC,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_SET_FPEXEC")] pub fn set_floating_point_exception_mode( @@ -330,9 +331,9 @@ impl TryFrom for TimingMethod { /// Get which process timing method is currently in use. /// /// # References -/// - [`prctl(PR_GET_TIMING,...)`] +/// - [`prctl(PR_GET_TIMING,…)`] /// -/// [`prctl(PR_GET_TIMING,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_GET_TIMING,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_GET_TIMING")] pub fn timing_method() -> io::Result { @@ -345,9 +346,9 @@ const PR_SET_TIMING: c_int = 14; /// accurate timestamp-based process timing. /// /// # References -/// - [`prctl(PR_SET_TIMING,...)`] +/// - [`prctl(PR_SET_TIMING,…)`] /// -/// [`prctl(PR_SET_TIMING,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_SET_TIMING,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_SET_TIMING")] pub fn set_timing_method(method: TimingMethod) -> io::Result<()> { @@ -392,9 +393,9 @@ impl TryFrom for EndianMode { /// Get the endianness of the calling process. /// /// # References -/// - [`prctl(PR_GET_ENDIAN,...)`] +/// - [`prctl(PR_GET_ENDIAN,…)`] /// -/// [`prctl(PR_GET_ENDIAN,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_GET_ENDIAN,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_GET_ENDIAN")] pub fn endian_mode() -> io::Result { @@ -406,14 +407,14 @@ const PR_SET_ENDIAN: c_int = 20; /// Set the endianness of the calling process. /// /// # References -/// - [`prctl(PR_SET_ENDIAN,...)`] +/// - [`prctl(PR_SET_ENDIAN,…)`] /// /// # Safety /// /// Please ensure the conditions necessary to safely call this function, as /// detailed in the references above. /// -/// [`prctl(PR_SET_ENDIAN,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_SET_ENDIAN,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_SET_ENDIAN")] pub unsafe fn set_endian_mode(mode: EndianMode) -> io::Result<()> { @@ -455,9 +456,9 @@ impl TryFrom for TimeStampCounterReadability { /// Get the state of the flag determining if the timestamp counter can be read. /// /// # References -/// - [`prctl(PR_GET_TSC,...)`] +/// - [`prctl(PR_GET_TSC,…)`] /// -/// [`prctl(PR_GET_TSC,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_GET_TSC,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_GET_TSC")] pub fn time_stamp_counter_readability() -> io::Result { @@ -470,9 +471,9 @@ const PR_SET_TSC: c_int = 26; /// by the process. /// /// # References -/// - [`prctl(PR_SET_TSC,...)`] +/// - [`prctl(PR_SET_TSC,…)`] /// -/// [`prctl(PR_SET_TSC,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_SET_TSC,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_SET_TSC")] pub fn set_time_stamp_counter_readability( @@ -491,11 +492,11 @@ const PR_TASK_PERF_EVENTS_ENABLE: c_int = 32; /// Enable or disable all performance counters attached to the calling process. /// /// # References -/// - [`prctl(PR_TASK_PERF_EVENTS_ENABLE,...)`] -/// - [`prctl(PR_TASK_PERF_EVENTS_DISABLE,...)`] +/// - [`prctl(PR_TASK_PERF_EVENTS_ENABLE,…)`] +/// - [`prctl(PR_TASK_PERF_EVENTS_DISABLE,…)`] /// -/// [`prctl(PR_TASK_PERF_EVENTS_ENABLE,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html -/// [`prctl(PR_TASK_PERF_EVENTS_DISABLE,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_TASK_PERF_EVENTS_ENABLE,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_TASK_PERF_EVENTS_DISABLE,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_TASK_PERF_EVENTS_ENABLE")] #[doc(alias = "PR_TASK_PERF_EVENTS_DISABLE")] @@ -552,9 +553,9 @@ impl TryFrom for MachineCheckMemoryCorruptionKillPolicy { /// Get the current per-process machine check kill policy. /// /// # References -/// - [`prctl(PR_MCE_KILL_GET,...)`] +/// - [`prctl(PR_MCE_KILL_GET,…)`] /// -/// [`prctl(PR_MCE_KILL_GET,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_MCE_KILL_GET,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_MCE_KILL_GET")] pub fn machine_check_memory_corruption_kill_policy( @@ -571,9 +572,9 @@ const PR_MCE_KILL_SET: usize = 1; /// Set the machine check memory corruption kill policy for the calling thread. /// /// # References -/// - [`prctl(PR_MCE_KILL,...)`] +/// - [`prctl(PR_MCE_KILL,…)`] /// -/// [`prctl(PR_MCE_KILL,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_MCE_KILL,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_MCE_KILL")] pub fn set_machine_check_memory_corruption_kill_policy( @@ -645,14 +646,14 @@ pub enum VirtualMemoryMapAddress { /// process. /// /// # References -/// - [`prctl(PR_SET_MM,...)`] +/// - [`prctl(PR_SET_MM,…)`] /// /// # Safety /// /// Please ensure the conditions necessary to safely call this function, as /// detailed in the references above. /// -/// [`prctl(PR_SET_MM,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_SET_MM,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_SET_MM")] pub unsafe fn set_virtual_memory_map_address( @@ -667,9 +668,9 @@ pub unsafe fn set_virtual_memory_map_address( /// new executable file. /// /// # References -/// - [`prctl(PR_SET_MM,PR_SET_MM_EXE_FILE,...)`] +/// - [`prctl(PR_SET_MM,PR_SET_MM_EXE_FILE,…)`] /// -/// [`prctl(PR_SET_MM,PR_SET_MM_EXE_FILE,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_SET_MM,PR_SET_MM_EXE_FILE,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_SET_MM")] #[doc(alias = "PR_SET_MM_EXE_FILE")] @@ -681,14 +682,14 @@ pub fn set_executable_file(fd: BorrowedFd<'_>) -> io::Result<()> { /// Set a new auxiliary vector. /// /// # References -/// - [`prctl(PR_SET_MM,PR_SET_MM_AUXV,...)`] +/// - [`prctl(PR_SET_MM,PR_SET_MM_AUXV,…)`] /// /// # Safety /// /// Please ensure the conditions necessary to safely call this function, as /// detailed in the references above. /// -/// [`prctl(PR_SET_MM,PR_SET_MM_AUXV,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_SET_MM,PR_SET_MM_AUXV,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_SET_MM")] #[doc(alias = "PR_SET_MM_AUXV")] @@ -706,9 +707,9 @@ pub unsafe fn set_auxiliary_vector(auxv: &[*const c_void]) -> io::Result<()> { /// Get the size of the [`PrctlMmMap`] the kernel expects. /// /// # References -/// - [`prctl(PR_SET_MM,PR_SET_MM_MAP_SIZE,...)`] +/// - [`prctl(PR_SET_MM,PR_SET_MM_MAP_SIZE,…)`] /// -/// [`prctl(PR_SET_MM,PR_SET_MM_MAP_SIZE,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_SET_MM,PR_SET_MM_MAP_SIZE,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_SET_MM")] #[doc(alias = "PR_SET_MM_MAP_SIZE")] @@ -760,14 +761,14 @@ pub struct PrctlMmMap { /// [`PrctlMmMap`]. /// /// # References -/// - [`prctl(PR_SET_MM,PR_SET_MM_MAP,...)`] +/// - [`prctl(PR_SET_MM,PR_SET_MM_MAP,…)`] /// /// # Safety /// /// Please ensure the conditions necessary to safely call this function, as /// detailed in the references above. /// -/// [`prctl(PR_SET_MM,PR_SET_MM_MAP,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_SET_MM,PR_SET_MM_MAP,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_SET_MM")] #[doc(alias = "PR_SET_MM_MAP")] @@ -805,9 +806,9 @@ pub enum PTracer { /// were a direct process ancestor. /// /// # References -/// - [`prctl(PR_SET_PTRACER,...)`] +/// - [`prctl(PR_SET_PTRACER,…)`] /// -/// [`prctl(PR_SET_PTRACER,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_SET_PTRACER,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_SET_PTRACER")] pub fn set_ptracer(tracer: PTracer) -> io::Result<()> { @@ -829,9 +830,9 @@ const PR_GET_CHILD_SUBREAPER: c_int = 37; /// Get the `child subreaper` setting of the calling process. /// /// # References -/// - [`prctl(PR_GET_CHILD_SUBREAPER,...)`] +/// - [`prctl(PR_GET_CHILD_SUBREAPER,…)`] /// -/// [`prctl(PR_GET_CHILD_SUBREAPER,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_GET_CHILD_SUBREAPER,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_GET_CHILD_SUBREAPER")] pub fn child_subreaper() -> io::Result> { @@ -846,9 +847,9 @@ const PR_SET_CHILD_SUBREAPER: c_int = 36; /// Set the `child subreaper` attribute of the calling process. /// /// # References -/// - [`prctl(PR_SET_CHILD_SUBREAPER,...)`] +/// - [`prctl(PR_SET_CHILD_SUBREAPER,…)`] /// -/// [`prctl(PR_SET_CHILD_SUBREAPER,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_SET_CHILD_SUBREAPER,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_SET_CHILD_SUBREAPER")] pub fn set_child_subreaper(pid: Option) -> io::Result<()> { @@ -891,9 +892,9 @@ impl TryFrom for FloatingPointMode { /// Get the current floating point mode. /// /// # References -/// - [`prctl(PR_GET_FP_MODE,...)`] +/// - [`prctl(PR_GET_FP_MODE,…)`] /// -/// [`prctl(PR_GET_FP_MODE,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_GET_FP_MODE,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_GET_FP_MODE")] pub fn floating_point_mode() -> io::Result { @@ -906,9 +907,9 @@ const PR_SET_FP_MODE: c_int = 45; /// Allow control of the floating point mode from user space. /// /// # References -/// - [`prctl(PR_SET_FP_MODE,...)`] +/// - [`prctl(PR_SET_FP_MODE,…)`] /// -/// [`prctl(PR_SET_FP_MODE,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_SET_FP_MODE,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_SET_FP_MODE")] pub fn set_floating_point_mode(mode: FloatingPointMode) -> io::Result<()> { @@ -993,9 +994,9 @@ bitflags! { /// Get the state of the speculation misfeature. /// /// # References -/// - [`prctl(PR_GET_SPECULATION_CTRL,...)`] +/// - [`prctl(PR_GET_SPECULATION_CTRL,…)`] /// -/// [`prctl(PR_GET_SPECULATION_CTRL,...)`]: https://www.kernel.org/doc/html/v5.18/userspace-api/spec_ctrl.html +/// [`prctl(PR_GET_SPECULATION_CTRL,…)`]: https://www.kernel.org/doc/html/v6.10/userspace-api/spec_ctrl.html #[inline] #[doc(alias = "PR_GET_SPECULATION_CTRL")] pub fn speculative_feature_state( @@ -1010,9 +1011,9 @@ const PR_SET_SPECULATION_CTRL: c_int = 53; /// Sets the state of the speculation misfeature. /// /// # References -/// - [`prctl(PR_SET_SPECULATION_CTRL,...)`] +/// - [`prctl(PR_SET_SPECULATION_CTRL,…)`] /// -/// [`prctl(PR_SET_SPECULATION_CTRL,...)`]: https://www.kernel.org/doc/html/v5.18/userspace-api/spec_ctrl.html +/// [`prctl(PR_SET_SPECULATION_CTRL,…)`]: https://www.kernel.org/doc/html/v6.10/userspace-api/spec_ctrl.html #[inline] #[doc(alias = "PR_SET_SPECULATION_CTRL")] pub fn control_speculative_feature( @@ -1033,9 +1034,9 @@ const PR_GET_IO_FLUSHER: c_int = 58; /// Get the `IO_FLUSHER` state of the caller. /// /// # References -/// - [`prctl(PR_GET_IO_FLUSHER,...)`] +/// - [`prctl(PR_GET_IO_FLUSHER,…)`] /// -/// [`prctl(PR_GET_IO_FLUSHER,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_GET_IO_FLUSHER,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_GET_IO_FLUSHER")] pub fn is_io_flusher() -> io::Result { @@ -1048,9 +1049,9 @@ const PR_SET_IO_FLUSHER: c_int = 57; /// when allocating memory. /// /// # References -/// - [`prctl(PR_SET_IO_FLUSHER,...)`] +/// - [`prctl(PR_SET_IO_FLUSHER,…)`] /// -/// [`prctl(PR_SET_IO_FLUSHER,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_SET_IO_FLUSHER,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_SET_IO_FLUSHER")] pub fn configure_io_flusher_behavior(enable: bool) -> io::Result<()> { @@ -1066,9 +1067,9 @@ const PR_PAC_GET_ENABLED_KEYS: c_int = 61; /// Get enabled pointer authentication keys. /// /// # References -/// - [`prctl(PR_PAC_GET_ENABLED_KEYS,...)`] +/// - [`prctl(PR_PAC_GET_ENABLED_KEYS,…)`] /// -/// [`prctl(PR_PAC_GET_ENABLED_KEYS,...)`]: https://www.kernel.org/doc/html/v5.18/arm64/pointer-authentication.html +/// [`prctl(PR_PAC_GET_ENABLED_KEYS,…)`]: https://www.kernel.org/doc/html/v6.10/arch/arm64/pointer-authentication.html #[inline] #[doc(alias = "PR_PAC_GET_ENABLED_KEYS")] pub fn enabled_pointer_authentication_keys() -> io::Result { @@ -1081,14 +1082,14 @@ const PR_PAC_SET_ENABLED_KEYS: c_int = 60; /// Set enabled pointer authentication keys. /// /// # References -/// - [`prctl(PR_PAC_SET_ENABLED_KEYS,...)`] +/// - [`prctl(PR_PAC_SET_ENABLED_KEYS,…)`] /// /// # Safety /// /// Please ensure the conditions necessary to safely call this function, as /// detailed in the references above. /// -/// [`prctl(PR_PAC_SET_ENABLED_KEYS,...)`]: https://www.kernel.org/doc/html/v5.18/arm64/pointer-authentication.html +/// [`prctl(PR_PAC_SET_ENABLED_KEYS,…)`]: https://www.kernel.org/doc/html/v6.10/arch/arm64/pointer-authentication.html #[inline] #[doc(alias = "PR_PAC_SET_ENABLED_KEYS")] pub unsafe fn configure_pointer_authentication_keys( @@ -1131,9 +1132,9 @@ const PR_SET_VMA_ANON_NAME: usize = 0; /// Set the name for a virtual memory region. /// /// # References -/// - [`prctl(PR_SET_VMA,PR_SET_VMA_ANON_NAME,...)`] +/// - [`prctl(PR_SET_VMA,PR_SET_VMA_ANON_NAME,…)`] /// -/// [`prctl(PR_SET_VMA,PR_SET_VMA_ANON_NAME,...)`]: https://lwn.net/Articles/867818/ +/// [`prctl(PR_SET_VMA,PR_SET_VMA_ANON_NAME,…)`]: https://lwn.net/Articles/867818/ #[inline] #[doc(alias = "PR_SET_VMA")] #[doc(alias = "PR_SET_VMA_ANON_NAME")] diff --git a/src/process/procctl.rs b/src/process/procctl.rs index bb6372299..e198d03b0 100644 --- a/src/process/procctl.rs +++ b/src/process/procctl.rs @@ -85,11 +85,11 @@ const PROC_PDEATHSIG_STATUS: c_int = 12; /// Get the current value of the parent process death signal. /// /// # References -/// - [Linux: `prctl(PR_GET_PDEATHSIG,...)`] -/// - [FreeBSD: `procctl(PROC_PDEATHSIG_STATUS,...)`] +/// - [Linux: `prctl(PR_GET_PDEATHSIG,…)`] +/// - [FreeBSD: `procctl(PROC_PDEATHSIG_STATUS,…)`] /// -/// [Linux: `prctl(PR_GET_PDEATHSIG,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html -/// [FreeBSD: `procctl(PROC_PDEATHSIG_STATUS,...)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 +/// [Linux: `prctl(PR_GET_PDEATHSIG,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [FreeBSD: `procctl(PROC_PDEATHSIG_STATUS,…)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 #[inline] pub fn parent_process_death_signal() -> io::Result> { unsafe { procctl_get_optional::(PROC_PDEATHSIG_STATUS, None) }.map(Signal::from_raw) @@ -100,11 +100,11 @@ const PROC_PDEATHSIG_CTL: c_int = 11; /// Set the parent-death signal of the calling process. /// /// # References -/// - [Linux: `prctl(PR_SET_PDEATHSIG,...)`] -/// - [FreeBSD: `procctl(PROC_PDEATHSIG_CTL,...)`] +/// - [Linux: `prctl(PR_SET_PDEATHSIG,…)`] +/// - [FreeBSD: `procctl(PROC_PDEATHSIG_CTL,…)`] /// -/// [Linux: `prctl(PR_SET_PDEATHSIG,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html -/// [FreeBSD: `procctl(PROC_PDEATHSIG_CTL,...)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 +/// [Linux: `prctl(PR_SET_PDEATHSIG,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [FreeBSD: `procctl(PROC_PDEATHSIG_CTL,…)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 #[inline] pub fn set_parent_process_death_signal(signal: Option) -> io::Result<()> { let signal = signal.map_or(0, |signal| signal as c_int); @@ -144,9 +144,9 @@ pub enum DumpableBehavior { /// Linux. /// /// # References -/// - [FreeBSD `procctl(PROC_TRACE_CTL,...)`] +/// - [FreeBSD `procctl(PROC_TRACE_CTL,…)`] /// -/// [FreeBSD `procctl(PROC_TRACE_CTL,...)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 +/// [FreeBSD `procctl(PROC_TRACE_CTL,…)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 #[inline] pub fn set_dumpable_behavior(process: ProcSelector, config: DumpableBehavior) -> io::Result<()> { unsafe { procctl(PROC_TRACE_CTL, process, config as usize as *mut _) } @@ -174,9 +174,9 @@ pub enum TracingStatus { /// Get the tracing status of the process indicated by `idtype` and `id`. /// /// # References -/// - [FreeBSD `procctl(PROC_TRACE_STATUS,...)`] +/// - [FreeBSD `procctl(PROC_TRACE_STATUS,…)`] /// -/// [FreeBSD `procctl(PROC_TRACE_STATUS,...)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 +/// [FreeBSD `procctl(PROC_TRACE_STATUS,…)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 #[inline] pub fn trace_status(process: ProcSelector) -> io::Result { let val = unsafe { procctl_get_optional::(PROC_TRACE_STATUS, process) }?; @@ -200,9 +200,9 @@ const PROC_REAP_RELEASE: c_int = 3; /// Acquire or release the reaper status of the calling process. /// /// # References -/// - [FreeBSD: `procctl(PROC_REAP_ACQUIRE/RELEASE,...)`] +/// - [FreeBSD: `procctl(PROC_REAP_ACQUIRE/RELEASE,…)`] /// -/// [FreeBSD: `procctl(PROC_REAP_ACQUIRE/RELEASE,...)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 +/// [FreeBSD: `procctl(PROC_REAP_ACQUIRE/RELEASE,…)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 #[inline] pub fn set_reaper_status(reaper: bool) -> io::Result<()> { unsafe { @@ -265,9 +265,9 @@ pub struct ReaperStatus { /// itself if it is a reaper). /// /// # References -/// - [FreeBSD: `procctl(PROC_REAP_STATUS,...)`] +/// - [FreeBSD: `procctl(PROC_REAP_STATUS,…)`] /// -/// [FreeBSD: `procctl(PROC_REAP_STATUS,...)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 +/// [FreeBSD: `procctl(PROC_REAP_STATUS,…)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 #[inline] pub fn get_reaper_status(process: ProcSelector) -> io::Result { let raw = unsafe { procctl_get_optional::(PROC_REAP_STATUS, process) }?; @@ -284,6 +284,7 @@ pub fn get_reaper_status(process: ProcSelector) -> io::Result { }) } +#[cfg(feature = "alloc")] const PROC_REAP_GETPIDS: c_int = 5; bitflags! { @@ -331,7 +332,7 @@ pub struct PidInfo { pub flags: PidInfoFlags, /// The pid of the process. pub pid: Pid, - /// The pid of the child of the reaper which is the (grand-..)parent of the + /// The pid of the child of the reaper which is the (grand-…)parent of the /// process. pub subtree: Pid, } @@ -339,13 +340,13 @@ pub struct PidInfo { /// Get the list of descendants of the specified reaper process. /// /// # References -/// - [FreeBSD: `procctl(PROC_REAP_GETPIDS,...)`] +/// - [FreeBSD: `procctl(PROC_REAP_GETPIDS,…)`] /// -/// [FreeBSD: `procctl(PROC_REAP_GETPIDS,...)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 +/// [FreeBSD: `procctl(PROC_REAP_GETPIDS,…)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 #[cfg(feature = "alloc")] pub fn get_reaper_pids(process: ProcSelector) -> io::Result> { // Sadly no better way to guarantee that we get all the results than to - // allocate ~8MB of memory.. + // allocate ≈8MB of memory… const PID_MAX: usize = 99999; let mut pids: Vec = vec![Default::default(); PID_MAX]; let mut pinfo = procctl_reaper_pids { @@ -400,12 +401,12 @@ pub struct KillResult { pub first_failed: Option, } -/// Deliver a signal to some subset of +/// Deliver a signal to some subset of the descendants of the reaper. /// /// # References -/// - [FreeBSD: `procctl(PROC_REAP_KILL,...)`] +/// - [FreeBSD: `procctl(PROC_REAP_KILL,…)`] /// -/// [FreeBSD: `procctl(PROC_REAP_KILL,...)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 +/// [FreeBSD: `procctl(PROC_REAP_KILL,…)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 pub fn reaper_kill( process: ProcSelector, signal: Signal, @@ -454,15 +455,15 @@ pub enum TrapCapBehavior { /// Set the current value of the capability mode violation trapping behavior. /// If this behavior is enabled, the kernel would deliver a [`Signal::Trap`] /// signal on any return from a system call that would result in a -/// [`io::Errno::NOTCAPABLE`]` or [`io::Errno::CAPMODE`] error. +/// [`io::Errno::NOTCAPABLE`] or [`io::Errno::CAPMODE`] error. /// /// This behavior is inherited by the children of the process and is kept /// across `execve` calls. /// /// # References -/// - [FreeBSD: `procctl(PROC_TRAPCAP_CTL,...)`] +/// - [FreeBSD: `procctl(PROC_TRAPCAP_CTL,…)`] /// -/// [FreeBSD: `procctl(PROC_TRAPCAP_CTL,...)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 +/// [FreeBSD: `procctl(PROC_TRAPCAP_CTL,…)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 #[inline] pub fn set_trap_cap_behavior(process: ProcSelector, config: TrapCapBehavior) -> io::Result<()> { let config = config as c_int; @@ -474,9 +475,9 @@ const PROC_TRAPCAP_STATUS: c_int = 10; /// Get the current value of the capability mode violation trapping behavior. /// /// # References -/// - [FreeBSD: `procctl(PROC_TRAPCAP_STATUS,...)`] +/// - [FreeBSD: `procctl(PROC_TRAPCAP_STATUS,…)`] /// -/// [FreeBSD: `procctl(PROC_TRAPCAP_STATUS,...)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 +/// [FreeBSD: `procctl(PROC_TRAPCAP_STATUS,…)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 #[inline] pub fn trap_cap_behavior(process: ProcSelector) -> io::Result { let val = unsafe { procctl_get_optional::(PROC_TRAPCAP_STATUS, process) }?; @@ -503,11 +504,11 @@ const PROC_NO_NEW_PRIVS_ENABLE: c_int = 1; /// to enable this mode and there's no going back. /// /// # References -/// - [Linux: `prctl(PR_SET_NO_NEW_PRIVS,...)`] -/// - [FreeBSD: `procctl(PROC_NO_NEW_PRIVS_CTL,...)`] +/// - [Linux: `prctl(PR_SET_NO_NEW_PRIVS,…)`] +/// - [FreeBSD: `procctl(PROC_NO_NEW_PRIVS_CTL,…)`] /// -/// [Linux: `prctl(PR_SET_NO_NEW_PRIVS,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html -/// [FreeBSD: `procctl(PROC_NO_NEW_PRIVS_CTL,...)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 +/// [Linux: `prctl(PR_SET_NO_NEW_PRIVS,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [FreeBSD: `procctl(PROC_NO_NEW_PRIVS_CTL,…)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 #[inline] pub fn set_no_new_privs(process: ProcSelector) -> io::Result<()> { unsafe { procctl_set::(PROC_NO_NEW_PRIVS_CTL, process, &PROC_NO_NEW_PRIVS_ENABLE) } @@ -518,11 +519,11 @@ const PROC_NO_NEW_PRIVS_STATUS: c_int = 20; /// Check the `no_new_privs` mode of the specified process. /// /// # References -/// - [Linux: `prctl(PR_GET_NO_NEW_PRIVS,...)`] -/// - [FreeBSD: `procctl(PROC_NO_NEW_PRIVS_STATUS,...)`] +/// - [Linux: `prctl(PR_GET_NO_NEW_PRIVS,…)`] +/// - [FreeBSD: `procctl(PROC_NO_NEW_PRIVS_STATUS,…)`] /// -/// [Linux: `prctl(PR_GET_NO_NEW_PRIVS,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html -/// [FreeBSD: `procctl(PROC_NO_NEW_PRIVS_STATUS,...)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 +/// [Linux: `prctl(PR_GET_NO_NEW_PRIVS,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [FreeBSD: `procctl(PROC_NO_NEW_PRIVS_STATUS,…)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 #[inline] pub fn no_new_privs(process: ProcSelector) -> io::Result { unsafe { procctl_get_optional::(PROC_NO_NEW_PRIVS_STATUS, process) } diff --git a/src/process/sched.rs b/src/process/sched.rs index 211d25d1f..f9614e8a0 100644 --- a/src/process/sched.rs +++ b/src/process/sched.rs @@ -77,27 +77,27 @@ impl Default for CpuSet { } impl fmt::Debug for CpuSet { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(fmt, "CpuSet {{")?; + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "CpuSet {{")?; let mut first = true; - for i in 0..CpuSet::MAX_CPU { + for i in 0..Self::MAX_CPU { if self.is_set(i) { if first { - write!(fmt, " ")?; + write!(f, " ")?; first = false; } else { - write!(fmt, ", ")?; + write!(f, ", ")?; } - write!(fmt, "cpu{}", i)?; + write!(f, "cpu{}", i)?; } } - write!(fmt, " }}") + write!(f, " }}") } } impl hash::Hash for CpuSet { fn hash(&self, state: &mut H) { - for i in 0..CpuSet::MAX_CPU { + for i in 0..Self::MAX_CPU { self.is_set(i).hash(state); } } @@ -151,7 +151,7 @@ pub fn sched_getaffinity(pid: Option) -> io::Result { /// - [Linux] /// - [DragonFly BSD] /// -/// [Linux]: https://man7.org/linux/man-pages/man2/sched_getcpu.2.html +/// [Linux]: https://man7.org/linux/man-pages/man3/sched_getcpu.3.html /// [DragonFly BSD]: https://man.dragonflybsd.org/?command=sched_getcpu§ion=2 // FreeBSD added `sched_getcpu` in 13.0. #[cfg(any(linux_kernel, target_os = "dragonfly"))] diff --git a/src/process/umask.rs b/src/process/umask.rs index 01779d7ed..c8655faef 100644 --- a/src/process/umask.rs +++ b/src/process/umask.rs @@ -14,7 +14,7 @@ use crate::fs::Mode; /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/umask.html /// [Linux]: https://man7.org/linux/man-pages/man2/umask.2.html #[cfg(feature = "fs")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "fs")))] +#[cfg_attr(docsrs, doc(cfg(feature = "fs")))] #[inline] pub fn umask(mask: Mode) -> Mode { backend::process::syscalls::umask(mask) diff --git a/src/process/wait.rs b/src/process/wait.rs index 4d0e6e25d..d6edab162 100644 --- a/src/process/wait.rs +++ b/src/process/wait.rs @@ -15,12 +15,14 @@ bitflags! { pub struct WaitOptions: u32 { /// Return immediately if no child has exited. const NOHANG = bitcast!(backend::process::wait::WNOHANG); - /// Return if a child has stopped (but not traced via [`ptrace`]) + /// Return if a child has stopped (but not traced via [`ptrace`]). /// /// [`ptrace`]: https://man7.org/linux/man-pages/man2/ptrace.2.html const UNTRACED = bitcast!(backend::process::wait::WUNTRACED); /// Return if a stopped child has been resumed by delivery of /// [`Signal::Cont`]. + /// + /// [`Signal::Cont`]: crate::process::Signal::Cont const CONTINUED = bitcast!(backend::process::wait::WCONTINUED); /// @@ -38,6 +40,8 @@ bitflags! { const NOHANG = bitcast!(backend::process::wait::WNOHANG); /// Return if a stopped child has been resumed by delivery of /// [`Signal::Cont`]. + /// + /// [`Signal::Cont`]: crate::process::Signal::Cont const CONTINUED = bitcast!(backend::process::wait::WCONTINUED); /// Wait for processed that have exited. const EXITED = bitcast!(backend::process::wait::WEXITED); diff --git a/src/procfs.rs b/src/procfs.rs index 60a4b6c34..384f0243b 100644 --- a/src/procfs.rs +++ b/src/procfs.rs @@ -228,7 +228,8 @@ fn is_mountpoint(file: BorrowedFd<'_>) -> bool { fn proc_opendirat(dirfd: Fd, path: P) -> io::Result { // We don't add `PATH` here because that disables `DIRECTORY`. And we don't // add `NOATIME` for the same reason as the comment in `open_and_check_file`. - let oflags = OFlags::NOFOLLOW | OFlags::DIRECTORY | OFlags::CLOEXEC | OFlags::NOCTTY; + let oflags = + OFlags::RDONLY | OFlags::NOFOLLOW | OFlags::DIRECTORY | OFlags::CLOEXEC | OFlags::NOCTTY; openat(dirfd, path, oflags, Mode::empty()).map_err(|_err| io::Errno::NOTSUP) } @@ -244,7 +245,7 @@ fn proc_opendirat(dirfd: Fd, path: P) -> io::Resu fn proc() -> io::Result<(BorrowedFd<'static>, &'static Stat)> { static PROC: StaticFd = StaticFd::new(); - // `OnceBox` is "racey" in that the initialization function may run + // `OnceBox` is “racy” in that the initialization function may run // multiple times. We're ok with that, since the initialization function // has no side effects. PROC.get_or_try_init(|| { @@ -308,7 +309,7 @@ fn proc_self() -> io::Result<(BorrowedFd<'static>, &'static Stat)> { /// - [Linux] /// /// [Linux]: https://man7.org/linux/man-pages/man5/proc.5.html -#[cfg_attr(doc_cfg, doc(cfg(feature = "procfs")))] +#[cfg_attr(docsrs, doc(cfg(feature = "procfs")))] pub fn proc_self_fd() -> io::Result> { static PROC_SELF_FD: StaticFd = StaticFd::new(); @@ -377,7 +378,7 @@ fn proc_self_fdinfo() -> io::Result<(BorrowedFd<'static>, &'static Stat)> { /// /// [Linux]: https://man7.org/linux/man-pages/man5/proc.5.html #[inline] -#[cfg_attr(doc_cfg, doc(cfg(feature = "procfs")))] +#[cfg_attr(docsrs, doc(cfg(feature = "procfs")))] pub fn proc_self_fdinfo_fd(fd: Fd) -> io::Result { _proc_self_fdinfo(fd.as_fd()) } @@ -405,7 +406,7 @@ fn _proc_self_fdinfo(fd: BorrowedFd<'_>) -> io::Result { /// [Linux]: https://man7.org/linux/man-pages/man5/proc.5.html /// [Linux pagemap]: https://www.kernel.org/doc/Documentation/vm/pagemap.txt #[inline] -#[cfg_attr(doc_cfg, doc(cfg(feature = "procfs")))] +#[cfg_attr(docsrs, doc(cfg(feature = "procfs")))] pub fn proc_self_pagemap() -> io::Result { proc_self_file(cstr!("pagemap")) } @@ -420,7 +421,7 @@ pub fn proc_self_pagemap() -> io::Result { /// /// [Linux]: https://man7.org/linux/man-pages/man5/proc.5.html #[inline] -#[cfg_attr(doc_cfg, doc(cfg(feature = "procfs")))] +#[cfg_attr(docsrs, doc(cfg(feature = "procfs")))] pub fn proc_self_maps() -> io::Result { proc_self_file(cstr!("maps")) } @@ -435,7 +436,7 @@ pub fn proc_self_maps() -> io::Result { /// /// [Linux]: https://man7.org/linux/man-pages/man5/proc.5.html #[inline] -#[cfg_attr(doc_cfg, doc(cfg(feature = "procfs")))] +#[cfg_attr(docsrs, doc(cfg(feature = "procfs")))] pub fn proc_self_status() -> io::Result { proc_self_file(cstr!("status")) } @@ -488,8 +489,18 @@ fn open_and_check_file( let mut found_file = false; let mut found_dot = false; + // Open a new fd, so that if we're called on multiple threads, they don't + // share a seek position. + let oflags = + OFlags::RDONLY | OFlags::CLOEXEC | OFlags::NOFOLLOW | OFlags::NOCTTY | OFlags::DIRECTORY; + let dir = openat(dir, cstr!("."), oflags, Mode::empty()).map_err(|_err| io::Errno::NOTSUP)?; + let check_dir_stat = fstat(&dir)?; + if check_dir_stat.st_dev != dir_stat.st_dev || check_dir_stat.st_ino != dir_stat.st_ino { + return Err(io::Errno::NOTSUP); + } + // Position the directory iteration at the start. - seek(dir, SeekFrom::Start(0))?; + seek(&dir, SeekFrom::Start(0))?; let mut buf = [MaybeUninit::uninit(); 2048]; let mut iter = RawDir::new(dir, &mut buf); diff --git a/src/pty.rs b/src/pty.rs index 926ebf789..4a9ca7812 100644 --- a/src/pty.rs +++ b/src/pty.rs @@ -141,7 +141,7 @@ pub fn unlockpt(fd: Fd) -> io::Result<()> { /// `grantpt(fd)`—Grant access to the user side of a pseudoterminal. /// /// On Linux, calling this function has no effect, as the kernel is expected to -/// grant the appropriate access. On all other platorms, this function has +/// grant the appropriate access. On all other platforms, this function has /// unspecified behavior if the calling process has a [`Signal::Child`] signal /// handler installed. /// @@ -170,7 +170,7 @@ pub fn grantpt(fd: Fd) -> io::Result<()> { } } -/// `ioctl(fd, TIOCGPTPEER)`—Open the user side of a pseduoterminal. +/// `ioctl(fd, TIOCGPTPEER)`—Open the user side of a pseudoterminal. /// /// This function is currently only implemented on Linux. /// diff --git a/src/rand/getrandom.rs b/src/rand/getrandom.rs index c7f117ad9..ab838c40c 100644 --- a/src/rand/getrandom.rs +++ b/src/rand/getrandom.rs @@ -1,3 +1,5 @@ +//! Wrappers for `getrandom`. + #![allow(unsafe_code)] use crate::buffer::split_init; @@ -11,6 +13,9 @@ pub use backend::rand::types::GetRandomFlags; /// This is a very low-level API which may be difficult to use correctly. Most /// users should prefer to use [`getrandom`] or [`rand`] APIs instead. /// +/// This takes a `&mut [u8]` which Rust requires to contain initialized memory. +/// To use an uninitialized buffer, use [`getrandom_uninit`]. +/// /// [`getrandom`]: https://crates.io/crates/getrandom /// [`rand`]: https://crates.io/crates/rand /// @@ -35,7 +40,7 @@ pub fn getrandom_uninit( ) -> io::Result<(&mut [u8], &mut [MaybeUninit])> { // Get number of initialized bytes. let length = unsafe { - backend::rand::syscalls::getrandom(buf.as_mut_ptr() as *mut u8, buf.len(), flags) + backend::rand::syscalls::getrandom(buf.as_mut_ptr().cast::(), buf.len(), flags) }; // Split into the initialized and uninitialized portions. diff --git a/src/runtime.rs b/src/runtime.rs index bd3eed2b3..43ac220ec 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -1,12 +1,24 @@ //! Experimental low-level implementation details for libc-like runtime //! libraries such as [Origin]. //! -//! Do not use the functions in this module unless you've read all of their -//! code. They don't always behave the same way as functions with similar names -//! in `libc`. Sometimes information about the differences is included in the -//! Linux documentation under “C library/kernel differences” sections. And, if -//! there is a libc in the process, these functions may have surprising -//! interactions with it. +//! ⚠ These are not normal functions. ⚠ +//! +//! - Some of the functions in this module cannot be used in a process which +//! also has a libc present. This can be true even for functions that have +//! the same name as a libc function that Rust code can use. +//! +//! - Some of the functions in this module don't behave exactly the same way +//! as functions in libc with similar names. Sometimes information about the +//! differences is included in the Linux documentation under “C +//! library/kernel differences” sections. But not always. +//! +//! - The safety requirements of the functions in this module are not fully +//! documented. +//! +//! - The API for these functions is not considered stable, and this module is +//! `doc(hidden)`. +//! +//! ⚠ Caution is indicated. ⚠ //! //! These functions are for implementing thread-local storage (TLS), managing //! threads, loaded libraries, and other process-wide resources. Most of @@ -14,9 +26,6 @@ //! program or what they're doing, but the features in this module generally //! can only be used by one entity within a process. //! -//! The API for these functions is not stable, and this module is -//! `doc(hidden)`. -//! //! [Origin]: https://github.com/sunfishcode/origin#readme //! //! # Safety @@ -320,7 +329,12 @@ pub unsafe fn fork() -> io::Result { /// the child can just do `getpid`. That's true, but it's more fun if it /// doesn't have to. pub enum Fork { + /// This is returned in the child process after a `fork`. It holds the PID + /// of the child. Child(Pid), + + /// This is returned in the parent process after a `fork`. It holds the PID + /// of the child. Parent(Pid), } @@ -338,7 +352,7 @@ pub enum Fork { /// [Linux]: https://man7.org/linux/man-pages/man2/execveat.2.html #[inline] #[cfg(feature = "fs")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "fs")))] +#[cfg_attr(docsrs, doc(cfg(feature = "fs")))] pub unsafe fn execveat( dirfd: Fd, path: &CStr, @@ -408,8 +422,8 @@ pub unsafe fn sigaltstack(new: Option) -> io::Result { /// # Safety /// /// You're on your own. And on top of all the troubles with signal handlers, -/// this implementation is highly experimental. The warning about the hazard -/// of recycled thread ID's applies. +/// this implementation is highly experimental. Also, this is not `tgkill`, so +/// the warning about the hazard of recycled thread ID's applies. /// /// # References /// - [Linux] @@ -420,7 +434,7 @@ pub unsafe fn tkill(tid: Pid, sig: Signal) -> io::Result<()> { backend::runtime::syscalls::tkill(tid, sig) } -/// `sigprocmask(how, set, oldset)`—Adjust the process signal mask. +/// `rt_sigprocmask(how, set, oldset)`—Adjust the process signal mask. /// /// # Safety /// @@ -429,13 +443,14 @@ pub unsafe fn tkill(tid: Pid, sig: Signal) -> io::Result<()> { /// the libc `sigprocmask` in several non-obvious and unsafe ways. /// /// # References -/// - [Linux `sigprocmask`] +/// - [Linux `rt_sigprocmask`] /// - [Linux `pthread_sigmask`] /// -/// [Linux `sigprocmask`]: https://man7.org/linux/man-pages/man2/sigprocmask.2.html +/// [Linux `rt_sigprocmask`]: https://man7.org/linux/man-pages/man2/rt_sigprocmask.2.html /// [Linux `pthread_sigmask`]: https://man7.org/linux/man-pages/man3/pthread_sigmask.3.html #[inline] #[doc(alias = "pthread_sigmask")] +#[doc(alias = "rt_sigprocmask")] pub unsafe fn sigprocmask(how: How, set: Option<&Sigset>) -> io::Result { backend::runtime::syscalls::sigprocmask(how, set) } @@ -573,7 +588,7 @@ pub const SIGRTMAX: u32 = { linux_raw_sys::general::SIGRTMAX } - // On platfoms that don't, derive it from `_NSIG`. + // On platforms that don't, derive it from `_NSIG`. #[cfg(any(target_arch = "arm", target_arch = "x86", target_arch = "x86_64"))] { linux_raw_sys::general::_NSIG - 1 diff --git a/src/shm.rs b/src/shm.rs index 450b6fcc6..1904dc4f9 100644 --- a/src/shm.rs +++ b/src/shm.rs @@ -1,10 +1,73 @@ //! POSIX shared memory +//! +//! # Example +//! +//! ``` +//! use rustix::fs::{ftruncate, Mode}; +//! use rustix::mm::{mmap, MapFlags, ProtFlags}; +//! use rustix::{io, shm}; +//! use std::mem::size_of; +//! use std::ptr::null_mut; +//! +//! # fn example() -> io::Result<()> { +//! // A type describing the data to be shared. +//! #[repr(C)] +//! struct MyBufferType { +//! // … +//! } +//! +//! // Create the shared memory object. +//! let shm_path = "/rustix-shm-example"; +//! let fd = shm::open( +//! shm_path, +//! shm::OFlags::CREATE | shm::OFlags::EXCL | shm::OFlags::RDWR, +//! Mode::RUSR | Mode::WUSR, +//! )?; +//! +//! // Resize the shared memory object to the size of our data. +//! ftruncate(&fd, size_of::() as u64)?; +//! +//! // Map the shared memory object into our address space. +//! // +//! // SAFETY: We're creating a new mapping that's independent of any existing +//! // memory allocations. There are interesting things to say about *using* +//! // `ptr`, but that's for another safety comment. +//! let ptr = unsafe { +//! mmap( +//! null_mut(), +//! size_of::(), +//! ProtFlags::READ | ProtFlags::WRITE, +//! MapFlags::SHARED, +//! &fd, +//! 0, +//! )? +//! }; +//! +//! // Use `ptr`… +//! +//! // Remove the shared memory object name. +//! shm::unlink(shm_path)?; +//! # Ok(()) +//! # } +//! ``` + +#![allow(unused_qualifications)] use crate::fd::OwnedFd; use crate::{backend, io, path}; +use super::shm; pub use crate::backend::fs::types::Mode; +pub use crate::backend::shm::types::ShmOFlags as OFlags; +#[deprecated(note = "Use `shm::OFlags`.")] +#[doc(hidden)] pub use crate::backend::shm::types::ShmOFlags; +#[deprecated(note = "Use `shm::open`.")] +#[doc(hidden)] +pub use open as shm_open; +#[deprecated(note = "Use `shm::unlink`.")] +#[doc(hidden)] +pub use unlink as shm_unlink; /// `shm_open(name, oflags, mode)`—Opens a shared memory object. /// @@ -12,7 +75,7 @@ pub use crate::backend::shm::types::ShmOFlags; /// slashes, and be no longer than an implementation-defined limit (255 on /// Linux). /// -/// Exactly one of [`ShmOFlags::RDONLY`] and [`ShmOFlags::RDWR`] should be +/// Exactly one of [`shm::OFlags::RDONLY`] and [`shm::OFlags::RDWR`] should be /// passed. The file descriptor will be opened with `FD_CLOEXEC` set. /// /// # References @@ -21,8 +84,9 @@ pub use crate::backend::shm::types::ShmOFlags; /// /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/shm_open.html /// [Linux]: https://man7.org/linux/man-pages/man3/shm_open.3.html +#[doc(alias = "shm_open")] #[inline] -pub fn shm_open(name: P, flags: ShmOFlags, mode: Mode) -> io::Result { +pub fn open(name: P, flags: shm::OFlags, mode: Mode) -> io::Result { name.into_with_c_str(|name| backend::shm::syscalls::shm_open(name, flags, mode)) } @@ -34,7 +98,8 @@ pub fn shm_open(name: P, flags: ShmOFlags, mode: Mode) -> io::Resu /// /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/shm_unlink.html /// [Linux]: https://man7.org/linux/man-pages/man3/shm_unlink.3.html +#[doc(alias = "shm_unlink")] #[inline] -pub fn shm_unlink(name: P) -> io::Result<()> { +pub fn unlink(name: P) -> io::Result<()> { name.into_with_c_str(backend::shm::syscalls::shm_unlink) } diff --git a/src/signal.rs b/src/signal.rs index e55126f37..91ab5c697 100644 --- a/src/signal.rs +++ b/src/signal.rs @@ -50,6 +50,7 @@ pub enum Signal { solarish, target_os = "aix", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", all( @@ -108,7 +109,7 @@ pub enum Signal { #[cfg(not(any(target_os = "haiku", target_os = "vita")))] Io = c::SIGIO, /// `SIGPWR` - #[cfg(not(any(bsd, target_os = "haiku", target_os = "vita")))] + #[cfg(not(any(bsd, target_os = "haiku", target_os = "hurd", target_os = "vita")))] #[doc(alias = "Pwr")] Power = c::SIGPWR, /// `SIGSYS`, aka `SIGUNUSED` @@ -171,6 +172,7 @@ impl Signal { solarish, target_os = "aix", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", all( @@ -212,7 +214,7 @@ impl Signal { c::SIGWINCH => Some(Self::Winch), #[cfg(not(any(target_os = "haiku", target_os = "vita")))] c::SIGIO => Some(Self::Io), - #[cfg(not(any(bsd, target_os = "haiku", target_os = "vita")))] + #[cfg(not(any(bsd, target_os = "haiku", target_os = "hurd", target_os = "vita")))] c::SIGPWR => Some(Self::Power), c::SIGSYS => Some(Self::Sys), #[cfg(any( diff --git a/src/stdio.rs b/src/stdio.rs index 99e118b35..528c6cf0f 100644 --- a/src/stdio.rs +++ b/src/stdio.rs @@ -4,8 +4,8 @@ //! //! These access the file descriptors by absolute index value, and nothing //! prevents them from being closed and reused. They should only be used in -//! `main` or other situations where one is in control of the process' -//! stdio streams. +//! `main` or other situations where one is in control of the process' stdio +//! streams. #![allow(unsafe_code)] use crate::backend; @@ -14,7 +14,11 @@ use backend::c; use backend::fd::{BorrowedFd, FromRawFd, RawFd}; #[cfg(not(any(windows, target_os = "wasi")))] -use {crate::io, backend::fd::AsFd, core::mem::forget}; +use { + crate::io, + backend::fd::{AsFd, AsRawFd}, + core::mem::ManuallyDrop, +}; /// `STDIN_FILENO`—Standard input, borrowed. /// @@ -473,39 +477,42 @@ pub const fn raw_stderr() -> RawFd { /// Utility function to safely `dup2` over stdin (fd 0). #[cfg(not(any(windows, target_os = "wasi")))] -#[allow(clippy::mem_forget)] #[inline] pub fn dup2_stdin(fd: Fd) -> io::Result<()> { - // SAFETY: We pass the returned `OwnedFd` to `forget` so that it isn't - // dropped. - let mut target = unsafe { take_stdin() }; - backend::io::syscalls::dup2(fd.as_fd(), &mut target)?; - forget(target); + let fd = fd.as_fd(); + if fd.as_raw_fd() != c::STDIN_FILENO { + // SAFETY: We wrap the returned `OwnedFd` to `ManuallyDrop` so that it + // isn't dropped. + let mut target = ManuallyDrop::new(unsafe { take_stdin() }); + backend::io::syscalls::dup2(fd, &mut target)?; + } Ok(()) } /// Utility function to safely `dup2` over stdout (fd 1). #[cfg(not(any(windows, target_os = "wasi")))] -#[allow(clippy::mem_forget)] #[inline] pub fn dup2_stdout(fd: Fd) -> io::Result<()> { - // SAFETY: We pass the returned `OwnedFd` to `forget` so that it isn't - // dropped. - let mut target = unsafe { take_stdout() }; - backend::io::syscalls::dup2(fd.as_fd(), &mut target)?; - forget(target); + let fd = fd.as_fd(); + if fd.as_raw_fd() != c::STDOUT_FILENO { + // SAFETY: We wrap the returned `OwnedFd` to `ManuallyDrop` so that it + // isn't dropped. + let mut target = ManuallyDrop::new(unsafe { take_stdout() }); + backend::io::syscalls::dup2(fd, &mut target)?; + } Ok(()) } /// Utility function to safely `dup2` over stderr (fd 2). #[cfg(not(any(windows, target_os = "wasi")))] -#[allow(clippy::mem_forget)] #[inline] pub fn dup2_stderr(fd: Fd) -> io::Result<()> { - // SAFETY: We pass the returned `OwnedFd` to `forget` so that it isn't - // dropped. - let mut target = unsafe { take_stderr() }; - backend::io::syscalls::dup2(fd.as_fd(), &mut target)?; - forget(target); + let fd = fd.as_fd(); + if fd.as_raw_fd() != c::STDERR_FILENO { + // SAFETY: We wrap the returned `OwnedFd` to `ManuallyDrop` so that it + // isn't dropped. + let mut target = ManuallyDrop::new(unsafe { take_stderr() }); + backend::io::syscalls::dup2(fd, &mut target)?; + } Ok(()) } diff --git a/src/system.rs b/src/system.rs index c1b9eaffe..891ff6090 100644 --- a/src/system.rs +++ b/src/system.rs @@ -109,11 +109,11 @@ impl Uname { } impl fmt::Debug for Uname { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { #[cfg(not(linux_kernel))] { write!( - fmt, + f, "{:?} {:?} {:?} {:?} {:?}", self.sysname(), self.nodename(), @@ -125,7 +125,7 @@ impl fmt::Debug for Uname { #[cfg(linux_kernel)] { write!( - fmt, + f, "{:?} {:?} {:?} {:?} {:?} {:?}", self.sysname(), self.nodename(), @@ -201,7 +201,7 @@ pub enum RebootCommand { SwSuspend = c::LINUX_REBOOT_CMD_SW_SUSPEND, } -/// `reboot`—Reboot the system or enable/disable Ctrl-Alt-Del +/// `reboot`—Reboot the system or enable/disable Ctrl-Alt-Del. /// /// The reboot syscall, despite the name, can actually do much more than /// reboot. @@ -224,7 +224,7 @@ pub fn reboot(cmd: RebootCommand) -> io::Result<()> { backend::system::syscalls::reboot(cmd) } -/// `init_module`—Load a kernel module +/// `init_module`—Load a kernel module. /// /// # References /// - [Linux] @@ -236,7 +236,7 @@ pub fn init_module(image: &[u8], param_values: &CStr) -> io::Result<()> { backend::system::syscalls::init_module(image, param_values) } -/// `finit_module`—Load a kernel module from a file descriptor +/// `finit_module`—Load a kernel module from a file descriptor. /// /// # References /// - [Linux] @@ -248,7 +248,7 @@ pub fn finit_module(fd: Fd, param_values: &CStr, flags: c_int) -> io:: backend::system::syscalls::finit_module(fd.as_fd(), param_values, flags) } -/// `delete_module`—Unload a kernel module +/// `delete_module`—Unload a kernel module. /// /// # References /// - [Linux] diff --git a/src/termios/ioctl.rs b/src/termios/ioctl.rs index 620ae4c71..8fcf1b110 100644 --- a/src/termios/ioctl.rs +++ b/src/termios/ioctl.rs @@ -22,7 +22,7 @@ use backend::c; #[inline] #[doc(alias = "TIOCEXCL")] pub fn ioctl_tiocexcl(fd: Fd) -> io::Result<()> { - // SAFETY: TIOCEXCL is a no-argument setter opcode. + // SAFETY: `TIOCEXCL` is a no-argument setter opcode. unsafe { let ctl = ioctl::NoArg::>::new(); ioctl::ioctl(fd, ctl) @@ -45,7 +45,7 @@ pub fn ioctl_tiocexcl(fd: Fd) -> io::Result<()> { #[inline] #[doc(alias = "TIOCNXCL")] pub fn ioctl_tiocnxcl(fd: Fd) -> io::Result<()> { - // SAFETY: TIOCNXCL is a no-argument setter opcode. + // SAFETY: `TIOCNXCL` is a no-argument setter opcode. unsafe { let ctl = ioctl::NoArg::>::new(); ioctl::ioctl(fd, ctl) diff --git a/src/termios/tc.rs b/src/termios/tc.rs index 4522cb82a..0f828448d 100644 --- a/src/termios/tc.rs +++ b/src/termios/tc.rs @@ -82,7 +82,7 @@ pub fn tcsetpgrp(fd: Fd, pid: Pid) -> io::Result<()> { /// `tcsetattr(fd)`—Set terminal attributes. /// -/// Also known as the `TCSETS` (or `TCSETS2 on Linux) operation with `ioctl`. +/// Also known as the `TCSETS` (or `TCSETS2` on Linux) operation with `ioctl`. /// /// # References /// - [POSIX `tcsetattr`] diff --git a/src/termios/tty.rs b/src/termios/tty.rs index b14e602cd..1a40cf4a9 100644 --- a/src/termios/tty.rs +++ b/src/termios/tty.rs @@ -34,7 +34,7 @@ pub fn isatty(fd: Fd) -> bool { /// [Linux]: https://man7.org/linux/man-pages/man3/ttyname.3.html #[cfg(not(any(target_os = "fuchsia", target_os = "wasi")))] #[cfg(all(feature = "alloc", feature = "procfs"))] -#[cfg_attr(doc_cfg, doc(cfg(feature = "procfs")))] +#[cfg_attr(docsrs, doc(cfg(feature = "procfs")))] #[doc(alias = "ttyname_r")] #[inline] pub fn ttyname>>(dirfd: Fd, reuse: B) -> io::Result { diff --git a/src/thread/clock.rs b/src/thread/clock.rs index eebefbdab..2db7d3ab1 100644 --- a/src/thread/clock.rs +++ b/src/thread/clock.rs @@ -101,15 +101,15 @@ pub enum NanosleepRelativeResult { } impl fmt::Debug for NanosleepRelativeResult { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - NanosleepRelativeResult::Ok => fmt.write_str("Ok"), - NanosleepRelativeResult::Interrupted(remaining) => write!( - fmt, + Self::Ok => f.write_str("Ok"), + Self::Interrupted(remaining) => write!( + f, "Interrupted(Timespec {{ tv_sec: {:?}, tv_nsec: {:?} }})", remaining.tv_sec, remaining.tv_nsec ), - NanosleepRelativeResult::Err(err) => write!(fmt, "Err({:?})", err), + Self::Err(err) => write!(f, "Err({:?})", err), } } } diff --git a/src/thread/futex.rs b/src/thread/futex.rs index 47947c8b5..97aa084fd 100644 --- a/src/thread/futex.rs +++ b/src/thread/futex.rs @@ -1,38 +1,486 @@ //! Linux `futex`. //! -//! # Safety +//! Futex is a very low-level mechanism for implementing concurrency primitives +//! such as mutexes, rwlocks, and condvars. //! -//! Futex is a very low-level mechanism for implementing concurrency -//! primitives. +//! # Examples +//! +//! ``` +//! use rustix::thread::futex; +//! use std::sync::atomic::AtomicU32; +//! +//! # fn test(futex: &AtomicU32) -> rustix::io::Result<()> { +//! // Wake up one waiter. +//! futex::wake(futex, futex::Flags::PRIVATE, 1)?; +//! # Ok(()) +//! # } +//! ``` + +//! # References +//! - [Linux `futex` system call] +//! - [Linux `futex` feature] +//! +//! [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html +//! [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html #![allow(unsafe_code)] -use crate::thread::Timespec; +use core::num::NonZeroU32; +use core::ptr; +use core::sync::atomic::AtomicU32; + +use crate::backend::thread::futex::Operation; +use crate::backend::thread::syscalls::{futex_timeout, futex_val2}; +use crate::fd::{FromRawFd, OwnedFd, RawFd}; +use crate::utils::option_as_ptr; use crate::{backend, io}; -pub use backend::thread::futex::{FutexFlags, FutexOperation}; +pub use crate::timespec::Timespec; + +pub use backend::thread::futex::{Flags, OWNER_DIED, WAITERS}; + +/// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_WAIT, val, timeout, NULL, 0)` +/// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links. +/// +/// # References +/// - [Linux `futex` system call] +/// - [Linux `futex` feature] +/// +/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html +/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html +#[inline] +pub fn wait( + uaddr: &AtomicU32, + flags: Flags, + val: u32, + timeout: Option, +) -> io::Result<()> { + // SAFETY: The raw pointers come from references or null. + unsafe { + futex_timeout( + uaddr, + Operation::Wait, + flags, + val, + option_as_ptr(timeout.as_ref()), + ptr::null(), + 0, + ) + .map(|val| { + debug_assert_eq!( + val, 0, + "The return value should always equal zero, if the call is successful" + ); + }) + } +} + +/// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_WAKE, val, NULL, NULL, 0)` +/// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links. +/// +/// # References +/// - [Linux `futex` system call] +/// - [Linux `futex` feature] +/// +/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html +/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html +#[inline] +pub fn wake(uaddr: &AtomicU32, flags: Flags, val: u32) -> io::Result { + // SAFETY: The raw pointers come from references or null. + unsafe { futex_val2(uaddr, Operation::Wake, flags, val, 0, ptr::null(), 0) } +} + +/// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_FD, val, NULL, NULL, 0)` +/// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links. +/// +/// # References +/// - [Linux `futex` system call] +/// - [Linux `futex` feature] +/// +/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html +/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html +#[inline] +pub fn fd(uaddr: &AtomicU32, flags: Flags, val: u32) -> io::Result { + // SAFETY: The raw pointers come from references or null. + unsafe { + futex_val2(uaddr, Operation::Fd, flags, val, 0, ptr::null(), 0).map(|val| { + let fd = val as RawFd; + debug_assert_eq!(fd as usize, val, "return value should be a valid fd"); + OwnedFd::from_raw_fd(fd) + }) + } +} + +/// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_REQUEUE, val, val2, uaddr2, 0)` +/// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links. +/// +/// # References +/// - [Linux `futex` system call] +/// - [Linux `futex` feature] +/// +/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html +/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html +#[inline] +pub fn requeue( + uaddr: &AtomicU32, + flags: Flags, + val: u32, + val2: u32, + uaddr2: &AtomicU32, +) -> io::Result { + // SAFETY: The raw pointers come from references or null. + unsafe { futex_val2(uaddr, Operation::Requeue, flags, val, val2, uaddr2, 0) } +} + +/// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_CMP_REQUEUE, val, val2, uaddr2, val3)` +/// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links. +/// +/// # References +/// - [Linux `futex` system call] +/// - [Linux `futex` feature] +/// +/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html +/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html +#[inline] +pub fn cmp_requeue( + uaddr: &AtomicU32, + flags: Flags, + val: u32, + val2: u32, + uaddr2: &AtomicU32, + val3: u32, +) -> io::Result { + // SAFETY: The raw pointers come from references or null. + unsafe { futex_val2(uaddr, Operation::CmpRequeue, flags, val, val2, uaddr2, val3) } +} + +/// `FUTEX_OP_*` operations for use with [`wake_op`]. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[repr(u32)] +#[allow(clippy::identity_op)] +pub enum WakeOp { + /// `FUTEX_OP_SET`: `uaddr2 = oparg;` + Set = 0, + /// `FUTEX_OP_ADD`: `uaddr2 += oparg;` + Add = 1, + /// `FUTEX_OP_OR`: `uaddr2 |= oparg;` + Or = 2, + /// `FUTEX_OP_ANDN`: `uaddr2 &= ~oparg;` + AndN = 3, + /// `FUTEX_OP_XOR`: `uaddr2 ^= oparg;` + XOr = 4, + /// `FUTEX_OP_SET | FUTEX_OP_ARG_SHIFT`: `uaddr2 = (oparg << 1);` + SetShift = 0 | 8, + /// `FUTEX_OP_ADD | FUTEX_OP_ARG_SHIFT`: `uaddr2 += (oparg << 1);` + AddShift = 1 | 8, + /// `FUTEX_OP_OR | FUTEX_OP_ARG_SHIFT`: `uaddr2 |= (oparg << 1);` + OrShift = 2 | 8, + /// `FUTEX_OP_ANDN | FUTEX_OP_ARG_SHIFT`: `uaddr2 &= !(oparg << 1);` + AndNShift = 3 | 8, + /// `FUTEX_OP_XOR | FUTEX_OP_ARG_SHIFT`: `uaddr2 ^= (oparg << 1);` + XOrShift = 4 | 8, +} + +/// `FUTEX_OP_CMP_*` operations for use with [`wake_op`]. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[repr(u32)] +pub enum WakeOpCmp { + /// `FUTEX_OP_CMP_EQ`: `if oldval == cmparg { wake(); }` + Eq = 0, + /// `FUTEX_OP_CMP_EQ`: `if oldval != cmparg { wake(); }` + Ne = 1, + /// `FUTEX_OP_CMP_EQ`: `if oldval < cmparg { wake(); }` + Lt = 2, + /// `FUTEX_OP_CMP_EQ`: `if oldval <= cmparg { wake(); }` + Le = 3, + /// `FUTEX_OP_CMP_EQ`: `if oldval > cmparg { wake(); }` + Gt = 4, + /// `FUTEX_OP_CMP_EQ`: `if oldval >= cmparg { wake(); }` + Ge = 5, +} + +/// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_WAKE_OP, val, val2, uaddr2, val3)` +/// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links. +/// +/// # References +/// - [Linux `futex` system call] +/// - [Linux `futex` feature] +/// +/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html +/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html +#[inline] +#[allow(clippy::too_many_arguments)] +pub fn wake_op( + uaddr: &AtomicU32, + flags: Flags, + val: u32, + val2: u32, + uaddr2: &AtomicU32, + op: WakeOp, + cmp: WakeOpCmp, + oparg: u16, + cmparg: u16, +) -> io::Result { + if oparg >= 1 << 12 || cmparg >= 1 << 12 { + return Err(io::Errno::INVAL); + } + + let val3 = + ((op as u32) << 28) | ((cmp as u32) << 24) | ((oparg as u32) << 12) | (cmparg as u32); + + // SAFETY: The raw pointers come from references or null. + unsafe { futex_val2(uaddr, Operation::WakeOp, flags, val, val2, uaddr2, val3) } +} -/// `futex(uaddr, op, val, utime, uaddr2, val3)` +/// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_LOCK_PI, 0, timeout, NULL, 0)` +/// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links. /// /// # References /// - [Linux `futex` system call] /// - [Linux `futex` feature] /// -/// # Safety +/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html +/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html +#[inline] +pub fn lock_pi(uaddr: &AtomicU32, flags: Flags, timeout: Option) -> io::Result<()> { + // SAFETY: The raw pointers come from references or null. + unsafe { + futex_timeout( + uaddr, + Operation::LockPi, + flags, + 0, + option_as_ptr(timeout.as_ref()), + ptr::null(), + 0, + ) + .map(|val| { + debug_assert_eq!( + val, 0, + "The return value should always equal zero, if the call is successful" + ); + }) + } +} + +/// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_UNLOCK_PI, 0, NULL, NULL, 0)` /// /// This is a very low-level feature for implementing synchronization -/// primitives. See the references links above. +/// primitives. See the references links. +/// +/// # References +/// - [Linux `futex` system call] +/// - [Linux `futex` feature] /// /// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html /// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html #[inline] -pub unsafe fn futex( - uaddr: *mut u32, - op: FutexOperation, - flags: FutexFlags, +pub fn unlock_pi(uaddr: &AtomicU32, flags: Flags) -> io::Result<()> { + // SAFETY: The raw pointers come from references or null. + unsafe { + futex_val2(uaddr, Operation::UnlockPi, flags, 0, 0, ptr::null(), 0).map(|val| { + debug_assert_eq!( + val, 0, + "The return value should always equal zero, if the call is successful" + ); + }) + } +} + +/// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_TRYLOCK_PI, 0, NULL, NULL, 0)` +/// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links. +/// +/// # References +/// - [Linux `futex` system call] +/// - [Linux `futex` feature] +/// +/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html +/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html +#[inline] +pub fn trylock_pi(uaddr: &AtomicU32, flags: Flags) -> io::Result { + // SAFETY: The raw pointers come from references or null. + unsafe { + futex_val2(uaddr, Operation::TrylockPi, flags, 0, 0, ptr::null(), 0).map(|ret| ret == 0) + } +} + +/// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_WAIT_BITSET, val, timeout, NULL, val3)` +/// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links. +/// +/// # References +/// - [Linux `futex` system call] +/// - [Linux `futex` feature] +/// +/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html +/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html +#[inline] +pub fn wait_bitset( + uaddr: &AtomicU32, + flags: Flags, val: u32, - utime: *const Timespec, - uaddr2: *mut u32, + timeout: Option, + val3: NonZeroU32, +) -> io::Result<()> { + // SAFETY: The raw pointers come from references or null. + unsafe { + futex_timeout( + uaddr, + Operation::WaitBitset, + flags, + val, + option_as_ptr(timeout.as_ref()), + ptr::null(), + val3.get(), + ) + .map(|val| { + debug_assert_eq!( + val, 0, + "The return value should always equal zero, if the call is successful" + ); + }) + } +} + +/// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_WAKE_BITSET, val, NULL, NULL, val3)` +/// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links. +/// +/// # References +/// - [Linux `futex` system call] +/// - [Linux `futex` feature] +/// +/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html +/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html +#[inline] +pub fn wake_bitset( + uaddr: &AtomicU32, + flags: Flags, + val: u32, + val3: NonZeroU32, +) -> io::Result { + // SAFETY: The raw pointers come from references or null. + unsafe { + futex_val2( + uaddr, + Operation::WakeBitset, + flags, + val, + 0, + ptr::null(), + val3.get(), + ) + } +} + +/// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_WAIT_REQUEUE_PI, val, timeout, uaddr2, 0)` +/// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links. +/// +/// # References +/// - [Linux `futex` system call] +/// - [Linux `futex` feature] +/// +/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html +/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html +#[inline] +pub fn wait_requeue_pi( + uaddr: &AtomicU32, + flags: Flags, + val: u32, + timeout: Option, + uaddr2: &AtomicU32, +) -> io::Result<()> { + // SAFETY: The raw pointers come from references or null. + unsafe { + futex_timeout( + uaddr, + Operation::WaitRequeuePi, + flags, + val, + option_as_ptr(timeout.as_ref()), + uaddr2, + 0, + ) + .map(|val| { + debug_assert_eq!( + val, 0, + "The return value should always equal zero, if the call is successful" + ); + }) + } +} + +/// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_CMP_REQUEUE_PI, 1, val2, uaddr2, val3)` +/// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links. +/// +/// # References +/// - [Linux `futex` system call] +/// - [Linux `futex` feature] +/// +/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html +/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html +#[inline] +pub fn cmp_requeue_pi( + uaddr: &AtomicU32, + flags: Flags, + val2: u32, + uaddr2: &AtomicU32, val3: u32, ) -> io::Result { - backend::thread::syscalls::futex(uaddr, op, flags, val, utime, uaddr2, val3) + // SAFETY: The raw pointers come from references or null. + unsafe { futex_val2(uaddr, Operation::CmpRequeuePi, flags, 1, val2, uaddr2, val3) } +} + +/// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_LOCK_PI2, 0, timeout, NULL, 0)` +/// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links. +/// +/// # References +/// - [Linux `futex` system call] +/// - [Linux `futex` feature] +/// +/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html +/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html +#[inline] +pub fn lock_pi2(uaddr: &AtomicU32, flags: Flags, timeout: Option) -> io::Result<()> { + // SAFETY: The raw pointers come from references or null. + unsafe { + futex_timeout( + uaddr, + Operation::LockPi2, + flags, + 0, + option_as_ptr(timeout.as_ref()), + ptr::null(), + 0, + ) + .map(|val| { + debug_assert_eq!( + val, 0, + "The return value should always equal zero, if the call is successful" + ); + }) + } } diff --git a/src/thread/mod.rs b/src/thread/mod.rs index f3b8b3ebd..cb22dd271 100644 --- a/src/thread/mod.rs +++ b/src/thread/mod.rs @@ -3,7 +3,7 @@ #[cfg(not(target_os = "redox"))] mod clock; #[cfg(linux_kernel)] -mod futex; +pub mod futex; #[cfg(linux_kernel)] mod id; #[cfg(linux_kernel)] @@ -13,11 +13,16 @@ mod prctl; #[cfg(linux_kernel)] mod setns; +#[allow(deprecated)] +#[cfg(linux_kernel)] +pub use crate::backend::thread::futex::FutexOperation; +#[cfg(linux_kernel)] +pub use crate::thread::futex::{ + Flags as FutexFlags, OWNER_DIED as FUTEX_OWNER_DIED, WAITERS as FUTEX_WAITERS, +}; #[cfg(not(target_os = "redox"))] pub use clock::*; #[cfg(linux_kernel)] -pub use futex::{futex, FutexFlags, FutexOperation}; -#[cfg(linux_kernel)] pub use id::{ gettid, set_thread_gid, set_thread_groups, set_thread_res_gid, set_thread_res_uid, set_thread_uid, Gid, Pid, RawGid, RawPid, RawUid, Uid, @@ -28,3 +33,60 @@ pub use libcap::{capabilities, set_capabilities, CapabilityFlags, CapabilitySets pub use prctl::*; #[cfg(linux_kernel)] pub use setns::*; + +/// DEPRECATED: There are now individual functions available to perform futex +/// operations with improved type safety. See the [futex module]. +/// +/// `futex(uaddr, op, val, utime, uaddr2, val3)` +/// +/// # References +/// - [Linux `futex` system call] +/// - [Linux `futex` feature] +/// +/// # Safety +/// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links above. +/// +/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html +/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html +/// [futex module]: mod@crate::thread::futex +#[cfg(linux_kernel)] +#[allow(unsafe_code, deprecated)] +#[inline] +pub unsafe fn futex( + uaddr: *mut u32, + op: FutexOperation, + flags: FutexFlags, + val: u32, + utime: *const Timespec, + uaddr2: *mut u32, + val3: u32, +) -> crate::io::Result { + use crate::backend::thread::futex::Operation; + use crate::backend::thread::syscalls::{futex_timeout, futex_val2}; + use core::mem::transmute; + use core::sync::atomic::AtomicU32; + use FutexOperation::*; + + match op { + Wait | LockPi | WaitBitset => futex_timeout( + uaddr as *const AtomicU32, + transmute::(op), + flags, + val, + utime, + uaddr2 as *const AtomicU32, + val3, + ), + Wake | Fd | Requeue | CmpRequeue | WakeOp | UnlockPi | TrylockPi => futex_val2( + uaddr as *const AtomicU32, + transmute::(op), + flags, + val, + utime as usize as u32, + uaddr2 as *const AtomicU32, + val3, + ), + } +} diff --git a/src/thread/prctl.rs b/src/thread/prctl.rs index 9d9e6b2d5..08ba0a2d6 100644 --- a/src/thread/prctl.rs +++ b/src/thread/prctl.rs @@ -1,12 +1,11 @@ //! Linux `prctl` wrappers. //! -//! Rustix wraps variadic/dynamic-dispatch functions like `prctl` in -//! type-safe wrappers. +//! Rustix wraps variadic/dynamic-dispatch functions like `prctl` in type-safe +//! wrappers. //! //! # Safety //! -//! The inner `prctl` calls are dynamically typed and must be called -//! correctly. +//! The inner `prctl` calls are dynamically typed and must be called correctly. #![allow(unsafe_code)] use core::mem::MaybeUninit; @@ -38,9 +37,9 @@ const PR_GET_KEEPCAPS: c_int = 7; /// Get the current state of the calling thread's `keep capabilities` flag. /// /// # References -/// - [`prctl(PR_GET_KEEPCAPS,...)`] +/// - [`prctl(PR_GET_KEEPCAPS,…)`] /// -/// [`prctl(PR_GET_KEEPCAPS,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_GET_KEEPCAPS,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn get_keep_capabilities() -> io::Result { unsafe { prctl_1arg(PR_GET_KEEPCAPS) }.map(|r| r != 0) @@ -51,9 +50,9 @@ const PR_SET_KEEPCAPS: c_int = 8; /// Set the state of the calling thread's `keep capabilities` flag. /// /// # References -/// - [`prctl(PR_SET_KEEPCAPS,...)`] +/// - [`prctl(PR_SET_KEEPCAPS,…)`] /// -/// [`prctl(PR_SET_KEEPCAPS,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_SET_KEEPCAPS,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn set_keep_capabilities(enable: bool) -> io::Result<()> { unsafe { prctl_2args(PR_SET_KEEPCAPS, usize::from(enable) as *mut _) }.map(|_r| ()) @@ -69,9 +68,9 @@ const PR_GET_NAME: c_int = 16; /// Get the name of the calling thread. /// /// # References -/// - [`prctl(PR_GET_NAME,...)`] +/// - [`prctl(PR_GET_NAME,…)`] /// -/// [`prctl(PR_GET_NAME,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_GET_NAME,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[cfg(feature = "alloc")] pub fn name() -> io::Result { @@ -90,9 +89,9 @@ const PR_SET_NAME: c_int = 15; /// 16 bytes, as the Linux syscall does. /// /// # References -/// - [`prctl(PR_SET_NAME,...)`] +/// - [`prctl(PR_SET_NAME,…)`] /// -/// [`prctl(PR_SET_NAME,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_SET_NAME,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn set_name(name: &CStr) -> io::Result<()> { unsafe { prctl_2args(PR_SET_NAME, name.as_ptr() as *mut _) }.map(|_r| ()) @@ -149,9 +148,9 @@ impl TryFrom for SecureComputingMode { /// the process is killed; see [the `proc` manual page]. /// /// # References -/// - [`prctl(PR_GET_SECCOMP,...)`] +/// - [`prctl(PR_GET_SECCOMP,…)`] /// -/// [`prctl(PR_GET_SECCOMP,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_GET_SECCOMP,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html /// [the `proc` manual page]: https://man7.org/linux/man-pages/man5/proc.5.html #[inline] pub fn secure_computing_mode() -> io::Result { @@ -165,9 +164,9 @@ const PR_SET_SECCOMP: c_int = 22; /// available system calls. /// /// # References -/// - [`prctl(PR_SET_SECCOMP,...)`] +/// - [`prctl(PR_SET_SECCOMP,…)`] /// -/// [`prctl(PR_SET_SECCOMP,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_SET_SECCOMP,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn set_secure_computing_mode(mode: SecureComputingMode) -> io::Result<()> { unsafe { prctl_2args(PR_SET_SECCOMP, mode as usize as *mut _) }.map(|_r| ()) @@ -367,7 +366,7 @@ pub enum Capability { /// - `bpf_probe_read` to read arbitrary kernel memory is allowed /// - `bpf_trace_printk` to print kernel memory is allowed /// - /// [`Capability::SystemAdmin`] is required to use bpf_probe_write_user. + /// [`Capability::SystemAdmin`] is required to use `bpf_probe_write_user`. /// /// [`Capability::SystemAdmin`] is required to iterate system-wide loaded /// programs, maps, links, and BTFs, and convert their IDs to file @@ -388,9 +387,9 @@ pub enum Capability { /// bounding set. /// /// # References -/// - [`prctl(PR_CAPBSET_READ,...)`] +/// - [`prctl(PR_CAPBSET_READ,…)`] /// -/// [`prctl(PR_CAPBSET_READ,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_CAPBSET_READ,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn capability_is_in_bounding_set(capability: Capability) -> io::Result { unsafe { prctl_2args(PR_CAPBSET_READ, capability as usize as *mut _) }.map(|r| r != 0) @@ -403,9 +402,9 @@ const PR_CAPBSET_DROP: c_int = 24; /// from the thread's capability bounding set. /// /// # References -/// - [`prctl(PR_CAPBSET_DROP,...)`] +/// - [`prctl(PR_CAPBSET_DROP,…)`] /// -/// [`prctl(PR_CAPBSET_DROP,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_CAPBSET_DROP,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn remove_capability_from_bounding_set(capability: Capability) -> io::Result<()> { unsafe { prctl_2args(PR_CAPBSET_DROP, capability as usize as *mut _) }.map(|_r| ()) @@ -427,6 +426,8 @@ bitflags! { /// with an effective or real UID of 0 calls `execve`. const NO_ROOT = 1_u32 << 0; /// Set [`NO_ROOT`] irreversibly. + /// + /// [`NO_ROOT`]: Self::NO_ROOT const NO_ROOT_LOCKED = 1_u32 << 1; /// Setting this flag stops the kernel from adjusting the process' /// permitted, effective, and ambient capability sets when the thread's @@ -434,17 +435,23 @@ bitflags! { /// values. const NO_SETUID_FIXUP = 1_u32 << 2; /// Set [`NO_SETUID_FIXUP`] irreversibly. + /// + /// [`NO_SETUID_FIXUP`]: Self::NO_SETUID_FIXUP const NO_SETUID_FIXUP_LOCKED = 1_u32 << 3; /// Setting this flag allows a thread that has one or more 0 UIDs to /// retain capabilities in its permitted set when it switches all of /// its UIDs to nonzero values. const KEEP_CAPS = 1_u32 << 4; /// Set [`KEEP_CAPS`] irreversibly. + /// + /// [`KEEP_CAPS`]: Self::KEEP_CAPS const KEEP_CAPS_LOCKED = 1_u32 << 5; /// Setting this flag disallows raising ambient capabilities via the /// `prctl`'s `PR_CAP_AMBIENT_RAISE` operation. const NO_CAP_AMBIENT_RAISE = 1_u32 << 6; /// Set [`NO_CAP_AMBIENT_RAISE`] irreversibly. + /// + /// [`NO_CAP_AMBIENT_RAISE`]: Self::NO_CAP_AMBIENT_RAISE const NO_CAP_AMBIENT_RAISE_LOCKED = 1_u32 << 7; /// @@ -455,9 +462,9 @@ bitflags! { /// Get the `securebits` flags of the calling thread. /// /// # References -/// - [`prctl(PR_GET_SECUREBITS,...)`] +/// - [`prctl(PR_GET_SECUREBITS,…)`] /// -/// [`prctl(PR_GET_SECUREBITS,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_GET_SECUREBITS,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn capabilities_secure_bits() -> io::Result { let r = unsafe { prctl_1arg(PR_GET_SECUREBITS)? } as c_uint; @@ -469,9 +476,9 @@ const PR_SET_SECUREBITS: c_int = 28; /// Set the `securebits` flags of the calling thread. /// /// # References -/// - [`prctl(PR_SET_SECUREBITS,...)`] +/// - [`prctl(PR_SET_SECUREBITS,…)`] /// -/// [`prctl(PR_SET_SECUREBITS,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_SET_SECUREBITS,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn set_capabilities_secure_bits(bits: CapabilitiesSecureBits) -> io::Result<()> { unsafe { prctl_2args(PR_SET_SECUREBITS, bits.bits() as usize as *mut _) }.map(|_r| ()) @@ -486,9 +493,9 @@ const PR_GET_TIMERSLACK: c_int = 30; /// Get the `current` timer slack value of the calling thread. /// /// # References -/// - [`prctl(PR_GET_TIMERSLACK,...)`] +/// - [`prctl(PR_GET_TIMERSLACK,…)`] /// -/// [`prctl(PR_GET_TIMERSLACK,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_GET_TIMERSLACK,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn current_timer_slack() -> io::Result { unsafe { prctl_1arg(PR_GET_TIMERSLACK) }.map(|r| r as u64) @@ -499,9 +506,9 @@ const PR_SET_TIMERSLACK: c_int = 29; /// Sets the `current` timer slack value for the calling thread. /// /// # References -/// - [`prctl(PR_SET_TIMERSLACK,...)`] +/// - [`prctl(PR_SET_TIMERSLACK,…)`] /// -/// [`prctl(PR_SET_TIMERSLACK,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_SET_TIMERSLACK,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn set_current_timer_slack(value: Option) -> io::Result<()> { let value = usize::try_from(value.map_or(0, NonZeroU64::get)).map_err(|_r| io::Errno::RANGE)?; @@ -517,9 +524,9 @@ const PR_GET_NO_NEW_PRIVS: c_int = 39; /// Get the value of the `no_new_privs` attribute for the calling thread. /// /// # References -/// - [`prctl(PR_GET_NO_NEW_PRIVS,...)`] +/// - [`prctl(PR_GET_NO_NEW_PRIVS,…)`] /// -/// [`prctl(PR_GET_NO_NEW_PRIVS,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_GET_NO_NEW_PRIVS,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn no_new_privs() -> io::Result { unsafe { prctl_1arg(PR_GET_NO_NEW_PRIVS) }.map(|r| r != 0) @@ -530,9 +537,9 @@ const PR_SET_NO_NEW_PRIVS: c_int = 38; /// Set the calling thread's `no_new_privs` attribute. /// /// # References -/// - [`prctl(PR_SET_NO_NEW_PRIVS,...)`] +/// - [`prctl(PR_SET_NO_NEW_PRIVS,…)`] /// -/// [`prctl(PR_SET_NO_NEW_PRIVS,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_SET_NO_NEW_PRIVS,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn set_no_new_privs(no_new_privs: bool) -> io::Result<()> { unsafe { prctl_2args(PR_SET_NO_NEW_PRIVS, usize::from(no_new_privs) as *mut _) }.map(|_r| ()) @@ -548,9 +555,9 @@ const PR_GET_TID_ADDRESS: c_int = 40; /// and `clone`'s `CLONE_CHILD_CLEARTID` flag. /// /// # References -/// - [`prctl(PR_GET_TID_ADDRESS,...)`] +/// - [`prctl(PR_GET_TID_ADDRESS,…)`] /// -/// [`prctl(PR_GET_TID_ADDRESS,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_GET_TID_ADDRESS,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn get_clear_child_tid_address() -> io::Result>> { unsafe { prctl_get_at_arg2_optional::<*mut c_void>(PR_GET_TID_ADDRESS) }.map(NonNull::new) @@ -565,9 +572,9 @@ const PR_GET_THP_DISABLE: c_int = 42; /// Get the current setting of the `THP disable` flag for the calling thread. /// /// # References -/// - [`prctl(PR_GET_THP_DISABLE,...)`] +/// - [`prctl(PR_GET_THP_DISABLE,…)`] /// -/// [`prctl(PR_GET_THP_DISABLE,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_GET_THP_DISABLE,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn transparent_huge_pages_are_disabled() -> io::Result { unsafe { prctl_1arg(PR_GET_THP_DISABLE) }.map(|r| r != 0) @@ -578,9 +585,9 @@ const PR_SET_THP_DISABLE: c_int = 41; /// Set the state of the `THP disable` flag for the calling thread. /// /// # References -/// - [`prctl(PR_SET_THP_DISABLE,...)`] +/// - [`prctl(PR_SET_THP_DISABLE,…)`] /// -/// [`prctl(PR_SET_THP_DISABLE,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_SET_THP_DISABLE,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn disable_transparent_huge_pages(thp_disable: bool) -> io::Result<()> { unsafe { prctl_2args(PR_SET_THP_DISABLE, usize::from(thp_disable) as *mut _) }.map(|_r| ()) @@ -597,9 +604,9 @@ const PR_CAP_AMBIENT_IS_SET: usize = 1; /// Check if the specified capability is in the ambient set. /// /// # References -/// - [`prctl(PR_CAP_AMBIENT,PR_CAP_AMBIENT_IS_SET,...)`] +/// - [`prctl(PR_CAP_AMBIENT,PR_CAP_AMBIENT_IS_SET,…)`] /// -/// [`prctl(PR_CAP_AMBIENT,PR_CAP_AMBIENT_IS_SET,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_CAP_AMBIENT,PR_CAP_AMBIENT_IS_SET,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn capability_is_in_ambient_set(capability: Capability) -> io::Result { let cap = capability as usize as *mut _; @@ -611,9 +618,9 @@ const PR_CAP_AMBIENT_CLEAR_ALL: usize = 4; /// Remove all capabilities from the ambient set. /// /// # References -/// - [`prctl(PR_CAP_AMBIENT,PR_CAP_AMBIENT_CLEAR_ALL,...)`] +/// - [`prctl(PR_CAP_AMBIENT,PR_CAP_AMBIENT_CLEAR_ALL,…)`] /// -/// [`prctl(PR_CAP_AMBIENT,PR_CAP_AMBIENT_CLEAR_ALL,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_CAP_AMBIENT,PR_CAP_AMBIENT_CLEAR_ALL,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn clear_ambient_capability_set() -> io::Result<()> { unsafe { prctl_2args(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL as *mut _) }.map(|_r| ()) @@ -625,9 +632,9 @@ const PR_CAP_AMBIENT_LOWER: usize = 3; /// Add or remove the specified capability to the ambient set. /// /// # References -/// - [`prctl(PR_CAP_AMBIENT,...)`] +/// - [`prctl(PR_CAP_AMBIENT,…)`] /// -/// [`prctl(PR_CAP_AMBIENT,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_CAP_AMBIENT,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn configure_capability_in_ambient_set(capability: Capability, enable: bool) -> io::Result<()> { let sub_operation = if enable { @@ -661,9 +668,9 @@ pub struct SVEVectorLengthConfig { /// Get the thread's current SVE vector length configuration. /// /// # References -/// - [`prctl(PR_SVE_GET_VL,...)`] +/// - [`prctl(PR_SVE_GET_VL,…)`] /// -/// [`prctl(PR_SVE_GET_VL,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_SVE_GET_VL,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn sve_vector_length_configuration() -> io::Result { let bits = unsafe { prctl_1arg(PR_SVE_GET_VL)? } as c_uint; @@ -680,14 +687,14 @@ const PR_SVE_SET_VL_ONEXEC: u32 = 1_u32 << 18; /// Configure the thread's vector length of Scalable Vector Extension. /// /// # References -/// - [`prctl(PR_SVE_SET_VL,...)`] +/// - [`prctl(PR_SVE_SET_VL,…)`] /// /// # Safety /// /// Please ensure the conditions necessary to safely call this function, /// as detailed in the references above. /// -/// [`prctl(PR_SVE_SET_VL,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_SVE_SET_VL,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub unsafe fn set_sve_vector_length_configuration( vector_length_in_bytes: usize, @@ -720,14 +727,14 @@ const PR_PAC_RESET_KEYS: c_int = 54; /// values generated by the kernel. /// /// # References -/// - [`prctl(PR_PAC_RESET_KEYS,...)`] +/// - [`prctl(PR_PAC_RESET_KEYS,…)`] /// /// # Safety /// /// Please ensure the conditions necessary to safely call this function, /// as detailed in the references above. /// -/// [`prctl(PR_PAC_RESET_KEYS,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_PAC_RESET_KEYS,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub unsafe fn reset_pointer_authentication_keys( keys: Option, @@ -767,9 +774,9 @@ bitflags! { /// Get the current tagged address mode for the calling thread. /// /// # References -/// - [`prctl(PR_GET_TAGGED_ADDR_CTRL,...)`] +/// - [`prctl(PR_GET_TAGGED_ADDR_CTRL,…)`] /// -/// [`prctl(PR_GET_TAGGED_ADDR_CTRL,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_GET_TAGGED_ADDR_CTRL,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn current_tagged_address_mode() -> io::Result<(Option, u32)> { let r = unsafe { prctl_1arg(PR_GET_TAGGED_ADDR_CTRL)? } as c_uint; @@ -783,14 +790,14 @@ const PR_SET_TAGGED_ADDR_CTRL: c_int = 55; /// Controls support for passing tagged user-space addresses to the kernel. /// /// # References -/// - [`prctl(PR_SET_TAGGED_ADDR_CTRL,...)`] +/// - [`prctl(PR_SET_TAGGED_ADDR_CTRL,…)`] /// /// # Safety /// /// Please ensure the conditions necessary to safely call this function, as /// detailed in the references above. /// -/// [`prctl(PR_SET_TAGGED_ADDR_CTRL,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_SET_TAGGED_ADDR_CTRL,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub unsafe fn set_current_tagged_address_mode( mode: Option, @@ -812,14 +819,14 @@ const PR_SYS_DISPATCH_OFF: usize = 0; /// Disable Syscall User Dispatch mechanism. /// /// # References -/// - [`prctl(PR_SET_SYSCALL_USER_DISPATCH,PR_SYS_DISPATCH_OFF,...)`] +/// - [`prctl(PR_SET_SYSCALL_USER_DISPATCH,PR_SYS_DISPATCH_OFF,…)`] /// /// # Safety /// /// Please ensure the conditions necessary to safely call this function, as /// detailed in the references above. /// -/// [`prctl(PR_SET_SYSCALL_USER_DISPATCH,PR_SYS_DISPATCH_OFF,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_SET_SYSCALL_USER_DISPATCH,PR_SYS_DISPATCH_OFF,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub unsafe fn disable_syscall_user_dispatch() -> io::Result<()> { prctl_2args(PR_SET_SYSCALL_USER_DISPATCH, PR_SYS_DISPATCH_OFF as *mut _).map(|_r| ()) @@ -858,14 +865,14 @@ impl TryFrom for SysCallUserDispatchFastSwitch { /// Enable Syscall User Dispatch mechanism. /// /// # References -/// - [`prctl(PR_SET_SYSCALL_USER_DISPATCH,PR_SYS_DISPATCH_ON,...)`] +/// - [`prctl(PR_SET_SYSCALL_USER_DISPATCH,PR_SYS_DISPATCH_ON,…)`] /// /// # Safety /// /// Please ensure the conditions necessary to safely call this function, as /// detailed in the references above. /// -/// [`prctl(PR_SET_SYSCALL_USER_DISPATCH,PR_SYS_DISPATCH_ON,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_SET_SYSCALL_USER_DISPATCH,PR_SYS_DISPATCH_ON,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub unsafe fn enable_syscall_user_dispatch( always_allowed_region: &[u8], @@ -922,9 +929,9 @@ impl TryFrom for CoreSchedulingScope { /// Get core scheduling cookie of a process. /// /// # References -/// - [`prctl(PR_SCHED_CORE,PR_SCHED_CORE_GET,...)`] +/// - [`prctl(PR_SCHED_CORE,PR_SCHED_CORE_GET,…)`] /// -/// [`prctl(PR_SCHED_CORE,PR_SCHED_CORE_GET,...)`]: https://www.kernel.org/doc/html/v5.18/admin-guide/hw-vuln/core-scheduling.html +/// [`prctl(PR_SCHED_CORE,PR_SCHED_CORE_GET,…)`]: https://www.kernel.org/doc/html/v6.10/admin-guide/hw-vuln/core-scheduling.html #[inline] pub fn core_scheduling_cookie(pid: Pid, scope: CoreSchedulingScope) -> io::Result { let mut value: MaybeUninit = MaybeUninit::uninit(); @@ -945,9 +952,9 @@ const PR_SCHED_CORE_CREATE: usize = 1; /// Create unique core scheduling cookie. /// /// # References -/// - [`prctl(PR_SCHED_CORE,PR_SCHED_CORE_CREATE,...)`] +/// - [`prctl(PR_SCHED_CORE,PR_SCHED_CORE_CREATE,…)`] /// -/// [`prctl(PR_SCHED_CORE,PR_SCHED_CORE_CREATE,...)`]: https://www.kernel.org/doc/html/v5.18/admin-guide/hw-vuln/core-scheduling.html +/// [`prctl(PR_SCHED_CORE,PR_SCHED_CORE_CREATE,…)`]: https://www.kernel.org/doc/html/v6.10/admin-guide/hw-vuln/core-scheduling.html #[inline] pub fn create_core_scheduling_cookie(pid: Pid, scope: CoreSchedulingScope) -> io::Result<()> { unsafe { @@ -967,9 +974,9 @@ const PR_SCHED_CORE_SHARE_TO: usize = 2; /// Push core scheduling cookie to a process. /// /// # References -/// - [`prctl(PR_SCHED_CORE,PR_SCHED_CORE_SHARE_TO,...)`] +/// - [`prctl(PR_SCHED_CORE,PR_SCHED_CORE_SHARE_TO,…)`] /// -/// [`prctl(PR_SCHED_CORE,PR_SCHED_CORE_SHARE_TO,...)`]: https://www.kernel.org/doc/html/v5.18/admin-guide/hw-vuln/core-scheduling.html +/// [`prctl(PR_SCHED_CORE,PR_SCHED_CORE_SHARE_TO,…)`]: https://www.kernel.org/doc/html/v6.10/admin-guide/hw-vuln/core-scheduling.html #[inline] pub fn push_core_scheduling_cookie(pid: Pid, scope: CoreSchedulingScope) -> io::Result<()> { unsafe { @@ -989,9 +996,9 @@ const PR_SCHED_CORE_SHARE_FROM: usize = 3; /// Pull core scheduling cookie from a process. /// /// # References -/// - [`prctl(PR_SCHED_CORE,PR_SCHED_CORE_SHARE_FROM,...)`] +/// - [`prctl(PR_SCHED_CORE,PR_SCHED_CORE_SHARE_FROM,…)`] /// -/// [`prctl(PR_SCHED_CORE,PR_SCHED_CORE_SHARE_FROM,...)`]: https://www.kernel.org/doc/html/v5.18/admin-guide/hw-vuln/core-scheduling.html +/// [`prctl(PR_SCHED_CORE,PR_SCHED_CORE_SHARE_FROM,…)`]: https://www.kernel.org/doc/html/v6.10/admin-guide/hw-vuln/core-scheduling.html #[inline] pub fn pull_core_scheduling_cookie(pid: Pid, scope: CoreSchedulingScope) -> io::Result<()> { unsafe { diff --git a/src/thread/setns.rs b/src/thread/setns.rs index ef61d11e8..48279dcda 100644 --- a/src/thread/setns.rs +++ b/src/thread/setns.rs @@ -101,6 +101,7 @@ bitflags! { /// - [Linux] /// /// [Linux]: https://man7.org/linux/man-pages/man2/setns.2.html +#[doc(alias = "setns")] pub fn move_into_link_name_space( fd: BorrowedFd<'_>, allowed_type: Option, @@ -118,6 +119,7 @@ pub fn move_into_link_name_space( /// - [Linux] /// /// [Linux]: https://man7.org/linux/man-pages/man2/setns.2.html +#[doc(alias = "setns")] pub fn move_into_thread_name_spaces( fd: BorrowedFd<'_>, allowed_types: ThreadNameSpaceType, diff --git a/tests/backends.rs b/tests/backends.rs index 085d8e90a..230f175b7 100644 --- a/tests/backends.rs +++ b/tests/backends.rs @@ -111,7 +111,7 @@ fn has_dependency( .arg("tree") .arg("--quiet") .arg("--edges=normal") - .arg(&format!("--invert={}", dependency)) + .arg(format!("--invert={}", dependency)) .current_dir(dir); command.args(args); diff --git a/tests/event/poll.rs b/tests/event/poll.rs index 27ad34e05..f5c595800 100644 --- a/tests/event/poll.rs +++ b/tests/event/poll.rs @@ -51,7 +51,7 @@ fn test_poll() { #[test] fn test_poll_fd_set_fd() { - // Make up some file descriptors so that we can test that set_fd works. + // Make up some file descriptors so that we can test that `set_fd` works. let a = unsafe { OwnedFd::from_raw_fd(777) }; let mut poll_fd = PollFd::new(&a, PollFlags::empty()); assert_eq!(poll_fd.as_fd().as_raw_fd(), 777); diff --git a/tests/fs/file.rs b/tests/fs/file.rs index d80015d36..ff503d5fe 100644 --- a/tests/fs/file.rs +++ b/tests/fs/file.rs @@ -112,8 +112,8 @@ fn test_file() { // Test `fcntl_getfl`. let fl = rustix::fs::fcntl_getfl(&file).unwrap(); - // Clear O_LARGEFILE, which may be set by rustix on 32-bit Linux or automatically by some - // kernel on 64-bit (Linux and illumos). + // Clear `O_LARGEFILE`, which may be set by rustix on 32-bit Linux or + // automatically by some kernel on 64-bit (Linux and illumos). #[cfg(any(linux_kernel, target_os = "illumos"))] let fl = fl - rustix::fs::OFlags::LARGEFILE; @@ -185,3 +185,16 @@ fn test_setfl_append() { assert_eq!(rustix::io::read(&file, &mut buf), Ok(19)); assert_eq!(&buf, b"uvwdefghijklmnopxyz\0\0\0\0\0\0\0\0\0\0\0\0\0"); } + +#[test] +fn test_mode() { + use rustix::fs::{Mode, RawMode}; + + let mode = Mode::from_raw_mode((libc::S_IFSOCK | libc::S_IRUSR) as RawMode); + assert_eq!(mode, Mode::RUSR); + assert_eq!(mode.bits(), libc::S_IRUSR as RawMode); + + let mode = Mode::from_raw_mode((libc::S_IFSOCK | libc::S_IRWXU) as RawMode); + assert_eq!(mode, Mode::RWXU); + assert_eq!(mode.bits(), libc::S_IRWXU as RawMode); +} diff --git a/tests/fs/inotify.rs b/tests/fs/inotify.rs new file mode 100644 index 000000000..72fc7bde4 --- /dev/null +++ b/tests/fs/inotify.rs @@ -0,0 +1,107 @@ +use rustix::fs::inotify::{self, CreateFlags, WatchFlags}; +use rustix::io::Errno; +use std::fmt::Write; +use std::fs::{create_dir_all, remove_file, rename, File}; +use std::mem::MaybeUninit; + +#[test] +fn test_inotify_iter() { + let inotify = inotify::init(CreateFlags::NONBLOCK).unwrap(); + create_dir_all("/tmp/.rustix-inotify-test").unwrap(); + inotify::add_watch( + &inotify, + "/tmp/.rustix-inotify-test", + WatchFlags::ALL_EVENTS, + ) + .unwrap(); + + File::create("/tmp/.rustix-inotify-test/foo").unwrap(); + rename( + "/tmp/.rustix-inotify-test/foo", + "/tmp/.rustix-inotify-test/bar", + ) + .unwrap(); + remove_file("/tmp/.rustix-inotify-test/bar").unwrap(); + + let mut output = String::new(); + let mut cookie = 0; + + let mut buf = [MaybeUninit::uninit(); 512]; + let mut iter = inotify::Reader::new(inotify, &mut buf); + loop { + let e = match iter.next() { + Err(Errno::WOULDBLOCK) => break, + r => r.unwrap(), + }; + + writeln!(output, "{e:#?}").unwrap(); + if e.cookie() != 0 { + cookie = e.cookie(); + } + } + + let expected = format!( + r#"InotifyEvent {{ + wd: 1, + events: ReadFlags( + CREATE, + ), + cookie: 0, + file_name: Some( + "foo", + ), +}} +InotifyEvent {{ + wd: 1, + events: ReadFlags( + OPEN, + ), + cookie: 0, + file_name: Some( + "foo", + ), +}} +InotifyEvent {{ + wd: 1, + events: ReadFlags( + CLOSE_WRITE, + ), + cookie: 0, + file_name: Some( + "foo", + ), +}} +InotifyEvent {{ + wd: 1, + events: ReadFlags( + MOVED_FROM, + ), + cookie: {cookie}, + file_name: Some( + "foo", + ), +}} +InotifyEvent {{ + wd: 1, + events: ReadFlags( + MOVED_TO, + ), + cookie: {cookie}, + file_name: Some( + "bar", + ), +}} +InotifyEvent {{ + wd: 1, + events: ReadFlags( + DELETE, + ), + cookie: 0, + file_name: Some( + "bar", + ), +}} +"# + ); + assert_eq!(expected, output); +} diff --git a/tests/fs/main.rs b/tests/fs/main.rs index 4ab608fc7..db074fedb 100644 --- a/tests/fs/main.rs +++ b/tests/fs/main.rs @@ -20,6 +20,8 @@ mod file; #[cfg(not(target_os = "wasi"))] mod flock; mod futimens; +#[cfg(linux_kernel)] +mod inotify; mod invalid_offset; #[cfg(not(target_os = "redox"))] mod ioctl; diff --git a/tests/fs/readdir.rs b/tests/fs/readdir.rs index 8b310f992..92db4ea84 100644 --- a/tests/fs/readdir.rs +++ b/tests/fs/readdir.rs @@ -18,7 +18,7 @@ fn dir_entries() { let entries = read_entries(&mut dir); assert!( - entries.get("file1").is_some(), + entries.contains_key("file1"), "directory contains `file1`: {:?}", entries ); @@ -27,12 +27,12 @@ fn dir_entries() { let _f2 = File::create(tmpdir.path().join("file2")).expect("create file1"); let entries = read_entries(&mut dir); assert!( - entries.get("file1").is_some(), + entries.contains_key("file1"), "directory contains `file1`: {:?}", entries ); assert!( - entries.get("file2").is_some(), + entries.contains_key("file2"), "directory contains `file2`: {:?}", entries ); @@ -88,7 +88,7 @@ fn test_raw_dir(buf: &mut [MaybeUninit]) { dirfd.seek(SeekFrom::Start(0)).unwrap(); let entries = read_raw_entries(&mut dir); assert!( - entries.get("file1").is_some(), + entries.contains("file1"), "directory contains `file1`: {:?}", entries ); @@ -98,12 +98,12 @@ fn test_raw_dir(buf: &mut [MaybeUninit]) { dirfd.seek(SeekFrom::Start(0)).unwrap(); let entries = read_raw_entries(&mut dir); assert!( - entries.get("file1").is_some(), + entries.contains("file1"), "directory contains `file1`: {:?}", entries ); assert!( - entries.get("file2").is_some(), + entries.contains("file2"), "directory contains `file2`: {:?}", entries ); diff --git a/tests/fs/readlinkat.rs b/tests/fs/readlinkat.rs index 9345cd0e4..80a786c4d 100644 --- a/tests/fs/readlinkat.rs +++ b/tests/fs/readlinkat.rs @@ -75,7 +75,7 @@ fn test_readlinkat_raw() { assert!(!no.is_empty()); let (yes, no) = readlinkat_raw(&dir, "link", &mut short).unwrap(); - assert_eq!(yes, &[b'f', b'i']); + assert_eq!(yes, b"fi"); assert!(no.is_empty()); symlinkat("link", &dir, "another").unwrap(); @@ -85,7 +85,7 @@ fn test_readlinkat_raw() { assert!(!no.is_empty()); let (yes, no) = readlinkat_raw(&dir, "link", &mut short).unwrap(); - assert_eq!(yes, &[b'f', b'i']); + assert_eq!(yes, b"fi"); assert!(no.is_empty()); let (yes, no) = readlinkat_raw(&dir, "another", &mut some).unwrap(); @@ -93,6 +93,6 @@ fn test_readlinkat_raw() { assert!(!no.is_empty()); let (yes, no) = readlinkat_raw(&dir, "another", &mut short).unwrap(); - assert_eq!(yes, &[b'l', b'i']); + assert_eq!(yes, b"li"); assert!(no.is_empty()); } diff --git a/tests/fs/seek.rs b/tests/fs/seek.rs index fe4f4409c..c483817fd 100644 --- a/tests/fs/seek.rs +++ b/tests/fs/seek.rs @@ -1,4 +1,4 @@ -/// Test seek positions related to file "holes". +/// Test seek positions related to file “holes”. #[cfg(any(apple, freebsdlike, linux_kernel, solarish))] #[test] fn test_seek_holes() { @@ -61,9 +61,9 @@ fn test_seek_holes() { #[test] fn test_seek_offsets() { - use rustix::fs::{openat, seek, Mode, OFlags, SeekFrom, CWD}; + use rustix::fs::{open, seek, Mode, OFlags, SeekFrom}; - let f = openat(CWD, "Cargo.toml", OFlags::RDONLY, Mode::empty()).unwrap(); + let f = open("Cargo.toml", OFlags::RDONLY, Mode::empty()).unwrap(); match seek(&f, SeekFrom::Start(0)) { Ok(_) => {} diff --git a/tests/io/close.rs b/tests/io/close.rs index a7a973192..920bd158d 100644 --- a/tests/io/close.rs +++ b/tests/io/close.rs @@ -18,3 +18,13 @@ fn test_close_socket() { rustix::io::close(raw); } } + +#[cfg(all(feature = "try_close", any(unix, target_os = "wasi")))] +#[test] +fn test_try_close() { + let file = std::fs::File::open("Cargo.toml").unwrap(); + let raw = file.into_raw_fd(); + unsafe { + rustix::io::try_close(raw).unwrap(); + } +} diff --git a/tests/io/dup.rs b/tests/io/dup.rs index 89d91b682..b132cbb70 100644 --- a/tests/io/dup.rs +++ b/tests/io/dup.rs @@ -17,7 +17,7 @@ fn test_dup() { let mut buf = [0_u8; 4]; assert_eq!(rustix::io::read(&file, &mut buf), Ok(4)); - // Both postitions updated. + // Both positions updated. assert_eq!( rustix::fs::seek(&file, rustix::fs::SeekFrom::Current(0)), Ok(4) @@ -29,7 +29,7 @@ fn test_dup() { assert_eq!(rustix::io::read(&alt, &mut buf), Ok(4)); - // Both postitions updated. + // Both positions updated. assert_eq!( rustix::fs::seek(&file, rustix::fs::SeekFrom::Current(0)), Ok(8) diff --git a/tests/io_uring/main.rs b/tests/io_uring/main.rs new file mode 100644 index 000000000..bcafe4926 --- /dev/null +++ b/tests/io_uring/main.rs @@ -0,0 +1,4 @@ +#[cfg(linux_kernel)] +#[cfg(feature = "io_uring")] +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +mod register; diff --git a/tests/io_uring/register.rs b/tests/io_uring/register.rs new file mode 100644 index 000000000..929241e76 --- /dev/null +++ b/tests/io_uring/register.rs @@ -0,0 +1,106 @@ +use libc::c_void; +use rustix::fd::{AsFd, AsRawFd, BorrowedFd}; +use rustix::io::Result; +use rustix::io_uring::{ + io_uring_params, io_uring_register_with, io_uring_rsrc_update, io_uring_setup, + IoringFeatureFlags, IoringRegisterFlags, IoringRegisterOp, +}; + +fn do_register( + fd: FD, + registered_fd: bool, + opcode: IoringRegisterOp, + arg: *const c_void, + arg_nr: u32, +) -> Result<()> +where + FD: AsFd, +{ + let flags = if registered_fd { + IoringRegisterFlags::USE_REGISTERED_RING + } else { + IoringRegisterFlags::default() + }; + + unsafe { + io_uring_register_with(fd, opcode, flags, arg, arg_nr)?; + } + + Ok(()) +} + +fn register_ring(fd: BorrowedFd<'_>) -> Result> { + let update = io_uring_rsrc_update { + data: fd.as_raw_fd() as u64, + offset: u32::MAX, + resv: 0, + }; + + do_register( + fd, + false, + IoringRegisterOp::RegisterRingFds, + (&update) as *const io_uring_rsrc_update as *const c_void, + 1, + )?; + + let registered_fd = unsafe { BorrowedFd::borrow_raw(update.offset as i32) }; + Ok(registered_fd) +} + +fn unregister_ring(fd: FD) -> Result<()> +where + FD: AsRawFd + AsFd, +{ + let update = io_uring_rsrc_update { + offset: fd.as_raw_fd() as u32, + data: 0, + resv: 0, + }; + + do_register( + fd, + true, + IoringRegisterOp::UnregisterRingFds, + (&update) as *const io_uring_rsrc_update as *const c_void, + 1, + )?; + + Ok(()) +} + +/// Set bounded and unbounded async kernel worker counts to 0, to test +/// registering with registered ring fd. +fn register_iowq_max_workers(fd: FD) -> Result<()> +where + FD: AsFd, +{ + let iowq_max_workers = [0u32; 2]; + do_register( + fd, + true, + IoringRegisterOp::RegisterIowqMaxWorkers, + (&iowq_max_workers) as *const [u32; 2] as *const c_void, + 2, + )?; + + Ok(()) +} + +#[test] +fn test_io_uring_register_with() { + let mut params = io_uring_params::default(); + let ring_fd = io_uring_setup(4, &mut params).unwrap(); + assert_eq!(params.sq_entries, 4); + assert_eq!(params.cq_entries, 8); + + if !params.features.contains(IoringFeatureFlags::REG_REG_RING) { + // Kernel does not support `io_uring_register` with a registered ring fd + return; + } + + let ring_fd = register_ring(ring_fd.as_fd()).unwrap(); + let register_result = register_iowq_max_workers(ring_fd); + unregister_ring(ring_fd).unwrap(); + register_result.unwrap(); +} diff --git a/tests/net/connect_bind_send.rs b/tests/net/connect_bind_send.rs index 95e68526e..05eae1990 100644 --- a/tests/net/connect_bind_send.rs +++ b/tests/net/connect_bind_send.rs @@ -9,6 +9,8 @@ use std::net::{IpAddr, Ipv4Addr, SocketAddr}; /// Test `connect_any`. #[test] fn net_v4_connect_any() { + crate::init(); + let localhost = IpAddr::V4(Ipv4Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET, SocketType::STREAM, None).unwrap(); @@ -40,6 +42,8 @@ fn net_v4_connect_any() { #[cfg(not(any(apple, windows, target_os = "haiku")))] #[test] fn net_v4_connect_any_accept_with() { + crate::init(); + let localhost = IpAddr::V4(Ipv4Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET, SocketType::STREAM, None).unwrap(); @@ -70,6 +74,8 @@ fn net_v4_connect_any_accept_with() { /// Similar, but with V6. #[test] fn net_v6_connect_any() { + crate::init(); + let localhost = IpAddr::V6(Ipv6Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET6, SocketType::STREAM, None).unwrap(); @@ -101,6 +107,8 @@ fn net_v6_connect_any() { #[cfg(not(any(apple, windows, target_os = "haiku")))] #[test] fn net_v6_connect_any_accept_with() { + crate::init(); + let localhost = IpAddr::V6(Ipv6Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET6, SocketType::STREAM, None).unwrap(); @@ -131,6 +139,8 @@ fn net_v6_connect_any_accept_with() { /// Test `connect` with a `SocketAddr`. #[test] fn net_v4_connect() { + crate::init(); + let localhost = IpAddr::V4(Ipv4Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET, SocketType::STREAM, None).unwrap(); @@ -164,6 +174,8 @@ fn net_v4_connect() { /// Similar, but use V6. #[test] fn net_v6_connect() { + crate::init(); + let localhost = IpAddr::V6(Ipv6Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET6, SocketType::STREAM, None).unwrap(); @@ -197,6 +209,8 @@ fn net_v6_connect() { /// Test `connect_unspec`. #[test] fn net_v4_connect_unspec() { + crate::init(); + const SOME_PORT: u16 = 47; let localhost_addr = SocketAddrV4::new(Ipv4Addr::LOCALHOST, SOME_PORT); @@ -241,6 +255,8 @@ fn net_v4_connect_unspec() { /// Test `connect_unspec`. #[test] fn net_v6_connect_unspec() { + crate::init(); + const SOME_PORT: u16 = 47; let localhost_addr = SocketAddrV6::new(Ipv6Addr::LOCALHOST, SOME_PORT, 0, 0); @@ -285,6 +301,8 @@ fn net_v6_connect_unspec() { /// Test `bind_any`. #[test] fn net_v4_bind_any() { + crate::init(); + let localhost = Ipv4Addr::LOCALHOST; let addr = SocketAddrV4::new(localhost, 0).into(); let listener = rustix::net::socket(AddressFamily::INET, SocketType::STREAM, None).unwrap(); @@ -314,6 +332,8 @@ fn net_v4_bind_any() { /// Similar, but use V6. #[test] fn net_v6_bind_any() { + crate::init(); + let localhost = Ipv6Addr::LOCALHOST; let addr = SocketAddrAny::V6(SocketAddrV6::new(localhost, 0, 0, 0)); let listener = rustix::net::socket(AddressFamily::INET6, SocketType::STREAM, None).unwrap(); @@ -343,6 +363,8 @@ fn net_v6_bind_any() { /// Test `sendto`. #[test] fn net_v4_sendto() { + crate::init(); + let localhost = IpAddr::V4(Ipv4Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET, SocketType::STREAM, None).unwrap(); @@ -378,6 +400,8 @@ fn net_v4_sendto() { /// Similar, but with V6. #[test] fn net_v6_sendto() { + crate::init(); + let localhost = IpAddr::V6(Ipv6Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET6, SocketType::STREAM, None).unwrap(); @@ -413,6 +437,8 @@ fn net_v6_sendto() { /// Test `sendto_any`. #[test] fn net_v4_sendto_any() { + crate::init(); + let localhost = IpAddr::V4(Ipv4Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET, SocketType::STREAM, None).unwrap(); @@ -445,6 +471,8 @@ fn net_v4_sendto_any() { /// Similar, but with V6. #[test] fn net_v6_sendto_any() { + crate::init(); + let localhost = IpAddr::V6(Ipv6Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET6, SocketType::STREAM, None).unwrap(); @@ -477,6 +505,8 @@ fn net_v6_sendto_any() { /// Test `acceptfrom`. #[test] fn net_v4_acceptfrom() { + crate::init(); + let localhost = IpAddr::V4(Ipv4Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET, SocketType::STREAM, None).unwrap(); @@ -531,6 +561,8 @@ fn net_v4_acceptfrom() { /// Similar, but with V6. #[test] fn net_v6_acceptfrom() { + crate::init(); + let localhost = IpAddr::V6(Ipv6Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET6, SocketType::STREAM, None).unwrap(); @@ -585,6 +617,8 @@ fn net_v6_acceptfrom() { /// Test `shutdown`. #[test] fn net_shutdown() { + crate::init(); + let localhost = IpAddr::V4(Ipv4Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET, SocketType::STREAM, None).unwrap(); diff --git a/tests/net/dgram.rs b/tests/net/dgram.rs index d8cebb24c..906be8a4e 100644 --- a/tests/net/dgram.rs +++ b/tests/net/dgram.rs @@ -9,6 +9,8 @@ use std::net::{IpAddr, Ipv4Addr, SocketAddr}; /// Test `connect_any`. #[test] fn net_dgram_v4_connect_any() { + crate::init(); + let localhost = IpAddr::V4(Ipv4Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET, SocketType::DGRAM, None).unwrap(); @@ -38,6 +40,8 @@ fn net_dgram_v4_connect_any() { #[cfg(not(any(apple, windows, target_os = "haiku")))] #[test] fn net_dgram_v4_connect_any_accept_with() { + crate::init(); + let localhost = IpAddr::V4(Ipv4Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET, SocketType::DGRAM, None).unwrap(); @@ -66,6 +70,8 @@ fn net_dgram_v4_connect_any_accept_with() { /// Similar, but with V6. #[test] fn net_dgram_v6_connect_any() { + crate::init(); + let localhost = IpAddr::V6(Ipv6Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET6, SocketType::DGRAM, None).unwrap(); @@ -95,6 +101,8 @@ fn net_dgram_v6_connect_any() { #[cfg(not(any(apple, windows, target_os = "haiku")))] #[test] fn net_dgram_v6_connect_any_accept_with() { + crate::init(); + let localhost = IpAddr::V6(Ipv6Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET6, SocketType::DGRAM, None).unwrap(); @@ -123,6 +131,8 @@ fn net_dgram_v6_connect_any_accept_with() { /// Test `connect` with a `SocketAddr`. #[test] fn net_dgram_v4_connect() { + crate::init(); + let localhost = IpAddr::V4(Ipv4Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET, SocketType::DGRAM, None).unwrap(); @@ -154,6 +164,8 @@ fn net_dgram_v4_connect() { /// Similar, but use V6. #[test] fn net_dgram_v6_connect() { + crate::init(); + let localhost = IpAddr::V6(Ipv6Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET6, SocketType::DGRAM, None).unwrap(); @@ -185,6 +197,8 @@ fn net_dgram_v6_connect() { /// Test `connect_unspec`. #[test] fn net_dgram_v4_connect_unspec() { + crate::init(); + const SOME_PORT: u16 = 47; let localhost_addr = SocketAddrV4::new(Ipv4Addr::LOCALHOST, SOME_PORT); @@ -229,6 +243,8 @@ fn net_dgram_v4_connect_unspec() { /// Test `connect_unspec`. #[test] fn net_dgram_v6_connect_unspec() { + crate::init(); + const SOME_PORT: u16 = 47; let localhost_addr = SocketAddrV6::new(Ipv6Addr::LOCALHOST, SOME_PORT, 0, 0); @@ -273,6 +289,8 @@ fn net_dgram_v6_connect_unspec() { /// Test `bind_any`. #[test] fn net_dgram_v4_bind_any() { + crate::init(); + let localhost = Ipv4Addr::LOCALHOST; let addr = SocketAddrV4::new(localhost, 0).into(); let listener = rustix::net::socket(AddressFamily::INET, SocketType::DGRAM, None).unwrap(); @@ -300,6 +318,8 @@ fn net_dgram_v4_bind_any() { /// Similar, but use V6. #[test] fn net_dgram_v6_bind_any() { + crate::init(); + let localhost = Ipv6Addr::LOCALHOST; let addr = SocketAddrAny::V6(SocketAddrV6::new(localhost, 0, 0, 0)); let listener = rustix::net::socket(AddressFamily::INET6, SocketType::DGRAM, None).unwrap(); @@ -328,6 +348,8 @@ fn net_dgram_v6_bind_any() { #[cfg(not(any(bsd, target_os = "illumos")))] #[test] fn net_dgram_v4_connect_sendto() { + crate::init(); + let localhost = IpAddr::V4(Ipv4Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET, SocketType::DGRAM, None).unwrap(); @@ -373,6 +395,8 @@ fn net_dgram_v4_connect_sendto() { /// Test `sendto` without calling `connect`. #[test] fn net_dgram_v4_sendto() { + crate::init(); + let localhost = IpAddr::V4(Ipv4Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET, SocketType::DGRAM, None).unwrap(); @@ -418,6 +442,8 @@ fn net_dgram_v4_sendto() { #[cfg(not(any(bsd, target_os = "illumos")))] #[test] fn net_dgram_v6_connect_sendto() { + crate::init(); + let localhost = IpAddr::V6(Ipv6Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET6, SocketType::DGRAM, None).unwrap(); @@ -463,6 +489,8 @@ fn net_dgram_v6_connect_sendto() { /// Similar, but with V6. #[test] fn net_dgram_v6_sendto() { + crate::init(); + let localhost = IpAddr::V6(Ipv6Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET6, SocketType::DGRAM, None).unwrap(); @@ -508,6 +536,8 @@ fn net_dgram_v6_sendto() { #[cfg(not(any(bsd, target_os = "illumos")))] #[test] fn net_dgram_v4_connect_sendto_any() { + crate::init(); + let localhost = IpAddr::V4(Ipv4Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET, SocketType::DGRAM, None).unwrap(); @@ -550,6 +580,8 @@ fn net_dgram_v4_connect_sendto_any() { /// Test `sendto_any` without calling connect. #[test] fn net_dgram_v4_sendto_any() { + crate::init(); + let localhost = IpAddr::V4(Ipv4Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET, SocketType::DGRAM, None).unwrap(); @@ -592,6 +624,8 @@ fn net_dgram_v4_sendto_any() { #[cfg(not(any(bsd, target_os = "illumos")))] #[test] fn net_dgram_v6_connect_sendto_any() { + crate::init(); + let localhost = IpAddr::V6(Ipv6Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET6, SocketType::DGRAM, None).unwrap(); @@ -634,6 +668,8 @@ fn net_dgram_v6_connect_sendto_any() { /// Similar, but with V6. #[test] fn net_dgram_v6_sendto_any() { + crate::init(); + let localhost = IpAddr::V6(Ipv6Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET6, SocketType::DGRAM, None).unwrap(); @@ -675,6 +711,8 @@ fn net_dgram_v6_sendto_any() { /// Test `acceptfrom`. #[test] fn net_dgram_v4_acceptfrom() { + crate::init(); + let localhost = IpAddr::V4(Ipv4Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET, SocketType::DGRAM, None).unwrap(); @@ -702,6 +740,8 @@ fn net_dgram_v4_acceptfrom() { /// Similar, but with V6. #[test] fn net_dgram_v6_acceptfrom() { + crate::init(); + let localhost = IpAddr::V6(Ipv6Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET6, SocketType::DGRAM, None).unwrap(); diff --git a/tests/net/main.rs b/tests/net/main.rs index 4af96f534..2c4b6a312 100644 --- a/tests/net/main.rs +++ b/tests/net/main.rs @@ -20,18 +20,31 @@ mod unix_alloc; mod v4; mod v6; -/// Windows requires us to call a setup function before using any of the -/// socket APIs. #[cfg(windows)] -#[ctor::ctor] -fn windows_startup() { - let _ = rustix::net::wsa_startup().unwrap(); +mod windows { + use std::sync::OnceLock; + + pub struct Thing; + + impl Thing { + pub fn new() -> Self { + let _ = rustix::net::wsa_startup().unwrap(); + Self + } + } + + impl Drop for Thing { + fn drop(&mut self) { + rustix::net::wsa_cleanup().unwrap(); + } + } + + pub static CLEANUP: OnceLock = OnceLock::new(); } -/// Windows requires us to call a cleanup function after using any of the -/// socket APIs. -#[cfg(windows)] -#[ctor::dtor] -fn windows_shutdown() { - rustix::net::wsa_cleanup().unwrap(); +/// Checks whether the Windows socket interface has been started already, and +/// if not, starts it. +pub fn init() { + #[cfg(windows)] + let _ = windows::CLEANUP.get_or_init(|| windows::Thing::new()); } diff --git a/tests/net/poll.rs b/tests/net/poll.rs index 053041e0b..a6d637fe6 100644 --- a/tests/net/poll.rs +++ b/tests/net/poll.rs @@ -95,6 +95,8 @@ fn client(ready: Arc<(Mutex, Condvar)>) { #[test] fn test_poll() { + crate::init(); + let ready = Arc::new((Mutex::new(0_u16), Condvar::new())); let ready_clone = Arc::clone(&ready); diff --git a/tests/net/sockopt.rs b/tests/net/sockopt.rs index bb555d71b..b98a6b4cd 100644 --- a/tests/net/sockopt.rs +++ b/tests/net/sockopt.rs @@ -59,7 +59,8 @@ fn test_sockopts_socket(s: &OwnedFd) { >= Duration::new(1, 1) ); } else { - // On FreeBSD <= 12 and NetBSD, it appears the system rounds the timeout down. + // On FreeBSD ≤ 12 and NetBSD, it appears the system rounds the timeout + // down. assert!( sockopt::get_socket_timeout(s, sockopt::Timeout::Recv) .unwrap() @@ -141,7 +142,7 @@ fn test_sockopts_socket(s: &OwnedFd) { // Check that the oobinline flag is set. assert!(sockopt::get_socket_oobinline(s).unwrap()); - // Check the initial value of SO_REUSEPORT, set it, and check it. + // Check the initial value of `SO_REUSEPORT`, set it, and check it. #[cfg(not(any(solarish, windows)))] { assert!(!sockopt::get_socket_reuseport(s).unwrap()); @@ -149,7 +150,7 @@ fn test_sockopts_socket(s: &OwnedFd) { assert!(sockopt::get_socket_reuseport(s).unwrap()); } - // Check the initial value of SO_REUSEPORT_LB, set it, and check it. + // Check the initial value of `SO_REUSEPORT_LB`, set it, and check it. #[cfg(target_os = "freebsd")] { assert!(!sockopt::get_socket_reuseport_lb(s).unwrap()); @@ -167,7 +168,7 @@ fn test_sockopts_socket(s: &OwnedFd) { ); } - // Check the initial value of SO_INCOMING_CPU, set it, and check it. + // Check the initial value of `SO_INCOMING_CPU`, set it, and check it. #[cfg(target_os = "linux")] { assert_eq!(sockopt::get_socket_incoming_cpu(s).unwrap(), u32::MAX); @@ -175,7 +176,7 @@ fn test_sockopts_socket(s: &OwnedFd) { assert_eq!(sockopt::get_socket_incoming_cpu(s).unwrap(), 3); } - // Check the initial value of SO_NOSIGPIPE, set it, and check it. + // Check the initial value of `SO_NOSIGPIPE`, set it, and check it. #[cfg(any(apple, freebsdlike, target_os = "netbsd"))] { assert_eq!(sockopt::get_socket_nosigpipe(s).unwrap(), false); @@ -186,7 +187,7 @@ fn test_sockopts_socket(s: &OwnedFd) { // Test `tcp` socket options. fn test_sockopts_tcp(s: &OwnedFd) { - #[cfg(any(linux_like, taraget_os = "fuchsia"))] + #[cfg(any(linux_like, target_os = "fuchsia"))] { assert_eq!(sockopt::get_tcp_user_timeout(s).unwrap(), 0); sockopt::set_tcp_user_timeout(s, 7).unwrap(); @@ -242,7 +243,7 @@ fn test_sockopts_tcp(s: &OwnedFd) { } } - // Check the initial value of TCP_QUICKACK, set it, and check it. + // Check the initial value of `TCP_QUICKACK`, set it, and check it. #[cfg(any(linux_like, target_os = "fuchsia"))] { assert!(sockopt::get_tcp_quickack(s).unwrap()); @@ -250,7 +251,7 @@ fn test_sockopts_tcp(s: &OwnedFd) { assert!(!sockopt::get_tcp_quickack(s).unwrap()); } - // Check the initial value of TCP_CONGESTION, set it, and check it. + // Check the initial value of `TCP_CONGESTION`, set it, and check it. // // Temporarily disable this test on non-x86 as qemu isn't yet aware of // TCP_CONGESTION. @@ -272,7 +273,7 @@ fn test_sockopts_tcp(s: &OwnedFd) { } } - // Check the initial value of TCP_THIN_LINEAR_TIMEOUTS, set it, and check + // Check the initial value of `TCP_THIN_LINEAR_TIMEOUTS`, set it, and check // it. #[cfg(any(linux_like, target_os = "fuchsia"))] { @@ -281,7 +282,7 @@ fn test_sockopts_tcp(s: &OwnedFd) { assert!(sockopt::get_tcp_thin_linear_timeouts(s).unwrap()); } - // Check the initial value of TCP_CORK, set it, and check it. + // Check the initial value of `TCP_CORK`, set it, and check it. #[cfg(any(linux_like, solarish, target_os = "fuchsia"))] { assert!(!sockopt::get_tcp_cork(s).unwrap()); @@ -292,6 +293,8 @@ fn test_sockopts_tcp(s: &OwnedFd) { #[test] fn test_sockopts_ipv4() { + crate::init(); + let s = rustix::net::socket(AddressFamily::INET, SocketType::STREAM, None).unwrap(); test_sockopts_socket(&s); @@ -329,7 +332,7 @@ fn test_sockopts_ipv4() { assert!(!sockopt::get_ip_multicast_loop(&s).unwrap()); } - // Check the initial value of IP TOS, set it, and check it. + // Check the initial value of `IP_TOS`, set it, and check it. #[cfg(any( bsd, linux_like, @@ -349,7 +352,7 @@ fn test_sockopts_ipv4() { } } - // Check the initial value of IP RECVTOS, set it, and check it. + // Check the initial value of `IP_RECVTOS`, set it, and check it. #[cfg(any(apple, linux_like, target_os = "freebsd", target_os = "fuchsia"))] { assert!(!sockopt::get_ip_recvtos(&s).unwrap()); @@ -357,7 +360,7 @@ fn test_sockopts_ipv4() { assert!(sockopt::get_ip_recvtos(&s).unwrap()); } - // Check the initial value of IP_FREEBIND, set it, and check it. + // Check the initial value of `IP_FREEBIND`, set it, and check it. #[cfg(any(linux_kernel, target_os = "fuchsia"))] { assert!(!sockopt::get_ip_freebind(&s).unwrap()); @@ -365,7 +368,7 @@ fn test_sockopts_ipv4() { assert!(sockopt::get_ip_freebind(&s).unwrap()); } - // Check that we can query SO_ORIGINAL_DST. + // Check that we can query `SO_ORIGINAL_DST`. #[cfg(any(linux_kernel, target_os = "fuchsia"))] { assert!(matches!( @@ -379,6 +382,8 @@ fn test_sockopts_ipv4() { #[test] fn test_sockopts_ipv6() { + crate::init(); + let s = rustix::net::socket(AddressFamily::INET6, SocketType::STREAM, None).unwrap(); test_sockopts_socket(&s); @@ -452,7 +457,7 @@ fn test_sockopts_ipv6() { // Check that the IPV6 unicast hops value is set. assert_eq!(sockopt::get_ipv6_unicast_hops(&s).unwrap(), 8); - // Check the initial value of IPV6 RECVTCLASS, set it, and check it. + // Check the initial value of `IPV6_RECVTCLASS`, set it, and check it. #[cfg(any( bsd, linux_like, @@ -466,7 +471,7 @@ fn test_sockopts_ipv6() { assert!(sockopt::get_ipv6_recvtclass(&s).unwrap()); } - // Check the initial value of IPV6_FREEBIND, set it, and check it. + // Check the initial value of `IPV6_FREEBIND`, set it, and check it. #[cfg(linux_kernel)] { assert!(!sockopt::get_ipv6_freebind(&s).unwrap()); @@ -474,7 +479,7 @@ fn test_sockopts_ipv6() { assert!(sockopt::get_ipv6_freebind(&s).unwrap()); } - // Check the initial value of IPV6_TCLASS, set it, and check it. + // Check the initial value of `IPV6_TCLASS`, set it, and check it. #[cfg(not(any(solarish, windows, target_os = "espidf", target_os = "haiku")))] { assert_eq!(sockopt::get_ipv6_tclass(&s).unwrap(), 0); @@ -482,7 +487,7 @@ fn test_sockopts_ipv6() { assert_eq!(sockopt::get_ipv6_tclass(&s).unwrap(), 12); } - // Check that we can query IP6T_SO_ORIGINAL_DST. + // Check that we can query `IP6T_SO_ORIGINAL_DST`. #[cfg(linux_kernel)] { assert!(matches!( diff --git a/tests/net/unix.rs b/tests/net/unix.rs index 9f55feec8..acfacdc0d 100644 --- a/tests/net/unix.rs +++ b/tests/net/unix.rs @@ -2,7 +2,8 @@ //! //! The client sends lists of integers and the server sends back sums. -// This test uses `AF_UNIX` with `SOCK_SEQPACKET` which is unsupported on macOS. +// This test uses `AF_UNIX` with `SOCK_SEQPACKET` which is unsupported on +// macOS. #![cfg(not(any(apple, target_os = "espidf", target_os = "redox", target_os = "wasi")))] // This test uses `DecInt`. #![cfg(feature = "itoa")] @@ -93,6 +94,8 @@ fn client(ready: Arc<(Mutex, Condvar)>, path: &Path, runs: &[(&[&str], i32 #[test] fn test_unix() { + crate::init(); + let ready = Arc::new((Mutex::new(false), Condvar::new())); let ready_clone = Arc::clone(&ready); @@ -360,6 +363,8 @@ fn do_test_unix_msg_unconnected(addr: SocketAddrUnix) { #[cfg(not(any(target_os = "espidf", target_os = "redox", target_os = "wasi")))] #[test] fn test_unix_msg() { + crate::init(); + let tmpdir = tempfile::tempdir().unwrap(); let path = tmpdir.path().join("scp_4804"); @@ -373,6 +378,8 @@ fn test_unix_msg() { #[cfg(not(any(target_os = "espidf", target_os = "redox", target_os = "wasi")))] #[test] fn test_unix_msg_unconnected() { + crate::init(); + let tmpdir = tempfile::tempdir().unwrap(); let path = tmpdir.path().join("scp_4804"); @@ -385,6 +392,8 @@ fn test_unix_msg_unconnected() { #[cfg(linux_kernel)] #[test] fn test_abstract_unix_msg() { + crate::init(); + use std::os::unix::ffi::OsStrExt; let tmpdir = tempfile::tempdir().unwrap(); @@ -398,6 +407,8 @@ fn test_abstract_unix_msg() { #[cfg(linux_kernel)] #[test] fn test_abstract_unix_msg_unconnected() { + crate::init(); + use std::os::unix::ffi::OsStrExt; let tmpdir = tempfile::tempdir().unwrap(); @@ -410,6 +421,8 @@ fn test_abstract_unix_msg_unconnected() { #[cfg(not(any(target_os = "redox", target_os = "wasi")))] #[test] fn test_unix_msg_with_scm_rights() { + crate::init(); + use rustix::fd::AsFd; use rustix::io::{IoSlice, IoSliceMut}; use rustix::net::{ @@ -583,6 +596,8 @@ fn test_unix_msg_with_scm_rights() { #[cfg(all(feature = "process", linux_kernel))] #[test] fn test_unix_peercred_explicit() { + crate::init(); + use rustix::io::{IoSlice, IoSliceMut}; use rustix::net::{ recvmsg, sendmsg, sockopt, RecvAncillaryBuffer, RecvAncillaryMessage, RecvFlags, @@ -627,7 +642,7 @@ fn test_unix_peercred_explicit() { match cmsg_buffer.drain().next().unwrap() { RecvAncillaryMessage::ScmCredentials(ucred2) => assert_eq!(ucred2, ucred), - _ => panic!("Unexpected ancilliary message"), + _ => panic!("Unexpected ancillary message"), }; } @@ -637,6 +652,8 @@ fn test_unix_peercred_explicit() { #[cfg(all(feature = "process", linux_kernel))] #[test] fn test_unix_peercred_implicit() { + crate::init(); + use rustix::io::{IoSlice, IoSliceMut}; use rustix::net::{ recvmsg, sendmsg, sockopt, RecvAncillaryBuffer, RecvAncillaryMessage, RecvFlags, @@ -683,7 +700,7 @@ fn test_unix_peercred_implicit() { match cmsg_buffer.drain().next().unwrap() { RecvAncillaryMessage::ScmCredentials(ucred2) => assert_eq!(ucred2, ucred), - _ => panic!("Unexpected ancilliary message"), + _ => panic!("Unexpected ancillary message"), }; } @@ -692,6 +709,8 @@ fn test_unix_peercred_implicit() { #[cfg(not(any(target_os = "redox", target_os = "wasi")))] #[test] fn test_unix_msg_with_combo() { + crate::init(); + use rustix::fd::AsFd; use rustix::io::{IoSlice, IoSliceMut}; use rustix::net::{ diff --git a/tests/net/unix_alloc.rs b/tests/net/unix_alloc.rs index b32af61ef..5ed2b4b28 100644 --- a/tests/net/unix_alloc.rs +++ b/tests/net/unix_alloc.rs @@ -1,6 +1,7 @@ //! Like unix.rs, but uses `Vec`s for the buffers. -// This test uses `AF_UNIX` with `SOCK_SEQPACKET` which is unsupported on macOS. +// This test uses `AF_UNIX` with `SOCK_SEQPACKET` which is unsupported on +// macOS. #![cfg(not(any(apple, target_os = "espidf", target_os = "redox", target_os = "wasi")))] // This test uses `DecInt`. #![cfg(feature = "itoa")] @@ -91,6 +92,8 @@ fn client(ready: Arc<(Mutex, Condvar)>, path: &Path, runs: &[(&[&str], i32 #[test] fn test_unix() { + crate::init(); + let ready = Arc::new((Mutex::new(false), Condvar::new())); let ready_clone = Arc::clone(&ready); @@ -358,6 +361,8 @@ fn do_test_unix_msg_unconnected(addr: SocketAddrUnix) { #[cfg(not(any(target_os = "espidf", target_os = "redox", target_os = "wasi")))] #[test] fn test_unix_msg() { + crate::init(); + let tmpdir = tempfile::tempdir().unwrap(); let path = tmpdir.path().join("scp_4804"); @@ -371,6 +376,8 @@ fn test_unix_msg() { #[cfg(not(any(target_os = "espidf", target_os = "redox", target_os = "wasi")))] #[test] fn test_unix_msg_unconnected() { + crate::init(); + let tmpdir = tempfile::tempdir().unwrap(); let path = tmpdir.path().join("scp_4804"); @@ -383,6 +390,8 @@ fn test_unix_msg_unconnected() { #[cfg(linux_kernel)] #[test] fn test_abstract_unix_msg() { + crate::init(); + use std::os::unix::ffi::OsStrExt; let tmpdir = tempfile::tempdir().unwrap(); @@ -396,6 +405,8 @@ fn test_abstract_unix_msg() { #[cfg(linux_kernel)] #[test] fn test_abstract_unix_msg_unconnected() { + crate::init(); + use std::os::unix::ffi::OsStrExt; let tmpdir = tempfile::tempdir().unwrap(); @@ -408,6 +419,8 @@ fn test_abstract_unix_msg_unconnected() { #[cfg(not(any(target_os = "redox", target_os = "wasi")))] #[test] fn test_unix_msg_with_scm_rights() { + crate::init(); + use rustix::fd::AsFd; use rustix::io::{IoSlice, IoSliceMut}; use rustix::net::{ @@ -581,6 +594,8 @@ fn test_unix_msg_with_scm_rights() { #[cfg(all(feature = "process", linux_kernel))] #[test] fn test_unix_peercred() { + crate::init(); + use rustix::io::{IoSlice, IoSliceMut}; use rustix::net::{ recvmsg, sendmsg, sockopt, RecvAncillaryBuffer, RecvAncillaryMessage, RecvFlags, @@ -630,7 +645,7 @@ fn test_unix_peercred() { match cmsg_buffer.drain().next().unwrap() { RecvAncillaryMessage::ScmCredentials(ucred2) => assert_eq!(ucred2, ucred), - _ => panic!("Unexpected ancilliary message"), + _ => panic!("Unexpected ancillary message"), }; } @@ -639,6 +654,8 @@ fn test_unix_peercred() { #[cfg(not(any(target_os = "redox", target_os = "wasi")))] #[test] fn test_unix_msg_with_combo() { + crate::init(); + use rustix::fd::AsFd; use rustix::io::{IoSlice, IoSliceMut}; use rustix::net::{ diff --git a/tests/net/v4.rs b/tests/net/v4.rs index 99c70e135..d770b657b 100644 --- a/tests/net/v4.rs +++ b/tests/net/v4.rs @@ -65,6 +65,8 @@ fn client(ready: Arc<(Mutex, Condvar)>) { #[test] fn test_v4() { + crate::init(); + let ready = Arc::new((Mutex::new(0_u16), Condvar::new())); let ready_clone = Arc::clone(&ready); @@ -87,6 +89,8 @@ fn test_v4() { #[cfg(not(any(windows, target_os = "espidf", target_os = "redox", target_os = "wasi")))] #[test] fn test_v4_msg() { + crate::init(); + use rustix::io::{IoSlice, IoSliceMut}; use rustix::net::{recvmsg, sendmsg}; diff --git a/tests/net/v6.rs b/tests/net/v6.rs index c4946a63f..0d0a596c9 100644 --- a/tests/net/v6.rs +++ b/tests/net/v6.rs @@ -65,6 +65,8 @@ fn client(ready: Arc<(Mutex, Condvar)>) { #[test] fn test_v6() { + crate::init(); + let ready = Arc::new((Mutex::new(0_u16), Condvar::new())); let ready_clone = Arc::clone(&ready); @@ -87,6 +89,8 @@ fn test_v6() { #[cfg(not(any(windows, target_os = "espidf", target_os = "redox", target_os = "wasi")))] #[test] fn test_v6_msg() { + crate::init(); + use rustix::io::{IoSlice, IoSliceMut}; use rustix::net::{recvmsg, sendmsg}; diff --git a/tests/param/auxv.rs b/tests/param/auxv.rs index 7806fa357..f7755aad7 100644 --- a/tests/param/auxv.rs +++ b/tests/param/auxv.rs @@ -49,11 +49,9 @@ fn test_linux_minsigstksz() { weak!(fn getauxval(libc::c_ulong) -> libc::c_ulong); if let Some(libc_getauxval) = getauxval.get() { - // FIXME: reuse const from libc when available? - const AT_MINSIGSTKSZ: libc::c_ulong = 51; assert_eq!( linux_minsigstksz(), - unsafe { libc_getauxval(AT_MINSIGSTKSZ) } as usize + unsafe { libc_getauxval(libc::AT_MINSIGSTKSZ) } as usize ); } } diff --git a/tests/param/weak.rs b/tests/param/weak.rs index 4859a5b37..8fdd946d5 100644 --- a/tests/param/weak.rs +++ b/tests/param/weak.rs @@ -77,7 +77,7 @@ impl Weak { // need to have loaded it with at least C11's consume // ordering in order to be guaranteed that the data we read // from the pointer isn't from before the pointer was - // stored. Rust has no equivalent to memory_order_consume, + // stored. Rust has no equivalent to `memory_order_consume`, // so we use an acquire fence (sorry, ARM). // // Now, in practice this likely isn't needed even on CPUs diff --git a/tests/process/working_directory.rs b/tests/process/working_directory.rs index e3ed56818..c80bb1134 100644 --- a/tests/process/working_directory.rs +++ b/tests/process/working_directory.rs @@ -39,8 +39,8 @@ fn test_changing_working_directory() { #[cfg(not(target_os = "fuchsia"))] rustix::process::fchdir(orig_fd_cwd).expect("changing dir to the original"); - #[cfg(target_os = "fushcia")] - rustix::process::chdir(orig_cwd).expect("changing dir to the original"); + #[cfg(target_os = "fuchsia")] + rustix::process::chdir(&orig_cwd).expect("changing dir to the original"); let ch2_cwd = rustix::process::getcwd(ch1_cwd).expect("get the cwd"); assert_eq!( diff --git a/tests/procfs/basic.rs b/tests/procfs/basic.rs index 6ceaa41a8..1512ffe96 100644 --- a/tests/procfs/basic.rs +++ b/tests/procfs/basic.rs @@ -14,3 +14,20 @@ fn test_status_twice() { let fd = rustix::procfs::proc_self_status().unwrap(); drop(fd); } + +#[test] +fn parallel_self_proc_status() { + const THREADS: usize = 3; + + fn self_proc_status() { + rustix::procfs::proc_self_status().expect("error getting proc/self/status pid"); + } + + let mut handles = Vec::with_capacity(THREADS); + for _ in 0..THREADS { + handles.push(std::thread::spawn(self_proc_status)); + } + for handle in handles.drain(..) { + handle.join().expect("thread crashed"); + } +} diff --git a/tests/stdio/dup2_stdio.rs b/tests/stdio/dup2_stdio.rs new file mode 100644 index 000000000..0a26c534c --- /dev/null +++ b/tests/stdio/dup2_stdio.rs @@ -0,0 +1,20 @@ +use rustix::io::fcntl_getfd; +use rustix::stdio::{dup2_stderr, dup2_stdin, dup2_stdout, stderr, stdin, stdout}; + +#[test] +fn dup2_stdin_stdin() { + let _ = dup2_stdin(stdin()); + fcntl_getfd(stdin()).unwrap(); +} + +#[test] +fn dup2_stdout_stdout() { + let _ = dup2_stdout(stdout()); + fcntl_getfd(stdout()).unwrap(); +} + +#[test] +fn dup2_stderr_stderr() { + let _ = dup2_stderr(stderr()); + fcntl_getfd(stderr()).unwrap(); +} diff --git a/tests/stdio/main.rs b/tests/stdio/main.rs index af294d86e..7e5736099 100644 --- a/tests/stdio/main.rs +++ b/tests/stdio/main.rs @@ -1,5 +1,11 @@ //! Tests for [`rustix::stdio`]. +#![cfg(feature = "stdio")] + +#[cfg(not(feature = "rustc-dep-of-std"))] +#[cfg(not(windows))] +#[cfg(not(target_os = "wasi"))] +mod dup2_stdio; #[cfg(not(feature = "rustc-dep-of-std"))] #[cfg(not(windows))] #[cfg(not(target_os = "wasi"))] diff --git a/tests/termios/isatty.rs b/tests/termios/isatty.rs index 605bf123c..cb9982dfe 100644 --- a/tests/termios/isatty.rs +++ b/tests/termios/isatty.rs @@ -11,10 +11,10 @@ fn tmpdir() -> TempDir { fn std_file_is_not_terminal() { let tmpdir = tempfile::tempdir().unwrap(); assert!(!isatty( - &std::fs::File::create(tmpdir.path().join("file")).unwrap() + std::fs::File::create(tmpdir.path().join("file")).unwrap() )); assert!(!isatty( - &std::fs::File::open(tmpdir.path().join("file")).unwrap() + std::fs::File::open(tmpdir.path().join("file")).unwrap() )); } diff --git a/tests/termios/pgrp.rs b/tests/termios/pgrp.rs index 6b9e2a798..d43198f62 100644 --- a/tests/termios/pgrp.rs +++ b/tests/termios/pgrp.rs @@ -19,8 +19,8 @@ fn pgrp_notty() { } // Disable on illumos where `tcgetattr` doesn't appear to support -// pseudoterminals. -#[cfg(not(target_os = "illumos"))] +// pseudoterminals. And on Redox which lacks `NOCTTY`. +#[cfg(not(any(target_os = "illumos", target_os = "redox")))] #[cfg(feature = "pty")] #[test] fn pgrp_pseudoterminal() { diff --git a/tests/termios/sid.rs b/tests/termios/sid.rs index 68de2e2c4..28bc94eb0 100644 --- a/tests/termios/sid.rs +++ b/tests/termios/sid.rs @@ -17,6 +17,8 @@ fn sid_notty() { assert_eq!(tcgetsid(&fd), Err(Errno::NOTTY)); } +// Disable on Redox which lacks `getsid`. +#[cfg(not(target_os = "redox"))] #[cfg(all(feature = "stdio", feature = "process"))] #[test] fn sid_match() { diff --git a/tests/thread/futex.rs b/tests/thread/futex.rs new file mode 100644 index 000000000..cd1df57e5 --- /dev/null +++ b/tests/thread/futex.rs @@ -0,0 +1,106 @@ +use core::sync::atomic::{AtomicU32, Ordering}; +use rustix::io::Errno; +use rustix::thread::futex; + +#[test] +fn test_lock_unlock_pi() { + let lock = AtomicU32::new(0); + futex::lock_pi(&lock, futex::Flags::empty(), None).unwrap(); + assert_ne!(lock.load(Ordering::SeqCst), 0); + + let err = futex::lock_pi(&lock, futex::Flags::empty(), None).unwrap_err(); + assert_eq!(err, Errno::DEADLK); + + futex::unlock_pi(&lock, futex::Flags::empty()).unwrap(); + assert_eq!(lock.load(Ordering::SeqCst), 0); + + let err = futex::unlock_pi(&lock, futex::Flags::empty()).unwrap_err(); + assert_eq!(err, Errno::PERM); +} + +#[cfg(feature = "std")] +#[test] +fn test_wait_wake() { + let lock = std::sync::Arc::new(AtomicU32::new(0)); + + match futex::wait(&lock, futex::Flags::empty(), 1, None) { + Ok(()) => panic!("Nobody should be waking us!"), + Err(Errno::AGAIN) => { + assert_eq!(lock.load(Ordering::SeqCst), 0, "the lock should still be 0") + } + Err(err) => panic!("{err}"), + } + + let other = std::thread::spawn({ + let lock = lock.clone(); + move || { + std::thread::sleep(std::time::Duration::from_millis(1)); + lock.store(1, Ordering::SeqCst); + futex::wake(&lock, futex::Flags::empty(), 1).unwrap(); + + std::thread::sleep(std::time::Duration::from_millis(50)); + match futex::wait(&lock, futex::Flags::empty(), 1, None) { + Ok(()) => (), + Err(Errno::AGAIN) => { + assert_eq!(lock.load(Ordering::SeqCst), 2, "the lock should now be 2") + } + Err(err) => panic!("{err}"), + } + } + }); + + match futex::wait(&lock, futex::Flags::empty(), 0, None) { + Ok(()) => (), + Err(Errno::AGAIN) => assert_eq!(lock.load(Ordering::SeqCst), 1, "the lock should now be 1"), + Err(err) => panic!("{err}"), + } + + lock.store(2, Ordering::SeqCst); + futex::wake(&lock, futex::Flags::empty(), 1).unwrap(); + + other.join().unwrap(); +} + +#[cfg(feature = "std")] +#[test] +fn test_timeout() { + use rustix::thread::futex::Timespec; + + let lock = AtomicU32::new(0); + + let err = futex::wait( + &lock, + futex::Flags::empty(), + 0, + Some(Timespec { + tv_sec: 1, + tv_nsec: 13, + }), + ) + .unwrap_err(); + assert_eq!(err, Errno::TIMEDOUT); + + let err = futex::wait( + &lock, + futex::Flags::empty(), + 0, + Some(Timespec { + tv_sec: 0, + tv_nsec: 1_000_000_000, + }), + ) + .unwrap_err(); + assert_eq!(err, Errno::INVAL); + + let err = futex::wait( + &lock, + futex::Flags::empty(), + 0, + Some(Timespec { + tv_sec: -1, + tv_nsec: 0, + }), + ) + .unwrap_err(); + assert_eq!(err, Errno::INVAL); +} diff --git a/tests/thread/main.rs b/tests/thread/main.rs index fccf2682c..bdd02dc6c 100644 --- a/tests/thread/main.rs +++ b/tests/thread/main.rs @@ -6,6 +6,8 @@ #[cfg(not(target_os = "redox"))] mod clocks; #[cfg(linux_kernel)] +mod futex; +#[cfg(linux_kernel)] mod id; #[cfg(linux_kernel)] mod libcap;