diff options
Diffstat (limited to 'vendor/rustix/src/backend/libc')
73 files changed, 15050 insertions, 0 deletions
diff --git a/vendor/rustix/src/backend/libc/c.rs b/vendor/rustix/src/backend/libc/c.rs new file mode 100644 index 0000000..d3a1e5f --- /dev/null +++ b/vendor/rustix/src/backend/libc/c.rs @@ -0,0 +1,468 @@ +//! Libc and supplemental types and constants. + +#![allow(unused_imports)] + +// Import everything from libc, but we'll add some stuff and override some +// things below. +pub(crate) use libc::*; + +/// `PROC_SUPER_MAGIC`—The magic number for the procfs filesystem. +#[cfg(all(linux_kernel, target_env = "musl"))] +pub(crate) const PROC_SUPER_MAGIC: u32 = 0x0000_9fa0; + +/// `NFS_SUPER_MAGIC`—The magic number for the NFS filesystem. +#[cfg(all(linux_kernel, target_env = "musl"))] +pub(crate) const NFS_SUPER_MAGIC: u32 = 0x0000_6969; + +#[cfg(feature = "process")] +#[cfg(not(any(target_os = "espidf", target_os = "wasi")))] +pub(crate) const EXIT_SIGNALED_SIGABRT: c_int = 128 + SIGABRT as c_int; + +// TODO: Upstream these. +#[cfg(all(linux_kernel, feature = "net"))] +pub(crate) const ETH_P_TSN: c_int = linux_raw_sys::if_ether::ETH_P_TSN as _; +#[cfg(all(linux_kernel, feature = "net"))] +pub(crate) const ETH_P_ERSPAN2: c_int = linux_raw_sys::if_ether::ETH_P_ERSPAN2 as _; +#[cfg(all(linux_kernel, feature = "net"))] +pub(crate) const ETH_P_ERSPAN: c_int = linux_raw_sys::if_ether::ETH_P_ERSPAN as _; +#[cfg(all(linux_kernel, feature = "net"))] +pub(crate) const ETH_P_PROFINET: c_int = linux_raw_sys::if_ether::ETH_P_PROFINET as _; +#[cfg(all(linux_kernel, feature = "net"))] +pub(crate) const ETH_P_REALTEK: c_int = linux_raw_sys::if_ether::ETH_P_REALTEK as _; +#[cfg(all(linux_kernel, feature = "net"))] +pub(crate) const ETH_P_ETHERCAT: c_int = linux_raw_sys::if_ether::ETH_P_ETHERCAT as _; +#[cfg(all(linux_kernel, feature = "net"))] +pub(crate) const ETH_P_PREAUTH: c_int = linux_raw_sys::if_ether::ETH_P_PREAUTH as _; +#[cfg(all(linux_kernel, feature = "net"))] +pub(crate) const ETH_P_LLDP: c_int = linux_raw_sys::if_ether::ETH_P_LLDP as _; +#[cfg(all(linux_kernel, feature = "net"))] +pub(crate) const ETH_P_MRP: c_int = linux_raw_sys::if_ether::ETH_P_MRP as _; +#[cfg(all(linux_kernel, feature = "net"))] +pub(crate) const ETH_P_NCSI: c_int = linux_raw_sys::if_ether::ETH_P_NCSI as _; +#[cfg(all(linux_kernel, feature = "net"))] +pub(crate) const ETH_P_CFM: c_int = linux_raw_sys::if_ether::ETH_P_CFM as _; +#[cfg(all(linux_kernel, feature = "net"))] +pub(crate) const ETH_P_IBOE: c_int = linux_raw_sys::if_ether::ETH_P_IBOE as _; +#[cfg(all(linux_kernel, feature = "net"))] +pub(crate) const ETH_P_HSR: c_int = linux_raw_sys::if_ether::ETH_P_HSR as _; +#[cfg(all(linux_kernel, feature = "net"))] +pub(crate) const ETH_P_NSH: c_int = linux_raw_sys::if_ether::ETH_P_NSH as _; +#[cfg(all(linux_kernel, feature = "net"))] +pub(crate) const ETH_P_DSA_8021Q: c_int = linux_raw_sys::if_ether::ETH_P_DSA_8021Q as _; +#[cfg(all(linux_kernel, feature = "net"))] +pub(crate) const ETH_P_DSA_A5PSW: c_int = linux_raw_sys::if_ether::ETH_P_DSA_A5PSW as _; +#[cfg(all(linux_kernel, feature = "net"))] +pub(crate) const ETH_P_IFE: c_int = linux_raw_sys::if_ether::ETH_P_IFE as _; +#[cfg(all(linux_kernel, feature = "net"))] +pub(crate) const ETH_P_CAN: c_int = linux_raw_sys::if_ether::ETH_P_CAN as _; +#[cfg(all(linux_kernel, feature = "net"))] +pub(crate) const ETH_P_CANXL: c_int = linux_raw_sys::if_ether::ETH_P_CANXL as _; +#[cfg(all(linux_kernel, feature = "net"))] +pub(crate) const ETH_P_XDSA: c_int = linux_raw_sys::if_ether::ETH_P_XDSA as _; +#[cfg(all(linux_kernel, feature = "net"))] +pub(crate) const ETH_P_MAP: c_int = linux_raw_sys::if_ether::ETH_P_MAP as _; +#[cfg(all(linux_kernel, feature = "net"))] +pub(crate) const ETH_P_MCTP: c_int = linux_raw_sys::if_ether::ETH_P_MCTP as _; + +#[cfg(all( + linux_kernel, + any( + target_arch = "mips", + target_arch = "mips32r6", + target_arch = "mips64", + target_arch = "mips64r6", + target_arch = "sparc", + target_arch = "sparc64" + ) +))] +pub(crate) const SIGEMT: c_int = linux_raw_sys::general::SIGEMT as _; + +// TODO: Upstream these. +#[cfg(all(linux_kernel, feature = "termios"))] +pub(crate) const IUCLC: tcflag_t = linux_raw_sys::general::IUCLC as _; +#[cfg(all(linux_kernel, feature = "termios"))] +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; + +// 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 +// have this. +#[cfg(all( + linux_kernel, + feature = "termios", + any(target_arch = "powerpc", target_arch = "powerpc64") +))] +pub(crate) use { + linux_raw_sys::general::{termios2, CIBAUD}, + linux_raw_sys::ioctl::{TCGETS2, TCSETS2, TCSETSF2, TCSETSW2}, +}; + +// Automatically enable “large file” support (LFS) features. + +#[cfg(target_os = "vxworks")] +pub(super) use libc::_Vx_ticks64_t as _Vx_ticks_t; +#[cfg(linux_kernel)] +pub(super) use libc::fallocate64 as fallocate; +#[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))] +#[cfg(any(linux_like, target_os = "aix"))] +pub(super) use libc::open64 as open; +#[cfg(any( + linux_kernel, + target_os = "aix", + target_os = "hurd", + target_os = "l4re" +))] +pub(super) use libc::posix_fallocate64 as posix_fallocate; +#[cfg(any(all(linux_like, not(target_os = "android")), target_os = "aix"))] +pub(super) use libc::{blkcnt64_t as blkcnt_t, rlim64_t as rlim_t}; +// TODO: AIX has `stat64x`, `fstat64x`, `lstat64x`, and `stat64xat`; add them +// to the upstream libc crate and implement rustix's `statat` etc. with them. +#[cfg(target_os = "aix")] +pub(super) use libc::{ + blksize64_t as blksize_t, fstat64 as fstat, fstatfs64 as fstatfs, fstatvfs64 as fstatvfs, + ftruncate64 as ftruncate, getrlimit64 as getrlimit, ino_t, lseek64 as lseek, mmap, + off64_t as off_t, openat, posix_fadvise64 as posix_fadvise, preadv, pwritev, + rlimit64 as rlimit, setrlimit64 as setrlimit, stat64at as fstatat, statfs64 as statfs, + statvfs64 as statvfs, RLIM_INFINITY, +}; +#[cfg(any(linux_like, target_os = "hurd"))] +pub(super) use libc::{ + fstat64 as fstat, fstatat64 as fstatat, fstatfs64 as fstatfs, fstatvfs64 as fstatvfs, + ftruncate64 as ftruncate, getrlimit64 as getrlimit, ino64_t as ino_t, lseek64 as lseek, + mmap64 as mmap, off64_t as off_t, openat64 as openat, posix_fadvise64 as posix_fadvise, + rlimit64 as rlimit, setrlimit64 as setrlimit, statfs64 as statfs, statvfs64 as statvfs, + RLIM64_INFINITY as RLIM_INFINITY, +}; +#[cfg(apple)] +pub(super) use libc::{ + host_info64_t as host_info_t, host_statistics64 as host_statistics, + vm_statistics64_t as vm_statistics_t, +}; +#[cfg(not(all( + linux_kernel, + any( + target_pointer_width = "32", + target_arch = "mips64", + target_arch = "mips64r6" + ) +)))] +#[cfg(any(linux_like, target_os = "aix", target_os = "hurd"))] +pub(super) use libc::{lstat64 as lstat, stat64 as stat}; +#[cfg(any( + linux_kernel, + target_os = "aix", + target_os = "hurd", + target_os = "emscripten" +))] +pub(super) use libc::{pread64 as pread, pwrite64 as pwrite}; +#[cfg(any(target_os = "linux", target_os = "hurd", target_os = "emscripten"))] +pub(super) use libc::{preadv64 as preadv, pwritev64 as pwritev}; + +#[cfg(all(target_os = "linux", target_env = "gnu"))] +pub(super) unsafe fn prlimit( + pid: libc::pid_t, + resource: libc::__rlimit_resource_t, + new_limit: *const libc::rlimit64, + old_limit: *mut libc::rlimit64, +) -> libc::c_int { + // `prlimit64` wasn't supported in glibc until 2.13. + weak_or_syscall! { + fn prlimit64( + pid: libc::pid_t, + resource: libc::__rlimit_resource_t, + new_limit: *const libc::rlimit64, + old_limit: *mut libc::rlimit64 + ) via SYS_prlimit64 -> libc::c_int + } + + prlimit64(pid, resource, new_limit, old_limit) +} + +#[cfg(all(target_os = "linux", target_env = "musl"))] +pub(super) unsafe fn prlimit( + pid: libc::pid_t, + resource: libc::c_int, + new_limit: *const libc::rlimit64, + old_limit: *mut libc::rlimit64, +) -> libc::c_int { + weak_or_syscall! { + fn prlimit64( + pid: libc::pid_t, + resource: libc::c_int, + new_limit: *const libc::rlimit64, + old_limit: *mut libc::rlimit64 + ) via SYS_prlimit64 -> libc::c_int + } + + prlimit64(pid, resource, new_limit, old_limit) +} + +#[cfg(target_os = "android")] +pub(super) unsafe fn prlimit( + pid: libc::pid_t, + resource: libc::c_int, + new_limit: *const libc::rlimit64, + old_limit: *mut libc::rlimit64, +) -> libc::c_int { + weak_or_syscall! { + fn prlimit64( + pid: libc::pid_t, + resource: libc::c_int, + new_limit: *const libc::rlimit64, + old_limit: *mut libc::rlimit64 + ) via SYS_prlimit64 -> libc::c_int + } + + prlimit64(pid, resource, new_limit, old_limit) +} + +#[cfg(target_os = "android")] +mod readwrite_pv64 { + use super::*; + + pub(in super::super) unsafe fn preadv64( + fd: libc::c_int, + iov: *const libc::iovec, + iovcnt: libc::c_int, + offset: libc::off64_t, + ) -> libc::ssize_t { + // Older Android libc lacks `preadv64`, so use the `weak!` mechanism to + // test for it, and call back to `libc::syscall`. We don't use + // `weak_or_syscall` here because we need to pass the 64-bit offset + // specially. + weak! { + fn preadv64(libc::c_int, *const libc::iovec, libc::c_int, libc::off64_t) -> libc::ssize_t + } + if let Some(fun) = preadv64.get() { + fun(fd, iov, iovcnt, offset) + } else { + // Unlike the plain "p" functions, the "pv" functions pass their + // offset in an endian-independent way, and always in two registers. + syscall! { + fn preadv( + fd: libc::c_int, + iov: *const libc::iovec, + iovcnt: libc::c_int, + offset_lo: usize, + offset_hi: usize + ) via SYS_preadv -> libc::ssize_t + } + preadv(fd, iov, iovcnt, offset as usize, (offset >> 32) as usize) + } + } + pub(in super::super) unsafe fn pwritev64( + fd: libc::c_int, + iov: *const libc::iovec, + iovcnt: libc::c_int, + offset: libc::off64_t, + ) -> libc::ssize_t { + // See the comments in `preadv64`. + weak! { + fn pwritev64(libc::c_int, *const libc::iovec, libc::c_int, libc::off64_t) -> libc::ssize_t + } + if let Some(fun) = pwritev64.get() { + fun(fd, iov, iovcnt, offset) + } else { + // Unlike the plain "p" functions, the "pv" functions pass their + // offset in an endian-independent way, and always in two registers. + syscall! { + fn pwritev( + fd: libc::c_int, + iov: *const libc::iovec, + iovcnt: libc::c_int, + offset_lo: usize, + offset_hi: usize + ) via SYS_pwritev -> libc::ssize_t + } + pwritev(fd, iov, iovcnt, offset as usize, (offset >> 32) as usize) + } + } +} +#[cfg(target_os = "android")] +pub(super) use readwrite_pv64::{preadv64 as preadv, pwritev64 as pwritev}; + +// macOS added `preadv` and `pwritev` in version 11.0. +#[cfg(apple)] +mod readwrite_pv { + weakcall! { + pub(in super::super) fn preadv( + fd: libc::c_int, + iov: *const libc::iovec, + iovcnt: libc::c_int, + offset: libc::off_t + ) -> libc::ssize_t + } + weakcall! { + pub(in super::super) fn pwritev( + fd: libc::c_int, + iov: *const libc::iovec, + iovcnt: libc::c_int, offset: libc::off_t + ) -> libc::ssize_t + } +} +#[cfg(apple)] +pub(super) use readwrite_pv::{preadv, pwritev}; + +// glibc added `preadv64v2` and `pwritev64v2` in version 2.26. +#[cfg(all(target_os = "linux", target_env = "gnu"))] +mod readwrite_pv64v2 { + use super::*; + + pub(in super::super) unsafe fn preadv64v2( + fd: libc::c_int, + iov: *const libc::iovec, + iovcnt: libc::c_int, + offset: libc::off64_t, + flags: libc::c_int, + ) -> libc::ssize_t { + // Older glibc lacks `preadv64v2`, so use the `weak!` mechanism to + // test for it, and call back to `libc::syscall`. We don't use + // `weak_or_syscall` here because we need to pass the 64-bit offset + // specially. + weak! { + fn preadv64v2(libc::c_int, *const libc::iovec, libc::c_int, libc::off64_t, libc::c_int) -> libc::ssize_t + } + if let Some(fun) = preadv64v2.get() { + fun(fd, iov, iovcnt, offset, flags) + } else { + // Unlike the plain "p" functions, the "pv" functions pass their + // offset in an endian-independent way, and always in two registers. + syscall! { + fn preadv2( + fd: libc::c_int, + iov: *const libc::iovec, + iovcnt: libc::c_int, + offset_lo: usize, + offset_hi: usize, + flags: libc::c_int + ) via SYS_preadv2 -> libc::ssize_t + } + preadv2( + fd, + iov, + iovcnt, + offset as usize, + (offset >> 32) as usize, + flags, + ) + } + } + pub(in super::super) unsafe fn pwritev64v2( + fd: libc::c_int, + iov: *const libc::iovec, + iovcnt: libc::c_int, + offset: libc::off64_t, + flags: libc::c_int, + ) -> libc::ssize_t { + // See the comments in `preadv64v2`. + weak! { + fn pwritev64v2(libc::c_int, *const libc::iovec, libc::c_int, libc::off64_t, libc::c_int) -> libc::ssize_t + } + if let Some(fun) = pwritev64v2.get() { + fun(fd, iov, iovcnt, offset, flags) + } else { + // Unlike the plain "p" functions, the "pv" functions pass their + // offset in an endian-independent way, and always in two registers. + syscall! { + fn pwritev2( + fd: libc::c_int, + iov: *const libc::iovec, + iovec: libc::c_int, + offset_lo: usize, + offset_hi: usize, + flags: libc::c_int + ) via SYS_pwritev2 -> libc::ssize_t + } + pwritev2( + fd, + iov, + iovcnt, + offset as usize, + (offset >> 32) as usize, + flags, + ) + } + } +} +#[cfg(all(target_os = "linux", target_env = "gnu"))] +pub(super) use readwrite_pv64v2::{preadv64v2 as preadv2, pwritev64v2 as pwritev2}; + +// On non-glibc, assume we don't have `pwritev2`/`preadv2` in libc and use +// `c::syscall` instead. +#[cfg(any( + target_os = "android", + all(target_os = "linux", not(target_env = "gnu")), +))] +mod readwrite_pv64v2 { + use super::*; + + pub(in super::super) unsafe fn preadv64v2( + fd: libc::c_int, + iov: *const libc::iovec, + iovcnt: libc::c_int, + offset: libc::off64_t, + flags: libc::c_int, + ) -> libc::ssize_t { + // Unlike the plain "p" functions, the "pv" functions pass their offset + // in an endian-independent way, and always in two registers. + syscall! { + fn preadv2( + fd: libc::c_int, + iov: *const libc::iovec, + iovcnt: libc::c_int, + offset_lo: usize, + offset_hi: usize, + flags: libc::c_int + ) via SYS_preadv2 -> libc::ssize_t + } + preadv2( + fd, + iov, + iovcnt, + offset as usize, + (offset >> 32) as usize, + flags, + ) + } + pub(in super::super) unsafe fn pwritev64v2( + fd: libc::c_int, + iov: *const libc::iovec, + iovcnt: libc::c_int, + offset: libc::off64_t, + flags: libc::c_int, + ) -> libc::ssize_t { + // Unlike the plain "p" functions, the "pv" functions pass their offset + // in an endian-independent way, and always in two registers. + syscall! { + fn pwritev2( + fd: libc::c_int, + iov: *const libc::iovec, + iovcnt: libc::c_int, + offset_lo: usize, + offset_hi: usize, + flags: libc::c_int + ) via SYS_pwritev2 -> libc::ssize_t + } + pwritev2( + fd, + iov, + iovcnt, + offset as usize, + (offset >> 32) as usize, + flags, + ) + } +} +#[cfg(any( + target_os = "android", + all(target_os = "linux", not(target_env = "gnu")), +))] +pub(super) use readwrite_pv64v2::{preadv64v2 as preadv2, pwritev64v2 as pwritev2}; diff --git a/vendor/rustix/src/backend/libc/conv.rs b/vendor/rustix/src/backend/libc/conv.rs new file mode 100644 index 0000000..2539510 --- /dev/null +++ b/vendor/rustix/src/backend/libc/conv.rs @@ -0,0 +1,247 @@ +//! Libc call arguments and return values are often things like `c_int`, +//! `c_uint`, or libc-specific pointer types. This module provides functions +//! for converting between rustix's types and libc types. + +use super::c; +#[cfg(all(feature = "alloc", not(any(windows, target_os = "espidf"))))] +use super::fd::IntoRawFd; +use super::fd::{AsRawFd, BorrowedFd, FromRawFd, LibcFd, OwnedFd, RawFd}; +#[cfg(not(windows))] +use crate::ffi::CStr; +use crate::io; + +#[cfg(not(windows))] +#[inline] +pub(super) fn c_str(c: &CStr) -> *const c::c_char { + c.as_ptr() +} + +#[cfg(not(any(windows, target_os = "espidf", target_os = "vita", target_os = "wasi")))] +#[inline] +pub(super) fn no_fd() -> LibcFd { + -1 +} + +#[inline] +pub(super) fn borrowed_fd(fd: BorrowedFd<'_>) -> LibcFd { + fd.as_raw_fd() as LibcFd +} + +#[cfg(all( + feature = "alloc", + not(any(windows, target_os = "espidf", target_os = "redox")) +))] +#[inline] +pub(super) fn owned_fd(fd: OwnedFd) -> LibcFd { + fd.into_raw_fd() as LibcFd +} + +#[inline] +pub(super) fn ret(raw: c::c_int) -> io::Result<()> { + if raw == 0 { + Ok(()) + } else { + Err(io::Errno::last_os_error()) + } +} + +#[cfg(apple)] +#[inline] +pub(super) fn nonnegative_ret(raw: c::c_int) -> io::Result<()> { + if raw >= 0 { + Ok(()) + } else { + Err(io::Errno::last_os_error()) + } +} + +#[cfg(not(any(windows, target_os = "wasi")))] +#[inline] +pub(super) unsafe fn ret_infallible(raw: c::c_int) { + debug_assert_eq!(raw, 0, "unexpected error: {:?}", io::Errno::last_os_error()); +} + +#[inline] +pub(super) fn ret_c_int(raw: c::c_int) -> io::Result<c::c_int> { + if raw == -1 { + Err(io::Errno::last_os_error()) + } else { + Ok(raw) + } +} + +#[cfg(linux_kernel)] +#[inline] +pub(super) fn ret_u32(raw: c::c_int) -> io::Result<u32> { + if raw == -1 { + Err(io::Errno::last_os_error()) + } else { + Ok(raw as u32) + } +} + +#[inline] +pub(super) fn ret_usize(raw: c::ssize_t) -> io::Result<usize> { + if raw == -1 { + Err(io::Errno::last_os_error()) + } else { + debug_assert!(raw >= 0); + Ok(raw as usize) + } +} + +#[cfg(not(windows))] +#[cfg(feature = "fs")] +#[inline] +pub(super) fn ret_off_t(raw: c::off_t) -> io::Result<c::off_t> { + if raw == -1 { + Err(io::Errno::last_os_error()) + } else { + Ok(raw) + } +} + +#[cfg(not(any(windows, target_os = "wasi")))] +#[inline] +pub(super) fn ret_pid_t(raw: c::pid_t) -> io::Result<c::pid_t> { + if raw == -1 { + Err(io::Errno::last_os_error()) + } else { + Ok(raw) + } +} + +/// Convert a `c_int` returned from a libc function to an `OwnedFd`, if valid. +/// +/// # Safety +/// +/// The caller must ensure that this is the return value of a libc function +/// which returns an owned file descriptor. +#[inline] +pub(super) unsafe fn ret_owned_fd(raw: LibcFd) -> io::Result<OwnedFd> { + if raw == !0 { + Err(io::Errno::last_os_error()) + } else { + Ok(OwnedFd::from_raw_fd(raw as RawFd)) + } +} + +#[cfg(not(any(windows, target_os = "wasi")))] +#[inline] +pub(super) fn ret_discarded_fd(raw: LibcFd) -> io::Result<()> { + if raw == !0 { + Err(io::Errno::last_os_error()) + } else { + Ok(()) + } +} + +#[cfg(all(feature = "alloc", not(any(windows, target_os = "wasi"))))] +#[inline] +pub(super) fn ret_discarded_char_ptr(raw: *mut c::c_char) -> io::Result<()> { + if raw.is_null() { + Err(io::Errno::last_os_error()) + } else { + Ok(()) + } +} + +/// Convert the buffer-length argument value of a `send` or `recv` call. +#[cfg(not(any(windows, target_os = "redox", target_os = "wasi")))] +#[inline] +pub(super) fn send_recv_len(len: usize) -> usize { + len +} + +/// Convert the buffer-length argument value of a `send` or `recv` call. +#[cfg(windows)] +#[inline] +pub(super) fn send_recv_len(len: usize) -> i32 { + // On Windows, the length argument has type `i32`; saturate the length, + // since `send` and `recv` are allowed to send and recv less data than + // requested. + len.try_into().unwrap_or(i32::MAX) +} + +/// Convert the return value of a `send` or `recv` call. +#[cfg(not(any(windows, target_os = "redox", target_os = "wasi")))] +#[inline] +pub(super) fn ret_send_recv(len: isize) -> io::Result<usize> { + ret_usize(len) +} + +/// Convert the return value of a `send` or `recv` call. +#[cfg(windows)] +#[inline] +pub(super) fn ret_send_recv(len: i32) -> io::Result<usize> { + ret_usize(len as isize) +} + +/// Convert the value to the `msg_iovlen` field of a `msghdr` struct. +#[cfg(all( + not(any(windows, target_os = "espidf", target_os = "redox", target_os = "wasi")), + any( + target_os = "android", + all(target_os = "linux", not(target_env = "musl")) + ) +))] +#[inline] +pub(super) fn msg_iov_len(len: usize) -> c::size_t { + len +} + +/// Convert the value to the `msg_iovlen` field of a `msghdr` struct. +#[cfg(all( + not(any( + windows, + target_os = "espidf", + target_os = "redox", + target_os = "vita", + target_os = "wasi" + )), + not(any( + target_os = "android", + all(target_os = "linux", not(target_env = "musl")) + )) +))] +#[inline] +pub(crate) fn msg_iov_len(len: usize) -> c::c_int { + len.try_into().unwrap_or(c::c_int::MAX) +} + +/// Convert the value to a `socklen_t`. +#[cfg(any( + bsd, + solarish, + target_env = "musl", + target_os = "aix", + target_os = "emscripten", + target_os = "fuchsia", + target_os = "haiku", + target_os = "nto", +))] +#[inline] +pub(crate) fn msg_control_len(len: usize) -> c::socklen_t { + len.try_into().unwrap_or(c::socklen_t::MAX) +} + +/// Convert the value to a `size_t`. +#[cfg(not(any( + bsd, + solarish, + windows, + target_env = "musl", + target_os = "aix", + target_os = "emscripten", + target_os = "espidf", + target_os = "fuchsia", + target_os = "haiku", + target_os = "nto", + target_os = "redox", + target_os = "vita", + target_os = "wasi", +)))] +#[inline] +pub(crate) fn msg_control_len(len: usize) -> c::size_t { + len +} diff --git a/vendor/rustix/src/backend/libc/event/epoll.rs b/vendor/rustix/src/backend/libc/event/epoll.rs new file mode 100644 index 0000000..ced3be1 --- /dev/null +++ b/vendor/rustix/src/backend/libc/event/epoll.rs @@ -0,0 +1,496 @@ +//! 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`]. + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct CreateFlags: u32 { + /// `EPOLL_CLOEXEC` + const CLOEXEC = bitcast!(c::EPOLL_CLOEXEC); + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} + +bitflags! { + /// `EPOLL*` for use with [`add`]. + #[repr(transparent)] + #[derive(Default, Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct EventFlags: u32 { + /// `EPOLLIN` + const IN = bitcast!(c::EPOLLIN); + + /// `EPOLLOUT` + const OUT = bitcast!(c::EPOLLOUT); + + /// `EPOLLPRI` + const PRI = bitcast!(c::EPOLLPRI); + + /// `EPOLLERR` + const ERR = bitcast!(c::EPOLLERR); + + /// `EPOLLHUP` + const HUP = bitcast!(c::EPOLLHUP); + + /// `EPOLLRDNORM` + const RDNORM = bitcast!(c::EPOLLRDNORM); + + /// `EPOLLRDBAND` + const RDBAND = bitcast!(c::EPOLLRDBAND); + + /// `EPOLLWRNORM` + const WRNORM = bitcast!(c::EPOLLWRNORM); + + /// `EPOLLWRBAND` + const WRBAND = bitcast!(c::EPOLLWRBAND); + + /// `EPOLLMSG` + const MSG = bitcast!(c::EPOLLMSG); + + /// `EPOLLRDHUP` + const RDHUP = bitcast!(c::EPOLLRDHUP); + + /// `EPOLLET` + const ET = bitcast!(c::EPOLLET); + + /// `EPOLLONESHOT` + const ONESHOT = bitcast!(c::EPOLLONESHOT); + + /// `EPOLLWAKEUP` + const WAKEUP = bitcast!(c::EPOLLWAKEUP); + + /// `EPOLLEXCLUSIVE` + #[cfg(not(target_os = "android"))] + const EXCLUSIVE = bitcast!(c::EPOLLEXCLUSIVE); + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + 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<OwnedFd> { + // 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<dyn SystemResource>`, then `epoll` can be thought to maintain a +/// `Weak<dyn SystemResource>` 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 elsehwere 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, + }) + .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 elsehwere 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, + }) + .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::<c::epoll_event>(), + 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<slice::Iter<'a, Event>>, +} + +impl<'a> Iterator for Iter<'a> { + type Item = Event; + + #[inline] + fn next(&mut self) -> Option<Self::Item> { + self.iter.next() + } +} + +/// A record of an event that occurred. +#[repr(C)] +#[cfg_attr( + 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, +} + +/// 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<H: Hasher>(&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<Event>, +} + +#[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/vendor/rustix/src/backend/libc/event/mod.rs b/vendor/rustix/src/backend/libc/event/mod.rs new file mode 100644 index 0000000..6aed461 --- /dev/null +++ b/vendor/rustix/src/backend/libc/event/mod.rs @@ -0,0 +1,9 @@ +pub(crate) mod poll_fd; +#[cfg(not(windows))] +pub(crate) mod types; + +#[cfg_attr(windows, path = "windows_syscalls.rs")] +pub(crate) mod syscalls; + +#[cfg(linux_kernel)] +pub mod epoll; diff --git a/vendor/rustix/src/backend/libc/event/poll_fd.rs b/vendor/rustix/src/backend/libc/event/poll_fd.rs new file mode 100644 index 0000000..32fd83d --- /dev/null +++ b/vendor/rustix/src/backend/libc/event/poll_fd.rs @@ -0,0 +1,142 @@ +use crate::backend::c; +use crate::backend::conv::borrowed_fd; +use crate::backend::fd::{AsFd, AsRawFd, BorrowedFd, LibcFd}; +use bitflags::bitflags; +use core::marker::PhantomData; +#[cfg(windows)] +use { + crate::backend::fd::{AsSocket, RawFd}, + core::fmt, +}; + +bitflags! { + /// `POLL*` flags for use with [`poll`]. + /// + /// [`poll`]: crate::event::poll + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct PollFlags: c::c_short { + /// `POLLIN` + const IN = c::POLLIN; + /// `POLLPRI` + #[cfg(not(target_os = "wasi"))] + const PRI = c::POLLPRI; + /// `POLLOUT` + const OUT = c::POLLOUT; + /// `POLLRDNORM` + const RDNORM = c::POLLRDNORM; + /// `POLLWRNORM` + #[cfg(not(target_os = "l4re"))] + const WRNORM = c::POLLWRNORM; + /// `POLLRDBAND` + #[cfg(not(any(target_os = "l4re", target_os = "wasi")))] + const RDBAND = c::POLLRDBAND; + /// `POLLWRBAND` + #[cfg(not(any(target_os = "l4re", target_os = "wasi")))] + const WRBAND = c::POLLWRBAND; + /// `POLLERR` + const ERR = c::POLLERR; + /// `POLLHUP` + const HUP = c::POLLHUP; + /// `POLLNVAL` + #[cfg(not(target_os = "espidf"))] + const NVAL = c::POLLNVAL; + /// `POLLRDHUP` + #[cfg(all( + linux_kernel, + not(any(target_arch = "sparc", target_arch = "sparc64"))), + )] + const RDHUP = c::POLLRDHUP; + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} + +/// `struct pollfd`—File descriptor and flags for use with [`poll`]. +/// +/// [`poll`]: crate::event::poll +#[doc(alias = "pollfd")] +#[derive(Clone)] +#[cfg_attr(not(windows), derive(Debug))] +#[repr(transparent)] +pub struct PollFd<'fd> { + pollfd: c::pollfd, + _phantom: PhantomData<BorrowedFd<'fd>>, +} + +#[cfg(windows)] +impl<'fd> fmt::Debug for PollFd<'fd> { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.debug_struct("pollfd") + .field("fd", &self.pollfd.fd) + .field("events", &self.pollfd.events) + .field("revents", &self.pollfd.revents) + .finish() + } +} + +impl<'fd> PollFd<'fd> { + /// Constructs a new `PollFd` holding `fd` and `events`. + #[inline] + pub fn new<Fd: AsFd>(fd: &'fd Fd, events: PollFlags) -> Self { + Self::from_borrowed_fd(fd.as_fd(), events) + } + + /// Sets the contained file descriptor to `fd`. + #[inline] + pub fn set_fd<Fd: AsFd>(&mut self, fd: &'fd Fd) { + self.pollfd.fd = fd.as_fd().as_raw_fd() as LibcFd; + } + + /// Clears the ready events. + #[inline] + pub fn clear_revents(&mut self) { + self.pollfd.revents = 0; + } + + /// Constructs a new `PollFd` holding `fd` and `events`. + /// + /// This is the same as `new`, but can be used to avoid borrowing the + /// `BorrowedFd`, which can be tricky in situations where the `BorrowedFd` + /// is a temporary. + #[inline] + pub fn from_borrowed_fd(fd: BorrowedFd<'fd>, events: PollFlags) -> Self { + Self { + pollfd: c::pollfd { + fd: borrowed_fd(fd), + events: events.bits(), + revents: 0, + }, + _phantom: PhantomData, + } + } + + /// Returns the ready events. + #[inline] + pub fn revents(&self) -> PollFlags { + // Use `.unwrap()` here because in theory we know we know all the bits + // the OS might set here, but OS's have added extensions in the past. + PollFlags::from_bits(self.pollfd.revents).unwrap() + } +} + +#[cfg(not(windows))] +impl<'fd> AsFd for PollFd<'fd> { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + // SAFETY: Our constructors and `set_fd` require `pollfd.fd` to be + // valid for the `'fd` lifetime. + unsafe { BorrowedFd::borrow_raw(self.pollfd.fd) } + } +} + +#[cfg(windows)] +impl<'fd> AsSocket for PollFd<'fd> { + #[inline] + fn as_socket(&self) -> BorrowedFd<'_> { + // SAFETY: Our constructors and `set_fd` require `pollfd.fd` to be + // valid for the `'fd` lifetime. + unsafe { BorrowedFd::borrow_raw(self.pollfd.fd as RawFd) } + } +} diff --git a/vendor/rustix/src/backend/libc/event/syscalls.rs b/vendor/rustix/src/backend/libc/event/syscalls.rs new file mode 100644 index 0000000..725ec82 --- /dev/null +++ b/vendor/rustix/src/backend/libc/event/syscalls.rs @@ -0,0 +1,191 @@ +//! 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(solarish)] +use { + crate::backend::conv::ret, crate::event::port::Event, crate::utils::as_mut_ptr, + core::ptr::null_mut, +}; +#[cfg(any( + linux_kernel, + target_os = "freebsd", + target_os = "illumos", + target_os = "espidf" +))] +use {crate::backend::conv::ret_owned_fd, crate::event::EventfdFlags}; +#[cfg(all(feature = "alloc", bsd))] +use {crate::event::kqueue::Event, crate::utils::as_ptr, core::ptr::null}; + +#[cfg(any( + linux_kernel, + target_os = "freebsd", + target_os = "illumos", + target_os = "espidf" +))] +pub(crate) fn eventfd(initval: u32, flags: EventfdFlags) -> io::Result<OwnedFd> { + #[cfg(linux_kernel)] + unsafe { + syscall! { + fn eventfd2( + initval: c::c_uint, + flags: c::c_int + ) via SYS_eventfd2 -> c::c_int + } + ret_owned_fd(eventfd2(initval, bitflags_bits!(flags))) + } + + // `eventfd` was added in FreeBSD 13, so it isn't available on FreeBSD 12. + #[cfg(target_os = "freebsd")] + unsafe { + weakcall! { + fn eventfd( + initval: c::c_uint, + flags: c::c_int + ) -> c::c_int + } + ret_owned_fd(eventfd(initval, bitflags_bits!(flags))) + } + + #[cfg(any(target_os = "illumos", target_os = "espidf"))] + unsafe { + ret_owned_fd(c::eventfd(initval, bitflags_bits!(flags))) + } +} + +#[cfg(all(feature = "alloc", bsd))] +pub(crate) fn kqueue() -> io::Result<OwnedFd> { + unsafe { ret_owned_fd(c::kqueue()) } +} + +#[cfg(all(feature = "alloc", bsd))] +pub(crate) unsafe fn kevent( + kq: BorrowedFd<'_>, + changelist: &[Event], + eventlist: &mut [MaybeUninit<Event>], + timeout: Option<&c::timespec>, +) -> io::Result<c::c_int> { + ret_c_int(c::kevent( + borrowed_fd(kq), + changelist.as_ptr().cast(), + changelist + .len() + .try_into() + .map_err(|_| io::Errno::OVERFLOW)?, + eventlist.as_mut_ptr().cast(), + eventlist + .len() + .try_into() + .map_err(|_| io::Errno::OVERFLOW)?, + timeout.map_or(null(), as_ptr), + )) +} + +#[inline] +pub(crate) fn poll(fds: &mut [PollFd<'_>], timeout: c::c_int) -> io::Result<usize> { + let nfds = fds + .len() + .try_into() + .map_err(|_convert_err| io::Errno::INVAL)?; + + ret_c_int(unsafe { c::poll(fds.as_mut_ptr().cast(), nfds, timeout) }) + .map(|nready| nready as usize) +} + +#[cfg(solarish)] +pub(crate) fn port_create() -> io::Result<OwnedFd> { + unsafe { ret_owned_fd(c::port_create()) } +} + +#[cfg(solarish)] +pub(crate) unsafe fn port_associate( + port: BorrowedFd<'_>, + source: c::c_int, + object: c::uintptr_t, + events: c::c_int, + user: *mut c::c_void, +) -> io::Result<()> { + ret(c::port_associate( + borrowed_fd(port), + source, + object, + events, + user, + )) +} + +#[cfg(solarish)] +pub(crate) unsafe fn port_dissociate( + port: BorrowedFd<'_>, + source: c::c_int, + object: c::uintptr_t, +) -> io::Result<()> { + ret(c::port_dissociate(borrowed_fd(port), source, object)) +} + +#[cfg(solarish)] +pub(crate) fn port_get( + port: BorrowedFd<'_>, + timeout: Option<&mut c::timespec>, +) -> io::Result<Event> { + let mut event = MaybeUninit::<c::port_event>::uninit(); + let timeout = timeout.map_or(null_mut(), as_mut_ptr); + + unsafe { + ret(c::port_get(borrowed_fd(port), event.as_mut_ptr(), timeout))?; + } + + // If we're done, initialize the event and return it. + Ok(Event(unsafe { event.assume_init() })) +} + +#[cfg(all(feature = "alloc", solarish))] +pub(crate) fn port_getn( + port: BorrowedFd<'_>, + timeout: Option<&mut c::timespec>, + events: &mut Vec<Event>, + mut nget: u32, +) -> io::Result<()> { + let timeout = timeout.map_or(null_mut(), as_mut_ptr); + unsafe { + ret(c::port_getn( + borrowed_fd(port), + events.as_mut_ptr().cast(), + events.len().try_into().unwrap(), + &mut nget, + timeout, + ))?; + } + + // Update the vector length. + unsafe { + events.set_len(nget.try_into().unwrap()); + } + + Ok(()) +} + +#[cfg(solarish)] +pub(crate) fn port_send( + port: BorrowedFd<'_>, + events: c::c_int, + userdata: *mut c::c_void, +) -> io::Result<()> { + unsafe { ret(c::port_send(borrowed_fd(port), events, userdata)) } +} + +#[cfg(not(any(windows, target_os = "redox", target_os = "wasi")))] +pub(crate) fn pause() { + let r = unsafe { libc::pause() }; + let errno = libc_errno::errno().0; + debug_assert_eq!(r, -1); + debug_assert_eq!(errno, libc::EINTR); +} diff --git a/vendor/rustix/src/backend/libc/event/types.rs b/vendor/rustix/src/backend/libc/event/types.rs new file mode 100644 index 0000000..a04d7e6 --- /dev/null +++ b/vendor/rustix/src/backend/libc/event/types.rs @@ -0,0 +1,37 @@ +#[cfg(any(linux_kernel, target_os = "freebsd", target_os = "illumos"))] +use crate::backend::c; +#[cfg(any( + linux_kernel, + target_os = "freebsd", + target_os = "illumos", + target_os = "espidf" +))] +use bitflags::bitflags; + +#[cfg(any( + linux_kernel, + target_os = "freebsd", + target_os = "illumos", + target_os = "espidf" +))] +bitflags! { + /// `EFD_*` flags for use with [`eventfd`]. + /// + /// [`eventfd`]: crate::event::eventfd + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct EventfdFlags: u32 { + /// `EFD_CLOEXEC` + #[cfg(not(target_os = "espidf"))] + const CLOEXEC = bitcast!(c::EFD_CLOEXEC); + /// `EFD_NONBLOCK` + #[cfg(not(target_os = "espidf"))] + const NONBLOCK = bitcast!(c::EFD_NONBLOCK); + /// `EFD_SEMAPHORE` + #[cfg(not(target_os = "espidf"))] + const SEMAPHORE = bitcast!(c::EFD_SEMAPHORE); + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} diff --git a/vendor/rustix/src/backend/libc/event/windows_syscalls.rs b/vendor/rustix/src/backend/libc/event/windows_syscalls.rs new file mode 100644 index 0000000..8ccad47 --- /dev/null +++ b/vendor/rustix/src/backend/libc/event/windows_syscalls.rs @@ -0,0 +1,16 @@ +//! Windows system calls in the `event` module. + +use crate::backend::c; +use crate::backend::conv::ret_c_int; +use crate::event::PollFd; +use crate::io; + +pub(crate) fn poll(fds: &mut [PollFd<'_>], timeout: c::c_int) -> io::Result<usize> { + let nfds = fds + .len() + .try_into() + .map_err(|_convert_err| io::Errno::INVAL)?; + + ret_c_int(unsafe { c::poll(fds.as_mut_ptr().cast(), nfds, timeout) }) + .map(|nready| nready as usize) +} diff --git a/vendor/rustix/src/backend/libc/fs/dir.rs b/vendor/rustix/src/backend/libc/fs/dir.rs new file mode 100644 index 0000000..82a0a90 --- /dev/null +++ b/vendor/rustix/src/backend/libc/fs/dir.rs @@ -0,0 +1,423 @@ +#[cfg(not(any(solarish, target_os = "haiku", target_os = "nto", target_os = "vita")))] +use super::types::FileType; +use crate::backend::c; +use crate::backend::conv::owned_fd; +use crate::fd::{AsFd, BorrowedFd, OwnedFd}; +use crate::ffi::{CStr, CString}; +use crate::fs::{fcntl_getfl, openat, Mode, OFlags}; +#[cfg(not(target_os = "vita"))] +use crate::fs::{fstat, Stat}; +#[cfg(not(any( + solarish, + target_os = "haiku", + target_os = "netbsd", + target_os = "nto", + target_os = "redox", + target_os = "vita", + target_os = "wasi", +)))] +use crate::fs::{fstatfs, StatFs}; +#[cfg(not(any( + solarish, + target_os = "haiku", + target_os = "redox", + target_os = "vita", + target_os = "wasi" +)))] +use crate::fs::{fstatvfs, StatVfs}; +use crate::io; +#[cfg(not(any(target_os = "fuchsia", target_os = "vita", target_os = "wasi")))] +#[cfg(feature = "process")] +use crate::process::fchdir; +use alloc::borrow::ToOwned; +#[cfg(not(any(linux_like, target_os = "hurd")))] +use c::readdir as libc_readdir; +#[cfg(any(linux_like, target_os = "hurd"))] +use c::readdir64 as libc_readdir; +use core::fmt; +use core::ptr::NonNull; +use libc_errno::{errno, set_errno, Errno}; + +/// `DIR*` +pub struct Dir { + /// The `libc` `DIR` pointer. + libc_dir: NonNull<c::DIR>, + + /// Have we seen any errors in this iteration? + any_errors: bool, +} + +impl Dir { + /// Take ownership of `fd` and construct a `Dir` that reads entries from + /// the given directory file descriptor. + #[inline] + pub fn new<Fd: Into<OwnedFd>>(fd: Fd) -> io::Result<Self> { + Self::_new(fd.into()) + } + + #[inline] + fn _new(fd: OwnedFd) -> io::Result<Self> { + let raw = owned_fd(fd); + unsafe { + let libc_dir = c::fdopendir(raw); + + if let Some(libc_dir) = NonNull::new(libc_dir) { + Ok(Self { + libc_dir, + any_errors: false, + }) + } else { + let err = io::Errno::last_os_error(); + let _ = c::close(raw); + Err(err) + } + } + } + + /// Borrow `fd` and construct a `Dir` that reads entries from the given + /// directory file descriptor. + #[inline] + pub fn read_from<Fd: AsFd>(fd: Fd) -> io::Result<Self> { + Self::_read_from(fd.as_fd()) + } + + #[inline] + #[allow(unused_mut)] + fn _read_from(fd: BorrowedFd<'_>) -> io::Result<Self> { + let mut any_errors = false; + + // Given an arbitrary `OwnedFd`, it's impossible to know whether the + // user holds a `dup`'d copy which could continue to modify the + // file description state, which would cause Undefined Behavior after + // our call to `fdopendir`. To prevent this, we obtain an independent + // `OwnedFd`. + let flags = fcntl_getfl(fd)?; + let fd_for_dir = match openat(fd, cstr!("."), flags | OFlags::CLOEXEC, Mode::empty()) { + Ok(fd) => fd, + #[cfg(not(target_os = "wasi"))] + Err(io::Errno::NOENT) => { + // If "." doesn't exist, it means the directory was removed. + // We treat that as iterating through a directory with no + // entries. + any_errors = true; + crate::io::dup(fd)? + } + Err(err) => return Err(err), + }; + + let raw = owned_fd(fd_for_dir); + unsafe { + let libc_dir = c::fdopendir(raw); + + if let Some(libc_dir) = NonNull::new(libc_dir) { + Ok(Self { + libc_dir, + any_errors, + }) + } else { + let err = io::Errno::last_os_error(); + let _ = c::close(raw); + Err(err) + } + } + } + + /// `rewinddir(self)` + #[inline] + pub fn rewind(&mut self) { + self.any_errors = false; + unsafe { c::rewinddir(self.libc_dir.as_ptr()) } + } + + /// `readdir(self)`, where `None` means the end of the directory. + pub fn read(&mut self) -> Option<io::Result<DirEntry>> { + // If we've seen errors, don't continue to try to read anyting further. + if self.any_errors { + return None; + } + + set_errno(Errno(0)); + let dirent_ptr = unsafe { libc_readdir(self.libc_dir.as_ptr()) }; + if dirent_ptr.is_null() { + let curr_errno = errno().0; + if curr_errno == 0 { + // We successfully reached the end of the stream. + None + } else { + // `errno` is unknown or non-zero, so an error occurred. + self.any_errors = true; + Some(Err(io::Errno(curr_errno))) + } + } else { + // We successfully read an entry. + unsafe { + let dirent = &*dirent_ptr; + + // We have our own copy of OpenBSD's dirent; check that the + // layout minimally matches libc's. + #[cfg(target_os = "openbsd")] + check_dirent_layout(dirent); + + let result = DirEntry { + #[cfg(not(any( + solarish, + target_os = "aix", + target_os = "haiku", + target_os = "nto", + target_os = "vita" + )))] + d_type: dirent.d_type, + + #[cfg(not(any(freebsdlike, netbsdlike, target_os = "vita")))] + d_ino: dirent.d_ino, + + #[cfg(any(freebsdlike, netbsdlike))] + d_fileno: dirent.d_fileno, + + name: CStr::from_ptr(dirent.d_name.as_ptr()).to_owned(), + }; + + Some(Ok(result)) + } + } + } + + /// `fstat(self)` + #[cfg(not(target_os = "vita"))] + #[inline] + pub fn stat(&self) -> io::Result<Stat> { + fstat(unsafe { BorrowedFd::borrow_raw(c::dirfd(self.libc_dir.as_ptr())) }) + } + + /// `fstatfs(self)` + #[cfg(not(any( + solarish, + target_os = "haiku", + target_os = "netbsd", + target_os = "nto", + target_os = "redox", + target_os = "vita", + target_os = "wasi", + )))] + #[inline] + pub fn statfs(&self) -> io::Result<StatFs> { + fstatfs(unsafe { BorrowedFd::borrow_raw(c::dirfd(self.libc_dir.as_ptr())) }) + } + + /// `fstatvfs(self)` + #[cfg(not(any( + solarish, + target_os = "haiku", + target_os = "redox", + target_os = "vita", + target_os = "wasi" + )))] + #[inline] + pub fn statvfs(&self) -> io::Result<StatVfs> { + fstatvfs(unsafe { BorrowedFd::borrow_raw(c::dirfd(self.libc_dir.as_ptr())) }) + } + + /// `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")))] + #[inline] + pub fn chdir(&self) -> io::Result<()> { + fchdir(unsafe { BorrowedFd::borrow_raw(c::dirfd(self.libc_dir.as_ptr())) }) + } +} + +/// `Dir` implements `Send` but not `Sync`, because we use `readdir` which is +/// not guaranteed to be thread-safe. Users can wrap this in a `Mutex` if they +/// need `Sync`, which is effectively what'd need to do to implement `Sync` +/// ourselves. +unsafe impl Send for Dir {} + +impl Drop for Dir { + #[inline] + fn drop(&mut self) { + unsafe { c::closedir(self.libc_dir.as_ptr()) }; + } +} + +impl Iterator for Dir { + type Item = io::Result<DirEntry>; + + #[inline] + fn next(&mut self) -> Option<Self::Item> { + Self::read(self) + } +} + +impl fmt::Debug for Dir { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut s = f.debug_struct("Dir"); + #[cfg(not(target_os = "vita"))] + s.field("fd", unsafe { &c::dirfd(self.libc_dir.as_ptr()) }); + s.finish() + } +} + +/// `struct dirent` +#[derive(Debug)] +pub struct DirEntry { + #[cfg(not(any( + solarish, + target_os = "aix", + target_os = "haiku", + target_os = "nto", + target_os = "vita" + )))] + d_type: u8, + + #[cfg(not(any(freebsdlike, netbsdlike, target_os = "vita")))] + d_ino: c::ino_t, + + #[cfg(any(freebsdlike, netbsdlike))] + d_fileno: c::ino_t, + + name: CString, +} + +impl DirEntry { + /// Returns the file name of this directory entry. + #[inline] + pub fn file_name(&self) -> &CStr { + &self.name + } + + /// Returns the type of this directory entry. + #[cfg(not(any( + solarish, + target_os = "aix", + target_os = "haiku", + target_os = "nto", + target_os = "vita" + )))] + #[inline] + pub fn file_type(&self) -> FileType { + FileType::from_dirent_d_type(self.d_type) + } + + /// Return the inode number of this directory entry. + #[cfg(not(any(freebsdlike, netbsdlike, target_os = "vita")))] + #[inline] + pub fn ino(&self) -> u64 { + self.d_ino as u64 + } + + /// Return the inode number of this directory entry. + #[cfg(any(freebsdlike, netbsdlike))] + #[inline] + pub fn ino(&self) -> u64 { + #[allow(clippy::useless_conversion)] + self.d_fileno.into() + } +} + +/// libc's OpenBSD `dirent` has a private field so we can't construct it +/// directly, so we declare it ourselves to make all fields accessible. +#[cfg(target_os = "openbsd")] +#[repr(C)] +#[derive(Debug)] +struct libc_dirent { + d_fileno: c::ino_t, + d_off: c::off_t, + d_reclen: u16, + d_type: u8, + d_namlen: u8, + __d_padding: [u8; 4], + d_name: [c::c_char; 256], +} + +/// We have our own copy of OpenBSD's dirent; check that the layout +/// minimally matches libc's. +#[cfg(target_os = "openbsd")] +fn check_dirent_layout(dirent: &c::dirent) { + use crate::utils::as_ptr; + + // Check that the basic layouts match. + #[cfg(test)] + { + assert_eq_size!(libc_dirent, c::dirent); + assert_eq_size!(libc_dirent, c::dirent); + } + + // Check that the field offsets match. + assert_eq!( + { + let z = libc_dirent { + d_fileno: 0_u64, + d_off: 0_i64, + d_reclen: 0_u16, + d_type: 0_u8, + d_namlen: 0_u8, + __d_padding: [0_u8; 4], + d_name: [0 as c::c_char; 256], + }; + let base = as_ptr(&z) as usize; + ( + (as_ptr(&z.d_fileno) as usize) - base, + (as_ptr(&z.d_off) as usize) - base, + (as_ptr(&z.d_reclen) as usize) - base, + (as_ptr(&z.d_type) as usize) - base, + (as_ptr(&z.d_namlen) as usize) - base, + (as_ptr(&z.d_name) as usize) - base, + ) + }, + { + let z = dirent; + let base = as_ptr(z) as usize; + ( + (as_ptr(&z.d_fileno) as usize) - base, + (as_ptr(&z.d_off) as usize) - base, + (as_ptr(&z.d_reclen) as usize) - base, + (as_ptr(&z.d_type) as usize) - base, + (as_ptr(&z.d_namlen) as usize) - base, + (as_ptr(&z.d_name) as usize) - base, + ) + } + ); +} + +#[test] +fn dir_iterator_handles_io_errors() { + // create a dir, keep the FD, then delete the dir + let tmp = tempfile::tempdir().unwrap(); + let fd = crate::fs::openat( + crate::fs::CWD, + tmp.path(), + crate::fs::OFlags::RDONLY | crate::fs::OFlags::CLOEXEC, + crate::fs::Mode::empty(), + ) + .unwrap(); + + let file_fd = crate::fs::openat( + &fd, + tmp.path().join("test.txt"), + crate::fs::OFlags::WRONLY | crate::fs::OFlags::CREATE, + crate::fs::Mode::RWXU, + ) + .unwrap(); + + let mut dir = Dir::read_from(&fd).unwrap(); + + // Reach inside the `Dir` and replace its directory with a file, which + // will cause the subsequent `readdir` to fail. + unsafe { + let raw_fd = c::dirfd(dir.libc_dir.as_ptr()); + let mut owned_fd: crate::fd::OwnedFd = crate::fd::FromRawFd::from_raw_fd(raw_fd); + crate::io::dup2(&file_fd, &mut owned_fd).unwrap(); + core::mem::forget(owned_fd); + } + + // FreeBSD and macOS seem to read some directory entries before we call + // `.next()`. + #[cfg(any(apple, freebsdlike))] + { + dir.rewind(); + } + + assert!(matches!(dir.next(), Some(Err(_)))); + assert!(dir.next().is_none()); +} diff --git a/vendor/rustix/src/backend/libc/fs/inotify.rs b/vendor/rustix/src/backend/libc/fs/inotify.rs new file mode 100644 index 0000000..2044bd9 --- /dev/null +++ b/vendor/rustix/src/backend/libc/fs/inotify.rs @@ -0,0 +1,131 @@ +//! 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`]. + /// + /// [`inotify_init`]: crate::fs::inotify::inotify_init + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct CreateFlags: u32 { + /// `IN_CLOEXEC` + const CLOEXEC = bitcast!(c::IN_CLOEXEC); + /// `IN_NONBLOCK` + const NONBLOCK = bitcast!(c::IN_NONBLOCK); + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} + +bitflags! { + /// `IN*` for use with [`inotify_add_watch`]. + /// + /// [`inotify_add_watch`]: crate::fs::inotify::inotify_add_watch + #[repr(transparent)] + #[derive(Default, Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct WatchFlags: 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; + + /// `IN_CLOSE` + const CLOSE = c::IN_CLOSE; + /// `IN_MOVE` + const MOVE = c::IN_MOVE; + /// `IN_ALL_EVENTS` + const ALL_EVENTS = c::IN_ALL_EVENTS; + + /// `IN_DONT_FOLLOW` + const DONT_FOLLOW = c::IN_DONT_FOLLOW; + /// `IN_EXCL_UNLINK` + const EXCL_UNLINK = 1; + /// `IN_MASK_ADD` + const MASK_ADD = 1; + /// `IN_MASK_CREATE` + const MASK_CREATE = 1; + /// `IN_ONESHOT` + const ONESHOT = c::IN_ONESHOT; + /// `IN_ONLYDIR` + const ONLYDIR = c::IN_ONLYDIR; + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} + +/// `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<OwnedFd> { + // SAFETY: `inotify_init1` has no safety preconditions. + unsafe { ret_owned_fd(c::inotify_init1(bitflags_bits!(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. +pub fn inotify_add_watch<P: crate::path::Arg>( + inot: BorrowedFd<'_>, + path: P, + flags: WatchFlags, +) -> io::Result<i32> { + 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(), + )) + } + }) +} + +/// `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)) } +} diff --git a/vendor/rustix/src/backend/libc/fs/makedev.rs b/vendor/rustix/src/backend/libc/fs/makedev.rs new file mode 100644 index 0000000..aa12102 --- /dev/null +++ b/vendor/rustix/src/backend/libc/fs/makedev.rs @@ -0,0 +1,138 @@ +#[cfg(not(all(target_os = "android", target_pointer_width = "32")))] +use crate::backend::c; +use crate::fs::Dev; + +#[cfg(not(any( + apple, + solarish, + target_os = "aix", + target_os = "android", + target_os = "emscripten", +)))] +#[inline] +pub(crate) fn makedev(maj: u32, min: u32) -> Dev { + c::makedev(maj, min) +} + +#[cfg(solarish)] +pub(crate) fn makedev(maj: u32, min: u32) -> Dev { + // SAFETY: Solarish's `makedev` is marked unsafe but it isn't doing + // anything unsafe. + unsafe { c::makedev(maj, min) } +} + +#[cfg(all(target_os = "android", not(target_pointer_width = "32")))] +#[inline] +pub(crate) fn makedev(maj: u32, min: u32) -> Dev { + c::makedev(maj, min) +} + +#[cfg(all(target_os = "android", target_pointer_width = "32"))] +#[inline] +pub(crate) fn makedev(maj: u32, min: u32) -> Dev { + // 32-bit Android's `dev_t` is 32-bit, but its `st_dev` is 64-bit, so we do + // it ourselves. + ((u64::from(maj) & 0xffff_f000_u64) << 32) + | ((u64::from(maj) & 0x0000_0fff_u64) << 8) + | ((u64::from(min) & 0xffff_ff00_u64) << 12) + | (u64::from(min) & 0x0000_00ff_u64) +} + +#[cfg(target_os = "emscripten")] +#[inline] +pub(crate) fn makedev(maj: u32, min: u32) -> Dev { + // Emscripten's `makedev` has a 32-bit return value. + Dev::from(c::makedev(maj, min)) +} + +#[cfg(apple)] +#[inline] +pub(crate) fn makedev(maj: u32, min: u32) -> Dev { + // Apple's `makedev` oddly has signed argument types and is `unsafe`. + unsafe { c::makedev(maj as i32, min as i32) } +} + +#[cfg(target_os = "aix")] +#[inline] +pub(crate) fn makedev(maj: u32, min: u32) -> Dev { + // AIX's `makedev` oddly is `unsafe`. + unsafe { c::makedev(maj, min) } +} + +#[cfg(not(any( + apple, + freebsdlike, + target_os = "android", + target_os = "emscripten", + target_os = "netbsd" +)))] +#[inline] +pub(crate) fn major(dev: Dev) -> u32 { + unsafe { c::major(dev) } +} + +#[cfg(any( + apple, + freebsdlike, + target_os = "netbsd", + all(target_os = "android", not(target_pointer_width = "32")), +))] +#[inline] +pub(crate) fn major(dev: Dev) -> u32 { + // On some platforms `major` oddly has signed return types. + (unsafe { c::major(dev) }) as u32 +} + +#[cfg(all(target_os = "android", target_pointer_width = "32"))] +#[inline] +pub(crate) fn major(dev: Dev) -> u32 { + // 32-bit Android's `dev_t` is 32-bit, but its `st_dev` is 64-bit, so we do + // it ourselves. + (((dev >> 31 >> 1) & 0xffff_f000) | ((dev >> 8) & 0x0000_0fff)) as u32 +} + +#[cfg(target_os = "emscripten")] +#[inline] +pub(crate) fn major(dev: Dev) -> u32 { + // Emscripten's `major` has a 32-bit argument value. + unsafe { c::major(dev as u32) } +} + +#[cfg(not(any( + apple, + freebsdlike, + target_os = "android", + target_os = "emscripten", + target_os = "netbsd" +)))] +#[inline] +pub(crate) fn minor(dev: Dev) -> u32 { + unsafe { c::minor(dev) } +} + +#[cfg(any( + apple, + freebsdlike, + target_os = "netbsd", + all(target_os = "android", not(target_pointer_width = "32")) +))] +#[inline] +pub(crate) fn minor(dev: Dev) -> u32 { + // On some platforms, `minor` oddly has signed return types. + (unsafe { c::minor(dev) }) as u32 +} + +#[cfg(all(target_os = "android", target_pointer_width = "32"))] +#[inline] +pub(crate) fn minor(dev: Dev) -> u32 { + // 32-bit Android's `dev_t` is 32-bit, but its `st_dev` is 64-bit, so we do + // it ourselves. + (((dev >> 12) & 0xffff_ff00) | (dev & 0x0000_00ff)) as u32 +} + +#[cfg(target_os = "emscripten")] +#[inline] +pub(crate) fn minor(dev: Dev) -> u32 { + // Emscripten's `minor` has a 32-bit argument value. + unsafe { c::minor(dev as u32) } +} diff --git a/vendor/rustix/src/backend/libc/fs/mod.rs b/vendor/rustix/src/backend/libc/fs/mod.rs new file mode 100644 index 0000000..c17e863 --- /dev/null +++ b/vendor/rustix/src/backend/libc/fs/mod.rs @@ -0,0 +1,23 @@ +#[cfg(all(feature = "alloc", not(any(target_os = "espidf", target_os = "redox"))))] +pub(crate) mod dir; +#[cfg(linux_kernel)] +pub mod inotify; +#[cfg(not(any( + target_os = "espidf", + target_os = "haiku", + target_os = "redox", + target_os = "vita", + target_os = "wasi" +)))] +pub(crate) mod makedev; +#[cfg(not(windows))] +pub(crate) mod syscalls; +pub(crate) mod types; + +// TODO: Fix linux-raw-sys to define ioctl codes for sparc. +#[cfg(all(linux_kernel, any(target_arch = "sparc", target_arch = "sparc64")))] +pub(crate) const EXT4_IOC_RESIZE_FS: crate::ioctl::RawOpcode = 0x8008_6610; + +#[cfg(all(linux_kernel, not(any(target_arch = "sparc", target_arch = "sparc64"))))] +pub(crate) const EXT4_IOC_RESIZE_FS: crate::ioctl::RawOpcode = + linux_raw_sys::ioctl::EXT4_IOC_RESIZE_FS as crate::ioctl::RawOpcode; diff --git a/vendor/rustix/src/backend/libc/fs/syscalls.rs b/vendor/rustix/src/backend/libc/fs/syscalls.rs new file mode 100644 index 0000000..70e86cf --- /dev/null +++ b/vendor/rustix/src/backend/libc/fs/syscalls.rs @@ -0,0 +1,2514 @@ +//! libc syscalls supporting `rustix::fs`. + +use crate::backend::c; +#[cfg(any( + not(target_os = "redox"), + feature = "alloc", + all(linux_kernel, feature = "procfs") +))] +use crate::backend::conv::ret_usize; +use crate::backend::conv::{borrowed_fd, c_str, ret, ret_c_int, ret_off_t, ret_owned_fd}; +use crate::fd::{BorrowedFd, OwnedFd}; +use crate::ffi::CStr; +#[cfg(all(apple, feature = "alloc"))] +use crate::ffi::CString; +#[cfg(not(any(target_os = "espidf", target_os = "vita")))] +use crate::fs::Access; +#[cfg(not(any( + apple, + netbsdlike, + solarish, + target_os = "dragonfly", + target_os = "espidf", + target_os = "haiku", + target_os = "redox", + target_os = "vita", +)))] +use crate::fs::Advice; +#[cfg(not(any(target_os = "espidf", target_os = "redox")))] +use crate::fs::AtFlags; +#[cfg(not(any( + netbsdlike, + solarish, + target_os = "aix", + target_os = "dragonfly", + target_os = "espidf", + target_os = "nto", + target_os = "redox", + target_os = "vita", +)))] +use crate::fs::FallocateFlags; +#[cfg(not(any(target_os = "espidf", target_os = "vita", target_os = "wasi")))] +use crate::fs::FlockOperation; +#[cfg(any(linux_kernel, target_os = "freebsd"))] +use crate::fs::MemfdFlags; +#[cfg(any(linux_kernel, target_os = "freebsd", target_os = "fuchsia"))] +use crate::fs::SealFlags; +#[cfg(not(any( + solarish, + target_os = "espidf", + target_os = "haiku", + target_os = "netbsd", + target_os = "nto", + target_os = "redox", + target_os = "vita", + target_os = "wasi", +)))] +use crate::fs::StatFs; +#[cfg(not(any(target_os = "espidf", target_os = "vita")))] +use crate::fs::Timestamps; +#[cfg(not(any( + apple, + target_os = "espidf", + target_os = "redox", + target_os = "vita", + target_os = "wasi" +)))] +use crate::fs::{Dev, FileType}; +use crate::fs::{Mode, OFlags, SeekFrom, Stat}; +#[cfg(not(any(target_os = "haiku", target_os = "redox", target_os = "wasi")))] +use crate::fs::{StatVfs, StatVfsMountFlags}; +use crate::io; +#[cfg(all(target_env = "gnu", fix_y2038))] +use crate::timespec::LibcTimespec; +#[cfg(not(target_os = "wasi"))] +use crate::ugid::{Gid, Uid}; +#[cfg(all(apple, feature = "alloc"))] +use alloc::vec; +use core::mem::MaybeUninit; +#[cfg(apple)] +use { + crate::backend::conv::nonnegative_ret, + crate::fs::{copyfile_state_t, CloneFlags, CopyfileFlags}, +}; +#[cfg(any(apple, linux_kernel))] +use {crate::fs::XattrFlags, core::mem::size_of, core::ptr::null_mut}; +#[cfg(linux_kernel)] +use { + crate::fs::{RenameFlags, ResolveFlags, Statx, StatxFlags, CWD}, + core::ptr::null, +}; + +#[cfg(all(target_env = "gnu", fix_y2038))] +weak!(fn __utimensat64(c::c_int, *const c::c_char, *const LibcTimespec, c::c_int) -> c::c_int); +#[cfg(all(target_env = "gnu", fix_y2038))] +weak!(fn __futimens64(c::c_int, *const LibcTimespec) -> c::c_int); + +/// Use a direct syscall (via libc) for `open`. +/// +/// This is only currently necessary as a workaround for old glibc; see below. +#[cfg(all(unix, target_env = "gnu"))] +fn open_via_syscall(path: &CStr, oflags: OFlags, mode: Mode) -> io::Result<OwnedFd> { + // Linux on aarch64, loongarch64 and riscv64 has no `open` syscall so use + // `openat`. + #[cfg(any( + target_arch = "aarch64", + target_arch = "riscv32", + target_arch = "riscv64", + target_arch = "csky", + target_arch = "loongarch64" + ))] + { + openat_via_syscall(CWD, path, oflags, mode) + } + + // Use the `open` syscall. + #[cfg(not(any( + target_arch = "aarch64", + target_arch = "riscv32", + target_arch = "riscv64", + target_arch = "csky", + target_arch = "loongarch64" + )))] + unsafe { + syscall! { + fn open( + pathname: *const c::c_char, + oflags: c::c_int, + mode: c::mode_t + ) via SYS_open -> c::c_int + } + + ret_owned_fd(open( + c_str(path), + bitflags_bits!(oflags), + bitflags_bits!(mode), + )) + } +} + +pub(crate) fn open(path: &CStr, oflags: OFlags, mode: Mode) -> io::Result<OwnedFd> { + // Work around <https://sourceware.org/bugzilla/show_bug.cgi?id=17523>. + // glibc versions before 2.25 don't handle `O_TMPFILE` correctly. + #[cfg(all(unix, target_env = "gnu", not(target_os = "hurd")))] + if oflags.contains(OFlags::TMPFILE) && crate::backend::if_glibc_is_less_than_2_25() { + return open_via_syscall(path, oflags, mode); + } + + // On these platforms, `mode_t` is `u16` and can't be passed directly to a + // variadic function. + #[cfg(any( + apple, + freebsdlike, + all(target_os = "android", target_pointer_width = "32") + ))] + let mode: c::c_uint = mode.bits().into(); + + // Otherwise, cast to `mode_t` as that's what `open` is documented to take. + #[cfg(not(any( + apple, + freebsdlike, + all(target_os = "android", target_pointer_width = "32") + )))] + let mode: c::mode_t = mode.bits() as _; + + unsafe { ret_owned_fd(c::open(c_str(path), bitflags_bits!(oflags), mode)) } +} + +/// Use a direct syscall (via libc) for `openat`. +/// +/// This is only currently necessary as a workaround for old glibc; see below. +#[cfg(all(unix, target_env = "gnu", not(target_os = "hurd")))] +fn openat_via_syscall( + dirfd: BorrowedFd<'_>, + path: &CStr, + oflags: OFlags, + mode: Mode, +) -> io::Result<OwnedFd> { + syscall! { + fn openat( + base_dirfd: c::c_int, + pathname: *const c::c_char, + oflags: c::c_int, + mode: c::mode_t + ) via SYS_openat -> c::c_int + } + + unsafe { + ret_owned_fd(openat( + borrowed_fd(dirfd), + c_str(path), + bitflags_bits!(oflags), + bitflags_bits!(mode), + )) + } +} + +#[cfg(not(target_os = "redox"))] +pub(crate) fn openat( + dirfd: BorrowedFd<'_>, + path: &CStr, + oflags: OFlags, + mode: Mode, +) -> io::Result<OwnedFd> { + // Work around <https://sourceware.org/bugzilla/show_bug.cgi?id=17523>. + // glibc versions before 2.25 don't handle `O_TMPFILE` correctly. + #[cfg(all(unix, target_env = "gnu", not(target_os = "hurd")))] + if oflags.contains(OFlags::TMPFILE) && crate::backend::if_glibc_is_less_than_2_25() { + return openat_via_syscall(dirfd, path, oflags, mode); + } + + // On these platforms, `mode_t` is `u16` and can't be passed directly to a + // variadic function. + #[cfg(any( + apple, + freebsdlike, + all(target_os = "android", target_pointer_width = "32") + ))] + let mode: c::c_uint = mode.bits().into(); + + // Otherwise, cast to `mode_t` as that's what `open` is documented to take. + #[cfg(not(any( + apple, + freebsdlike, + all(target_os = "android", target_pointer_width = "32") + )))] + let mode: c::mode_t = mode.bits() as _; + + unsafe { + ret_owned_fd(c::openat( + borrowed_fd(dirfd), + c_str(path), + bitflags_bits!(oflags), + mode, + )) + } +} + +#[cfg(not(any( + solarish, + target_os = "espidf", + target_os = "haiku", + target_os = "netbsd", + target_os = "nto", + target_os = "redox", + target_os = "vita", + target_os = "wasi", +)))] +#[inline] +pub(crate) fn statfs(filename: &CStr) -> io::Result<StatFs> { + unsafe { + let mut result = MaybeUninit::<StatFs>::uninit(); + ret(c::statfs(c_str(filename), result.as_mut_ptr()))?; + Ok(result.assume_init()) + } +} + +#[cfg(not(any(target_os = "haiku", target_os = "redox", target_os = "wasi")))] +#[inline] +pub(crate) fn statvfs(filename: &CStr) -> io::Result<StatVfs> { + unsafe { + let mut result = MaybeUninit::<c::statvfs>::uninit(); + ret(c::statvfs(c_str(filename), result.as_mut_ptr()))?; + Ok(libc_statvfs_to_statvfs(result.assume_init())) + } +} + +#[cfg(feature = "alloc")] +#[inline] +pub(crate) fn readlink(path: &CStr, buf: &mut [u8]) -> io::Result<usize> { + unsafe { + ret_usize( + c::readlink(c_str(path), buf.as_mut_ptr().cast::<c::c_char>(), buf.len()) as isize, + ) + } +} + +#[cfg(not(target_os = "redox"))] +#[inline] +pub(crate) fn readlinkat( + dirfd: BorrowedFd<'_>, + path: &CStr, + buf: &mut [MaybeUninit<u8>], +) -> io::Result<usize> { + unsafe { + ret_usize(c::readlinkat( + borrowed_fd(dirfd), + c_str(path), + buf.as_mut_ptr().cast::<c::c_char>(), + buf.len(), + ) as isize) + } +} + +pub(crate) fn mkdir(path: &CStr, mode: Mode) -> io::Result<()> { + unsafe { ret(c::mkdir(c_str(path), mode.bits() as c::mode_t)) } +} + +#[cfg(not(target_os = "redox"))] +pub(crate) fn mkdirat(dirfd: BorrowedFd<'_>, path: &CStr, mode: Mode) -> io::Result<()> { + unsafe { + ret(c::mkdirat( + borrowed_fd(dirfd), + c_str(path), + mode.bits() as c::mode_t, + )) + } +} + +#[cfg(linux_kernel)] +pub(crate) fn getdents_uninit( + fd: BorrowedFd<'_>, + buf: &mut [MaybeUninit<u8>], +) -> io::Result<usize> { + syscall! { + fn getdents64( + fd: c::c_int, + dirp: *mut c::c_void, + count: usize + ) via SYS_getdents64 -> c::ssize_t + } + unsafe { + ret_usize(getdents64( + borrowed_fd(fd), + buf.as_mut_ptr().cast::<c::c_void>(), + buf.len(), + )) + } +} + +pub(crate) fn link(old_path: &CStr, new_path: &CStr) -> io::Result<()> { + unsafe { ret(c::link(c_str(old_path), c_str(new_path))) } +} + +#[cfg(not(any(target_os = "espidf", target_os = "redox")))] +pub(crate) fn linkat( + old_dirfd: BorrowedFd<'_>, + old_path: &CStr, + new_dirfd: BorrowedFd<'_>, + new_path: &CStr, + flags: AtFlags, +) -> io::Result<()> { + // macOS <= 10.9 lacks `linkat`. + #[cfg(target_os = "macos")] + unsafe { + weak! { + fn linkat( + c::c_int, + *const c::c_char, + c::c_int, + *const c::c_char, + c::c_int + ) -> c::c_int + } + // If we have `linkat`, use it. + if let Some(libc_linkat) = linkat.get() { + return ret(libc_linkat( + borrowed_fd(old_dirfd), + c_str(old_path), + borrowed_fd(new_dirfd), + c_str(new_path), + bitflags_bits!(flags), + )); + } + // Otherwise, see if we can emulate the `AT_FDCWD` case. + if borrowed_fd(old_dirfd) != c::AT_FDCWD || borrowed_fd(new_dirfd) != c::AT_FDCWD { + return Err(io::Errno::NOSYS); + } + if flags.intersects(!AtFlags::SYMLINK_FOLLOW) { + return Err(io::Errno::INVAL); + } + if !flags.is_empty() { + return Err(io::Errno::OPNOTSUPP); + } + ret(c::link(c_str(old_path), c_str(new_path))) + } + + #[cfg(not(target_os = "macos"))] + unsafe { + ret(c::linkat( + borrowed_fd(old_dirfd), + c_str(old_path), + borrowed_fd(new_dirfd), + c_str(new_path), + bitflags_bits!(flags), + )) + } +} + +pub(crate) fn rmdir(path: &CStr) -> io::Result<()> { + unsafe { ret(c::rmdir(c_str(path))) } +} + +pub(crate) fn unlink(path: &CStr) -> io::Result<()> { + unsafe { ret(c::unlink(c_str(path))) } +} + +#[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`. + #[cfg(target_os = "macos")] + unsafe { + weak! { + fn unlinkat( + c::c_int, + *const c::c_char, + c::c_int + ) -> c::c_int + } + // If we have `unlinkat`, use it. + if let Some(libc_unlinkat) = unlinkat.get() { + return ret(libc_unlinkat( + borrowed_fd(dirfd), + c_str(path), + bitflags_bits!(flags), + )); + } + // Otherwise, see if we can emulate the `AT_FDCWD` case. + if borrowed_fd(dirfd) != c::AT_FDCWD { + return Err(io::Errno::NOSYS); + } + if flags.intersects(!AtFlags::REMOVEDIR) { + return Err(io::Errno::INVAL); + } + if flags.contains(AtFlags::REMOVEDIR) { + ret(c::rmdir(c_str(path))) + } else { + ret(c::unlink(c_str(path))) + } + } + + #[cfg(not(target_os = "macos"))] + unsafe { + ret(c::unlinkat( + borrowed_fd(dirfd), + c_str(path), + bitflags_bits!(flags), + )) + } +} + +pub(crate) fn rename(old_path: &CStr, new_path: &CStr) -> io::Result<()> { + unsafe { ret(c::rename(c_str(old_path), c_str(new_path))) } +} + +#[cfg(not(target_os = "redox"))] +pub(crate) fn renameat( + old_dirfd: BorrowedFd<'_>, + old_path: &CStr, + new_dirfd: BorrowedFd<'_>, + new_path: &CStr, +) -> io::Result<()> { + // macOS <= 10.9 lacks `renameat`. + #[cfg(target_os = "macos")] + unsafe { + weak! { + fn renameat( + c::c_int, + *const c::c_char, + c::c_int, + *const c::c_char + ) -> c::c_int + } + // If we have `renameat`, use it. + if let Some(libc_renameat) = renameat.get() { + return ret(libc_renameat( + borrowed_fd(old_dirfd), + c_str(old_path), + borrowed_fd(new_dirfd), + c_str(new_path), + )); + } + // Otherwise, see if we can emulate the `AT_FDCWD` case. + if borrowed_fd(old_dirfd) != c::AT_FDCWD || borrowed_fd(new_dirfd) != c::AT_FDCWD { + return Err(io::Errno::NOSYS); + } + ret(c::rename(c_str(old_path), c_str(new_path))) + } + + #[cfg(not(target_os = "macos"))] + unsafe { + ret(c::renameat( + borrowed_fd(old_dirfd), + c_str(old_path), + borrowed_fd(new_dirfd), + c_str(new_path), + )) + } +} + +#[cfg(all(target_os = "linux", target_env = "gnu"))] +pub(crate) fn renameat2( + old_dirfd: BorrowedFd<'_>, + old_path: &CStr, + new_dirfd: BorrowedFd<'_>, + new_path: &CStr, + flags: RenameFlags, +) -> io::Result<()> { + // `renameat2` wasn't supported in glibc until 2.28. + weak_or_syscall! { + fn renameat2( + olddirfd: c::c_int, + oldpath: *const c::c_char, + newdirfd: c::c_int, + newpath: *const c::c_char, + flags: c::c_uint + ) via SYS_renameat2 -> c::c_int + } + + unsafe { + ret(renameat2( + borrowed_fd(old_dirfd), + c_str(old_path), + borrowed_fd(new_dirfd), + c_str(new_path), + flags.bits(), + )) + } +} + +#[cfg(any( + target_os = "android", + all(target_os = "linux", not(target_env = "gnu")), +))] +#[inline] +pub(crate) fn renameat2( + old_dirfd: BorrowedFd<'_>, + old_path: &CStr, + new_dirfd: BorrowedFd<'_>, + new_path: &CStr, + flags: RenameFlags, +) -> io::Result<()> { + // At present, `libc` only has `renameat2` defined for glibc. If we have + // no flags, we can use plain `renameat`, but otherwise we use `syscall!`. + // to call `renameat2` ourselves. + if flags.is_empty() { + renameat(old_dirfd, old_path, new_dirfd, new_path) + } else { + syscall! { + fn renameat2( + olddirfd: c::c_int, + oldpath: *const c::c_char, + newdirfd: c::c_int, + newpath: *const c::c_char, + flags: c::c_uint + ) via SYS_renameat2 -> c::c_int + } + + unsafe { + ret(renameat2( + borrowed_fd(old_dirfd), + c_str(old_path), + borrowed_fd(new_dirfd), + c_str(new_path), + flags.bits(), + )) + } + } +} + +pub(crate) fn symlink(old_path: &CStr, new_path: &CStr) -> io::Result<()> { + unsafe { ret(c::symlink(c_str(old_path), c_str(new_path))) } +} + +#[cfg(not(target_os = "redox"))] +pub(crate) fn symlinkat( + old_path: &CStr, + new_dirfd: BorrowedFd<'_>, + new_path: &CStr, +) -> io::Result<()> { + unsafe { + ret(c::symlinkat( + c_str(old_path), + borrowed_fd(new_dirfd), + c_str(new_path), + )) + } +} + +pub(crate) fn stat(path: &CStr) -> io::Result<Stat> { + // See the comments in `fstat` about using `crate::fs::statx` here. + #[cfg(all( + linux_kernel, + any( + target_pointer_width = "32", + target_arch = "mips64", + target_arch = "mips64r6" + ) + ))] + { + match crate::fs::statx( + crate::fs::CWD, + path, + AtFlags::empty(), + StatxFlags::BASIC_STATS, + ) { + Ok(x) => statx_to_stat(x), + Err(io::Errno::NOSYS) => statat_old(crate::fs::CWD, path, AtFlags::empty()), + Err(err) => Err(err), + } + } + + // Main version: libc is y2038 safe. Or, the platform is not y2038 safe and + // there's nothing practical we can do. + #[cfg(not(all( + linux_kernel, + any( + target_pointer_width = "32", + target_arch = "mips64", + target_arch = "mips64r6" + ) + )))] + unsafe { + let mut stat = MaybeUninit::<Stat>::uninit(); + ret(c::stat(c_str(path), stat.as_mut_ptr()))?; + Ok(stat.assume_init()) + } +} + +pub(crate) fn lstat(path: &CStr) -> io::Result<Stat> { + // See the comments in `fstat` about using `crate::fs::statx` here. + #[cfg(all( + linux_kernel, + any( + target_pointer_width = "32", + target_arch = "mips64", + target_arch = "mips64r6" + ) + ))] + { + match crate::fs::statx( + crate::fs::CWD, + path, + AtFlags::SYMLINK_NOFOLLOW, + StatxFlags::BASIC_STATS, + ) { + Ok(x) => statx_to_stat(x), + Err(io::Errno::NOSYS) => statat_old(crate::fs::CWD, path, AtFlags::SYMLINK_NOFOLLOW), + Err(err) => Err(err), + } + } + + // Main version: libc is y2038 safe. Or, the platform is not y2038 safe and + // there's nothing practical we can do. + #[cfg(not(all( + linux_kernel, + any( + target_pointer_width = "32", + target_arch = "mips64", + target_arch = "mips64r6" + ) + )))] + unsafe { + let mut stat = MaybeUninit::<Stat>::uninit(); + ret(c::lstat(c_str(path), stat.as_mut_ptr()))?; + Ok(stat.assume_init()) + } +} + +#[cfg(not(any(target_os = "espidf", target_os = "redox")))] +pub(crate) fn statat(dirfd: BorrowedFd<'_>, path: &CStr, flags: AtFlags) -> io::Result<Stat> { + // See the comments in `fstat` about using `crate::fs::statx` here. + #[cfg(all( + linux_kernel, + any( + target_pointer_width = "32", + target_arch = "mips64", + target_arch = "mips64r6" + ) + ))] + { + match crate::fs::statx(dirfd, path, flags, StatxFlags::BASIC_STATS) { + Ok(x) => statx_to_stat(x), + Err(io::Errno::NOSYS) => statat_old(dirfd, path, flags), + Err(err) => Err(err), + } + } + + // Main version: libc is y2038 safe. Or, the platform is not y2038 safe and + // there's nothing practical we can do. + #[cfg(not(all( + linux_kernel, + any( + target_pointer_width = "32", + target_arch = "mips64", + target_arch = "mips64r6" + ) + )))] + unsafe { + let mut stat = MaybeUninit::<Stat>::uninit(); + ret(c::fstatat( + borrowed_fd(dirfd), + c_str(path), + stat.as_mut_ptr(), + bitflags_bits!(flags), + ))?; + Ok(stat.assume_init()) + } +} + +#[cfg(all( + linux_kernel, + any( + target_pointer_width = "32", + target_arch = "mips64", + target_arch = "mips64r6" + ) +))] +fn statat_old(dirfd: BorrowedFd<'_>, path: &CStr, flags: AtFlags) -> io::Result<Stat> { + unsafe { + let mut result = MaybeUninit::<c::stat64>::uninit(); + ret(c::fstatat( + borrowed_fd(dirfd), + c_str(path), + result.as_mut_ptr(), + bitflags_bits!(flags), + ))?; + stat64_to_stat(result.assume_init()) + } +} + +#[cfg(not(any(target_os = "espidf", target_os = "emscripten", target_os = "vita")))] +pub(crate) fn access(path: &CStr, access: Access) -> io::Result<()> { + unsafe { ret(c::access(c_str(path), access.bits())) } +} + +#[cfg(not(any( + target_os = "emscripten", + target_os = "espidf", + target_os = "redox", + target_os = "vita" +)))] +pub(crate) fn accessat( + dirfd: BorrowedFd<'_>, + path: &CStr, + access: Access, + flags: AtFlags, +) -> io::Result<()> { + // macOS <= 10.9 lacks `faccessat`. + #[cfg(target_os = "macos")] + unsafe { + weak! { + fn faccessat( + c::c_int, + *const c::c_char, + c::c_int, + c::c_int + ) -> c::c_int + } + // If we have `faccessat`, use it. + if let Some(libc_faccessat) = faccessat.get() { + return ret(libc_faccessat( + borrowed_fd(dirfd), + c_str(path), + bitflags_bits!(access), + bitflags_bits!(flags), + )); + } + // Otherwise, see if we can emulate the `AT_FDCWD` case. + if borrowed_fd(dirfd) != c::AT_FDCWD { + return Err(io::Errno::NOSYS); + } + if flags.intersects(!(AtFlags::EACCESS | AtFlags::SYMLINK_NOFOLLOW)) { + return Err(io::Errno::INVAL); + } + if !flags.is_empty() { + return Err(io::Errno::OPNOTSUPP); + } + ret(c::access(c_str(path), bitflags_bits!(access))) + } + + #[cfg(not(target_os = "macos"))] + unsafe { + ret(c::faccessat( + borrowed_fd(dirfd), + c_str(path), + bitflags_bits!(access), + bitflags_bits!(flags), + )) + } +} + +#[cfg(target_os = "emscripten")] +pub(crate) fn access(_path: &CStr, _access: Access) -> io::Result<()> { + Ok(()) +} + +#[cfg(target_os = "emscripten")] +pub(crate) fn accessat( + _dirfd: BorrowedFd<'_>, + _path: &CStr, + _access: Access, + _flags: AtFlags, +) -> io::Result<()> { + Ok(()) +} + +#[cfg(not(any(target_os = "espidf", target_os = "redox", target_os = "vita")))] +pub(crate) fn utimensat( + dirfd: BorrowedFd<'_>, + path: &CStr, + times: &Timestamps, + flags: AtFlags, +) -> io::Result<()> { + // Old 32-bit version: libc has `utimensat` but it is not y2038 safe by + // default. But there may be a `__utimensat16` we can use. + #[cfg(fix_y2038)] + { + #[cfg(target_env = "gnu")] + if let Some(libc_utimensat) = __utimensat64.get() { + let libc_times: [LibcTimespec; 2] = [ + times.last_access.clone().into(), + times.last_modification.clone().into(), + ]; + + unsafe { + return ret(libc_utimensat( + borrowed_fd(dirfd), + c_str(path), + libc_times.as_ptr(), + bitflags_bits!(flags), + )); + } + } + + utimensat_old(dirfd, path, times, flags) + } + + // Main version: libc is y2038 safe and has `utimensat`. Or, the platform + // is not y2038 safe and there's nothing practical we can do. + #[cfg(not(any(apple, fix_y2038)))] + unsafe { + use crate::utils::as_ptr; + + ret(c::utimensat( + borrowed_fd(dirfd), + c_str(path), + as_ptr(times).cast(), + bitflags_bits!(flags), + )) + } + + // Apple version: `utimensat` was introduced in macOS 10.13. + #[cfg(apple)] + unsafe { + use crate::utils::as_ptr; + + // ABI details + weak! { + fn utimensat( + c::c_int, + *const c::c_char, + *const c::timespec, + c::c_int + ) -> c::c_int + } + extern "C" { + fn setattrlist( + path: *const c::c_char, + attr_list: *const Attrlist, + attr_buf: *const c::c_void, + attr_buf_size: c::size_t, + options: c::c_ulong, + ) -> c::c_int; + } + const FSOPT_NOFOLLOW: c::c_ulong = 0x0000_0001; + + // If we have `utimensat`, use it. + if let Some(have_utimensat) = utimensat.get() { + return ret(have_utimensat( + borrowed_fd(dirfd), + c_str(path), + as_ptr(times).cast(), + bitflags_bits!(flags), + )); + } + + // `setattrlistat` was introduced in 10.13 along with `utimensat`, so + // if we don't have `utimensat`, we don't have `setattrlistat` either. + // Emulate it using `fork`, and `fchdir` and [`setattrlist`]. + // + // [`setattrlist`]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/setattrlist.2.html + match c::fork() { + -1 => Err(io::Errno::IO), + 0 => { + if c::fchdir(borrowed_fd(dirfd)) != 0 { + let code = match libc_errno::errno().0 { + c::EACCES => 2, + c::ENOTDIR => 3, + _ => 1, + }; + c::_exit(code); + } + + let mut flags_arg = 0; + if flags.contains(AtFlags::SYMLINK_NOFOLLOW) { + flags_arg |= FSOPT_NOFOLLOW; + } + + let (attrbuf_size, times, attrs) = times_to_attrlist(times); + + if setattrlist( + c_str(path), + &attrs, + as_ptr(×).cast(), + attrbuf_size, + flags_arg, + ) != 0 + { + // Translate expected `errno` codes into ad-hoc integer + // values suitable for exit statuses. + let code = match libc_errno::errno().0 { + c::EACCES => 2, + c::ENOTDIR => 3, + c::EPERM => 4, + c::EROFS => 5, + c::ELOOP => 6, + c::ENOENT => 7, + c::ENAMETOOLONG => 8, + c::EINVAL => 9, + c::ESRCH => 10, + c::ENOTSUP => 11, + _ => 1, + }; + c::_exit(code); + } + + c::_exit(0); + } + child_pid => { + let mut wstatus = 0; + let _ = ret_c_int(c::waitpid(child_pid, &mut wstatus, 0))?; + if c::WIFEXITED(wstatus) { + // Translate our ad-hoc exit statuses back to `errno` + // codes. + match c::WEXITSTATUS(wstatus) { + 0 => Ok(()), + 2 => Err(io::Errno::ACCESS), + 3 => Err(io::Errno::NOTDIR), + 4 => Err(io::Errno::PERM), + 5 => Err(io::Errno::ROFS), + 6 => Err(io::Errno::LOOP), + 7 => Err(io::Errno::NOENT), + 8 => Err(io::Errno::NAMETOOLONG), + 9 => Err(io::Errno::INVAL), + 10 => Err(io::Errno::SRCH), + 11 => Err(io::Errno::NOTSUP), + _ => Err(io::Errno::IO), + } + } else { + Err(io::Errno::IO) + } + } + } + } +} + +#[cfg(fix_y2038)] +fn utimensat_old( + dirfd: BorrowedFd<'_>, + path: &CStr, + times: &Timestamps, + flags: AtFlags, +) -> io::Result<()> { + let old_times = [ + c::timespec { + tv_sec: times + .last_access + .tv_sec + .try_into() + .map_err(|_| io::Errno::OVERFLOW)?, + tv_nsec: times + .last_access + .tv_nsec + .try_into() + .map_err(|_| io::Errno::OVERFLOW)?, + }, + c::timespec { + tv_sec: times + .last_modification + .tv_sec + .try_into() + .map_err(|_| io::Errno::OVERFLOW)?, + tv_nsec: times + .last_modification + .tv_nsec + .try_into() + .map_err(|_| io::Errno::OVERFLOW)?, + }, + ]; + unsafe { + ret(c::utimensat( + borrowed_fd(dirfd), + c_str(path), + old_times.as_ptr(), + bitflags_bits!(flags), + )) + } +} + +#[cfg(not(target_os = "wasi"))] +pub(crate) fn chmod(path: &CStr, mode: Mode) -> io::Result<()> { + unsafe { ret(c::chmod(c_str(path), mode.bits() as c::mode_t)) } +} + +#[cfg(not(any( + linux_kernel, + target_os = "espidf", + target_os = "redox", + target_os = "wasi" +)))] +pub(crate) fn chmodat( + dirfd: BorrowedFd<'_>, + path: &CStr, + mode: Mode, + flags: AtFlags, +) -> io::Result<()> { + unsafe { + ret(c::fchmodat( + borrowed_fd(dirfd), + c_str(path), + mode.bits() as c::mode_t, + bitflags_bits!(flags), + )) + } +} + +#[cfg(linux_kernel)] +pub(crate) fn chmodat( + dirfd: BorrowedFd<'_>, + path: &CStr, + mode: Mode, + flags: AtFlags, +) -> io::Result<()> { + // Linux's `fchmodat` does not have a flags argument. + // + // Use `c::syscall` rather than `c::fchmodat` because some libc + // implementations, such as musl, add extra logic to `fchmod` to emulate + // support for `AT_SYMLINK_NOFOLLOW`, which uses `/proc` outside our + // control. + syscall! { + fn fchmodat( + base_dirfd: c::c_int, + pathname: *const c::c_char, + mode: c::mode_t + ) via SYS_fchmodat -> c::c_int + } + if flags == AtFlags::SYMLINK_NOFOLLOW { + return Err(io::Errno::OPNOTSUPP); + } + if !flags.is_empty() { + return Err(io::Errno::INVAL); + } + unsafe { + ret(fchmodat( + borrowed_fd(dirfd), + c_str(path), + mode.bits() as c::mode_t, + )) + } +} + +#[cfg(apple)] +pub(crate) fn fclonefileat( + srcfd: BorrowedFd<'_>, + dst_dirfd: BorrowedFd<'_>, + dst: &CStr, + flags: CloneFlags, +) -> io::Result<()> { + syscall! { + fn fclonefileat( + srcfd: BorrowedFd<'_>, + dst_dirfd: BorrowedFd<'_>, + dst: *const c::c_char, + flags: c::c_int + ) via SYS_fclonefileat -> c::c_int + } + + unsafe { + ret(fclonefileat( + srcfd, + dst_dirfd, + c_str(dst), + bitflags_bits!(flags), + )) + } +} + +#[cfg(not(any(target_os = "espidf", target_os = "redox", target_os = "wasi")))] +pub(crate) fn chownat( + dirfd: BorrowedFd<'_>, + path: &CStr, + owner: Option<Uid>, + group: Option<Gid>, + flags: AtFlags, +) -> io::Result<()> { + unsafe { + let (ow, gr) = crate::ugid::translate_fchown_args(owner, group); + ret(c::fchownat( + borrowed_fd(dirfd), + c_str(path), + ow, + gr, + bitflags_bits!(flags), + )) + } +} + +#[cfg(not(any( + apple, + target_os = "espidf", + target_os = "redox", + target_os = "vita", + target_os = "wasi" +)))] +pub(crate) fn mknodat( + dirfd: BorrowedFd<'_>, + path: &CStr, + file_type: FileType, + mode: Mode, + dev: Dev, +) -> io::Result<()> { + unsafe { + ret(c::mknodat( + borrowed_fd(dirfd), + c_str(path), + (mode.bits() | file_type.as_raw_mode()) as c::mode_t, + dev.try_into().map_err(|_e| io::Errno::PERM)?, + )) + } +} + +#[cfg(linux_kernel)] +pub(crate) fn copy_file_range( + fd_in: BorrowedFd<'_>, + off_in: Option<&mut u64>, + fd_out: BorrowedFd<'_>, + off_out: Option<&mut u64>, + len: usize, +) -> io::Result<usize> { + syscall! { + fn copy_file_range( + fd_in: c::c_int, + off_in: *mut c::loff_t, + fd_out: c::c_int, + off_out: *mut c::loff_t, + len: usize, + flags: c::c_uint + ) via SYS_copy_file_range -> c::ssize_t + } + + let mut off_in_val: c::loff_t = 0; + let mut off_out_val: c::loff_t = 0; + // Silently cast; we'll get `EINVAL` if the value is negative. + let off_in_ptr = if let Some(off_in) = &off_in { + off_in_val = **off_in as i64; + &mut off_in_val + } else { + null_mut() + }; + let off_out_ptr = if let Some(off_out) = &off_out { + off_out_val = **off_out as i64; + &mut off_out_val + } else { + null_mut() + }; + let copied = unsafe { + ret_usize(copy_file_range( + borrowed_fd(fd_in), + off_in_ptr, + borrowed_fd(fd_out), + off_out_ptr, + len, + 0, // no flags are defined yet + ))? + }; + if let Some(off_in) = off_in { + *off_in = off_in_val as u64; + } + if let Some(off_out) = off_out { + *off_out = off_out_val as u64; + } + Ok(copied) +} + +#[cfg(not(any( + apple, + netbsdlike, + solarish, + target_os = "dragonfly", + target_os = "espidf", + target_os = "haiku", + target_os = "redox", + target_os = "vita", +)))] +pub(crate) fn fadvise(fd: BorrowedFd<'_>, offset: u64, len: u64, advice: Advice) -> io::Result<()> { + let offset = offset as i64; + let len = len as i64; + + // FreeBSD returns `EINVAL` on invalid offsets; emulate the POSIX behavior. + #[cfg(target_os = "freebsd")] + let offset = if (offset as i64) < 0 { + i64::MAX + } else { + offset + }; + + // FreeBSD returns `EINVAL` on overflow; emulate the POSIX behavior. + #[cfg(target_os = "freebsd")] + let len = if len > 0 && offset.checked_add(len).is_none() { + i64::MAX - offset + } else { + len + }; + + let err = unsafe { c::posix_fadvise(borrowed_fd(fd), offset, len, advice as c::c_int) }; + + // `posix_fadvise` returns its error status rather than using `errno`. + if err == 0 { + Ok(()) + } else { + Err(io::Errno(err)) + } +} + +pub(crate) fn fcntl_getfl(fd: BorrowedFd<'_>) -> io::Result<OFlags> { + let flags = unsafe { ret_c_int(c::fcntl(borrowed_fd(fd), c::F_GETFL))? }; + Ok(OFlags::from_bits_retain(bitcast!(flags))) +} + +pub(crate) fn fcntl_setfl(fd: BorrowedFd<'_>, flags: OFlags) -> io::Result<()> { + unsafe { ret(c::fcntl(borrowed_fd(fd), c::F_SETFL, flags.bits())) } +} + +#[cfg(any(linux_kernel, target_os = "freebsd", target_os = "fuchsia"))] +pub(crate) fn fcntl_get_seals(fd: BorrowedFd<'_>) -> io::Result<SealFlags> { + let flags = unsafe { ret_c_int(c::fcntl(borrowed_fd(fd), c::F_GET_SEALS))? }; + Ok(SealFlags::from_bits_retain(bitcast!(flags))) +} + +#[cfg(any(linux_kernel, target_os = "freebsd", target_os = "fuchsia"))] +pub(crate) fn fcntl_add_seals(fd: BorrowedFd<'_>, seals: SealFlags) -> io::Result<()> { + unsafe { ret(c::fcntl(borrowed_fd(fd), c::F_ADD_SEALS, seals.bits())) } +} + +#[cfg(not(any( + target_os = "emscripten", + target_os = "espidf", + target_os = "fuchsia", + target_os = "redox", + target_os = "vita", + target_os = "wasi" +)))] +#[inline] +pub(crate) fn fcntl_lock(fd: BorrowedFd<'_>, operation: FlockOperation) -> io::Result<()> { + use c::{flock, F_RDLCK, F_SETLK, F_SETLKW, F_UNLCK, F_WRLCK, SEEK_SET}; + + let (cmd, l_type) = match operation { + FlockOperation::LockShared => (F_SETLKW, F_RDLCK), + FlockOperation::LockExclusive => (F_SETLKW, F_WRLCK), + FlockOperation::Unlock => (F_SETLKW, F_UNLCK), + FlockOperation::NonBlockingLockShared => (F_SETLK, F_RDLCK), + FlockOperation::NonBlockingLockExclusive => (F_SETLK, F_WRLCK), + FlockOperation::NonBlockingUnlock => (F_SETLK, F_UNLCK), + }; + + unsafe { + let mut lock: flock = core::mem::zeroed(); + lock.l_type = l_type as _; + + // When `l_len` is zero, this locks all the bytes from + // `l_whence`/`l_start` to the end of the file, even as the + // file grows dynamically. + lock.l_whence = SEEK_SET as _; + lock.l_start = 0; + lock.l_len = 0; + + ret(c::fcntl(borrowed_fd(fd), cmd, &lock)) + } +} + +pub(crate) fn seek(fd: BorrowedFd<'_>, pos: SeekFrom) -> io::Result<u64> { + let (whence, offset) = match pos { + SeekFrom::Start(pos) => { + let pos: u64 = pos; + // Silently cast; we'll get `EINVAL` if the value is negative. + (c::SEEK_SET, pos as i64) + } + SeekFrom::End(offset) => (c::SEEK_END, offset), + SeekFrom::Current(offset) => (c::SEEK_CUR, offset), + #[cfg(any(apple, freebsdlike, linux_kernel, solarish))] + SeekFrom::Data(offset) => (c::SEEK_DATA, offset), + #[cfg(any(apple, freebsdlike, linux_kernel, solarish))] + SeekFrom::Hole(offset) => (c::SEEK_HOLE, offset), + }; + + // ESP-IDF and Vita don't support 64-bit offsets. + #[cfg(any(target_os = "espidf", target_os = "vita"))] + let offset: i32 = offset.try_into().map_err(|_| io::Errno::OVERFLOW)?; + + let offset = unsafe { ret_off_t(c::lseek(borrowed_fd(fd), offset, whence))? }; + Ok(offset as u64) +} + +pub(crate) fn tell(fd: BorrowedFd<'_>) -> io::Result<u64> { + let offset = unsafe { ret_off_t(c::lseek(borrowed_fd(fd), 0, c::SEEK_CUR))? }; + Ok(offset as u64) +} + +#[cfg(not(any(linux_kernel, target_os = "wasi")))] +pub(crate) fn fchmod(fd: BorrowedFd<'_>, mode: Mode) -> io::Result<()> { + unsafe { ret(c::fchmod(borrowed_fd(fd), bitflags_bits!(mode))) } +} + +#[cfg(linux_kernel)] +pub(crate) fn fchmod(fd: BorrowedFd<'_>, mode: Mode) -> io::Result<()> { + // Use `c::syscall` rather than `c::fchmod` because some libc + // implementations, such as musl, add extra logic to `fchmod` to emulate + // support for `O_PATH`, which uses `/proc` outside our control and + // interferes with our own use of `O_PATH`. + syscall! { + fn fchmod( + fd: c::c_int, + mode: c::mode_t + ) via SYS_fchmod -> c::c_int + } + unsafe { ret(fchmod(borrowed_fd(fd), mode.bits() as c::mode_t)) } +} + +#[cfg(not(target_os = "wasi"))] +pub(crate) fn chown(path: &CStr, owner: Option<Uid>, group: Option<Gid>) -> io::Result<()> { + unsafe { + let (ow, gr) = crate::ugid::translate_fchown_args(owner, group); + ret(c::chown(c_str(path), ow, gr)) + } +} + +#[cfg(linux_kernel)] +pub(crate) fn fchown(fd: BorrowedFd<'_>, owner: Option<Uid>, group: Option<Gid>) -> io::Result<()> { + // Use `c::syscall` rather than `c::fchown` because some libc + // implementations, such as musl, add extra logic to `fchown` to emulate + // support for `O_PATH`, which uses `/proc` outside our control and + // interferes with our own use of `O_PATH`. + syscall! { + fn fchown( + fd: c::c_int, + owner: c::uid_t, + group: c::gid_t + ) via SYS_fchown -> c::c_int + } + unsafe { + let (ow, gr) = crate::ugid::translate_fchown_args(owner, group); + ret(fchown(borrowed_fd(fd), ow, gr)) + } +} + +#[cfg(not(any(linux_kernel, target_os = "wasi")))] +pub(crate) fn fchown(fd: BorrowedFd<'_>, owner: Option<Uid>, group: Option<Gid>) -> io::Result<()> { + unsafe { + let (ow, gr) = crate::ugid::translate_fchown_args(owner, group); + ret(c::fchown(borrowed_fd(fd), ow, gr)) + } +} + +#[cfg(not(any( + target_os = "espidf", + target_os = "solaris", + target_os = "vita", + target_os = "wasi" +)))] +pub(crate) fn flock(fd: BorrowedFd<'_>, operation: FlockOperation) -> io::Result<()> { + unsafe { ret(c::flock(borrowed_fd(fd), operation as c::c_int)) } +} + +#[cfg(linux_kernel)] +pub(crate) fn syncfs(fd: BorrowedFd<'_>) -> io::Result<()> { + // Some versions of Android libc lack a `syncfs` function. + #[cfg(target_os = "android")] + syscall! { + fn syncfs(fd: c::c_int) via SYS_syncfs -> c::c_int + } + + // `syncfs` was added to glibc in 2.20. + #[cfg(not(target_os = "android"))] + weak_or_syscall! { + fn syncfs(fd: c::c_int) via SYS_syncfs -> c::c_int + } + + unsafe { ret(syncfs(borrowed_fd(fd))) } +} + +#[cfg(not(any( + target_os = "espidf", + target_os = "redox", + target_os = "vita", + target_os = "wasi" +)))] +pub(crate) fn sync() { + unsafe { c::sync() } +} + +pub(crate) fn fstat(fd: BorrowedFd<'_>) -> io::Result<Stat> { + // 32-bit and mips64 Linux: `struct stat64` is not y2038 compatible; use + // `statx`. + // + // And, some old platforms don't support `statx`, and some fail with a + // confusing error code, so we call `crate::fs::statx` to handle that. If + // `statx` isn't available, fall back to the buggy system call. + #[cfg(all( + linux_kernel, + any( + target_pointer_width = "32", + target_arch = "mips64", + target_arch = "mips64r6" + ) + ))] + { + match crate::fs::statx(fd, cstr!(""), AtFlags::EMPTY_PATH, StatxFlags::BASIC_STATS) { + Ok(x) => statx_to_stat(x), + Err(io::Errno::NOSYS) => fstat_old(fd), + Err(err) => Err(err), + } + } + + // Main version: libc is y2038 safe. Or, the platform is not y2038 safe and + // there's nothing practical we can do. + #[cfg(not(all( + linux_kernel, + any( + target_pointer_width = "32", + target_arch = "mips64", + target_arch = "mips64r6" + ) + )))] + unsafe { + let mut stat = MaybeUninit::<Stat>::uninit(); + ret(c::fstat(borrowed_fd(fd), stat.as_mut_ptr()))?; + Ok(stat.assume_init()) + } +} + +#[cfg(all( + linux_kernel, + any( + target_pointer_width = "32", + target_arch = "mips64", + target_arch = "mips64r6" + ) +))] +fn fstat_old(fd: BorrowedFd<'_>) -> io::Result<Stat> { + unsafe { + let mut result = MaybeUninit::<c::stat64>::uninit(); + ret(c::fstat(borrowed_fd(fd), result.as_mut_ptr()))?; + stat64_to_stat(result.assume_init()) + } +} + +#[cfg(not(any( + solarish, + target_os = "espidf", + target_os = "haiku", + target_os = "netbsd", + target_os = "nto", + target_os = "redox", + target_os = "vita", + target_os = "wasi", +)))] +pub(crate) fn fstatfs(fd: BorrowedFd<'_>) -> io::Result<StatFs> { + let mut statfs = MaybeUninit::<StatFs>::uninit(); + unsafe { + ret(c::fstatfs(borrowed_fd(fd), statfs.as_mut_ptr()))?; + Ok(statfs.assume_init()) + } +} + +#[cfg(not(any(target_os = "haiku", target_os = "redox", target_os = "wasi")))] +pub(crate) fn fstatvfs(fd: BorrowedFd<'_>) -> io::Result<StatVfs> { + let mut statvfs = MaybeUninit::<c::statvfs>::uninit(); + unsafe { + ret(c::fstatvfs(borrowed_fd(fd), statvfs.as_mut_ptr()))?; + Ok(libc_statvfs_to_statvfs(statvfs.assume_init())) + } +} + +#[cfg(not(any(target_os = "haiku", target_os = "redox", target_os = "wasi")))] +fn libc_statvfs_to_statvfs(from: c::statvfs) -> StatVfs { + StatVfs { + f_bsize: from.f_bsize as u64, + f_frsize: from.f_frsize as u64, + f_blocks: from.f_blocks as u64, + f_bfree: from.f_bfree as u64, + f_bavail: from.f_bavail as u64, + f_files: from.f_files as u64, + f_ffree: from.f_ffree as u64, + f_favail: from.f_ffree as u64, + #[cfg(not(target_os = "aix"))] + f_fsid: from.f_fsid as u64, + #[cfg(target_os = "aix")] + f_fsid: ((from.f_fsid.val[0] as u64) << 32) | from.f_fsid.val[1], + f_flag: StatVfsMountFlags::from_bits_retain(from.f_flag as u64), + f_namemax: from.f_namemax as u64, + } +} + +#[cfg(not(any(target_os = "espidf", target_os = "vita")))] +pub(crate) fn futimens(fd: BorrowedFd<'_>, times: &Timestamps) -> io::Result<()> { + // Old 32-bit version: libc has `futimens` but it is not y2038 safe by + // default. But there may be a `__futimens64` we can use. + #[cfg(fix_y2038)] + { + #[cfg(target_env = "gnu")] + if let Some(libc_futimens) = __futimens64.get() { + let libc_times: [LibcTimespec; 2] = [ + times.last_access.clone().into(), + times.last_modification.clone().into(), + ]; + + unsafe { + return ret(libc_futimens(borrowed_fd(fd), libc_times.as_ptr())); + } + } + + futimens_old(fd, times) + } + + // Main version: libc is y2038 safe and has `futimens`. Or, the platform + // is not y2038 safe and there's nothing practical we can do. + #[cfg(not(any(apple, fix_y2038)))] + unsafe { + use crate::utils::as_ptr; + + ret(c::futimens(borrowed_fd(fd), as_ptr(times).cast())) + } + + // Apple version: `futimens` was introduced in macOS 10.13. + #[cfg(apple)] + unsafe { + use crate::utils::as_ptr; + + // ABI details. + weak! { + fn futimens(c::c_int, *const c::timespec) -> c::c_int + } + extern "C" { + fn fsetattrlist( + fd: c::c_int, + attr_list: *const Attrlist, + attr_buf: *const c::c_void, + attr_buf_size: c::size_t, + options: c::c_ulong, + ) -> c::c_int; + } + + // If we have `futimens`, use it. + if let Some(have_futimens) = futimens.get() { + return ret(have_futimens(borrowed_fd(fd), as_ptr(times).cast())); + } + + // Otherwise use `fsetattrlist`. + let (attrbuf_size, times, attrs) = times_to_attrlist(times); + + ret(fsetattrlist( + borrowed_fd(fd), + &attrs, + as_ptr(×).cast(), + attrbuf_size, + 0, + )) + } +} + +#[cfg(fix_y2038)] +fn futimens_old(fd: BorrowedFd<'_>, times: &Timestamps) -> io::Result<()> { + let old_times = [ + c::timespec { + tv_sec: times + .last_access + .tv_sec + .try_into() + .map_err(|_| io::Errno::OVERFLOW)?, + tv_nsec: times + .last_access + .tv_nsec + .try_into() + .map_err(|_| io::Errno::OVERFLOW)?, + }, + c::timespec { + tv_sec: times + .last_modification + .tv_sec + .try_into() + .map_err(|_| io::Errno::OVERFLOW)?, + tv_nsec: times + .last_modification + .tv_nsec + .try_into() + .map_err(|_| io::Errno::OVERFLOW)?, + }, + ]; + + unsafe { ret(c::futimens(borrowed_fd(fd), old_times.as_ptr())) } +} + +#[cfg(not(any( + apple, + netbsdlike, + solarish, + target_os = "aix", + target_os = "dragonfly", + target_os = "espidf", + target_os = "nto", + target_os = "redox", + target_os = "vita", +)))] +pub(crate) fn fallocate( + fd: BorrowedFd<'_>, + mode: FallocateFlags, + offset: u64, + len: u64, +) -> io::Result<()> { + // Silently cast; we'll get `EINVAL` if the value is negative. + let offset = offset as i64; + let len = len as i64; + + #[cfg(any(linux_kernel, target_os = "fuchsia"))] + unsafe { + ret(c::fallocate( + borrowed_fd(fd), + bitflags_bits!(mode), + offset, + len, + )) + } + + #[cfg(not(any(linux_kernel, target_os = "fuchsia")))] + { + assert!(mode.is_empty()); + let err = unsafe { c::posix_fallocate(borrowed_fd(fd), offset, len) }; + + // `posix_fallocate` returns its error status rather than using + // `errno`. + if err == 0 { + Ok(()) + } else { + Err(io::Errno(err)) + } + } +} + +#[cfg(apple)] +pub(crate) fn fallocate( + fd: BorrowedFd<'_>, + mode: FallocateFlags, + offset: u64, + len: u64, +) -> io::Result<()> { + let offset: i64 = offset.try_into().map_err(|_e| io::Errno::INVAL)?; + let len = len as i64; + + assert!(mode.is_empty()); + + let new_len = offset.checked_add(len).ok_or(io::Errno::FBIG)?; + let mut store = c::fstore_t { + fst_flags: c::F_ALLOCATECONTIG, + fst_posmode: c::F_PEOFPOSMODE, + fst_offset: 0, + fst_length: new_len, + fst_bytesalloc: 0, + }; + unsafe { + if c::fcntl(borrowed_fd(fd), c::F_PREALLOCATE, &store) == -1 { + // Unable to allocate contiguous disk space; attempt to allocate + // non-contiguously. + store.fst_flags = c::F_ALLOCATEALL; + let _ = ret_c_int(c::fcntl(borrowed_fd(fd), c::F_PREALLOCATE, &store))?; + } + ret(c::ftruncate(borrowed_fd(fd), new_len)) + } +} + +pub(crate) fn fsync(fd: BorrowedFd<'_>) -> io::Result<()> { + unsafe { ret(c::fsync(borrowed_fd(fd))) } +} + +#[cfg(not(any( + apple, + target_os = "dragonfly", + target_os = "espidf", + target_os = "haiku", + target_os = "redox", + target_os = "vita", +)))] +pub(crate) fn fdatasync(fd: BorrowedFd<'_>) -> io::Result<()> { + unsafe { ret(c::fdatasync(borrowed_fd(fd))) } +} + +pub(crate) fn ftruncate(fd: BorrowedFd<'_>, length: u64) -> io::Result<()> { + let length = length.try_into().map_err(|_overflow_err| io::Errno::FBIG)?; + unsafe { ret(c::ftruncate(borrowed_fd(fd), length)) } +} + +#[cfg(any(linux_kernel, target_os = "freebsd"))] +pub(crate) fn memfd_create(name: &CStr, flags: MemfdFlags) -> io::Result<OwnedFd> { + #[cfg(target_os = "freebsd")] + weakcall! { + fn memfd_create( + name: *const c::c_char, + flags: c::c_uint + ) -> c::c_int + } + + #[cfg(linux_kernel)] + weak_or_syscall! { + fn memfd_create( + name: *const c::c_char, + flags: c::c_uint + ) via SYS_memfd_create -> c::c_int + } + + unsafe { ret_owned_fd(memfd_create(c_str(name), bitflags_bits!(flags))) } +} + +#[cfg(linux_kernel)] +pub(crate) fn openat2( + dirfd: BorrowedFd<'_>, + path: &CStr, + oflags: OFlags, + mode: Mode, + resolve: ResolveFlags, +) -> io::Result<OwnedFd> { + use linux_raw_sys::general::open_how; + + syscall! { + fn openat2( + base_dirfd: c::c_int, + pathname: *const c::c_char, + how: *mut open_how, + size: usize + ) via SYS_OPENAT2 -> c::c_int + } + + let oflags = oflags.bits(); + let mut open_how = open_how { + flags: u64::from(oflags), + mode: u64::from(mode.bits()), + resolve: resolve.bits(), + }; + + unsafe { + ret_owned_fd(openat2( + borrowed_fd(dirfd), + c_str(path), + &mut open_how, + size_of::<open_how>(), + )) + } +} +#[cfg(all(linux_kernel, target_pointer_width = "32"))] +const SYS_OPENAT2: i32 = 437; +#[cfg(all(linux_kernel, target_pointer_width = "64"))] +const SYS_OPENAT2: i64 = 437; + +#[cfg(target_os = "linux")] +pub(crate) fn sendfile( + out_fd: BorrowedFd<'_>, + in_fd: BorrowedFd<'_>, + offset: Option<&mut u64>, + count: usize, +) -> io::Result<usize> { + unsafe { + ret_usize(c::sendfile64( + borrowed_fd(out_fd), + borrowed_fd(in_fd), + offset.map_or(null_mut(), crate::utils::as_mut_ptr).cast(), + count, + )) + } +} + +/// Convert from a Linux `statx` value to rustix's `Stat`. +#[cfg(all(linux_kernel, target_pointer_width = "32"))] +fn statx_to_stat(x: crate::fs::Statx) -> io::Result<Stat> { + Ok(Stat { + st_dev: crate::fs::makedev(x.stx_dev_major, x.stx_dev_minor).into(), + st_mode: x.stx_mode.into(), + st_nlink: x.stx_nlink.into(), + st_uid: x.stx_uid.into(), + st_gid: x.stx_gid.into(), + st_rdev: crate::fs::makedev(x.stx_rdev_major, x.stx_rdev_minor).into(), + st_size: x.stx_size.try_into().map_err(|_| io::Errno::OVERFLOW)?, + st_blksize: x.stx_blksize.into(), + st_blocks: x.stx_blocks.into(), + st_atime: x + .stx_atime + .tv_sec + .try_into() + .map_err(|_| io::Errno::OVERFLOW)?, + st_atime_nsec: x.stx_atime.tv_nsec as _, + st_mtime: x + .stx_mtime + .tv_sec + .try_into() + .map_err(|_| io::Errno::OVERFLOW)?, + st_mtime_nsec: x.stx_mtime.tv_nsec as _, + st_ctime: x + .stx_ctime + .tv_sec + .try_into() + .map_err(|_| io::Errno::OVERFLOW)?, + st_ctime_nsec: x.stx_ctime.tv_nsec as _, + st_ino: x.stx_ino.into(), + }) +} + +/// Convert from a Linux `statx` value to rustix's `Stat`. +/// +/// mips64' `struct stat64` in libc has private fields, and `stx_blocks` +#[cfg(all(linux_kernel, any(target_arch = "mips64", target_arch = "mips64r6")))] +fn statx_to_stat(x: crate::fs::Statx) -> io::Result<Stat> { + let mut result: Stat = unsafe { core::mem::zeroed() }; + + result.st_dev = crate::fs::makedev(x.stx_dev_major, x.stx_dev_minor); + result.st_mode = x.stx_mode.into(); + result.st_nlink = x.stx_nlink.into(); + result.st_uid = x.stx_uid.into(); + result.st_gid = x.stx_gid.into(); + result.st_rdev = crate::fs::makedev(x.stx_rdev_major, x.stx_rdev_minor); + result.st_size = x.stx_size.try_into().map_err(|_| io::Errno::OVERFLOW)?; + result.st_blksize = x.stx_blksize.into(); + result.st_blocks = x.stx_blocks.try_into().map_err(|_e| io::Errno::OVERFLOW)?; + result.st_atime = x + .stx_atime + .tv_sec + .try_into() + .map_err(|_| io::Errno::OVERFLOW)?; + result.st_atime_nsec = x.stx_atime.tv_nsec as _; + result.st_mtime = x + .stx_mtime + .tv_sec + .try_into() + .map_err(|_| io::Errno::OVERFLOW)?; + result.st_mtime_nsec = x.stx_mtime.tv_nsec as _; + result.st_ctime = x + .stx_ctime + .tv_sec + .try_into() + .map_err(|_| io::Errno::OVERFLOW)?; + result.st_ctime_nsec = x.stx_ctime.tv_nsec as _; + result.st_ino = x.stx_ino.into(); + + Ok(result) +} + +/// Convert from a Linux `stat64` value to rustix's `Stat`. +#[cfg(all(linux_kernel, target_pointer_width = "32"))] +fn stat64_to_stat(s64: c::stat64) -> io::Result<Stat> { + Ok(Stat { + st_dev: s64.st_dev.try_into().map_err(|_| io::Errno::OVERFLOW)?, + st_mode: s64.st_mode.try_into().map_err(|_| io::Errno::OVERFLOW)?, + st_nlink: s64.st_nlink.try_into().map_err(|_| io::Errno::OVERFLOW)?, + st_uid: s64.st_uid.try_into().map_err(|_| io::Errno::OVERFLOW)?, + st_gid: s64.st_gid.try_into().map_err(|_| io::Errno::OVERFLOW)?, + st_rdev: s64.st_rdev.try_into().map_err(|_| io::Errno::OVERFLOW)?, + st_size: s64.st_size.try_into().map_err(|_| io::Errno::OVERFLOW)?, + st_blksize: s64.st_blksize.try_into().map_err(|_| io::Errno::OVERFLOW)?, + st_blocks: s64.st_blocks.try_into().map_err(|_| io::Errno::OVERFLOW)?, + st_atime: s64.st_atime.try_into().map_err(|_| io::Errno::OVERFLOW)?, + st_atime_nsec: s64 + .st_atime_nsec + .try_into() + .map_err(|_| io::Errno::OVERFLOW)?, + st_mtime: s64.st_mtime.try_into().map_err(|_| io::Errno::OVERFLOW)?, + st_mtime_nsec: s64 + .st_mtime_nsec + .try_into() + .map_err(|_| io::Errno::OVERFLOW)?, + st_ctime: s64.st_ctime.try_into().map_err(|_| io::Errno::OVERFLOW)?, + st_ctime_nsec: s64 + .st_ctime_nsec + .try_into() + .map_err(|_| io::Errno::OVERFLOW)?, + st_ino: s64.st_ino.try_into().map_err(|_| io::Errno::OVERFLOW)?, + }) +} + +/// Convert from a Linux `stat64` value to rustix's `Stat`. +/// +/// mips64' `struct stat64` in libc has private fields, and `st_blocks` has +/// type `i64`. +#[cfg(all(linux_kernel, any(target_arch = "mips64", target_arch = "mips64r6")))] +fn stat64_to_stat(s64: c::stat64) -> io::Result<Stat> { + let mut result: Stat = unsafe { core::mem::zeroed() }; + + result.st_dev = s64.st_dev.try_into().map_err(|_| io::Errno::OVERFLOW)?; + result.st_mode = s64.st_mode.try_into().map_err(|_| io::Errno::OVERFLOW)?; + result.st_nlink = s64.st_nlink.try_into().map_err(|_| io::Errno::OVERFLOW)?; + result.st_uid = s64.st_uid.try_into().map_err(|_| io::Errno::OVERFLOW)?; + result.st_gid = s64.st_gid.try_into().map_err(|_| io::Errno::OVERFLOW)?; + result.st_rdev = s64.st_rdev.try_into().map_err(|_| io::Errno::OVERFLOW)?; + result.st_size = s64.st_size.try_into().map_err(|_| io::Errno::OVERFLOW)?; + result.st_blksize = s64.st_blksize.try_into().map_err(|_| io::Errno::OVERFLOW)?; + result.st_blocks = s64.st_blocks.try_into().map_err(|_| io::Errno::OVERFLOW)?; + result.st_atime = s64.st_atime.try_into().map_err(|_| io::Errno::OVERFLOW)?; + result.st_atime_nsec = s64 + .st_atime_nsec + .try_into() + .map_err(|_| io::Errno::OVERFLOW)?; + result.st_mtime = s64.st_mtime.try_into().map_err(|_| io::Errno::OVERFLOW)?; + result.st_mtime_nsec = s64 + .st_mtime_nsec + .try_into() + .map_err(|_| io::Errno::OVERFLOW)?; + result.st_ctime = s64.st_ctime.try_into().map_err(|_| io::Errno::OVERFLOW)?; + result.st_ctime_nsec = s64 + .st_ctime_nsec + .try_into() + .map_err(|_| io::Errno::OVERFLOW)?; + result.st_ino = s64.st_ino.try_into().map_err(|_| io::Errno::OVERFLOW)?; + + Ok(result) +} + +#[cfg(linux_kernel)] +#[allow(non_upper_case_globals)] +mod sys { + use super::{c, BorrowedFd, Statx}; + + weak_or_syscall! { + pub(super) fn statx( + dirfd_: BorrowedFd<'_>, + path: *const c::c_char, + flags: c::c_int, + mask: c::c_uint, + buf: *mut Statx + ) via SYS_statx -> c::c_int + } +} + +#[cfg(linux_kernel)] +#[allow(non_upper_case_globals)] +pub(crate) fn statx( + dirfd: BorrowedFd<'_>, + path: &CStr, + flags: AtFlags, + mask: StatxFlags, +) -> io::Result<Statx> { + // If a future Linux kernel adds more fields to `struct statx` and users + // passing flags unknown to rustix in `StatxFlags`, we could end up + // writing outside of the buffer. To prevent this possibility, we mask off + // any flags that we don't know about. + // + // This includes `STATX__RESERVED`, which has a value that we know, but + // which could take on arbitrary new meaning in the future. Linux currently + // rejects this flag with `EINVAL`, so we do the same. + // + // This doesn't rely on `STATX_ALL` because [it's deprecated] and already + // doesn't represent all the known flags. + // + // [it's deprecated]: https://patchwork.kernel.org/project/linux-fsdevel/patch/20200505095915.11275-7-mszeredi@redhat.com/ + #[cfg(not(any(target_os = "android", target_env = "musl")))] + const STATX__RESERVED: u32 = c::STATX__RESERVED as u32; + #[cfg(any(target_os = "android", target_env = "musl"))] + const STATX__RESERVED: u32 = linux_raw_sys::general::STATX__RESERVED; + if (mask.bits() & STATX__RESERVED) == STATX__RESERVED { + return Err(io::Errno::INVAL); + } + let mask = mask & StatxFlags::all(); + + let mut statx_buf = MaybeUninit::<Statx>::uninit(); + unsafe { + ret(sys::statx( + dirfd, + c_str(path), + bitflags_bits!(flags), + mask.bits(), + statx_buf.as_mut_ptr(), + ))?; + Ok(statx_buf.assume_init()) + } +} + +#[cfg(linux_kernel)] +#[inline] +pub(crate) fn is_statx_available() -> bool { + unsafe { + // Call `statx` with null pointers so that if it fails for any reason + // other than `EFAULT`, we know it's not supported. + matches!( + ret(sys::statx(CWD, null(), 0, 0, null_mut())), + Err(io::Errno::FAULT) + ) + } +} + +#[cfg(apple)] +pub(crate) unsafe fn fcopyfile( + from: BorrowedFd<'_>, + to: BorrowedFd<'_>, + state: copyfile_state_t, + flags: CopyfileFlags, +) -> io::Result<()> { + extern "C" { + fn fcopyfile( + from: c::c_int, + to: c::c_int, + state: copyfile_state_t, + flags: c::c_uint, + ) -> c::c_int; + } + + nonnegative_ret(fcopyfile( + borrowed_fd(from), + borrowed_fd(to), + state, + bitflags_bits!(flags), + )) +} + +#[cfg(apple)] +pub(crate) fn copyfile_state_alloc() -> io::Result<copyfile_state_t> { + extern "C" { + fn copyfile_state_alloc() -> copyfile_state_t; + } + + let result = unsafe { copyfile_state_alloc() }; + if result.0.is_null() { + Err(io::Errno::last_os_error()) + } else { + Ok(result) + } +} + +#[cfg(apple)] +pub(crate) unsafe fn copyfile_state_free(state: copyfile_state_t) -> io::Result<()> { + extern "C" { + fn copyfile_state_free(state: copyfile_state_t) -> c::c_int; + } + + nonnegative_ret(copyfile_state_free(state)) +} + +#[cfg(apple)] +const COPYFILE_STATE_COPIED: u32 = 8; + +#[cfg(apple)] +pub(crate) unsafe fn copyfile_state_get_copied(state: copyfile_state_t) -> io::Result<u64> { + let mut copied = MaybeUninit::<u64>::uninit(); + copyfile_state_get(state, COPYFILE_STATE_COPIED, copied.as_mut_ptr().cast())?; + Ok(copied.assume_init()) +} + +#[cfg(apple)] +pub(crate) unsafe fn copyfile_state_get( + state: copyfile_state_t, + flag: u32, + dst: *mut c::c_void, +) -> io::Result<()> { + extern "C" { + fn copyfile_state_get(state: copyfile_state_t, flag: u32, dst: *mut c::c_void) -> c::c_int; + } + + nonnegative_ret(copyfile_state_get(state, flag, dst)) +} + +#[cfg(all(apple, feature = "alloc"))] +pub(crate) fn getpath(fd: BorrowedFd<'_>) -> io::Result<CString> { + // The use of `PATH_MAX` is generally not encouraged, but it + // is inevitable in this case because macOS defines `fcntl` with + // `F_GETPATH` in terms of `MAXPATHLEN`, and there are no + // alternatives. If a better method is invented, it should be used + // instead. + let mut buf = vec![0; c::PATH_MAX as usize]; + + // From the [macOS `fcntl` manual page]: + // `F_GETPATH` - Get the path of the file descriptor `Fildes`. The argument + // must be a buffer of size `MAXPATHLEN` or greater. + // + // [macOS `fcntl` manual page]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/fcntl.2.html + unsafe { + ret(c::fcntl(borrowed_fd(fd), c::F_GETPATH, buf.as_mut_ptr()))?; + } + + let l = buf.iter().position(|&c| c == 0).unwrap(); + buf.truncate(l); + buf.shrink_to_fit(); + + Ok(CString::new(buf).unwrap()) +} + +#[cfg(apple)] +pub(crate) fn fcntl_rdadvise(fd: BorrowedFd<'_>, offset: u64, len: u64) -> io::Result<()> { + // From the [macOS `fcntl` manual page]: + // `F_RDADVISE` - Issue an advisory read async with no copy to user. + // + // The `F_RDADVISE` command operates on the following structure which holds + // information passed from the user to the system: + // + // ```c + // struct radvisory { + // off_t ra_offset; /* offset into the file */ + // int ra_count; /* size of the read */ + // }; + // ``` + // + // [macOS `fcntl` manual page]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/fcntl.2.html + let ra_offset = match offset.try_into() { + Ok(len) => len, + // If this conversion fails, the user is providing an offset outside + // any possible file extent, so just ignore it. + Err(_) => return Ok(()), + }; + let ra_count = match len.try_into() { + Ok(len) => len, + // If this conversion fails, the user is providing a dubiously large + // hint which is unlikely to improve performance. + Err(_) => return Ok(()), + }; + unsafe { + let radvisory = c::radvisory { + ra_offset, + ra_count, + }; + ret(c::fcntl(borrowed_fd(fd), c::F_RDADVISE, &radvisory)) + } +} + +#[cfg(apple)] +pub(crate) fn fcntl_fullfsync(fd: BorrowedFd<'_>) -> io::Result<()> { + unsafe { ret(c::fcntl(borrowed_fd(fd), c::F_FULLFSYNC)) } +} + +#[cfg(apple)] +pub(crate) fn fcntl_nocache(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> { + unsafe { ret(c::fcntl(borrowed_fd(fd), c::F_NOCACHE, value as c::c_int)) } +} + +#[cfg(apple)] +pub(crate) fn fcntl_global_nocache(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> { + unsafe { + ret(c::fcntl( + borrowed_fd(fd), + c::F_GLOBAL_NOCACHE, + value as c::c_int, + )) + } +} + +/// Convert `times` from a `futimens`/`utimensat` argument into `setattrlist` +/// arguments. +#[cfg(apple)] +fn times_to_attrlist(times: &Timestamps) -> (c::size_t, [c::timespec; 2], Attrlist) { + // ABI details. + const ATTR_CMN_MODTIME: u32 = 0x0000_0400; + const ATTR_CMN_ACCTIME: u32 = 0x0000_1000; + const ATTR_BIT_MAP_COUNT: u16 = 5; + + let mut times = times.clone(); + + // If we have any `UTIME_NOW` elements, replace them with the current time. + if times.last_access.tv_nsec == c::UTIME_NOW || times.last_modification.tv_nsec == c::UTIME_NOW + { + let now = { + let mut tv = c::timeval { + tv_sec: 0, + tv_usec: 0, + }; + unsafe { + let r = c::gettimeofday(&mut tv, null_mut()); + assert_eq!(r, 0); + } + c::timespec { + tv_sec: tv.tv_sec, + tv_nsec: (tv.tv_usec * 1000) as _, + } + }; + if times.last_access.tv_nsec == c::UTIME_NOW { + times.last_access = now; + } + if times.last_modification.tv_nsec == c::UTIME_NOW { + times.last_modification = now; + } + } + + // Pack the return values following the rules for [`getattrlist`]. + // + // [`getattrlist`]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/getattrlist.2.html + let mut times_size = 0; + let mut attrs = Attrlist { + bitmapcount: ATTR_BIT_MAP_COUNT, + reserved: 0, + commonattr: 0, + volattr: 0, + dirattr: 0, + fileattr: 0, + forkattr: 0, + }; + let mut return_times = [c::timespec { + tv_sec: 0, + tv_nsec: 0, + }; 2]; + let mut times_index = 0; + if times.last_modification.tv_nsec != c::UTIME_OMIT { + attrs.commonattr |= ATTR_CMN_MODTIME; + return_times[times_index] = times.last_modification; + times_index += 1; + times_size += size_of::<c::timespec>(); + } + if times.last_access.tv_nsec != c::UTIME_OMIT { + attrs.commonattr |= ATTR_CMN_ACCTIME; + return_times[times_index] = times.last_access; + times_size += size_of::<c::timespec>(); + } + + (times_size, return_times, attrs) +} + +/// Support type for `Attrlist`. +#[cfg(apple)] +type Attrgroup = u32; + +/// Attribute list for use with `setattrlist`. +#[cfg(apple)] +#[repr(C)] +struct Attrlist { + bitmapcount: u16, + reserved: u16, + commonattr: Attrgroup, + volattr: Attrgroup, + dirattr: Attrgroup, + fileattr: Attrgroup, + forkattr: Attrgroup, +} + +#[cfg(any(apple, linux_kernel))] +pub(crate) fn getxattr(path: &CStr, name: &CStr, value: &mut [u8]) -> io::Result<usize> { + let value_ptr = value.as_mut_ptr(); + + #[cfg(not(apple))] + unsafe { + ret_usize(c::getxattr( + path.as_ptr(), + name.as_ptr(), + value_ptr.cast::<c::c_void>(), + value.len(), + )) + } + + #[cfg(apple)] + unsafe { + ret_usize(c::getxattr( + path.as_ptr(), + name.as_ptr(), + value_ptr.cast::<c::c_void>(), + value.len(), + 0, + 0, + )) + } +} + +#[cfg(any(apple, linux_kernel))] +pub(crate) fn lgetxattr(path: &CStr, name: &CStr, value: &mut [u8]) -> io::Result<usize> { + let value_ptr = value.as_mut_ptr(); + + #[cfg(not(apple))] + unsafe { + ret_usize(c::lgetxattr( + path.as_ptr(), + name.as_ptr(), + value_ptr.cast::<c::c_void>(), + value.len(), + )) + } + + #[cfg(apple)] + unsafe { + ret_usize(c::getxattr( + path.as_ptr(), + name.as_ptr(), + value_ptr.cast::<c::c_void>(), + value.len(), + 0, + c::XATTR_NOFOLLOW, + )) + } +} + +#[cfg(any(apple, linux_kernel))] +pub(crate) fn fgetxattr(fd: BorrowedFd<'_>, name: &CStr, value: &mut [u8]) -> io::Result<usize> { + let value_ptr = value.as_mut_ptr(); + + #[cfg(not(apple))] + unsafe { + ret_usize(c::fgetxattr( + borrowed_fd(fd), + name.as_ptr(), + value_ptr.cast::<c::c_void>(), + value.len(), + )) + } + + #[cfg(apple)] + unsafe { + ret_usize(c::fgetxattr( + borrowed_fd(fd), + name.as_ptr(), + value_ptr.cast::<c::c_void>(), + value.len(), + 0, + 0, + )) + } +} + +#[cfg(any(apple, linux_kernel))] +pub(crate) fn setxattr( + path: &CStr, + name: &CStr, + value: &[u8], + flags: XattrFlags, +) -> io::Result<()> { + #[cfg(not(apple))] + unsafe { + ret(c::setxattr( + path.as_ptr(), + name.as_ptr(), + value.as_ptr().cast::<c::c_void>(), + value.len(), + flags.bits() as i32, + )) + } + + #[cfg(apple)] + unsafe { + ret(c::setxattr( + path.as_ptr(), + name.as_ptr(), + value.as_ptr().cast::<c::c_void>(), + value.len(), + 0, + flags.bits() as i32, + )) + } +} + +#[cfg(any(apple, linux_kernel))] +pub(crate) fn lsetxattr( + path: &CStr, + name: &CStr, + value: &[u8], + flags: XattrFlags, +) -> io::Result<()> { + #[cfg(not(apple))] + unsafe { + ret(c::lsetxattr( + path.as_ptr(), + name.as_ptr(), + value.as_ptr().cast::<c::c_void>(), + value.len(), + flags.bits() as i32, + )) + } + + #[cfg(apple)] + unsafe { + ret(c::setxattr( + path.as_ptr(), + name.as_ptr(), + value.as_ptr().cast::<c::c_void>(), + value.len(), + 0, + flags.bits() as i32 | c::XATTR_NOFOLLOW, + )) + } +} + +#[cfg(any(apple, linux_kernel))] +pub(crate) fn fsetxattr( + fd: BorrowedFd<'_>, + name: &CStr, + value: &[u8], + flags: XattrFlags, +) -> io::Result<()> { + #[cfg(not(apple))] + unsafe { + ret(c::fsetxattr( + borrowed_fd(fd), + name.as_ptr(), + value.as_ptr().cast::<c::c_void>(), + value.len(), + flags.bits() as i32, + )) + } + + #[cfg(apple)] + unsafe { + ret(c::fsetxattr( + borrowed_fd(fd), + name.as_ptr(), + value.as_ptr().cast::<c::c_void>(), + value.len(), + 0, + flags.bits() as i32, + )) + } +} + +#[cfg(any(apple, linux_kernel))] +pub(crate) fn listxattr(path: &CStr, list: &mut [c::c_char]) -> io::Result<usize> { + #[cfg(not(apple))] + unsafe { + ret_usize(c::listxattr(path.as_ptr(), list.as_mut_ptr(), list.len())) + } + + #[cfg(apple)] + unsafe { + ret_usize(c::listxattr( + path.as_ptr(), + list.as_mut_ptr(), + list.len(), + 0, + )) + } +} + +#[cfg(any(apple, linux_kernel))] +pub(crate) fn llistxattr(path: &CStr, list: &mut [c::c_char]) -> io::Result<usize> { + #[cfg(not(apple))] + unsafe { + ret_usize(c::llistxattr(path.as_ptr(), list.as_mut_ptr(), list.len())) + } + + #[cfg(apple)] + unsafe { + ret_usize(c::listxattr( + path.as_ptr(), + list.as_mut_ptr(), + list.len(), + c::XATTR_NOFOLLOW, + )) + } +} + +#[cfg(any(apple, linux_kernel))] +pub(crate) fn flistxattr(fd: BorrowedFd<'_>, list: &mut [c::c_char]) -> io::Result<usize> { + let fd = borrowed_fd(fd); + + #[cfg(not(apple))] + unsafe { + ret_usize(c::flistxattr(fd, list.as_mut_ptr(), list.len())) + } + + #[cfg(apple)] + unsafe { + ret_usize(c::flistxattr(fd, list.as_mut_ptr(), list.len(), 0)) + } +} + +#[cfg(any(apple, linux_kernel))] +pub(crate) fn removexattr(path: &CStr, name: &CStr) -> io::Result<()> { + #[cfg(not(apple))] + unsafe { + ret(c::removexattr(path.as_ptr(), name.as_ptr())) + } + + #[cfg(apple)] + unsafe { + ret(c::removexattr(path.as_ptr(), name.as_ptr(), 0)) + } +} + +#[cfg(any(apple, linux_kernel))] +pub(crate) fn lremovexattr(path: &CStr, name: &CStr) -> io::Result<()> { + #[cfg(not(apple))] + unsafe { + ret(c::lremovexattr(path.as_ptr(), name.as_ptr())) + } + + #[cfg(apple)] + unsafe { + ret(c::removexattr( + path.as_ptr(), + name.as_ptr(), + c::XATTR_NOFOLLOW, + )) + } +} + +#[cfg(any(apple, linux_kernel))] +pub(crate) fn fremovexattr(fd: BorrowedFd<'_>, name: &CStr) -> io::Result<()> { + let fd = borrowed_fd(fd); + + #[cfg(not(apple))] + unsafe { + ret(c::fremovexattr(fd, name.as_ptr())) + } + + #[cfg(apple)] + unsafe { + ret(c::fremovexattr(fd, name.as_ptr(), 0)) + } +} + +#[test] +fn test_sizes() { + #[cfg(linux_kernel)] + assert_eq_size!(c::loff_t, u64); + + // Assert that `Timestamps` has the expected layout. If we're not fixing + // y2038, libc's type should match ours. If we are, it's smaller. + #[cfg(not(fix_y2038))] + assert_eq_size!([c::timespec; 2], Timestamps); + #[cfg(fix_y2038)] + assert!(core::mem::size_of::<[c::timespec; 2]>() < core::mem::size_of::<Timestamps>()); +} diff --git a/vendor/rustix/src/backend/libc/fs/types.rs b/vendor/rustix/src/backend/libc/fs/types.rs new file mode 100644 index 0000000..19508c2 --- /dev/null +++ b/vendor/rustix/src/backend/libc/fs/types.rs @@ -0,0 +1,1164 @@ +use crate::backend::c; +use bitflags::bitflags; + +#[cfg(not(any(target_os = "espidf", target_os = "vita")))] +bitflags! { + /// `*_OK` constants for use with [`accessat`]. + /// + /// [`accessat`]: fn.accessat.html + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct Access: c::c_int { + /// `R_OK` + const READ_OK = c::R_OK; + + /// `W_OK` + const WRITE_OK = c::W_OK; + + /// `X_OK` + const EXEC_OK = c::X_OK; + + /// `F_OK` + const EXISTS = c::F_OK; + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} + +#[cfg(not(any(target_os = "espidf", target_os = "redox")))] +bitflags! { + /// `AT_*` constants for use with [`openat`], [`statat`], and other `*at` + /// functions. + /// + /// [`openat`]: crate::fs::openat + /// [`statat`]: crate::fs::statat + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct AtFlags: u32 { + /// `AT_SYMLINK_NOFOLLOW` + const SYMLINK_NOFOLLOW = bitcast!(c::AT_SYMLINK_NOFOLLOW); + + /// `AT_EACCESS` + #[cfg(not(any(target_os = "emscripten", target_os = "android")))] + const EACCESS = bitcast!(c::AT_EACCESS); + + /// `AT_REMOVEDIR` + const REMOVEDIR = bitcast!(c::AT_REMOVEDIR); + + /// `AT_SYMLINK_FOLLOW` + const SYMLINK_FOLLOW = bitcast!(c::AT_SYMLINK_FOLLOW); + + /// `AT_NO_AUTOMOUNT` + #[cfg(any(linux_like, target_os = "fuchsia"))] + const NO_AUTOMOUNT = bitcast!(c::AT_NO_AUTOMOUNT); + + /// `AT_EMPTY_PATH` + #[cfg(any( + linux_kernel, + target_os = "freebsd", + target_os = "fuchsia", + ))] + const EMPTY_PATH = bitcast!(c::AT_EMPTY_PATH); + + /// `AT_RESOLVE_BENEATH` + #[cfg(target_os = "freebsd")] + const RESOLVE_BENEATH = bitcast!(c::AT_RESOLVE_BENEATH); + + /// `AT_STATX_SYNC_AS_STAT` + #[cfg(all(target_os = "linux", target_env = "gnu"))] + const STATX_SYNC_AS_STAT = bitcast!(c::AT_STATX_SYNC_AS_STAT); + + /// `AT_STATX_FORCE_SYNC` + #[cfg(all(target_os = "linux", target_env = "gnu"))] + const STATX_FORCE_SYNC = bitcast!(c::AT_STATX_FORCE_SYNC); + + /// `AT_STATX_DONT_SYNC` + #[cfg(all(target_os = "linux", target_env = "gnu"))] + const STATX_DONT_SYNC = bitcast!(c::AT_STATX_DONT_SYNC); + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} + +bitflags! { + /// `S_I*` constants for use with [`openat`], [`chmodat`], and [`fchmod`]. + /// + /// [`openat`]: crate::fs::openat + /// [`chmodat`]: crate::fs::chmodat + /// [`fchmod`]: crate::fs::fchmod + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct Mode: RawMode { + /// `S_IRWXU` + #[cfg(not(any(target_os = "espidf", target_os = "vita")))] + const RWXU = c::S_IRWXU as RawMode; + + /// `S_IRUSR` + #[cfg(not(any(target_os = "espidf", target_os = "vita")))] + const RUSR = c::S_IRUSR as RawMode; + + /// `S_IWUSR` + #[cfg(not(any(target_os = "espidf", target_os = "vita")))] + const WUSR = c::S_IWUSR as RawMode; + + /// `S_IXUSR` + #[cfg(not(any(target_os = "espidf", target_os = "vita")))] + const XUSR = c::S_IXUSR as RawMode; + + /// `S_IRWXG` + #[cfg(not(any(target_os = "espidf", target_os = "vita")))] + const RWXG = c::S_IRWXG as RawMode; + + /// `S_IRGRP` + #[cfg(not(any(target_os = "espidf", target_os = "vita")))] + const RGRP = c::S_IRGRP as RawMode; + + /// `S_IWGRP` + #[cfg(not(any(target_os = "espidf", target_os = "vita")))] + const WGRP = c::S_IWGRP as RawMode; + + /// `S_IXGRP` + #[cfg(not(any(target_os = "espidf", target_os = "vita")))] + const XGRP = c::S_IXGRP as RawMode; + + /// `S_IRWXO` + #[cfg(not(any(target_os = "espidf", target_os = "vita")))] + const RWXO = c::S_IRWXO as RawMode; + + /// `S_IROTH` + #[cfg(not(any(target_os = "espidf", target_os = "vita")))] + const ROTH = c::S_IROTH as RawMode; + + /// `S_IWOTH` + #[cfg(not(any(target_os = "espidf", target_os = "vita")))] + const WOTH = c::S_IWOTH as RawMode; + + /// `S_IXOTH` + #[cfg(not(any(target_os = "espidf", target_os = "vita")))] + const XOTH = c::S_IXOTH as RawMode; + + /// `S_ISUID` + #[cfg(not(any(target_os = "espidf", target_os = "vita")))] + const SUID = c::S_ISUID as RawMode; + + /// `S_ISGID` + #[cfg(not(any(target_os = "espidf", target_os = "vita")))] + const SGID = c::S_ISGID as RawMode; + + /// `S_ISVTX` + #[cfg(not(any(target_os = "espidf", target_os = "vita")))] + const SVTX = c::S_ISVTX as RawMode; + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} + +#[cfg(not(target_os = "espidf"))] +impl Mode { + /// Construct a `Mode` from the mode bits of the `st_mode` field of a + /// `Mode`. + #[inline] + pub const fn from_raw_mode(st_mode: RawMode) -> Self { + Self::from_bits_truncate(st_mode) + } + + /// Construct an `st_mode` value from a `Mode`. + #[inline] + pub const fn as_raw_mode(self) -> RawMode { + self.bits() + } +} + +#[cfg(not(target_os = "espidf"))] +impl From<RawMode> for Mode { + /// Support conversions from raw mode values to `Mode`. + /// + /// ``` + /// use rustix::fs::{Mode, RawMode}; + /// assert_eq!(Mode::from(0o700), Mode::RWXU); + /// ``` + #[inline] + fn from(st_mode: RawMode) -> Self { + Self::from_raw_mode(st_mode) + } +} + +#[cfg(not(target_os = "espidf"))] +impl From<Mode> for RawMode { + /// Support conversions from `Mode` to raw mode values. + /// + /// ``` + /// use rustix::fs::{Mode, RawMode}; + /// assert_eq!(RawMode::from(Mode::RWXU), 0o700); + /// ``` + #[inline] + fn from(mode: Mode) -> Self { + mode.as_raw_mode() + } +} + +bitflags! { + /// `O_*` constants for use with [`openat`]. + /// + /// [`openat`]: crate::fs::openat + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct OFlags: u32 { + /// `O_ACCMODE` + const ACCMODE = bitcast!(c::O_ACCMODE); + + /// Similar to `ACCMODE`, but just includes the read/write flags, and + /// no other flags. + /// + /// On some platforms, `PATH` may be included in `ACCMODE`, when + /// sometimes we really just want the read/write bits. Caution is + /// indicated, as the presence of `PATH` may mean that the read/write + /// bits don't have their usual meaning. + const RWMODE = bitcast!(c::O_RDONLY | c::O_WRONLY | c::O_RDWR); + + /// `O_APPEND` + const APPEND = bitcast!(c::O_APPEND); + + /// `O_CREAT` + #[doc(alias = "CREAT")] + const CREATE = bitcast!(c::O_CREAT); + + /// `O_DIRECTORY` + #[cfg(not(target_os = "espidf"))] + const DIRECTORY = bitcast!(c::O_DIRECTORY); + + /// `O_DSYNC` + #[cfg(not(any(target_os = "dragonfly", target_os = "espidf", target_os = "l4re", target_os = "redox", target_os = "vita")))] + const DSYNC = bitcast!(c::O_DSYNC); + + /// `O_EXCL` + const EXCL = bitcast!(c::O_EXCL); + + /// `O_FSYNC` + #[cfg(any( + bsd, + all(target_os = "linux", not(target_env = "musl")), + ))] + const FSYNC = bitcast!(c::O_FSYNC); + + /// `O_NOFOLLOW` + #[cfg(not(target_os = "espidf"))] + const NOFOLLOW = bitcast!(c::O_NOFOLLOW); + + /// `O_NONBLOCK` + const NONBLOCK = bitcast!(c::O_NONBLOCK); + + /// `O_RDONLY` + const RDONLY = bitcast!(c::O_RDONLY); + + /// `O_WRONLY` + const WRONLY = bitcast!(c::O_WRONLY); + + /// `O_RDWR` + /// + /// This is not equal to `RDONLY | WRONLY`. It's a distinct flag. + const RDWR = bitcast!(c::O_RDWR); + + /// `O_NOCTTY` + #[cfg(not(any(target_os = "espidf", target_os = "l4re", target_os = "redox", target_os = "vita")))] + const NOCTTY = bitcast!(c::O_NOCTTY); + + /// `O_RSYNC` + #[cfg(any( + linux_kernel, + netbsdlike, + target_os = "emscripten", + target_os = "wasi", + ))] + const RSYNC = bitcast!(c::O_RSYNC); + + /// `O_SYNC` + #[cfg(not(any(target_os = "l4re", target_os = "redox")))] + const SYNC = bitcast!(c::O_SYNC); + + /// `O_TRUNC` + const TRUNC = bitcast!(c::O_TRUNC); + + /// `O_PATH` + #[cfg(any( + linux_kernel, + target_os = "emscripten", + target_os = "freebsd", + target_os = "fuchsia", + target_os = "redox", + ))] + const PATH = bitcast!(c::O_PATH); + + /// `O_CLOEXEC` + const CLOEXEC = bitcast!(c::O_CLOEXEC); + + /// `O_TMPFILE` + #[cfg(any( + linux_kernel, + target_os = "emscripten", + target_os = "fuchsia", + ))] + const TMPFILE = bitcast!(c::O_TMPFILE); + + /// `O_NOATIME` + #[cfg(any( + linux_kernel, + target_os = "fuchsia", + ))] + const NOATIME = bitcast!(c::O_NOATIME); + + /// `O_DIRECT` + #[cfg(any( + linux_kernel, + target_os = "emscripten", + target_os = "freebsd", + target_os = "fuchsia", + target_os = "netbsd", + ))] + const DIRECT = bitcast!(c::O_DIRECT); + + /// `O_RESOLVE_BENEATH` + #[cfg(target_os = "freebsd")] + const RESOLVE_BENEATH = bitcast!(c::O_RESOLVE_BENEATH); + + /// `O_EMPTY_PATH` + #[cfg(target_os = "freebsd")] + const EMPTY_PATH = bitcast!(c::O_EMPTY_PATH); + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} + +#[cfg(apple)] +bitflags! { + /// `CLONE_*` constants for use with [`fclonefileat`]. + /// + /// [`fclonefileat`]: crate::fs::fclonefileat + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct CloneFlags: u32 { + /// `CLONE_NOFOLLOW` + const NOFOLLOW = 1; + + /// `CLONE_NOOWNERCOPY` + const NOOWNERCOPY = 2; + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} + +#[cfg(apple)] +mod copyfile { + pub(super) const ACL: u32 = 1 << 0; + pub(super) const STAT: u32 = 1 << 1; + pub(super) const XATTR: u32 = 1 << 2; + pub(super) const DATA: u32 = 1 << 3; + pub(super) const SECURITY: u32 = STAT | ACL; + pub(super) const METADATA: u32 = SECURITY | XATTR; + pub(super) const ALL: u32 = METADATA | DATA; +} + +#[cfg(apple)] +bitflags! { + /// `COPYFILE_*` constants for use with [`fcopyfile`]. + /// + /// [`fcopyfile`]: crate::fs::fcopyfile + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct CopyfileFlags: c::c_uint { + /// `COPYFILE_ACL` + const ACL = copyfile::ACL; + + /// `COPYFILE_STAT` + const STAT = copyfile::STAT; + + /// `COPYFILE_XATTR` + const XATTR = copyfile::XATTR; + + /// `COPYFILE_DATA` + const DATA = copyfile::DATA; + + /// `COPYFILE_SECURITY` + const SECURITY = copyfile::SECURITY; + + /// `COPYFILE_METADATA` + const METADATA = copyfile::METADATA; + + /// `COPYFILE_ALL` + const ALL = copyfile::ALL; + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} + +#[cfg(linux_kernel)] +bitflags! { + /// `RESOLVE_*` constants for use with [`openat2`]. + /// + /// [`openat2`]: crate::fs::openat2 + #[repr(transparent)] + #[derive(Default, Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct ResolveFlags: u64 { + /// `RESOLVE_NO_XDEV` + const NO_XDEV = 0x01; + + /// `RESOLVE_NO_MAGICLINKS` + const NO_MAGICLINKS = 0x02; + + /// `RESOLVE_NO_SYMLINKS` + const NO_SYMLINKS = 0x04; + + /// `RESOLVE_BENEATH` + const BENEATH = 0x08; + + /// `RESOLVE_IN_ROOT` + const IN_ROOT = 0x10; + + /// `RESOLVE_CACHED` (since Linux 5.12) + const CACHED = 0x20; + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} + +#[cfg(linux_kernel)] +bitflags! { + /// `RENAME_*` constants for use with [`renameat_with`]. + /// + /// [`renameat_with`]: crate::fs::renameat_with + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct RenameFlags: c::c_uint { + /// `RENAME_EXCHANGE` + const EXCHANGE = bitcast!(c::RENAME_EXCHANGE); + + /// `RENAME_NOREPLACE` + const NOREPLACE = bitcast!(c::RENAME_NOREPLACE); + + /// `RENAME_WHITEOUT` + const WHITEOUT = bitcast!(c::RENAME_WHITEOUT); + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} + +/// `S_IF*` constants for use with [`mknodat`] and [`Stat`]'s `st_mode` field. +/// +/// [`mknodat`]: crate::fs::mknodat +/// [`Stat`]: crate::fs::Stat +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum FileType { + /// `S_IFREG` + RegularFile = c::S_IFREG as isize, + + /// `S_IFDIR` + Directory = c::S_IFDIR as isize, + + /// `S_IFLNK` + Symlink = c::S_IFLNK as isize, + + /// `S_IFIFO` + #[cfg(not(target_os = "wasi"))] // TODO: Use WASI's `S_IFIFO`. + #[doc(alias = "IFO")] + Fifo = c::S_IFIFO as isize, + + /// `S_IFSOCK` + #[cfg(not(target_os = "wasi"))] // TODO: Use WASI's `S_IFSOCK`. + Socket = c::S_IFSOCK as isize, + + /// `S_IFCHR` + CharacterDevice = c::S_IFCHR as isize, + + /// `S_IFBLK` + BlockDevice = c::S_IFBLK as isize, + + /// An unknown filesystem object. + Unknown, +} + +impl FileType { + /// Construct a `FileType` from the `S_IFMT` bits of the `st_mode` field of + /// a `Stat`. + #[inline] + pub const fn from_raw_mode(st_mode: RawMode) -> Self { + match (st_mode as c::mode_t) & c::S_IFMT { + c::S_IFREG => Self::RegularFile, + c::S_IFDIR => Self::Directory, + c::S_IFLNK => Self::Symlink, + #[cfg(not(target_os = "wasi"))] // TODO: Use WASI's `S_IFIFO`. + c::S_IFIFO => Self::Fifo, + #[cfg(not(target_os = "wasi"))] // TODO: Use WASI's `S_IFSOCK`. + c::S_IFSOCK => Self::Socket, + c::S_IFCHR => Self::CharacterDevice, + c::S_IFBLK => Self::BlockDevice, + _ => Self::Unknown, + } + } + + /// Construct an `st_mode` value from a `FileType`. + #[inline] + pub const fn as_raw_mode(self) -> RawMode { + match self { + Self::RegularFile => c::S_IFREG as RawMode, + Self::Directory => c::S_IFDIR as RawMode, + Self::Symlink => c::S_IFLNK as RawMode, + #[cfg(not(target_os = "wasi"))] // TODO: Use WASI's `S_IFIFO`. + Self::Fifo => c::S_IFIFO as RawMode, + #[cfg(not(target_os = "wasi"))] // TODO: Use WASI's `S_IFSOCK`. + Self::Socket => c::S_IFSOCK as RawMode, + Self::CharacterDevice => c::S_IFCHR as RawMode, + Self::BlockDevice => c::S_IFBLK as RawMode, + Self::Unknown => c::S_IFMT as RawMode, + } + } + + /// Construct a `FileType` from the `d_type` field of a `c::dirent`. + #[cfg(not(any( + solarish, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "nto", + target_os = "redox", + target_os = "vita" + )))] + #[inline] + pub(crate) const fn from_dirent_d_type(d_type: u8) -> Self { + match d_type { + c::DT_REG => Self::RegularFile, + c::DT_DIR => Self::Directory, + c::DT_LNK => Self::Symlink, + #[cfg(not(target_os = "wasi"))] // TODO: Use WASI's `DT_SOCK`. + c::DT_SOCK => Self::Socket, + #[cfg(not(target_os = "wasi"))] // TODO: Use WASI's `DT_FIFO`. + c::DT_FIFO => Self::Fifo, + c::DT_CHR => Self::CharacterDevice, + c::DT_BLK => Self::BlockDevice, + // c::DT_UNKNOWN | + _ => Self::Unknown, + } + } +} + +/// `POSIX_FADV_*` constants for use with [`fadvise`]. +/// +/// [`fadvise`]: crate::fs::fadvise +#[cfg(not(any( + apple, + netbsdlike, + solarish, + target_os = "dragonfly", + target_os = "espidf", + target_os = "haiku", + target_os = "redox", + target_os = "vita", +)))] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[repr(u32)] +pub enum Advice { + /// `POSIX_FADV_NORMAL` + Normal = c::POSIX_FADV_NORMAL as c::c_uint, + + /// `POSIX_FADV_SEQUENTIAL` + Sequential = c::POSIX_FADV_SEQUENTIAL as c::c_uint, + + /// `POSIX_FADV_RANDOM` + Random = c::POSIX_FADV_RANDOM as c::c_uint, + + /// `POSIX_FADV_NOREUSE` + NoReuse = c::POSIX_FADV_NOREUSE as c::c_uint, + + /// `POSIX_FADV_WILLNEED` + WillNeed = c::POSIX_FADV_WILLNEED as c::c_uint, + + /// `POSIX_FADV_DONTNEED` + DontNeed = c::POSIX_FADV_DONTNEED as c::c_uint, +} + +#[cfg(any(linux_kernel, target_os = "freebsd"))] +bitflags! { + /// `MFD_*` constants for use with [`memfd_create`]. + /// + /// [`memfd_create`]: crate::fs::memfd_create + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct MemfdFlags: c::c_uint { + /// `MFD_CLOEXEC` + const CLOEXEC = c::MFD_CLOEXEC; + + /// `MFD_ALLOW_SEALING` + const ALLOW_SEALING = c::MFD_ALLOW_SEALING; + + /// `MFD_HUGETLB` (since Linux 4.14) + const HUGETLB = c::MFD_HUGETLB; + + /// `MFD_HUGE_64KB` + const HUGE_64KB = c::MFD_HUGE_64KB; + /// `MFD_HUGE_512JB` + const HUGE_512KB = c::MFD_HUGE_512KB; + /// `MFD_HUGE_1MB` + const HUGE_1MB = c::MFD_HUGE_1MB; + /// `MFD_HUGE_2MB` + const HUGE_2MB = c::MFD_HUGE_2MB; + /// `MFD_HUGE_8MB` + const HUGE_8MB = c::MFD_HUGE_8MB; + /// `MFD_HUGE_16MB` + const HUGE_16MB = c::MFD_HUGE_16MB; + /// `MFD_HUGE_32MB` + const HUGE_32MB = c::MFD_HUGE_32MB; + /// `MFD_HUGE_256MB` + const HUGE_256MB = c::MFD_HUGE_256MB; + /// `MFD_HUGE_512MB` + const HUGE_512MB = c::MFD_HUGE_512MB; + /// `MFD_HUGE_1GB` + const HUGE_1GB = c::MFD_HUGE_1GB; + /// `MFD_HUGE_2GB` + const HUGE_2GB = c::MFD_HUGE_2GB; + /// `MFD_HUGE_16GB` + const HUGE_16GB = c::MFD_HUGE_16GB; + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} + +#[cfg(any(linux_kernel, target_os = "freebsd", target_os = "fuchsia"))] +bitflags! { + /// `F_SEAL_*` constants for use with [`fcntl_add_seals`] and + /// [`fcntl_get_seals`]. + /// + /// [`fcntl_add_seals`]: crate::fs::fcntl_add_seals + /// [`fcntl_get_seals`]: crate::fs::fcntl_get_seals + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct SealFlags: u32 { + /// `F_SEAL_SEAL` + const SEAL = bitcast!(c::F_SEAL_SEAL); + /// `F_SEAL_SHRINK` + const SHRINK = bitcast!(c::F_SEAL_SHRINK); + /// `F_SEAL_GROW` + const GROW = bitcast!(c::F_SEAL_GROW); + /// `F_SEAL_WRITE` + const WRITE = bitcast!(c::F_SEAL_WRITE); + /// `F_SEAL_FUTURE_WRITE` (since Linux 5.1) + #[cfg(linux_kernel)] + const FUTURE_WRITE = bitcast!(c::F_SEAL_FUTURE_WRITE); + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} + +#[cfg(all(target_os = "linux", target_env = "gnu"))] +bitflags! { + /// `STATX_*` constants for use with [`statx`]. + /// + /// [`statx`]: crate::fs::statx + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct StatxFlags: u32 { + /// `STATX_TYPE` + const TYPE = c::STATX_TYPE; + + /// `STATX_MODE` + const MODE = c::STATX_MODE; + + /// `STATX_NLINK` + const NLINK = c::STATX_NLINK; + + /// `STATX_UID` + const UID = c::STATX_UID; + + /// `STATX_GID` + const GID = c::STATX_GID; + + /// `STATX_ATIME` + const ATIME = c::STATX_ATIME; + + /// `STATX_MTIME` + const MTIME = c::STATX_MTIME; + + /// `STATX_CTIME` + const CTIME = c::STATX_CTIME; + + /// `STATX_INO` + const INO = c::STATX_INO; + + /// `STATX_SIZE` + const SIZE = c::STATX_SIZE; + + /// `STATX_BLOCKS` + const BLOCKS = c::STATX_BLOCKS; + + /// `STATX_BASIC_STATS` + const BASIC_STATS = c::STATX_BASIC_STATS; + + /// `STATX_BTIME` + const BTIME = c::STATX_BTIME; + + /// `STATX_MNT_ID` (since Linux 5.8) + const MNT_ID = c::STATX_MNT_ID; + + /// `STATX_DIOALIGN` (since Linux 6.1) + const DIOALIGN = c::STATX_DIOALIGN; + + /// `STATX_ALL` + const ALL = c::STATX_ALL; + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} + +#[cfg(any( + target_os = "android", + all(target_os = "linux", not(target_env = "gnu")), +))] +bitflags! { + /// `STATX_*` constants for use with [`statx`]. + /// + /// [`statx`]: crate::fs::statx + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct StatxFlags: u32 { + /// `STATX_TYPE` + const TYPE = 0x0001; + + /// `STATX_MODE` + const MODE = 0x0002; + + /// `STATX_NLINK` + const NLINK = 0x0004; + + /// `STATX_UID` + const UID = 0x0008; + + /// `STATX_GID` + const GID = 0x0010; + + /// `STATX_ATIME` + const ATIME = 0x0020; + + /// `STATX_MTIME` + const MTIME = 0x0040; + + /// `STATX_CTIME` + const CTIME = 0x0080; + + /// `STATX_INO` + const INO = 0x0100; + + /// `STATX_SIZE` + const SIZE = 0x0200; + + /// `STATX_BLOCKS` + const BLOCKS = 0x0400; + + /// `STATX_BASIC_STATS` + const BASIC_STATS = 0x07ff; + + /// `STATX_BTIME` + const BTIME = 0x800; + + /// `STATX_MNT_ID` (since Linux 5.8) + const MNT_ID = 0x1000; + + /// `STATX_ALL` + const ALL = 0xfff; + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} + +#[cfg(not(any( + netbsdlike, + solarish, + target_os = "aix", + target_os = "espidf", + target_os = "nto", + target_os = "redox", + target_os = "vita" +)))] +bitflags! { + /// `FALLOC_FL_*` constants for use with [`fallocate`]. + /// + /// [`fallocate`]: crate::fs::fallocate + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct FallocateFlags: u32 { + /// `FALLOC_FL_KEEP_SIZE` + #[cfg(not(any( + bsd, + target_os = "aix", + target_os = "haiku", + target_os = "hurd", + target_os = "wasi", + )))] + const KEEP_SIZE = bitcast!(c::FALLOC_FL_KEEP_SIZE); + /// `FALLOC_FL_PUNCH_HOLE` + #[cfg(not(any( + bsd, + target_os = "aix", + target_os = "haiku", + target_os = "hurd", + target_os = "wasi", + )))] + const PUNCH_HOLE = bitcast!(c::FALLOC_FL_PUNCH_HOLE); + /// `FALLOC_FL_NO_HIDE_STALE` + #[cfg(not(any( + bsd, + target_os = "aix", + target_os = "emscripten", + target_os = "fuchsia", + target_os = "haiku", + target_os = "hurd", + target_os = "l4re", + target_os = "linux", + target_os = "wasi", + )))] + const NO_HIDE_STALE = bitcast!(c::FALLOC_FL_NO_HIDE_STALE); + /// `FALLOC_FL_COLLAPSE_RANGE` + #[cfg(not(any( + bsd, + target_os = "aix", + target_os = "haiku", + target_os = "hurd", + target_os = "emscripten", + target_os = "wasi", + )))] + const COLLAPSE_RANGE = bitcast!(c::FALLOC_FL_COLLAPSE_RANGE); + /// `FALLOC_FL_ZERO_RANGE` + #[cfg(not(any( + bsd, + target_os = "aix", + target_os = "haiku", + target_os = "hurd", + target_os = "emscripten", + target_os = "wasi", + )))] + const ZERO_RANGE = bitcast!(c::FALLOC_FL_ZERO_RANGE); + /// `FALLOC_FL_INSERT_RANGE` + #[cfg(not(any( + bsd, + target_os = "aix", + target_os = "haiku", + target_os = "hurd", + target_os = "emscripten", + target_os = "wasi", + )))] + const INSERT_RANGE = bitcast!(c::FALLOC_FL_INSERT_RANGE); + /// `FALLOC_FL_UNSHARE_RANGE` + #[cfg(not(any( + bsd, + target_os = "aix", + target_os = "haiku", + target_os = "hurd", + target_os = "emscripten", + target_os = "wasi", + )))] + const UNSHARE_RANGE = bitcast!(c::FALLOC_FL_UNSHARE_RANGE); + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} + +#[cfg(not(any(target_os = "haiku", target_os = "redox", target_os = "wasi")))] +bitflags! { + /// `ST_*` constants for use with [`StatVfs`]. + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct StatVfsMountFlags: u64 { + /// `ST_MANDLOCK` + #[cfg(any(linux_kernel, target_os = "emscripten", target_os = "fuchsia"))] + const MANDLOCK = c::ST_MANDLOCK as u64; + + /// `ST_NOATIME` + #[cfg(any(linux_kernel, target_os = "emscripten", target_os = "fuchsia"))] + const NOATIME = c::ST_NOATIME as u64; + + /// `ST_NODEV` + #[cfg(any( + linux_kernel, + target_os = "aix", + target_os = "emscripten", + target_os = "fuchsia" + ))] + const NODEV = c::ST_NODEV as u64; + + /// `ST_NODIRATIME` + #[cfg(any(linux_kernel, target_os = "emscripten", target_os = "fuchsia"))] + const NODIRATIME = c::ST_NODIRATIME as u64; + + /// `ST_NOEXEC` + #[cfg(any(linux_kernel, target_os = "emscripten", target_os = "fuchsia"))] + const NOEXEC = c::ST_NOEXEC as u64; + + /// `ST_NOSUID` + #[cfg(not(any(target_os = "espidf", target_os = "vita")))] + const NOSUID = c::ST_NOSUID as u64; + + /// `ST_RDONLY` + #[cfg(not(any(target_os = "espidf", target_os = "vita")))] + const RDONLY = c::ST_RDONLY as u64; + + /// `ST_RELATIME` + #[cfg(any(target_os = "android", all(target_os = "linux", target_env = "gnu")))] + const RELATIME = c::ST_RELATIME as u64; + + /// `ST_SYNCHRONOUS` + #[cfg(any(linux_kernel, target_os = "emscripten", target_os = "fuchsia"))] + const SYNCHRONOUS = c::ST_SYNCHRONOUS as u64; + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} + +/// `LOCK_*` constants for use with [`flock`] and [`fcntl_lock`]. +/// +/// [`flock`]: crate::fs::flock +/// [`fcntl_lock`]: crate::fs::fcntl_lock +#[cfg(not(any(target_os = "espidf", target_os = "vita", target_os = "wasi")))] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[repr(u32)] +pub enum FlockOperation { + /// `LOCK_SH` + LockShared = bitcast!(c::LOCK_SH), + /// `LOCK_EX` + LockExclusive = bitcast!(c::LOCK_EX), + /// `LOCK_UN` + Unlock = bitcast!(c::LOCK_UN), + /// `LOCK_SH | LOCK_NB` + NonBlockingLockShared = bitcast!(c::LOCK_SH | c::LOCK_NB), + /// `LOCK_EX | LOCK_NB` + NonBlockingLockExclusive = bitcast!(c::LOCK_EX | c::LOCK_NB), + /// `LOCK_UN | LOCK_NB` + NonBlockingUnlock = bitcast!(c::LOCK_UN | c::LOCK_NB), +} + +/// `struct stat` for use with [`statat`] and [`fstat`]. +/// +/// [`statat`]: crate::fs::statat +/// [`fstat`]: crate::fs::fstat +#[cfg(not(any(linux_like, target_os = "hurd")))] +pub type Stat = c::stat; + +/// `struct stat` for use with [`statat`] and [`fstat`]. +/// +/// [`statat`]: crate::fs::statat +/// [`fstat`]: crate::fs::fstat +#[cfg(any( + all(linux_kernel, target_pointer_width = "64"), + target_os = "hurd", + target_os = "emscripten", + target_os = "l4re", +))] +pub type Stat = c::stat64; + +/// `struct stat` for use with [`statat`] and [`fstat`]. +/// +/// [`statat`]: crate::fs::statat +/// [`fstat`]: crate::fs::fstat +// On 32-bit, Linux's `struct stat64` has a 32-bit `st_mtime` and friends, so +// we use our own struct, populated from `statx` where possible, to avoid the +// y2038 bug. +#[cfg(all(linux_kernel, target_pointer_width = "32"))] +#[repr(C)] +#[derive(Debug, Copy, Clone)] +#[allow(missing_docs)] +pub struct Stat { + pub st_dev: u64, + pub st_mode: u32, + pub st_nlink: u32, + pub st_uid: u32, + pub st_gid: u32, + pub st_rdev: u64, + pub st_size: i64, + pub st_blksize: u32, + pub st_blocks: u64, + pub st_atime: u64, + pub st_atime_nsec: u32, + pub st_mtime: u64, + pub st_mtime_nsec: u32, + pub st_ctime: u64, + pub st_ctime_nsec: u32, + pub st_ino: u64, +} + +/// `struct statfs` for use with [`statfs`] and [`fstatfs`]. +/// +/// [`statfs`]: crate::fs::statfs +/// [`fstatfs`]: crate::fs::fstatfs +#[cfg(not(any( + linux_like, + solarish, + target_os = "espidf", + target_os = "haiku", + target_os = "netbsd", + target_os = "nto", + target_os = "redox", + target_os = "vita", + target_os = "wasi", +)))] +#[allow(clippy::module_name_repetitions)] +pub type StatFs = c::statfs; + +/// `struct statfs` for use with [`statfs`] and [`fstatfs`]. +/// +/// [`statfs`]: crate::fs::statfs +/// [`fstatfs`]: crate::fs::fstatfs +#[cfg(linux_like)] +pub type StatFs = c::statfs64; + +/// `struct statvfs` for use with [`statvfs`] and [`fstatvfs`]. +/// +/// [`statvfs`]: crate::fs::statvfs +/// [`fstatvfs`]: crate::fs::fstatvfs +#[cfg(not(any(target_os = "haiku", target_os = "redox", target_os = "wasi")))] +#[allow(missing_docs)] +pub struct StatVfs { + pub f_bsize: u64, + pub f_frsize: u64, + pub f_blocks: u64, + pub f_bfree: u64, + pub f_bavail: u64, + pub f_files: u64, + pub f_ffree: u64, + pub f_favail: u64, + pub f_fsid: u64, + pub f_flag: StatVfsMountFlags, + pub f_namemax: u64, +} + +/// `struct statx` for use with [`statx`]. +/// +/// [`statx`]: crate::fs::statx +#[cfg(all(target_os = "linux", target_env = "gnu"))] +// Use the glibc `struct statx`. +pub type Statx = c::statx; + +/// `struct statx_timestamp` for use with [`Statx`]. +#[cfg(all(target_os = "linux", target_env = "gnu"))] +// Use the glibc `struct statx_timestamp`. +pub type StatxTimestamp = c::statx; + +/// `struct statx` for use with [`statx`]. +/// +/// [`statx`]: crate::fs::statx +// Non-glibc ABIs don't currently declare a `struct statx`, so we declare it +// ourselves. +#[cfg(any( + target_os = "android", + all(target_os = "linux", not(target_env = "gnu")), +))] +#[repr(C)] +#[allow(missing_docs)] +pub struct Statx { + pub stx_mask: u32, + pub stx_blksize: u32, + pub stx_attributes: u64, + pub stx_nlink: u32, + pub stx_uid: u32, + pub stx_gid: u32, + pub stx_mode: u16, + __statx_pad1: [u16; 1], + pub stx_ino: u64, + pub stx_size: u64, + pub stx_blocks: u64, + pub stx_attributes_mask: u64, + pub stx_atime: StatxTimestamp, + pub stx_btime: StatxTimestamp, + pub stx_ctime: StatxTimestamp, + pub stx_mtime: StatxTimestamp, + pub stx_rdev_major: u32, + pub stx_rdev_minor: u32, + pub stx_dev_major: u32, + pub stx_dev_minor: u32, + pub stx_mnt_id: u64, + __statx_pad2: u64, + __statx_pad3: [u64; 12], +} + +/// `struct statx_timestamp` for use with [`Statx`]. +// Non-glibc ABIs don't currently declare a `struct statx_timestamp`, so we +// declare it ourselves. +#[cfg(any( + target_os = "android", + all(target_os = "linux", not(target_env = "gnu")), +))] +#[repr(C)] +#[allow(missing_docs)] +pub struct StatxTimestamp { + pub tv_sec: i64, + pub tv_nsec: u32, + pub __statx_timestamp_pad1: [i32; 1], +} + +/// `mode_t` +#[cfg(not(all(target_os = "android", target_pointer_width = "32")))] +pub type RawMode = c::mode_t; + +/// `mode_t` +#[cfg(all(target_os = "android", target_pointer_width = "32"))] +pub type RawMode = c::c_uint; + +/// `dev_t` +#[cfg(not(all(target_os = "android", target_pointer_width = "32")))] +pub type Dev = c::dev_t; + +/// `dev_t` +#[cfg(all(target_os = "android", target_pointer_width = "32"))] +pub type Dev = c::c_ulonglong; + +/// `__fsword_t` +#[cfg(all( + target_os = "linux", + not(target_env = "musl"), + not(target_arch = "s390x"), +))] +pub type FsWord = c::__fsword_t; + +/// `__fsword_t` +#[cfg(all( + any(target_os = "android", all(target_os = "linux", target_env = "musl")), + target_pointer_width = "32", +))] +pub type FsWord = u32; + +/// `__fsword_t` +#[cfg(all( + any(target_os = "android", all(target_os = "linux", target_env = "musl")), + not(target_arch = "s390x"), + target_pointer_width = "64", +))] +pub type FsWord = u64; + +/// `__fsword_t` +// s390x uses `u32` for `statfs` entries on glibc, even though `__fsword_t` is +// `u64`. +#[cfg(all(target_os = "linux", target_arch = "s390x", target_env = "gnu"))] +pub type FsWord = u32; + +/// `__fsword_t` +// s390x uses `u64` for `statfs` entries on musl. +#[cfg(all(target_os = "linux", target_arch = "s390x", target_env = "musl"))] +pub type FsWord = u64; + +/// `copyfile_state_t`—State for use with [`fcopyfile`]. +/// +/// [`fcopyfile`]: crate::fs::fcopyfile +#[cfg(apple)] +#[allow(non_camel_case_types)] +#[repr(transparent)] +#[derive(Copy, Clone)] +pub struct copyfile_state_t(pub(crate) *mut c::c_void); diff --git a/vendor/rustix/src/backend/libc/io/errno.rs b/vendor/rustix/src/backend/libc/io/errno.rs new file mode 100644 index 0000000..805ea67 --- /dev/null +++ b/vendor/rustix/src/backend/libc/io/errno.rs @@ -0,0 +1,1052 @@ +//! The `rustix` `Errno` type. +//! +//! This type holds an OS error code, which conceptually corresponds to an +//! `errno` value. + +use crate::backend::c; +use libc_errno::errno; + +/// `errno`—An error code. +/// +/// The error type for `rustix` APIs. This is similar to [`std::io::Error`], +/// but only holds an OS error code, and no extra error value. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// - [Winsock] +/// - [FreeBSD] +/// - [NetBSD] +/// - [OpenBSD] +/// - [DragonFly BSD] +/// - [illumos] +/// - [glibc] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/errno.html +/// [Linux]: https://man7.org/linux/man-pages/man3/errno.3.html +/// [Winsock]: https://learn.microsoft.com/en-us/windows/win32/winsock/windows-sockets-error-codes-2 +/// [FreeBSD]: https://man.freebsd.org/cgi/man.cgi?errno +/// [NetBSD]: https://man.netbsd.org/errno.2 +/// [OpenBSD]: https://man.openbsd.org/errno.2 +/// [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)] +pub struct Errno(pub(crate) c::c_int); + +impl Errno { + /// `EACCES` + #[doc(alias = "ACCES")] + pub const ACCESS: Self = Self(c::EACCES); + /// `EADDRINUSE` + pub const ADDRINUSE: Self = Self(c::EADDRINUSE); + /// `EADDRNOTAVAIL` + pub const ADDRNOTAVAIL: Self = Self(c::EADDRNOTAVAIL); + /// `EADV` + #[cfg(not(any( + bsd, + windows, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "hurd", + target_os = "l4re", + target_os = "vita", + target_os = "wasi", + )))] + pub const ADV: Self = Self(c::EADV); + /// `EAFNOSUPPORT` + #[cfg(not(target_os = "l4re"))] + pub const AFNOSUPPORT: Self = Self(c::EAFNOSUPPORT); + /// `EAGAIN` + pub const AGAIN: Self = Self(c::EAGAIN); + /// `EALREADY` + #[cfg(not(target_os = "l4re"))] + pub const ALREADY: Self = Self(c::EALREADY); + /// `EAUTH` + #[cfg(bsd)] + pub const AUTH: Self = Self(c::EAUTH); + /// `EBADE` + #[cfg(not(any( + bsd, + windows, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "hurd", + target_os = "l4re", + target_os = "vita", + target_os = "wasi", + )))] + pub const BADE: Self = Self(c::EBADE); + /// `EBADF` + pub const BADF: Self = Self(c::EBADF); + /// `EBADFD` + #[cfg(not(any( + bsd, + windows, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "hurd", + target_os = "l4re", + target_os = "vita", + target_os = "wasi", + )))] + pub const BADFD: Self = Self(c::EBADFD); + /// `EBADMSG` + #[cfg(not(any(windows, target_os = "l4re")))] + pub const BADMSG: Self = Self(c::EBADMSG); + /// `EBADR` + #[cfg(not(any( + bsd, + windows, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "hurd", + target_os = "l4re", + target_os = "vita", + target_os = "wasi", + )))] + pub const BADR: Self = Self(c::EBADR); + /// `EBADRPC` + #[cfg(bsd)] + pub const BADRPC: Self = Self(c::EBADRPC); + /// `EBADRQC` + #[cfg(not(any( + bsd, + windows, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "hurd", + target_os = "l4re", + target_os = "vita", + target_os = "wasi", + )))] + pub const BADRQC: Self = Self(c::EBADRQC); + /// `EBADSLT` + #[cfg(not(any( + bsd, + windows, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "hurd", + target_os = "l4re", + target_os = "vita", + target_os = "wasi", + )))] + pub const BADSLT: Self = Self(c::EBADSLT); + /// `EBFONT` + #[cfg(not(any( + bsd, + windows, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "hurd", + target_os = "l4re", + target_os = "vita", + target_os = "wasi", + )))] + pub const BFONT: Self = Self(c::EBFONT); + /// `EBUSY` + #[cfg(not(windows))] + pub const BUSY: Self = Self(c::EBUSY); + /// `ECANCELED` + #[cfg(not(target_os = "l4re"))] + pub const CANCELED: Self = Self(c::ECANCELED); + /// `ECAPMODE` + #[cfg(target_os = "freebsd")] + pub const CAPMODE: Self = Self(c::ECAPMODE); + /// `ECHILD` + #[cfg(not(windows))] + pub const CHILD: Self = Self(c::ECHILD); + /// `ECHRNG` + #[cfg(not(any( + bsd, + windows, + target_os = "espidf", + target_os = "haiku", + target_os = "hurd", + target_os = "l4re", + target_os = "vita", + target_os = "wasi" + )))] + pub const CHRNG: Self = Self(c::ECHRNG); + /// `ECOMM` + #[cfg(not(any( + bsd, + windows, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "hurd", + target_os = "l4re", + target_os = "vita", + target_os = "wasi", + )))] + pub const COMM: Self = Self(c::ECOMM); + /// `ECONNABORTED` + pub const CONNABORTED: Self = Self(c::ECONNABORTED); + /// `ECONNREFUSED` + pub const CONNREFUSED: Self = Self(c::ECONNREFUSED); + /// `ECONNRESET` + pub const CONNRESET: Self = Self(c::ECONNRESET); + /// `EDEADLK` + #[cfg(not(windows))] + pub const DEADLK: Self = Self(c::EDEADLK); + /// `EDEADLOCK` + #[cfg(not(any( + bsd, + windows, + target_os = "aix", + target_os = "android", + target_os = "espidf", + target_os = "haiku", + target_os = "hurd", + target_os = "vita", + target_os = "wasi", + )))] + pub const DEADLOCK: Self = Self(c::EDEADLOCK); + /// `EDESTADDRREQ` + #[cfg(not(target_os = "l4re"))] + pub const DESTADDRREQ: Self = Self(c::EDESTADDRREQ); + /// `EDISCON` + #[cfg(windows)] + pub const DISCON: Self = Self(c::EDISCON); + /// `EDOM` + #[cfg(not(windows))] + pub const DOM: Self = Self(c::EDOM); + /// `EDOOFUS` + #[cfg(freebsdlike)] + pub const DOOFUS: Self = Self(c::EDOOFUS); + /// `EDOTDOT` + #[cfg(not(any( + bsd, + solarish, + windows, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "hurd", + target_os = "l4re", + target_os = "nto", + target_os = "vita", + target_os = "wasi", + )))] + pub const DOTDOT: Self = Self(c::EDOTDOT); + /// `EDQUOT` + pub const DQUOT: Self = Self(c::EDQUOT); + /// `EEXIST` + #[cfg(not(windows))] + pub const EXIST: Self = Self(c::EEXIST); + /// `EFAULT` + pub const FAULT: Self = Self(c::EFAULT); + /// `EFBIG` + #[cfg(not(windows))] + pub const FBIG: Self = Self(c::EFBIG); + /// `EFTYPE` + #[cfg(any(bsd, target_env = "newlib"))] + pub const FTYPE: Self = Self(c::EFTYPE); + /// `EHOSTDOWN` + #[cfg(not(any(target_os = "l4re", target_os = "wasi")))] + pub const HOSTDOWN: Self = Self(c::EHOSTDOWN); + /// `EHOSTUNREACH` + pub const HOSTUNREACH: Self = Self(c::EHOSTUNREACH); + /// `EHWPOISON` + #[cfg(not(any( + bsd, + solarish, + windows, + target_os = "aix", + target_os = "android", + target_os = "espidf", + target_os = "haiku", + target_os = "hurd", + target_os = "l4re", + target_os = "nto", + target_os = "redox", + target_os = "vita", + target_os = "wasi", + )))] + pub const HWPOISON: Self = Self(c::EHWPOISON); + /// `EIDRM` + #[cfg(not(any(windows, target_os = "l4re")))] + pub const IDRM: Self = Self(c::EIDRM); + /// `EILSEQ` + #[cfg(not(any(windows, target_os = "l4re")))] + pub const ILSEQ: Self = Self(c::EILSEQ); + /// `EINPROGRESS` + #[cfg(not(target_os = "l4re"))] + pub const INPROGRESS: Self = Self(c::EINPROGRESS); + /// `EINTR` + /// + /// For a convenient way to retry system calls that exit with `INTR`, use + /// [`retry_on_intr`]. + /// + /// [`retry_on_intr`]: crate::io::retry_on_intr + pub const INTR: Self = Self(c::EINTR); + /// `EINVAL` + pub const INVAL: Self = Self(c::EINVAL); + /// `EINVALIDPROCTABLE` + #[cfg(windows)] + pub const INVALIDPROCTABLE: Self = Self(c::EINVALIDPROCTABLE); + /// `EINVALIDPROVIDER` + #[cfg(windows)] + pub const INVALIDPROVIDER: Self = Self(c::EINVALIDPROVIDER); + /// `EIO` + #[cfg(not(windows))] + pub const IO: Self = Self(c::EIO); + /// `EISCONN` + #[cfg(not(target_os = "l4re"))] + pub const ISCONN: Self = Self(c::EISCONN); + /// `EISDIR` + #[cfg(not(windows))] + pub const ISDIR: Self = Self(c::EISDIR); + /// `EISNAM` + #[cfg(not(any( + bsd, + solarish, + windows, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "hurd", + target_os = "l4re", + target_os = "nto", + target_os = "vita", + target_os = "wasi", + )))] + pub const ISNAM: Self = Self(c::EISNAM); + /// `EKEYEXPIRED` + #[cfg(not(any( + bsd, + solarish, + windows, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "hurd", + target_os = "l4re", + target_os = "nto", + target_os = "vita", + target_os = "wasi", + )))] + pub const KEYEXPIRED: Self = Self(c::EKEYEXPIRED); + /// `EKEYREJECTED` + #[cfg(not(any( + bsd, + solarish, + windows, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "hurd", + target_os = "l4re", + target_os = "nto", + target_os = "vita", + target_os = "wasi", + )))] + pub const KEYREJECTED: Self = Self(c::EKEYREJECTED); + /// `EKEYREVOKED` + #[cfg(not(any( + bsd, + solarish, + windows, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "hurd", + target_os = "l4re", + target_os = "nto", + target_os = "vita", + target_os = "wasi", + )))] + pub const KEYREVOKED: Self = Self(c::EKEYREVOKED); + /// `EL2HLT` + #[cfg(not(any( + bsd, + windows, + target_os = "espidf", + target_os = "haiku", + target_os = "hurd", + target_os = "l4re", + target_os = "vita", + target_os = "wasi" + )))] + pub const L2HLT: Self = Self(c::EL2HLT); + /// `EL2NSYNC` + #[cfg(not(any( + bsd, + windows, + target_os = "espidf", + target_os = "haiku", + target_os = "hurd", + target_os = "l4re", + target_os = "vita", + target_os = "wasi" + )))] + pub const L2NSYNC: Self = Self(c::EL2NSYNC); + /// `EL3HLT` + #[cfg(not(any( + bsd, + windows, + target_os = "espidf", + target_os = "haiku", + target_os = "hurd", + target_os = "l4re", + target_os = "vita", + target_os = "wasi" + )))] + pub const L3HLT: Self = Self(c::EL3HLT); + /// `EL3RST` + #[cfg(not(any( + bsd, + windows, + target_os = "espidf", + target_os = "haiku", + target_os = "hurd", + target_os = "l4re", + target_os = "vita", + target_os = "wasi" + )))] + pub const L3RST: Self = Self(c::EL3RST); + /// `ELIBACC` + #[cfg(not(any( + bsd, + windows, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "hurd", + target_os = "l4re", + target_os = "vita", + target_os = "wasi", + )))] + pub const LIBACC: Self = Self(c::ELIBACC); + /// `ELIBBAD` + #[cfg(not(any( + bsd, + windows, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "hurd", + target_os = "l4re", + target_os = "vita", + target_os = "wasi", + )))] + pub const LIBBAD: Self = Self(c::ELIBBAD); + /// `ELIBEXEC` + #[cfg(not(any( + bsd, + windows, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "l4re", + target_os = "vita", + target_os = "wasi", + )))] + pub const LIBEXEC: Self = Self(c::ELIBEXEC); + /// `ELIBMAX` + #[cfg(not(any( + bsd, + windows, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "hurd", + target_os = "l4re", + target_os = "vita", + target_os = "wasi", + )))] + pub const LIBMAX: Self = Self(c::ELIBMAX); + /// `ELIBSCN` + #[cfg(not(any( + bsd, + windows, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "hurd", + target_os = "l4re", + target_os = "vita", + target_os = "wasi", + )))] + pub const LIBSCN: Self = Self(c::ELIBSCN); + /// `ELNRNG` + #[cfg(not(any( + bsd, + windows, + target_os = "espidf", + target_os = "haiku", + target_os = "hurd", + target_os = "l4re", + target_os = "vita", + target_os = "wasi" + )))] + pub const LNRNG: Self = Self(c::ELNRNG); + /// `ELOOP` + pub const LOOP: Self = Self(c::ELOOP); + /// `EMEDIUMTYPE` + #[cfg(not(any( + bsd, + solarish, + windows, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "hurd", + target_os = "l4re", + target_os = "nto", + target_os = "vita", + target_os = "wasi", + )))] + pub const MEDIUMTYPE: Self = Self(c::EMEDIUMTYPE); + /// `EMFILE` + pub const MFILE: Self = Self(c::EMFILE); + /// `EMLINK` + #[cfg(not(windows))] + pub const MLINK: Self = Self(c::EMLINK); + /// `EMSGSIZE` + #[cfg(not(target_os = "l4re"))] + pub const MSGSIZE: Self = Self(c::EMSGSIZE); + /// `EMULTIHOP` + #[cfg(not(any(windows, target_os = "l4re", target_os = "openbsd")))] + pub const MULTIHOP: Self = Self(c::EMULTIHOP); + /// `ENAMETOOLONG` + pub const NAMETOOLONG: Self = Self(c::ENAMETOOLONG); + /// `ENAVAIL` + #[cfg(not(any( + bsd, + solarish, + windows, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "hurd", + target_os = "l4re", + target_os = "nto", + target_os = "vita", + target_os = "wasi", + )))] + pub const NAVAIL: Self = Self(c::ENAVAIL); + /// `ENEEDAUTH` + #[cfg(bsd)] + pub const NEEDAUTH: Self = Self(c::ENEEDAUTH); + /// `ENETDOWN` + pub const NETDOWN: Self = Self(c::ENETDOWN); + /// `ENETRESET` + #[cfg(not(target_os = "l4re"))] + pub const NETRESET: Self = Self(c::ENETRESET); + /// `ENETUNREACH` + pub const NETUNREACH: Self = Self(c::ENETUNREACH); + /// `ENFILE` + #[cfg(not(windows))] + pub const NFILE: Self = Self(c::ENFILE); + /// `ENOANO` + #[cfg(not(any( + bsd, + windows, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "hurd", + target_os = "l4re", + target_os = "vita", + target_os = "wasi", + )))] + pub const NOANO: Self = Self(c::ENOANO); + /// `ENOATTR` + #[cfg(any(bsd, target_os = "haiku"))] + pub const NOATTR: Self = Self(c::ENOATTR); + /// `ENOBUFS` + #[cfg(not(target_os = "l4re"))] + pub const NOBUFS: Self = Self(c::ENOBUFS); + /// `ENOCSI` + #[cfg(not(any( + bsd, + windows, + target_os = "espidf", + target_os = "haiku", + target_os = "hurd", + target_os = "l4re", + target_os = "vita", + target_os = "wasi" + )))] + pub const NOCSI: Self = Self(c::ENOCSI); + /// `ENODATA` + #[cfg(not(any( + freebsdlike, + windows, + target_os = "haiku", + target_os = "openbsd", + target_os = "wasi", + )))] + pub const NODATA: Self = Self(c::ENODATA); + /// `ENODEV` + #[cfg(not(windows))] + pub const NODEV: Self = Self(c::ENODEV); + /// `ENOENT` + #[cfg(not(windows))] + pub const NOENT: Self = Self(c::ENOENT); + /// `ENOEXEC` + #[cfg(not(windows))] + pub const NOEXEC: Self = Self(c::ENOEXEC); + /// `ENOKEY` + #[cfg(not(any( + solarish, + bsd, + windows, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "hurd", + target_os = "l4re", + target_os = "nto", + target_os = "vita", + target_os = "wasi", + )))] + pub const NOKEY: Self = Self(c::ENOKEY); + /// `ENOLCK` + #[cfg(not(any(windows, target_os = "l4re")))] + pub const NOLCK: Self = Self(c::ENOLCK); + /// `ENOLINK` + #[cfg(not(any(windows, target_os = "l4re", target_os = "openbsd")))] + pub const NOLINK: Self = Self(c::ENOLINK); + /// `ENOMEDIUM` + #[cfg(not(any( + bsd, + solarish, + windows, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "hurd", + target_os = "l4re", + target_os = "nto", + target_os = "vita", + target_os = "wasi", + )))] + pub const NOMEDIUM: Self = Self(c::ENOMEDIUM); + /// `ENOMEM` + #[cfg(not(windows))] + pub const NOMEM: Self = Self(c::ENOMEM); + /// `ENOMORE` + #[cfg(windows)] + pub const NOMORE: Self = Self(c::ENOMORE); + /// `ENOMSG` + #[cfg(not(any(windows, target_os = "l4re")))] + pub const NOMSG: Self = Self(c::ENOMSG); + /// `ENONET` + #[cfg(not(any( + bsd, + windows, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "hurd", + target_os = "l4re", + target_os = "vita", + target_os = "wasi", + )))] + pub const NONET: Self = Self(c::ENONET); + /// `ENOPKG` + #[cfg(not(any( + bsd, + windows, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "hurd", + target_os = "l4re", + target_os = "vita", + target_os = "wasi", + )))] + pub const NOPKG: Self = Self(c::ENOPKG); + /// `ENOPROTOOPT` + #[cfg(not(target_os = "l4re"))] + pub const NOPROTOOPT: Self = Self(c::ENOPROTOOPT); + /// `ENOSPC` + #[cfg(not(windows))] + pub const NOSPC: Self = Self(c::ENOSPC); + /// `ENOSR` + #[cfg(not(any( + freebsdlike, + windows, + target_os = "haiku", + target_os = "l4re", + target_os = "openbsd", + target_os = "wasi", + )))] + pub const NOSR: Self = Self(c::ENOSR); + /// `ENOSTR` + #[cfg(not(any( + freebsdlike, + windows, + target_os = "haiku", + target_os = "l4re", + target_os = "openbsd", + target_os = "wasi", + )))] + pub const NOSTR: Self = Self(c::ENOSTR); + /// `ENOSYS` + #[cfg(not(windows))] + pub const NOSYS: Self = Self(c::ENOSYS); + /// `ENOTBLK` + #[cfg(not(any( + windows, + target_os = "espidf", + target_os = "haiku", + target_os = "vita", + target_os = "wasi" + )))] + pub const NOTBLK: Self = Self(c::ENOTBLK); + /// `ENOTCAPABLE` + #[cfg(any(target_os = "freebsd", target_os = "wasi"))] + pub const NOTCAPABLE: Self = Self(c::ENOTCAPABLE); + /// `ENOTCONN` + pub const NOTCONN: Self = Self(c::ENOTCONN); + /// `ENOTDIR` + #[cfg(not(windows))] + pub const NOTDIR: Self = Self(c::ENOTDIR); + /// `ENOTEMPTY` + pub const NOTEMPTY: Self = Self(c::ENOTEMPTY); + /// `ENOTNAM` + #[cfg(not(any( + bsd, + solarish, + windows, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "hurd", + target_os = "l4re", + target_os = "nto", + target_os = "vita", + target_os = "wasi", + )))] + pub const NOTNAM: Self = Self(c::ENOTNAM); + /// `ENOTRECOVERABLE` + #[cfg(not(any( + freebsdlike, + netbsdlike, + windows, + target_os = "haiku", + target_os = "l4re" + )))] + pub const NOTRECOVERABLE: Self = Self(c::ENOTRECOVERABLE); + /// `ENOTSOCK` + #[cfg(not(target_os = "l4re"))] + pub const NOTSOCK: Self = Self(c::ENOTSOCK); + /// `ENOTSUP` + #[cfg(not(any(windows, target_os = "haiku", target_os = "redox")))] + pub const NOTSUP: Self = Self(c::ENOTSUP); + /// `ENOTTY` + #[cfg(not(windows))] + pub const NOTTY: Self = Self(c::ENOTTY); + /// `ENOTUNIQ` + #[cfg(not(any( + bsd, + windows, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "hurd", + target_os = "l4re", + target_os = "vita", + target_os = "wasi", + )))] + pub const NOTUNIQ: Self = Self(c::ENOTUNIQ); + /// `ENXIO` + #[cfg(not(windows))] + pub const NXIO: Self = Self(c::ENXIO); + /// `EOPNOTSUPP` + pub const OPNOTSUPP: Self = Self(c::EOPNOTSUPP); + /// `EOVERFLOW` + #[cfg(not(any(windows, target_os = "l4re")))] + pub const OVERFLOW: Self = Self(c::EOVERFLOW); + /// `EOWNERDEAD` + #[cfg(not(any( + freebsdlike, + netbsdlike, + windows, + target_os = "haiku", + target_os = "l4re" + )))] + pub const OWNERDEAD: Self = Self(c::EOWNERDEAD); + /// `EPERM` + #[cfg(not(windows))] + pub const PERM: Self = Self(c::EPERM); + /// `EPFNOSUPPORT` + #[cfg(not(any(target_os = "l4re", target_os = "wasi")))] + pub const PFNOSUPPORT: Self = Self(c::EPFNOSUPPORT); + /// `EPIPE` + #[cfg(not(windows))] + pub const PIPE: Self = Self(c::EPIPE); + /// `EPROCLIM` + #[cfg(bsd)] + pub const PROCLIM: Self = Self(c::EPROCLIM); + /// `EPROCUNAVAIL` + #[cfg(bsd)] + pub const PROCUNAVAIL: Self = Self(c::EPROCUNAVAIL); + /// `EPROGMISMATCH` + #[cfg(bsd)] + pub const PROGMISMATCH: Self = Self(c::EPROGMISMATCH); + /// `EPROGUNAVAIL` + #[cfg(bsd)] + pub const PROGUNAVAIL: Self = Self(c::EPROGUNAVAIL); + /// `EPROTO` + #[cfg(not(any(windows, target_os = "l4re")))] + pub const PROTO: Self = Self(c::EPROTO); + /// `EPROTONOSUPPORT` + #[cfg(not(target_os = "l4re"))] + pub const PROTONOSUPPORT: Self = Self(c::EPROTONOSUPPORT); + /// `EPROTOTYPE` + #[cfg(not(target_os = "l4re"))] + pub const PROTOTYPE: Self = Self(c::EPROTOTYPE); + /// `EPROVIDERFAILEDINIT` + #[cfg(windows)] + pub const PROVIDERFAILEDINIT: Self = Self(c::EPROVIDERFAILEDINIT); + /// `ERANGE` + #[cfg(not(windows))] + pub const RANGE: Self = Self(c::ERANGE); + /// `EREFUSED` + #[cfg(windows)] + pub const REFUSED: Self = Self(c::EREFUSED); + /// `EREMCHG` + #[cfg(not(any( + bsd, + windows, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "hurd", + target_os = "l4re", + target_os = "vita", + target_os = "wasi", + )))] + pub const REMCHG: Self = Self(c::EREMCHG); + /// `EREMOTE` + #[cfg(not(any( + target_os = "espidf", + target_os = "haiku", + target_os = "l4re", + target_os = "vita", + target_os = "wasi" + )))] + pub const REMOTE: Self = Self(c::EREMOTE); + /// `EREMOTEIO` + #[cfg(not(any( + bsd, + solarish, + windows, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "hurd", + target_os = "l4re", + target_os = "nto", + target_os = "vita", + target_os = "wasi", + )))] + pub const REMOTEIO: Self = Self(c::EREMOTEIO); + /// `ERESTART` + #[cfg(not(any( + bsd, + windows, + target_os = "espidf", + target_os = "haiku", + target_os = "hurd", + target_os = "l4re", + target_os = "vita", + target_os = "wasi" + )))] + pub const RESTART: Self = Self(c::ERESTART); + /// `ERFKILL` + #[cfg(not(any( + bsd, + solarish, + windows, + target_os = "aix", + target_os = "android", + target_os = "espidf", + target_os = "haiku", + target_os = "hurd", + target_os = "l4re", + target_os = "nto", + target_os = "redox", + target_os = "vita", + target_os = "wasi", + )))] + pub const RFKILL: Self = Self(c::ERFKILL); + /// `EROFS` + #[cfg(not(windows))] + pub const ROFS: Self = Self(c::EROFS); + /// `ERPCMISMATCH` + #[cfg(bsd)] + pub const RPCMISMATCH: Self = Self(c::ERPCMISMATCH); + /// `ESHUTDOWN` + #[cfg(not(any( + target_os = "espidf", + target_os = "l4re", + target_os = "vita", + target_os = "wasi" + )))] + pub const SHUTDOWN: Self = Self(c::ESHUTDOWN); + /// `ESOCKTNOSUPPORT` + #[cfg(not(any( + target_os = "espidf", + target_os = "haiku", + target_os = "l4re", + target_os = "vita", + target_os = "wasi" + )))] + pub const SOCKTNOSUPPORT: Self = Self(c::ESOCKTNOSUPPORT); + /// `ESPIPE` + #[cfg(not(windows))] + pub const SPIPE: Self = Self(c::ESPIPE); + /// `ESRCH` + #[cfg(not(windows))] + pub const SRCH: Self = Self(c::ESRCH); + /// `ESRMNT` + #[cfg(not(any( + bsd, + windows, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "hurd", + target_os = "l4re", + target_os = "vita", + target_os = "wasi", + )))] + pub const SRMNT: Self = Self(c::ESRMNT); + /// `ESTALE` + pub const STALE: Self = Self(c::ESTALE); + /// `ESTRPIPE` + #[cfg(not(any( + bsd, + windows, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "hurd", + target_os = "l4re", + target_os = "vita", + target_os = "wasi", + )))] + pub const STRPIPE: Self = Self(c::ESTRPIPE); + /// `ETIME` + #[cfg(not(any( + freebsdlike, + windows, + target_os = "l4re", + target_os = "openbsd", + target_os = "wasi" + )))] + pub const TIME: Self = Self(c::ETIME); + /// `ETIMEDOUT` + pub const TIMEDOUT: Self = Self(c::ETIMEDOUT); + /// `E2BIG` + #[cfg(not(windows))] + #[doc(alias = "2BIG")] + pub const TOOBIG: Self = Self(c::E2BIG); + /// `ETOOMANYREFS` + #[cfg(not(any(target_os = "haiku", target_os = "l4re", target_os = "wasi")))] + pub const TOOMANYREFS: Self = Self(c::ETOOMANYREFS); + /// `ETXTBSY` + #[cfg(not(windows))] + pub const TXTBSY: Self = Self(c::ETXTBSY); + /// `EUCLEAN` + #[cfg(not(any( + bsd, + solarish, + windows, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "hurd", + target_os = "l4re", + target_os = "nto", + target_os = "vita", + target_os = "wasi", + )))] + pub const UCLEAN: Self = Self(c::EUCLEAN); + /// `EUNATCH` + #[cfg(not(any( + bsd, + windows, + target_os = "espidf", + target_os = "haiku", + target_os = "hurd", + target_os = "l4re", + target_os = "vita", + target_os = "wasi" + )))] + pub const UNATCH: Self = Self(c::EUNATCH); + /// `EUSERS` + #[cfg(not(any( + target_os = "espidf", + target_os = "haiku", + target_os = "l4re", + target_os = "vita", + target_os = "wasi" + )))] + pub const USERS: Self = Self(c::EUSERS); + /// `EWOULDBLOCK` + pub const WOULDBLOCK: Self = Self(c::EWOULDBLOCK); + /// `EXDEV` + #[cfg(not(windows))] + pub const XDEV: Self = Self(c::EXDEV); + /// `EXFULL` + #[cfg(not(any( + bsd, + windows, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "hurd", + target_os = "l4re", + target_os = "vita", + target_os = "wasi", + )))] + pub const XFULL: Self = Self(c::EXFULL); +} + +impl Errno { + /// Extract an `Errno` value from a `std::io::Error`. + /// + /// This isn't a `From` conversion because it's expected to be relatively + /// uncommon. + #[cfg(feature = "std")] + #[inline] + pub fn from_io_error(io_err: &std::io::Error) -> Option<Self> { + io_err + .raw_os_error() + .and_then(|raw| if raw != 0 { Some(Self(raw)) } else { None }) + } + + /// Extract the raw OS error number from this error. + #[inline] + pub const fn raw_os_error(self) -> i32 { + self.0 + } + + /// Construct an `Errno` from a raw OS error number. + #[inline] + pub const fn from_raw_os_error(raw: i32) -> Self { + Self(raw) + } + + pub(crate) fn last_os_error() -> Self { + Self(errno().0) + } +} diff --git a/vendor/rustix/src/backend/libc/io/mod.rs b/vendor/rustix/src/backend/libc/io/mod.rs new file mode 100644 index 0000000..4873885 --- /dev/null +++ b/vendor/rustix/src/backend/libc/io/mod.rs @@ -0,0 +1,6 @@ +pub(crate) mod errno; +#[cfg(not(windows))] +pub(crate) mod types; + +#[cfg_attr(windows, path = "windows_syscalls.rs")] +pub(crate) mod syscalls; diff --git a/vendor/rustix/src/backend/libc/io/syscalls.rs b/vendor/rustix/src/backend/libc/io/syscalls.rs new file mode 100644 index 0000000..e28e6be --- /dev/null +++ b/vendor/rustix/src/backend/libc/io/syscalls.rs @@ -0,0 +1,340 @@ +//! libc syscalls supporting `rustix::io`. + +use crate::backend::c; +#[cfg(not(target_os = "wasi"))] +use crate::backend::conv::ret_discarded_fd; +use crate::backend::conv::{borrowed_fd, ret, ret_c_int, ret_owned_fd, ret_usize}; +use crate::fd::{AsFd, BorrowedFd, OwnedFd, RawFd}; +#[cfg(not(any( + target_os = "aix", + target_os = "espidf", + target_os = "nto", + target_os = "vita", + target_os = "wasi" +)))] +use crate::io::DupFlags; +#[cfg(linux_kernel)] +use crate::io::ReadWriteFlags; +use crate::io::{self, FdFlags}; +use crate::ioctl::{IoctlOutput, RawOpcode}; +use core::cmp::min; +#[cfg(all(feature = "fs", feature = "net"))] +use libc_errno::errno; +#[cfg(not(target_os = "espidf"))] +use { + crate::backend::MAX_IOV, + crate::io::{IoSlice, IoSliceMut}, +}; + +pub(crate) unsafe fn read(fd: BorrowedFd<'_>, buf: *mut u8, len: usize) -> io::Result<usize> { + ret_usize(c::read(borrowed_fd(fd), buf.cast(), min(len, READ_LIMIT))) +} + +pub(crate) fn write(fd: BorrowedFd<'_>, buf: &[u8]) -> io::Result<usize> { + unsafe { + ret_usize(c::write( + borrowed_fd(fd), + buf.as_ptr().cast(), + min(buf.len(), READ_LIMIT), + )) + } +} + +pub(crate) unsafe fn pread( + fd: BorrowedFd<'_>, + buf: *mut u8, + len: usize, + offset: u64, +) -> io::Result<usize> { + let len = min(len, READ_LIMIT); + + // Silently cast; we'll get `EINVAL` if the value is negative. + let offset = offset as i64; + + // ESP-IDF and Vita don't support 64-bit offsets. + #[cfg(any(target_os = "espidf", target_os = "vita"))] + let offset: i32 = offset.try_into().map_err(|_| io::Errno::OVERFLOW)?; + + ret_usize(c::pread(borrowed_fd(fd), buf.cast(), len, offset)) +} + +pub(crate) fn pwrite(fd: BorrowedFd<'_>, buf: &[u8], offset: u64) -> io::Result<usize> { + let len = min(buf.len(), READ_LIMIT); + + // Silently cast; we'll get `EINVAL` if the value is negative. + let offset = offset as i64; + + // ESP-IDF and Vita don't support 64-bit offsets. + #[cfg(any(target_os = "espidf", target_os = "vita"))] + let offset: i32 = offset.try_into().map_err(|_| io::Errno::OVERFLOW)?; + + unsafe { ret_usize(c::pwrite(borrowed_fd(fd), buf.as_ptr().cast(), len, offset)) } +} + +#[cfg(not(target_os = "espidf"))] +pub(crate) fn readv(fd: BorrowedFd<'_>, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { + unsafe { + ret_usize(c::readv( + borrowed_fd(fd), + bufs.as_ptr().cast::<c::iovec>(), + min(bufs.len(), MAX_IOV) as c::c_int, + )) + } +} + +#[cfg(not(target_os = "espidf"))] +pub(crate) fn writev(fd: BorrowedFd<'_>, bufs: &[IoSlice<'_>]) -> io::Result<usize> { + unsafe { + ret_usize(c::writev( + borrowed_fd(fd), + bufs.as_ptr().cast::<c::iovec>(), + min(bufs.len(), MAX_IOV) as c::c_int, + )) + } +} + +#[cfg(not(any( + target_os = "espidf", + target_os = "haiku", + target_os = "nto", + target_os = "redox", + target_os = "solaris", + target_os = "vita" +)))] +pub(crate) fn preadv( + fd: BorrowedFd<'_>, + bufs: &mut [IoSliceMut<'_>], + offset: u64, +) -> io::Result<usize> { + // Silently cast; we'll get `EINVAL` if the value is negative. + let offset = offset as i64; + unsafe { + ret_usize(c::preadv( + borrowed_fd(fd), + bufs.as_ptr().cast::<c::iovec>(), + min(bufs.len(), MAX_IOV) as c::c_int, + offset, + )) + } +} + +#[cfg(not(any( + target_os = "espidf", + target_os = "haiku", + target_os = "nto", + target_os = "redox", + target_os = "solaris", + target_os = "vita" +)))] +pub(crate) fn pwritev(fd: BorrowedFd<'_>, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> { + // Silently cast; we'll get `EINVAL` if the value is negative. + let offset = offset as i64; + unsafe { + ret_usize(c::pwritev( + borrowed_fd(fd), + bufs.as_ptr().cast::<c::iovec>(), + min(bufs.len(), MAX_IOV) as c::c_int, + offset, + )) + } +} + +#[cfg(linux_kernel)] +pub(crate) fn preadv2( + fd: BorrowedFd<'_>, + bufs: &mut [IoSliceMut<'_>], + offset: u64, + flags: ReadWriteFlags, +) -> io::Result<usize> { + // Silently cast; we'll get `EINVAL` if the value is negative. + let offset = offset as i64; + unsafe { + ret_usize(c::preadv2( + borrowed_fd(fd), + bufs.as_ptr().cast::<c::iovec>(), + min(bufs.len(), MAX_IOV) as c::c_int, + offset, + bitflags_bits!(flags), + )) + } +} + +#[cfg(linux_kernel)] +pub(crate) fn pwritev2( + fd: BorrowedFd<'_>, + bufs: &[IoSlice<'_>], + offset: u64, + flags: ReadWriteFlags, +) -> io::Result<usize> { + // Silently cast; we'll get `EINVAL` if the value is negative. + let offset = offset as i64; + unsafe { + ret_usize(c::pwritev2( + borrowed_fd(fd), + bufs.as_ptr().cast::<c::iovec>(), + min(bufs.len(), MAX_IOV) as c::c_int, + offset, + bitflags_bits!(flags), + )) + } +} + +// These functions are derived from Rust's library/std/src/sys/unix/fd.rs at +// revision 326ef470a8b379a180d6dc4bbef08990698a737a. + +// The maximum read limit on most POSIX-like systems is `SSIZE_MAX`, with the +// manual page quoting that if the count of bytes to read is greater than +// `SSIZE_MAX` the result is “unspecified”. +// +// On macOS, however, apparently the 64-bit libc is either buggy or +// intentionally showing odd behavior by rejecting any read with a size larger +// than or equal to `INT_MAX`. To handle both of these the read size is capped +// on both platforms. +#[cfg(target_os = "macos")] +const READ_LIMIT: usize = c::c_int::MAX as usize - 1; +#[cfg(not(target_os = "macos"))] +const READ_LIMIT: usize = c::ssize_t::MAX as usize; + +pub(crate) unsafe fn close(raw_fd: RawFd) { + let _ = c::close(raw_fd as c::c_int); +} + +#[inline] +pub(crate) unsafe fn ioctl( + fd: BorrowedFd<'_>, + request: RawOpcode, + arg: *mut c::c_void, +) -> io::Result<IoctlOutput> { + ret_c_int(c::ioctl(borrowed_fd(fd), request, arg)) +} + +#[inline] +pub(crate) unsafe fn ioctl_readonly( + fd: BorrowedFd<'_>, + request: RawOpcode, + arg: *mut c::c_void, +) -> io::Result<IoctlOutput> { + ioctl(fd, request, arg) +} + +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] +#[cfg(all(feature = "fs", feature = "net"))] +pub(crate) fn is_read_write(fd: BorrowedFd<'_>) -> io::Result<(bool, bool)> { + use core::mem::MaybeUninit; + + let (mut read, mut write) = crate::fs::fd::_is_file_read_write(fd)?; + let mut not_socket = false; + if read { + // Do a `recv` with `PEEK` and `DONTWAIT` for 1 byte. A 0 indicates + // the read side is shut down; an `EWOULDBLOCK` indicates the read + // side is still open. + match unsafe { + c::recv( + borrowed_fd(fd), + MaybeUninit::<[u8; 1]>::uninit() + .as_mut_ptr() + .cast::<c::c_void>(), + 1, + c::MSG_PEEK | c::MSG_DONTWAIT, + ) + } { + 0 => read = false, + -1 => { + #[allow(unreachable_patterns)] // `EAGAIN` may equal `EWOULDBLOCK` + match errno().0 { + c::EAGAIN | c::EWOULDBLOCK => (), + c::ENOTSOCK => not_socket = true, + err => return Err(io::Errno(err)), + } + } + _ => (), + } + } + if write && !not_socket { + // Do a `send` with `DONTWAIT` for 0 bytes. An `EPIPE` indicates + // the write side is shut down. + if unsafe { c::send(borrowed_fd(fd), [].as_ptr(), 0, c::MSG_DONTWAIT) } == -1 { + #[allow(unreachable_patterns)] // `EAGAIN` may equal `EWOULDBLOCK` + match errno().0 { + c::EAGAIN | c::EWOULDBLOCK | c::ENOTSOCK => (), + c::EPIPE => write = false, + err => return Err(io::Errno(err)), + } + } + } + Ok((read, write)) +} + +#[cfg(target_os = "wasi")] +#[cfg(all(feature = "fs", feature = "net"))] +pub(crate) fn is_read_write(_fd: BorrowedFd<'_>) -> io::Result<(bool, bool)> { + todo!("Implement is_read_write for WASI in terms of fd_fdstat_get"); +} + +pub(crate) fn fcntl_getfd(fd: BorrowedFd<'_>) -> io::Result<FdFlags> { + let flags = unsafe { ret_c_int(c::fcntl(borrowed_fd(fd), c::F_GETFD))? }; + Ok(FdFlags::from_bits_retain(bitcast!(flags))) +} + +pub(crate) fn fcntl_setfd(fd: BorrowedFd<'_>, flags: FdFlags) -> io::Result<()> { + unsafe { ret(c::fcntl(borrowed_fd(fd), c::F_SETFD, flags.bits())) } +} + +#[cfg(not(any(target_os = "espidf", target_os = "wasi")))] +pub(crate) fn fcntl_dupfd_cloexec(fd: BorrowedFd<'_>, min: RawFd) -> io::Result<OwnedFd> { + unsafe { ret_owned_fd(c::fcntl(borrowed_fd(fd), c::F_DUPFD_CLOEXEC, min)) } +} + +#[cfg(target_os = "espidf")] +pub(crate) fn fcntl_dupfd(fd: BorrowedFd<'_>, min: RawFd) -> io::Result<OwnedFd> { + unsafe { ret_owned_fd(c::fcntl(borrowed_fd(fd), c::F_DUPFD, min)) } +} + +#[cfg(not(target_os = "wasi"))] +pub(crate) fn dup(fd: BorrowedFd<'_>) -> io::Result<OwnedFd> { + unsafe { ret_owned_fd(c::dup(borrowed_fd(fd))) } +} + +#[allow(clippy::needless_pass_by_ref_mut)] +#[cfg(not(target_os = "wasi"))] +pub(crate) fn dup2(fd: BorrowedFd<'_>, new: &mut OwnedFd) -> io::Result<()> { + unsafe { ret_discarded_fd(c::dup2(borrowed_fd(fd), borrowed_fd(new.as_fd()))) } +} + +#[allow(clippy::needless_pass_by_ref_mut)] +#[cfg(not(any( + apple, + target_os = "aix", + target_os = "android", + target_os = "dragonfly", + target_os = "espidf", + target_os = "haiku", + target_os = "nto", + target_os = "redox", + target_os = "vita", + target_os = "wasi", +)))] +pub(crate) fn dup3(fd: BorrowedFd<'_>, new: &mut OwnedFd, flags: DupFlags) -> io::Result<()> { + unsafe { + ret_discarded_fd(c::dup3( + borrowed_fd(fd), + borrowed_fd(new.as_fd()), + bitflags_bits!(flags), + )) + } +} + +#[cfg(any( + apple, + target_os = "android", + target_os = "dragonfly", + target_os = "haiku", + target_os = "redox", +))] +pub(crate) fn dup3(fd: BorrowedFd<'_>, new: &mut OwnedFd, _flags: DupFlags) -> io::Result<()> { + // Android 5.0 has `dup3`, but libc doesn't have bindings. Emulate it + // using `dup2`. We don't need to worry about the difference between + // `dup2` and `dup3` when the file descriptors are equal because we + // have an `&mut OwnedFd` which means `fd` doesn't alias it. + dup2(fd, new) +} diff --git a/vendor/rustix/src/backend/libc/io/types.rs b/vendor/rustix/src/backend/libc/io/types.rs new file mode 100644 index 0000000..510206f --- /dev/null +++ b/vendor/rustix/src/backend/libc/io/types.rs @@ -0,0 +1,65 @@ +use crate::backend::c; +use bitflags::bitflags; + +bitflags! { + /// `FD_*` constants for use with [`fcntl_getfd`] and [`fcntl_setfd`]. + /// + /// [`fcntl_getfd`]: crate::io::fcntl_getfd + /// [`fcntl_setfd`]: crate::io::fcntl_setfd + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct FdFlags: u32 { + /// `FD_CLOEXEC` + const CLOEXEC = bitcast!(c::FD_CLOEXEC); + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} + +#[cfg(linux_kernel)] +bitflags! { + /// `RWF_*` constants for use with [`preadv2`] and [`pwritev2`]. + /// + /// [`preadv2`]: crate::io::preadv2 + /// [`pwritev2`]: crate::io::pwritev + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct ReadWriteFlags: u32 { + /// `RWF_DSYNC` (since Linux 4.7) + const DSYNC = linux_raw_sys::general::RWF_DSYNC; + /// `RWF_HIPRI` (since Linux 4.6) + const HIPRI = linux_raw_sys::general::RWF_HIPRI; + /// `RWF_SYNC` (since Linux 4.7) + const SYNC = linux_raw_sys::general::RWF_SYNC; + /// `RWF_NOWAIT` (since Linux 4.14) + const NOWAIT = linux_raw_sys::general::RWF_NOWAIT; + /// `RWF_APPEND` (since Linux 4.16) + const APPEND = linux_raw_sys::general::RWF_APPEND; + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} + +#[cfg(not(target_os = "wasi"))] +bitflags! { + /// `O_*` constants for use with [`dup2`]. + /// + /// [`dup2`]: crate::io::dup2 + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct DupFlags: u32 { + /// `O_CLOEXEC` + #[cfg(not(any( + apple, + target_os = "aix", + target_os = "android", + target_os = "redox", + )))] // Android 5.0 has dup3, but libc doesn't have bindings + const CLOEXEC = bitcast!(c::O_CLOEXEC); + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} diff --git a/vendor/rustix/src/backend/libc/io/windows_syscalls.rs b/vendor/rustix/src/backend/libc/io/windows_syscalls.rs new file mode 100644 index 0000000..049221d --- /dev/null +++ b/vendor/rustix/src/backend/libc/io/windows_syscalls.rs @@ -0,0 +1,30 @@ +//! Windows system calls in the `io` module. + +use crate::backend::c; +use crate::backend::conv::{borrowed_fd, ret_c_int}; +use crate::backend::fd::LibcFd; +use crate::fd::{BorrowedFd, RawFd}; +use crate::io; +use crate::ioctl::{IoctlOutput, RawOpcode}; + +pub(crate) unsafe fn close(raw_fd: RawFd) { + let _ = c::close(raw_fd as LibcFd); +} + +#[inline] +pub(crate) unsafe fn ioctl( + fd: BorrowedFd<'_>, + request: RawOpcode, + arg: *mut c::c_void, +) -> io::Result<IoctlOutput> { + ret_c_int(c::ioctl(borrowed_fd(fd), request, arg.cast())) +} + +#[inline] +pub(crate) unsafe fn ioctl_readonly( + fd: BorrowedFd<'_>, + request: RawOpcode, + arg: *mut c::c_void, +) -> io::Result<IoctlOutput> { + ioctl(fd, request, arg) +} diff --git a/vendor/rustix/src/backend/libc/io_uring/mod.rs b/vendor/rustix/src/backend/libc/io_uring/mod.rs new file mode 100644 index 0000000..ef944f0 --- /dev/null +++ b/vendor/rustix/src/backend/libc/io_uring/mod.rs @@ -0,0 +1 @@ +pub(crate) mod syscalls; diff --git a/vendor/rustix/src/backend/libc/io_uring/syscalls.rs b/vendor/rustix/src/backend/libc/io_uring/syscalls.rs new file mode 100644 index 0000000..8e81824 --- /dev/null +++ b/vendor/rustix/src/backend/libc/io_uring/syscalls.rs @@ -0,0 +1,70 @@ +//! libc syscalls supporting `rustix::io_uring`. + +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}; + +#[inline] +pub(crate) fn io_uring_setup(entries: u32, params: &mut io_uring_params) -> io::Result<OwnedFd> { + syscall! { + fn io_uring_setup( + entries: u32, + params: *mut io_uring_params + ) via SYS_io_uring_setup -> c::c_int + } + unsafe { ret_owned_fd(io_uring_setup(entries, params)) } +} + +#[inline] +pub(crate) unsafe fn io_uring_register( + fd: BorrowedFd<'_>, + opcode: IoringRegisterOp, + arg: *const c::c_void, + nr_args: u32, +) -> io::Result<u32> { + 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, + arg, + nr_args, + )) +} + +#[inline] +pub(crate) unsafe fn io_uring_enter( + fd: BorrowedFd<'_>, + to_submit: u32, + min_complete: u32, + flags: IoringEnterFlags, + arg: *const c::c_void, + size: usize, +) -> io::Result<u32> { + syscall! { + fn io_uring_enter2( + fd: c::c_uint, + to_submit: c::c_uint, + min_complete: c::c_uint, + flags: c::c_uint, + arg: *const c::c_void, + size: usize + ) via SYS_io_uring_enter -> c::c_int + } + ret_u32(io_uring_enter2( + borrowed_fd(fd) as _, + to_submit, + min_complete, + bitflags_bits!(flags), + arg, + size, + )) +} diff --git a/vendor/rustix/src/backend/libc/mm/mod.rs b/vendor/rustix/src/backend/libc/mm/mod.rs new file mode 100644 index 0000000..1e0181a --- /dev/null +++ b/vendor/rustix/src/backend/libc/mm/mod.rs @@ -0,0 +1,2 @@ +pub(crate) mod syscalls; +pub(crate) mod types; diff --git a/vendor/rustix/src/backend/libc/mm/syscalls.rs b/vendor/rustix/src/backend/libc/mm/syscalls.rs new file mode 100644 index 0000000..33bc9ca --- /dev/null +++ b/vendor/rustix/src/backend/libc/mm/syscalls.rs @@ -0,0 +1,244 @@ +//! libc syscalls supporting `rustix::mm`. + +#[cfg(not(target_os = "redox"))] +use super::types::Advice; +#[cfg(any(linux_kernel, freebsdlike, netbsdlike))] +use super::types::MlockAllFlags; +#[cfg(any(target_os = "emscripten", target_os = "linux"))] +use super::types::MremapFlags; +use super::types::{MapFlags, MprotectFlags, MsyncFlags, ProtFlags}; +#[cfg(linux_kernel)] +use super::types::{MlockFlags, UserfaultfdFlags}; +use crate::backend::c; +#[cfg(linux_kernel)] +use crate::backend::conv::ret_owned_fd; +use crate::backend::conv::{borrowed_fd, no_fd, ret}; +use crate::fd::BorrowedFd; +#[cfg(linux_kernel)] +use crate::fd::OwnedFd; +use crate::io; + +#[cfg(not(target_os = "redox"))] +pub(crate) fn madvise(addr: *mut c::c_void, len: usize, advice: Advice) -> io::Result<()> { + // On Linux platforms, `MADV_DONTNEED` has the same value as + // `POSIX_MADV_DONTNEED` but different behavior. We remap it to a different + // value, and check for it here. + #[cfg(target_os = "linux")] + if let Advice::LinuxDontNeed = advice { + return unsafe { ret(c::madvise(addr, len, c::MADV_DONTNEED)) }; + } + + #[cfg(not(target_os = "android"))] + { + let err = unsafe { c::posix_madvise(addr, len, advice as c::c_int) }; + + // `posix_madvise` returns its error status rather than using `errno`. + if err == 0 { + Ok(()) + } else { + Err(io::Errno(err)) + } + } + + #[cfg(target_os = "android")] + { + if let Advice::DontNeed = advice { + // Do nothing. Linux's `MADV_DONTNEED` isn't the same as + // `POSIX_MADV_DONTNEED`, so just discard `MADV_DONTNEED`. + Ok(()) + } else { + unsafe { ret(c::madvise(addr, len, advice as c::c_int)) } + } + } +} + +pub(crate) unsafe fn msync(addr: *mut c::c_void, len: usize, flags: MsyncFlags) -> io::Result<()> { + let err = c::msync(addr, len, bitflags_bits!(flags)); + + // `msync` returns its error status rather than using `errno`. + if err == 0 { + Ok(()) + } else { + Err(io::Errno(err)) + } +} + +/// # Safety +/// +/// `mmap` is primarily unsafe due to the `addr` parameter, as anything working +/// with memory pointed to by raw pointers is unsafe. +pub(crate) unsafe fn mmap( + ptr: *mut c::c_void, + len: usize, + prot: ProtFlags, + flags: MapFlags, + fd: BorrowedFd<'_>, + offset: u64, +) -> io::Result<*mut c::c_void> { + let res = c::mmap( + ptr, + len, + bitflags_bits!(prot), + bitflags_bits!(flags), + borrowed_fd(fd), + offset as i64, + ); + if res == c::MAP_FAILED { + Err(io::Errno::last_os_error()) + } else { + Ok(res) + } +} + +/// # Safety +/// +/// `mmap` is primarily unsafe due to the `addr` parameter, as anything working +/// with memory pointed to by raw pointers is unsafe. +pub(crate) unsafe fn mmap_anonymous( + ptr: *mut c::c_void, + len: usize, + prot: ProtFlags, + flags: MapFlags, +) -> io::Result<*mut c::c_void> { + let res = c::mmap( + ptr, + len, + bitflags_bits!(prot), + bitflags_bits!(flags | MapFlags::from_bits_retain(bitcast!(c::MAP_ANONYMOUS))), + no_fd(), + 0, + ); + if res == c::MAP_FAILED { + Err(io::Errno::last_os_error()) + } else { + Ok(res) + } +} + +pub(crate) unsafe fn mprotect( + ptr: *mut c::c_void, + len: usize, + flags: MprotectFlags, +) -> io::Result<()> { + ret(c::mprotect(ptr, len, bitflags_bits!(flags))) +} + +pub(crate) unsafe fn munmap(ptr: *mut c::c_void, len: usize) -> io::Result<()> { + ret(c::munmap(ptr, len)) +} + +/// # Safety +/// +/// `mremap` is primarily unsafe due to the `old_address` parameter, as +/// anything working with memory pointed to by raw pointers is unsafe. +#[cfg(any(target_os = "emscripten", target_os = "linux"))] +pub(crate) unsafe fn mremap( + old_address: *mut c::c_void, + old_size: usize, + new_size: usize, + flags: MremapFlags, +) -> io::Result<*mut c::c_void> { + let res = c::mremap(old_address, old_size, new_size, bitflags_bits!(flags)); + if res == c::MAP_FAILED { + Err(io::Errno::last_os_error()) + } else { + Ok(res) + } +} + +/// # Safety +/// +/// `mremap_fixed` is primarily unsafe due to the `old_address` and +/// `new_address` parameters, as anything working with memory pointed to by raw +/// pointers is unsafe. +#[cfg(any(target_os = "emscripten", target_os = "linux"))] +pub(crate) unsafe fn mremap_fixed( + old_address: *mut c::c_void, + old_size: usize, + new_size: usize, + flags: MremapFlags, + new_address: *mut c::c_void, +) -> io::Result<*mut c::c_void> { + let res = c::mremap( + old_address, + old_size, + new_size, + bitflags_bits!(flags | MremapFlags::from_bits_retain(bitcast!(c::MAP_FIXED))), + new_address, + ); + if res == c::MAP_FAILED { + Err(io::Errno::last_os_error()) + } else { + Ok(res) + } +} + +/// # Safety +/// +/// `mlock` operates on raw pointers and may round out to the nearest page +/// boundaries. +#[inline] +pub(crate) unsafe fn mlock(addr: *mut c::c_void, length: usize) -> io::Result<()> { + ret(c::mlock(addr, length)) +} + +/// # Safety +/// +/// `mlock_with` operates on raw pointers and may round out to the nearest page +/// boundaries. +#[cfg(linux_kernel)] +#[inline] +pub(crate) unsafe fn mlock_with( + addr: *mut c::c_void, + length: usize, + flags: MlockFlags, +) -> io::Result<()> { + weak_or_syscall! { + fn mlock2( + addr: *const c::c_void, + len: c::size_t, + flags: c::c_int + ) via SYS_mlock2 -> c::c_int + } + + ret(mlock2(addr, length, bitflags_bits!(flags))) +} + +/// # Safety +/// +/// `munlock` operates on raw pointers and may round out to the nearest page +/// boundaries. +#[inline] +pub(crate) unsafe fn munlock(addr: *mut c::c_void, length: usize) -> io::Result<()> { + ret(c::munlock(addr, length)) +} + +#[cfg(linux_kernel)] +pub(crate) unsafe fn userfaultfd(flags: UserfaultfdFlags) -> io::Result<OwnedFd> { + syscall! { + fn userfaultfd( + flags: c::c_int + ) via SYS_userfaultfd -> c::c_int + } + ret_owned_fd(userfaultfd(bitflags_bits!(flags))) +} + +/// Locks all pages mapped into the address space of the calling process. +/// +/// This includes the pages of the code, data, and stack segment, as well as +/// shared libraries, user space kernel data, shared memory, and memory-mapped +/// files. All mapped pages are guaranteed to be resident in RAM when the call +/// returns successfully; the pages are guaranteed to stay in RAM until later +/// unlocked. +#[inline] +#[cfg(any(linux_kernel, freebsdlike, netbsdlike))] +pub(crate) fn mlockall(flags: MlockAllFlags) -> io::Result<()> { + unsafe { ret(c::mlockall(bitflags_bits!(flags))) } +} + +/// Unlocks all pages mapped into the address space of the calling process. +#[inline] +#[cfg(any(linux_kernel, freebsdlike, netbsdlike))] +pub(crate) fn munlockall() -> io::Result<()> { + unsafe { ret(c::munlockall()) } +} diff --git a/vendor/rustix/src/backend/libc/mm/types.rs b/vendor/rustix/src/backend/libc/mm/types.rs new file mode 100644 index 0000000..a4aa3e2 --- /dev/null +++ b/vendor/rustix/src/backend/libc/mm/types.rs @@ -0,0 +1,477 @@ +use crate::backend::c; +use bitflags::bitflags; + +bitflags! { + /// `PROT_*` flags for use with [`mmap`]. + /// + /// For `PROT_NONE`, use `ProtFlags::empty()`. + /// + /// [`mmap`]: crate::mm::mmap + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct ProtFlags: u32 { + /// `PROT_READ` + const READ = bitcast!(c::PROT_READ); + /// `PROT_WRITE` + const WRITE = bitcast!(c::PROT_WRITE); + /// `PROT_EXEC` + const EXEC = bitcast!(c::PROT_EXEC); + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} + +bitflags! { + /// `PROT_*` flags for use with [`mprotect`]. + /// + /// For `PROT_NONE`, use `MprotectFlags::empty()`. + /// + /// [`mprotect`]: crate::mm::mprotect + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct MprotectFlags: u32 { + /// `PROT_READ` + const READ = bitcast!(c::PROT_READ); + /// `PROT_WRITE` + const WRITE = bitcast!(c::PROT_WRITE); + /// `PROT_EXEC` + const EXEC = bitcast!(c::PROT_EXEC); + /// `PROT_GROWSUP` + #[cfg(linux_kernel)] + const GROWSUP = bitcast!(c::PROT_GROWSUP); + /// `PROT_GROWSDOWN` + #[cfg(linux_kernel)] + const GROWSDOWN = bitcast!(c::PROT_GROWSDOWN); + /// `PROT_SEM` + #[cfg(linux_kernel)] + const SEM = linux_raw_sys::general::PROT_SEM; + /// `PROT_BTI` + #[cfg(all(linux_kernel, target_arch = "aarch64"))] + const BTI = linux_raw_sys::general::PROT_BTI; + /// `PROT_MTE` + #[cfg(all(linux_kernel, target_arch = "aarch64"))] + const MTE = linux_raw_sys::general::PROT_MTE; + /// `PROT_SAO` + #[cfg(all(linux_kernel, any(target_arch = "powerpc", target_arch = "powerpc64")))] + const SAO = linux_raw_sys::general::PROT_SAO; + /// `PROT_ADI` + #[cfg(all(linux_kernel, any(target_arch = "sparc", target_arch = "sparc64")))] + const ADI = linux_raw_sys::general::PROT_ADI; + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} + +bitflags! { + /// `MAP_*` flags for use with [`mmap`]. + /// + /// For `MAP_ANONYMOUS` (aka `MAP_ANON`), see [`mmap_anonymous`]. + /// + /// [`mmap`]: crate::mm::mmap + /// [`mmap_anonymous`]: crates::mm::mmap_anonymous + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct MapFlags: u32 { + /// `MAP_SHARED` + const SHARED = bitcast!(c::MAP_SHARED); + /// `MAP_SHARED_VALIDATE` + #[cfg(not(any( + bsd, + solarish, + target_os = "aix", + target_os = "android", + target_os = "emscripten", + target_os = "fuchsia", + target_os = "haiku", + target_os = "nto", + target_os = "redox", + )))] + const SHARED_VALIDATE = bitcast!(c::MAP_SHARED_VALIDATE); + /// `MAP_PRIVATE` + const PRIVATE = bitcast!(c::MAP_PRIVATE); + /// `MAP_DENYWRITE` + #[cfg(not(any( + bsd, + solarish, + target_os = "aix", + target_os = "haiku", + target_os = "nto", + target_os = "redox", + )))] + const DENYWRITE = bitcast!(c::MAP_DENYWRITE); + /// `MAP_FIXED` + const FIXED = bitcast!(c::MAP_FIXED); + /// `MAP_FIXED_NOREPLACE` + #[cfg(not(any( + bsd, + solarish, + target_os = "aix", + target_os = "android", + target_os = "emscripten", + target_os = "fuchsia", + target_os = "haiku", + target_os = "nto", + target_os = "redox", + )))] + const FIXED_NOREPLACE = bitcast!(c::MAP_FIXED_NOREPLACE); + /// `MAP_GROWSDOWN` + #[cfg(not(any( + bsd, + solarish, + target_os = "aix", + target_os = "haiku", + target_os = "nto", + target_os = "redox", + )))] + const GROWSDOWN = bitcast!(c::MAP_GROWSDOWN); + /// `MAP_HUGETLB` + #[cfg(not(any( + bsd, + solarish, + target_os = "aix", + target_os = "haiku", + target_os = "nto", + target_os = "redox", + )))] + const HUGETLB = bitcast!(c::MAP_HUGETLB); + /// `MAP_HUGE_2MB` + #[cfg(not(any( + bsd, + solarish, + target_os = "aix", + target_os = "android", + target_os = "emscripten", + target_os = "fuchsia", + target_os = "haiku", + target_os = "nto", + target_os = "redox", + )))] + const HUGE_2MB = bitcast!(c::MAP_HUGE_2MB); + /// `MAP_HUGE_1GB` + #[cfg(not(any( + bsd, + solarish, + target_os = "aix", + target_os = "android", + target_os = "emscripten", + target_os = "fuchsia", + target_os = "haiku", + target_os = "nto", + target_os = "redox", + )))] + const HUGE_1GB = bitcast!(c::MAP_HUGE_1GB); + /// `MAP_LOCKED` + #[cfg(not(any( + bsd, + solarish, + target_os = "aix", + target_os = "haiku", + target_os = "nto", + target_os = "redox", + )))] + const LOCKED = bitcast!(c::MAP_LOCKED); + /// `MAP_NOCORE` + #[cfg(freebsdlike)] + const NOCORE = bitcast!(c::MAP_NOCORE); + /// `MAP_NORESERVE` + #[cfg(not(any( + freebsdlike, + target_os = "aix", + target_os = "nto", + target_os = "redox", + )))] + const NORESERVE = bitcast!(c::MAP_NORESERVE); + /// `MAP_NOSYNC` + #[cfg(freebsdlike)] + const NOSYNC = bitcast!(c::MAP_NOSYNC); + /// `MAP_POPULATE` + #[cfg(not(any( + bsd, + solarish, + target_os = "aix", + target_os = "haiku", + target_os = "nto", + target_os = "redox", + )))] + const POPULATE = bitcast!(c::MAP_POPULATE); + /// `MAP_STACK` + #[cfg(not(any( + apple, + solarish, + target_os = "aix", + target_os = "dragonfly", + target_os = "haiku", + target_os = "netbsd", + target_os = "redox", + )))] + const STACK = bitcast!(c::MAP_STACK); + /// `MAP_PREFAULT_READ` + #[cfg(target_os = "freebsd")] + const PREFAULT_READ = bitcast!(c::MAP_PREFAULT_READ); + /// `MAP_SYNC` + #[cfg(not(any( + bsd, + solarish, + target_os = "aix", + target_os = "android", + target_os = "emscripten", + target_os = "fuchsia", + target_os = "haiku", + target_os = "nto", + target_os = "redox", + all( + linux_kernel, + any(target_arch = "mips", target_arch = "mips32r6", target_arch = "mips64", target_arch = "mips64r6"), + ) + )))] + const SYNC = bitcast!(c::MAP_SYNC); + /// `MAP_UNINITIALIZED` + #[cfg(any())] + const UNINITIALIZED = bitcast!(c::MAP_UNINITIALIZED); + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} + +#[cfg(any(target_os = "emscripten", target_os = "linux"))] +bitflags! { + /// `MREMAP_*` flags for use with [`mremap`]. + /// + /// For `MREMAP_FIXED`, see [`mremap_fixed`]. + /// + /// [`mremap`]: crate::mm::mremap + /// [`mremap_fixed`]: crate::mm::mremap_fixed + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct MremapFlags: u32 { + /// `MREMAP_MAYMOVE` + const MAYMOVE = bitcast!(c::MREMAP_MAYMOVE); + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} + +bitflags! { + /// `MS_*` flags for use with [`msync`]. + /// + /// [`msync`]: crate::mm::msync + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct MsyncFlags: u32 { + /// `MS_SYNC`—Requests an update and waits for it to complete. + const SYNC = bitcast!(c::MS_SYNC); + /// `MS_ASYNC`—Specifies that an update be scheduled, but the call + /// returns immediately. + const ASYNC = bitcast!(c::MS_ASYNC); + /// `MS_INVALIDATE`—Asks to invalidate other mappings of the same + /// file (so that they can be updated with the fresh values just + /// written). + const INVALIDATE = bitcast!(c::MS_INVALIDATE); + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} + +#[cfg(linux_kernel)] +bitflags! { + /// `MLOCK_*` flags for use with [`mlock_with`]. + /// + /// [`mlock_with`]: crate::mm::mlock_with + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct MlockFlags: u32 { + /// `MLOCK_ONFAULT` + const ONFAULT = bitcast!(c::MLOCK_ONFAULT); + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} + +/// `POSIX_MADV_*` constants for use with [`madvise`]. +/// +/// [`madvise`]: crate::mm::madvise +#[cfg(not(target_os = "redox"))] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[repr(u32)] +#[non_exhaustive] +pub enum Advice { + /// `POSIX_MADV_NORMAL` + #[cfg(not(any(target_os = "android", target_os = "haiku")))] + Normal = bitcast!(c::POSIX_MADV_NORMAL), + + /// `POSIX_MADV_NORMAL` + #[cfg(any(target_os = "android", target_os = "haiku"))] + Normal = bitcast!(c::MADV_NORMAL), + + /// `POSIX_MADV_SEQUENTIAL` + #[cfg(not(any(target_os = "android", target_os = "haiku")))] + Sequential = bitcast!(c::POSIX_MADV_SEQUENTIAL), + + /// `POSIX_MADV_SEQUENTIAL` + #[cfg(any(target_os = "android", target_os = "haiku"))] + Sequential = bitcast!(c::MADV_SEQUENTIAL), + + /// `POSIX_MADV_RANDOM` + #[cfg(not(any(target_os = "android", target_os = "haiku")))] + Random = bitcast!(c::POSIX_MADV_RANDOM), + + /// `POSIX_MADV_RANDOM` + #[cfg(any(target_os = "android", target_os = "haiku"))] + Random = bitcast!(c::MADV_RANDOM), + + /// `POSIX_MADV_WILLNEED` + #[cfg(not(any(target_os = "android", target_os = "haiku")))] + WillNeed = bitcast!(c::POSIX_MADV_WILLNEED), + + /// `POSIX_MADV_WILLNEED` + #[cfg(any(target_os = "android", target_os = "haiku"))] + WillNeed = bitcast!(c::MADV_WILLNEED), + + /// `POSIX_MADV_DONTNEED` + #[cfg(not(any(target_os = "android", target_os = "emscripten", target_os = "haiku")))] + DontNeed = bitcast!(c::POSIX_MADV_DONTNEED), + + /// `POSIX_MADV_DONTNEED` + #[cfg(any(target_os = "android", target_os = "haiku"))] + DontNeed = bitcast!(i32::MAX - 1), + + /// `MADV_DONTNEED` + // `MADV_DONTNEED` has the same value as `POSIX_MADV_DONTNEED`. We don't + // have a separate `posix_madvise` from `madvise`, so we expose a special + // value which we special-case. + #[cfg(target_os = "linux")] + LinuxDontNeed = bitcast!(i32::MAX), + + /// `MADV_DONTNEED` + #[cfg(target_os = "android")] + LinuxDontNeed = bitcast!(c::MADV_DONTNEED), + /// `MADV_FREE` + #[cfg(linux_kernel)] + LinuxFree = bitcast!(c::MADV_FREE), + /// `MADV_REMOVE` + #[cfg(linux_kernel)] + LinuxRemove = bitcast!(c::MADV_REMOVE), + /// `MADV_DONTFORK` + #[cfg(linux_kernel)] + LinuxDontFork = bitcast!(c::MADV_DONTFORK), + /// `MADV_DOFORK` + #[cfg(linux_kernel)] + LinuxDoFork = bitcast!(c::MADV_DOFORK), + /// `MADV_HWPOISON` + #[cfg(linux_kernel)] + LinuxHwPoison = bitcast!(c::MADV_HWPOISON), + /// `MADV_SOFT_OFFLINE` + #[cfg(all( + linux_kernel, + not(any( + target_arch = "mips", + target_arch = "mips32r6", + target_arch = "mips64", + target_arch = "mips64r6" + )) + ))] + LinuxSoftOffline = bitcast!(c::MADV_SOFT_OFFLINE), + /// `MADV_MERGEABLE` + #[cfg(linux_kernel)] + LinuxMergeable = bitcast!(c::MADV_MERGEABLE), + /// `MADV_UNMERGEABLE` + #[cfg(linux_kernel)] + LinuxUnmergeable = bitcast!(c::MADV_UNMERGEABLE), + /// `MADV_HUGEPAGE` + #[cfg(linux_kernel)] + LinuxHugepage = bitcast!(c::MADV_HUGEPAGE), + /// `MADV_NOHUGEPAGE` + #[cfg(linux_kernel)] + LinuxNoHugepage = bitcast!(c::MADV_NOHUGEPAGE), + /// `MADV_DONTDUMP` (since Linux 3.4) + #[cfg(linux_kernel)] + LinuxDontDump = bitcast!(c::MADV_DONTDUMP), + /// `MADV_DODUMP` (since Linux 3.4) + #[cfg(linux_kernel)] + LinuxDoDump = bitcast!(c::MADV_DODUMP), + /// `MADV_WIPEONFORK` (since Linux 4.14) + #[cfg(linux_kernel)] + LinuxWipeOnFork = bitcast!(c::MADV_WIPEONFORK), + /// `MADV_KEEPONFORK` (since Linux 4.14) + #[cfg(linux_kernel)] + LinuxKeepOnFork = bitcast!(c::MADV_KEEPONFORK), + /// `MADV_COLD` (since Linux 5.4) + #[cfg(linux_kernel)] + LinuxCold = bitcast!(c::MADV_COLD), + /// `MADV_PAGEOUT` (since Linux 5.4) + #[cfg(linux_kernel)] + LinuxPageOut = bitcast!(c::MADV_PAGEOUT), + /// `MADV_POPULATE_READ` (since Linux 5.14) + #[cfg(linux_kernel)] + LinuxPopulateRead = bitcast!(c::MADV_POPULATE_READ), + /// `MADV_POPULATE_WRITE` (since Linux 5.14) + #[cfg(linux_kernel)] + LinuxPopulateWrite = bitcast!(c::MADV_POPULATE_WRITE), + /// `MADV_DONTNEED_LOCKED` (since Linux 5.18) + #[cfg(linux_kernel)] + LinuxDontneedLocked = bitcast!(c::MADV_DONTNEED_LOCKED), +} + +#[cfg(target_os = "emscripten")] +#[allow(non_upper_case_globals)] +impl Advice { + /// `POSIX_MADV_DONTNEED` + pub const DontNeed: Self = Self::Normal; +} + +#[cfg(linux_kernel)] +bitflags! { + /// `O_*` flags for use with [`userfaultfd`]. + /// + /// [`userfaultfd`]: crate::mm::userfaultfd + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct UserfaultfdFlags: u32 { + /// `O_CLOEXEC` + const CLOEXEC = bitcast!(c::O_CLOEXEC); + /// `O_NONBLOCK` + const NONBLOCK = bitcast!(c::O_NONBLOCK); + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} + +#[cfg(any(linux_kernel, freebsdlike, netbsdlike))] +bitflags! { + /// `MCL_*` flags for use with [`mlockall`]. + /// + /// [`mlockall`]: crate::mm::mlockall + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct MlockAllFlags: u32 { + /// Used together with `MCL_CURRENT`, `MCL_FUTURE`, or both. Mark all + /// current (with `MCL_CURRENT`) or future (with `MCL_FUTURE`) mappings + /// to lock pages when they are faulted in. When used with + /// `MCL_CURRENT`, all present pages are locked, but `mlockall` will + /// not fault in non-present pages. When used with `MCL_FUTURE`, all + /// future mappings will be marked to lock pages when they are faulted + /// in, but they will not be populated by the lock when the mapping is + /// created. `MCL_ONFAULT` must be used with either `MCL_CURRENT` or + /// `MCL_FUTURE` or both. + #[cfg(linux_kernel)] + const ONFAULT = bitcast!(libc::MCL_ONFAULT); + /// Lock all pages which will become mapped into the address space of + /// the process in the future. These could be, for instance, new pages + /// required by a growing heap and stack as well as new memory-mapped + /// files or shared memory regions. + const FUTURE = bitcast!(libc::MCL_FUTURE); + /// Lock all pages which are currently mapped into the address space of + /// the process. + const CURRENT = bitcast!(libc::MCL_CURRENT); + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} diff --git a/vendor/rustix/src/backend/libc/mod.rs b/vendor/rustix/src/backend/libc/mod.rs new file mode 100644 index 0000000..9ecb09f --- /dev/null +++ b/vendor/rustix/src/backend/libc/mod.rs @@ -0,0 +1,215 @@ +//! The libc backend. +//! +//! On most platforms, this uses the `libc` crate to make system calls. On +//! Windows, this uses the Winsock API in `windows-sys`, which can be adapted +//! to have a very `libc`-like interface. + +// Every FFI call requires an unsafe block, and there are a lot of FFI +// calls. For now, set this to allow for the libc backend. +#![allow(clippy::undocumented_unsafe_blocks)] +// Lots of libc types vary between platforms, so we often need a `.into()` on +// one platform where it's redundant on another. +#![allow(clippy::useless_conversion)] + +mod conv; + +#[cfg(windows)] +pub(crate) mod fd { + pub use crate::maybe_polyfill::os::windows::io::{ + AsRawSocket, AsSocket, BorrowedSocket as BorrowedFd, FromRawSocket, IntoRawSocket, + OwnedSocket as OwnedFd, RawSocket as RawFd, + }; + pub(crate) use windows_sys::Win32::Networking::WinSock::SOCKET as LibcFd; + + /// A version of [`AsRawFd`] for use with Winsock API. + /// + /// [`AsRawFd`]: https://doc.rust-lang.org/stable/std/os/fd/trait.AsRawFd.html + pub trait AsRawFd { + /// A version of [`as_raw_fd`] for use with Winsock API. + /// + /// [`as_raw_fd`]: https://doc.rust-lang.org/stable/std/os/fd/trait.FromRawFd.html#tymethod.as_raw_fd + fn as_raw_fd(&self) -> RawFd; + } + impl<T: AsRawSocket> AsRawFd for T { + #[inline] + fn as_raw_fd(&self) -> RawFd { + self.as_raw_socket() + } + } + + /// A version of [`IntoRawFd`] for use with Winsock API. + /// + /// [`IntoRawFd`]: https://doc.rust-lang.org/stable/std/os/fd/trait.IntoRawFd.html + pub trait IntoRawFd { + /// A version of [`into_raw_fd`] for use with Winsock API. + /// + /// [`into_raw_fd`]: https://doc.rust-lang.org/stable/std/os/fd/trait.FromRawFd.html#tymethod.into_raw_fd + fn into_raw_fd(self) -> RawFd; + } + impl<T: IntoRawSocket> IntoRawFd for T { + #[inline] + fn into_raw_fd(self) -> RawFd { + self.into_raw_socket() + } + } + + /// A version of [`FromRawFd`] for use with Winsock API. + /// + /// [`FromRawFd`]: https://doc.rust-lang.org/stable/std/os/fd/trait.FromRawFd.html + pub trait FromRawFd { + /// A version of [`from_raw_fd`] for use with Winsock API. + /// + /// # Safety + /// + /// See the [safety requirements] for [`from_raw_fd`]. + /// + /// [`from_raw_fd`]: https://doc.rust-lang.org/stable/std/os/fd/trait.FromRawFd.html#tymethod.from_raw_fd + /// [safety requirements]: https://doc.rust-lang.org/stable/std/os/fd/trait.FromRawFd.html#safety + unsafe fn from_raw_fd(raw_fd: RawFd) -> Self; + } + impl<T: FromRawSocket> FromRawFd for T { + #[inline] + unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { + Self::from_raw_socket(raw_fd) + } + } + + /// A version of [`AsFd`] for use with Winsock API. + /// + /// [`AsFd`]: https://doc.rust-lang.org/stable/std/os/fd/trait.AsFd.html + pub trait AsFd { + /// An `as_fd` function for Winsock, where a `Fd` is a `Socket`. + fn as_fd(&self) -> BorrowedFd; + } + impl<T: AsSocket> AsFd for T { + #[inline] + fn as_fd(&self) -> BorrowedFd { + self.as_socket() + } + } +} +#[cfg(not(windows))] +pub(crate) mod fd { + pub use crate::maybe_polyfill::os::fd::{ + AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd, + }; + #[allow(unused_imports)] + pub(crate) use RawFd as LibcFd; +} + +// On Windows we emulate selected libc-compatible interfaces. On non-Windows, +// we just use libc here, since this is the libc backend. +#[cfg_attr(windows, path = "winsock_c.rs")] +pub(crate) mod c; + +#[cfg(feature = "event")] +pub(crate) mod event; +#[cfg(not(windows))] +#[cfg(feature = "fs")] +pub(crate) mod fs; +pub(crate) mod io; +#[cfg(linux_kernel)] +#[cfg(feature = "io_uring")] +pub(crate) mod io_uring; +#[cfg(not(any(windows, target_os = "espidf", target_os = "vita", target_os = "wasi")))] +#[cfg(feature = "mm")] +pub(crate) mod mm; +#[cfg(linux_kernel)] +#[cfg(feature = "mount")] +pub(crate) mod mount; +#[cfg(linux_kernel)] +#[cfg(all(feature = "fs", not(feature = "mount")))] +pub(crate) mod mount; // for deprecated mount functions in "fs" +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] +#[cfg(feature = "net")] +pub(crate) mod net; +#[cfg(not(any(windows, target_os = "espidf")))] +#[cfg(any( + feature = "param", + feature = "runtime", + feature = "time", + target_arch = "x86", +))] +pub(crate) mod param; +#[cfg(not(windows))] +#[cfg(feature = "pipe")] +pub(crate) mod pipe; +#[cfg(not(windows))] +#[cfg(feature = "process")] +pub(crate) mod process; +#[cfg(not(windows))] +#[cfg(not(target_os = "wasi"))] +#[cfg(feature = "pty")] +pub(crate) mod pty; +#[cfg(not(windows))] +#[cfg(feature = "rand")] +pub(crate) mod rand; +#[cfg(not(windows))] +#[cfg(not(target_os = "wasi"))] +#[cfg(feature = "system")] +pub(crate) mod system; +#[cfg(not(any(windows, target_os = "vita")))] +#[cfg(feature = "termios")] +pub(crate) mod termios; +#[cfg(not(windows))] +#[cfg(feature = "thread")] +pub(crate) mod thread; +#[cfg(not(any(windows, target_os = "espidf")))] +#[cfg(feature = "time")] +pub(crate) mod time; + +/// If the host libc is glibc, return `true` if it is less than version 2.25. +/// +/// To restate and clarify, this function returning true does not mean the libc +/// is glibc just that if it is glibc, it is less than version 2.25. +/// +/// For now, this function is only available on Linux, but if it ends up being +/// used beyond that, this could be changed to e.g. `#[cfg(unix)]`. +#[cfg(all(unix, target_env = "gnu"))] +pub(crate) fn if_glibc_is_less_than_2_25() -> bool { + // This is also defined inside `weak_or_syscall!` in + // backend/libc/rand/syscalls.rs, but it's not convenient to re-export the + // weak symbol from that macro, so we duplicate it at a small cost here. + weak! { fn getrandom(*mut c::c_void, c::size_t, c::c_uint) -> c::ssize_t } + + // glibc 2.25 has `getrandom`, which is how we satisfy the API contract of + // this function. But, there are likely other libc versions which have it. + getrandom.get().is_none() +} + +// Private modules used by multiple public modules. +#[cfg(any(feature = "procfs", feature = "process", feature = "runtime"))] +#[cfg(not(any(windows, target_os = "wasi")))] +pub(crate) mod pid; +#[cfg(any(feature = "process", feature = "thread"))] +#[cfg(linux_kernel)] +pub(crate) mod prctl; +#[cfg(not(any( + windows, + target_os = "android", + target_os = "espidf", + target_os = "vita", + target_os = "wasi" +)))] +#[cfg(feature = "shm")] +pub(crate) mod shm; +#[cfg(any(feature = "fs", feature = "thread", feature = "process"))] +#[cfg(not(any(windows, target_os = "wasi")))] +pub(crate) mod ugid; + +#[cfg(bsd)] +const MAX_IOV: usize = c::IOV_MAX as usize; + +#[cfg(any(linux_kernel, target_os = "emscripten", target_os = "nto"))] +const MAX_IOV: usize = c::UIO_MAXIOV as usize; + +#[cfg(not(any( + bsd, + linux_kernel, + windows, + target_os = "emscripten", + target_os = "espidf", + target_os = "nto", + target_os = "horizon", +)))] +const MAX_IOV: usize = 16; // The minimum value required by POSIX. diff --git a/vendor/rustix/src/backend/libc/mount/mod.rs b/vendor/rustix/src/backend/libc/mount/mod.rs new file mode 100644 index 0000000..1e0181a --- /dev/null +++ b/vendor/rustix/src/backend/libc/mount/mod.rs @@ -0,0 +1,2 @@ +pub(crate) mod syscalls; +pub(crate) mod types; diff --git a/vendor/rustix/src/backend/libc/mount/syscalls.rs b/vendor/rustix/src/backend/libc/mount/syscalls.rs new file mode 100644 index 0000000..7245114 --- /dev/null +++ b/vendor/rustix/src/backend/libc/mount/syscalls.rs @@ -0,0 +1,272 @@ +use crate::backend::c; +use crate::backend::conv::ret; +#[cfg(feature = "mount")] +use crate::backend::conv::{borrowed_fd, c_str, ret_owned_fd}; +#[cfg(feature = "mount")] +use crate::fd::{BorrowedFd, OwnedFd}; +use crate::ffi::CStr; +use crate::io; +use core::ptr::null; + +#[cfg(linux_kernel)] +pub(crate) fn mount( + source: Option<&CStr>, + target: &CStr, + file_system_type: Option<&CStr>, + flags: super::types::MountFlagsArg, + data: Option<&CStr>, +) -> io::Result<()> { + unsafe { + ret(c::mount( + source.map_or_else(null, CStr::as_ptr), + target.as_ptr(), + file_system_type.map_or_else(null, CStr::as_ptr), + flags.0, + data.map_or_else(null, CStr::as_ptr).cast(), + )) + } +} + +#[cfg(linux_kernel)] +pub(crate) fn unmount(target: &CStr, flags: super::types::UnmountFlags) -> io::Result<()> { + unsafe { ret(c::umount2(target.as_ptr(), bitflags_bits!(flags))) } +} + +#[cfg(linux_kernel)] +#[cfg(feature = "mount")] +pub(crate) fn fsopen(fs_name: &CStr, flags: super::types::FsOpenFlags) -> io::Result<OwnedFd> { + syscall! { + fn fsopen( + fs_name: *const c::c_char, + flags: c::c_uint + ) via SYS_fsopen -> c::c_int + } + unsafe { ret_owned_fd(fsopen(c_str(fs_name), flags.bits())) } +} + +#[cfg(linux_kernel)] +#[cfg(feature = "mount")] +pub(crate) fn fsmount( + fs_fd: BorrowedFd<'_>, + flags: super::types::FsMountFlags, + attr_flags: super::types::MountAttrFlags, +) -> io::Result<OwnedFd> { + syscall! { + fn fsmount( + fs_fd: c::c_int, + flags: c::c_uint, + attr_flags: c::c_uint + ) via SYS_fsmount -> c::c_int + } + unsafe { ret_owned_fd(fsmount(borrowed_fd(fs_fd), flags.bits(), attr_flags.bits())) } +} + +#[cfg(linux_kernel)] +#[cfg(feature = "mount")] +pub(crate) fn move_mount( + from_dfd: BorrowedFd<'_>, + from_pathname: &CStr, + to_dfd: BorrowedFd<'_>, + to_pathname: &CStr, + flags: super::types::MoveMountFlags, +) -> io::Result<()> { + syscall! { + fn move_mount( + from_dfd: c::c_int, + from_pathname: *const c::c_char, + to_dfd: c::c_int, + to_pathname: *const c::c_char, + flags: c::c_uint + ) via SYS_move_mount -> c::c_int + } + unsafe { + ret(move_mount( + borrowed_fd(from_dfd), + c_str(from_pathname), + borrowed_fd(to_dfd), + c_str(to_pathname), + flags.bits(), + )) + } +} + +#[cfg(linux_kernel)] +#[cfg(feature = "mount")] +pub(crate) fn open_tree( + dfd: BorrowedFd<'_>, + filename: &CStr, + flags: super::types::OpenTreeFlags, +) -> io::Result<OwnedFd> { + syscall! { + fn open_tree( + dfd: c::c_int, + filename: *const c::c_char, + flags: c::c_uint + ) via SYS_open_tree -> c::c_int + } + + unsafe { ret_owned_fd(open_tree(borrowed_fd(dfd), c_str(filename), flags.bits())) } +} + +#[cfg(linux_kernel)] +#[cfg(feature = "mount")] +pub(crate) fn fspick( + dfd: BorrowedFd<'_>, + path: &CStr, + flags: super::types::FsPickFlags, +) -> io::Result<OwnedFd> { + syscall! { + fn fspick( + dfd: c::c_int, + path: *const c::c_char, + flags: c::c_uint + ) via SYS_fspick -> c::c_int + } + + unsafe { ret_owned_fd(fspick(borrowed_fd(dfd), c_str(path), flags.bits())) } +} + +#[cfg(feature = "mount")] +#[cfg(linux_kernel)] +syscall! { + fn fsconfig( + fs_fd: c::c_int, + cmd: c::c_uint, + key: *const c::c_char, + val: *const c::c_char, + aux: c::c_int + ) via SYS_fsconfig -> c::c_int +} + +#[cfg(linux_kernel)] +#[cfg(feature = "mount")] +pub(crate) fn fsconfig_set_flag(fs_fd: BorrowedFd<'_>, key: &CStr) -> io::Result<()> { + unsafe { + ret(fsconfig( + borrowed_fd(fs_fd), + super::types::FsConfigCmd::SetFlag as _, + c_str(key), + null(), + 0, + )) + } +} + +#[cfg(linux_kernel)] +#[cfg(feature = "mount")] +pub(crate) fn fsconfig_set_string( + fs_fd: BorrowedFd<'_>, + key: &CStr, + value: &CStr, +) -> io::Result<()> { + unsafe { + ret(fsconfig( + borrowed_fd(fs_fd), + super::types::FsConfigCmd::SetString as _, + c_str(key), + c_str(value), + 0, + )) + } +} + +#[cfg(linux_kernel)] +#[cfg(feature = "mount")] +pub(crate) fn fsconfig_set_binary( + fs_fd: BorrowedFd<'_>, + key: &CStr, + value: &[u8], +) -> io::Result<()> { + unsafe { + ret(fsconfig( + borrowed_fd(fs_fd), + super::types::FsConfigCmd::SetBinary as _, + c_str(key), + value.as_ptr().cast(), + value.len().try_into().map_err(|_| io::Errno::OVERFLOW)?, + )) + } +} + +#[cfg(linux_kernel)] +#[cfg(feature = "mount")] +pub(crate) fn fsconfig_set_fd( + fs_fd: BorrowedFd<'_>, + key: &CStr, + fd: BorrowedFd<'_>, +) -> io::Result<()> { + unsafe { + ret(fsconfig( + borrowed_fd(fs_fd), + super::types::FsConfigCmd::SetFd as _, + c_str(key), + null(), + borrowed_fd(fd), + )) + } +} + +#[cfg(linux_kernel)] +#[cfg(feature = "mount")] +pub(crate) fn fsconfig_set_path( + fs_fd: BorrowedFd<'_>, + key: &CStr, + path: &CStr, + fd: BorrowedFd<'_>, +) -> io::Result<()> { + unsafe { + ret(fsconfig( + borrowed_fd(fs_fd), + super::types::FsConfigCmd::SetPath as _, + c_str(key), + c_str(path), + borrowed_fd(fd), + )) + } +} + +#[cfg(linux_kernel)] +#[cfg(feature = "mount")] +pub(crate) fn fsconfig_set_path_empty( + fs_fd: BorrowedFd<'_>, + key: &CStr, + fd: BorrowedFd<'_>, +) -> io::Result<()> { + unsafe { + ret(fsconfig( + borrowed_fd(fs_fd), + super::types::FsConfigCmd::SetPathEmpty as _, + c_str(key), + c_str(cstr!("")), + borrowed_fd(fd), + )) + } +} + +#[cfg(linux_kernel)] +#[cfg(feature = "mount")] +pub(crate) fn fsconfig_create(fs_fd: BorrowedFd<'_>) -> io::Result<()> { + unsafe { + ret(fsconfig( + borrowed_fd(fs_fd), + super::types::FsConfigCmd::Create as _, + null(), + null(), + 0, + )) + } +} + +#[cfg(linux_kernel)] +#[cfg(feature = "mount")] +pub(crate) fn fsconfig_reconfigure(fs_fd: BorrowedFd<'_>) -> io::Result<()> { + unsafe { + ret(fsconfig( + borrowed_fd(fs_fd), + super::types::FsConfigCmd::Reconfigure as _, + null(), + null(), + 0, + )) + } +} diff --git a/vendor/rustix/src/backend/libc/mount/types.rs b/vendor/rustix/src/backend/libc/mount/types.rs new file mode 100644 index 0000000..1023686 --- /dev/null +++ b/vendor/rustix/src/backend/libc/mount/types.rs @@ -0,0 +1,340 @@ +use crate::backend::c; +use bitflags::bitflags; + +#[cfg(linux_kernel)] +bitflags! { + /// `MS_*` constants for use with [`mount`]. + /// + /// [`mount`]: crate::mount::mount + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct MountFlags: c::c_ulong { + /// `MS_BIND` + const BIND = c::MS_BIND; + + /// `MS_DIRSYNC` + const DIRSYNC = c::MS_DIRSYNC; + + /// `MS_LAZYTIME` + const LAZYTIME = c::MS_LAZYTIME; + + /// `MS_MANDLOCK` + #[doc(alias = "MANDLOCK")] + const PERMIT_MANDATORY_FILE_LOCKING = c::MS_MANDLOCK; + + /// `MS_NOATIME` + const NOATIME = c::MS_NOATIME; + + /// `MS_NODEV` + const NODEV = c::MS_NODEV; + + /// `MS_NODIRATIME` + const NODIRATIME = c::MS_NODIRATIME; + + /// `MS_NOEXEC` + const NOEXEC = c::MS_NOEXEC; + + /// `MS_NOSUID` + const NOSUID = c::MS_NOSUID; + + /// `MS_RDONLY` + const RDONLY = c::MS_RDONLY; + + /// `MS_REC` + const REC = c::MS_REC; + + /// `MS_RELATIME` + const RELATIME = c::MS_RELATIME; + + /// `MS_SILENT` + const SILENT = c::MS_SILENT; + + /// `MS_STRICTATIME` + const STRICTATIME = c::MS_STRICTATIME; + + /// `MS_SYNCHRONOUS` + const SYNCHRONOUS = c::MS_SYNCHRONOUS; + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} + +#[cfg(linux_kernel)] +bitflags! { + /// `MNT_*` constants for use with [`unmount`]. + /// + /// [`unmount`]: crate::mount::unmount + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct UnmountFlags: u32 { + /// `MNT_FORCE` + const FORCE = bitcast!(c::MNT_FORCE); + /// `MNT_DETACH` + const DETACH = bitcast!(c::MNT_DETACH); + /// `MNT_EXPIRE` + const EXPIRE = bitcast!(c::MNT_EXPIRE); + /// `UMOUNT_NOFOLLOW` + const NOFOLLOW = bitcast!(c::UMOUNT_NOFOLLOW); + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} + +#[cfg(feature = "mount")] +#[cfg(linux_kernel)] +bitflags! { + /// `FSOPEN_*` constants for use with [`fsopen`]. + /// + /// [`fsopen`]: crate::mount::fsopen + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct FsOpenFlags: c::c_uint { + /// `FSOPEN_CLOEXEC` + const FSOPEN_CLOEXEC = 0x0000_0001; + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} + +#[cfg(feature = "mount")] +#[cfg(linux_kernel)] +bitflags! { + /// `FSMOUNT_*` constants for use with [`fsmount`]. + /// + /// [`fsmount`]: crate::mount::fsmount + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct FsMountFlags: c::c_uint { + /// `FSMOUNT_CLOEXEC` + const FSMOUNT_CLOEXEC = 0x0000_0001; + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} + +/// `FSCONFIG_*` constants for use with the `fsconfig` syscall. +#[cfg(feature = "mount")] +#[cfg(linux_kernel)] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[repr(u32)] +pub(crate) enum FsConfigCmd { + /// `FSCONFIG_SET_FLAG` + SetFlag = 0, + + /// `FSCONFIG_SET_STRING` + SetString = 1, + + /// `FSCONFIG_SET_BINARY` + SetBinary = 2, + + /// `FSCONFIG_SET_PATH` + SetPath = 3, + + /// `FSCONFIG_SET_PATH_EMPTY` + SetPathEmpty = 4, + + /// `FSCONFIG_SET_FD` + SetFd = 5, + + /// `FSCONFIG_CMD_CREATE` + Create = 6, + + /// `FSCONFIG_CMD_RECONFIGURE` + Reconfigure = 7, +} + +#[cfg(feature = "mount")] +#[cfg(linux_kernel)] +bitflags! { + /// `MOUNT_ATTR_*` constants for use with [`fsmount`]. + /// + /// [`fsmount`]: crate::mount::fsmount + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct MountAttrFlags: c::c_uint { + /// `MOUNT_ATTR_RDONLY` + const MOUNT_ATTR_RDONLY = 0x0000_0001; + + /// `MOUNT_ATTR_NOSUID` + const MOUNT_ATTR_NOSUID = 0x0000_0002; + + /// `MOUNT_ATTR_NODEV` + const MOUNT_ATTR_NODEV = 0x0000_0004; + + /// `MOUNT_ATTR_NOEXEC` + const MOUNT_ATTR_NOEXEC = 0x0000_0008; + + /// `MOUNT_ATTR__ATIME` + const MOUNT_ATTR__ATIME = 0x0000_0070; + + /// `MOUNT_ATTR_RELATIME` + const MOUNT_ATTR_RELATIME = 0x0000_0000; + + /// `MOUNT_ATTR_NOATIME` + const MOUNT_ATTR_NOATIME = 0x0000_0010; + + /// `MOUNT_ATTR_STRICTATIME` + const MOUNT_ATTR_STRICTATIME = 0x0000_0020; + + /// `MOUNT_ATTR_NODIRATIME` + const MOUNT_ATTR_NODIRATIME = 0x0000_0080; + + /// `MOUNT_ATTR_NOUSER` + const MOUNT_ATTR_IDMAP = 0x0010_0000; + + /// `MOUNT_ATTR__ATIME_FLAGS` + const MOUNT_ATTR_NOSYMFOLLOW = 0x0020_0000; + + /// `MOUNT_ATTR__ATIME_FLAGS` + const MOUNT_ATTR_SIZE_VER0 = 32; + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} + +#[cfg(feature = "mount")] +#[cfg(linux_kernel)] +bitflags! { + /// `MOVE_MOUNT_*` constants for use with [`move_mount`]. + /// + /// [`move_mount`]: crate::mount::move_mount + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct MoveMountFlags: c::c_uint { + /// `MOVE_MOUNT_F_EMPTY_PATH` + const MOVE_MOUNT_F_SYMLINKS = 0x0000_0001; + + /// `MOVE_MOUNT_F_AUTOMOUNTS` + const MOVE_MOUNT_F_AUTOMOUNTS = 0x0000_0002; + + /// `MOVE_MOUNT_F_EMPTY_PATH` + const MOVE_MOUNT_F_EMPTY_PATH = 0x0000_0004; + + /// `MOVE_MOUNT_T_SYMLINKS` + const MOVE_MOUNT_T_SYMLINKS = 0x0000_0010; + + /// `MOVE_MOUNT_T_AUTOMOUNTS` + const MOVE_MOUNT_T_AUTOMOUNTS = 0x0000_0020; + + /// `MOVE_MOUNT_T_EMPTY_PATH` + const MOVE_MOUNT_T_EMPTY_PATH = 0x0000_0040; + + /// `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__MASK` + const MOVE_MOUNT__MASK = 0x0000_0377; + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} + +#[cfg(feature = "mount")] +#[cfg(linux_kernel)] +bitflags! { + /// `OPENTREE_*` constants for use with [`open_tree`]. + /// + /// [`open_tree`]: crate::mount::open_tree + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct OpenTreeFlags: c::c_uint { + /// `OPENTREE_CLONE` + const OPEN_TREE_CLONE = 1; + + /// `OPENTREE_CLOEXEC` + const OPEN_TREE_CLOEXEC = c::O_CLOEXEC as c::c_uint; + + /// `AT_EMPTY_PATH` + const AT_EMPTY_PATH = c::AT_EMPTY_PATH as c::c_uint; + + /// `AT_NO_AUTOMOUNT` + const AT_NO_AUTOMOUNT = c::AT_NO_AUTOMOUNT as c::c_uint; + + /// `AT_RECURSIVE` + const AT_RECURSIVE = c::AT_RECURSIVE as c::c_uint; + + /// `AT_SYMLINK_NOFOLLOW` + const AT_SYMLINK_NOFOLLOW = c::AT_SYMLINK_NOFOLLOW as c::c_uint; + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} + +#[cfg(feature = "mount")] +#[cfg(linux_kernel)] +bitflags! { + /// `FSPICK_*` constants for use with [`fspick`]. + /// + /// [`fspick`]: crate::mount::fspick + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct FsPickFlags: c::c_uint { + /// `FSPICK_CLOEXEC` + const FSPICK_CLOEXEC = 0x0000_0001; + + /// `FSPICK_SYMLINK_NOFOLLOW` + const FSPICK_SYMLINK_NOFOLLOW = 0x0000_0002; + + /// `FSPICK_NO_AUTOMOUNT` + const FSPICK_NO_AUTOMOUNT = 0x0000_0004; + + /// `FSPICK_EMPTY_PATH` + const FSPICK_EMPTY_PATH = 0x0000_0008; + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} + +#[cfg(linux_kernel)] +bitflags! { + /// `MS_*` constants for use with [`mount_change`]. + /// + /// [`mount_change`]: crate::mount::mount_change + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct MountPropagationFlags: c::c_ulong { + /// `MS_SILENT` + const SILENT = c::MS_SILENT; + /// `MS_SHARED` + const SHARED = c::MS_SHARED; + /// `MS_PRIVATE` + const PRIVATE = c::MS_PRIVATE; + /// `MS_SLAVE` + const SLAVE = c::MS_SLAVE; + /// `MS_UNBINDABLE` + const UNBINDABLE = c::MS_UNBINDABLE; + /// `MS_REC` + const REC = c::MS_REC; + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} + +#[cfg(linux_kernel)] +bitflags! { + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub(crate) struct InternalMountFlags: c::c_ulong { + const REMOUNT = c::MS_REMOUNT; + const MOVE = c::MS_MOVE; + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} + +#[cfg(linux_kernel)] +pub(crate) struct MountFlagsArg(pub(crate) c::c_ulong); diff --git a/vendor/rustix/src/backend/libc/net/addr.rs b/vendor/rustix/src/backend/libc/net/addr.rs new file mode 100644 index 0000000..719a549 --- /dev/null +++ b/vendor/rustix/src/backend/libc/net/addr.rs @@ -0,0 +1,245 @@ +//! Socket address utilities. + +use crate::backend::c; +#[cfg(unix)] +use { + crate::ffi::CStr, + crate::io, + crate::path, + core::cmp::Ordering, + core::fmt, + core::hash::{Hash, Hasher}, + core::slice, +}; + +/// `struct sockaddr_un` +#[cfg(unix)] +#[derive(Clone)] +#[doc(alias = "sockaddr_un")] +pub struct SocketAddrUnix { + pub(crate) unix: c::sockaddr_un, + #[cfg(not(any(bsd, target_os = "haiku")))] + len: c::socklen_t, +} + +#[cfg(unix)] +impl SocketAddrUnix { + /// Construct a new Unix-domain address from a filesystem path. + #[inline] + pub fn new<P: path::Arg>(path: P) -> io::Result<Self> { + path.into_with_c_str(Self::_new) + } + + #[inline] + fn _new(path: &CStr) -> io::Result<Self> { + let mut unix = Self::init(); + let bytes = path.to_bytes_with_nul(); + if bytes.len() > unix.sun_path.len() { + return Err(io::Errno::NAMETOOLONG); + } + for (i, b) in bytes.iter().enumerate() { + unix.sun_path[i] = *b as c::c_char; + } + + #[cfg(any(bsd, target_os = "haiku"))] + { + unix.sun_len = (offsetof_sun_path() + bytes.len()).try_into().unwrap(); + } + + Ok(Self { + unix, + #[cfg(not(any(bsd, target_os = "haiku")))] + len: (offsetof_sun_path() + bytes.len()).try_into().unwrap(), + }) + } + + /// Construct a new abstract Unix-domain address from a byte slice. + #[cfg(linux_kernel)] + #[inline] + pub fn new_abstract_name(name: &[u8]) -> io::Result<Self> { + let mut unix = Self::init(); + if 1 + name.len() > unix.sun_path.len() { + return Err(io::Errno::NAMETOOLONG); + } + unix.sun_path[0] = 0; + for (i, b) in name.iter().enumerate() { + unix.sun_path[1 + i] = *b as c::c_char; + } + let len = offsetof_sun_path() + 1 + name.len(); + let len = len.try_into().unwrap(); + Ok(Self { + unix, + #[cfg(not(any(bsd, target_os = "haiku")))] + len, + }) + } + + fn init() -> c::sockaddr_un { + c::sockaddr_un { + #[cfg(any(bsd, target_os = "aix", target_os = "haiku", target_os = "nto"))] + sun_len: 0, + #[cfg(target_os = "vita")] + ss_len: 0, + sun_family: c::AF_UNIX as _, + #[cfg(any(bsd, target_os = "nto"))] + sun_path: [0; 104], + #[cfg(not(any(bsd, target_os = "aix", target_os = "haiku", target_os = "nto")))] + sun_path: [0; 108], + #[cfg(target_os = "haiku")] + sun_path: [0; 126], + #[cfg(target_os = "aix")] + sun_path: [0; 1023], + } + } + + /// For a filesystem path address, return the path. + #[inline] + pub fn path(&self) -> Option<&CStr> { + let len = self.len(); + if len != 0 && self.unix.sun_path[0] != 0 { + let end = len as usize - offsetof_sun_path(); + let bytes = &self.unix.sun_path[..end]; + // SAFETY: `from_raw_parts` to convert from `&[c_char]` to `&[u8]`. + // And `from_bytes_with_nul_unchecked` since the string is + // NUL-terminated. + unsafe { + Some(CStr::from_bytes_with_nul_unchecked(slice::from_raw_parts( + bytes.as_ptr().cast(), + bytes.len(), + ))) + } + } else { + None + } + } + + /// For an abstract address, return the identifier. + #[cfg(linux_kernel)] + #[inline] + pub fn abstract_name(&self) -> Option<&[u8]> { + let len = self.len(); + if len != 0 && self.unix.sun_path[0] == 0 { + let end = len as usize - offsetof_sun_path(); + let bytes = &self.unix.sun_path[1..end]; + // SAFETY: `from_raw_parts` to convert from `&[c_char]` to `&[u8]`. + unsafe { Some(slice::from_raw_parts(bytes.as_ptr().cast(), bytes.len())) } + } else { + None + } + } + + #[inline] + pub(crate) fn addr_len(&self) -> c::socklen_t { + #[cfg(not(any(bsd, target_os = "haiku")))] + { + self.len + } + #[cfg(any(bsd, target_os = "haiku"))] + { + c::socklen_t::from(self.unix.sun_len) + } + } + + #[inline] + pub(crate) fn len(&self) -> usize { + self.addr_len() as usize + } +} + +#[cfg(unix)] +impl PartialEq for SocketAddrUnix { + #[inline] + fn eq(&self, other: &Self) -> bool { + let self_len = self.len() - offsetof_sun_path(); + let other_len = other.len() - offsetof_sun_path(); + self.unix.sun_path[..self_len].eq(&other.unix.sun_path[..other_len]) + } +} + +#[cfg(unix)] +impl Eq for SocketAddrUnix {} + +#[cfg(unix)] +impl PartialOrd for SocketAddrUnix { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option<Ordering> { + Some(self.cmp(other)) + } +} + +#[cfg(unix)] +impl Ord for SocketAddrUnix { + #[inline] + fn cmp(&self, other: &Self) -> Ordering { + let self_len = self.len() - offsetof_sun_path(); + let other_len = other.len() - offsetof_sun_path(); + self.unix.sun_path[..self_len].cmp(&other.unix.sun_path[..other_len]) + } +} + +#[cfg(unix)] +impl Hash for SocketAddrUnix { + #[inline] + fn hash<H: Hasher>(&self, state: &mut H) { + let self_len = self.len() - offsetof_sun_path(); + self.unix.sun_path[..self_len].hash(state) + } +} + +#[cfg(unix)] +impl fmt::Debug for SocketAddrUnix { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + if let Some(path) = self.path() { + path.fmt(fmt) + } else { + #[cfg(linux_kernel)] + if let Some(name) = self.abstract_name() { + return name.fmt(fmt); + } + + "(unnamed)".fmt(fmt) + } + } +} + +/// `struct sockaddr_storage` as a raw struct. +pub type SocketAddrStorage = c::sockaddr_storage; + +/// Return the offset of the `sun_path` field of `sockaddr_un`. +#[cfg(not(windows))] +#[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"))] + sun_len: 0_u8, + #[cfg(target_os = "vita")] + ss_len: 0, + #[cfg(any( + bsd, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "nto", + target_os = "vita" + ))] + sun_family: 0_u8, + #[cfg(not(any( + bsd, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "nto", + target_os = "vita" + )))] + sun_family: 0_u16, + #[cfg(any(bsd, target_os = "nto"))] + sun_path: [0; 104], + #[cfg(not(any(bsd, target_os = "aix", target_os = "haiku", target_os = "nto")))] + sun_path: [0; 108], + #[cfg(target_os = "haiku")] + sun_path: [0; 126], + #[cfg(target_os = "aix")] + sun_path: [0; 1023], + }; + (crate::utils::as_ptr(&z.sun_path) as usize) - (crate::utils::as_ptr(&z) as usize) +} diff --git a/vendor/rustix/src/backend/libc/net/ext.rs b/vendor/rustix/src/backend/libc/net/ext.rs new file mode 100644 index 0000000..2e11c05 --- /dev/null +++ b/vendor/rustix/src/backend/libc/net/ext.rs @@ -0,0 +1,135 @@ +use crate::backend::c; + +/// The windows `sockaddr_in6` type is a union with accessor functions which +/// are not `const fn`. Define our own layout-compatible version so that we +/// can transmute in and out of it. +#[cfg(windows)] +#[repr(C)] +struct sockaddr_in6 { + sin6_family: u16, + sin6_port: u16, + sin6_flowinfo: u32, + sin6_addr: c::in6_addr, + sin6_scope_id: u32, +} + +#[cfg(not(windows))] +#[inline] +pub(crate) const fn in_addr_s_addr(addr: c::in_addr) -> u32 { + addr.s_addr +} + +#[cfg(windows)] +#[inline] +pub(crate) const fn in_addr_s_addr(addr: c::in_addr) -> u32 { + // This should be `*addr.S_un.S_addr()`, except that isn't a `const fn`. + unsafe { core::mem::transmute(addr) } +} + +#[cfg(not(windows))] +#[inline] +pub(crate) const fn in_addr_new(s_addr: u32) -> c::in_addr { + c::in_addr { s_addr } +} + +#[cfg(windows)] +#[inline] +pub(crate) const fn in_addr_new(s_addr: u32) -> c::in_addr { + unsafe { core::mem::transmute(s_addr) } +} + +#[cfg(not(windows))] +#[inline] +pub(crate) const fn in6_addr_s6_addr(addr: c::in6_addr) -> [u8; 16] { + addr.s6_addr +} + +#[cfg(windows)] +#[inline] +pub(crate) const fn in6_addr_s6_addr(addr: c::in6_addr) -> [u8; 16] { + unsafe { core::mem::transmute(addr) } +} + +#[cfg(not(windows))] +#[inline] +pub(crate) const fn in6_addr_new(s6_addr: [u8; 16]) -> c::in6_addr { + c::in6_addr { s6_addr } +} + +#[cfg(windows)] +#[inline] +pub(crate) const fn in6_addr_new(s6_addr: [u8; 16]) -> c::in6_addr { + unsafe { core::mem::transmute(s6_addr) } +} + +#[cfg(not(windows))] +#[inline] +pub(crate) const fn sockaddr_in6_sin6_scope_id(addr: &c::sockaddr_in6) -> u32 { + addr.sin6_scope_id +} + +#[cfg(windows)] +#[inline] +pub(crate) const fn sockaddr_in6_sin6_scope_id(addr: &c::sockaddr_in6) -> u32 { + let addr: &sockaddr_in6 = unsafe { core::mem::transmute(addr) }; + addr.sin6_scope_id +} + +#[cfg(not(windows))] +#[inline] +pub(crate) const fn sockaddr_in6_new( + #[cfg(any( + bsd, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "nto", + target_os = "vita" + ))] + sin6_len: u8, + sin6_family: c::sa_family_t, + sin6_port: u16, + sin6_flowinfo: u32, + sin6_addr: c::in6_addr, + sin6_scope_id: u32, +) -> c::sockaddr_in6 { + c::sockaddr_in6 { + #[cfg(any( + bsd, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "nto", + target_os = "vita" + ))] + sin6_len, + sin6_family, + sin6_port, + sin6_flowinfo, + sin6_addr, + sin6_scope_id, + #[cfg(solarish)] + __sin6_src_id: 0, + #[cfg(target_os = "vita")] + sin6_vport: 0, + } +} + +#[cfg(windows)] +#[inline] +pub(crate) const fn sockaddr_in6_new( + sin6_family: u16, + sin6_port: u16, + sin6_flowinfo: u32, + sin6_addr: c::in6_addr, + sin6_scope_id: u32, +) -> c::sockaddr_in6 { + let addr = sockaddr_in6 { + sin6_family, + sin6_port, + sin6_flowinfo, + sin6_addr, + sin6_scope_id, + }; + unsafe { core::mem::transmute(addr) } +} diff --git a/vendor/rustix/src/backend/libc/net/mod.rs b/vendor/rustix/src/backend/libc/net/mod.rs new file mode 100644 index 0000000..d7ab68d --- /dev/null +++ b/vendor/rustix/src/backend/libc/net/mod.rs @@ -0,0 +1,16 @@ +pub(crate) mod addr; +pub(crate) mod ext; +#[cfg(not(any( + windows, + target_os = "espidf", + target_os = "redox", + target_os = "vita", + target_os = "wasi" +)))] +pub(crate) mod msghdr; +pub(crate) mod read_sockaddr; +pub(crate) mod send_recv; +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] +pub(crate) mod sockopt; +pub(crate) mod syscalls; +pub(crate) mod write_sockaddr; diff --git a/vendor/rustix/src/backend/libc/net/msghdr.rs b/vendor/rustix/src/backend/libc/net/msghdr.rs new file mode 100644 index 0000000..dd9b156 --- /dev/null +++ b/vendor/rustix/src/backend/libc/net/msghdr.rs @@ -0,0 +1,134 @@ +//! Utilities for dealing with message headers. +//! +//! These take closures rather than returning a `c::msghdr` directly because +//! the message headers may reference stack-local data. + +use crate::backend::c; +use crate::backend::conv::{msg_control_len, msg_iov_len}; +use crate::backend::net::write_sockaddr::{encode_sockaddr_v4, encode_sockaddr_v6}; + +use crate::io::{self, IoSlice, IoSliceMut}; +use crate::net::{RecvAncillaryBuffer, SendAncillaryBuffer, SocketAddrV4, SocketAddrV6}; +use crate::utils::as_ptr; + +use core::mem::{size_of, zeroed, MaybeUninit}; + +/// Create a message header intended to receive a datagram. +pub(crate) fn with_recv_msghdr<R>( + name: &mut MaybeUninit<c::sockaddr_storage>, + iov: &mut [IoSliceMut<'_>], + control: &mut RecvAncillaryBuffer<'_>, + f: impl FnOnce(&mut c::msghdr) -> io::Result<R>, +) -> io::Result<R> { + control.clear(); + + let namelen = size_of::<c::sockaddr_storage>() as c::socklen_t; + let mut msghdr = { + let mut h = zero_msghdr(); + h.msg_name = name.as_mut_ptr().cast(); + h.msg_namelen = namelen; + h.msg_iov = iov.as_mut_ptr().cast(); + h.msg_iovlen = msg_iov_len(iov.len()); + h.msg_control = control.as_control_ptr().cast(); + h.msg_controllen = msg_control_len(control.control_len()); + h + }; + + let res = f(&mut msghdr); + + // Reset the control length. + if res.is_ok() { + unsafe { + control.set_control_len(msghdr.msg_controllen.try_into().unwrap_or(usize::MAX)); + } + } + + res +} + +/// Create a message header intended to send without an address. +pub(crate) fn with_noaddr_msghdr<R>( + iov: &[IoSlice<'_>], + control: &mut SendAncillaryBuffer<'_, '_, '_>, + f: impl FnOnce(c::msghdr) -> R, +) -> R { + f({ + let mut h = zero_msghdr(); + h.msg_iov = iov.as_ptr() as _; + h.msg_iovlen = msg_iov_len(iov.len()); + h.msg_control = control.as_control_ptr().cast(); + h.msg_controllen = msg_control_len(control.control_len()); + h + }) +} + +/// Create a message header intended to send with an IPv4 address. +pub(crate) fn with_v4_msghdr<R>( + addr: &SocketAddrV4, + iov: &[IoSlice<'_>], + control: &mut SendAncillaryBuffer<'_, '_, '_>, + f: impl FnOnce(c::msghdr) -> R, +) -> R { + let encoded = encode_sockaddr_v4(addr); + + f({ + let mut h = zero_msghdr(); + h.msg_name = as_ptr(&encoded) as _; + h.msg_namelen = size_of::<SocketAddrV4>() as _; + h.msg_iov = iov.as_ptr() as _; + h.msg_iovlen = msg_iov_len(iov.len()); + h.msg_control = control.as_control_ptr().cast(); + h.msg_controllen = msg_control_len(control.control_len()); + h + }) +} + +/// Create a message header intended to send with an IPv6 address. +pub(crate) fn with_v6_msghdr<R>( + addr: &SocketAddrV6, + iov: &[IoSlice<'_>], + control: &mut SendAncillaryBuffer<'_, '_, '_>, + f: impl FnOnce(c::msghdr) -> R, +) -> R { + let encoded = encode_sockaddr_v6(addr); + + f({ + let mut h = zero_msghdr(); + h.msg_name = as_ptr(&encoded) as _; + h.msg_namelen = size_of::<SocketAddrV6>() as _; + h.msg_iov = iov.as_ptr() as _; + h.msg_iovlen = msg_iov_len(iov.len()); + h.msg_control = control.as_control_ptr().cast(); + h.msg_controllen = msg_control_len(control.control_len()); + h + }) +} + +/// Create a message header intended to send with a Unix address. +#[cfg(all(unix, not(target_os = "redox")))] +pub(crate) fn with_unix_msghdr<R>( + addr: &crate::net::SocketAddrUnix, + iov: &[IoSlice<'_>], + control: &mut SendAncillaryBuffer<'_, '_, '_>, + f: impl FnOnce(c::msghdr) -> R, +) -> R { + f({ + let mut h = zero_msghdr(); + h.msg_name = as_ptr(&addr.unix) as _; + h.msg_namelen = addr.addr_len(); + h.msg_iov = iov.as_ptr() as _; + h.msg_iovlen = msg_iov_len(iov.len()); + h.msg_control = control.as_control_ptr().cast(); + h.msg_controllen = msg_control_len(control.control_len()); + h + }) +} + +/// Create a zero-initialized message header struct value. +#[cfg(all(unix, not(target_os = "redox")))] +pub(crate) fn zero_msghdr() -> c::msghdr { + // SAFETY: We can't initialize all the fields by value because on some + // platforms the `msghdr` struct in the libc crate contains private padding + // fields. But it is still a C type that's meant to be zero-initializable. + unsafe { zeroed() } +} diff --git a/vendor/rustix/src/backend/libc/net/read_sockaddr.rs b/vendor/rustix/src/backend/libc/net/read_sockaddr.rs new file mode 100644 index 0000000..6da7a50 --- /dev/null +++ b/vendor/rustix/src/backend/libc/net/read_sockaddr.rs @@ -0,0 +1,306 @@ +//! The BSD sockets API requires us to read the `ss_family` field before we can +//! interpret the rest of a `sockaddr` produced by the kernel. + +#[cfg(unix)] +use super::addr::SocketAddrUnix; +use super::ext::{in6_addr_s6_addr, in_addr_s_addr, sockaddr_in6_sin6_scope_id}; +use crate::backend::c; +#[cfg(not(windows))] +use crate::ffi::CStr; +use crate::io; +use crate::net::{Ipv4Addr, Ipv6Addr, SocketAddrAny, SocketAddrV4, SocketAddrV6}; +use core::mem::size_of; + +// This must match the header of `sockaddr`. +#[repr(C)] +struct sockaddr_header { + #[cfg(any( + bsd, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "nto", + target_os = "vita" + ))] + sa_len: u8, + #[cfg(any( + bsd, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "nto", + target_os = "vita" + ))] + ss_family: u8, + #[cfg(not(any( + bsd, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "nto", + target_os = "vita" + )))] + ss_family: u16, +} + +/// Read the `ss_family` field from a socket address returned from the OS. +/// +/// # Safety +/// +/// `storage` must point to a valid socket address returned from the OS. +#[inline] +unsafe fn read_ss_family(storage: *const c::sockaddr_storage) -> u16 { + // Assert that we know the layout of `sockaddr`. + let _ = c::sockaddr { + #[cfg(any( + bsd, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "nto", + target_os = "vita" + ))] + sa_len: 0_u8, + #[cfg(any( + bsd, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "nto", + target_os = "vita" + ))] + sa_family: 0_u8, + #[cfg(not(any( + bsd, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "nto", + target_os = "vita" + )))] + sa_family: 0_u16, + #[cfg(not(target_os = "haiku"))] + sa_data: [0; 14], + #[cfg(target_os = "haiku")] + sa_data: [0; 30], + }; + + (*storage.cast::<sockaddr_header>()).ss_family.into() +} + +/// Set the `ss_family` field of a socket address to `AF_UNSPEC`, so that we +/// can test for `AF_UNSPEC` to test whether it was stored to. +pub(crate) unsafe fn initialize_family_to_unspec(storage: *mut c::sockaddr_storage) { + (*storage.cast::<sockaddr_header>()).ss_family = c::AF_UNSPEC as _; +} + +/// Read a socket address encoded in a platform-specific format. +/// +/// # Safety +/// +/// `storage` must point to valid socket address storage. +pub(crate) unsafe fn read_sockaddr( + storage: *const c::sockaddr_storage, + len: usize, +) -> io::Result<SocketAddrAny> { + #[cfg(unix)] + let offsetof_sun_path = super::addr::offsetof_sun_path(); + + if len < size_of::<c::sa_family_t>() { + return Err(io::Errno::INVAL); + } + match read_ss_family(storage).into() { + c::AF_INET => { + if len < size_of::<c::sockaddr_in>() { + return Err(io::Errno::INVAL); + } + let decode = &*storage.cast::<c::sockaddr_in>(); + Ok(SocketAddrAny::V4(SocketAddrV4::new( + Ipv4Addr::from(u32::from_be(in_addr_s_addr(decode.sin_addr))), + u16::from_be(decode.sin_port), + ))) + } + c::AF_INET6 => { + if len < size_of::<c::sockaddr_in6>() { + return Err(io::Errno::INVAL); + } + let decode = &*storage.cast::<c::sockaddr_in6>(); + #[cfg(not(windows))] + let s6_addr = decode.sin6_addr.s6_addr; + #[cfg(windows)] + let s6_addr = decode.sin6_addr.u.Byte; + #[cfg(not(windows))] + let sin6_scope_id = decode.sin6_scope_id; + #[cfg(windows)] + let sin6_scope_id = decode.Anonymous.sin6_scope_id; + Ok(SocketAddrAny::V6(SocketAddrV6::new( + Ipv6Addr::from(s6_addr), + u16::from_be(decode.sin6_port), + u32::from_be(decode.sin6_flowinfo), + sin6_scope_id, + ))) + } + #[cfg(unix)] + c::AF_UNIX => { + if len < offsetof_sun_path { + return Err(io::Errno::INVAL); + } + if len == offsetof_sun_path { + SocketAddrUnix::new(&[][..]).map(SocketAddrAny::Unix) + } else { + let decode = &*storage.cast::<c::sockaddr_un>(); + + // On Linux check for Linux's [abstract namespace]. + // + // [abstract namespace]: https://man7.org/linux/man-pages/man7/unix.7.html + #[cfg(linux_kernel)] + if decode.sun_path[0] == 0 { + return SocketAddrUnix::new_abstract_name(core::mem::transmute::< + &[c::c_char], + &[u8], + >( + &decode.sun_path[1..len - offsetof_sun_path], + )) + .map(SocketAddrAny::Unix); + } + + // Otherwise we expect a NUL-terminated filesystem path. + + // Trim off unused bytes from the end of `path_bytes`. + let path_bytes = if cfg!(any(solarish, target_os = "freebsd")) { + // FreeBSD and illumos sometimes set the length to longer + // than the length of the NUL-terminated string. Find the + // NUL and truncate the string accordingly. + &decode.sun_path[..decode + .sun_path + .iter() + .position(|b| *b == 0) + .ok_or(io::Errno::INVAL)?] + } else { + // Otherwise, use the provided length. + let provided_len = len - 1 - offsetof_sun_path; + if decode.sun_path[provided_len] != 0 { + return Err(io::Errno::INVAL); + } + debug_assert_eq!( + CStr::from_ptr(decode.sun_path.as_ptr()).to_bytes().len(), + provided_len + ); + &decode.sun_path[..provided_len] + }; + + SocketAddrUnix::new(core::mem::transmute::<&[c::c_char], &[u8]>(path_bytes)) + .map(SocketAddrAny::Unix) + } + } + _ => Err(io::Errno::INVAL), + } +} + +/// Read an optional socket address returned from the OS. +/// +/// # Safety +/// +/// `storage` must point to a valid socket address returned from the OS. +pub(crate) unsafe fn maybe_read_sockaddr_os( + storage: *const c::sockaddr_storage, + len: usize, +) -> Option<SocketAddrAny> { + if len == 0 { + return None; + } + + assert!(len >= size_of::<c::sa_family_t>()); + let family = read_ss_family(storage).into(); + if family == c::AF_UNSPEC { + None + } else { + Some(inner_read_sockaddr_os(family, storage, len)) + } +} + +/// Read a socket address returned from the OS. +/// +/// # Safety +/// +/// `storage` must point to a valid socket address returned from the OS. +pub(crate) unsafe fn read_sockaddr_os( + storage: *const c::sockaddr_storage, + len: usize, +) -> SocketAddrAny { + assert!(len >= size_of::<c::sa_family_t>()); + let family = read_ss_family(storage).into(); + inner_read_sockaddr_os(family, storage, len) +} + +unsafe fn inner_read_sockaddr_os( + family: c::c_int, + storage: *const c::sockaddr_storage, + len: usize, +) -> SocketAddrAny { + #[cfg(unix)] + let offsetof_sun_path = super::addr::offsetof_sun_path(); + + assert!(len >= size_of::<c::sa_family_t>()); + match family { + c::AF_INET => { + assert!(len >= size_of::<c::sockaddr_in>()); + let decode = &*storage.cast::<c::sockaddr_in>(); + SocketAddrAny::V4(SocketAddrV4::new( + Ipv4Addr::from(u32::from_be(in_addr_s_addr(decode.sin_addr))), + u16::from_be(decode.sin_port), + )) + } + c::AF_INET6 => { + assert!(len >= size_of::<c::sockaddr_in6>()); + let decode = &*storage.cast::<c::sockaddr_in6>(); + SocketAddrAny::V6(SocketAddrV6::new( + Ipv6Addr::from(in6_addr_s6_addr(decode.sin6_addr)), + u16::from_be(decode.sin6_port), + u32::from_be(decode.sin6_flowinfo), + sockaddr_in6_sin6_scope_id(decode), + )) + } + #[cfg(unix)] + c::AF_UNIX => { + assert!(len >= offsetof_sun_path); + if len == offsetof_sun_path { + SocketAddrAny::Unix(SocketAddrUnix::new(&[][..]).unwrap()) + } else { + let decode = &*storage.cast::<c::sockaddr_un>(); + + // On Linux check for Linux's [abstract namespace]. + // + // [abstract namespace]: https://man7.org/linux/man-pages/man7/unix.7.html + #[cfg(linux_kernel)] + if decode.sun_path[0] == 0 { + return SocketAddrAny::Unix( + SocketAddrUnix::new_abstract_name(core::mem::transmute::< + &[c::c_char], + &[u8], + >( + &decode.sun_path[1..len - offsetof_sun_path], + )) + .unwrap(), + ); + } + + // Otherwise we expect a NUL-terminated filesystem path. + assert_eq!(decode.sun_path[len - 1 - offsetof_sun_path], 0); + let path_bytes = &decode.sun_path[..len - 1 - offsetof_sun_path]; + + // FreeBSD and illumos sometimes set the length to longer than + // the length of the NUL-terminated string. Find the NUL and + // truncate the string accordingly. + #[cfg(any(solarish, target_os = "freebsd"))] + let path_bytes = &path_bytes[..path_bytes.iter().position(|b| *b == 0).unwrap()]; + + SocketAddrAny::Unix( + SocketAddrUnix::new(core::mem::transmute::<&[c::c_char], &[u8]>(path_bytes)) + .unwrap(), + ) + } + } + other => unimplemented!("{:?}", other), + } +} diff --git a/vendor/rustix/src/backend/libc/net/send_recv.rs b/vendor/rustix/src/backend/libc/net/send_recv.rs new file mode 100644 index 0000000..5dc60dd --- /dev/null +++ b/vendor/rustix/src/backend/libc/net/send_recv.rs @@ -0,0 +1,103 @@ +use crate::backend::c; +use bitflags::bitflags; + +bitflags! { + /// `MSG_*` flags for use with [`send`], [`send_to`], and related + /// functions. + /// + /// [`send`]: crate::net::send + /// [`sendto`]: crate::net::sendto + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct SendFlags: u32 { + /// `MSG_CONFIRM` + #[cfg(not(any( + bsd, + solarish, + windows, + target_os = "aix", + target_os = "espidf", + target_os = "nto", + target_os = "haiku", + target_os = "vita", + )))] + const CONFIRM = bitcast!(c::MSG_CONFIRM); + /// `MSG_DONTROUTE` + const DONTROUTE = bitcast!(c::MSG_DONTROUTE); + /// `MSG_DONTWAIT` + #[cfg(not(windows))] + const DONTWAIT = bitcast!(c::MSG_DONTWAIT); + /// `MSG_EOR` + #[cfg(not(windows))] + const EOT = bitcast!(c::MSG_EOR); + /// `MSG_MORE` + #[cfg(not(any( + bsd, + solarish, + windows, + target_os = "aix", + target_os = "haiku", + target_os = "nto", + target_os = "vita", + )))] + const MORE = bitcast!(c::MSG_MORE); + #[cfg(not(any(apple, windows, target_os = "vita")))] + /// `MSG_NOSIGNAL` + const NOSIGNAL = bitcast!(c::MSG_NOSIGNAL); + /// `MSG_OOB` + const OOB = bitcast!(c::MSG_OOB); + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} + +bitflags! { + /// `MSG_*` flags for use with [`recv`], [`recvfrom`], and related + /// functions. + /// + /// [`recv`]: crate::net::recv + /// [`recvfrom`]: crate::net::recvfrom + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct RecvFlags: u32 { + #[cfg(not(any( + apple, + solarish, + windows, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "nto", + target_os = "vita", + )))] + /// `MSG_CMSG_CLOEXEC` + const CMSG_CLOEXEC = bitcast!(c::MSG_CMSG_CLOEXEC); + /// `MSG_DONTWAIT` + #[cfg(not(windows))] + const DONTWAIT = bitcast!(c::MSG_DONTWAIT); + /// `MSG_ERRQUEUE` + #[cfg(not(any( + bsd, + solarish, + windows, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "nto", + target_os = "vita", + )))] + const ERRQUEUE = bitcast!(c::MSG_ERRQUEUE); + /// `MSG_OOB` + const OOB = bitcast!(c::MSG_OOB); + /// `MSG_PEEK` + const PEEK = bitcast!(c::MSG_PEEK); + /// `MSG_TRUNC` + const TRUNC = bitcast!(c::MSG_TRUNC); + /// `MSG_WAITALL` + const WAITALL = bitcast!(c::MSG_WAITALL); + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} diff --git a/vendor/rustix/src/backend/libc/net/sockopt.rs b/vendor/rustix/src/backend/libc/net/sockopt.rs new file mode 100644 index 0000000..cff2ca2 --- /dev/null +++ b/vendor/rustix/src/backend/libc/net/sockopt.rs @@ -0,0 +1,1065 @@ +//! libc syscalls supporting `rustix::net::sockopt`. + +use super::ext::{in6_addr_new, in_addr_new}; +use crate::backend::c; +use crate::backend::conv::{borrowed_fd, ret}; +use crate::fd::BorrowedFd; +#[cfg(feature = "alloc")] +#[cfg(any( + linux_like, + target_os = "freebsd", + target_os = "fuchsia", + target_os = "illumos" +))] +use crate::ffi::CStr; +use crate::io; +use crate::net::sockopt::Timeout; +#[cfg(not(any( + apple, + windows, + target_os = "aix", + target_os = "dragonfly", + target_os = "emscripten", + target_os = "espidf", + target_os = "haiku", + target_os = "netbsd", + target_os = "nto", + target_os = "vita", +)))] +use crate::net::AddressFamily; +#[cfg(any( + linux_kernel, + target_os = "freebsd", + target_os = "fuchsia", + target_os = "openbsd", + target_os = "redox", + target_env = "newlib" +))] +use crate::net::Protocol; +#[cfg(any( + linux_kernel, + target_os = "freebsd", + target_os = "fuchsia", + target_os = "openbsd", + target_os = "redox", + target_env = "newlib" +))] +use crate::net::RawProtocol; +use crate::net::{Ipv4Addr, Ipv6Addr, SocketType}; +#[cfg(any(linux_kernel, target_os = "fuchsia"))] +use crate::net::{SocketAddrAny, SocketAddrStorage, SocketAddrV4}; +#[cfg(linux_kernel)] +use crate::net::{SocketAddrV6, UCred}; +use crate::utils::as_mut_ptr; +#[cfg(feature = "alloc")] +#[cfg(any( + linux_like, + target_os = "freebsd", + target_os = "fuchsia", + target_os = "illumos" +))] +use alloc::borrow::ToOwned; +#[cfg(feature = "alloc")] +#[cfg(any( + linux_like, + target_os = "freebsd", + target_os = "fuchsia", + target_os = "illumos" +))] +use alloc::string::String; +#[cfg(apple)] +use c::TCP_KEEPALIVE as TCP_KEEPIDLE; +#[cfg(not(any(apple, target_os = "openbsd", target_os = "haiku", target_os = "nto")))] +use c::TCP_KEEPIDLE; +use core::mem::{size_of, MaybeUninit}; +use core::time::Duration; +#[cfg(windows)] +use windows_sys::Win32::Foundation::BOOL; + +#[inline] +fn getsockopt<T: Copy>(fd: BorrowedFd<'_>, level: i32, optname: i32) -> io::Result<T> { + let mut optlen = core::mem::size_of::<T>().try_into().unwrap(); + debug_assert!( + optlen as usize >= core::mem::size_of::<c::c_int>(), + "Socket APIs don't ever use `bool` directly" + ); + + let mut value = MaybeUninit::<T>::zeroed(); + getsockopt_raw(fd, level, optname, &mut value, &mut optlen)?; + + // On Windows at least, `getsockopt` has been observed writing 1 + // byte on at least (`IPPROTO_TCP`, `TCP_NODELAY`), even though + // Windows' documentation says that should write a 4-byte `BOOL`. + // So, we initialize the memory to zeros above, and just assert + // that `getsockopt` doesn't write too many bytes here. + assert!( + optlen as usize <= size_of::<T>(), + "unexpected getsockopt size" + ); + + unsafe { Ok(value.assume_init()) } +} + +#[inline] +fn getsockopt_raw<T>( + fd: BorrowedFd<'_>, + level: i32, + optname: i32, + value: &mut MaybeUninit<T>, + optlen: &mut c::socklen_t, +) -> io::Result<()> { + unsafe { + ret(c::getsockopt( + borrowed_fd(fd), + level, + optname, + as_mut_ptr(value).cast(), + optlen, + )) + } +} + +#[inline] +fn setsockopt<T: Copy>(fd: BorrowedFd<'_>, level: i32, optname: i32, value: T) -> io::Result<()> { + let optlen = core::mem::size_of::<T>().try_into().unwrap(); + debug_assert!( + optlen as usize >= core::mem::size_of::<c::c_int>(), + "Socket APIs don't ever use `bool` directly" + ); + setsockopt_raw(fd, level, optname, &value, optlen) +} + +#[inline] +fn setsockopt_raw<T>( + fd: BorrowedFd<'_>, + level: i32, + optname: i32, + ptr: *const T, + optlen: c::socklen_t, +) -> io::Result<()> { + unsafe { + ret(c::setsockopt( + borrowed_fd(fd), + level, + optname, + ptr.cast(), + optlen, + )) + } +} + +#[inline] +pub(crate) fn get_socket_type(fd: BorrowedFd<'_>) -> io::Result<SocketType> { + getsockopt(fd, c::SOL_SOCKET, c::SO_TYPE) +} + +#[inline] +pub(crate) fn set_socket_reuseaddr(fd: BorrowedFd<'_>, reuseaddr: bool) -> io::Result<()> { + setsockopt(fd, c::SOL_SOCKET, c::SO_REUSEADDR, from_bool(reuseaddr)) +} + +#[inline] +pub(crate) fn get_socket_reuseaddr(fd: BorrowedFd<'_>) -> io::Result<bool> { + getsockopt(fd, c::SOL_SOCKET, c::SO_REUSEADDR).map(to_bool) +} + +#[inline] +pub(crate) fn set_socket_broadcast(fd: BorrowedFd<'_>, broadcast: bool) -> io::Result<()> { + setsockopt(fd, c::SOL_SOCKET, c::SO_BROADCAST, from_bool(broadcast)) +} + +#[inline] +pub(crate) fn get_socket_broadcast(fd: BorrowedFd<'_>) -> io::Result<bool> { + getsockopt(fd, c::SOL_SOCKET, c::SO_BROADCAST).map(to_bool) +} + +#[inline] +pub(crate) fn set_socket_linger(fd: BorrowedFd<'_>, linger: Option<Duration>) -> io::Result<()> { + // Convert `linger` to seconds, rounding up. + let l_linger = if let Some(linger) = linger { + duration_to_secs(linger)? + } else { + 0 + }; + let linger = c::linger { + l_onoff: linger.is_some().into(), + l_linger, + }; + setsockopt(fd, c::SOL_SOCKET, c::SO_LINGER, linger) +} + +#[inline] +pub(crate) fn get_socket_linger(fd: BorrowedFd<'_>) -> io::Result<Option<Duration>> { + let linger: c::linger = getsockopt(fd, c::SOL_SOCKET, c::SO_LINGER)?; + Ok((linger.l_onoff != 0).then(|| Duration::from_secs(linger.l_linger as u64))) +} + +#[cfg(linux_kernel)] +#[inline] +pub(crate) fn set_socket_passcred(fd: BorrowedFd<'_>, passcred: bool) -> io::Result<()> { + setsockopt(fd, c::SOL_SOCKET, c::SO_PASSCRED, from_bool(passcred)) +} + +#[cfg(linux_kernel)] +#[inline] +pub(crate) fn get_socket_passcred(fd: BorrowedFd<'_>) -> io::Result<bool> { + getsockopt(fd, c::SOL_SOCKET, c::SO_PASSCRED).map(to_bool) +} + +#[inline] +pub(crate) fn set_socket_timeout( + fd: BorrowedFd<'_>, + id: Timeout, + timeout: Option<Duration>, +) -> io::Result<()> { + let optname = match id { + Timeout::Recv => c::SO_RCVTIMEO, + Timeout::Send => c::SO_SNDTIMEO, + }; + + #[cfg(not(windows))] + let timeout = match timeout { + Some(timeout) => { + if timeout == Duration::ZERO { + return Err(io::Errno::INVAL); + } + + // Rust's musl libc bindings deprecated `time_t` while they + // transition to 64-bit `time_t`. What we want here is just + // “whatever type `timeval`'s `tv_sec` is”, so we're ok using + // the deprecated type. + #[allow(deprecated)] + let tv_sec = timeout.as_secs().try_into().unwrap_or(c::time_t::MAX); + + // `subsec_micros` rounds down, so we use `subsec_nanos` and + // manually round up. + let mut timeout = c::timeval { + tv_sec, + tv_usec: ((timeout.subsec_nanos() + 999) / 1000) as _, + }; + if timeout.tv_sec == 0 && timeout.tv_usec == 0 { + timeout.tv_usec = 1; + } + timeout + } + None => c::timeval { + tv_sec: 0, + tv_usec: 0, + }, + }; + + #[cfg(windows)] + let timeout: u32 = match timeout { + Some(timeout) => { + if timeout == Duration::ZERO { + return Err(io::Errno::INVAL); + } + + // `as_millis` rounds down, so we use `as_nanos` and + // manually round up. + let mut timeout: u32 = ((timeout.as_nanos() + 999_999) / 1_000_000) + .try_into() + .map_err(|_convert_err| io::Errno::INVAL)?; + if timeout == 0 { + timeout = 1; + } + timeout + } + None => 0, + }; + + setsockopt(fd, c::SOL_SOCKET, optname, timeout) +} + +#[inline] +pub(crate) fn get_socket_timeout(fd: BorrowedFd<'_>, id: Timeout) -> io::Result<Option<Duration>> { + let optname = match id { + Timeout::Recv => c::SO_RCVTIMEO, + Timeout::Send => c::SO_SNDTIMEO, + }; + + #[cfg(not(windows))] + { + let timeout: c::timeval = getsockopt(fd, c::SOL_SOCKET, optname)?; + if timeout.tv_sec == 0 && timeout.tv_usec == 0 { + Ok(None) + } else { + Ok(Some( + Duration::from_secs(timeout.tv_sec as u64) + + Duration::from_micros(timeout.tv_usec as u64), + )) + } + } + + #[cfg(windows)] + { + let timeout: u32 = getsockopt(fd, c::SOL_SOCKET, optname)?; + if timeout == 0 { + Ok(None) + } else { + Ok(Some(Duration::from_millis(timeout as u64))) + } + } +} + +#[cfg(any(apple, freebsdlike, target_os = "netbsd"))] +#[inline] +pub(crate) fn get_socket_nosigpipe(fd: BorrowedFd<'_>) -> io::Result<bool> { + getsockopt(fd, c::SOL_SOCKET, c::SO_NOSIGPIPE).map(to_bool) +} + +#[cfg(any(apple, freebsdlike, target_os = "netbsd"))] +#[inline] +pub(crate) fn set_socket_nosigpipe(fd: BorrowedFd<'_>, val: bool) -> io::Result<()> { + setsockopt(fd, c::SOL_SOCKET, c::SO_NOSIGPIPE, from_bool(val)) +} + +#[inline] +pub(crate) fn get_socket_error(fd: BorrowedFd<'_>) -> io::Result<Result<(), io::Errno>> { + let err: c::c_int = getsockopt(fd, c::SOL_SOCKET, c::SO_ERROR)?; + Ok(if err == 0 { + Ok(()) + } else { + Err(io::Errno::from_raw_os_error(err)) + }) +} + +#[inline] +pub(crate) fn set_socket_keepalive(fd: BorrowedFd<'_>, keepalive: bool) -> io::Result<()> { + setsockopt(fd, c::SOL_SOCKET, c::SO_KEEPALIVE, from_bool(keepalive)) +} + +#[inline] +pub(crate) fn get_socket_keepalive(fd: BorrowedFd<'_>) -> io::Result<bool> { + getsockopt(fd, c::SOL_SOCKET, c::SO_KEEPALIVE).map(to_bool) +} + +#[inline] +pub(crate) fn set_socket_recv_buffer_size(fd: BorrowedFd<'_>, size: usize) -> io::Result<()> { + let size: c::c_int = size.try_into().map_err(|_| io::Errno::INVAL)?; + setsockopt(fd, c::SOL_SOCKET, c::SO_RCVBUF, size) +} + +#[inline] +pub(crate) fn get_socket_recv_buffer_size(fd: BorrowedFd<'_>) -> io::Result<usize> { + getsockopt(fd, c::SOL_SOCKET, c::SO_RCVBUF).map(|size: u32| size as usize) +} + +#[inline] +pub(crate) fn set_socket_send_buffer_size(fd: BorrowedFd<'_>, size: usize) -> io::Result<()> { + let size: c::c_int = size.try_into().map_err(|_| io::Errno::INVAL)?; + setsockopt(fd, c::SOL_SOCKET, c::SO_SNDBUF, size) +} + +#[inline] +pub(crate) fn get_socket_send_buffer_size(fd: BorrowedFd<'_>) -> io::Result<usize> { + getsockopt(fd, c::SOL_SOCKET, c::SO_SNDBUF).map(|size: u32| size as usize) +} + +#[inline] +#[cfg(not(any( + apple, + windows, + target_os = "aix", + target_os = "dragonfly", + target_os = "emscripten", + target_os = "espidf", + target_os = "haiku", + target_os = "netbsd", + target_os = "nto", + target_os = "vita", +)))] +pub(crate) fn get_socket_domain(fd: BorrowedFd<'_>) -> io::Result<AddressFamily> { + let domain: c::c_int = getsockopt(fd, c::SOL_SOCKET, c::SO_DOMAIN)?; + Ok(AddressFamily( + domain.try_into().map_err(|_| io::Errno::OPNOTSUPP)?, + )) +} + +#[inline] +#[cfg(not(apple))] // Apple platforms declare the constant, but do not actually implement it. +pub(crate) fn get_socket_acceptconn(fd: BorrowedFd<'_>) -> io::Result<bool> { + getsockopt(fd, c::SOL_SOCKET, c::SO_ACCEPTCONN).map(to_bool) +} + +#[inline] +pub(crate) fn set_socket_oobinline(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> { + setsockopt(fd, c::SOL_SOCKET, c::SO_OOBINLINE, from_bool(value)) +} + +#[inline] +pub(crate) fn get_socket_oobinline(fd: BorrowedFd<'_>) -> io::Result<bool> { + getsockopt(fd, c::SOL_SOCKET, c::SO_OOBINLINE).map(to_bool) +} + +#[cfg(not(any(solarish, windows)))] +#[inline] +pub(crate) fn set_socket_reuseport(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> { + setsockopt(fd, c::SOL_SOCKET, c::SO_REUSEPORT, from_bool(value)) +} + +#[cfg(not(any(solarish, windows)))] +#[inline] +pub(crate) fn get_socket_reuseport(fd: BorrowedFd<'_>) -> io::Result<bool> { + getsockopt(fd, c::SOL_SOCKET, c::SO_REUSEPORT).map(to_bool) +} + +#[cfg(target_os = "freebsd")] +#[inline] +pub(crate) fn set_socket_reuseport_lb(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> { + setsockopt(fd, c::SOL_SOCKET, c::SO_REUSEPORT_LB, from_bool(value)) +} + +#[cfg(target_os = "freebsd")] +#[inline] +pub(crate) fn get_socket_reuseport_lb(fd: BorrowedFd<'_>) -> io::Result<bool> { + getsockopt(fd, c::SOL_SOCKET, c::SO_REUSEPORT_LB).map(to_bool) +} + +#[cfg(any( + linux_kernel, + target_os = "freebsd", + target_os = "fuchsia", + target_os = "openbsd", + target_os = "redox", + target_env = "newlib" +))] +#[inline] +pub(crate) fn get_socket_protocol(fd: BorrowedFd<'_>) -> io::Result<Option<Protocol>> { + getsockopt(fd, c::SOL_SOCKET, c::SO_PROTOCOL) + .map(|raw| RawProtocol::new(raw).map(Protocol::from_raw)) +} + +#[cfg(target_os = "linux")] +#[inline] +pub(crate) fn get_socket_cookie(fd: BorrowedFd<'_>) -> io::Result<u64> { + getsockopt(fd, c::SOL_SOCKET, c::SO_COOKIE) +} + +#[cfg(target_os = "linux")] +#[inline] +pub(crate) fn get_socket_incoming_cpu(fd: BorrowedFd<'_>) -> io::Result<u32> { + getsockopt(fd, c::SOL_SOCKET, c::SO_INCOMING_CPU) +} + +#[cfg(target_os = "linux")] +#[inline] +pub(crate) fn set_socket_incoming_cpu(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> { + setsockopt(fd, c::SOL_SOCKET, c::SO_INCOMING_CPU, value) +} + +#[inline] +pub(crate) fn set_ip_ttl(fd: BorrowedFd<'_>, ttl: u32) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_IP, c::IP_TTL, ttl) +} + +#[inline] +pub(crate) fn get_ip_ttl(fd: BorrowedFd<'_>) -> io::Result<u32> { + getsockopt(fd, c::IPPROTO_IP, c::IP_TTL) +} + +#[inline] +pub(crate) fn set_ipv6_v6only(fd: BorrowedFd<'_>, only_v6: bool) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_IPV6, c::IPV6_V6ONLY, from_bool(only_v6)) +} + +#[inline] +pub(crate) fn get_ipv6_v6only(fd: BorrowedFd<'_>) -> io::Result<bool> { + getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_V6ONLY).map(to_bool) +} + +#[inline] +pub(crate) fn set_ip_multicast_loop(fd: BorrowedFd<'_>, multicast_loop: bool) -> io::Result<()> { + setsockopt( + fd, + c::IPPROTO_IP, + c::IP_MULTICAST_LOOP, + from_bool(multicast_loop), + ) +} + +#[inline] +pub(crate) fn get_ip_multicast_loop(fd: BorrowedFd<'_>) -> io::Result<bool> { + getsockopt(fd, c::IPPROTO_IP, c::IP_MULTICAST_LOOP).map(to_bool) +} + +#[inline] +pub(crate) fn set_ip_multicast_ttl(fd: BorrowedFd<'_>, multicast_ttl: u32) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_IP, c::IP_MULTICAST_TTL, multicast_ttl) +} + +#[inline] +pub(crate) fn get_ip_multicast_ttl(fd: BorrowedFd<'_>) -> io::Result<u32> { + getsockopt(fd, c::IPPROTO_IP, c::IP_MULTICAST_TTL) +} + +#[inline] +pub(crate) fn set_ipv6_multicast_loop(fd: BorrowedFd<'_>, multicast_loop: bool) -> io::Result<()> { + setsockopt( + fd, + c::IPPROTO_IPV6, + c::IPV6_MULTICAST_LOOP, + from_bool(multicast_loop), + ) +} + +#[inline] +pub(crate) fn get_ipv6_multicast_loop(fd: BorrowedFd<'_>) -> io::Result<bool> { + getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_MULTICAST_LOOP).map(to_bool) +} + +#[inline] +pub(crate) fn set_ipv6_multicast_hops(fd: BorrowedFd<'_>, multicast_hops: u32) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_IP, c::IPV6_MULTICAST_HOPS, multicast_hops) +} + +#[inline] +pub(crate) fn get_ipv6_multicast_hops(fd: BorrowedFd<'_>) -> io::Result<u32> { + getsockopt(fd, c::IPPROTO_IP, c::IPV6_MULTICAST_HOPS) +} + +#[inline] +pub(crate) fn set_ip_add_membership( + fd: BorrowedFd<'_>, + multiaddr: &Ipv4Addr, + interface: &Ipv4Addr, +) -> io::Result<()> { + let mreq = to_ip_mreq(multiaddr, interface); + setsockopt(fd, c::IPPROTO_IP, c::IP_ADD_MEMBERSHIP, mreq) +} + +#[cfg(any( + apple, + freebsdlike, + linux_like, + target_os = "fuchsia", + target_os = "openbsd" +))] +#[inline] +pub(crate) fn set_ip_add_membership_with_ifindex( + fd: BorrowedFd<'_>, + multiaddr: &Ipv4Addr, + address: &Ipv4Addr, + ifindex: i32, +) -> io::Result<()> { + let mreqn = to_ip_mreqn(multiaddr, address, ifindex); + setsockopt(fd, c::IPPROTO_IP, c::IP_ADD_MEMBERSHIP, mreqn) +} + +#[cfg(any(apple, freebsdlike, linux_like, solarish, target_os = "aix"))] +#[inline] +pub(crate) fn set_ip_add_source_membership( + fd: BorrowedFd<'_>, + multiaddr: &Ipv4Addr, + interface: &Ipv4Addr, + sourceaddr: &Ipv4Addr, +) -> io::Result<()> { + let mreq_source = to_imr_source(multiaddr, interface, sourceaddr); + setsockopt(fd, c::IPPROTO_IP, c::IP_ADD_SOURCE_MEMBERSHIP, mreq_source) +} + +#[cfg(any(apple, freebsdlike, linux_like, solarish, target_os = "aix"))] +#[inline] +pub(crate) fn set_ip_drop_source_membership( + fd: BorrowedFd<'_>, + multiaddr: &Ipv4Addr, + interface: &Ipv4Addr, + sourceaddr: &Ipv4Addr, +) -> io::Result<()> { + let mreq_source = to_imr_source(multiaddr, interface, sourceaddr); + setsockopt(fd, c::IPPROTO_IP, c::IP_DROP_SOURCE_MEMBERSHIP, mreq_source) +} + +#[inline] +pub(crate) fn set_ipv6_add_membership( + fd: BorrowedFd<'_>, + multiaddr: &Ipv6Addr, + interface: u32, +) -> io::Result<()> { + #[cfg(not(any( + bsd, + solarish, + target_os = "haiku", + target_os = "l4re", + target_os = "nto" + )))] + use c::IPV6_ADD_MEMBERSHIP; + #[cfg(any( + bsd, + solarish, + target_os = "haiku", + target_os = "l4re", + target_os = "nto" + ))] + use c::IPV6_JOIN_GROUP as IPV6_ADD_MEMBERSHIP; + + let mreq = to_ipv6mr(multiaddr, interface); + setsockopt(fd, c::IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, mreq) +} + +#[inline] +pub(crate) fn set_ip_drop_membership( + fd: BorrowedFd<'_>, + multiaddr: &Ipv4Addr, + interface: &Ipv4Addr, +) -> io::Result<()> { + let mreq = to_ip_mreq(multiaddr, interface); + setsockopt(fd, c::IPPROTO_IP, c::IP_DROP_MEMBERSHIP, mreq) +} + +#[cfg(any( + apple, + freebsdlike, + linux_like, + target_os = "fuchsia", + target_os = "openbsd" +))] +#[inline] +pub(crate) fn set_ip_drop_membership_with_ifindex( + fd: BorrowedFd<'_>, + multiaddr: &Ipv4Addr, + address: &Ipv4Addr, + ifindex: i32, +) -> io::Result<()> { + let mreqn = to_ip_mreqn(multiaddr, address, ifindex); + setsockopt(fd, c::IPPROTO_IP, c::IP_DROP_MEMBERSHIP, mreqn) +} + +#[inline] +pub(crate) fn set_ipv6_drop_membership( + fd: BorrowedFd<'_>, + multiaddr: &Ipv6Addr, + interface: u32, +) -> io::Result<()> { + #[cfg(not(any( + bsd, + solarish, + target_os = "haiku", + target_os = "l4re", + target_os = "nto" + )))] + use c::IPV6_DROP_MEMBERSHIP; + #[cfg(any( + bsd, + solarish, + target_os = "haiku", + target_os = "l4re", + target_os = "nto" + ))] + use c::IPV6_LEAVE_GROUP as IPV6_DROP_MEMBERSHIP; + + let mreq = to_ipv6mr(multiaddr, interface); + setsockopt(fd, c::IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, mreq) +} + +#[inline] +pub(crate) fn get_ipv6_unicast_hops(fd: BorrowedFd<'_>) -> io::Result<u8> { + getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_UNICAST_HOPS).map(|hops: c::c_int| hops as u8) +} + +#[inline] +pub(crate) fn set_ipv6_unicast_hops(fd: BorrowedFd<'_>, hops: Option<u8>) -> io::Result<()> { + let hops = match hops { + Some(hops) => hops as c::c_int, + None => -1, + }; + setsockopt(fd, c::IPPROTO_IPV6, c::IPV6_UNICAST_HOPS, hops) +} + +#[cfg(any( + bsd, + linux_like, + target_os = "aix", + target_os = "fuchsia", + target_os = "haiku", + target_os = "nto", + target_env = "newlib" +))] +#[inline] +pub(crate) fn set_ip_tos(fd: BorrowedFd<'_>, value: u8) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_IP, c::IP_TOS, i32::from(value)) +} + +#[cfg(any( + bsd, + linux_like, + target_os = "aix", + target_os = "fuchsia", + target_os = "haiku", + target_os = "nto", + target_env = "newlib" +))] +#[inline] +pub(crate) fn get_ip_tos(fd: BorrowedFd<'_>) -> io::Result<u8> { + let value: i32 = getsockopt(fd, c::IPPROTO_IP, c::IP_TOS)?; + Ok(value as u8) +} + +#[cfg(any(apple, linux_like, target_os = "freebsd", target_os = "fuchsia"))] +#[inline] +pub(crate) fn set_ip_recvtos(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_IP, c::IP_RECVTOS, from_bool(value)) +} + +#[cfg(any(apple, linux_like, target_os = "freebsd", target_os = "fuchsia"))] +#[inline] +pub(crate) fn get_ip_recvtos(fd: BorrowedFd<'_>) -> io::Result<bool> { + getsockopt(fd, c::IPPROTO_IP, c::IP_RECVTOS).map(to_bool) +} + +#[cfg(any( + bsd, + linux_like, + target_os = "aix", + target_os = "fuchsia", + target_os = "nto" +))] +#[inline] +pub(crate) fn set_ipv6_recvtclass(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_IPV6, c::IPV6_RECVTCLASS, from_bool(value)) +} + +#[cfg(any( + bsd, + linux_like, + target_os = "aix", + target_os = "fuchsia", + target_os = "nto" +))] +#[inline] +pub(crate) fn get_ipv6_recvtclass(fd: BorrowedFd<'_>) -> io::Result<bool> { + getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_RECVTCLASS).map(to_bool) +} + +#[cfg(any(linux_kernel, target_os = "fuchsia"))] +#[inline] +pub(crate) fn set_ip_freebind(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_IP, c::IP_FREEBIND, from_bool(value)) +} + +#[cfg(any(linux_kernel, target_os = "fuchsia"))] +#[inline] +pub(crate) fn get_ip_freebind(fd: BorrowedFd<'_>) -> io::Result<bool> { + getsockopt(fd, c::IPPROTO_IP, c::IP_FREEBIND).map(to_bool) +} + +#[cfg(linux_kernel)] +#[inline] +pub(crate) fn set_ipv6_freebind(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_IPV6, c::IPV6_FREEBIND, from_bool(value)) +} + +#[cfg(linux_kernel)] +#[inline] +pub(crate) fn get_ipv6_freebind(fd: BorrowedFd<'_>) -> io::Result<bool> { + getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_FREEBIND).map(to_bool) +} + +#[cfg(any(linux_kernel, target_os = "fuchsia"))] +#[inline] +pub(crate) fn get_ip_original_dst(fd: BorrowedFd<'_>) -> io::Result<SocketAddrV4> { + let level = c::IPPROTO_IP; + let optname = c::SO_ORIGINAL_DST; + let mut value = MaybeUninit::<SocketAddrStorage>::uninit(); + let mut optlen = core::mem::size_of_val(&value).try_into().unwrap(); + + getsockopt_raw(fd, level, optname, &mut value, &mut optlen)?; + + let any = unsafe { SocketAddrAny::read(value.as_ptr(), optlen as usize)? }; + match any { + SocketAddrAny::V4(v4) => Ok(v4), + _ => unreachable!(), + } +} + +#[cfg(linux_kernel)] +#[inline] +pub(crate) fn get_ipv6_original_dst(fd: BorrowedFd<'_>) -> io::Result<SocketAddrV6> { + let level = c::IPPROTO_IPV6; + let optname = c::IP6T_SO_ORIGINAL_DST; + let mut value = MaybeUninit::<SocketAddrStorage>::uninit(); + let mut optlen = core::mem::size_of_val(&value).try_into().unwrap(); + + getsockopt_raw(fd, level, optname, &mut value, &mut optlen)?; + + let any = unsafe { SocketAddrAny::read(value.as_ptr(), optlen as usize)? }; + match any { + SocketAddrAny::V6(v6) => Ok(v6), + _ => unreachable!(), + } +} + +#[cfg(not(any( + solarish, + windows, + target_os = "espidf", + target_os = "haiku", + target_os = "vita" +)))] +#[inline] +pub(crate) fn set_ipv6_tclass(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_IPV6, c::IPV6_TCLASS, value) +} + +#[cfg(not(any( + solarish, + windows, + target_os = "espidf", + target_os = "haiku", + target_os = "vita" +)))] +#[inline] +pub(crate) fn get_ipv6_tclass(fd: BorrowedFd<'_>) -> io::Result<u32> { + getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_TCLASS) +} + +#[inline] +pub(crate) fn set_tcp_nodelay(fd: BorrowedFd<'_>, nodelay: bool) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_TCP, c::TCP_NODELAY, from_bool(nodelay)) +} + +#[inline] +pub(crate) fn get_tcp_nodelay(fd: BorrowedFd<'_>) -> io::Result<bool> { + getsockopt(fd, c::IPPROTO_TCP, c::TCP_NODELAY).map(to_bool) +} + +#[inline] +#[cfg(not(any(target_os = "openbsd", target_os = "haiku", target_os = "nto")))] +pub(crate) fn set_tcp_keepcnt(fd: BorrowedFd<'_>, count: u32) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_TCP, c::TCP_KEEPCNT, count) +} + +#[inline] +#[cfg(not(any(target_os = "openbsd", target_os = "haiku", target_os = "nto")))] +pub(crate) fn get_tcp_keepcnt(fd: BorrowedFd<'_>) -> io::Result<u32> { + getsockopt(fd, c::IPPROTO_TCP, c::TCP_KEEPCNT) +} + +#[inline] +#[cfg(not(any(target_os = "openbsd", target_os = "haiku", target_os = "nto")))] +pub(crate) fn set_tcp_keepidle(fd: BorrowedFd<'_>, duration: Duration) -> io::Result<()> { + let secs: c::c_uint = duration_to_secs(duration)?; + setsockopt(fd, c::IPPROTO_TCP, TCP_KEEPIDLE, secs) +} + +#[inline] +#[cfg(not(any(target_os = "openbsd", target_os = "haiku", target_os = "nto")))] +pub(crate) fn get_tcp_keepidle(fd: BorrowedFd<'_>) -> io::Result<Duration> { + let secs: c::c_uint = getsockopt(fd, c::IPPROTO_TCP, TCP_KEEPIDLE)?; + Ok(Duration::from_secs(secs as u64)) +} + +#[inline] +#[cfg(not(any(target_os = "openbsd", target_os = "haiku", target_os = "nto")))] +pub(crate) fn set_tcp_keepintvl(fd: BorrowedFd<'_>, duration: Duration) -> io::Result<()> { + let secs: c::c_uint = duration_to_secs(duration)?; + setsockopt(fd, c::IPPROTO_TCP, c::TCP_KEEPINTVL, secs) +} + +#[inline] +#[cfg(not(any(target_os = "openbsd", target_os = "haiku", target_os = "nto")))] +pub(crate) fn get_tcp_keepintvl(fd: BorrowedFd<'_>) -> io::Result<Duration> { + let secs: c::c_uint = getsockopt(fd, c::IPPROTO_TCP, c::TCP_KEEPINTVL)?; + Ok(Duration::from_secs(secs as u64)) +} + +#[inline] +#[cfg(any(linux_like, target_os = "fuchsia"))] +pub(crate) fn set_tcp_user_timeout(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_TCP, c::TCP_USER_TIMEOUT, value) +} + +#[inline] +#[cfg(any(linux_like, target_os = "fuchsia"))] +pub(crate) fn get_tcp_user_timeout(fd: BorrowedFd<'_>) -> io::Result<u32> { + getsockopt(fd, c::IPPROTO_TCP, c::TCP_USER_TIMEOUT) +} + +#[cfg(any(linux_like, target_os = "fuchsia"))] +#[inline] +pub(crate) fn set_tcp_quickack(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_TCP, c::TCP_QUICKACK, from_bool(value)) +} + +#[cfg(any(linux_like, target_os = "fuchsia"))] +#[inline] +pub(crate) fn get_tcp_quickack(fd: BorrowedFd<'_>) -> io::Result<bool> { + getsockopt(fd, c::IPPROTO_TCP, c::TCP_QUICKACK).map(to_bool) +} + +#[cfg(any( + linux_like, + target_os = "freebsd", + target_os = "fuchsia", + target_os = "illumos" +))] +#[inline] +pub(crate) fn set_tcp_congestion(fd: BorrowedFd<'_>, value: &str) -> io::Result<()> { + let level = c::IPPROTO_TCP; + let optname = c::TCP_CONGESTION; + let optlen = value.len().try_into().unwrap(); + setsockopt_raw(fd, level, optname, value.as_ptr(), optlen) +} + +#[cfg(feature = "alloc")] +#[cfg(any( + linux_like, + target_os = "freebsd", + target_os = "fuchsia", + target_os = "illumos" +))] +#[inline] +pub(crate) fn get_tcp_congestion(fd: BorrowedFd<'_>) -> io::Result<String> { + let level = c::IPPROTO_TCP; + let optname = c::TCP_CONGESTION; + const OPTLEN: c::socklen_t = 16; + let mut value = MaybeUninit::<[MaybeUninit<u8>; OPTLEN as usize]>::uninit(); + let mut optlen = OPTLEN; + getsockopt_raw(fd, level, optname, &mut value, &mut optlen)?; + unsafe { + let value = value.assume_init(); + let slice: &[u8] = core::mem::transmute(&value[..optlen as usize]); + assert!(slice.iter().any(|b| *b == b'\0')); + Ok( + core::str::from_utf8(CStr::from_ptr(slice.as_ptr().cast()).to_bytes()) + .unwrap() + .to_owned(), + ) + } +} + +#[cfg(any(linux_like, target_os = "fuchsia"))] +#[inline] +pub(crate) fn set_tcp_thin_linear_timeouts(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> { + setsockopt( + fd, + c::IPPROTO_TCP, + c::TCP_THIN_LINEAR_TIMEOUTS, + from_bool(value), + ) +} + +#[cfg(any(linux_like, target_os = "fuchsia"))] +#[inline] +pub(crate) fn get_tcp_thin_linear_timeouts(fd: BorrowedFd<'_>) -> io::Result<bool> { + getsockopt(fd, c::IPPROTO_TCP, c::TCP_THIN_LINEAR_TIMEOUTS).map(to_bool) +} + +#[cfg(any(linux_like, solarish, target_os = "fuchsia"))] +#[inline] +pub(crate) fn set_tcp_cork(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_TCP, c::TCP_CORK, from_bool(value)) +} + +#[cfg(any(linux_like, solarish, target_os = "fuchsia"))] +#[inline] +pub(crate) fn get_tcp_cork(fd: BorrowedFd<'_>) -> io::Result<bool> { + getsockopt(fd, c::IPPROTO_TCP, c::TCP_CORK).map(to_bool) +} + +#[cfg(linux_kernel)] +#[inline] +pub(crate) fn get_socket_peercred(fd: BorrowedFd<'_>) -> io::Result<UCred> { + getsockopt(fd, c::SOL_SOCKET, c::SO_PEERCRED) +} + +#[inline] +fn to_ip_mreq(multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> c::ip_mreq { + c::ip_mreq { + imr_multiaddr: to_imr_addr(multiaddr), + imr_interface: to_imr_addr(interface), + } +} + +#[cfg(any( + apple, + freebsdlike, + linux_like, + target_os = "fuchsia", + target_os = "openbsd" +))] +#[inline] +fn to_ip_mreqn(multiaddr: &Ipv4Addr, address: &Ipv4Addr, ifindex: i32) -> c::ip_mreqn { + c::ip_mreqn { + imr_multiaddr: to_imr_addr(multiaddr), + imr_address: to_imr_addr(address), + imr_ifindex: ifindex, + } +} + +#[cfg(any(apple, freebsdlike, linux_like, solarish, target_os = "aix"))] +#[inline] +fn to_imr_source( + multiaddr: &Ipv4Addr, + interface: &Ipv4Addr, + sourceaddr: &Ipv4Addr, +) -> c::ip_mreq_source { + c::ip_mreq_source { + imr_multiaddr: to_imr_addr(multiaddr), + imr_interface: to_imr_addr(interface), + imr_sourceaddr: to_imr_addr(sourceaddr), + } +} + +#[inline] +fn to_imr_addr(addr: &Ipv4Addr) -> c::in_addr { + in_addr_new(u32::from_ne_bytes(addr.octets())) +} + +#[inline] +fn to_ipv6mr(multiaddr: &Ipv6Addr, interface: u32) -> c::ipv6_mreq { + c::ipv6_mreq { + ipv6mr_multiaddr: to_ipv6mr_multiaddr(multiaddr), + ipv6mr_interface: to_ipv6mr_interface(interface), + } +} + +#[inline] +fn to_ipv6mr_multiaddr(multiaddr: &Ipv6Addr) -> c::in6_addr { + in6_addr_new(multiaddr.octets()) +} + +#[cfg(target_os = "android")] +#[inline] +fn to_ipv6mr_interface(interface: u32) -> c::c_int { + interface as c::c_int +} + +#[cfg(not(target_os = "android"))] +#[inline] +fn to_ipv6mr_interface(interface: u32) -> c::c_uint { + interface as c::c_uint +} + +// `getsockopt` and `setsockopt` represent boolean values as integers. +#[cfg(not(windows))] +type RawSocketBool = c::c_int; +#[cfg(windows)] +type RawSocketBool = BOOL; + +// Wrap `RawSocketBool` in a newtype to discourage misuse. +#[repr(transparent)] +#[derive(Copy, Clone)] +struct SocketBool(RawSocketBool); + +// Convert from a `bool` to a `SocketBool`. +#[inline] +fn from_bool(value: bool) -> SocketBool { + SocketBool(value.into()) +} + +// Convert from a `SocketBool` to a `bool`. +#[inline] +fn to_bool(value: SocketBool) -> bool { + value.0 != 0 +} + +/// Convert to seconds, rounding up if necessary. +#[inline] +fn duration_to_secs<T: TryFrom<u64>>(duration: Duration) -> io::Result<T> { + let mut secs = duration.as_secs(); + if duration.subsec_nanos() != 0 { + secs = secs.checked_add(1).ok_or(io::Errno::INVAL)?; + } + T::try_from(secs).map_err(|_e| io::Errno::INVAL) +} diff --git a/vendor/rustix/src/backend/libc/net/syscalls.rs b/vendor/rustix/src/backend/libc/net/syscalls.rs new file mode 100644 index 0000000..48dbf1f --- /dev/null +++ b/vendor/rustix/src/backend/libc/net/syscalls.rs @@ -0,0 +1,568 @@ +//! libc syscalls supporting `rustix::net`. + +#[cfg(unix)] +use super::addr::SocketAddrUnix; +use crate::backend::c; +use crate::backend::conv::{borrowed_fd, ret, ret_owned_fd, ret_send_recv, send_recv_len}; +use crate::fd::{BorrowedFd, OwnedFd}; +use crate::io; +use crate::net::{SocketAddrAny, SocketAddrV4, SocketAddrV6}; +use crate::utils::as_ptr; +use core::mem::{size_of, MaybeUninit}; +#[cfg(not(any( + windows, + target_os = "espidf", + target_os = "redox", + target_os = "vita", + target_os = "wasi" +)))] +use { + super::msghdr::{with_noaddr_msghdr, with_recv_msghdr, with_v4_msghdr, with_v6_msghdr}, + crate::io::{IoSlice, IoSliceMut}, + crate::net::{RecvAncillaryBuffer, RecvMsgReturn, SendAncillaryBuffer}, +}; +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] +use { + super::read_sockaddr::{initialize_family_to_unspec, maybe_read_sockaddr_os, read_sockaddr_os}, + super::send_recv::{RecvFlags, SendFlags}, + super::write_sockaddr::{encode_sockaddr_v4, encode_sockaddr_v6}, + crate::net::{AddressFamily, Protocol, Shutdown, SocketFlags, SocketType}, + core::ptr::null_mut, +}; + +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] +pub(crate) unsafe fn recv( + fd: BorrowedFd<'_>, + buf: *mut u8, + len: usize, + flags: RecvFlags, +) -> io::Result<usize> { + ret_send_recv(c::recv( + borrowed_fd(fd), + buf.cast(), + send_recv_len(len), + bitflags_bits!(flags), + )) +} + +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] +pub(crate) fn send(fd: BorrowedFd<'_>, buf: &[u8], flags: SendFlags) -> io::Result<usize> { + unsafe { + ret_send_recv(c::send( + borrowed_fd(fd), + buf.as_ptr().cast(), + send_recv_len(buf.len()), + bitflags_bits!(flags), + )) + } +} + +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] +pub(crate) unsafe fn recvfrom( + fd: BorrowedFd<'_>, + buf: *mut u8, + buf_len: usize, + flags: RecvFlags, +) -> io::Result<(usize, Option<SocketAddrAny>)> { + let mut storage = MaybeUninit::<c::sockaddr_storage>::uninit(); + let mut len = size_of::<c::sockaddr_storage>() as c::socklen_t; + + // `recvfrom` does not write to the storage if the socket is + // connection-oriented sockets, so we initialize the family field to + // `AF_UNSPEC` so that we can detect this case. + initialize_family_to_unspec(storage.as_mut_ptr()); + + ret_send_recv(c::recvfrom( + borrowed_fd(fd), + buf.cast(), + send_recv_len(buf_len), + bitflags_bits!(flags), + storage.as_mut_ptr().cast(), + &mut len, + )) + .map(|nread| { + ( + nread, + maybe_read_sockaddr_os(storage.as_ptr(), len.try_into().unwrap()), + ) + }) +} + +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] +pub(crate) fn sendto_v4( + fd: BorrowedFd<'_>, + buf: &[u8], + flags: SendFlags, + addr: &SocketAddrV4, +) -> io::Result<usize> { + unsafe { + ret_send_recv(c::sendto( + borrowed_fd(fd), + buf.as_ptr().cast(), + send_recv_len(buf.len()), + bitflags_bits!(flags), + as_ptr(&encode_sockaddr_v4(addr)).cast::<c::sockaddr>(), + size_of::<c::sockaddr_in>() as c::socklen_t, + )) + } +} + +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] +pub(crate) fn sendto_v6( + fd: BorrowedFd<'_>, + buf: &[u8], + flags: SendFlags, + addr: &SocketAddrV6, +) -> io::Result<usize> { + unsafe { + ret_send_recv(c::sendto( + borrowed_fd(fd), + buf.as_ptr().cast(), + send_recv_len(buf.len()), + bitflags_bits!(flags), + as_ptr(&encode_sockaddr_v6(addr)).cast::<c::sockaddr>(), + size_of::<c::sockaddr_in6>() as c::socklen_t, + )) + } +} + +#[cfg(not(any(windows, target_os = "redox", target_os = "wasi")))] +pub(crate) fn sendto_unix( + fd: BorrowedFd<'_>, + buf: &[u8], + flags: SendFlags, + addr: &SocketAddrUnix, +) -> io::Result<usize> { + unsafe { + ret_send_recv(c::sendto( + borrowed_fd(fd), + buf.as_ptr().cast(), + send_recv_len(buf.len()), + bitflags_bits!(flags), + as_ptr(&addr.unix).cast(), + addr.addr_len(), + )) + } +} + +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] +pub(crate) fn socket( + domain: AddressFamily, + type_: SocketType, + protocol: Option<Protocol>, +) -> io::Result<OwnedFd> { + let raw_protocol = match protocol { + Some(p) => p.0.get(), + None => 0, + }; + unsafe { + ret_owned_fd(c::socket( + domain.0 as c::c_int, + type_.0 as c::c_int, + raw_protocol as c::c_int, + )) + } +} + +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] +pub(crate) fn socket_with( + domain: AddressFamily, + type_: SocketType, + flags: SocketFlags, + protocol: Option<Protocol>, +) -> io::Result<OwnedFd> { + let raw_protocol = match protocol { + Some(p) => p.0.get(), + None => 0, + }; + unsafe { + ret_owned_fd(c::socket( + domain.0 as c::c_int, + (type_.0 | flags.bits()) as c::c_int, + raw_protocol as c::c_int, + )) + } +} + +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] +pub(crate) fn bind_v4(sockfd: BorrowedFd<'_>, addr: &SocketAddrV4) -> io::Result<()> { + unsafe { + ret(c::bind( + borrowed_fd(sockfd), + as_ptr(&encode_sockaddr_v4(addr)).cast(), + size_of::<c::sockaddr_in>() as c::socklen_t, + )) + } +} + +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] +pub(crate) fn bind_v6(sockfd: BorrowedFd<'_>, addr: &SocketAddrV6) -> io::Result<()> { + unsafe { + ret(c::bind( + borrowed_fd(sockfd), + as_ptr(&encode_sockaddr_v6(addr)).cast(), + size_of::<c::sockaddr_in6>() as c::socklen_t, + )) + } +} + +#[cfg(not(any(windows, target_os = "redox", target_os = "wasi")))] +pub(crate) fn bind_unix(sockfd: BorrowedFd<'_>, addr: &SocketAddrUnix) -> io::Result<()> { + unsafe { + ret(c::bind( + borrowed_fd(sockfd), + as_ptr(&addr.unix).cast(), + addr.addr_len(), + )) + } +} + +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] +pub(crate) fn connect_v4(sockfd: BorrowedFd<'_>, addr: &SocketAddrV4) -> io::Result<()> { + unsafe { + ret(c::connect( + borrowed_fd(sockfd), + as_ptr(&encode_sockaddr_v4(addr)).cast(), + size_of::<c::sockaddr_in>() as c::socklen_t, + )) + } +} + +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] +pub(crate) fn connect_v6(sockfd: BorrowedFd<'_>, addr: &SocketAddrV6) -> io::Result<()> { + unsafe { + ret(c::connect( + borrowed_fd(sockfd), + as_ptr(&encode_sockaddr_v6(addr)).cast(), + size_of::<c::sockaddr_in6>() as c::socklen_t, + )) + } +} + +#[cfg(not(any(windows, target_os = "redox", target_os = "wasi")))] +pub(crate) fn connect_unix(sockfd: BorrowedFd<'_>, addr: &SocketAddrUnix) -> io::Result<()> { + unsafe { + ret(c::connect( + borrowed_fd(sockfd), + as_ptr(&addr.unix).cast(), + addr.addr_len(), + )) + } +} + +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] +pub(crate) fn connect_unspec(sockfd: BorrowedFd<'_>) -> io::Result<()> { + debug_assert_eq!(c::AF_UNSPEC, 0); + let addr = MaybeUninit::<c::sockaddr_storage>::zeroed(); + unsafe { + ret(c::connect( + borrowed_fd(sockfd), + as_ptr(&addr).cast(), + size_of::<c::sockaddr_storage>() as c::socklen_t, + )) + } +} + +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] +pub(crate) fn listen(sockfd: BorrowedFd<'_>, backlog: c::c_int) -> io::Result<()> { + unsafe { ret(c::listen(borrowed_fd(sockfd), backlog)) } +} + +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] +pub(crate) fn accept(sockfd: BorrowedFd<'_>) -> io::Result<OwnedFd> { + unsafe { + let owned_fd = ret_owned_fd(c::accept(borrowed_fd(sockfd), null_mut(), null_mut()))?; + Ok(owned_fd) + } +} + +#[cfg(not(any( + windows, + target_os = "espidf", + target_os = "redox", + target_os = "vita", + target_os = "wasi" +)))] +pub(crate) fn recvmsg( + sockfd: BorrowedFd<'_>, + iov: &mut [IoSliceMut<'_>], + control: &mut RecvAncillaryBuffer<'_>, + msg_flags: RecvFlags, +) -> io::Result<RecvMsgReturn> { + let mut storage = MaybeUninit::<c::sockaddr_storage>::uninit(); + + with_recv_msghdr(&mut storage, iov, control, |msghdr| { + let result = unsafe { + ret_send_recv(c::recvmsg( + borrowed_fd(sockfd), + msghdr, + bitflags_bits!(msg_flags), + )) + }; + + result.map(|bytes| { + // Get the address of the sender, if any. + let addr = + unsafe { maybe_read_sockaddr_os(msghdr.msg_name as _, msghdr.msg_namelen as _) }; + + RecvMsgReturn { + bytes, + address: addr, + flags: RecvFlags::from_bits_retain(bitcast!(msghdr.msg_flags)), + } + }) + }) +} + +#[cfg(not(any( + windows, + target_os = "espidf", + target_os = "redox", + target_os = "vita", + target_os = "wasi" +)))] +pub(crate) fn sendmsg( + sockfd: BorrowedFd<'_>, + iov: &[IoSlice<'_>], + control: &mut SendAncillaryBuffer<'_, '_, '_>, + msg_flags: SendFlags, +) -> io::Result<usize> { + with_noaddr_msghdr(iov, control, |msghdr| unsafe { + ret_send_recv(c::sendmsg( + borrowed_fd(sockfd), + &msghdr, + bitflags_bits!(msg_flags), + )) + }) +} + +#[cfg(not(any( + windows, + target_os = "espidf", + target_os = "redox", + target_os = "vita", + target_os = "wasi" +)))] +pub(crate) fn sendmsg_v4( + sockfd: BorrowedFd<'_>, + addr: &SocketAddrV4, + iov: &[IoSlice<'_>], + control: &mut SendAncillaryBuffer<'_, '_, '_>, + msg_flags: SendFlags, +) -> io::Result<usize> { + with_v4_msghdr(addr, iov, control, |msghdr| unsafe { + ret_send_recv(c::sendmsg( + borrowed_fd(sockfd), + &msghdr, + bitflags_bits!(msg_flags), + )) + }) +} + +#[cfg(not(any( + windows, + target_os = "espidf", + target_os = "redox", + target_os = "vita", + target_os = "wasi" +)))] +pub(crate) fn sendmsg_v6( + sockfd: BorrowedFd<'_>, + addr: &SocketAddrV6, + iov: &[IoSlice<'_>], + control: &mut SendAncillaryBuffer<'_, '_, '_>, + msg_flags: SendFlags, +) -> io::Result<usize> { + with_v6_msghdr(addr, iov, control, |msghdr| unsafe { + ret_send_recv(c::sendmsg( + borrowed_fd(sockfd), + &msghdr, + bitflags_bits!(msg_flags), + )) + }) +} + +#[cfg(all( + unix, + not(any(target_os = "espidf", target_os = "redox", target_os = "vita")) +))] +pub(crate) fn sendmsg_unix( + sockfd: BorrowedFd<'_>, + addr: &SocketAddrUnix, + iov: &[IoSlice<'_>], + control: &mut SendAncillaryBuffer<'_, '_, '_>, + msg_flags: SendFlags, +) -> io::Result<usize> { + super::msghdr::with_unix_msghdr(addr, iov, control, |msghdr| unsafe { + ret_send_recv(c::sendmsg( + borrowed_fd(sockfd), + &msghdr, + bitflags_bits!(msg_flags), + )) + }) +} + +#[cfg(not(any( + apple, + windows, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "redox", + target_os = "nto", + target_os = "vita", + target_os = "wasi", +)))] +pub(crate) fn accept_with(sockfd: BorrowedFd<'_>, flags: SocketFlags) -> io::Result<OwnedFd> { + unsafe { + let owned_fd = ret_owned_fd(c::accept4( + borrowed_fd(sockfd), + null_mut(), + null_mut(), + flags.bits() as c::c_int, + ))?; + Ok(owned_fd) + } +} + +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] +pub(crate) fn acceptfrom(sockfd: BorrowedFd<'_>) -> io::Result<(OwnedFd, Option<SocketAddrAny>)> { + unsafe { + let mut storage = MaybeUninit::<c::sockaddr_storage>::uninit(); + let mut len = size_of::<c::sockaddr_storage>() as c::socklen_t; + let owned_fd = ret_owned_fd(c::accept( + borrowed_fd(sockfd), + storage.as_mut_ptr().cast(), + &mut len, + ))?; + Ok(( + owned_fd, + maybe_read_sockaddr_os(storage.as_ptr(), len.try_into().unwrap()), + )) + } +} + +#[cfg(not(any( + apple, + windows, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "nto", + target_os = "redox", + target_os = "vita", + target_os = "wasi", +)))] +pub(crate) fn acceptfrom_with( + sockfd: BorrowedFd<'_>, + flags: SocketFlags, +) -> io::Result<(OwnedFd, Option<SocketAddrAny>)> { + unsafe { + let mut storage = MaybeUninit::<c::sockaddr_storage>::uninit(); + let mut len = size_of::<c::sockaddr_storage>() as c::socklen_t; + let owned_fd = ret_owned_fd(c::accept4( + borrowed_fd(sockfd), + storage.as_mut_ptr().cast(), + &mut len, + flags.bits() as c::c_int, + ))?; + Ok(( + owned_fd, + maybe_read_sockaddr_os(storage.as_ptr(), len.try_into().unwrap()), + )) + } +} + +/// Darwin lacks `accept4`, but does have `accept`. We define +/// `SocketFlags` to have no flags, so we can discard it here. +#[cfg(any( + apple, + windows, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "nto", + target_os = "vita", +))] +pub(crate) fn accept_with(sockfd: BorrowedFd<'_>, _flags: SocketFlags) -> io::Result<OwnedFd> { + accept(sockfd) +} + +/// Darwin lacks `accept4`, but does have `accept`. We define +/// `SocketFlags` to have no flags, so we can discard it here. +#[cfg(any( + apple, + windows, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "nto", + target_os = "vita", +))] +pub(crate) fn acceptfrom_with( + sockfd: BorrowedFd<'_>, + _flags: SocketFlags, +) -> io::Result<(OwnedFd, Option<SocketAddrAny>)> { + acceptfrom(sockfd) +} + +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] +pub(crate) fn shutdown(sockfd: BorrowedFd<'_>, how: Shutdown) -> io::Result<()> { + unsafe { ret(c::shutdown(borrowed_fd(sockfd), how as c::c_int)) } +} + +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] +pub(crate) fn getsockname(sockfd: BorrowedFd<'_>) -> io::Result<SocketAddrAny> { + unsafe { + let mut storage = MaybeUninit::<c::sockaddr_storage>::uninit(); + let mut len = size_of::<c::sockaddr_storage>() as c::socklen_t; + ret(c::getsockname( + borrowed_fd(sockfd), + storage.as_mut_ptr().cast(), + &mut len, + ))?; + Ok(read_sockaddr_os(storage.as_ptr(), len.try_into().unwrap())) + } +} + +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] +pub(crate) fn getpeername(sockfd: BorrowedFd<'_>) -> io::Result<Option<SocketAddrAny>> { + unsafe { + let mut storage = MaybeUninit::<c::sockaddr_storage>::uninit(); + let mut len = size_of::<c::sockaddr_storage>() as c::socklen_t; + ret(c::getpeername( + borrowed_fd(sockfd), + storage.as_mut_ptr().cast(), + &mut len, + ))?; + Ok(maybe_read_sockaddr_os( + storage.as_ptr(), + len.try_into().unwrap(), + )) + } +} + +#[cfg(not(any(windows, target_os = "redox", target_os = "wasi")))] +pub(crate) fn socketpair( + domain: AddressFamily, + type_: SocketType, + flags: SocketFlags, + protocol: Option<Protocol>, +) -> io::Result<(OwnedFd, OwnedFd)> { + let raw_protocol = match protocol { + Some(p) => p.0.get(), + None => 0, + }; + unsafe { + let mut fds = MaybeUninit::<[OwnedFd; 2]>::uninit(); + ret(c::socketpair( + c::c_int::from(domain.0), + (type_.0 | flags.bits()) as c::c_int, + raw_protocol as c::c_int, + fds.as_mut_ptr().cast::<c::c_int>(), + ))?; + + let [fd0, fd1] = fds.assume_init(); + Ok((fd0, fd1)) + } +} diff --git a/vendor/rustix/src/backend/libc/net/write_sockaddr.rs b/vendor/rustix/src/backend/libc/net/write_sockaddr.rs new file mode 100644 index 0000000..2eee98c --- /dev/null +++ b/vendor/rustix/src/backend/libc/net/write_sockaddr.rs @@ -0,0 +1,103 @@ +//! The BSD sockets API requires us to read the `ss_family` field before we can +//! interpret the rest of a `sockaddr` produced by the kernel. + +use super::addr::SocketAddrStorage; +#[cfg(unix)] +use super::addr::SocketAddrUnix; +use super::ext::{in6_addr_new, in_addr_new, sockaddr_in6_new}; +use crate::backend::c; +use crate::net::{SocketAddrAny, SocketAddrV4, SocketAddrV6}; +use core::mem::size_of; + +pub(crate) unsafe fn write_sockaddr( + addr: &SocketAddrAny, + storage: *mut SocketAddrStorage, +) -> usize { + match addr { + SocketAddrAny::V4(v4) => write_sockaddr_v4(v4, storage), + SocketAddrAny::V6(v6) => write_sockaddr_v6(v6, storage), + #[cfg(unix)] + SocketAddrAny::Unix(unix) => write_sockaddr_unix(unix, storage), + } +} + +pub(crate) fn encode_sockaddr_v4(v4: &SocketAddrV4) -> c::sockaddr_in { + c::sockaddr_in { + #[cfg(any( + bsd, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "nto", + target_os = "vita", + ))] + sin_len: size_of::<c::sockaddr_in>() as _, + sin_family: c::AF_INET as _, + sin_port: u16::to_be(v4.port()), + sin_addr: in_addr_new(u32::from_ne_bytes(v4.ip().octets())), + #[cfg(not(any(target_os = "haiku", target_os = "vita")))] + sin_zero: [0; 8_usize], + #[cfg(target_os = "haiku")] + sin_zero: [0; 24_usize], + #[cfg(target_os = "vita")] + sin_zero: [0; 6_usize], + #[cfg(target_os = "vita")] + sin_vport: 0, + } +} + +unsafe fn write_sockaddr_v4(v4: &SocketAddrV4, storage: *mut SocketAddrStorage) -> usize { + let encoded = encode_sockaddr_v4(v4); + core::ptr::write(storage.cast(), encoded); + size_of::<c::sockaddr_in>() +} + +pub(crate) fn encode_sockaddr_v6(v6: &SocketAddrV6) -> c::sockaddr_in6 { + #[cfg(any( + bsd, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "nto", + target_os = "vita" + ))] + { + sockaddr_in6_new( + size_of::<c::sockaddr_in6>() as _, + c::AF_INET6 as _, + u16::to_be(v6.port()), + u32::to_be(v6.flowinfo()), + in6_addr_new(v6.ip().octets()), + v6.scope_id(), + ) + } + #[cfg(not(any( + bsd, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "nto", + target_os = "vita" + )))] + { + sockaddr_in6_new( + c::AF_INET6 as _, + u16::to_be(v6.port()), + u32::to_be(v6.flowinfo()), + in6_addr_new(v6.ip().octets()), + v6.scope_id(), + ) + } +} + +unsafe fn write_sockaddr_v6(v6: &SocketAddrV6, storage: *mut SocketAddrStorage) -> usize { + let encoded = encode_sockaddr_v6(v6); + core::ptr::write(storage.cast(), encoded); + size_of::<c::sockaddr_in6>() +} + +#[cfg(unix)] +unsafe fn write_sockaddr_unix(unix: &SocketAddrUnix, storage: *mut SocketAddrStorage) -> usize { + core::ptr::write(storage.cast(), unix.unix); + unix.len() +} diff --git a/vendor/rustix/src/backend/libc/param/auxv.rs b/vendor/rustix/src/backend/libc/param/auxv.rs new file mode 100644 index 0000000..880a1d4 --- /dev/null +++ b/vendor/rustix/src/backend/libc/param/auxv.rs @@ -0,0 +1,54 @@ +use crate::backend::c; +#[cfg(any( + all(target_os = "android", target_pointer_width = "64"), + target_os = "linux", +))] +use crate::ffi::CStr; + +// `getauxval` wasn't supported in glibc until 2.16. +#[cfg(any( + all(target_os = "android", target_pointer_width = "64"), + target_os = "linux", +))] +weak!(fn getauxval(c::c_ulong) -> *mut c::c_void); + +#[inline] +pub(crate) fn page_size() -> usize { + unsafe { c::sysconf(c::_SC_PAGESIZE) as usize } +} + +#[cfg(not(any(target_os = "vita", target_os = "wasi")))] +#[inline] +pub(crate) fn clock_ticks_per_second() -> u64 { + unsafe { c::sysconf(c::_SC_CLK_TCK) as u64 } +} + +#[cfg(any( + all(target_os = "android", target_pointer_width = "64"), + target_os = "linux", +))] +#[inline] +pub(crate) fn linux_hwcap() -> (usize, usize) { + if let Some(libc_getauxval) = getauxval.get() { + unsafe { + let hwcap = libc_getauxval(c::AT_HWCAP) as usize; + let hwcap2 = libc_getauxval(c::AT_HWCAP2) as usize; + (hwcap, hwcap2) + } + } else { + (0, 0) + } +} + +#[cfg(any( + all(target_os = "android", target_pointer_width = "64"), + target_os = "linux", +))] +#[inline] +pub(crate) fn linux_execfn() -> &'static CStr { + if let Some(libc_getauxval) = getauxval.get() { + unsafe { CStr::from_ptr(libc_getauxval(c::AT_EXECFN).cast()) } + } else { + cstr!("") + } +} diff --git a/vendor/rustix/src/backend/libc/param/mod.rs b/vendor/rustix/src/backend/libc/param/mod.rs new file mode 100644 index 0000000..2cb2fe7 --- /dev/null +++ b/vendor/rustix/src/backend/libc/param/mod.rs @@ -0,0 +1 @@ +pub(crate) mod auxv; diff --git a/vendor/rustix/src/backend/libc/pid/mod.rs b/vendor/rustix/src/backend/libc/pid/mod.rs new file mode 100644 index 0000000..ef944f0 --- /dev/null +++ b/vendor/rustix/src/backend/libc/pid/mod.rs @@ -0,0 +1 @@ +pub(crate) mod syscalls; diff --git a/vendor/rustix/src/backend/libc/pid/syscalls.rs b/vendor/rustix/src/backend/libc/pid/syscalls.rs new file mode 100644 index 0000000..d0ed4bc --- /dev/null +++ b/vendor/rustix/src/backend/libc/pid/syscalls.rs @@ -0,0 +1,14 @@ +//! libc syscalls for PIDs + +use crate::backend::c; +use crate::pid::Pid; + +#[cfg(not(target_os = "wasi"))] +#[inline] +#[must_use] +pub(crate) fn getpid() -> Pid { + unsafe { + let pid = c::getpid(); + Pid::from_raw_unchecked(pid) + } +} diff --git a/vendor/rustix/src/backend/libc/pipe/mod.rs b/vendor/rustix/src/backend/libc/pipe/mod.rs new file mode 100644 index 0000000..1e0181a --- /dev/null +++ b/vendor/rustix/src/backend/libc/pipe/mod.rs @@ -0,0 +1,2 @@ +pub(crate) mod syscalls; +pub(crate) mod types; diff --git a/vendor/rustix/src/backend/libc/pipe/syscalls.rs b/vendor/rustix/src/backend/libc/pipe/syscalls.rs new file mode 100644 index 0000000..cff932d --- /dev/null +++ b/vendor/rustix/src/backend/libc/pipe/syscalls.rs @@ -0,0 +1,125 @@ +use crate::backend::c; +use crate::backend::conv::ret; +use crate::fd::OwnedFd; +use crate::io; +#[cfg(not(any( + apple, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "nto", + target_os = "wasi" +)))] +use crate::pipe::PipeFlags; +use core::mem::MaybeUninit; +#[cfg(linux_kernel)] +use { + crate::backend::conv::{borrowed_fd, ret_c_int, ret_usize}, + crate::backend::MAX_IOV, + crate::fd::BorrowedFd, + crate::pipe::{IoSliceRaw, SpliceFlags}, + crate::utils::option_as_mut_ptr, + core::cmp::min, +}; + +#[cfg(not(target_os = "wasi"))] +pub(crate) fn pipe() -> io::Result<(OwnedFd, OwnedFd)> { + unsafe { + let mut result = MaybeUninit::<[OwnedFd; 2]>::uninit(); + ret(c::pipe(result.as_mut_ptr().cast::<i32>()))?; + let [p0, p1] = result.assume_init(); + Ok((p0, p1)) + } +} + +#[cfg(not(any( + apple, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "nto", + target_os = "wasi" +)))] +pub(crate) fn pipe_with(flags: PipeFlags) -> io::Result<(OwnedFd, OwnedFd)> { + unsafe { + let mut result = MaybeUninit::<[OwnedFd; 2]>::uninit(); + ret(c::pipe2( + result.as_mut_ptr().cast::<i32>(), + bitflags_bits!(flags), + ))?; + let [p0, p1] = result.assume_init(); + Ok((p0, p1)) + } +} + +#[cfg(linux_kernel)] +#[inline] +pub fn splice( + fd_in: BorrowedFd<'_>, + off_in: Option<&mut u64>, + fd_out: BorrowedFd<'_>, + off_out: Option<&mut u64>, + len: usize, + flags: SpliceFlags, +) -> io::Result<usize> { + let off_in = option_as_mut_ptr(off_in).cast(); + let off_out = option_as_mut_ptr(off_out).cast(); + + unsafe { + ret_usize(c::splice( + borrowed_fd(fd_in), + off_in, + borrowed_fd(fd_out), + off_out, + len, + flags.bits(), + )) + } +} + +#[cfg(linux_kernel)] +#[inline] +pub unsafe fn vmsplice( + fd: BorrowedFd<'_>, + bufs: &[IoSliceRaw<'_>], + flags: SpliceFlags, +) -> io::Result<usize> { + ret_usize(c::vmsplice( + borrowed_fd(fd), + bufs.as_ptr().cast::<c::iovec>(), + min(bufs.len(), MAX_IOV), + flags.bits(), + )) +} + +#[cfg(linux_kernel)] +#[inline] +pub fn tee( + fd_in: BorrowedFd<'_>, + fd_out: BorrowedFd<'_>, + len: usize, + flags: SpliceFlags, +) -> io::Result<usize> { + unsafe { + ret_usize(c::tee( + borrowed_fd(fd_in), + borrowed_fd(fd_out), + len, + flags.bits(), + )) + } +} + +#[cfg(linux_kernel)] +#[inline] +pub(crate) fn fcntl_getpipe_sz(fd: BorrowedFd<'_>) -> io::Result<usize> { + unsafe { ret_c_int(c::fcntl(borrowed_fd(fd), c::F_GETPIPE_SZ)).map(|size| size as usize) } +} + +#[cfg(linux_kernel)] +#[inline] +pub(crate) fn fcntl_setpipe_sz(fd: BorrowedFd<'_>, size: usize) -> io::Result<()> { + let size: c::c_int = size.try_into().map_err(|_| io::Errno::PERM)?; + + unsafe { ret(c::fcntl(borrowed_fd(fd), c::F_SETPIPE_SZ, size)) } +} diff --git a/vendor/rustix/src/backend/libc/pipe/types.rs b/vendor/rustix/src/backend/libc/pipe/types.rs new file mode 100644 index 0000000..78fc2fc --- /dev/null +++ b/vendor/rustix/src/backend/libc/pipe/types.rs @@ -0,0 +1,103 @@ +#[cfg(linux_kernel)] +use core::marker::PhantomData; +#[cfg(not(any(apple, target_os = "wasi")))] +use {crate::backend::c, bitflags::bitflags}; + +#[cfg(not(any(apple, target_os = "wasi")))] +bitflags! { + /// `O_*` constants for use with [`pipe_with`]. + /// + /// [`pipe_with`]: crate::pipe::pipe_with + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct PipeFlags: u32 { + /// `O_CLOEXEC` + const CLOEXEC = bitcast!(c::O_CLOEXEC); + /// `O_DIRECT` + #[cfg(not(any( + solarish, + target_os = "espidf", + target_os = "haiku", + target_os = "nto", + target_os = "openbsd", + target_os = "redox", + target_os = "vita", + )))] + const DIRECT = bitcast!(c::O_DIRECT); + /// `O_NONBLOCK` + const NONBLOCK = bitcast!(c::O_NONBLOCK); + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} + +#[cfg(linux_kernel)] +bitflags! { + /// `SPLICE_F_*` constants for use with [`splice`], [`vmsplice`], and + /// [`tee`]. + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct SpliceFlags: c::c_uint { + /// `SPLICE_F_MOVE` + const MOVE = c::SPLICE_F_MOVE; + /// `SPLICE_F_NONBLOCK` + const NONBLOCK = c::SPLICE_F_NONBLOCK; + /// `SPLICE_F_MORE` + const MORE = c::SPLICE_F_MORE; + /// `SPLICE_F_GIFT` + const GIFT = c::SPLICE_F_GIFT; + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} + +/// A buffer type for use with [`vmsplice`]. +/// +/// It is guaranteed to be ABI compatible with the iovec type on Unix platforms +/// and `WSABUF` on Windows. Unlike `IoSlice` and `IoSliceMut` it is +/// semantically like a raw pointer, and therefore can be shared or mutated as +/// needed. +/// +/// [`vmsplice`]: crate::pipe::vmsplice +#[cfg(linux_kernel)] +#[repr(transparent)] +pub struct IoSliceRaw<'a> { + _buf: c::iovec, + _lifetime: PhantomData<&'a ()>, +} + +#[cfg(linux_kernel)] +impl<'a> IoSliceRaw<'a> { + /// Creates a new `IoSlice` wrapping a byte slice. + pub fn from_slice(buf: &'a [u8]) -> Self { + IoSliceRaw { + _buf: c::iovec { + iov_base: buf.as_ptr() as *mut u8 as *mut c::c_void, + iov_len: buf.len() as _, + }, + _lifetime: PhantomData, + } + } + + /// Creates a new `IoSlice` wrapping a mutable byte slice. + pub fn from_slice_mut(buf: &'a mut [u8]) -> Self { + IoSliceRaw { + _buf: c::iovec { + iov_base: buf.as_mut_ptr() as *mut c::c_void, + iov_len: buf.len() as _, + }, + _lifetime: PhantomData, + } + } +} + +#[cfg(not(any(apple, target_os = "wasi")))] +#[test] +fn test_types() { + assert_eq_size!(PipeFlags, c::c_int); + + #[cfg(linux_kernel)] + assert_eq_size!(SpliceFlags, c::c_int); +} diff --git a/vendor/rustix/src/backend/libc/prctl/mod.rs b/vendor/rustix/src/backend/libc/prctl/mod.rs new file mode 100644 index 0000000..ef944f0 --- /dev/null +++ b/vendor/rustix/src/backend/libc/prctl/mod.rs @@ -0,0 +1 @@ +pub(crate) mod syscalls; diff --git a/vendor/rustix/src/backend/libc/prctl/syscalls.rs b/vendor/rustix/src/backend/libc/prctl/syscalls.rs new file mode 100644 index 0000000..451cecc --- /dev/null +++ b/vendor/rustix/src/backend/libc/prctl/syscalls.rs @@ -0,0 +1,14 @@ +use crate::backend::c; +use crate::backend::conv::ret_c_int; +use crate::io; + +#[inline] +pub(crate) unsafe fn prctl( + option: c::c_int, + arg2: *mut c::c_void, + arg3: *mut c::c_void, + arg4: *mut c::c_void, + arg5: *mut c::c_void, +) -> io::Result<c::c_int> { + ret_c_int(c::prctl(option, arg2, arg3, arg4, arg5)) +} diff --git a/vendor/rustix/src/backend/libc/process/cpu_set.rs b/vendor/rustix/src/backend/libc/process/cpu_set.rs new file mode 100644 index 0000000..4cf06b9 --- /dev/null +++ b/vendor/rustix/src/backend/libc/process/cpu_set.rs @@ -0,0 +1,50 @@ +//! Rust implementation of the `CPU_*` macro API. + +#![allow(non_snake_case)] + +use super::types::{RawCpuSet, CPU_SETSIZE}; +use crate::backend::c; + +#[inline] +pub(crate) fn CPU_SET(cpu: usize, cpuset: &mut RawCpuSet) { + assert!( + cpu < CPU_SETSIZE, + "cpu out of bounds: the cpu max is {} but the cpu is {}", + CPU_SETSIZE, + cpu + ); + unsafe { c::CPU_SET(cpu, cpuset) } +} + +#[inline] +pub(crate) fn CPU_ZERO(cpuset: &mut RawCpuSet) { + unsafe { c::CPU_ZERO(cpuset) } +} + +#[inline] +pub(crate) fn CPU_CLR(cpu: usize, cpuset: &mut RawCpuSet) { + assert!( + cpu < CPU_SETSIZE, + "cpu out of bounds: the cpu max is {} but the cpu is {}", + CPU_SETSIZE, + cpu + ); + unsafe { c::CPU_CLR(cpu, cpuset) } +} + +#[inline] +pub(crate) fn CPU_ISSET(cpu: usize, cpuset: &RawCpuSet) -> bool { + assert!( + cpu < CPU_SETSIZE, + "cpu out of bounds: the cpu max is {} but the cpu is {}", + CPU_SETSIZE, + cpu + ); + unsafe { c::CPU_ISSET(cpu, cpuset) } +} + +#[cfg(linux_kernel)] +#[inline] +pub(crate) fn CPU_COUNT(cpuset: &RawCpuSet) -> u32 { + unsafe { c::CPU_COUNT(cpuset).try_into().unwrap() } +} diff --git a/vendor/rustix/src/backend/libc/process/mod.rs b/vendor/rustix/src/backend/libc/process/mod.rs new file mode 100644 index 0000000..39803b5 --- /dev/null +++ b/vendor/rustix/src/backend/libc/process/mod.rs @@ -0,0 +1,7 @@ +#[cfg(any(freebsdlike, linux_kernel, target_os = "fuchsia"))] +pub(crate) mod cpu_set; +#[cfg(not(windows))] +pub(crate) mod syscalls; +pub(crate) mod types; +#[cfg(not(any(target_os = "espidf", target_os = "vita", target_os = "wasi")))] +pub(crate) mod wait; diff --git a/vendor/rustix/src/backend/libc/process/syscalls.rs b/vendor/rustix/src/backend/libc/process/syscalls.rs new file mode 100644 index 0000000..46fd182 --- /dev/null +++ b/vendor/rustix/src/backend/libc/process/syscalls.rs @@ -0,0 +1,713 @@ +//! libc syscalls supporting `rustix::process`. + +#[cfg(any(freebsdlike, linux_kernel, target_os = "fuchsia"))] +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")] +use crate::backend::conv::c_str; +#[cfg(all(feature = "alloc", feature = "fs", not(target_os = "wasi")))] +use crate::backend::conv::ret_discarded_char_ptr; +#[cfg(not(any( + target_os = "espidf", + target_os = "fuchsia", + target_os = "redox", + target_os = "vita", + target_os = "wasi" +)))] +use crate::backend::conv::ret_infallible; +#[cfg(not(target_os = "wasi"))] +use crate::backend::conv::ret_pid_t; +#[cfg(linux_kernel)] +use crate::backend::conv::ret_u32; +#[cfg(all(feature = "alloc", not(target_os = "wasi")))] +use crate::backend::conv::ret_usize; +use crate::backend::conv::{ret, ret_c_int}; +#[cfg(not(any(target_os = "wasi", target_os = "fuchsia")))] +use crate::fd::BorrowedFd; +#[cfg(target_os = "linux")] +use crate::fd::{AsRawFd, OwnedFd, RawFd}; +#[cfg(feature = "fs")] +use crate::ffi::CStr; +#[cfg(feature = "fs")] +use crate::fs::Mode; +use crate::io; +#[cfg(all(feature = "alloc", not(target_os = "wasi")))] +use crate::process::Gid; +#[cfg(not(target_os = "wasi"))] +use crate::process::Pid; +#[cfg(not(any(target_os = "espidf", target_os = "wasi")))] +use crate::process::Signal; +#[cfg(not(any( + target_os = "espidf", + target_os = "fuchsia", + target_os = "vita", + target_os = "wasi" +)))] +use crate::process::Uid; +#[cfg(linux_kernel)] +use crate::process::{Cpuid, MembarrierCommand, MembarrierQuery}; +#[cfg(not(any(target_os = "espidf", target_os = "vita", target_os = "wasi")))] +use crate::process::{RawPid, WaitOptions, WaitStatus}; +#[cfg(not(any( + target_os = "espidf", + target_os = "fuchsia", + target_os = "redox", + target_os = "vita", + target_os = "wasi" +)))] +use crate::process::{Resource, Rlimit}; +#[cfg(not(any( + target_os = "espidf", + target_os = "redox", + target_os = "openbsd", + target_os = "vita", + target_os = "wasi" +)))] +use crate::process::{WaitId, WaitidOptions, WaitidStatus}; +use core::mem::MaybeUninit; +#[cfg(target_os = "linux")] +use { + super::super::conv::ret_owned_fd, crate::process::PidfdFlags, crate::process::PidfdGetfdFlags, +}; + +#[cfg(any(linux_kernel, target_os = "dragonfly"))] +#[inline] +pub(crate) fn sched_getcpu() -> usize { + let r = unsafe { libc::sched_getcpu() }; + debug_assert!(r >= 0); + r as usize +} + +#[cfg(feature = "fs")] +#[cfg(not(target_os = "wasi"))] +pub(crate) fn chdir(path: &CStr) -> io::Result<()> { + unsafe { ret(c::chdir(c_str(path))) } +} + +#[cfg(not(any(target_os = "fuchsia", target_os = "wasi")))] +pub(crate) fn fchdir(dirfd: BorrowedFd<'_>) -> io::Result<()> { + unsafe { ret(c::fchdir(borrowed_fd(dirfd))) } +} + +#[cfg(feature = "fs")] +#[cfg(not(any(target_os = "fuchsia", target_os = "wasi")))] +pub(crate) fn chroot(path: &CStr) -> io::Result<()> { + unsafe { ret(c::chroot(c_str(path))) } +} + +#[cfg(all(feature = "alloc", feature = "fs"))] +#[cfg(not(target_os = "wasi"))] +pub(crate) fn getcwd(buf: &mut [MaybeUninit<u8>]) -> io::Result<()> { + unsafe { ret_discarded_char_ptr(c::getcwd(buf.as_mut_ptr().cast(), buf.len())) } +} + +// The `membarrier` syscall has a third argument, but it's only used when +// the `flags` argument is `MEMBARRIER_CMD_FLAG_CPU`. +#[cfg(linux_kernel)] +syscall! { + fn membarrier_all( + cmd: c::c_int, + flags: c::c_uint + ) via SYS_membarrier -> c::c_int +} + +#[cfg(linux_kernel)] +pub(crate) fn membarrier_query() -> MembarrierQuery { + // glibc does not have a wrapper for `membarrier`; [the documentation] + // says to use `syscall`. + // + // [the documentation]: https://man7.org/linux/man-pages/man2/membarrier.2.html#NOTES + const MEMBARRIER_CMD_QUERY: u32 = 0; + unsafe { + match ret_u32(membarrier_all(MEMBARRIER_CMD_QUERY as i32, 0)) { + Ok(query) => MembarrierQuery::from_bits_retain(query), + Err(_) => MembarrierQuery::empty(), + } + } +} + +#[cfg(linux_kernel)] +pub(crate) fn membarrier(cmd: MembarrierCommand) -> io::Result<()> { + unsafe { ret(membarrier_all(cmd as i32, 0)) } +} + +#[cfg(linux_kernel)] +pub(crate) fn membarrier_cpu(cmd: MembarrierCommand, cpu: Cpuid) -> io::Result<()> { + const MEMBARRIER_CMD_FLAG_CPU: u32 = 1; + + syscall! { + fn membarrier_cpu( + cmd: c::c_int, + flags: c::c_uint, + cpu_id: c::c_int + ) via SYS_membarrier -> c::c_int + } + + unsafe { + ret(membarrier_cpu( + cmd as i32, + MEMBARRIER_CMD_FLAG_CPU, + bitcast!(cpu.as_raw()), + )) + } +} + +#[cfg(not(target_os = "wasi"))] +#[inline] +#[must_use] +pub(crate) fn getppid() -> Option<Pid> { + unsafe { + let pid: i32 = c::getppid(); + Pid::from_raw(pid) + } +} + +#[cfg(not(target_os = "wasi"))] +#[inline] +pub(crate) fn getpgid(pid: Option<Pid>) -> io::Result<Pid> { + unsafe { + let pgid = ret_pid_t(c::getpgid(Pid::as_raw(pid) as _))?; + Ok(Pid::from_raw_unchecked(pgid)) + } +} + +#[cfg(not(target_os = "wasi"))] +#[inline] +pub(crate) fn setpgid(pid: Option<Pid>, pgid: Option<Pid>) -> io::Result<()> { + unsafe { ret(c::setpgid(Pid::as_raw(pid) as _, Pid::as_raw(pgid) as _)) } +} + +#[cfg(not(target_os = "wasi"))] +#[inline] +#[must_use] +pub(crate) fn getpgrp() -> Pid { + unsafe { + let pgid = c::getpgrp(); + Pid::from_raw_unchecked(pgid) + } +} + +#[cfg(any(freebsdlike, linux_kernel, target_os = "fuchsia"))] +#[inline] +pub(crate) fn sched_getaffinity(pid: Option<Pid>, cpuset: &mut RawCpuSet) -> io::Result<()> { + unsafe { + ret(c::sched_getaffinity( + Pid::as_raw(pid) as _, + core::mem::size_of::<RawCpuSet>(), + cpuset, + )) + } +} + +#[cfg(any(freebsdlike, linux_kernel, target_os = "fuchsia"))] +#[inline] +pub(crate) fn sched_setaffinity(pid: Option<Pid>, cpuset: &RawCpuSet) -> io::Result<()> { + unsafe { + ret(c::sched_setaffinity( + Pid::as_raw(pid) as _, + core::mem::size_of::<RawCpuSet>(), + cpuset, + )) + } +} + +#[inline] +pub(crate) fn sched_yield() { + unsafe { + let _ = c::sched_yield(); + } +} + +#[cfg(not(target_os = "wasi"))] +#[cfg(feature = "fs")] +#[inline] +pub(crate) fn umask(mask: Mode) -> Mode { + unsafe { Mode::from_bits_retain(c::umask(mask.bits() as c::mode_t).into()) } +} + +#[cfg(not(any(target_os = "fuchsia", target_os = "vita", target_os = "wasi")))] +#[inline] +pub(crate) fn nice(inc: i32) -> io::Result<i32> { + libc_errno::set_errno(libc_errno::Errno(0)); + let r = unsafe { c::nice(inc) }; + if libc_errno::errno().0 != 0 { + ret_c_int(r) + } else { + Ok(r) + } +} + +#[cfg(not(any( + target_os = "espidf", + target_os = "fuchsia", + target_os = "vita", + target_os = "wasi" +)))] +#[inline] +pub(crate) fn getpriority_user(uid: Uid) -> io::Result<i32> { + libc_errno::set_errno(libc_errno::Errno(0)); + let r = unsafe { c::getpriority(c::PRIO_USER, uid.as_raw() as _) }; + if libc_errno::errno().0 != 0 { + ret_c_int(r) + } else { + Ok(r) + } +} + +#[cfg(not(any( + target_os = "espidf", + target_os = "fuchsia", + target_os = "vita", + target_os = "wasi" +)))] +#[inline] +pub(crate) fn getpriority_pgrp(pgid: Option<Pid>) -> io::Result<i32> { + libc_errno::set_errno(libc_errno::Errno(0)); + let r = unsafe { c::getpriority(c::PRIO_PGRP, Pid::as_raw(pgid) as _) }; + if libc_errno::errno().0 != 0 { + ret_c_int(r) + } else { + Ok(r) + } +} + +#[cfg(not(any( + target_os = "espidf", + target_os = "fuchsia", + target_os = "vita", + target_os = "wasi" +)))] +#[inline] +pub(crate) fn getpriority_process(pid: Option<Pid>) -> io::Result<i32> { + libc_errno::set_errno(libc_errno::Errno(0)); + let r = unsafe { c::getpriority(c::PRIO_PROCESS, Pid::as_raw(pid) as _) }; + if libc_errno::errno().0 != 0 { + ret_c_int(r) + } else { + Ok(r) + } +} + +#[cfg(not(any( + target_os = "espidf", + target_os = "fuchsia", + target_os = "vita", + target_os = "wasi" +)))] +#[inline] +pub(crate) fn setpriority_user(uid: Uid, priority: i32) -> io::Result<()> { + unsafe { ret(c::setpriority(c::PRIO_USER, uid.as_raw() as _, priority)) } +} + +#[cfg(not(any( + target_os = "espidf", + target_os = "fuchsia", + target_os = "vita", + target_os = "wasi" +)))] +#[inline] +pub(crate) fn setpriority_pgrp(pgid: Option<Pid>, priority: i32) -> io::Result<()> { + unsafe { + ret(c::setpriority( + c::PRIO_PGRP, + Pid::as_raw(pgid) as _, + priority, + )) + } +} + +#[cfg(not(any( + target_os = "espidf", + target_os = "fuchsia", + target_os = "vita", + target_os = "wasi" +)))] +#[inline] +pub(crate) fn setpriority_process(pid: Option<Pid>, priority: i32) -> io::Result<()> { + unsafe { + ret(c::setpriority( + c::PRIO_PROCESS, + Pid::as_raw(pid) as _, + priority, + )) + } +} + +#[cfg(not(any( + target_os = "espidf", + target_os = "fuchsia", + target_os = "redox", + target_os = "vita", + target_os = "wasi" +)))] +#[inline] +pub(crate) fn getrlimit(limit: Resource) -> Rlimit { + let mut result = MaybeUninit::<c::rlimit>::uninit(); + unsafe { + ret_infallible(c::getrlimit(limit as _, result.as_mut_ptr())); + rlimit_from_libc(result.assume_init()) + } +} + +#[cfg(not(any( + target_os = "espidf", + target_os = "fuchsia", + target_os = "redox", + target_os = "vita", + target_os = "wasi" +)))] +#[inline] +pub(crate) fn setrlimit(limit: Resource, new: Rlimit) -> io::Result<()> { + let lim = rlimit_to_libc(new)?; + unsafe { ret(c::setrlimit(limit as _, &lim)) } +} + +#[cfg(linux_kernel)] +#[inline] +pub(crate) fn prlimit(pid: Option<Pid>, limit: Resource, new: Rlimit) -> io::Result<Rlimit> { + let lim = rlimit_to_libc(new)?; + let mut result = MaybeUninit::<c::rlimit>::uninit(); + unsafe { + ret(c::prlimit( + Pid::as_raw(pid), + limit as _, + &lim, + result.as_mut_ptr(), + ))?; + Ok(rlimit_from_libc(result.assume_init())) + } +} + +/// Convert a Rust [`Rlimit`] to a C `c::rlimit`. +#[cfg(not(any( + target_os = "espidf", + target_os = "fuchsia", + target_os = "redox", + target_os = "vita", + target_os = "wasi" +)))] +fn rlimit_from_libc(lim: c::rlimit) -> Rlimit { + let current = if lim.rlim_cur == c::RLIM_INFINITY { + None + } else { + Some(lim.rlim_cur.try_into().unwrap()) + }; + let maximum = if lim.rlim_max == c::RLIM_INFINITY { + None + } else { + Some(lim.rlim_max.try_into().unwrap()) + }; + Rlimit { current, maximum } +} + +/// Convert a C `c::rlimit` to a Rust `Rlimit`. +#[cfg(not(any( + target_os = "espidf", + target_os = "fuchsia", + target_os = "redox", + target_os = "vita", + target_os = "wasi" +)))] +fn rlimit_to_libc(lim: Rlimit) -> io::Result<c::rlimit> { + let Rlimit { current, maximum } = lim; + let rlim_cur = match current { + Some(r) => r.try_into().map_err(|_e| io::Errno::INVAL)?, + None => c::RLIM_INFINITY as _, + }; + let rlim_max = match maximum { + Some(r) => r.try_into().map_err(|_e| io::Errno::INVAL)?, + None => c::RLIM_INFINITY as _, + }; + Ok(c::rlimit { rlim_cur, rlim_max }) +} + +#[cfg(not(any(target_os = "espidf", target_os = "vita", target_os = "wasi")))] +#[inline] +pub(crate) fn wait(waitopts: WaitOptions) -> io::Result<Option<(Pid, WaitStatus)>> { + _waitpid(!0, waitopts) +} + +#[cfg(not(any(target_os = "espidf", target_os = "vita", target_os = "wasi")))] +#[inline] +pub(crate) fn waitpid( + pid: Option<Pid>, + waitopts: WaitOptions, +) -> io::Result<Option<(Pid, WaitStatus)>> { + _waitpid(Pid::as_raw(pid), waitopts) +} + +#[cfg(not(any(target_os = "espidf", target_os = "vita", target_os = "wasi")))] +#[inline] +pub(crate) fn waitpgid(pgid: Pid, waitopts: WaitOptions) -> io::Result<Option<(Pid, WaitStatus)>> { + _waitpid(-pgid.as_raw_nonzero().get(), waitopts) +} + +#[cfg(not(any(target_os = "espidf", target_os = "vita", target_os = "wasi")))] +#[inline] +pub(crate) fn _waitpid( + pid: RawPid, + waitopts: WaitOptions, +) -> io::Result<Option<(Pid, WaitStatus)>> { + unsafe { + let mut status: c::c_int = 0; + let pid = ret_c_int(c::waitpid(pid as _, &mut status, waitopts.bits() as _))?; + Ok(Pid::from_raw(pid).map(|pid| (pid, WaitStatus::new(status as _)))) + } +} + +#[cfg(not(any( + target_os = "espidf", + target_os = "redox", + target_os = "openbsd", + target_os = "vita", + target_os = "wasi" +)))] +#[inline] +pub(crate) fn waitid(id: WaitId<'_>, options: WaitidOptions) -> io::Result<Option<WaitidStatus>> { + // Get the id to wait on. + match id { + WaitId::All => _waitid_all(options), + WaitId::Pid(pid) => _waitid_pid(pid, options), + WaitId::Pgid(pgid) => _waitid_pgid(pgid, options), + #[cfg(target_os = "linux")] + WaitId::PidFd(fd) => _waitid_pidfd(fd, options), + #[cfg(not(target_os = "linux"))] + WaitId::__EatLifetime(_) => unreachable!(), + } +} + +#[cfg(not(any( + target_os = "espidf", + target_os = "redox", + target_os = "openbsd", + target_os = "vita", + target_os = "wasi" +)))] +#[inline] +fn _waitid_all(options: WaitidOptions) -> io::Result<Option<WaitidStatus>> { + // `waitid` can return successfully without initializing the struct (no + // children found when using `WNOHANG`) + let mut status = MaybeUninit::<c::siginfo_t>::zeroed(); + unsafe { + ret(c::waitid( + c::P_ALL, + 0, + status.as_mut_ptr(), + options.bits() as _, + ))? + }; + + Ok(unsafe { cvt_waitid_status(status) }) +} + +#[cfg(not(any( + target_os = "espidf", + target_os = "redox", + target_os = "openbsd", + target_os = "vita", + target_os = "wasi" +)))] +#[inline] +fn _waitid_pid(pid: Pid, options: WaitidOptions) -> io::Result<Option<WaitidStatus>> { + // `waitid` can return successfully without initializing the struct (no + // children found when using `WNOHANG`) + let mut status = MaybeUninit::<c::siginfo_t>::zeroed(); + unsafe { + ret(c::waitid( + c::P_PID, + Pid::as_raw(Some(pid)) as _, + status.as_mut_ptr(), + options.bits() as _, + ))? + }; + + Ok(unsafe { cvt_waitid_status(status) }) +} + +#[cfg(not(any( + target_os = "espidf", + target_os = "redox", + target_os = "openbsd", + target_os = "vita", + target_os = "wasi" +)))] +#[inline] +fn _waitid_pgid(pgid: Option<Pid>, options: WaitidOptions) -> io::Result<Option<WaitidStatus>> { + // `waitid` can return successfully without initializing the struct (no + // children found when using `WNOHANG`) + let mut status = MaybeUninit::<c::siginfo_t>::zeroed(); + unsafe { + ret(c::waitid( + c::P_PGID, + Pid::as_raw(pgid) as _, + status.as_mut_ptr(), + options.bits() as _, + ))? + }; + + Ok(unsafe { cvt_waitid_status(status) }) +} + +#[cfg(target_os = "linux")] +#[inline] +fn _waitid_pidfd(fd: BorrowedFd<'_>, options: WaitidOptions) -> io::Result<Option<WaitidStatus>> { + // `waitid` can return successfully without initializing the struct (no + // children found when using `WNOHANG`) + let mut status = MaybeUninit::<c::siginfo_t>::zeroed(); + unsafe { + ret(c::waitid( + c::P_PIDFD, + fd.as_raw_fd() as _, + status.as_mut_ptr(), + options.bits() as _, + ))? + }; + + Ok(unsafe { cvt_waitid_status(status) }) +} + +/// Convert a `siginfo_t` to a `WaitidStatus`. +/// +/// # Safety +/// +/// The caller must ensure that `status` is initialized and that `waitid` +/// returned successfully. +#[cfg(not(any( + target_os = "espidf", + target_os = "openbsd", + target_os = "redox", + target_os = "vita", + target_os = "wasi" +)))] +#[inline] +unsafe fn cvt_waitid_status(status: MaybeUninit<c::siginfo_t>) -> Option<WaitidStatus> { + 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”. + // 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 + // should set `SIGCHLD` here. + if status.si_signo == 0 { + None + } else { + Some(WaitidStatus(status)) + } +} + +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] +#[inline] +pub(crate) fn getsid(pid: Option<Pid>) -> io::Result<Pid> { + unsafe { + let pid = ret_pid_t(c::getsid(Pid::as_raw(pid) as _))?; + Ok(Pid::from_raw_unchecked(pid)) + } +} + +#[cfg(not(target_os = "wasi"))] +#[inline] +pub(crate) fn setsid() -> io::Result<Pid> { + unsafe { + let pid = ret_c_int(c::setsid())?; + Ok(Pid::from_raw_unchecked(pid)) + } +} + +#[cfg(not(any(target_os = "espidf", target_os = "wasi")))] +#[inline] +pub(crate) fn kill_process(pid: Pid, sig: Signal) -> io::Result<()> { + unsafe { ret(c::kill(pid.as_raw_nonzero().get(), sig as i32)) } +} + +#[cfg(not(any(target_os = "espidf", target_os = "wasi")))] +#[inline] +pub(crate) fn kill_process_group(pid: Pid, sig: Signal) -> io::Result<()> { + unsafe { + ret(c::kill( + pid.as_raw_nonzero().get().wrapping_neg(), + sig as i32, + )) + } +} + +#[cfg(not(any(target_os = "espidf", target_os = "wasi")))] +#[inline] +pub(crate) fn kill_current_process_group(sig: Signal) -> io::Result<()> { + unsafe { ret(c::kill(0, sig as i32)) } +} + +#[cfg(not(any(target_os = "espidf", target_os = "wasi")))] +pub(crate) fn test_kill_process(pid: Pid) -> io::Result<()> { + unsafe { ret(c::kill(pid.as_raw_nonzero().get(), 0)) } +} + +#[cfg(not(any(target_os = "espidf", target_os = "wasi")))] +#[inline] +pub(crate) fn test_kill_process_group(pid: Pid) -> io::Result<()> { + unsafe { ret(c::kill(pid.as_raw_nonzero().get().wrapping_neg(), 0)) } +} + +#[cfg(not(any(target_os = "espidf", target_os = "wasi")))] +#[inline] +pub(crate) fn test_kill_current_process_group() -> io::Result<()> { + unsafe { ret(c::kill(0, 0)) } +} + +#[cfg(freebsdlike)] +#[inline] +pub(crate) unsafe fn procctl( + idtype: c::idtype_t, + id: c::id_t, + option: c::c_int, + data: *mut c::c_void, +) -> io::Result<()> { + ret(c::procctl(idtype, id, option, data)) +} + +#[cfg(target_os = "linux")] +pub(crate) fn pidfd_open(pid: Pid, flags: PidfdFlags) -> io::Result<OwnedFd> { + syscall! { + fn pidfd_open( + pid: c::pid_t, + flags: c::c_uint + ) via SYS_pidfd_open -> c::c_int + } + unsafe { + ret_owned_fd(pidfd_open( + pid.as_raw_nonzero().get(), + bitflags_bits!(flags), + )) + } +} + +#[cfg(target_os = "linux")] +pub(crate) fn pidfd_getfd( + pidfd: BorrowedFd<'_>, + targetfd: RawFd, + flags: PidfdGetfdFlags, +) -> io::Result<OwnedFd> { + syscall! { + fn pidfd_getfd( + pidfd: c::c_int, + targetfd: c::c_int, + flags: c::c_uint + ) via SYS_pidfd_getfd -> c::c_int + } + unsafe { + ret_owned_fd(pidfd_getfd( + borrowed_fd(pidfd), + targetfd, + bitflags_bits!(flags), + )) + } +} + +#[cfg(all(feature = "alloc", not(target_os = "wasi")))] +pub(crate) fn getgroups(buf: &mut [Gid]) -> io::Result<usize> { + let len = buf.len().try_into().map_err(|_| io::Errno::NOMEM)?; + + unsafe { ret_usize(c::getgroups(len, buf.as_mut_ptr().cast()) as isize) } +} diff --git a/vendor/rustix/src/backend/libc/process/types.rs b/vendor/rustix/src/backend/libc/process/types.rs new file mode 100644 index 0000000..f914128 --- /dev/null +++ b/vendor/rustix/src/backend/libc/process/types.rs @@ -0,0 +1,172 @@ +#[cfg(not(any(target_os = "espidf", target_os = "vita")))] +use crate::backend::c; + +/// A command for use with [`membarrier`] and [`membarrier_cpu`]. +/// +/// For `MEMBARRIER_CMD_QUERY`, see [`membarrier_query`]. +/// +/// [`membarrier`]: crate::process::membarrier +/// [`membarrier_cpu`]: crate::process::membarrier_cpu +/// [`membarrier_query`]: crate::process::membarrier_query +#[cfg(linux_kernel)] +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +#[repr(u32)] +pub enum MembarrierCommand { + /// `MEMBARRIER_CMD_GLOBAL` + #[doc(alias = "Shared")] + #[doc(alias = "MEMBARRIER_CMD_SHARED")] + Global = c::MEMBARRIER_CMD_GLOBAL as u32, + /// `MEMBARRIER_CMD_GLOBAL_EXPEDITED` + GlobalExpedited = c::MEMBARRIER_CMD_GLOBAL_EXPEDITED as u32, + /// `MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED` + RegisterGlobalExpedited = c::MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED as u32, + /// `MEMBARRIER_CMD_PRIVATE_EXPEDITED` + PrivateExpedited = c::MEMBARRIER_CMD_PRIVATE_EXPEDITED as u32, + /// `MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED` + RegisterPrivateExpedited = c::MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED as u32, + /// `MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE` + PrivateExpeditedSyncCore = c::MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE as u32, + /// `MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE` + RegisterPrivateExpeditedSyncCore = + c::MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE as u32, + /// `MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ` (since Linux 5.10) + PrivateExpeditedRseq = c::MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ as u32, + /// `MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_RSEQ` (since Linux 5.10) + RegisterPrivateExpeditedRseq = c::MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_RSEQ as u32, +} + +/// A resource value for use with [`getrlimit`], [`setrlimit`], and +/// [`prlimit`]. +/// +/// [`getrlimit`]: crate::process::getrlimit +/// [`setrlimit`]: crate::process::setrlimit +/// [`prlimit`]: crate::process::prlimit +#[cfg(not(any( + target_os = "espidf", + target_os = "fuchsia", + target_os = "redox", + target_os = "vita", + target_os = "wasi" +)))] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[cfg_attr(not(target_os = "l4re"), repr(u32))] +#[cfg_attr(target_os = "l4re", repr(u64))] +pub enum Resource { + /// `RLIMIT_CPU` + Cpu = bitcast!(c::RLIMIT_CPU), + /// `RLIMIT_FSIZE` + Fsize = bitcast!(c::RLIMIT_FSIZE), + /// `RLIMIT_DATA` + Data = bitcast!(c::RLIMIT_DATA), + /// `RLIMIT_STACK` + Stack = bitcast!(c::RLIMIT_STACK), + /// `RLIMIT_CORE` + #[cfg(not(target_os = "haiku"))] + Core = bitcast!(c::RLIMIT_CORE), + /// `RLIMIT_RSS` + // "nto" has `RLIMIT_RSS`, but it has the same value as `RLIMIT_AS`. + #[cfg(not(any(apple, solarish, target_os = "nto", target_os = "haiku")))] + Rss = bitcast!(c::RLIMIT_RSS), + /// `RLIMIT_NPROC` + #[cfg(not(any(solarish, target_os = "haiku")))] + Nproc = bitcast!(c::RLIMIT_NPROC), + /// `RLIMIT_NOFILE` + Nofile = bitcast!(c::RLIMIT_NOFILE), + /// `RLIMIT_MEMLOCK` + #[cfg(not(any(solarish, target_os = "aix", target_os = "haiku")))] + Memlock = bitcast!(c::RLIMIT_MEMLOCK), + /// `RLIMIT_AS` + #[cfg(not(target_os = "openbsd"))] + As = bitcast!(c::RLIMIT_AS), + /// `RLIMIT_LOCKS` + #[cfg(not(any( + bsd, + solarish, + target_os = "aix", + target_os = "haiku", + target_os = "hurd", + target_os = "nto" + )))] + Locks = bitcast!(c::RLIMIT_LOCKS), + /// `RLIMIT_SIGPENDING` + #[cfg(not(any( + bsd, + solarish, + target_os = "aix", + target_os = "haiku", + target_os = "hurd", + target_os = "nto" + )))] + Sigpending = bitcast!(c::RLIMIT_SIGPENDING), + /// `RLIMIT_MSGQUEUE` + #[cfg(not(any( + bsd, + solarish, + target_os = "aix", + target_os = "haiku", + target_os = "hurd", + target_os = "nto" + )))] + Msgqueue = bitcast!(c::RLIMIT_MSGQUEUE), + /// `RLIMIT_NICE` + #[cfg(not(any( + bsd, + solarish, + target_os = "aix", + target_os = "haiku", + target_os = "hurd", + target_os = "nto" + )))] + Nice = bitcast!(c::RLIMIT_NICE), + /// `RLIMIT_RTPRIO` + #[cfg(not(any( + bsd, + solarish, + target_os = "aix", + target_os = "haiku", + target_os = "hurd", + target_os = "nto" + )))] + Rtprio = bitcast!(c::RLIMIT_RTPRIO), + /// `RLIMIT_RTTIME` + #[cfg(not(any( + bsd, + solarish, + target_os = "aix", + target_os = "android", + target_os = "emscripten", + target_os = "haiku", + target_os = "hurd", + target_os = "nto", + )))] + Rttime = bitcast!(c::RLIMIT_RTTIME), +} + +#[cfg(apple)] +#[allow(non_upper_case_globals)] +impl Resource { + /// `RLIMIT_RSS` + pub const Rss: Self = Self::As; +} + +/// A CPU identifier as a raw integer. +#[cfg(linux_kernel)] +pub type RawCpuid = u32; +#[cfg(freebsdlike)] +pub type RawId = c::id_t; + +#[cfg(any(linux_kernel, target_os = "fuchsia"))] +pub(crate) type RawCpuSet = c::cpu_set_t; +#[cfg(freebsdlike)] +pub(crate) type RawCpuSet = c::cpuset_t; + +#[cfg(any(freebsdlike, linux_kernel, target_os = "fuchsia"))] +#[inline] +pub(crate) fn raw_cpu_set_new() -> RawCpuSet { + let mut set = unsafe { core::mem::zeroed() }; + super::cpu_set::CPU_ZERO(&mut set); + set +} + +#[cfg(any(freebsdlike, linux_kernel, target_os = "fuchsia"))] +pub(crate) const CPU_SETSIZE: usize = c::CPU_SETSIZE as usize; diff --git a/vendor/rustix/src/backend/libc/process/wait.rs b/vendor/rustix/src/backend/libc/process/wait.rs new file mode 100644 index 0000000..9a932cf --- /dev/null +++ b/vendor/rustix/src/backend/libc/process/wait.rs @@ -0,0 +1,9 @@ +use crate::backend::c; + +pub(crate) use c::{ + WCONTINUED, WEXITSTATUS, WIFCONTINUED, WIFEXITED, WIFSIGNALED, WIFSTOPPED, WNOHANG, WSTOPSIG, + WTERMSIG, WUNTRACED, +}; + +#[cfg(not(any(target_os = "openbsd", target_os = "redox", target_os = "wasi")))] +pub(crate) use c::{WEXITED, WNOWAIT, WSTOPPED}; diff --git a/vendor/rustix/src/backend/libc/pty/mod.rs b/vendor/rustix/src/backend/libc/pty/mod.rs new file mode 100644 index 0000000..ef944f0 --- /dev/null +++ b/vendor/rustix/src/backend/libc/pty/mod.rs @@ -0,0 +1 @@ +pub(crate) mod syscalls; diff --git a/vendor/rustix/src/backend/libc/pty/syscalls.rs b/vendor/rustix/src/backend/libc/pty/syscalls.rs new file mode 100644 index 0000000..2395efd --- /dev/null +++ b/vendor/rustix/src/backend/libc/pty/syscalls.rs @@ -0,0 +1,106 @@ +//! libc syscalls supporting `rustix::pty`. + +use crate::backend::c; +use crate::backend::conv::{borrowed_fd, ret}; +use crate::fd::BorrowedFd; +use crate::io; +#[cfg(all( + feature = "alloc", + any(apple, linux_like, target_os = "freebsd", target_os = "fuchsia") +))] +use { + crate::ffi::{CStr, CString}, + crate::path::SMALL_PATH_BUFFER_SIZE, + alloc::borrow::ToOwned, + alloc::vec::Vec, +}; + +#[cfg(not(linux_kernel))] +use crate::{backend::conv::ret_owned_fd, fd::OwnedFd, pty::OpenptFlags}; + +#[cfg(not(linux_kernel))] +#[inline] +pub(crate) fn openpt(flags: OpenptFlags) -> io::Result<OwnedFd> { + unsafe { ret_owned_fd(c::posix_openpt(flags.bits() as _)) } +} + +#[cfg(all( + feature = "alloc", + any(apple, linux_like, target_os = "freebsd", target_os = "fuchsia") +))] +#[inline] +pub(crate) fn ptsname(fd: BorrowedFd<'_>, mut buffer: Vec<u8>) -> io::Result<CString> { + // This code would benefit from having a better way to read into + // uninitialized memory, but that requires `unsafe`. + buffer.clear(); + buffer.reserve(SMALL_PATH_BUFFER_SIZE); + buffer.resize(buffer.capacity(), 0_u8); + + loop { + // On platforms with `ptsname_r`, use it. + #[cfg(any(linux_like, target_os = "fuchsia"))] + let r = unsafe { c::ptsname_r(borrowed_fd(fd), buffer.as_mut_ptr().cast(), buffer.len()) }; + + // FreeBSD 12 doesn't have `ptsname_r`. + #[cfg(target_os = "freebsd")] + let r = unsafe { + weak! { + fn ptsname_r( + c::c_int, + *mut c::c_char, + c::size_t + ) -> c::c_int + } + if let Some(func) = ptsname_r.get() { + func(borrowed_fd(fd), buffer.as_mut_ptr().cast(), buffer.len()) + } else { + libc::ENOSYS + } + }; + + // macOS 10.13.4 has `ptsname_r`; use it if we have it, otherwise fall + // back to calling the underlying ioctl directly. + #[cfg(apple)] + let r = unsafe { + weak! { fn ptsname_r(c::c_int, *mut c::c_char, c::size_t) -> c::c_int } + + if let Some(libc_ptsname_r) = ptsname_r.get() { + libc_ptsname_r(borrowed_fd(fd), buffer.as_mut_ptr().cast(), buffer.len()) + } else { + // The size declared in the `TIOCPTYGNAME` macro in + // sys/ttycom.h is 128. + let mut name: [u8; 128] = [0_u8; 128]; + match c::ioctl(borrowed_fd(fd), c::TIOCPTYGNAME as _, &mut name) { + 0 => { + let len = CStr::from_ptr(name.as_ptr().cast()).to_bytes().len(); + std::ptr::copy_nonoverlapping(name.as_ptr(), buffer.as_mut_ptr(), len + 1); + 0 + } + _ => libc_errno::errno().0, + } + } + }; + + if r == 0 { + return Ok(unsafe { CStr::from_ptr(buffer.as_ptr().cast()).to_owned() }); + } + if r != c::ERANGE { + return Err(io::Errno::from_raw_os_error(r)); + } + + // Use `Vec` reallocation strategy to grow capacity exponentially. + buffer.reserve(1); + buffer.resize(buffer.capacity(), 0_u8); + } +} + +#[inline] +pub(crate) fn unlockpt(fd: BorrowedFd<'_>) -> io::Result<()> { + unsafe { ret(c::unlockpt(borrowed_fd(fd))) } +} + +#[cfg(not(linux_kernel))] +#[inline] +pub(crate) fn grantpt(fd: BorrowedFd<'_>) -> io::Result<()> { + unsafe { ret(c::grantpt(borrowed_fd(fd))) } +} diff --git a/vendor/rustix/src/backend/libc/rand/mod.rs b/vendor/rustix/src/backend/libc/rand/mod.rs new file mode 100644 index 0000000..1e0181a --- /dev/null +++ b/vendor/rustix/src/backend/libc/rand/mod.rs @@ -0,0 +1,2 @@ +pub(crate) mod syscalls; +pub(crate) mod types; diff --git a/vendor/rustix/src/backend/libc/rand/syscalls.rs b/vendor/rustix/src/backend/libc/rand/syscalls.rs new file mode 100644 index 0000000..3a3929e --- /dev/null +++ b/vendor/rustix/src/backend/libc/rand/syscalls.rs @@ -0,0 +1,18 @@ +//! libc syscalls supporting `rustix::rand`. + +#[cfg(linux_kernel)] +use {crate::backend::c, crate::backend::conv::ret_usize, crate::io, crate::rand::GetRandomFlags}; + +#[cfg(linux_kernel)] +pub(crate) unsafe fn getrandom( + buf: *mut u8, + cap: usize, + flags: GetRandomFlags, +) -> io::Result<usize> { + // `getrandom` wasn't supported in glibc until 2.25. + weak_or_syscall! { + fn getrandom(buf: *mut c::c_void, buflen: c::size_t, flags: c::c_uint) via SYS_getrandom -> c::ssize_t + } + + ret_usize(getrandom(buf.cast(), cap, flags.bits())) +} diff --git a/vendor/rustix/src/backend/libc/rand/types.rs b/vendor/rustix/src/backend/libc/rand/types.rs new file mode 100644 index 0000000..46690b5 --- /dev/null +++ b/vendor/rustix/src/backend/libc/rand/types.rs @@ -0,0 +1,24 @@ +#[cfg(linux_kernel)] +use crate::backend::c; +#[cfg(linux_kernel)] +use bitflags::bitflags; + +#[cfg(linux_kernel)] +bitflags! { + /// `GRND_*` flags for use with [`getrandom`]. + /// + /// [`getrandom`]: crate::rand::getrandom + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct GetRandomFlags: u32 { + /// `GRND_RANDOM` + const RANDOM = c::GRND_RANDOM; + /// `GRND_NONBLOCK` + const NONBLOCK = c::GRND_NONBLOCK; + /// `GRND_INSECURE` + const INSECURE = c::GRND_INSECURE; + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} diff --git a/vendor/rustix/src/backend/libc/shm/mod.rs b/vendor/rustix/src/backend/libc/shm/mod.rs new file mode 100644 index 0000000..1e0181a --- /dev/null +++ b/vendor/rustix/src/backend/libc/shm/mod.rs @@ -0,0 +1,2 @@ +pub(crate) mod syscalls; +pub(crate) mod types; diff --git a/vendor/rustix/src/backend/libc/shm/syscalls.rs b/vendor/rustix/src/backend/libc/shm/syscalls.rs new file mode 100644 index 0000000..b0d907f --- /dev/null +++ b/vendor/rustix/src/backend/libc/shm/syscalls.rs @@ -0,0 +1,25 @@ +use crate::ffi::CStr; + +use crate::backend::c; +use crate::backend::conv::{c_str, ret, ret_owned_fd}; +use crate::fd::OwnedFd; +use crate::fs::Mode; +use crate::io; +use crate::shm::ShmOFlags; + +pub(crate) fn shm_open(name: &CStr, oflags: ShmOFlags, mode: Mode) -> io::Result<OwnedFd> { + // On this platforms, `mode_t` is `u16` and can't be passed directly to a + // variadic function. + #[cfg(apple)] + let mode: c::c_uint = mode.bits().into(); + + // Otherwise, cast to `mode_t` as that's what `open` is documented to take. + #[cfg(not(apple))] + let mode: c::mode_t = mode.bits() as _; + + unsafe { ret_owned_fd(c::shm_open(c_str(name), bitflags_bits!(oflags), mode)) } +} + +pub(crate) fn shm_unlink(name: &CStr) -> io::Result<()> { + unsafe { ret(c::shm_unlink(c_str(name))) } +} diff --git a/vendor/rustix/src/backend/libc/shm/types.rs b/vendor/rustix/src/backend/libc/shm/types.rs new file mode 100644 index 0000000..6575ef5 --- /dev/null +++ b/vendor/rustix/src/backend/libc/shm/types.rs @@ -0,0 +1,30 @@ +use crate::backend::c; +use bitflags::bitflags; + +bitflags! { + /// `O_*` constants for use with [`shm_open`]. + /// + /// [`shm_open`]: crate:shm::shm_open + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct ShmOFlags: u32 { + /// `O_CREAT` + #[doc(alias = "CREAT")] + const CREATE = bitcast!(c::O_CREAT); + + /// `O_EXCL` + const EXCL = bitcast!(c::O_EXCL); + + /// `O_RDONLY` + const RDONLY = bitcast!(c::O_RDONLY); + + /// `O_RDWR` + const RDWR = bitcast!(c::O_RDWR); + + /// `O_TRUNC` + const TRUNC = bitcast!(c::O_TRUNC); + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} diff --git a/vendor/rustix/src/backend/libc/system/mod.rs b/vendor/rustix/src/backend/libc/system/mod.rs new file mode 100644 index 0000000..bff7fd5 --- /dev/null +++ b/vendor/rustix/src/backend/libc/system/mod.rs @@ -0,0 +1,3 @@ +#[cfg(not(windows))] +pub(crate) mod syscalls; +pub(crate) mod types; diff --git a/vendor/rustix/src/backend/libc/system/syscalls.rs b/vendor/rustix/src/backend/libc/system/syscalls.rs new file mode 100644 index 0000000..05d674b --- /dev/null +++ b/vendor/rustix/src/backend/libc/system/syscalls.rs @@ -0,0 +1,67 @@ +//! libc syscalls supporting `rustix::process`. + +use super::types::RawUname; +use crate::backend::c; +#[cfg(not(target_os = "wasi"))] +use crate::backend::conv::ret_infallible; +#[cfg(target_os = "linux")] +use crate::system::RebootCommand; +#[cfg(linux_kernel)] +use crate::system::Sysinfo; +use core::mem::MaybeUninit; +#[cfg(not(any( + target_os = "emscripten", + target_os = "espidf", + target_os = "redox", + target_os = "vita", + target_os = "wasi" +)))] +use {crate::backend::conv::ret, crate::io}; + +#[cfg(not(target_os = "wasi"))] +#[inline] +pub(crate) fn uname() -> RawUname { + let mut uname = MaybeUninit::<RawUname>::uninit(); + unsafe { + let r = c::uname(uname.as_mut_ptr()); + + // On POSIX, `uname` is documented to return non-negative on success + // instead of the usual 0, though some specific systems do document + // that they always use zero allowing us to skip this check. + #[cfg(not(any(apple, freebsdlike, linux_like, target_os = "netbsd")))] + let r = core::cmp::min(r, 0); + + ret_infallible(r); + uname.assume_init() + } +} + +#[cfg(linux_kernel)] +pub(crate) fn sysinfo() -> Sysinfo { + let mut info = MaybeUninit::<Sysinfo>::uninit(); + unsafe { + ret_infallible(c::sysinfo(info.as_mut_ptr())); + info.assume_init() + } +} + +#[cfg(not(any( + target_os = "emscripten", + target_os = "espidf", + target_os = "redox", + target_os = "vita", + target_os = "wasi" +)))] +pub(crate) fn sethostname(name: &[u8]) -> io::Result<()> { + unsafe { + ret(c::sethostname( + name.as_ptr().cast(), + name.len().try_into().map_err(|_| io::Errno::INVAL)?, + )) + } +} + +#[cfg(target_os = "linux")] +pub(crate) fn reboot(cmd: RebootCommand) -> io::Result<()> { + unsafe { ret(c::reboot(cmd as i32)) } +} diff --git a/vendor/rustix/src/backend/libc/system/types.rs b/vendor/rustix/src/backend/libc/system/types.rs new file mode 100644 index 0000000..731e89b --- /dev/null +++ b/vendor/rustix/src/backend/libc/system/types.rs @@ -0,0 +1,8 @@ +use crate::backend::c; + +/// `sysinfo` +#[cfg(linux_kernel)] +pub type Sysinfo = c::sysinfo; + +#[cfg(not(target_os = "wasi"))] +pub(crate) type RawUname = c::utsname; diff --git a/vendor/rustix/src/backend/libc/termios/mod.rs b/vendor/rustix/src/backend/libc/termios/mod.rs new file mode 100644 index 0000000..ef944f0 --- /dev/null +++ b/vendor/rustix/src/backend/libc/termios/mod.rs @@ -0,0 +1 @@ +pub(crate) mod syscalls; diff --git a/vendor/rustix/src/backend/libc/termios/syscalls.rs b/vendor/rustix/src/backend/libc/termios/syscalls.rs new file mode 100644 index 0000000..a833aea --- /dev/null +++ b/vendor/rustix/src/backend/libc/termios/syscalls.rs @@ -0,0 +1,403 @@ +//! libc syscalls supporting `rustix::termios`. +//! +//! # Safety +//! +//! See the `rustix::backend::syscalls` module documentation for details. + +use crate::backend::c; +#[cfg(not(target_os = "wasi"))] +use crate::backend::conv::ret_pid_t; +use crate::backend::conv::{borrowed_fd, ret}; +use crate::fd::BorrowedFd; +#[cfg(all(feature = "alloc", feature = "procfs"))] +#[cfg(not(any(target_os = "fuchsia", target_os = "wasi")))] +use crate::ffi::CStr; +#[cfg(any( + not(target_os = "espidf"), + all( + feature = "procfs", + not(any(target_os = "fuchsia", target_os = "wasi")) + ) +))] +use core::mem::MaybeUninit; +#[cfg(not(target_os = "wasi"))] +use {crate::io, crate::pid::Pid}; +#[cfg(not(any(target_os = "espidf", target_os = "wasi")))] +use { + crate::termios::{Action, OptionalActions, QueueSelector, Termios, Winsize}, + crate::utils::as_mut_ptr, +}; + +#[cfg(not(any(target_os = "espidf", target_os = "wasi")))] +pub(crate) fn tcgetattr(fd: BorrowedFd<'_>) -> io::Result<Termios> { + // If we have `TCGETS2`, use it, so that we fill in the `c_ispeed` and + // `c_ospeed` fields. + #[cfg(linux_kernel)] + { + use crate::termios::{ControlModes, InputModes, LocalModes, OutputModes, SpecialCodes}; + use crate::utils::default_array; + + let termios2 = unsafe { + let mut termios2 = MaybeUninit::<c::termios2>::uninit(); + + // QEMU's `TCGETS2` doesn't currently set `input_speed` or + // `output_speed` on PowerPC, so zero out the fields ourselves. + #[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))] + { + termios2.write(core::mem::zeroed()); + } + + ret(c::ioctl( + borrowed_fd(fd), + c::TCGETS2 as _, + termios2.as_mut_ptr(), + ))?; + + termios2.assume_init() + }; + + // Convert from the Linux `termios2` to our `Termios`. + let mut result = Termios { + input_modes: InputModes::from_bits_retain(termios2.c_iflag), + output_modes: OutputModes::from_bits_retain(termios2.c_oflag), + control_modes: ControlModes::from_bits_retain(termios2.c_cflag), + local_modes: LocalModes::from_bits_retain(termios2.c_lflag), + line_discipline: termios2.c_line, + special_codes: SpecialCodes(default_array()), + input_speed: termios2.c_ispeed, + output_speed: termios2.c_ospeed, + }; + + // QEMU's `TCGETS2` doesn't currently set `input_speed` or + // `output_speed` on PowerPC, so set them manually if we can. + #[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))] + { + use crate::termios::speed; + + if result.output_speed == 0 && (termios2.c_cflag & c::CBAUD) != c::BOTHER { + if let Some(output_speed) = speed::decode(termios2.c_cflag & c::CBAUD) { + result.output_speed = output_speed; + } + } + if result.input_speed == 0 + && ((termios2.c_cflag & c::CIBAUD) >> c::IBSHIFT) != c::BOTHER + { + // For input speeds, `B0` is special-cased to mean the input + // speed is the same as the output speed. + if ((termios2.c_cflag & c::CIBAUD) >> c::IBSHIFT) == c::B0 { + result.input_speed = result.output_speed; + } else if let Some(input_speed) = + speed::decode((termios2.c_cflag & c::CIBAUD) >> c::IBSHIFT) + { + result.input_speed = input_speed; + } + } + } + + result.special_codes.0[..termios2.c_cc.len()].copy_from_slice(&termios2.c_cc); + + Ok(result) + } + + #[cfg(not(linux_kernel))] + unsafe { + let mut result = MaybeUninit::<Termios>::uninit(); + + // `result` is a `Termios` which starts with the same layout as + // `libc::termios`, so we can cast the pointer. + ret(c::tcgetattr(borrowed_fd(fd), result.as_mut_ptr().cast()))?; + + Ok(result.assume_init()) + } +} + +#[cfg(not(target_os = "wasi"))] +pub(crate) fn tcgetpgrp(fd: BorrowedFd<'_>) -> io::Result<Pid> { + unsafe { + 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 + // pseudo-terminal device fd. For now, translate it into `OPNOTSUPP`. + #[cfg(linux_kernel)] + if pid == 0 { + return Err(io::Errno::OPNOTSUPP); + } + + Ok(Pid::from_raw_unchecked(pid)) + } +} + +#[cfg(not(target_os = "wasi"))] +pub(crate) fn tcsetpgrp(fd: BorrowedFd<'_>, pid: Pid) -> io::Result<()> { + unsafe { ret(c::tcsetpgrp(borrowed_fd(fd), pid.as_raw_nonzero().get())) } +} + +#[cfg(not(any(target_os = "espidf", target_os = "wasi")))] +pub(crate) fn tcsetattr( + fd: BorrowedFd<'_>, + optional_actions: OptionalActions, + termios: &Termios, +) -> io::Result<()> { + // If we have `TCSETS2`, use it, so that we use the `c_ispeed` and + // `c_ospeed` fields. + #[cfg(linux_kernel)] + { + use crate::termios::speed; + use crate::utils::default_array; + use linux_raw_sys::general::{termios2, BOTHER, CBAUD, IBSHIFT}; + + #[cfg(not(any(target_arch = "sparc", target_arch = "sparc64")))] + use linux_raw_sys::ioctl::{TCSETS, TCSETS2}; + + // linux-raw-sys' ioctl-generation script for sparc isn't working yet, + // so as a temporary workaround, declare these manually. + #[cfg(any(target_arch = "sparc", target_arch = "sparc64"))] + const TCSETS: u32 = 0x8024_5409; + #[cfg(any(target_arch = "sparc", target_arch = "sparc64"))] + const TCSETS2: u32 = 0x802c_540d; + + // Translate from `optional_actions` into an ioctl request code. On + // MIPS, `optional_actions` already has `TCGETS` added to it. + let request = TCSETS2 + + if cfg!(any( + target_arch = "mips", + target_arch = "mips32r6", + target_arch = "mips64", + target_arch = "mips64r6" + )) { + optional_actions as u32 - TCSETS + } else { + optional_actions as u32 + }; + + let input_speed = termios.input_speed(); + let output_speed = termios.output_speed(); + let mut termios2 = termios2 { + c_iflag: termios.input_modes.bits(), + c_oflag: termios.output_modes.bits(), + c_cflag: termios.control_modes.bits(), + c_lflag: termios.local_modes.bits(), + c_line: termios.line_discipline, + c_cc: default_array(), + c_ispeed: input_speed, + c_ospeed: output_speed, + }; + // Ensure that our input and output speeds are set, as `libc` + // routines don't always support setting these separately. + termios2.c_cflag &= !CBAUD; + termios2.c_cflag |= speed::encode(output_speed).unwrap_or(BOTHER); + termios2.c_cflag &= !(CBAUD << IBSHIFT); + termios2.c_cflag |= speed::encode(input_speed).unwrap_or(BOTHER) << IBSHIFT; + let nccs = termios2.c_cc.len(); + termios2 + .c_cc + .copy_from_slice(&termios.special_codes.0[..nccs]); + + unsafe { ret(c::ioctl(borrowed_fd(fd), request as _, &termios2)) } + } + + #[cfg(not(linux_kernel))] + unsafe { + ret(c::tcsetattr( + borrowed_fd(fd), + optional_actions as _, + crate::utils::as_ptr(termios).cast(), + )) + } +} + +#[cfg(not(target_os = "wasi"))] +pub(crate) fn tcsendbreak(fd: BorrowedFd<'_>) -> io::Result<()> { + unsafe { ret(c::tcsendbreak(borrowed_fd(fd), 0)) } +} + +#[cfg(not(any(target_os = "espidf", target_os = "wasi")))] +pub(crate) fn tcdrain(fd: BorrowedFd<'_>) -> io::Result<()> { + unsafe { ret(c::tcdrain(borrowed_fd(fd))) } +} + +#[cfg(not(any(target_os = "espidf", target_os = "wasi")))] +pub(crate) fn tcflush(fd: BorrowedFd<'_>, queue_selector: QueueSelector) -> io::Result<()> { + unsafe { ret(c::tcflush(borrowed_fd(fd), queue_selector as _)) } +} + +#[cfg(not(any(target_os = "espidf", target_os = "wasi")))] +pub(crate) fn tcflow(fd: BorrowedFd<'_>, action: Action) -> io::Result<()> { + unsafe { ret(c::tcflow(borrowed_fd(fd), action as _)) } +} + +#[cfg(not(target_os = "wasi"))] +pub(crate) fn tcgetsid(fd: BorrowedFd<'_>) -> io::Result<Pid> { + unsafe { + let pid = ret_pid_t(c::tcgetsid(borrowed_fd(fd)))?; + Ok(Pid::from_raw_unchecked(pid)) + } +} + +#[cfg(not(any(target_os = "espidf", target_os = "wasi")))] +pub(crate) fn tcsetwinsize(fd: BorrowedFd<'_>, winsize: Winsize) -> io::Result<()> { + unsafe { ret(c::ioctl(borrowed_fd(fd), c::TIOCSWINSZ, &winsize)) } +} + +#[cfg(not(any(target_os = "espidf", target_os = "wasi")))] +pub(crate) fn tcgetwinsize(fd: BorrowedFd<'_>) -> io::Result<Winsize> { + unsafe { + let mut buf = MaybeUninit::<Winsize>::uninit(); + ret(c::ioctl( + borrowed_fd(fd), + c::TIOCGWINSZ.into(), + buf.as_mut_ptr(), + ))?; + Ok(buf.assume_init()) + } +} + +#[cfg(not(any(target_os = "espidf", target_os = "nto", target_os = "wasi")))] +#[inline] +pub(crate) fn set_speed(termios: &mut Termios, arbitrary_speed: u32) -> io::Result<()> { + #[cfg(bsd)] + let encoded_speed = arbitrary_speed; + + #[cfg(not(bsd))] + let encoded_speed = match crate::termios::speed::encode(arbitrary_speed) { + Some(encoded_speed) => encoded_speed, + #[cfg(linux_kernel)] + None => c::BOTHER, + #[cfg(not(linux_kernel))] + None => return Err(io::Errno::INVAL), + }; + + #[cfg(not(linux_kernel))] + unsafe { + ret(c::cfsetspeed( + as_mut_ptr(termios).cast(), + encoded_speed.into(), + )) + } + + // Linux libc implementations don't support arbitrary speeds, so we encode + // the speed manually. + #[cfg(linux_kernel)] + { + use crate::termios::ControlModes; + + debug_assert_eq!(encoded_speed & !c::CBAUD, 0); + + termios.control_modes -= ControlModes::from_bits_retain(c::CBAUD | c::CIBAUD); + termios.control_modes |= + ControlModes::from_bits_retain(encoded_speed | (encoded_speed << c::IBSHIFT)); + + termios.input_speed = arbitrary_speed; + termios.output_speed = arbitrary_speed; + + Ok(()) + } +} + +#[cfg(not(any(target_os = "espidf", target_os = "wasi")))] +#[inline] +pub(crate) fn set_output_speed(termios: &mut Termios, arbitrary_speed: u32) -> io::Result<()> { + #[cfg(bsd)] + let encoded_speed = arbitrary_speed; + + #[cfg(not(bsd))] + let encoded_speed = match crate::termios::speed::encode(arbitrary_speed) { + Some(encoded_speed) => encoded_speed, + #[cfg(linux_kernel)] + None => c::BOTHER, + #[cfg(not(linux_kernel))] + None => return Err(io::Errno::INVAL), + }; + + #[cfg(not(linux_kernel))] + unsafe { + ret(c::cfsetospeed( + as_mut_ptr(termios).cast(), + encoded_speed.into(), + )) + } + + // Linux libc implementations don't support arbitrary speeds or setting the + // input and output speeds separately, so we encode the speed manually. + #[cfg(linux_kernel)] + { + use crate::termios::ControlModes; + + debug_assert_eq!(encoded_speed & !c::CBAUD, 0); + + termios.control_modes -= ControlModes::from_bits_retain(c::CBAUD); + termios.control_modes |= ControlModes::from_bits_retain(encoded_speed); + + termios.output_speed = arbitrary_speed; + + Ok(()) + } +} + +#[cfg(not(any(target_os = "espidf", target_os = "wasi")))] +#[inline] +pub(crate) fn set_input_speed(termios: &mut Termios, arbitrary_speed: u32) -> io::Result<()> { + #[cfg(bsd)] + let encoded_speed = arbitrary_speed; + + #[cfg(not(bsd))] + let encoded_speed = match crate::termios::speed::encode(arbitrary_speed) { + Some(encoded_speed) => encoded_speed, + #[cfg(linux_kernel)] + None => c::BOTHER, + #[cfg(not(linux_kernel))] + None => return Err(io::Errno::INVAL), + }; + + #[cfg(not(linux_kernel))] + unsafe { + ret(c::cfsetispeed( + as_mut_ptr(termios).cast(), + encoded_speed.into(), + )) + } + + // Linux libc implementations don't support arbitrary speeds or setting the + // input and output speeds separately, so we encode the speed manually. + #[cfg(linux_kernel)] + { + use crate::termios::ControlModes; + + debug_assert_eq!(encoded_speed & !c::CBAUD, 0); + + termios.control_modes -= ControlModes::from_bits_retain(c::CIBAUD); + termios.control_modes |= ControlModes::from_bits_retain(encoded_speed << c::IBSHIFT); + + termios.input_speed = arbitrary_speed; + + Ok(()) + } +} + +#[cfg(not(any(target_os = "espidf", target_os = "nto", target_os = "wasi")))] +#[inline] +pub(crate) fn cfmakeraw(termios: &mut Termios) { + unsafe { c::cfmakeraw(as_mut_ptr(termios).cast()) } +} + +pub(crate) fn isatty(fd: BorrowedFd<'_>) -> bool { + // Use the return value of `isatty` alone. We don't check `errno` because + // we return `bool` rather than `io::Result<bool>`, because we assume + // `BorrowedFd` protects us from `EBADF`, and any other reasonably + // anticipated `errno` value would end up interpreted as “assume it's not a + // terminal” anyway. + unsafe { c::isatty(borrowed_fd(fd)) != 0 } +} + +#[cfg(all(feature = "alloc", feature = "procfs"))] +#[cfg(not(any(target_os = "fuchsia", target_os = "wasi")))] +pub(crate) fn ttyname(dirfd: BorrowedFd<'_>, buf: &mut [MaybeUninit<u8>]) -> io::Result<usize> { + unsafe { + // `ttyname_r` returns its error status rather than using `errno`. + match c::ttyname_r(borrowed_fd(dirfd), buf.as_mut_ptr().cast(), buf.len()) { + 0 => Ok(CStr::from_ptr(buf.as_ptr().cast()).to_bytes().len()), + err => Err(io::Errno::from_raw_os_error(err)), + } + } +} diff --git a/vendor/rustix/src/backend/libc/thread/futex.rs b/vendor/rustix/src/backend/libc/thread/futex.rs new file mode 100644 index 0000000..44d96f0 --- /dev/null +++ b/vendor/rustix/src/backend/libc/thread/futex.rs @@ -0,0 +1,43 @@ +use crate::backend::c; + +bitflags::bitflags! { + /// `FUTEX_*` flags for use with [`futex`]. + /// + /// [`futex`]: crate::thread::futex + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct FutexFlags: u32 { + /// `FUTEX_PRIVATE_FLAG` + const PRIVATE = bitcast!(c::FUTEX_PRIVATE_FLAG); + /// `FUTEX_CLOCK_REALTIME` + const CLOCK_REALTIME = bitcast!(c::FUTEX_CLOCK_REALTIME); + } +} + +/// `FUTEX_*` operations for use with [`futex`]. +/// +/// [`futex`]: crate::thread::futex +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[repr(u32)] +pub enum FutexOperation { + /// `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), +} diff --git a/vendor/rustix/src/backend/libc/thread/mod.rs b/vendor/rustix/src/backend/libc/thread/mod.rs new file mode 100644 index 0000000..4f8c87c --- /dev/null +++ b/vendor/rustix/src/backend/libc/thread/mod.rs @@ -0,0 +1,4 @@ +#[cfg(linux_kernel)] +pub(crate) mod futex; +#[cfg(not(windows))] +pub(crate) mod syscalls; diff --git a/vendor/rustix/src/backend/libc/thread/syscalls.rs b/vendor/rustix/src/backend/libc/thread/syscalls.rs new file mode 100644 index 0000000..33750f4 --- /dev/null +++ b/vendor/rustix/src/backend/libc/thread/syscalls.rs @@ -0,0 +1,523 @@ +//! libc syscalls supporting `rustix::thread`. + +use crate::backend::c; +use crate::backend::conv::ret; +use crate::io; +#[cfg(not(target_os = "redox"))] +use crate::thread::{NanosleepRelativeResult, Timespec}; +#[cfg(all(target_env = "gnu", fix_y2038))] +use crate::timespec::LibcTimespec; +use core::mem::MaybeUninit; +#[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::utils::as_mut_ptr, +}; +#[cfg(not(any( + apple, + freebsdlike, + target_os = "emscripten", + target_os = "espidf", + target_os = "haiku", + target_os = "openbsd", + target_os = "redox", + target_os = "vita", + target_os = "wasi", +)))] +use {crate::thread::ClockId, core::ptr::null_mut}; + +#[cfg(all(target_env = "gnu", fix_y2038))] +weak!(fn __clock_nanosleep_time64(c::clockid_t, c::c_int, *const LibcTimespec, *mut LibcTimespec) -> c::c_int); +#[cfg(all(target_env = "gnu", fix_y2038))] +weak!(fn __nanosleep64(*const LibcTimespec, *mut LibcTimespec) -> c::c_int); + +#[cfg(not(any( + apple, + target_os = "dragonfly", + target_os = "emscripten", + target_os = "espidf", + target_os = "freebsd", // FreeBSD 12 has clock_nanosleep, but libc targets FreeBSD 11. + target_os = "haiku", + target_os = "openbsd", + target_os = "redox", + target_os = "vita", + target_os = "wasi", +)))] +#[inline] +pub(crate) fn clock_nanosleep_relative(id: ClockId, request: &Timespec) -> NanosleepRelativeResult { + // Old 32-bit version: libc has `clock_nanosleep` but it is not y2038 safe + // by default. But there may be a `__clock_nanosleep_time64` we can use. + #[cfg(fix_y2038)] + { + #[cfg(target_env = "gnu")] + if let Some(libc_clock_nanosleep) = __clock_nanosleep_time64.get() { + let flags = 0; + let mut remain = MaybeUninit::<LibcTimespec>::uninit(); + + unsafe { + return match libc_clock_nanosleep( + id as c::clockid_t, + flags, + &request.clone().into(), + remain.as_mut_ptr(), + ) { + 0 => NanosleepRelativeResult::Ok, + err if err == io::Errno::INTR.0 => { + NanosleepRelativeResult::Interrupted(remain.assume_init().into()) + } + err => NanosleepRelativeResult::Err(io::Errno(err)), + }; + } + } + + clock_nanosleep_relative_old(id, request) + } + + // Main version: libc is y2038 safe and has `clock_nanosleep`. + #[cfg(not(fix_y2038))] + unsafe { + let flags = 0; + let mut remain = MaybeUninit::<Timespec>::uninit(); + + match c::clock_nanosleep(id as c::clockid_t, flags, request, remain.as_mut_ptr()) { + 0 => NanosleepRelativeResult::Ok, + err if err == io::Errno::INTR.0 => { + NanosleepRelativeResult::Interrupted(remain.assume_init()) + } + err => NanosleepRelativeResult::Err(io::Errno(err)), + } + } +} + +#[cfg(all( + fix_y2038, + not(any( + apple, + target_os = "emscripten", + target_os = "haiku", + target_os = "vita" + )) +))] +fn clock_nanosleep_relative_old(id: ClockId, request: &Timespec) -> NanosleepRelativeResult { + let tv_sec = match request.tv_sec.try_into() { + Ok(tv_sec) => tv_sec, + Err(_) => return NanosleepRelativeResult::Err(io::Errno::OVERFLOW), + }; + let tv_nsec = match request.tv_nsec.try_into() { + Ok(tv_nsec) => tv_nsec, + Err(_) => return NanosleepRelativeResult::Err(io::Errno::INVAL), + }; + let old_request = c::timespec { tv_sec, tv_nsec }; + let mut old_remain = MaybeUninit::<c::timespec>::uninit(); + let flags = 0; + + unsafe { + match c::clock_nanosleep( + id as c::clockid_t, + flags, + &old_request, + old_remain.as_mut_ptr(), + ) { + 0 => NanosleepRelativeResult::Ok, + err if err == io::Errno::INTR.0 => { + let old_remain = old_remain.assume_init(); + let remain = Timespec { + tv_sec: old_remain.tv_sec.into(), + tv_nsec: old_remain.tv_nsec.into(), + }; + NanosleepRelativeResult::Interrupted(remain) + } + err => NanosleepRelativeResult::Err(io::Errno(err)), + } + } +} + +#[cfg(not(any( + apple, + target_os = "dragonfly", + target_os = "emscripten", + target_os = "espidf", + target_os = "freebsd", // FreeBSD 12 has clock_nanosleep, but libc targets FreeBSD 11. + target_os = "haiku", + target_os = "openbsd", + target_os = "redox", + target_os = "vita", + target_os = "wasi", +)))] +#[inline] +pub(crate) fn clock_nanosleep_absolute(id: ClockId, request: &Timespec) -> io::Result<()> { + // Old 32-bit version: libc has `clock_nanosleep` but it is not y2038 safe + // by default. But there may be a `__clock_nanosleep_time64` we can use. + #[cfg(fix_y2038)] + { + #[cfg(target_env = "gnu")] + if let Some(libc_clock_nanosleep) = __clock_nanosleep_time64.get() { + let flags = c::TIMER_ABSTIME; + unsafe { + return match { + libc_clock_nanosleep( + id as c::clockid_t, + flags, + &request.clone().into(), + null_mut(), + ) + } { + 0 => Ok(()), + err => Err(io::Errno(err)), + }; + } + } + + clock_nanosleep_absolute_old(id, request) + } + + // Main version: libc is y2038 safe and has `clock_nanosleep`. + #[cfg(not(fix_y2038))] + { + let flags = c::TIMER_ABSTIME; + + match unsafe { c::clock_nanosleep(id as c::clockid_t, flags as _, request, null_mut()) } { + 0 => Ok(()), + err => Err(io::Errno(err)), + } + } +} + +#[cfg(all( + fix_y2038, + not(any( + apple, + target_os = "emscripten", + target_os = "haiku", + target_os = "vita" + )) +))] +fn clock_nanosleep_absolute_old(id: ClockId, request: &Timespec) -> io::Result<()> { + let flags = c::TIMER_ABSTIME; + + let old_request = c::timespec { + tv_sec: request.tv_sec.try_into().map_err(|_| io::Errno::OVERFLOW)?, + tv_nsec: request.tv_nsec.try_into().map_err(|_| io::Errno::INVAL)?, + }; + match unsafe { c::clock_nanosleep(id as c::clockid_t, flags, &old_request, null_mut()) } { + 0 => Ok(()), + err => Err(io::Errno(err)), + } +} + +#[cfg(not(target_os = "redox"))] +#[inline] +pub(crate) fn nanosleep(request: &Timespec) -> NanosleepRelativeResult { + // Old 32-bit version: libc has `nanosleep` but it is not y2038 safe by + // default. But there may be a `__nanosleep64` we can use. + #[cfg(fix_y2038)] + { + #[cfg(target_env = "gnu")] + if let Some(libc_nanosleep) = __nanosleep64.get() { + let mut remain = MaybeUninit::<LibcTimespec>::uninit(); + unsafe { + return match ret(libc_nanosleep(&request.clone().into(), remain.as_mut_ptr())) { + Ok(()) => NanosleepRelativeResult::Ok, + Err(io::Errno::INTR) => { + NanosleepRelativeResult::Interrupted(remain.assume_init().into()) + } + Err(err) => NanosleepRelativeResult::Err(err), + }; + } + } + + nanosleep_old(request) + } + + // Main version: libc is y2038 safe and has `nanosleep`. + #[cfg(not(fix_y2038))] + unsafe { + let mut remain = MaybeUninit::<Timespec>::uninit(); + + match ret(c::nanosleep(request, remain.as_mut_ptr())) { + Ok(()) => NanosleepRelativeResult::Ok, + Err(io::Errno::INTR) => NanosleepRelativeResult::Interrupted(remain.assume_init()), + Err(err) => NanosleepRelativeResult::Err(err), + } + } +} + +#[cfg(fix_y2038)] +fn nanosleep_old(request: &Timespec) -> NanosleepRelativeResult { + let tv_sec = match request.tv_sec.try_into() { + Ok(tv_sec) => tv_sec, + Err(_) => return NanosleepRelativeResult::Err(io::Errno::OVERFLOW), + }; + let tv_nsec = match request.tv_nsec.try_into() { + Ok(tv_nsec) => tv_nsec, + Err(_) => return NanosleepRelativeResult::Err(io::Errno::INVAL), + }; + let old_request = c::timespec { tv_sec, tv_nsec }; + let mut old_remain = MaybeUninit::<c::timespec>::uninit(); + + unsafe { + match ret(c::nanosleep(&old_request, old_remain.as_mut_ptr())) { + Ok(()) => NanosleepRelativeResult::Ok, + Err(io::Errno::INTR) => { + let old_remain = old_remain.assume_init(); + let remain = Timespec { + tv_sec: old_remain.tv_sec.into(), + tv_nsec: old_remain.tv_nsec.into(), + }; + NanosleepRelativeResult::Interrupted(remain) + } + Err(err) => NanosleepRelativeResult::Err(err), + } + } +} + +#[cfg(linux_kernel)] +#[inline] +#[must_use] +pub(crate) fn gettid() -> Pid { + // `gettid` wasn't supported in glibc until 2.30, and musl until 1.2.2, + // so use `syscall`. + // <https://sourceware.org/bugzilla/show_bug.cgi?id=6399#c62> + weak_or_syscall! { + fn gettid() via SYS_gettid -> c::pid_t + } + + unsafe { + let tid = gettid(); + Pid::from_raw_unchecked(tid) + } +} + +#[cfg(linux_kernel)] +#[inline] +pub(crate) fn setns(fd: BorrowedFd<'_>, nstype: c::c_int) -> io::Result<c::c_int> { + // `setns` wasn't supported in glibc until 2.14, and musl until 0.9.5, + // so use `syscall`. + weak_or_syscall! { + fn setns(fd: c::c_int, nstype: c::c_int) via SYS_setns -> c::c_int + } + + unsafe { ret_c_int(setns(borrowed_fd(fd), nstype)) } +} + +#[cfg(linux_kernel)] +#[inline] +pub(crate) fn unshare(flags: crate::thread::UnshareFlags) -> io::Result<()> { + unsafe { ret(c::unshare(flags.bits() as i32)) } +} + +#[cfg(linux_kernel)] +#[inline] +pub(crate) fn capget( + header: &mut linux_raw_sys::general::__user_cap_header_struct, + data: &mut [MaybeUninit<linux_raw_sys::general::__user_cap_data_struct>], +) -> io::Result<()> { + syscall! { + fn capget( + hdrp: *mut linux_raw_sys::general::__user_cap_header_struct, + data: *mut linux_raw_sys::general::__user_cap_data_struct + ) via SYS_capget -> c::c_int + } + + unsafe { + ret(capget( + as_mut_ptr(header), + data.as_mut_ptr() + .cast::<linux_raw_sys::general::__user_cap_data_struct>(), + )) + } +} + +#[cfg(linux_kernel)] +#[inline] +pub(crate) fn capset( + header: &mut linux_raw_sys::general::__user_cap_header_struct, + data: &[linux_raw_sys::general::__user_cap_data_struct], +) -> io::Result<()> { + syscall! { + fn capset( + hdrp: *mut linux_raw_sys::general::__user_cap_header_struct, + data: *const linux_raw_sys::general::__user_cap_data_struct + ) via SYS_capset -> c::c_int + } + + unsafe { ret(capset(as_mut_ptr(header), data.as_ptr())) } +} + +#[cfg(linux_kernel)] +#[inline] +pub(crate) fn setuid_thread(uid: crate::ugid::Uid) -> io::Result<()> { + syscall! { + fn setuid(uid: c::uid_t) via SYS_setuid -> c::c_int + } + + unsafe { ret(setuid(uid.as_raw())) } +} + +#[cfg(linux_kernel)] +#[inline] +pub(crate) fn setresuid_thread( + ruid: crate::ugid::Uid, + euid: crate::ugid::Uid, + suid: crate::ugid::Uid, +) -> io::Result<()> { + #[cfg(any(target_arch = "x86", target_arch = "arm", target_arch = "sparc"))] + const SYS: c::c_long = c::SYS_setresuid32 as c::c_long; + #[cfg(not(any(target_arch = "x86", target_arch = "arm", target_arch = "sparc")))] + const SYS: c::c_long = c::SYS_setresuid as c::c_long; + + syscall! { + fn setresuid(ruid: c::uid_t, euid: c::uid_t, suid: c::uid_t) via SYS -> c::c_int + } + + unsafe { ret(setresuid(ruid.as_raw(), euid.as_raw(), suid.as_raw())) } +} + +#[cfg(linux_kernel)] +#[inline] +pub(crate) fn setgid_thread(gid: crate::ugid::Gid) -> io::Result<()> { + syscall! { + fn setgid(gid: c::gid_t) via SYS_setgid -> c::c_int + } + + unsafe { ret(setgid(gid.as_raw())) } +} + +#[cfg(linux_kernel)] +#[inline] +pub(crate) fn setresgid_thread( + rgid: crate::ugid::Gid, + egid: crate::ugid::Gid, + sgid: crate::ugid::Gid, +) -> io::Result<()> { + #[cfg(any(target_arch = "x86", target_arch = "arm", target_arch = "sparc"))] + const SYS: c::c_long = c::SYS_setresgid32 as c::c_long; + #[cfg(not(any(target_arch = "x86", target_arch = "arm", target_arch = "sparc")))] + const SYS: c::c_long = c::SYS_setresgid as c::c_long; + + syscall! { + fn setresgid(rgid: c::gid_t, egid: c::gid_t, sgid: c::gid_t) via SYS -> c::c_int + } + + unsafe { ret(setresgid(rgid.as_raw(), egid.as_raw(), sgid.as_raw())) } +} + +// TODO: This could be de-multiplexed. +#[cfg(linux_kernel)] +pub(crate) unsafe fn futex( + uaddr: *mut u32, + op: FutexOperation, + flags: FutexFlags, + val: u32, + utime: *const Timespec, + uaddr2: *mut u32, + val3: u32, +) -> io::Result<usize> { + #[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: *mut u32, + futex_op: c::c_int, + val: u32, + timeout: *const Timespec, + uaddr2: *mut u32, + val3: u32 + ) via SYS_futex_time64 -> c::ssize_t + } + + ret_usize(futex_time64( + uaddr, + op as i32 | flags.bits() as i32, + val, + utime, + uaddr2, + val3, + )) + .or_else(|err| { + // 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) + } else { + Err(err) + } + }) + } + + #[cfg(any( + target_pointer_width = "64", + target_arch = "aarch64", + target_arch = "x86_64" + ))] + { + syscall! { + fn futex( + uaddr: *mut u32, + futex_op: c::c_int, + val: u32, + timeout: *const linux_raw_sys::general::__kernel_timespec, + uaddr2: *mut u32, + val3: u32 + ) via SYS_futex -> c::c_long + } + + ret_usize(futex( + uaddr, + op as i32 | flags.bits() as i32, + val, + utime.cast(), + uaddr2, + val3, + ) as isize) + } +} + +#[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, + val: u32, + utime: *const Timespec, + uaddr2: *mut u32, + val3: u32, +) -> io::Result<usize> { + syscall! { + fn futex( + uaddr: *mut u32, + futex_op: c::c_int, + val: u32, + timeout: *const linux_raw_sys::general::__kernel_old_timespec, + uaddr2: *mut u32, + 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)?, + }; + ret_usize(futex( + uaddr, + op as i32 | flags.bits() as i32, + val, + &old_utime, + uaddr2, + val3, + ) as isize) +} diff --git a/vendor/rustix/src/backend/libc/time/mod.rs b/vendor/rustix/src/backend/libc/time/mod.rs new file mode 100644 index 0000000..bff7fd5 --- /dev/null +++ b/vendor/rustix/src/backend/libc/time/mod.rs @@ -0,0 +1,3 @@ +#[cfg(not(windows))] +pub(crate) mod syscalls; +pub(crate) mod types; diff --git a/vendor/rustix/src/backend/libc/time/syscalls.rs b/vendor/rustix/src/backend/libc/time/syscalls.rs new file mode 100644 index 0000000..6b1c9fd --- /dev/null +++ b/vendor/rustix/src/backend/libc/time/syscalls.rs @@ -0,0 +1,452 @@ +//! libc syscalls supporting `rustix::time`. + +use crate::backend::c; +use crate::backend::conv::ret; +#[cfg(any(linux_kernel, target_os = "fuchsia"))] +#[cfg(feature = "time")] +#[cfg(any(all(target_env = "gnu", fix_y2038), not(fix_y2038)))] +use crate::backend::time::types::LibcItimerspec; +#[cfg(not(target_os = "wasi"))] +use crate::clockid::{ClockId, DynamicClockId}; +use crate::io; +#[cfg(all(target_env = "gnu", fix_y2038))] +use crate::timespec::LibcTimespec; +use crate::timespec::Timespec; +use core::mem::MaybeUninit; +#[cfg(any(linux_kernel, target_os = "fuchsia"))] +#[cfg(feature = "time")] +use { + crate::backend::conv::{borrowed_fd, ret_owned_fd}, + crate::fd::{BorrowedFd, OwnedFd}, + crate::time::{Itimerspec, TimerfdClockId, TimerfdFlags, TimerfdTimerFlags}, +}; + +#[cfg(all(target_env = "gnu", fix_y2038))] +weak!(fn __clock_gettime64(c::clockid_t, *mut LibcTimespec) -> c::c_int); +#[cfg(all(target_env = "gnu", fix_y2038))] +weak!(fn __clock_settime64(c::clockid_t, *const LibcTimespec) -> c::c_int); +#[cfg(all(target_env = "gnu", fix_y2038))] +weak!(fn __clock_getres64(c::clockid_t, *mut LibcTimespec) -> c::c_int); +#[cfg(any(linux_kernel, target_os = "fuchsia"))] +#[cfg(all(target_env = "gnu", fix_y2038))] +#[cfg(feature = "time")] +weak!(fn __timerfd_gettime64(c::c_int, *mut LibcItimerspec) -> c::c_int); +#[cfg(any(linux_kernel, target_os = "fuchsia"))] +#[cfg(all(target_env = "gnu", fix_y2038))] +#[cfg(feature = "time")] +weak!(fn __timerfd_settime64(c::c_int, c::c_int, *const LibcItimerspec, *mut LibcItimerspec) -> c::c_int); + +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] +#[inline] +#[must_use] +pub(crate) fn clock_getres(id: ClockId) -> Timespec { + // Old 32-bit version: libc has `clock_getres` but it is not y2038 safe by + // default. But there may be a `__clock_getres64` we can use. + #[cfg(fix_y2038)] + { + #[cfg(target_env = "gnu")] + if let Some(libc_clock_getres) = __clock_getres64.get() { + let mut timespec = MaybeUninit::<LibcTimespec>::uninit(); + unsafe { + ret(libc_clock_getres(id as c::clockid_t, timespec.as_mut_ptr())).unwrap(); + return timespec.assume_init().into(); + } + } + + clock_getres_old(id) + } + + // Main version: libc is y2038 safe and has `clock_getres`. + #[cfg(not(fix_y2038))] + unsafe { + let mut timespec = MaybeUninit::<Timespec>::uninit(); + let _ = c::clock_getres(id as c::clockid_t, timespec.as_mut_ptr()); + timespec.assume_init() + } +} + +#[cfg(fix_y2038)] +#[must_use] +fn clock_getres_old(id: ClockId) -> Timespec { + let mut old_timespec = MaybeUninit::<c::timespec>::uninit(); + + let old_timespec = unsafe { + ret(c::clock_getres( + id as c::clockid_t, + old_timespec.as_mut_ptr(), + )) + .unwrap(); + old_timespec.assume_init() + }; + + Timespec { + tv_sec: old_timespec.tv_sec.into(), + tv_nsec: old_timespec.tv_nsec.into(), + } +} + +#[cfg(not(target_os = "wasi"))] +#[inline] +#[must_use] +pub(crate) fn clock_gettime(id: ClockId) -> Timespec { + // Old 32-bit version: libc has `clock_gettime` but it is not y2038 safe by + // default. But there may be a `__clock_gettime64` we can use. + #[cfg(fix_y2038)] + { + #[cfg(target_env = "gnu")] + if let Some(libc_clock_gettime) = __clock_gettime64.get() { + let mut timespec = MaybeUninit::<LibcTimespec>::uninit(); + unsafe { + ret(libc_clock_gettime( + id as c::clockid_t, + timespec.as_mut_ptr(), + )) + .unwrap(); + return timespec.assume_init().into(); + } + } + + clock_gettime_old(id) + } + + // Use `.unwrap()` here because `clock_getres` can fail if the clock itself + // overflows a number of seconds, but if that happens, the monotonic clocks + // can't maintain their invariants, or the realtime clocks aren't properly + // configured. + #[cfg(not(fix_y2038))] + unsafe { + let mut timespec = MaybeUninit::<Timespec>::uninit(); + ret(c::clock_gettime(id as c::clockid_t, timespec.as_mut_ptr())).unwrap(); + timespec.assume_init() + } +} + +#[cfg(fix_y2038)] +#[must_use] +fn clock_gettime_old(id: ClockId) -> Timespec { + let mut old_timespec = MaybeUninit::<c::timespec>::uninit(); + + let old_timespec = unsafe { + ret(c::clock_gettime( + id as c::clockid_t, + old_timespec.as_mut_ptr(), + )) + .unwrap(); + old_timespec.assume_init() + }; + + Timespec { + tv_sec: old_timespec.tv_sec.into(), + tv_nsec: old_timespec.tv_nsec.into(), + } +} + +#[cfg(not(target_os = "wasi"))] +#[inline] +pub(crate) fn clock_gettime_dynamic(id: DynamicClockId<'_>) -> io::Result<Timespec> { + let id: c::clockid_t = match id { + DynamicClockId::Known(id) => id as c::clockid_t, + + #[cfg(linux_kernel)] + DynamicClockId::Dynamic(fd) => { + use crate::fd::AsRawFd; + const CLOCKFD: i32 = 3; + (!fd.as_raw_fd() << 3) | CLOCKFD + } + + #[cfg(not(linux_kernel))] + DynamicClockId::Dynamic(_fd) => { + // Dynamic clocks are not supported on this platform. + return Err(io::Errno::INVAL); + } + + #[cfg(linux_kernel)] + DynamicClockId::RealtimeAlarm => c::CLOCK_REALTIME_ALARM, + + #[cfg(linux_kernel)] + DynamicClockId::Tai => c::CLOCK_TAI, + + #[cfg(any( + freebsdlike, + linux_kernel, + target_os = "fuchsia", + target_os = "openbsd" + ))] + DynamicClockId::Boottime => c::CLOCK_BOOTTIME, + + #[cfg(any(linux_kernel, target_os = "fuchsia"))] + DynamicClockId::BoottimeAlarm => c::CLOCK_BOOTTIME_ALARM, + }; + + // Old 32-bit version: libc has `clock_gettime` but it is not y2038 + // safe by default. But there may be a `__clock_gettime64` we can use. + #[cfg(fix_y2038)] + { + #[cfg(target_env = "gnu")] + if let Some(libc_clock_gettime) = __clock_gettime64.get() { + let mut timespec = MaybeUninit::<LibcTimespec>::uninit(); + unsafe { + ret(libc_clock_gettime( + id as c::clockid_t, + timespec.as_mut_ptr(), + ))?; + + return Ok(timespec.assume_init().into()); + } + } + + clock_gettime_dynamic_old(id) + } + + // Main version: libc is y2038 safe and has `clock_gettime`. + #[cfg(not(fix_y2038))] + unsafe { + let mut timespec = MaybeUninit::<Timespec>::uninit(); + + ret(c::clock_gettime(id as c::clockid_t, timespec.as_mut_ptr()))?; + + Ok(timespec.assume_init()) + } +} + +#[cfg(fix_y2038)] +#[inline] +fn clock_gettime_dynamic_old(id: c::clockid_t) -> io::Result<Timespec> { + let mut old_timespec = MaybeUninit::<c::timespec>::uninit(); + + let old_timespec = unsafe { + ret(c::clock_gettime( + id as c::clockid_t, + old_timespec.as_mut_ptr(), + ))?; + + old_timespec.assume_init() + }; + + Ok(Timespec { + tv_sec: old_timespec.tv_sec.into(), + tv_nsec: old_timespec.tv_nsec.into(), + }) +} + +#[cfg(not(any( + target_os = "redox", + target_os = "wasi", + all(apple, not(target_os = "macos")) +)))] +#[inline] +pub(crate) fn clock_settime(id: ClockId, timespec: Timespec) -> io::Result<()> { + // Old 32-bit version: libc has `clock_gettime` but it is not y2038 safe by + // default. But there may be a `__clock_settime64` we can use. + #[cfg(fix_y2038)] + { + #[cfg(target_env = "gnu")] + if let Some(libc_clock_settime) = __clock_settime64.get() { + unsafe { + let mut new_timespec = core::mem::zeroed::<LibcTimespec>(); + new_timespec.tv_sec = timespec.tv_sec; + new_timespec.tv_nsec = timespec.tv_nsec as _; + return ret(libc_clock_settime(id as c::clockid_t, &new_timespec)); + } + } + + clock_settime_old(id, timespec) + } + + // Main version: libc is y2038 safe and has `clock_settime`. + #[cfg(not(fix_y2038))] + unsafe { + ret(c::clock_settime(id as c::clockid_t, ×pec)) + } +} + +#[cfg(not(any( + target_os = "redox", + target_os = "wasi", + all(apple, not(target_os = "macos")) +)))] +#[cfg(fix_y2038)] +fn clock_settime_old(id: ClockId, timespec: Timespec) -> io::Result<()> { + let old_timespec = c::timespec { + tv_sec: timespec + .tv_sec + .try_into() + .map_err(|_| io::Errno::OVERFLOW)?, + tv_nsec: timespec.tv_nsec as _, + }; + + unsafe { ret(c::clock_settime(id as c::clockid_t, &old_timespec)) } +} + +#[cfg(any(linux_kernel, target_os = "fuchsia"))] +#[cfg(feature = "time")] +pub(crate) fn timerfd_create(id: TimerfdClockId, flags: TimerfdFlags) -> io::Result<OwnedFd> { + unsafe { ret_owned_fd(c::timerfd_create(id as c::clockid_t, bitflags_bits!(flags))) } +} + +#[cfg(any(linux_kernel, target_os = "fuchsia"))] +#[cfg(feature = "time")] +pub(crate) fn timerfd_settime( + fd: BorrowedFd<'_>, + flags: TimerfdTimerFlags, + new_value: &Itimerspec, +) -> io::Result<Itimerspec> { + // Old 32-bit version: libc has `timerfd_settime` but it is not y2038 safe + // by default. But there may be a `__timerfd_settime64` we can use. + #[cfg(fix_y2038)] + { + #[cfg(target_env = "gnu")] + if let Some(libc_timerfd_settime) = __timerfd_settime64.get() { + let mut result = MaybeUninit::<LibcItimerspec>::uninit(); + unsafe { + ret(libc_timerfd_settime( + borrowed_fd(fd), + bitflags_bits!(flags), + &new_value.clone().into(), + result.as_mut_ptr(), + ))?; + return Ok(result.assume_init().into()); + } + } + + timerfd_settime_old(fd, flags, new_value) + } + + #[cfg(not(fix_y2038))] + unsafe { + let mut result = MaybeUninit::<LibcItimerspec>::uninit(); + ret(c::timerfd_settime( + borrowed_fd(fd), + bitflags_bits!(flags), + new_value, + result.as_mut_ptr(), + ))?; + Ok(result.assume_init()) + } +} + +#[cfg(any(linux_kernel, target_os = "fuchsia"))] +#[cfg(fix_y2038)] +#[cfg(feature = "time")] +fn timerfd_settime_old( + fd: BorrowedFd<'_>, + flags: TimerfdTimerFlags, + new_value: &Itimerspec, +) -> io::Result<Itimerspec> { + let mut old_result = MaybeUninit::<c::itimerspec>::uninit(); + + // Convert `new_value` to the old `itimerspec` format. + let old_new_value = c::itimerspec { + it_interval: c::timespec { + tv_sec: new_value + .it_interval + .tv_sec + .try_into() + .map_err(|_| io::Errno::OVERFLOW)?, + tv_nsec: new_value + .it_interval + .tv_nsec + .try_into() + .map_err(|_| io::Errno::INVAL)?, + }, + it_value: c::timespec { + tv_sec: new_value + .it_value + .tv_sec + .try_into() + .map_err(|_| io::Errno::OVERFLOW)?, + tv_nsec: new_value + .it_value + .tv_nsec + .try_into() + .map_err(|_| io::Errno::INVAL)?, + }, + }; + + let old_result = unsafe { + ret(c::timerfd_settime( + borrowed_fd(fd), + bitflags_bits!(flags), + &old_new_value, + old_result.as_mut_ptr(), + ))?; + old_result.assume_init() + }; + + Ok(Itimerspec { + it_interval: Timespec { + tv_sec: old_result + .it_interval + .tv_sec + .try_into() + .map_err(|_| io::Errno::OVERFLOW)?, + tv_nsec: old_result.it_interval.tv_nsec as _, + }, + it_value: Timespec { + tv_sec: old_result + .it_interval + .tv_sec + .try_into() + .map_err(|_| io::Errno::OVERFLOW)?, + tv_nsec: old_result.it_interval.tv_nsec as _, + }, + }) +} + +#[cfg(any(linux_kernel, target_os = "fuchsia"))] +#[cfg(feature = "time")] +pub(crate) fn timerfd_gettime(fd: BorrowedFd<'_>) -> io::Result<Itimerspec> { + // Old 32-bit version: libc has `timerfd_gettime` but it is not y2038 safe + // by default. But there may be a `__timerfd_gettime64` we can use. + #[cfg(fix_y2038)] + { + #[cfg(target_env = "gnu")] + if let Some(libc_timerfd_gettime) = __timerfd_gettime64.get() { + let mut result = MaybeUninit::<LibcItimerspec>::uninit(); + unsafe { + ret(libc_timerfd_gettime(borrowed_fd(fd), result.as_mut_ptr()))?; + return Ok(result.assume_init().into()); + } + } + + timerfd_gettime_old(fd) + } + + #[cfg(not(fix_y2038))] + unsafe { + let mut result = MaybeUninit::<LibcItimerspec>::uninit(); + ret(c::timerfd_gettime(borrowed_fd(fd), result.as_mut_ptr()))?; + Ok(result.assume_init()) + } +} + +#[cfg(any(linux_kernel, target_os = "fuchsia"))] +#[cfg(fix_y2038)] +#[cfg(feature = "time")] +fn timerfd_gettime_old(fd: BorrowedFd<'_>) -> io::Result<Itimerspec> { + let mut old_result = MaybeUninit::<c::itimerspec>::uninit(); + + let old_result = unsafe { + ret(c::timerfd_gettime(borrowed_fd(fd), old_result.as_mut_ptr()))?; + old_result.assume_init() + }; + + Ok(Itimerspec { + it_interval: Timespec { + tv_sec: old_result + .it_interval + .tv_sec + .try_into() + .map_err(|_| io::Errno::OVERFLOW)?, + tv_nsec: old_result.it_interval.tv_nsec as _, + }, + it_value: Timespec { + tv_sec: old_result + .it_interval + .tv_sec + .try_into() + .map_err(|_| io::Errno::OVERFLOW)?, + tv_nsec: old_result.it_interval.tv_nsec as _, + }, + }) +} diff --git a/vendor/rustix/src/backend/libc/time/types.rs b/vendor/rustix/src/backend/libc/time/types.rs new file mode 100644 index 0000000..5254c6b --- /dev/null +++ b/vendor/rustix/src/backend/libc/time/types.rs @@ -0,0 +1,177 @@ +#[cfg(any(linux_kernel, target_os = "fuchsia"))] +use crate::backend::c; +#[cfg(any(linux_kernel, target_os = "fuchsia"))] +#[cfg(fix_y2038)] +use crate::timespec::LibcTimespec; +#[cfg(any(linux_kernel, target_os = "fuchsia"))] +#[cfg(fix_y2038)] +use crate::timespec::Timespec; +#[cfg(any(linux_kernel, target_os = "fuchsia"))] +use bitflags::bitflags; + +/// `struct itimerspec` for use with [`timerfd_gettime`] and +/// [`timerfd_settime`]. +/// +/// [`timerfd_gettime`]: crate::time::timerfd_gettime +/// [`timerfd_settime`]: crate::time::timerfd_settime +#[cfg(any(linux_kernel, target_os = "fuchsia"))] +#[cfg(not(fix_y2038))] +pub type Itimerspec = c::itimerspec; + +/// `struct itimerspec` for use with [`timerfd_gettime`] and +/// [`timerfd_settime`]. +/// +/// [`timerfd_gettime`]: crate::time::timerfd_gettime +/// [`timerfd_settime`]: crate::time::timerfd_settime +#[cfg(any(linux_kernel, target_os = "fuchsia"))] +#[cfg(fix_y2038)] +#[repr(C)] +#[derive(Debug, Clone)] +pub struct Itimerspec { + /// The interval of an interval timer. + pub it_interval: Timespec, + /// Time remaining in the current interval. + pub it_value: Timespec, +} + +/// On most platforms, `LibcItimerspec` is just `Itimerspec`. +#[cfg(any(linux_kernel, target_os = "fuchsia"))] +#[cfg(not(fix_y2038))] +pub(crate) type LibcItimerspec = Itimerspec; + +/// On 32-bit glibc platforms, `LibcTimespec` differs from `Timespec`, so we +/// define our own struct, with bidirectional `From` impls. +#[cfg(any(linux_kernel, target_os = "fuchsia"))] +#[cfg(fix_y2038)] +#[repr(C)] +#[derive(Debug, Clone)] +pub(crate) struct LibcItimerspec { + pub it_interval: LibcTimespec, + pub it_value: LibcTimespec, +} + +#[cfg(any(linux_kernel, target_os = "fuchsia"))] +#[cfg(fix_y2038)] +impl From<LibcItimerspec> for Itimerspec { + #[inline] + fn from(t: LibcItimerspec) -> Self { + Self { + it_interval: t.it_interval.into(), + it_value: t.it_value.into(), + } + } +} + +#[cfg(any(linux_kernel, target_os = "fuchsia"))] +#[cfg(fix_y2038)] +impl From<Itimerspec> for LibcItimerspec { + #[inline] + fn from(t: Itimerspec) -> Self { + Self { + it_interval: t.it_interval.into(), + it_value: t.it_value.into(), + } + } +} + +#[cfg(any(linux_kernel, target_os = "fuchsia"))] +bitflags! { + /// `TFD_*` flags for use with [`timerfd_create`]. + /// + /// [`timerfd_create`]: crate::time::timerfd_create + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct TimerfdFlags: u32 { + /// `TFD_NONBLOCK` + #[doc(alias = "TFD_NONBLOCK")] + const NONBLOCK = bitcast!(c::TFD_NONBLOCK); + + /// `TFD_CLOEXEC` + #[doc(alias = "TFD_CLOEXEC")] + const CLOEXEC = bitcast!(c::TFD_CLOEXEC); + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} + +#[cfg(any(linux_kernel, target_os = "fuchsia"))] +bitflags! { + /// `TFD_TIMER_*` flags for use with [`timerfd_settime`]. + /// + /// [`timerfd_settime`]: crate::time::timerfd_settime + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct TimerfdTimerFlags: u32 { + /// `TFD_TIMER_ABSTIME` + #[doc(alias = "TFD_TIMER_ABSTIME")] + const ABSTIME = bitcast!(c::TFD_TIMER_ABSTIME); + + /// `TFD_TIMER_CANCEL_ON_SET` + #[cfg(linux_kernel)] + #[doc(alias = "TFD_TIMER_CANCEL_ON_SET")] + const CANCEL_ON_SET = bitcast!(c::TFD_TIMER_CANCEL_ON_SET); + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} + +/// `CLOCK_*` constants for use with [`timerfd_create`]. +/// +/// [`timerfd_create`]: crate::time::timerfd_create +#[cfg(any(linux_kernel, target_os = "fuchsia"))] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +#[repr(u32)] +#[non_exhaustive] +pub enum TimerfdClockId { + /// `CLOCK_REALTIME`—A clock that tells the “real” time. + /// + /// This is a clock that tells the amount of time elapsed since the Unix + /// epoch, 1970-01-01T00:00:00Z. The clock is externally settable, so it is + /// not monotonic. Successive reads may see decreasing times, so it isn't + /// reliable for measuring durations. + #[doc(alias = "CLOCK_REALTIME")] + Realtime = bitcast!(c::CLOCK_REALTIME), + + /// `CLOCK_MONOTONIC`—A clock that tells an abstract time. + /// + /// Unlike `Realtime`, this clock is not based on a fixed known epoch, so + /// individual times aren't meaningful. However, since it isn't settable, + /// it is reliable for measuring durations. + /// + /// This clock does not advance while the system is suspended; see + /// `Boottime` for a clock that does. + #[doc(alias = "CLOCK_MONOTONIC")] + Monotonic = bitcast!(c::CLOCK_MONOTONIC), + + /// `CLOCK_BOOTTIME`—Like `Monotonic`, but advances while suspended. + /// + /// This clock is similar to `Monotonic`, but does advance while the system + /// is suspended. + #[doc(alias = "CLOCK_BOOTTIME")] + Boottime = bitcast!(c::CLOCK_BOOTTIME), + + /// `CLOCK_REALTIME_ALARM`—Like `Realtime`, but wakes a suspended system. + /// + /// This clock is like `Realtime`, but can wake up a suspended system. + /// + /// Use of this clock requires the `CAP_WAKE_ALARM` Linux capability. + #[doc(alias = "CLOCK_REALTIME_ALARM")] + RealtimeAlarm = bitcast!(c::CLOCK_REALTIME_ALARM), + + /// `CLOCK_BOOTTIME_ALARM`—Like `Boottime`, but wakes a suspended system. + /// + /// This clock is like `Boottime`, but can wake up a suspended system. + /// + /// Use of this clock requires the `CAP_WAKE_ALARM` Linux capability. + #[doc(alias = "CLOCK_BOOTTIME_ALARM")] + BoottimeAlarm = bitcast!(c::CLOCK_BOOTTIME_ALARM), +} + +#[cfg(any(linux_kernel, target_os = "fuchsia"))] +#[test] +fn test_types() { + assert_eq_size!(TimerfdFlags, c::c_int); + assert_eq_size!(TimerfdTimerFlags, c::c_int); +} diff --git a/vendor/rustix/src/backend/libc/ugid/mod.rs b/vendor/rustix/src/backend/libc/ugid/mod.rs new file mode 100644 index 0000000..ef944f0 --- /dev/null +++ b/vendor/rustix/src/backend/libc/ugid/mod.rs @@ -0,0 +1 @@ +pub(crate) mod syscalls; diff --git a/vendor/rustix/src/backend/libc/ugid/syscalls.rs b/vendor/rustix/src/backend/libc/ugid/syscalls.rs new file mode 100644 index 0000000..0d3f622 --- /dev/null +++ b/vendor/rustix/src/backend/libc/ugid/syscalls.rs @@ -0,0 +1,42 @@ +use crate::backend::c; +use crate::ugid::{Gid, Uid}; + +#[cfg(not(target_os = "wasi"))] +#[inline] +#[must_use] +pub(crate) fn getuid() -> Uid { + unsafe { + let uid = c::getuid(); + Uid::from_raw(uid) + } +} + +#[cfg(not(target_os = "wasi"))] +#[inline] +#[must_use] +pub(crate) fn geteuid() -> Uid { + unsafe { + let uid = c::geteuid(); + Uid::from_raw(uid) + } +} + +#[cfg(not(target_os = "wasi"))] +#[inline] +#[must_use] +pub(crate) fn getgid() -> Gid { + unsafe { + let gid = c::getgid(); + Gid::from_raw(gid) + } +} + +#[cfg(not(target_os = "wasi"))] +#[inline] +#[must_use] +pub(crate) fn getegid() -> Gid { + unsafe { + let gid = c::getegid(); + Gid::from_raw(gid) + } +} diff --git a/vendor/rustix/src/backend/libc/winsock_c.rs b/vendor/rustix/src/backend/libc/winsock_c.rs new file mode 100644 index 0000000..ee2704a --- /dev/null +++ b/vendor/rustix/src/backend/libc/winsock_c.rs @@ -0,0 +1,59 @@ +//! Adapt the Winsock API to resemble a POSIX-style libc API. + +#![allow(unused_imports)] +#![allow(non_camel_case_types)] +#![allow(dead_code)] + +use windows_sys::Win32::Networking::WinSock; + +// Define the basic C types. With Rust 1.64, we can use these from `core::ffi`. +pub(crate) type c_schar = i8; +pub(crate) type c_uchar = u8; +pub(crate) type c_short = i16; +pub(crate) type c_ushort = u16; +pub(crate) type c_int = i32; +pub(crate) type c_uint = u32; +pub(crate) type c_longlong = i64; +pub(crate) type c_ulonglong = u64; +pub(crate) type ssize_t = isize; +pub(crate) type c_char = i8; +pub(crate) type c_long = i32; +pub(crate) type c_ulong = u32; +pub(crate) use core::ffi::c_void; + +// windows-sys declares these constants as u16. For better compatibility +// with Unix-family APIs, redeclare them as u32. +pub(crate) const AF_INET: i32 = WinSock::AF_INET as _; +pub(crate) const AF_INET6: i32 = WinSock::AF_INET6 as _; +pub(crate) const AF_UNSPEC: i32 = WinSock::AF_UNSPEC as _; + +// Include the contents of `WinSock`, renaming as needed to match POSIX. +// +// Use `WSA_E_CANCELLED` for `ECANCELED` instead of `WSAECANCELLED`, because +// `WSAECANCELLED` will be removed in the future. +// <https://docs.microsoft.com/en-us/windows/win32/api/ws2spi/nc-ws2spi-lpnsplookupserviceend#remarks> +pub(crate) use WinSock::{ + closesocket as close, ioctlsocket as ioctl, WSAPoll as poll, ADDRESS_FAMILY as sa_family_t, + ADDRINFOA as addrinfo, IN6_ADDR as in6_addr, IN_ADDR as in_addr, IPV6_MREQ as ipv6_mreq, + IP_MREQ as ip_mreq, LINGER as linger, SD_BOTH as SHUT_RDWR, SD_RECEIVE as SHUT_RD, + SD_SEND as SHUT_WR, SOCKADDR as sockaddr, SOCKADDR_IN as sockaddr_in, + SOCKADDR_IN6 as sockaddr_in6, SOCKADDR_STORAGE as sockaddr_storage, WSAEACCES as EACCES, + WSAEADDRINUSE as EADDRINUSE, WSAEADDRNOTAVAIL as EADDRNOTAVAIL, + WSAEAFNOSUPPORT as EAFNOSUPPORT, WSAEALREADY as EALREADY, WSAEBADF as EBADF, + WSAECONNABORTED as ECONNABORTED, WSAECONNREFUSED as ECONNREFUSED, WSAECONNRESET as ECONNRESET, + WSAEDESTADDRREQ as EDESTADDRREQ, WSAEDISCON as EDISCON, WSAEDQUOT as EDQUOT, + WSAEFAULT as EFAULT, WSAEHOSTDOWN as EHOSTDOWN, WSAEHOSTUNREACH as EHOSTUNREACH, + WSAEINPROGRESS as EINPROGRESS, WSAEINTR as EINTR, WSAEINVAL as EINVAL, + WSAEINVALIDPROCTABLE as EINVALIDPROCTABLE, WSAEINVALIDPROVIDER as EINVALIDPROVIDER, + WSAEISCONN as EISCONN, WSAELOOP as ELOOP, WSAEMFILE as EMFILE, WSAEMSGSIZE as EMSGSIZE, + WSAENAMETOOLONG as ENAMETOOLONG, WSAENETDOWN as ENETDOWN, WSAENETRESET as ENETRESET, + WSAENETUNREACH as ENETUNREACH, WSAENOBUFS as ENOBUFS, WSAENOMORE as ENOMORE, + WSAENOPROTOOPT as ENOPROTOOPT, WSAENOTCONN as ENOTCONN, WSAENOTEMPTY as ENOTEMPTY, + WSAENOTSOCK as ENOTSOCK, WSAEOPNOTSUPP as EOPNOTSUPP, WSAEPFNOSUPPORT as EPFNOSUPPORT, + WSAEPROCLIM as EPROCLIM, WSAEPROTONOSUPPORT as EPROTONOSUPPORT, WSAEPROTOTYPE as EPROTOTYPE, + WSAEPROVIDERFAILEDINIT as EPROVIDERFAILEDINIT, WSAEREFUSED as EREFUSED, WSAEREMOTE as EREMOTE, + WSAESHUTDOWN as ESHUTDOWN, WSAESOCKTNOSUPPORT as ESOCKTNOSUPPORT, WSAESTALE as ESTALE, + WSAETIMEDOUT as ETIMEDOUT, WSAETOOMANYREFS as ETOOMANYREFS, WSAEUSERS as EUSERS, + WSAEWOULDBLOCK as EWOULDBLOCK, WSAEWOULDBLOCK as EAGAIN, WSAPOLLFD as pollfd, + WSA_E_CANCELLED as ECANCELED, *, +}; |