Vendor dependencies for 0.3.0 release

This commit is contained in:
2025-09-27 10:29:08 -05:00
parent 0c8d39d483
commit 82ab7f317b
26803 changed files with 16134934 additions and 0 deletions

595
vendor/rustix/src/backend/libc/c.rs vendored Normal file
View File

@@ -0,0 +1,595 @@
//! 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_raw_dep, feature = "net"))]
pub(crate) const ETH_P_TSN: c_int = linux_raw_sys::if_ether::ETH_P_TSN as _;
#[cfg(all(linux_raw_dep, feature = "net"))]
pub(crate) const ETH_P_ERSPAN2: c_int = linux_raw_sys::if_ether::ETH_P_ERSPAN2 as _;
#[cfg(all(linux_raw_dep, feature = "net"))]
pub(crate) const ETH_P_ERSPAN: c_int = linux_raw_sys::if_ether::ETH_P_ERSPAN as _;
#[cfg(all(linux_raw_dep, feature = "net"))]
pub(crate) const ETH_P_PROFINET: c_int = linux_raw_sys::if_ether::ETH_P_PROFINET as _;
#[cfg(all(linux_raw_dep, feature = "net"))]
pub(crate) const ETH_P_REALTEK: c_int = linux_raw_sys::if_ether::ETH_P_REALTEK as _;
#[cfg(all(linux_raw_dep, feature = "net"))]
pub(crate) const ETH_P_ETHERCAT: c_int = linux_raw_sys::if_ether::ETH_P_ETHERCAT as _;
#[cfg(all(linux_raw_dep, feature = "net"))]
pub(crate) const ETH_P_PREAUTH: c_int = linux_raw_sys::if_ether::ETH_P_PREAUTH as _;
#[cfg(all(linux_raw_dep, feature = "net"))]
pub(crate) const ETH_P_LLDP: c_int = linux_raw_sys::if_ether::ETH_P_LLDP as _;
#[cfg(all(linux_raw_dep, feature = "net"))]
pub(crate) const ETH_P_MRP: c_int = linux_raw_sys::if_ether::ETH_P_MRP as _;
#[cfg(all(linux_raw_dep, feature = "net"))]
pub(crate) const ETH_P_NCSI: c_int = linux_raw_sys::if_ether::ETH_P_NCSI as _;
#[cfg(all(linux_raw_dep, feature = "net"))]
pub(crate) const ETH_P_CFM: c_int = linux_raw_sys::if_ether::ETH_P_CFM as _;
#[cfg(all(linux_raw_dep, feature = "net"))]
pub(crate) const ETH_P_IBOE: c_int = linux_raw_sys::if_ether::ETH_P_IBOE as _;
#[cfg(all(linux_raw_dep, feature = "net"))]
pub(crate) const ETH_P_HSR: c_int = linux_raw_sys::if_ether::ETH_P_HSR as _;
#[cfg(all(linux_raw_dep, feature = "net"))]
pub(crate) const ETH_P_NSH: c_int = linux_raw_sys::if_ether::ETH_P_NSH as _;
#[cfg(all(linux_raw_dep, feature = "net"))]
pub(crate) const ETH_P_DSA_8021Q: c_int = linux_raw_sys::if_ether::ETH_P_DSA_8021Q as _;
#[cfg(all(linux_raw_dep, feature = "net"))]
pub(crate) const ETH_P_DSA_A5PSW: c_int = linux_raw_sys::if_ether::ETH_P_DSA_A5PSW as _;
#[cfg(all(linux_raw_dep, feature = "net"))]
pub(crate) const ETH_P_IFE: c_int = linux_raw_sys::if_ether::ETH_P_IFE as _;
#[cfg(all(linux_raw_dep, feature = "net"))]
pub(crate) const ETH_P_CAN: c_int = linux_raw_sys::if_ether::ETH_P_CAN as _;
#[cfg(all(linux_raw_dep, feature = "net"))]
pub(crate) const ETH_P_CANXL: c_int = linux_raw_sys::if_ether::ETH_P_CANXL as _;
#[cfg(all(linux_raw_dep, feature = "net"))]
pub(crate) const ETH_P_XDSA: c_int = linux_raw_sys::if_ether::ETH_P_XDSA as _;
#[cfg(all(linux_raw_dep, feature = "net"))]
pub(crate) const ETH_P_MAP: c_int = linux_raw_sys::if_ether::ETH_P_MAP as _;
#[cfg(all(linux_raw_dep, feature = "net"))]
pub(crate) const ETH_P_MCTP: c_int = linux_raw_sys::if_ether::ETH_P_MCTP as _;
#[cfg(all(linux_raw_dep, feature = "mount"))]
pub(crate) const MS_NOSYMFOLLOW: c_ulong = linux_raw_sys::general::MS_NOSYMFOLLOW 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_raw_dep, feature = "termios"))]
pub(crate) const IUCLC: tcflag_t = linux_raw_sys::general::IUCLC as _;
#[cfg(all(linux_raw_dep, 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 = MSG_NONBLOCK;
// `O_LARGEFILE` can be automatically set by the kernel on Linux:
// <https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/fs/open.c?h=v6.13#n1423>
// so libc implementations may leave it undefined or defined to zero.
#[cfg(linux_raw_dep)]
pub(crate) const O_LARGEFILE: c_int = linux_raw_sys::general::O_LARGEFILE as _;
// Gated under `_LARGEFILE_SOURCE` but automatically set by the kernel.
// <https://github.com/illumos/illumos-gate/blob/fb2cb638e5604b214d8ea8d4f01ad2e77b437c17/usr/src/ucbhead/sys/fcntl.h#L64>
#[cfg(solarish)]
pub(crate) const O_LARGEFILE: c_int = 0x2000;
// On PowerPC, the regular `termios` has the `termios2` fields and there is no
// `termios2`, so we define aliases.
#[cfg(all(
linux_kernel,
feature = "termios",
any(target_arch = "powerpc", target_arch = "powerpc64")
))]
pub(crate) use {
termios as termios2, TCGETS as TCGETS2, TCSETS as TCSETS2, TCSETSF as TCSETSF2,
TCSETSW as TCSETSW2,
};
// And PowerPC doesn't define `CIBAUD`, but it does define `IBSHIFT`, so we can
// compute `CIBAUD` ourselves.
#[cfg(all(
linux_kernel,
feature = "termios",
any(target_arch = "powerpc", target_arch = "powerpc64")
))]
pub(crate) const CIBAUD: u32 = CBAUD << IBSHIFT;
// Automatically enable “large file” support (LFS) features.
#[cfg(target_os = "vxworks")]
pub(super) use _Vx_ticks64_t as _Vx_ticks_t;
#[cfg(linux_kernel)]
pub(super) use fallocate64 as fallocate;
#[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))]
#[cfg(any(linux_like, target_os = "aix"))]
pub(super) use open64 as open;
#[cfg(any(
linux_kernel,
target_os = "aix",
target_os = "hurd",
target_os = "l4re"
))]
pub(super) use posix_fallocate64 as posix_fallocate;
#[cfg(any(all(linux_like, not(target_os = "android")), target_os = "aix"))]
pub(super) use {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 {
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 {
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 {
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 {lstat64 as lstat, stat64 as stat};
#[cfg(any(
linux_kernel,
target_os = "aix",
target_os = "hurd",
target_os = "emscripten"
))]
pub(super) use {pread64 as pread, pwrite64 as pwrite};
#[cfg(any(target_os = "linux", target_os = "hurd", target_os = "emscripten"))]
pub(super) use {preadv64 as preadv, pwritev64 as pwritev};
#[cfg(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")))]
pub(super) unsafe fn prlimit(
pid: pid_t,
resource: __rlimit_resource_t,
new_limit: *const rlimit64,
old_limit: *mut rlimit64,
) -> c_int {
// `prlimit64` wasn't supported in glibc until 2.13.
weak_or_syscall! {
fn prlimit64(
pid: pid_t,
resource: __rlimit_resource_t,
new_limit: *const rlimit64,
old_limit: *mut rlimit64
) via SYS_prlimit64 -> c_int
}
prlimit64(pid, resource, new_limit, old_limit)
}
#[cfg(all(target_os = "linux", target_env = "musl"))]
pub(super) unsafe fn prlimit(
pid: pid_t,
resource: c_int,
new_limit: *const rlimit64,
old_limit: *mut rlimit64,
) -> c_int {
weak_or_syscall! {
fn prlimit64(
pid: pid_t,
resource: c_int,
new_limit: *const rlimit64,
old_limit: *mut rlimit64
) via SYS_prlimit64 -> c_int
}
prlimit64(pid, resource, new_limit, old_limit)
}
#[cfg(target_os = "android")]
pub(super) unsafe fn prlimit(
pid: pid_t,
resource: c_int,
new_limit: *const rlimit64,
old_limit: *mut rlimit64,
) -> c_int {
weak_or_syscall! {
fn prlimit64(
pid: pid_t,
resource: c_int,
new_limit: *const rlimit64,
old_limit: *mut rlimit64
) via SYS_prlimit64 -> 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: c_int,
iov: *const iovec,
iovcnt: c_int,
offset: off64_t,
) -> ssize_t {
// Older Android libc lacks `preadv64`, so use the `weak!` mechanism to
// test for it, and call back to `syscall`. We don't use
// `weak_or_syscall` here because we need to pass the 64-bit offset
// specially.
weak! {
fn preadv64(c_int, *const iovec, c_int, off64_t) -> 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: c_int,
iov: *const iovec,
iovcnt: c_int,
offset_lo: usize,
offset_hi: usize
) via SYS_preadv -> ssize_t
}
preadv(fd, iov, iovcnt, offset as usize, (offset >> 32) as usize)
}
}
pub(in super::super) unsafe fn pwritev64(
fd: c_int,
iov: *const iovec,
iovcnt: c_int,
offset: off64_t,
) -> ssize_t {
// See the comments in `preadv64`.
weak! {
fn pwritev64(c_int, *const iovec, c_int, off64_t) -> 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: c_int,
iov: *const iovec,
iovcnt: c_int,
offset_lo: usize,
offset_hi: usize
) via SYS_pwritev -> 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 {
use super::*;
weakcall! {
pub(in super::super) fn preadv(
fd: c_int,
iov: *const iovec,
iovcnt: c_int,
offset: off_t
) -> ssize_t
}
weakcall! {
pub(in super::super) fn pwritev(
fd: c_int,
iov: *const iovec,
iovcnt: c_int, offset: off_t
) -> 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: c_int,
iov: *const iovec,
iovcnt: c_int,
offset: off64_t,
flags: c_int,
) -> ssize_t {
// Older glibc lacks `preadv64v2`, so use the `weak!` mechanism to
// test for it, and call back to `syscall`. We don't use
// `weak_or_syscall` here because we need to pass the 64-bit offset
// specially.
weak! {
fn preadv64v2(c_int, *const iovec, c_int, off64_t, c_int) -> 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: c_int,
iov: *const iovec,
iovcnt: c_int,
offset_lo: usize,
offset_hi: usize,
flags: c_int
) via SYS_preadv2 -> ssize_t
}
preadv2(
fd,
iov,
iovcnt,
offset as usize,
(offset >> 32) as usize,
flags,
)
}
}
pub(in super::super) unsafe fn pwritev64v2(
fd: c_int,
iov: *const iovec,
iovcnt: c_int,
offset: off64_t,
flags: c_int,
) -> ssize_t {
// See the comments in `preadv64v2`.
weak! {
fn pwritev64v2(c_int, *const iovec, c_int, off64_t, c_int) -> 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: c_int,
iov: *const iovec,
iovec: c_int,
offset_lo: usize,
offset_hi: usize,
flags: c_int
) via SYS_pwritev2 -> 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: c_int,
iov: *const iovec,
iovcnt: c_int,
offset: off64_t,
flags: c_int,
) -> 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: c_int,
iov: *const iovec,
iovcnt: c_int,
offset_lo: usize,
offset_hi: usize,
flags: c_int
) via SYS_preadv2 -> ssize_t
}
preadv2(
fd,
iov,
iovcnt,
offset as usize,
(offset >> 32) as usize,
flags,
)
}
pub(in super::super) unsafe fn pwritev64v2(
fd: c_int,
iov: *const iovec,
iovcnt: c_int,
offset: off64_t,
flags: c_int,
) -> 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: c_int,
iov: *const iovec,
iovcnt: c_int,
offset_lo: usize,
offset_hi: usize,
flags: c_int
) via SYS_pwritev2 -> 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};
// Rust's libc crate lacks statx for Non-glibc targets.
#[cfg(feature = "fs")]
#[cfg(all(
linux_like,
linux_raw_dep,
not(any(
target_os = "emscripten",
target_env = "gnu",
all(target_arch = "loongarch64", target_env = "musl")
))
))]
mod statx_flags {
pub(crate) use linux_raw_sys::general::{
STATX_ALL, STATX_ATIME, STATX_BASIC_STATS, STATX_BLOCKS, STATX_BTIME, STATX_CTIME,
STATX_DIOALIGN, STATX_GID, STATX_INO, STATX_MNT_ID, STATX_MODE, STATX_MTIME, STATX_NLINK,
STATX_SIZE, STATX_TYPE, STATX_UID,
};
pub(crate) use linux_raw_sys::general::{
STATX_ATTR_APPEND, STATX_ATTR_AUTOMOUNT, STATX_ATTR_COMPRESSED, STATX_ATTR_DAX,
STATX_ATTR_ENCRYPTED, STATX_ATTR_IMMUTABLE, STATX_ATTR_MOUNT_ROOT, STATX_ATTR_NODUMP,
STATX_ATTR_VERITY,
};
}
#[cfg(feature = "fs")]
#[cfg(all(
linux_like,
linux_raw_dep,
not(any(
target_os = "emscripten",
target_env = "gnu",
all(target_arch = "loongarch64", target_env = "musl")
))
))]
pub(crate) use statx_flags::*;
#[cfg(feature = "fs")]
#[cfg(target_os = "android")]
pub(crate) use __fsid_t as fsid_t;
// FreeBSD added `timerfd_*` in FreeBSD 14. NetBSD added then in NetBSD 10.
#[cfg(all(feature = "time", any(target_os = "freebsd", target_os = "netbsd")))]
syscall!(pub(crate) fn timerfd_create(
clockid: c_int,
flags: c_int
) via SYS_timerfd_create -> c_int);
#[cfg(all(feature = "time", any(target_os = "freebsd", target_os = "netbsd")))]
syscall!(pub(crate) fn timerfd_gettime(
fd: c_int,
curr_value: *mut itimerspec
) via SYS_timerfd_gettime -> c_int);
#[cfg(all(feature = "time", any(target_os = "freebsd", target_os = "netbsd")))]
syscall!(pub(crate) fn timerfd_settime(
fd: c_int,
flags: c_int,
new_value: *const itimerspec,
old_value: *mut itimerspec
) via SYS_timerfd_settime -> c_int);
#[cfg(all(feature = "time", target_os = "illumos"))]
extern "C" {
pub(crate) fn timerfd_create(clockid: c_int, flags: c_int) -> c_int;
pub(crate) fn timerfd_gettime(fd: c_int, curr_value: *mut itimerspec) -> c_int;
pub(crate) fn timerfd_settime(
fd: c_int,
flags: c_int,
new_value: *const itimerspec,
old_value: *mut itimerspec,
) -> c_int;
}
// illumos and NetBSD timerfd support.
// Submitted upstream in <https://github.com/rust-lang/libc/pull/4333>.
// <https://code.illumos.org/plugins/gitiles/illumos-gate/+/refs/heads/master/usr/src/uts/common/sys/timerfd.h#34>
#[cfg(all(feature = "time", target_os = "illumos"))]
pub(crate) const TFD_CLOEXEC: i32 = 0o2000000;
#[cfg(all(feature = "time", target_os = "illumos"))]
pub(crate) const TFD_NONBLOCK: i32 = 0o4000;
#[cfg(all(feature = "time", target_os = "illumos"))]
pub(crate) const TFD_TIMER_ABSTIME: i32 = 1 << 0;
#[cfg(all(feature = "time", target_os = "illumos"))]
pub(crate) const TFD_TIMER_CANCEL_ON_SET: i32 = 1 << 1;
// <https://nxr.netbsd.org/xref/src/sys/sys/timerfd.h#44>
#[cfg(all(feature = "time", target_os = "netbsd"))]
pub(crate) const TFD_CLOEXEC: i32 = O_CLOEXEC;
#[cfg(all(feature = "time", target_os = "netbsd"))]
pub(crate) const TFD_NONBLOCK: i32 = O_NONBLOCK;
#[cfg(all(feature = "time", target_os = "netbsd"))]
pub(crate) const TFD_TIMER_ABSTIME: i32 = O_WRONLY;
#[cfg(all(feature = "time", target_os = "netbsd"))]
pub(crate) const TFD_TIMER_CANCEL_ON_SET: i32 = O_RDWR;
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[cfg(linux_kernel)]
fn test_flags() {
// libc may publicly define `O_LARGEFILE` to 0, but we want the real
// non-zero value.
assert_ne!(O_LARGEFILE, 0);
}
}

188
vendor/rustix/src/backend/libc/conv.rs vendored Normal file
View File

@@ -0,0 +1,188 @@
//! 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 as _;
use super::fd::{AsRawFd as _, BorrowedFd, FromRawFd as _, 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().cast()
}
#[cfg(not(any(
windows,
target_os = "espidf",
target_os = "horizon",
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(any(
linux_kernel,
all(target_os = "illumos", feature = "event"),
all(target_os = "redox", feature = "event")
))]
#[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 = "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 = "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)
}

View File

@@ -0,0 +1,74 @@
use crate::backend::c;
use bitflags::bitflags;
bitflags! {
/// `EPOLL_*` for use with [`epoll::create`].
///
/// [`epoll::create`]: crate::event::epoll::create
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct CreateFlags: u32 {
/// `EPOLL_CLOEXEC`
const CLOEXEC = bitcast!(c::EPOLL_CLOEXEC);
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}
bitflags! {
/// `EPOLL*` for use with [`epoll::add`].
///
/// [`epoll::add`]: crate::event::epoll::add
#[repr(transparent)]
#[derive(Default, Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct EventFlags: u32 {
/// `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`
const EXCLUSIVE = bitcast!(c::EPOLLEXCLUSIVE);
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}

View File

@@ -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(any(linux_kernel, target_os = "illumos", target_os = "redox"))]
pub mod epoll;

View File

@@ -0,0 +1,143 @@
use crate::backend::c;
use crate::backend::conv::borrowed_fd;
use crate::backend::fd::{AsFd, AsRawFd as _, BorrowedFd, LibcFd};
#[cfg(windows)]
use crate::backend::fd::{AsSocket, RawFd};
use crate::ffi;
use bitflags::bitflags;
use core::fmt;
use core::marker::PhantomData;
bitflags! {
/// `POLL*` flags for use with [`poll`].
///
/// [`poll`]: crate::event::poll
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct PollFlags: ffi::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(any(
target_os = "freebsd",
target_os = "illumos",
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)]
#[repr(transparent)]
pub struct PollFd<'fd> {
pollfd: c::pollfd,
_phantom: PhantomData<BorrowedFd<'fd>>,
}
impl<'fd> fmt::Debug for PollFd<'fd> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("PollFd")
.field("fd", &self.pollfd.fd)
.field("events", &self.pollfd.events)
.field("revents", &self.pollfd.revents)
.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) }
}
}

View File

@@ -0,0 +1,631 @@
//! libc syscalls supporting `rustix::event`.
use crate::backend::c;
#[cfg(any(linux_kernel, solarish, target_os = "redox"))]
use crate::backend::conv::ret;
use crate::backend::conv::ret_c_int;
#[cfg(any(linux_kernel, target_os = "illumos", target_os = "redox"))]
use crate::backend::conv::ret_u32;
#[cfg(bsd)]
use crate::event::kqueue::Event;
#[cfg(solarish)]
use crate::event::port::Event;
#[cfg(any(
linux_kernel,
target_os = "freebsd",
target_os = "illumos",
target_os = "espidf"
))]
use crate::event::EventfdFlags;
#[cfg(any(bsd, linux_kernel, target_os = "wasi"))]
use crate::event::FdSetElement;
use crate::event::{PollFd, Timespec};
use crate::io;
#[cfg(any(linux_kernel, target_os = "illumos", target_os = "redox"))]
use crate::utils::as_ptr;
#[cfg(solarish)]
use core::mem::MaybeUninit;
#[cfg(any(
bsd,
linux_kernel,
target_os = "fuchsia",
target_os = "haiku",
target_os = "hurd",
target_os = "netbsd",
target_os = "wasi"
))]
use core::ptr::null;
#[cfg(any(bsd, linux_kernel, solarish, target_os = "redox", target_os = "wasi"))]
use core::ptr::null_mut;
#[cfg(any(bsd, linux_kernel, solarish, target_os = "redox"))]
use {crate::backend::conv::borrowed_fd, crate::fd::BorrowedFd};
#[cfg(any(
bsd,
linux_kernel,
solarish,
target_os = "freebsd",
target_os = "illumos",
target_os = "espidf",
target_os = "redox"
))]
use {crate::backend::conv::ret_owned_fd, crate::fd::OwnedFd};
#[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(bsd)]
pub(crate) fn kqueue() -> io::Result<OwnedFd> {
unsafe { ret_owned_fd(c::kqueue()) }
}
#[cfg(bsd)]
pub(crate) unsafe fn kevent(
kq: BorrowedFd<'_>,
changelist: &[Event],
eventlist: (*mut Event, usize),
timeout: Option<&Timespec>,
) -> io::Result<c::c_int> {
// If we don't have to fix y2038 on this platform, `Timespec` is the same
// as `c::timespec` and it's easy.
#[cfg(not(fix_y2038))]
let timeout = crate::timespec::option_as_libc_timespec_ptr(timeout);
// If we do have to fix y2038 on this platform, convert to `c::timespec`.
#[cfg(fix_y2038)]
let converted_timeout;
#[cfg(fix_y2038)]
let timeout = match timeout {
None => null(),
Some(timeout) => {
converted_timeout = c::timespec {
tv_sec: timeout.tv_sec.try_into().map_err(|_| io::Errno::OVERFLOW)?,
tv_nsec: timeout.tv_nsec as _,
};
&converted_timeout
}
};
ret_c_int(c::kevent(
borrowed_fd(kq),
changelist.as_ptr().cast(),
changelist
.len()
.try_into()
.map_err(|_| io::Errno::OVERFLOW)?,
eventlist.0.cast(),
eventlist.1.try_into().map_err(|_| io::Errno::OVERFLOW)?,
timeout,
))
}
#[inline]
pub(crate) fn poll(fds: &mut [PollFd<'_>], timeout: Option<&Timespec>) -> io::Result<usize> {
let nfds = fds
.len()
.try_into()
.map_err(|_convert_err| io::Errno::INVAL)?;
// If we have `ppoll`, it supports a `timespec` timeout, so use it.
#[cfg(any(
linux_kernel,
freebsdlike,
target_os = "fuchsia",
target_os = "haiku",
target_os = "hurd",
target_os = "netbsd"
))]
{
// If we don't have to fix y2038 on this platform, `Timespec` is
// the same as `c::timespec` and it's easy.
#[cfg(not(fix_y2038))]
let timeout = crate::timespec::option_as_libc_timespec_ptr(timeout);
// If we do have to fix y2038 on this platform, convert to
// `c::timespec`.
#[cfg(fix_y2038)]
let converted_timeout;
#[cfg(fix_y2038)]
let timeout = match timeout {
None => null(),
Some(timeout) => {
converted_timeout = c::timespec {
tv_sec: timeout.tv_sec.try_into().map_err(|_| io::Errno::OVERFLOW)?,
tv_nsec: timeout.tv_nsec as _,
};
&converted_timeout
}
};
#[cfg(not(target_os = "netbsd"))]
{
ret_c_int(unsafe { c::ppoll(fds.as_mut_ptr().cast(), nfds, timeout, null()) })
.map(|nready| nready as usize)
}
// NetBSD 9.x lacks `ppoll`, so use a weak symbol and fall back to
// plain `poll` if needed.
#[cfg(target_os = "netbsd")]
{
weak! {
fn ppoll(
*mut c::pollfd,
c::nfds_t,
*const c::timespec,
*const c::sigset_t
) -> c::c_int
}
if let Some(func) = ppoll.get() {
return ret_c_int(unsafe { func(fds.as_mut_ptr().cast(), nfds, timeout, null()) })
.map(|nready| nready as usize);
}
}
}
// If we don't have `ppoll`, convert the timeout to `c_int` and use `poll`.
#[cfg(not(any(
linux_kernel,
freebsdlike,
target_os = "fuchsia",
target_os = "haiku",
target_os = "hurd"
)))]
{
let timeout = match timeout {
None => -1,
Some(timeout) => timeout.as_c_int_millis().ok_or(io::Errno::INVAL)?,
};
ret_c_int(unsafe { c::poll(fds.as_mut_ptr().cast(), nfds, timeout) })
.map(|nready| nready as usize)
}
}
#[cfg(any(bsd, linux_kernel))]
pub(crate) unsafe fn select(
nfds: i32,
readfds: Option<&mut [FdSetElement]>,
writefds: Option<&mut [FdSetElement]>,
exceptfds: Option<&mut [FdSetElement]>,
timeout: Option<&Timespec>,
) -> io::Result<i32> {
let len = crate::event::fd_set_num_elements_for_bitvector(nfds);
let readfds = match readfds {
Some(readfds) => {
assert!(readfds.len() >= len);
readfds.as_mut_ptr()
}
None => null_mut(),
};
let writefds = match writefds {
Some(writefds) => {
assert!(writefds.len() >= len);
writefds.as_mut_ptr()
}
None => null_mut(),
};
let exceptfds = match exceptfds {
Some(exceptfds) => {
assert!(exceptfds.len() >= len);
exceptfds.as_mut_ptr()
}
None => null_mut(),
};
let timeout_data;
let timeout_ptr = match timeout {
Some(timeout) => {
// Convert from `Timespec` to `c::timeval`.
timeout_data = c::timeval {
tv_sec: timeout.tv_sec.try_into().map_err(|_| io::Errno::INVAL)?,
tv_usec: ((timeout.tv_nsec + 999) / 1000) as _,
};
&timeout_data
}
None => null(),
};
// On Apple platforms, use the specially mangled `select` which doesn't
// have an `FD_SETSIZE` limitation.
#[cfg(apple)]
{
extern "C" {
#[link_name = "select$DARWIN_EXTSN$NOCANCEL"]
fn select(
nfds: c::c_int,
readfds: *mut FdSetElement,
writefds: *mut FdSetElement,
errorfds: *mut FdSetElement,
timeout: *const c::timeval,
) -> c::c_int;
}
ret_c_int(select(nfds, readfds, writefds, exceptfds, timeout_ptr))
}
// Otherwise just use the normal `select`.
#[cfg(not(apple))]
{
ret_c_int(c::select(
nfds,
readfds.cast(),
writefds.cast(),
exceptfds.cast(),
timeout_ptr as *mut c::timeval,
))
}
}
// WASI uses a count + array instead of a bitvector.
#[cfg(target_os = "wasi")]
pub(crate) unsafe fn select(
nfds: i32,
readfds: Option<&mut [FdSetElement]>,
writefds: Option<&mut [FdSetElement]>,
exceptfds: Option<&mut [FdSetElement]>,
timeout: Option<&Timespec>,
) -> io::Result<i32> {
let len = crate::event::fd_set_num_elements_for_fd_array(nfds as usize);
let readfds = match readfds {
Some(readfds) => {
assert!(readfds.len() >= len);
readfds.as_mut_ptr()
}
None => null_mut(),
};
let writefds = match writefds {
Some(writefds) => {
assert!(writefds.len() >= len);
writefds.as_mut_ptr()
}
None => null_mut(),
};
let exceptfds = match exceptfds {
Some(exceptfds) => {
assert!(exceptfds.len() >= len);
exceptfds.as_mut_ptr()
}
None => null_mut(),
};
let timeout_data;
let timeout_ptr = match timeout {
Some(timeout) => {
// Convert from `Timespec` to `c::timeval`.
timeout_data = c::timeval {
tv_sec: timeout.tv_sec.try_into().map_err(|_| io::Errno::INVAL)?,
tv_usec: ((timeout.tv_nsec + 999) / 1000) as _,
};
&timeout_data
}
None => null(),
};
ret_c_int(c::select(
nfds,
readfds.cast(),
writefds.cast(),
exceptfds.cast(),
timeout_ptr as *mut c::timeval,
))
}
#[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<&Timespec>) -> io::Result<Event> {
// If we don't have to fix y2038 on this platform, `Timespec` is
// the same as `c::timespec` and it's easy.
#[cfg(not(fix_y2038))]
let timeout = crate::timespec::option_as_libc_timespec_ptr(timeout);
// If we do have to fix y2038 on this platform, convert to
// `c::timespec`.
#[cfg(fix_y2038)]
let converted_timeout;
#[cfg(fix_y2038)]
let timeout = match timeout {
None => null(),
Some(timeout) => {
converted_timeout = c::timespec {
tv_sec: timeout.tv_sec.try_into().map_err(|_| io::Errno::OVERFLOW)?,
tv_nsec: timeout.tv_nsec as _,
};
&converted_timeout
}
};
let mut event = MaybeUninit::<c::port_event>::uninit();
// In Rust ≥ 1.65, the `as _` can be `.cast_mut()`.
unsafe {
ret(c::port_get(
borrowed_fd(port),
event.as_mut_ptr(),
timeout as _,
))?;
}
// If we're done, initialize the event and return it.
Ok(Event(unsafe { event.assume_init() }))
}
#[cfg(solarish)]
pub(crate) unsafe fn port_getn(
port: BorrowedFd<'_>,
events: (*mut Event, usize),
mut nget: u32,
timeout: Option<&Timespec>,
) -> io::Result<usize> {
// If we don't have to fix y2038 on this platform, `Timespec` is
// the same as `c::timespec` and it's easy.
#[cfg(not(fix_y2038))]
let timeout = crate::timespec::option_as_libc_timespec_ptr(timeout);
// If we do have to fix y2038 on this platform, convert to
// `c::timespec`.
#[cfg(fix_y2038)]
let converted_timeout;
#[cfg(fix_y2038)]
let timeout = match timeout {
None => null(),
Some(timeout) => {
converted_timeout = c::timespec {
tv_sec: timeout.tv_sec.try_into().map_err(|_| io::Errno::OVERFLOW)?,
tv_nsec: timeout.tv_nsec as _,
};
&converted_timeout
}
};
// `port_getn` special-cases a max value of 0 to be a query that returns
// the number of events, so bail out early if needed.
if events.1 == 0 {
return Ok(0);
}
// In Rust ≥ 1.65, the `as _` can be `.cast_mut()`.
ret(c::port_getn(
borrowed_fd(port),
events.0.cast(),
events.1.try_into().unwrap_or(u32::MAX),
&mut nget,
timeout as _,
))?;
Ok(nget as usize)
}
#[cfg(solarish)]
pub(crate) fn port_getn_query(port: BorrowedFd<'_>) -> io::Result<u32> {
let mut nget: u32 = 0;
// Pass a `max` of 0 to query the number of available events.
unsafe {
ret(c::port_getn(
borrowed_fd(port),
null_mut(),
0,
&mut nget,
null_mut(),
))?;
}
Ok(nget)
}
#[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(target_os = "redox", target_os = "wasi")))]
pub(crate) fn pause() {
let r = unsafe { c::pause() };
let errno = libc_errno::errno().0;
debug_assert_eq!(r, -1);
debug_assert_eq!(errno, c::EINTR);
}
#[inline]
#[cfg(any(linux_kernel, target_os = "illumos", target_os = "redox"))]
pub(crate) fn epoll_create(flags: super::epoll::CreateFlags) -> io::Result<OwnedFd> {
unsafe { ret_owned_fd(c::epoll_create1(bitflags_bits!(flags))) }
}
#[inline]
#[cfg(any(linux_kernel, target_os = "illumos", target_os = "redox"))]
pub(crate) fn epoll_add(
epoll: BorrowedFd<'_>,
source: BorrowedFd<'_>,
event: &crate::event::epoll::Event,
) -> io::Result<()> {
// We use our own `Event` struct instead of libc's because
// ours preserves pointer provenance instead of just using a `u64`,
// and we have tests elsewhere for layout equivalence.
unsafe {
ret(c::epoll_ctl(
borrowed_fd(epoll),
c::EPOLL_CTL_ADD,
borrowed_fd(source),
// The event is read-only even though libc has a non-const pointer.
as_ptr(event) as *mut c::epoll_event,
))
}
}
#[inline]
#[cfg(any(linux_kernel, target_os = "illumos", target_os = "redox"))]
pub(crate) fn epoll_mod(
epoll: BorrowedFd<'_>,
source: BorrowedFd<'_>,
event: &crate::event::epoll::Event,
) -> io::Result<()> {
unsafe {
ret(c::epoll_ctl(
borrowed_fd(epoll),
c::EPOLL_CTL_MOD,
borrowed_fd(source),
// The event is read-only even though libc has a non-const pointer.
as_ptr(event) as *mut c::epoll_event,
))
}
}
#[inline]
#[cfg(any(linux_kernel, target_os = "illumos", target_os = "redox"))]
pub(crate) fn epoll_del(epoll: BorrowedFd<'_>, source: BorrowedFd<'_>) -> io::Result<()> {
unsafe {
ret(c::epoll_ctl(
borrowed_fd(epoll),
c::EPOLL_CTL_DEL,
borrowed_fd(source),
null_mut(),
))
}
}
#[inline]
#[cfg(any(linux_kernel, target_os = "illumos", target_os = "redox"))]
pub(crate) unsafe fn epoll_wait(
epoll: BorrowedFd<'_>,
events: (*mut crate::event::epoll::Event, usize),
timeout: Option<&Timespec>,
) -> io::Result<usize> {
// If we're on Linux ≥ 5.11 and a libc that has an `epoll_pwait2`
// function, and it's y2038-safe, use it.
#[cfg(all(
linux_kernel,
feature = "linux_5_11",
target_env = "gnu",
not(fix_y2038)
))]
{
weak! {
fn epoll_pwait2(
c::c_int,
*mut c::epoll_event,
c::c_int,
*const c::timespec,
*const c::sigset_t
) -> c::c_int
}
if let Some(epoll_pwait2_func) = epoll_pwait2.get() {
return ret_u32(epoll_pwait2_func(
borrowed_fd(epoll),
events.0.cast::<c::epoll_event>(),
events.1.try_into().unwrap_or(i32::MAX),
crate::utils::option_as_ptr(timeout).cast(),
null(),
))
.map(|i| i as usize);
}
}
// If we're on Linux ≥ 5.11, use `epoll_pwait2` via `libc::syscall`.
#[cfg(all(linux_kernel, feature = "linux_5_11"))]
{
syscall! {
fn epoll_pwait2(
epfd: c::c_int,
events: *mut c::epoll_event,
maxevents: c::c_int,
timeout: *const Timespec,
sigmask: *const c::sigset_t
) via SYS_epoll_pwait2 -> c::c_int
}
ret_u32(epoll_pwait2(
borrowed_fd(epoll),
events.0.cast::<c::epoll_event>(),
events.1.try_into().unwrap_or(i32::MAX),
crate::utils::option_as_ptr(timeout).cast(),
null(),
))
.map(|i| i as usize)
}
// Otherwise just use `epoll_wait`.
#[cfg(not(all(linux_kernel, feature = "linux_5_11")))]
{
let timeout = match timeout {
None => -1,
Some(timeout) => timeout.as_c_int_millis().ok_or(io::Errno::INVAL)?,
};
ret_u32(c::epoll_wait(
borrowed_fd(epoll),
events.0.cast::<c::epoll_event>(),
events.1.try_into().unwrap_or(i32::MAX),
timeout,
))
.map(|i| i as usize)
}
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,79 @@
//! Windows system calls in the `event` module.
use crate::backend::c;
use crate::backend::conv::ret_c_int;
use crate::event::{FdSetElement, PollFd, Timespec};
use crate::io;
pub(crate) fn poll(fds: &mut [PollFd<'_>], timeout: Option<&Timespec>) -> io::Result<usize> {
let nfds = fds
.len()
.try_into()
.map_err(|_convert_err| io::Errno::INVAL)?;
let timeout = match timeout {
None => -1,
Some(timeout) => timeout.as_c_int_millis().ok_or(io::Errno::INVAL)?,
};
ret_c_int(unsafe { c::poll(fds.as_mut_ptr().cast(), nfds, timeout) })
.map(|nready| nready as usize)
}
pub(crate) fn select(
nfds: i32,
readfds: Option<&mut [FdSetElement]>,
writefds: Option<&mut [FdSetElement]>,
exceptfds: Option<&mut [FdSetElement]>,
timeout: Option<&crate::timespec::Timespec>,
) -> io::Result<i32> {
use core::ptr::{null, null_mut};
let readfds = match readfds {
Some(readfds) => {
assert!(readfds.len() >= readfds[0].0 as usize);
readfds.as_mut_ptr()
}
None => null_mut(),
};
let writefds = match writefds {
Some(writefds) => {
assert!(writefds.len() >= writefds[0].0 as usize);
writefds.as_mut_ptr()
}
None => null_mut(),
};
let exceptfds = match exceptfds {
Some(exceptfds) => {
assert!(exceptfds.len() >= exceptfds[0].0 as usize);
exceptfds.as_mut_ptr()
}
None => null_mut(),
};
let timeout_data;
let timeout_ptr = match timeout {
Some(timeout) => {
// Convert from `Timespec` to `TIMEVAL`.
timeout_data = c::TIMEVAL {
tv_sec: timeout
.tv_sec
.try_into()
.map_err(|_| io::Errno::OPNOTSUPP)?,
tv_usec: ((timeout.tv_nsec + 999) / 1000) as _,
};
&timeout_data
}
None => null(),
};
unsafe {
ret_c_int(c::select(
nfds,
readfds.cast(),
writefds.cast(),
exceptfds.cast(),
timeout_ptr,
))
}
}

489
vendor/rustix/src/backend/libc/fs/dir.rs vendored Normal file
View File

@@ -0,0 +1,489 @@
#[cfg(not(any(
solarish,
target_os = "aix",
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 = "horizon",
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 = "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 as _;
#[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()) }
}
/// `seekdir(self, offset)`
///
/// This function is only available on 64-bit platforms because it's
/// implemented using [`libc::seekdir`] which only supports offsets that
/// fit in a `c_long`.
///
/// [`libc::seekdir`]: https://docs.rs/libc/*/arm-unknown-linux-gnueabihf/libc/fn.seekdir.html
#[cfg(target_pointer_width = "64")]
#[cfg_attr(docsrs, doc(cfg(target_pointer_width = "64")))]
#[doc(alias = "seekdir")]
#[inline]
pub fn seek(&mut self, offset: i64) -> io::Result<()> {
self.any_errors = false;
unsafe { c::seekdir(self.libc_dir.as_ptr(), offset) }
Ok(())
}
/// `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 anything
// 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(
linux_like,
solarish,
target_os = "fuchsia",
target_os = "hermit",
target_os = "openbsd",
target_os = "redox"
))]
d_off: dirent.d_off,
#[cfg(any(freebsdlike, netbsdlike))]
d_fileno: dirent.d_fileno,
name: CStr::from_ptr(dirent.d_name.as_ptr().cast()).to_owned(),
};
Some(Ok(result))
}
}
}
/// `fstat(self)`
#[cfg(not(any(target_os = "horizon", 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 = "horizon",
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 = "horizon",
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 = "horizon",
target_os = "vita",
target_os = "wasi"
)))]
#[cfg_attr(docsrs, doc(cfg(feature = "process")))]
#[inline]
pub fn chdir(&self) -> io::Result<()> {
fchdir(unsafe { BorrowedFd::borrow_raw(c::dirfd(self.libc_dir.as_ptr())) })
}
}
/// `Dir` is `Send` and `Sync`, because even though it contains internal
/// state, all methods that modify the state require a `mut &self` and
/// can therefore not be called concurrently. Calling them from different
/// threads sequentially is fine.
unsafe impl Send for Dir {}
unsafe impl Sync 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(any(target_os = "horizon", 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,
#[cfg(any(
linux_like,
solarish,
target_os = "fuchsia",
target_os = "hermit",
target_os = "openbsd",
target_os = "redox"
))]
d_off: c::off_t,
}
impl DirEntry {
/// Returns the file name of this directory entry.
#[inline]
pub fn file_name(&self) -> &CStr {
&self.name
}
/// Returns the “offset” of this directory entry. This is not a true
/// numerical offset but an opaque cookie that identifies a position in the
/// given stream.
#[cfg(any(
linux_like,
solarish,
target_os = "fuchsia",
target_os = "hermit",
target_os = "openbsd",
target_os = "redox"
))]
#[inline]
pub fn offset(&self) -> i64 {
self.d_off as i64
}
/// 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,
)
}
);
}
#[cfg(test)]
mod tests {
use super::*;
#[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());
}
}

View File

@@ -0,0 +1,124 @@
//! inotify support for working with inotify objects.
use crate::backend::c;
use bitflags::bitflags;
bitflags! {
/// `IN_*` for use with [`inotify::init`].
///
/// [`inotify::init`]: crate::fs::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::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;
}
}
bitflags! {
/// `IN*` for use with [`inotify::Reader`].
///
/// [`inotify::Reader`]: crate::fs::inotify::Reader
#[repr(transparent)]
#[derive(Default, Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct ReadFlags: u32 {
/// `IN_ACCESS`
const ACCESS = c::IN_ACCESS;
/// `IN_ATTRIB`
const ATTRIB = c::IN_ATTRIB;
/// `IN_CLOSE_NOWRITE`
const CLOSE_NOWRITE = c::IN_CLOSE_NOWRITE;
/// `IN_CLOSE_WRITE`
const CLOSE_WRITE = c::IN_CLOSE_WRITE;
/// `IN_CREATE`
const CREATE = c::IN_CREATE;
/// `IN_DELETE`
const DELETE = c::IN_DELETE;
/// `IN_DELETE_SELF`
const DELETE_SELF = c::IN_DELETE_SELF;
/// `IN_MODIFY`
const MODIFY = c::IN_MODIFY;
/// `IN_MOVE_SELF`
const MOVE_SELF = c::IN_MOVE_SELF;
/// `IN_MOVED_FROM`
const MOVED_FROM = c::IN_MOVED_FROM;
/// `IN_MOVED_TO`
const MOVED_TO = c::IN_MOVED_TO;
/// `IN_OPEN`
const OPEN = c::IN_OPEN;
/// `IN_IGNORED`
const IGNORED = c::IN_IGNORED;
/// `IN_ISDIR`
const ISDIR = c::IN_ISDIR;
/// `IN_Q_OVERFLOW`
const QUEUE_OVERFLOW = c::IN_Q_OVERFLOW;
/// `IN_UNMOUNT`
const UNMOUNT = c::IN_UNMOUNT;
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}

View File

@@ -0,0 +1,144 @@
// TODO: Remove the unsafe blocks. libc 0.2.171 removed `unsafe` from several
// of these functions. Eventually we should depend on that version and remove
// the `unsafe` blocks in the code, but for now, disable that warning so that
// we're compatible with older libc versions.
#![allow(unused_unsafe)]
#[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) }
}

View File

@@ -0,0 +1,27 @@
#[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 = "horizon",
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_raw_dep, any(target_arch = "sparc", target_arch = "sparc64")))]
pub(crate) const EXT4_IOC_RESIZE_FS: crate::ioctl::Opcode = 0x8008_6610;
#[cfg(all(
linux_raw_dep,
not(any(target_arch = "sparc", target_arch = "sparc64"))
))]
pub(crate) const EXT4_IOC_RESIZE_FS: crate::ioctl::Opcode =
linux_raw_sys::ioctl::EXT4_IOC_RESIZE_FS as crate::ioctl::Opcode;

File diff suppressed because it is too large Load Diff

1153
vendor/rustix/src/backend/libc/fs/types.rs vendored Normal file

File diff suppressed because it is too large Load Diff

1114
vendor/rustix/src/backend/libc/io/errno.rs vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -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;

View File

@@ -0,0 +1,303 @@
//! 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 as _, 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(all(linux_kernel, not(target_os = "android")))]
use crate::io::ReadWriteFlags;
use crate::io::{self, FdFlags};
use crate::ioctl::{IoctlOutput, Opcode};
use core::cmp::min;
#[cfg(not(any(target_os = "espidf", target_os = "horizon")))]
use {
crate::backend::MAX_IOV,
crate::io::{IoSlice, IoSliceMut},
};
pub(crate) unsafe fn read(fd: BorrowedFd<'_>, buf: (*mut u8, usize)) -> io::Result<usize> {
ret_usize(c::read(
borrowed_fd(fd),
buf.0.cast(),
min(buf.1, 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, usize),
offset: u64,
) -> io::Result<usize> {
let len = min(buf.1, 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, for example.
let offset = offset.try_into().map_err(|_| io::Errno::OVERFLOW)?;
ret_usize(c::pread(borrowed_fd(fd), buf.0.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, for example.
let offset = offset.try_into().map_err(|_| io::Errno::OVERFLOW)?;
unsafe { ret_usize(c::pwrite(borrowed_fd(fd), buf.as_ptr().cast(), len, offset)) }
}
#[cfg(not(any(target_os = "espidf", target_os = "horizon")))]
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(any(target_os = "espidf", target_os = "horizon")))]
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 = "cygwin",
target_os = "espidf",
target_os = "haiku",
target_os = "horizon",
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;
// ESP-IDF and Vita don't support 64-bit offsets, for example.
let offset = offset.try_into().map_err(|_| io::Errno::OVERFLOW)?;
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 = "cygwin",
target_os = "espidf",
target_os = "haiku",
target_os = "nto",
target_os = "horizon",
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;
// ESP-IDF and Vita don't support 64-bit offsets, for example.
let offset = offset.try_into().map_err(|_| io::Errno::OVERFLOW)?;
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(all(linux_kernel, not(target_os = "android")))]
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(all(linux_kernel, not(target_os = "android")))]
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);
}
#[cfg(feature = "try_close")]
pub(crate) unsafe fn try_close(raw_fd: RawFd) -> io::Result<()> {
ret(c::close(raw_fd as c::c_int))
}
#[inline]
pub(crate) unsafe fn ioctl(
fd: BorrowedFd<'_>,
request: Opcode,
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: Opcode,
arg: *mut c::c_void,
) -> io::Result<IoctlOutput> {
ioctl(fd, request, arg)
}
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 = "horizon",
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)
}

View File

@@ -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(all(linux_kernel, not(target_os = "android")))]
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 = libc::RWF_DSYNC as u32;
/// `RWF_HIPRI` (since Linux 4.6)
const HIPRI = libc::RWF_HIPRI as u32;
/// `RWF_SYNC` (since Linux 4.7)
const SYNC = libc::RWF_SYNC as u32;
/// `RWF_NOWAIT` (since Linux 4.14)
const NOWAIT = libc::RWF_NOWAIT as u32;
/// `RWF_APPEND` (since Linux 4.16)
const APPEND = libc::RWF_APPEND as u32;
/// <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;
}
}

View File

@@ -0,0 +1,58 @@
//! Windows system calls in the `io` module.
use crate::backend::c;
#[cfg(feature = "try_close")]
use crate::backend::conv::ret;
use crate::backend::conv::{borrowed_fd, ret_c_int, ret_send_recv, send_recv_len};
use crate::fd::{BorrowedFd, RawFd};
use crate::io;
use crate::ioctl::{IoctlOutput, Opcode};
pub(crate) unsafe fn read(fd: BorrowedFd<'_>, buf: (*mut u8, usize)) -> io::Result<usize> {
// `read` on a socket is equivalent to `recv` with no flags.
ret_send_recv(c::recv(
borrowed_fd(fd),
buf.0.cast(),
send_recv_len(buf.1),
0,
))
}
pub(crate) fn write(fd: BorrowedFd<'_>, buf: &[u8]) -> io::Result<usize> {
// `write` on a socket is equivalent to `send` with no flags.
unsafe {
ret_send_recv(c::send(
borrowed_fd(fd),
buf.as_ptr().cast(),
send_recv_len(buf.len()),
0,
))
}
}
pub(crate) unsafe fn close(raw_fd: RawFd) {
let _ = c::closesocket(raw_fd as c::SOCKET);
}
#[cfg(feature = "try_close")]
pub(crate) unsafe fn try_close(raw_fd: RawFd) -> io::Result<()> {
ret(c::closesocket(raw_fd as c::SOCKET))
}
#[inline]
pub(crate) unsafe fn ioctl(
fd: BorrowedFd<'_>,
request: Opcode,
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: Opcode,
arg: *mut c::c_void,
) -> io::Result<IoctlOutput> {
ioctl(fd, request, arg)
}

View File

@@ -0,0 +1 @@
pub(crate) mod syscalls;

View File

@@ -0,0 +1,94 @@
//! 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, IoringRegisterFlags, 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_register_with(
fd: BorrowedFd<'_>,
opcode: IoringRegisterOp,
flags: IoringRegisterFlags,
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) | bitflags_bits!(flags),
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,
))
}

View File

@@ -0,0 +1,2 @@
pub(crate) mod syscalls;
pub(crate) mod types;

View File

@@ -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()) }
}

View File

@@ -0,0 +1,506 @@
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_raw_dep)]
const SEM = linux_raw_sys::general::PROT_SEM;
/// `PROT_BTI`
#[cfg(all(linux_raw_dep, target_arch = "aarch64"))]
const BTI = linux_raw_sys::general::PROT_BTI;
/// `PROT_MTE`
#[cfg(all(linux_raw_dep, target_arch = "aarch64"))]
const MTE = linux_raw_sys::general::PROT_MTE;
/// `PROT_SAO`
#[cfg(all(linux_raw_dep, any(target_arch = "powerpc", target_arch = "powerpc64")))]
const SAO = linux_raw_sys::general::PROT_SAO;
/// `PROT_ADI`
#[cfg(all(linux_raw_dep, 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 = "cygwin",
target_os = "emscripten",
target_os = "fuchsia",
target_os = "haiku",
target_os = "hurd",
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 = "cygwin",
target_os = "haiku",
target_os = "hurd",
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 = "cygwin",
target_os = "emscripten",
target_os = "fuchsia",
target_os = "haiku",
target_os = "hurd",
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 = "cygwin",
target_os = "haiku",
target_os = "hurd",
target_os = "nto",
target_os = "redox",
)))]
const GROWSDOWN = bitcast!(c::MAP_GROWSDOWN);
/// `MAP_HUGETLB`
#[cfg(not(any(
bsd,
solarish,
target_os = "aix",
target_os = "cygwin",
target_os = "haiku",
target_os = "hurd",
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 = "cygwin",
target_os = "emscripten",
target_os = "fuchsia",
target_os = "haiku",
target_os = "hurd",
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 = "cygwin",
target_os = "emscripten",
target_os = "fuchsia",
target_os = "haiku",
target_os = "hurd",
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 = "cygwin",
target_os = "haiku",
target_os = "hurd",
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 = "hurd",
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 = "cygwin",
target_os = "haiku",
target_os = "hurd",
target_os = "nto",
target_os = "redox",
)))]
const POPULATE = bitcast!(c::MAP_POPULATE);
/// `MAP_STACK`
#[cfg(not(any(
apple,
solarish,
target_os = "aix",
target_os = "cygwin",
target_os = "haiku",
target_os = "hurd",
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 = "cygwin",
target_os = "emscripten",
target_os = "fuchsia",
target_os = "haiku",
target_os = "hurd",
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);
/// `MAP_DROPPABLE`
#[cfg(all(linux_kernel, not(target_os = "android")))]
const DROPPABLE = bitcast!(c::MAP_DROPPABLE);
/// <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",
target_os = "hurd",
)))]
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!(c::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!(c::MCL_FUTURE);
/// Lock all pages which are currently mapped into the address space of
/// the process.
const CURRENT = bitcast!(c::MCL_CURRENT);
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}

220
vendor/rustix/src/backend/libc/mod.rs vendored Normal file
View File

@@ -0,0 +1,220 @@
//! 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 {
// Re-export `AsSocket` etc. too, as users can't implement `AsFd` etc. on
// Windows due to them having blanket impls on Windows, so users must
// implement `AsSocket` etc.
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.AsRawFd.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.IntoRawFd.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 an `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::*;
#[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 = "horizon",
target_os = "vita",
target_os = "wasi"
)))]
#[cfg(feature = "mm")]
pub(crate) mod mm;
#[cfg(linux_kernel)]
#[cfg(feature = "mount")]
pub(crate) mod mount;
#[cfg(not(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 = "horizon", 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 = "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 = "horizon",
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.

View File

@@ -0,0 +1,2 @@
pub(crate) mod syscalls;
pub(crate) mod types;

View File

@@ -0,0 +1,268 @@
use crate::backend::c;
use crate::backend::conv::{borrowed_fd, c_str, ret, ret_owned_fd};
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)]
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)]
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)]
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)]
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)]
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(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)]
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)]
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)]
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)]
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)]
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)]
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)]
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)]
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,
))
}
}
#[cfg(linux_kernel)]
pub(crate) fn fsconfig_create_excl(fs_fd: BorrowedFd<'_>) -> io::Result<()> {
unsafe {
ret(fsconfig(
borrowed_fd(fs_fd),
super::types::FsConfigCmd::CreateExclusive as _,
null(),
null(),
0,
))
}
}

View File

@@ -0,0 +1,348 @@
use crate::backend::c;
use crate::ffi;
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: ffi::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;
/// `MS_NOSYMFOLLOW`
#[cfg(linux_raw_dep)]
const NOSYMFOLLOW = c::MS_NOSYMFOLLOW;
/// <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(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: ffi::c_uint {
/// `FSOPEN_CLOEXEC`
const FSOPEN_CLOEXEC = 0x0000_0001;
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}
#[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: ffi::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(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,
/// `FSCONFIG_CMD_CREATE_EXCL` (since Linux 6.6)
CreateExclusive = 8,
}
#[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: ffi::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(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: ffi::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;
/// `MOVE_MOUNT_BENEATH` (since Linux 6.5)
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(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: ffi::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(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: ffi::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: ffi::c_ulong {
/// `MS_SILENT`
const SILENT = c::MS_SILENT;
/// `MS_SHARED`
const SHARED = c::MS_SHARED;
/// `MS_PRIVATE`
const PRIVATE = c::MS_PRIVATE;
/// Mark a mount as a downstream of its current peer group.
///
/// Mount and unmount events propagate from the upstream peer group
/// into the downstream.
///
/// In Linux documentation, this flag is named `MS_SLAVE`, and the
/// concepts of “upstream” and “downstream” are called
/// “master” and “slave”.
#[doc(alias = "SLAVE")]
const DOWNSTREAM = 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);

View File

@@ -0,0 +1,390 @@
//! Socket address utilities.
use crate::backend::c;
use crate::net::AddressFamily;
#[cfg(unix)]
use {
crate::ffi::CStr,
crate::io,
crate::net::addr::SocketAddrLen,
crate::path,
core::cmp::Ordering,
core::fmt,
core::hash::{Hash, Hasher},
core::slice,
};
#[cfg(all(unix, feature = "alloc"))]
use {crate::ffi::CString, alloc::borrow::Cow, alloc::vec::Vec};
/// `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 mut bytes = path.to_bytes_with_nul();
if bytes.len() > unix.sun_path.len() {
bytes = path.to_bytes(); // without 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,
})
}
/// Construct a new unnamed address.
///
/// The kernel will assign an abstract Unix-domain address to the socket
/// when you call [`bind`][crate::net::bind]. You can inspect the assigned
/// name with [`getsockname`][crate::net::getsockname].
///
/// # References
/// - [Linux]
///
/// [Linux]: https://www.man7.org/linux/man-pages/man7/unix.7.html
#[cfg(linux_kernel)]
#[inline]
pub fn new_unnamed() -> Self {
Self {
unix: Self::init(),
#[cfg(not(any(bsd, target_os = "haiku")))]
len: offsetof_sun_path() as c::socklen_t,
}
}
const fn init() -> c::sockaddr_un {
c::sockaddr_un {
#[cfg(any(
bsd,
target_os = "aix",
target_os = "haiku",
target_os = "horizon",
target_os = "nto",
target_os = "hurd",
))]
sun_len: 0,
#[cfg(target_os = "vita")]
ss_len: 0,
sun_family: c::AF_UNIX as _,
#[cfg(any(bsd, target_os = "horizon", target_os = "nto"))]
sun_path: [0; 104],
#[cfg(not(any(
bsd,
target_os = "aix",
target_os = "haiku",
target_os = "horizon",
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]
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
pub fn path(&self) -> Option<Cow<'_, CStr>> {
let bytes = self.bytes()?;
if !bytes.is_empty() && bytes[0] != 0 {
if self.unix.sun_path.len() == bytes.len() {
// SAFETY: There are no NULs contained in bytes.
unsafe { Self::path_with_termination(bytes) }
} else {
// SAFETY: `from_bytes_with_nul_unchecked` since the string is
// NUL-terminated.
Some(unsafe { CStr::from_bytes_with_nul_unchecked(bytes) }.into())
}
} else {
None
}
}
/// If the `sun_path` field is not NUL-terminated, terminate it.
///
/// SAFETY: The input `bytes` must not contain any NULs.
#[cfg(feature = "alloc")]
#[cold]
unsafe fn path_with_termination(bytes: &[u8]) -> Option<Cow<'_, CStr>> {
let mut owned = Vec::with_capacity(bytes.len() + 1);
owned.extend_from_slice(bytes);
owned.push(b'\0');
// SAFETY: `from_vec_with_nul_unchecked` since the string is
// NUL-terminated and `bytes` does not contain any NULs.
Some(Cow::Owned(
CString::from_vec_with_nul_unchecked(owned).into(),
))
}
/// For a filesystem path address, return the path as a byte sequence,
/// excluding the NUL terminator.
#[inline]
pub fn path_bytes(&self) -> Option<&[u8]> {
let bytes = self.bytes()?;
if !bytes.is_empty() && bytes[0] != 0 {
if self.unix.sun_path.len() == self.len() - offsetof_sun_path() {
// There is no NUL terminator.
Some(bytes)
} else {
// Remove the NUL terminator.
Some(&bytes[..bytes.len() - 1])
}
} else {
None
}
}
/// For an abstract address, return the identifier.
#[cfg(linux_kernel)]
#[inline]
pub fn abstract_name(&self) -> Option<&[u8]> {
if let [0, bytes @ ..] = self.bytes()? {
Some(bytes)
} else {
None
}
}
/// `true` if the socket address is unnamed.
#[cfg(linux_kernel)]
#[inline]
pub fn is_unnamed(&self) -> bool {
self.bytes() == Some(&[])
}
#[inline]
pub(crate) fn addr_len(&self) -> SocketAddrLen {
#[cfg(not(any(bsd, target_os = "haiku")))]
{
bitcast!(self.len)
}
#[cfg(any(bsd, target_os = "haiku"))]
{
bitcast!(c::socklen_t::from(self.unix.sun_len))
}
}
#[inline]
pub(crate) fn len(&self) -> usize {
self.addr_len() as usize
}
#[inline]
fn bytes(&self) -> Option<&[u8]> {
let len = self.len();
if len != 0 {
let bytes = &self.unix.sun_path[..len - offsetof_sun_path()];
// SAFETY: `from_raw_parts` to convert from `&[c_char]` to `&[u8]`.
Some(unsafe { slice::from_raw_parts(bytes.as_ptr().cast(), bytes.len()) })
} else {
None
}
}
}
#[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, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[cfg(feature = "alloc")]
if let Some(path) = self.path() {
return path.fmt(f);
}
if let Some(bytes) = self.path_bytes() {
if let Ok(s) = core::str::from_utf8(bytes) {
return s.fmt(f);
}
return bytes.fmt(f);
}
#[cfg(linux_kernel)]
if let Some(name) = self.abstract_name() {
return name.fmt(f);
}
"(unnamed)".fmt(f)
}
}
/// `struct sockaddr_storage`
///
/// This type is guaranteed to be large enough to hold any encoded socket
/// address.
#[repr(transparent)]
#[derive(Copy, Clone)]
#[doc(alias = "sockaddr_storage")]
pub struct SocketAddrStorage(c::sockaddr_storage);
impl SocketAddrStorage {
/// Return a socket addr storage initialized to all zero bytes. The
/// `sa_family` is set to [`AddressFamily::UNSPEC`].
pub fn zeroed() -> Self {
assert_eq!(c::AF_UNSPEC, 0);
// SAFETY: `sockaddr_storage` is meant to be zero-initializable.
unsafe { core::mem::zeroed() }
}
/// Return the `sa_family` of this socket address.
pub fn family(&self) -> AddressFamily {
// SAFETY: `self.0` is a `sockaddr_storage` so it has enough space.
unsafe {
AddressFamily::from_raw(crate::backend::net::read_sockaddr::read_sa_family(
crate::utils::as_ptr(&self.0).cast::<c::sockaddr>(),
))
}
}
/// Clear the `sa_family` of this socket address to
/// [`AddressFamily::UNSPEC`].
pub fn clear_family(&mut self) {
// SAFETY: `self.0` is a `sockaddr_storage` so it has enough space.
unsafe {
crate::backend::net::read_sockaddr::initialize_family_to_unspec(
crate::utils::as_mut_ptr(&mut self.0).cast::<c::sockaddr>(),
)
}
}
}
/// 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 = "horizon",
target_os = "hurd",
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 = "hurd",
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 = "hurd",
target_os = "nto",
target_os = "vita"
)))]
sun_family: 0_u16,
#[cfg(any(bsd, target_os = "horizon", target_os = "nto"))]
sun_path: [0; 104],
#[cfg(not(any(
bsd,
target_os = "aix",
target_os = "haiku",
target_os = "horizon",
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)
}

View File

@@ -0,0 +1,137 @@
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 = "hurd",
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 = "hurd",
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) }
}

View File

@@ -0,0 +1,17 @@
pub(crate) mod addr;
pub(crate) mod ext;
#[cfg(not(any(
windows,
target_os = "espidf",
target_os = "horizon",
target_os = "redox",
target_os = "vita"
)))]
pub(crate) mod msghdr;
#[cfg(linux_kernel)]
pub(crate) mod netdevice;
pub(crate) mod read_sockaddr;
pub(crate) mod send_recv;
pub(crate) mod sockopt;
pub(crate) mod syscalls;
pub(crate) mod write_sockaddr;

View File

@@ -0,0 +1,188 @@
//! 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::io::{self, IoSlice, IoSliceMut};
use crate::net::addr::SocketAddrArg;
use crate::net::{RecvAncillaryBuffer, SendAncillaryBuffer, SocketAddrBuf};
use core::mem::zeroed;
/// Convert the value to the `msg_iovlen` field of a `msghdr` struct.
#[cfg(all(
not(any(windows, target_os = "espidf", target_os = "wasi")),
any(
target_os = "android",
all(
target_os = "linux",
not(target_env = "musl"),
not(all(target_env = "uclibc", any(target_arch = "arm", target_arch = "mips")))
)
)
))]
#[inline]
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"),
not(all(target_env = "uclibc", any(target_arch = "arm", target_arch = "mips")))
)
))
))]
#[inline]
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 = "cygwin",
target_os = "emscripten",
target_os = "fuchsia",
target_os = "haiku",
target_os = "hurd",
target_os = "nto",
))]
#[inline]
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 = "cygwin",
target_os = "emscripten",
target_os = "espidf",
target_os = "fuchsia",
target_os = "haiku",
target_os = "hurd",
target_os = "nto",
target_os = "redox",
target_os = "vita",
target_os = "wasi",
)))]
#[inline]
fn msg_control_len(len: usize) -> c::size_t {
len
}
/// Create a message header intended to receive a datagram.
///
/// # Safety
///
/// If `f` dereferences the pointers in the `msghdr`, it must do so only within
/// the bounds indicated by the associated lengths in the `msghdr`.
///
/// And, if `f` returns `Ok`, it must have updated the `msg_controllen` field
/// of the `msghdr` to indicate how many bytes it initialized.
pub(crate) unsafe fn with_recv_msghdr<R>(
name: &mut SocketAddrBuf,
iov: &mut [IoSliceMut<'_>],
control: &mut RecvAncillaryBuffer<'_>,
f: impl FnOnce(&mut c::msghdr) -> io::Result<R>,
) -> io::Result<R> {
control.clear();
let mut msghdr = zero_msghdr();
msghdr.msg_name = name.storage.as_mut_ptr().cast();
msghdr.msg_namelen = name.len;
msghdr.msg_iov = iov.as_mut_ptr().cast();
msghdr.msg_iovlen = msg_iov_len(iov.len());
msghdr.msg_control = control.as_control_ptr().cast();
msghdr.msg_controllen = msg_control_len(control.control_len());
let res = f(&mut msghdr);
// Reset the control length.
if res.is_ok() {
// SAFETY: `f` returned `Ok`, so our safety condition requires `f` to
// have initialized `msg_controllen` bytes.
control.set_control_len(msghdr.msg_controllen as usize);
}
name.len = msghdr.msg_namelen;
res
}
/// Create a message header intended to send without an address.
///
/// The returned `msghdr` will contain raw pointers to the memory
/// referenced by `iov` and `control`.
pub(crate) fn noaddr_msghdr(
iov: &[IoSlice<'_>],
control: &mut SendAncillaryBuffer<'_, '_, '_>,
) -> c::msghdr {
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 the specified address.
///
/// This creates a `c::msghdr` and calls a function `f` on it. The `msghdr`'s
/// raw pointers may point to temporaries, so this function should avoid
/// storing the pointers anywhere that would outlive the function call.
///
/// # Safety
///
/// If `f` dereferences the pointers in the `msghdr`, it must do so only within
/// the bounds indicated by the associated lengths in the `msghdr`.
pub(crate) unsafe fn with_msghdr<R>(
addr: &impl SocketAddrArg,
iov: &[IoSlice<'_>],
control: &mut SendAncillaryBuffer<'_, '_, '_>,
f: impl FnOnce(&c::msghdr) -> R,
) -> R {
addr.with_sockaddr(|addr_ptr, addr_len| {
let mut h = zero_msghdr();
h.msg_name = addr_ptr as *mut _;
h.msg_namelen = bitcast!(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());
// Pass a reference to the `c::msghdr` instead of passing it by value
// because it may contain pointers to temporary objects that won't
// live beyond the call to `with_sockaddr`.
f(&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() }
}

View File

@@ -0,0 +1,50 @@
//! Wrappers for netdevice ioctls.
#![allow(unsafe_code)]
use crate::backend::c;
use crate::backend::io::syscalls::ioctl;
use crate::fd::BorrowedFd;
use crate::io;
use c::{__c_anonymous_ifr_ifru, c_char, ifreq, IFNAMSIZ, SIOCGIFINDEX, SIOCGIFNAME};
pub(crate) fn name_to_index(fd: BorrowedFd<'_>, if_name: &str) -> io::Result<u32> {
let if_name_bytes = if_name.as_bytes();
if if_name_bytes.len() >= IFNAMSIZ as usize {
return Err(io::Errno::NODEV);
}
let mut ifreq = ifreq {
ifr_name: [0; 16],
ifr_ifru: __c_anonymous_ifr_ifru { ifru_ifindex: 0 },
};
let mut if_name_c_char_iter = if_name_bytes.iter().map(|byte| *byte as c_char);
ifreq.ifr_name[..if_name_bytes.len()].fill_with(|| if_name_c_char_iter.next().unwrap());
unsafe { ioctl(fd, SIOCGIFINDEX as _, &mut ifreq as *mut ifreq as _) }?;
let index = unsafe { ifreq.ifr_ifru.ifru_ifindex };
Ok(index as u32)
}
pub(crate) fn index_to_name(fd: BorrowedFd<'_>, index: u32) -> io::Result<(usize, [u8; 16])> {
let mut ifreq = ifreq {
ifr_name: [0; 16],
ifr_ifru: __c_anonymous_ifr_ifru {
ifru_ifindex: index as _,
},
};
unsafe { ioctl(fd, SIOCGIFNAME as _, &mut ifreq as *mut ifreq as _) }?;
if let Some(nul_byte) = ifreq.ifr_name.iter().position(|char| *char == 0) {
let mut buf = [0u8; 16];
ifreq.ifr_name.iter().enumerate().for_each(|(idx, c)| {
buf[idx] = *c as u8;
});
Ok((nul_byte, buf))
} else {
Err(io::Errno::INVAL)
}
}

View File

@@ -0,0 +1,264 @@
//! The BSD sockets API requires us to read the `sa_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::Errno;
use crate::net::addr::SocketAddrLen;
#[cfg(linux_kernel)]
use crate::net::netlink::SocketAddrNetlink;
#[cfg(target_os = "linux")]
use crate::net::xdp::{SocketAddrXdp, SocketAddrXdpFlags};
use crate::net::{AddressFamily, Ipv4Addr, Ipv6Addr, SocketAddrAny, SocketAddrV4, SocketAddrV6};
use core::mem::size_of;
// This must match the header of `sockaddr`.
#[repr(C)]
pub(crate) 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"
))]
sa_family: u8,
#[cfg(not(any(
bsd,
target_os = "aix",
target_os = "espidf",
target_os = "haiku",
target_os = "nto",
target_os = "vita"
)))]
sa_family: u16,
}
/// Read the `sa_family` field from a socket address returned from the OS.
///
/// # Safety
///
/// `storage` must point to a valid socket address returned from the OS.
#[inline]
pub(crate) unsafe fn read_sa_family(storage: *const c::sockaddr) -> 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 = "hurd",
target_os = "nto",
target_os = "vita"
))]
sa_len: 0_u8,
#[cfg(any(
bsd,
target_os = "aix",
target_os = "espidf",
target_os = "haiku",
target_os = "hurd",
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 = "hurd",
target_os = "nto",
target_os = "vita"
)))]
sa_family: 0_u16,
#[cfg(not(any(target_os = "haiku", target_os = "horizon")))]
sa_data: [0; 14],
#[cfg(target_os = "horizon")]
sa_data: [0; 26],
#[cfg(target_os = "haiku")]
sa_data: [0; 30],
};
(*storage.cast::<sockaddr_header>()).sa_family.into()
}
/// Read the first byte of the `sun_path` field, assuming we have an `AF_UNIX`
/// socket address.
#[cfg(apple)]
#[inline]
unsafe fn read_sun_path0(storage: *const c::sockaddr) -> u8 {
// In `read_sa_family` we assert that we know the layout of `sockaddr`.
storage
.cast::<u8>()
.add(super::addr::offsetof_sun_path())
.read()
}
/// Check if a socket address returned from the OS is considered non-empty.
///
/// # Safety
///
/// `storage` must point to a least an initialized `sockaddr_header`.
#[inline]
pub(crate) unsafe fn sockaddr_nonempty(storage: *const c::sockaddr, len: SocketAddrLen) -> bool {
if len == 0 {
return false;
}
assert!(len as usize >= size_of::<c::sa_family_t>());
let family: c::c_int = read_sa_family(storage.cast::<c::sockaddr>()).into();
if family == c::AF_UNSPEC {
return false;
}
// On macOS, if we get an `AF_UNIX` with an empty path, treat it as an
// absent address.
#[cfg(apple)]
if family == c::AF_UNIX && read_sun_path0(storage) == 0 {
return false;
}
true
}
/// Set the `sa_family` field of a socket address to `AF_UNSPEC`, so that we
/// can test for `AF_UNSPEC` to test whether it was stored to.
///
/// # Safety
///
/// `storage` must point to a least an initialized `sockaddr_header`.
pub(crate) unsafe fn initialize_family_to_unspec(storage: *mut c::sockaddr) {
(*storage.cast::<sockaddr_header>()).sa_family = c::AF_UNSPEC as _;
}
#[inline]
pub(crate) fn read_sockaddr_v4(addr: &SocketAddrAny) -> Result<SocketAddrV4, Errno> {
if addr.address_family() != AddressFamily::INET {
return Err(Errno::AFNOSUPPORT);
}
assert!(addr.addr_len() as usize >= size_of::<c::sockaddr_in>());
let decode = unsafe { &*addr.as_ptr().cast::<c::sockaddr_in>() };
Ok(SocketAddrV4::new(
Ipv4Addr::from(u32::from_be(in_addr_s_addr(decode.sin_addr))),
u16::from_be(decode.sin_port),
))
}
#[inline]
pub(crate) fn read_sockaddr_v6(addr: &SocketAddrAny) -> Result<SocketAddrV6, Errno> {
if addr.address_family() != AddressFamily::INET6 {
return Err(Errno::AFNOSUPPORT);
}
assert!(addr.addr_len() as usize >= size_of::<c::sockaddr_in6>());
let decode = unsafe { &*addr.as_ptr().cast::<c::sockaddr_in6>() };
Ok(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)]
#[inline]
pub(crate) fn read_sockaddr_unix(addr: &SocketAddrAny) -> Result<SocketAddrUnix, Errno> {
if addr.address_family() != AddressFamily::UNIX {
return Err(Errno::AFNOSUPPORT);
}
let offsetof_sun_path = super::addr::offsetof_sun_path();
let len = addr.addr_len() as usize;
assert!(len >= offsetof_sun_path);
if len == offsetof_sun_path {
SocketAddrUnix::new(&[][..])
} else {
let decode = unsafe { &*addr.as_ptr().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 {
let name = &decode.sun_path[1..len - offsetof_sun_path];
let name = unsafe { core::mem::transmute::<&[c::c_char], &[u8]>(name) };
return SocketAddrUnix::new_abstract_name(name);
}
// 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(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(Errno::INVAL);
}
debug_assert_eq!(
unsafe { CStr::from_ptr(decode.sun_path.as_ptr().cast()) }
.to_bytes()
.len(),
provided_len
);
&decode.sun_path[..provided_len]
};
SocketAddrUnix::new(unsafe { core::mem::transmute::<&[c::c_char], &[u8]>(path_bytes) })
}
}
#[cfg(target_os = "linux")]
#[inline]
pub(crate) fn read_sockaddr_xdp(addr: &SocketAddrAny) -> Result<SocketAddrXdp, Errno> {
if addr.address_family() != AddressFamily::XDP {
return Err(Errno::AFNOSUPPORT);
}
assert!(addr.addr_len() as usize >= size_of::<c::sockaddr_xdp>());
let decode = unsafe { &*addr.as_ptr().cast::<c::sockaddr_xdp>() };
// This ignores the `sxdp_shared_umem_fd` field, which is only expected to
// be significant in `bind` calls, and not returned from `acceptfrom` or
// `recvmsg` or similar.
Ok(SocketAddrXdp::new(
SocketAddrXdpFlags::from_bits_retain(decode.sxdp_flags),
u32::from_be(decode.sxdp_ifindex),
u32::from_be(decode.sxdp_queue_id),
))
}
#[cfg(linux_kernel)]
#[inline]
pub(crate) fn read_sockaddr_netlink(addr: &SocketAddrAny) -> Result<SocketAddrNetlink, Errno> {
if addr.address_family() != AddressFamily::NETLINK {
return Err(Errno::AFNOSUPPORT);
}
assert!(addr.addr_len() as usize >= size_of::<c::sockaddr_nl>());
let decode = unsafe { &*addr.as_ptr().cast::<c::sockaddr_nl>() };
Ok(SocketAddrNetlink::new(decode.nl_pid, decode.nl_groups))
}

View File

@@ -0,0 +1,153 @@
use crate::backend::c;
use bitflags::bitflags;
bitflags! {
/// `MSG_*` flags for use with [`send`], [`sendto`], 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 = "cygwin",
target_os = "espidf",
target_os = "nto",
target_os = "haiku",
target_os = "horizon",
target_os = "hurd",
target_os = "redox",
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(any(windows, target_os = "horizon")))]
const EOR = bitcast!(c::MSG_EOR);
/// `MSG_MORE`
#[cfg(not(any(
bsd,
solarish,
windows,
target_os = "aix",
target_os = "cygwin",
target_os = "haiku",
target_os = "hurd",
target_os = "nto",
target_os = "redox",
target_os = "vita",
)))]
const MORE = bitcast!(c::MSG_MORE);
#[cfg(not(any(apple, windows, target_os = "redox", 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 {
/// `MSG_CMSG_CLOEXEC`
#[cfg(not(any(
apple,
solarish,
windows,
target_os = "aix",
target_os = "espidf",
target_os = "haiku",
target_os = "horizon",
target_os = "nto",
target_os = "redox",
target_os = "vita",
)))]
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 = "cygwin",
target_os = "espidf",
target_os = "haiku",
target_os = "horizon",
target_os = "hurd",
target_os = "nto",
target_os = "redox",
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`
// Apple, illumos, and NetBSD have `MSG_TRUNC` but it's not documented
// for use with `recv` and friends, and in practice appears to be
// ignored.
#[cfg(not(any(apple, solarish, target_os = "horizon", target_os = "netbsd")))]
const TRUNC = bitcast!(c::MSG_TRUNC);
/// `MSG_WAITALL`
const WAITALL = bitcast!(c::MSG_WAITALL);
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}
bitflags! {
/// `MSG_*` flags returned from [`recvmsg`], in the `flags` field of
/// [`RecvMsg`]
///
/// [`recvmsg`]: crate::net::recvmsg
/// [`RecvMsg`]: crate::net::RecvMsg
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct ReturnFlags: u32 {
/// `MSG_OOB`
const OOB = bitcast!(c::MSG_OOB);
/// `MSG_EOR`
#[cfg(not(any(windows, target_os = "horizon")))]
const EOR = bitcast!(c::MSG_EOR);
/// `MSG_TRUNC`
#[cfg(not(target_os = "horizon"))]
const TRUNC = bitcast!(c::MSG_TRUNC);
/// `MSG_CTRUNC`
#[cfg(not(target_os = "horizon"))]
const CTRUNC = bitcast!(c::MSG_CTRUNC);
/// `MSG_CMSG_CLOEXEC`
#[cfg(linux_kernel)]
const CMSG_CLOEXEC = bitcast!(c::MSG_CMSG_CLOEXEC);
/// `MSG_ERRQUEUE`
#[cfg(linux_kernel)]
const ERRQUEUE = bitcast!(c::MSG_ERRQUEUE);
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,438 @@
//! libc syscalls supporting `rustix::net`.
use super::read_sockaddr::initialize_family_to_unspec;
use super::send_recv::{RecvFlags, SendFlags};
use crate::backend::c;
#[cfg(target_os = "linux")]
use crate::backend::conv::ret_u32;
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::addr::SocketAddrArg;
#[cfg(target_os = "linux")]
use crate::net::MMsgHdr;
use crate::net::{
AddressFamily, Protocol, Shutdown, SocketAddrAny, SocketAddrBuf, SocketFlags, SocketType,
};
use crate::utils::as_ptr;
use core::mem::{size_of, MaybeUninit};
use core::ptr::null_mut;
#[cfg(not(any(
windows,
target_os = "espidf",
target_os = "horizon",
target_os = "redox",
target_os = "vita"
)))]
use {
super::msghdr::{noaddr_msghdr, with_msghdr, with_recv_msghdr},
super::send_recv::ReturnFlags,
crate::io::{IoSlice, IoSliceMut},
crate::net::{RecvAncillaryBuffer, RecvMsg, SendAncillaryBuffer},
};
pub(crate) unsafe fn recv(
fd: BorrowedFd<'_>,
buf: (*mut u8, usize),
flags: RecvFlags,
) -> io::Result<usize> {
ret_send_recv(c::recv(
borrowed_fd(fd),
buf.0.cast(),
send_recv_len(buf.1),
bitflags_bits!(flags),
))
}
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),
))
}
}
pub(crate) unsafe fn recvfrom(
fd: BorrowedFd<'_>,
buf: (*mut u8, usize),
flags: RecvFlags,
) -> io::Result<(usize, Option<SocketAddrAny>)> {
let mut addr = SocketAddrBuf::new();
// `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(addr.storage.as_mut_ptr().cast::<c::sockaddr>());
let nread = ret_send_recv(c::recvfrom(
borrowed_fd(fd),
buf.0.cast(),
send_recv_len(buf.1),
bitflags_bits!(flags),
addr.storage.as_mut_ptr().cast::<c::sockaddr>(),
&mut addr.len,
))?;
Ok((nread, addr.into_any_option()))
}
pub(crate) fn sendto(
fd: BorrowedFd<'_>,
buf: &[u8],
flags: SendFlags,
addr: &impl SocketAddrArg,
) -> io::Result<usize> {
unsafe {
addr.with_sockaddr(|addr_ptr, addr_len| {
ret_send_recv(c::sendto(
borrowed_fd(fd),
buf.as_ptr().cast(),
send_recv_len(buf.len()),
bitflags_bits!(flags),
addr_ptr.cast(),
bitcast!(addr_len),
))
})
}
}
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,
))
}
}
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,
))
}
}
pub(crate) fn bind(sockfd: BorrowedFd<'_>, addr: &impl SocketAddrArg) -> io::Result<()> {
unsafe {
addr.with_sockaddr(|addr_ptr, addr_len| {
ret(c::bind(
borrowed_fd(sockfd),
addr_ptr.cast(),
bitcast!(addr_len),
))
})
}
}
pub(crate) fn connect(sockfd: BorrowedFd<'_>, addr: &impl SocketAddrArg) -> io::Result<()> {
unsafe {
addr.with_sockaddr(|addr_ptr, addr_len| {
ret(c::connect(
borrowed_fd(sockfd),
addr_ptr.cast(),
bitcast!(addr_len),
))
})
}
}
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,
))
}
}
pub(crate) fn listen(sockfd: BorrowedFd<'_>, backlog: c::c_int) -> io::Result<()> {
unsafe { ret(c::listen(borrowed_fd(sockfd), backlog)) }
}
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 = "horizon",
target_os = "redox",
target_os = "vita"
)))]
pub(crate) fn recvmsg(
sockfd: BorrowedFd<'_>,
iov: &mut [IoSliceMut<'_>],
control: &mut RecvAncillaryBuffer<'_>,
msg_flags: RecvFlags,
) -> io::Result<RecvMsg> {
let mut addr = SocketAddrBuf::new();
// SAFETY: This passes the `msghdr` reference to the OS which reads the
// buffers only within the designated bounds.
let (bytes, flags) = unsafe {
with_recv_msghdr(&mut addr, iov, control, |msghdr| {
let bytes = ret_send_recv(c::recvmsg(
borrowed_fd(sockfd),
msghdr,
bitflags_bits!(msg_flags),
))?;
Ok((bytes, msghdr.msg_flags))
})?
};
Ok(RecvMsg {
bytes,
address: unsafe { addr.into_any_option() },
flags: ReturnFlags::from_bits_retain(bitcast!(flags)),
})
}
#[cfg(not(any(
windows,
target_os = "espidf",
target_os = "horizon",
target_os = "redox",
target_os = "vita"
)))]
pub(crate) fn sendmsg(
sockfd: BorrowedFd<'_>,
iov: &[IoSlice<'_>],
control: &mut SendAncillaryBuffer<'_, '_, '_>,
msg_flags: SendFlags,
) -> io::Result<usize> {
let msghdr = noaddr_msghdr(iov, control);
unsafe {
ret_send_recv(c::sendmsg(
borrowed_fd(sockfd),
&msghdr,
bitflags_bits!(msg_flags),
))
}
}
#[cfg(not(any(
windows,
target_os = "espidf",
target_os = "horizon",
target_os = "redox",
target_os = "vita"
)))]
pub(crate) fn sendmsg_addr(
sockfd: BorrowedFd<'_>,
addr: &impl SocketAddrArg,
iov: &[IoSlice<'_>],
control: &mut SendAncillaryBuffer<'_, '_, '_>,
msg_flags: SendFlags,
) -> io::Result<usize> {
// SAFETY: This passes the `msghdr` reference to the OS which reads the
// buffers only within the designated bounds.
unsafe {
with_msghdr(addr, iov, control, |msghdr| {
ret_send_recv(c::sendmsg(
borrowed_fd(sockfd),
msghdr,
bitflags_bits!(msg_flags),
))
})
}
}
#[cfg(target_os = "linux")]
pub(crate) fn sendmmsg(
sockfd: BorrowedFd<'_>,
msgs: &mut [MMsgHdr<'_>],
flags: SendFlags,
) -> io::Result<usize> {
unsafe {
ret_u32(c::sendmmsg(
borrowed_fd(sockfd),
msgs.as_mut_ptr() as _,
msgs.len().try_into().unwrap_or(c::c_uint::MAX),
bitflags_bits!(flags),
))
.map(|ret| ret as usize)
}
}
#[cfg(not(any(
apple,
windows,
target_os = "aix",
target_os = "espidf",
target_os = "haiku",
target_os = "horizon",
target_os = "nto",
target_os = "redox",
target_os = "vita",
)))]
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)
}
}
pub(crate) fn acceptfrom(sockfd: BorrowedFd<'_>) -> io::Result<(OwnedFd, Option<SocketAddrAny>)> {
unsafe {
let mut addr = SocketAddrBuf::new();
let owned_fd = ret_owned_fd(c::accept(
borrowed_fd(sockfd),
addr.storage.as_mut_ptr().cast::<c::sockaddr>(),
&mut addr.len,
))?;
Ok((owned_fd, addr.into_any_option()))
}
}
#[cfg(not(any(
apple,
windows,
target_os = "aix",
target_os = "espidf",
target_os = "haiku",
target_os = "horizon",
target_os = "nto",
target_os = "redox",
target_os = "vita",
)))]
pub(crate) fn acceptfrom_with(
sockfd: BorrowedFd<'_>,
flags: SocketFlags,
) -> io::Result<(OwnedFd, Option<SocketAddrAny>)> {
unsafe {
let mut addr = SocketAddrBuf::new();
let owned_fd = ret_owned_fd(c::accept4(
borrowed_fd(sockfd),
addr.storage.as_mut_ptr().cast::<c::sockaddr>(),
&mut addr.len,
flags.bits() as c::c_int,
))?;
Ok((owned_fd, addr.into_any_option()))
}
}
/// 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 = "horizon",
target_os = "nto",
target_os = "redox",
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 = "horizon",
target_os = "nto",
target_os = "redox",
target_os = "vita",
))]
pub(crate) fn acceptfrom_with(
sockfd: BorrowedFd<'_>,
_flags: SocketFlags,
) -> io::Result<(OwnedFd, Option<SocketAddrAny>)> {
acceptfrom(sockfd)
}
pub(crate) fn shutdown(sockfd: BorrowedFd<'_>, how: Shutdown) -> io::Result<()> {
unsafe { ret(c::shutdown(borrowed_fd(sockfd), how as c::c_int)) }
}
pub(crate) fn getsockname(sockfd: BorrowedFd<'_>) -> io::Result<SocketAddrAny> {
unsafe {
let mut addr = SocketAddrBuf::new();
ret(c::getsockname(
borrowed_fd(sockfd),
addr.storage.as_mut_ptr().cast::<c::sockaddr>(),
&mut addr.len,
))?;
Ok(addr.into_any())
}
}
pub(crate) fn getpeername(sockfd: BorrowedFd<'_>) -> io::Result<Option<SocketAddrAny>> {
unsafe {
let mut addr = SocketAddrBuf::new();
ret(c::getpeername(
borrowed_fd(sockfd),
addr.storage.as_mut_ptr().cast::<c::sockaddr>(),
&mut addr.len,
))?;
Ok(addr.into_any_option())
}
}
#[cfg(not(windows))]
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))
}
}

View File

@@ -0,0 +1,72 @@
//! The BSD sockets API requires us to read the `sa_family` field before we can
//! interpret the rest of a `sockaddr` produced by the kernel.
use super::ext::{in6_addr_new, in_addr_new, sockaddr_in6_new};
use crate::backend::c;
use crate::net::{SocketAddrV4, SocketAddrV6};
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 = "hurd",
target_os = "nto",
target_os = "vita",
))]
sin_len: core::mem::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,
}
}
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 = "hurd",
target_os = "nto",
target_os = "vita"
))]
{
sockaddr_in6_new(
core::mem::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 = "hurd",
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(),
)
}
}

View File

@@ -0,0 +1,67 @@
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 = "horizon", 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_minsigstksz() -> usize {
if let Some(libc_getauxval) = getauxval.get() {
unsafe { libc_getauxval(c::AT_MINSIGSTKSZ) as usize }
} else {
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!("")
}
}

View File

@@ -0,0 +1 @@
pub(crate) mod auxv;

View File

@@ -0,0 +1 @@
pub(crate) mod syscalls;

View File

@@ -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)
}
}

View File

@@ -0,0 +1,2 @@
pub(crate) mod syscalls;
pub(crate) mod types;

View File

@@ -0,0 +1,126 @@
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 = "horizon",
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(crate) 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(crate) 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(crate) 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_size(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_size(fd: BorrowedFd<'_>, size: usize) -> io::Result<usize> {
let size: c::c_int = size.try_into().map_err(|_| io::Errno::PERM)?;
unsafe { ret_c_int(c::fcntl(borrowed_fd(fd), c::F_SETPIPE_SZ, size)).map(|size| size as usize) }
}

View File

@@ -0,0 +1,117 @@
#[cfg(linux_kernel)]
use crate::ffi;
#[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 = "horizon",
target_os = "hurd",
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`].
///
/// [`splice`]: crate::pipe::splice
/// [`vmsplice`]: crate::pipe::splice
/// [`tee`]: crate::pipe::tee
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct SpliceFlags: ffi::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).cast::<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().cast::<c::c_void>(),
iov_len: buf.len() as _,
},
_lifetime: PhantomData,
}
}
}
#[cfg(test)]
mod tests {
#[allow(unused_imports)]
use super::*;
#[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);
}
}

View File

@@ -0,0 +1 @@
pub(crate) mod syscalls;

View File

@@ -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))
}

View File

@@ -0,0 +1,5 @@
#[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;

View File

@@ -0,0 +1,704 @@
//! libc syscalls supporting `rustix::process`.
use crate::backend::c;
#[cfg(not(any(target_os = "fuchsia", target_os = "wasi")))]
use crate::backend::conv::borrowed_fd;
#[cfg(any(target_os = "linux", feature = "fs"))]
use crate::backend::conv::c_str;
#[cfg(all(feature = "alloc", feature = "fs", not(target_os = "wasi")))]
use crate::backend::conv::ret_discarded_char_ptr;
#[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(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 = "fuchsia", target_os = "wasi")))]
use crate::fd::BorrowedFd;
#[cfg(target_os = "linux")]
use crate::fd::{AsRawFd as _, OwnedFd, RawFd};
#[cfg(any(target_os = "linux", feature = "fs"))]
use crate::ffi::CStr;
#[cfg(feature = "fs")]
use crate::fs::Mode;
use crate::io;
#[cfg(not(any(
target_os = "emscripten",
target_os = "espidf",
target_os = "fuchsia",
target_os = "horizon",
target_os = "redox",
target_os = "vita",
target_os = "wasi"
)))]
use crate::process::Flock;
#[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(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 = "horizon",
target_os = "redox",
target_os = "vita",
target_os = "wasi"
)))]
use crate::process::{Resource, Rlimit};
#[cfg(not(any(
target_os = "cygwin",
target_os = "espidf",
target_os = "horizon",
target_os = "openbsd",
target_os = "redox",
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(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())) }
}
#[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(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 = "horizon",
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 = "horizon",
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 = "horizon",
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 = "horizon",
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 = "horizon",
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 = "horizon",
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 = "horizon",
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 = "horizon",
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 C `c::rlimit` to a Rust `Rlimit`.
#[cfg(not(any(
target_os = "espidf",
target_os = "fuchsia",
target_os = "horizon",
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 Rust [`Rlimit`] to a C `c::rlimit`.
#[cfg(not(any(
target_os = "espidf",
target_os = "fuchsia",
target_os = "horizon",
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 = "cygwin",
target_os = "espidf",
target_os = "horizon",
target_os = "openbsd",
target_os = "redox",
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 = "cygwin",
target_os = "espidf",
target_os = "horizon",
target_os = "openbsd",
target_os = "redox",
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 = "cygwin",
target_os = "espidf",
target_os = "horizon",
target_os = "openbsd",
target_os = "redox",
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 = "cygwin",
target_os = "espidf",
target_os = "horizon",
target_os = "openbsd",
target_os = "redox",
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 = "cygwin",
target_os = "espidf",
target_os = "horizon",
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 manual page says about the `WNOHANG` case “zero
// out the si_pid field before the call and check for a nonzero value”.
// But e.g. NetBSD/OpenBSD don't have it exposed in the libc crate for now,
// and some platforms don't have it at all. For simplicity, always check
// `si_signo`. We have zero-initialized the whole struct, and all kernels
// 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_raw())) }
}
#[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_raw(),
))
}
}
#[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_raw())) }
}
#[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_send_signal(pidfd: BorrowedFd<'_>, sig: Signal) -> io::Result<()> {
syscall! {
fn pidfd_send_signal(
pid: c::pid_t,
sig: c::c_int,
info: *const c::siginfo_t,
flags: c::c_int
) via SYS_pidfd_send_signal -> c::c_int
}
unsafe {
ret(pidfd_send_signal(
borrowed_fd(pidfd),
sig.as_raw(),
core::ptr::null(),
0,
))
}
}
#[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(target_os = "linux")]
pub(crate) fn pivot_root(new_root: &CStr, put_old: &CStr) -> io::Result<()> {
syscall! {
fn pivot_root(
new_root: *const c::c_char,
put_old: *const c::c_char
) via SYS_pivot_root -> c::c_int
}
unsafe { ret(pivot_root(c_str(new_root), c_str(put_old))) }
}
#[cfg(all(feature = "alloc", not(target_os = "wasi")))]
pub(crate) fn getgroups(buf: &mut [Gid]) -> io::Result<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) }
}
#[cfg(not(any(
target_os = "emscripten",
target_os = "espidf",
target_os = "fuchsia",
target_os = "horizon",
target_os = "redox",
target_os = "vita",
target_os = "wasi"
)))]
#[inline]
pub(crate) fn fcntl_getlk(fd: BorrowedFd<'_>, lock: &Flock) -> io::Result<Option<Flock>> {
let mut curr_lock: c::flock = lock.as_raw();
unsafe { ret(c::fcntl(borrowed_fd(fd), c::F_GETLK, &mut curr_lock))? };
// If no blocking lock is found, `fcntl(GETLK, ..)` sets `l_type` to
// `F_UNLCK`.
if curr_lock.l_type == c::F_UNLCK as _ {
Ok(None)
} else {
Ok(Some(unsafe { Flock::from_raw_unchecked(curr_lock) }))
}
}

View File

@@ -0,0 +1,139 @@
#[cfg(not(any(
target_os = "espidf",
target_os = "fuchsia",
target_os = "redox",
target_os = "vita",
target_os = "wasi"
)))]
use crate::backend::c;
/// 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 = "horizon",
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))]
#[non_exhaustive]
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 = "cygwin",
target_os = "haiku",
target_os = "nto",
)))]
Rss = bitcast!(c::RLIMIT_RSS),
/// `RLIMIT_NPROC`
#[cfg(not(any(solarish, target_os = "cygwin", 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 = "cygwin", 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 = "cygwin",
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 = "cygwin",
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 = "cygwin",
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 = "cygwin",
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 = "cygwin",
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 = "cygwin",
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;
}
#[cfg(freebsdlike)]
pub type RawId = c::id_t;

View File

@@ -0,0 +1,17 @@
use crate::backend::c;
pub(crate) use c::{
WEXITSTATUS, WIFCONTINUED, WIFEXITED, WIFSIGNALED, WIFSTOPPED, WNOHANG, WSTOPSIG, WTERMSIG,
};
#[cfg(not(target_os = "horizon"))]
pub(crate) use c::{WCONTINUED, WUNTRACED};
#[cfg(not(any(
target_os = "cygwin",
target_os = "horizon",
target_os = "openbsd",
target_os = "redox",
target_os = "wasi",
)))]
pub(crate) use c::{WEXITED, WNOWAIT, WSTOPPED};

View File

@@ -0,0 +1 @@
pub(crate) mod syscalls;

View File

@@ -0,0 +1,118 @@
//! 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",
target_os = "illumos"
)
))]
use {
crate::ffi::{CStr, CString},
crate::path::SMALL_PATH_BUFFER_SIZE,
alloc::borrow::ToOwned as _,
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",
target_os = "illumos"
)
))]
#[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", target_os = "illumos"))]
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 {
c::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();
core::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))) }
}

View File

@@ -0,0 +1,2 @@
pub(crate) mod syscalls;
pub(crate) mod types;

View File

@@ -0,0 +1,14 @@
//! 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, 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.0.cast(), buf.1, flags.bits()))
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,2 @@
pub(crate) mod syscalls;
pub(crate) mod types;

View File

@@ -0,0 +1,24 @@
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, shm};
pub(crate) fn shm_open(name: &CStr, oflags: shm::OFlags, 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))) }
}

View File

@@ -0,0 +1,30 @@
use crate::backend::c;
use bitflags::bitflags;
bitflags! {
/// `O_*` constants for use with [`shm::open`].
///
/// [`shm::open`]: crate: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;
}
}

View File

@@ -0,0 +1,3 @@
#[cfg(not(windows))]
pub(crate) mod syscalls;
pub(crate) mod types;

View File

@@ -0,0 +1,162 @@
//! 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;
use core::mem::MaybeUninit;
#[cfg(linux_kernel)]
use {
crate::backend::conv::c_str, crate::fd::BorrowedFd, crate::ffi::CStr, crate::system::Sysinfo,
};
#[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 = "horizon",
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(not(any(
target_os = "android",
target_os = "cygwin",
target_os = "emscripten",
target_os = "espidf",
target_os = "illumos",
target_os = "haiku",
target_os = "horizon",
target_os = "redox",
target_os = "solaris",
target_os = "vita",
target_os = "wasi",
)))]
pub(crate) fn setdomainname(name: &[u8]) -> io::Result<()> {
unsafe {
ret(c::setdomainname(
name.as_ptr().cast(),
name.len().try_into().map_err(|_| io::Errno::INVAL)?,
))
}
}
// <https://github.com/rust-lang/libc/pull/4212>
#[cfg(target_os = "android")]
pub(crate) fn setdomainname(name: &[u8]) -> io::Result<()> {
syscall! {
fn setdomainname(
name: *const c::c_char,
len: c::size_t
) via SYS_setdomainname -> c::c_int
}
unsafe {
ret(setdomainname(
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)) }
}
#[cfg(linux_kernel)]
#[inline]
pub(crate) fn init_module(image: &[u8], param_values: &CStr) -> io::Result<()> {
syscall! {
fn init_module(
module_image: *const c::c_void,
len: c::c_ulong,
param_values: *const c::c_char
) via SYS_init_module -> c::c_int
}
unsafe {
ret(init_module(
image.as_ptr().cast(),
image.len() as _,
c_str(param_values),
))
}
}
#[cfg(linux_kernel)]
#[inline]
pub(crate) fn finit_module(
fd: BorrowedFd<'_>,
param_values: &CStr,
flags: c::c_int,
) -> io::Result<()> {
use crate::fd::AsRawFd as _;
syscall! {
fn finit_module(
fd: c::c_int,
param_values: *const c::c_char,
flags: c::c_int
) via SYS_finit_module -> c::c_int
}
unsafe { ret(finit_module(fd.as_raw_fd(), c_str(param_values), flags)) }
}
#[cfg(linux_kernel)]
#[inline]
pub(crate) fn delete_module(name: &CStr, flags: c::c_int) -> io::Result<()> {
syscall! {
fn delete_module(
name: *const c::c_char,
flags: c::c_int
) via SYS_delete_module -> c::c_int
}
unsafe { ret(delete_module(c_str(name), flags)) }
}

View File

@@ -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;

View File

@@ -0,0 +1,2 @@
pub(crate) mod syscalls;
pub(crate) mod types;

View File

@@ -0,0 +1,531 @@
//! 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(feature = "alloc")]
#[cfg(not(any(target_os = "fuchsia", target_os = "wasi")))]
use crate::ffi::CStr;
#[cfg(any(
not(target_os = "espidf"),
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> {
// On Linux, use `TCGETS2`, and fall back to `TCGETS` if needed.
#[cfg(linux_kernel)]
{
use crate::termios::{ControlModes, InputModes, LocalModes, OutputModes, SpecialCodes};
let mut termios2 = MaybeUninit::<c::termios2>::uninit();
let ptr = termios2.as_mut_ptr();
// SAFETY: This invokes the `TCGETS2` ioctl, which initializes the full
// `Termios` structure.
let termios2 = unsafe {
match ret(c::ioctl(borrowed_fd(fd), c::TCGETS2 as _, ptr)) {
Ok(()) => {}
// A `NOTTY` or `ACCESS` might mean the OS doesn't support
// `TCGETS2`, for example a seccomp environment or WSL that
// only knows about `TCGETS`. Fall back to the old `TCGETS`.
#[cfg(not(any(target_arch = "powerpc", target_arch = "powerpc64")))]
Err(io::Errno::NOTTY) | Err(io::Errno::ACCESS) => {
tcgetattr_fallback(fd, &mut termios2)?
}
Err(err) => return Err(err),
}
// Now all the fields are set.
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::default()),
// On PowerPC musl targets, `c_ispeed`/`c_ospeed` are named
// `__c_ispeed`/`__c_ospeed`.
#[cfg(not(all(
target_env = "musl",
any(target_arch = "powerpc", target_arch = "powerpc64")
)))]
input_speed: termios2.c_ispeed,
#[cfg(not(all(
target_env = "musl",
any(target_arch = "powerpc", target_arch = "powerpc64")
)))]
output_speed: termios2.c_ospeed,
#[cfg(all(
target_env = "musl",
any(target_arch = "powerpc", target_arch = "powerpc64")
))]
input_speed: termios2.__c_ispeed,
#[cfg(all(
target_env = "musl",
any(target_arch = "powerpc", target_arch = "powerpc64")
))]
output_speed: termios2.__c_ospeed,
};
// Copy in the control codes, since libc's `c_cc` array may have a
// different length from the ioctl's.
let nccs = termios2.c_cc.len();
result.special_codes.0[..nccs].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
// `c::termios`, so we can cast the pointer.
ret(c::tcgetattr(borrowed_fd(fd), result.as_mut_ptr().cast()))?;
Ok(result.assume_init())
}
}
/// Implement `tcgetattr` using the old `TCGETS` ioctl.
#[cfg(all(
linux_kernel,
not(any(target_arch = "powerpc", target_arch = "powerpc64"))
))]
#[cold]
fn tcgetattr_fallback(
fd: BorrowedFd<'_>,
termios: &mut MaybeUninit<c::termios2>,
) -> io::Result<()> {
use crate::termios::speed;
use core::ptr::{addr_of, addr_of_mut};
// SAFETY: This invokes the `TCGETS` ioctl, which, if it succeeds,
// initializes the `Termios` structure except for the `input_speed` and
// `output_speed` fields, which we manually initialize before forming a
// reference to the full `Termios`.
unsafe {
let ptr = termios.as_mut_ptr();
// Do the old `TCGETS` call, which doesn't initialize `input_speed` or
// `output_speed`.
ret(c::ioctl(borrowed_fd(fd), c::TCGETS as _, ptr))?;
// Read the `control_modes` field without forming a reference to the
// `Termios` because it isn't fully initialized yet.
let control_modes = addr_of!((*ptr).c_cflag).read();
// Infer `output_speed`.
let encoded_out = control_modes & c::CBAUD;
let output_speed = match speed::decode(encoded_out) {
Some(output_speed) => output_speed,
None => return Err(io::Errno::RANGE),
};
addr_of_mut!((*ptr).c_ospeed).write(output_speed);
// Infer `input_speed`. For input speeds, `B0` is special-cased to mean
// the input speed is the same as the output speed.
let encoded_in = (control_modes & c::CIBAUD) >> c::IBSHIFT;
let input_speed = if encoded_in == c::B0 {
output_speed
} else {
match speed::decode(encoded_in) {
Some(input_speed) => input_speed,
None => return Err(io::Errno::RANGE),
}
};
addr_of_mut!((*ptr).c_ispeed).write(input_speed);
}
Ok(())
}
#[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 succeed and set the pid to 0 if we pass it a
// pseudo-terminal device fd. For now, translate it into `OPNOTSUPP`.
#[cfg(linux_kernel)]
if pid == 0 {
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<()> {
// On Linux, use `TCSETS2`, and fall back to `TCSETS` if needed.
#[cfg(linux_kernel)]
{
use crate::termios::speed;
let output_speed = termios.output_speed();
let input_speed = termios.input_speed();
let mut termios2 = c::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::default(),
// On PowerPC musl targets, `c_ispeed`/`c_ospeed` are named
// `__c_ispeed`/`__c_ospeed`.
#[cfg(not(all(
target_env = "musl",
any(target_arch = "powerpc", target_arch = "powerpc64")
)))]
c_ispeed: input_speed,
#[cfg(not(all(
target_env = "musl",
any(target_arch = "powerpc", target_arch = "powerpc64")
)))]
c_ospeed: output_speed,
#[cfg(all(
target_env = "musl",
any(target_arch = "powerpc", target_arch = "powerpc64")
))]
__c_ispeed: input_speed,
#[cfg(all(
target_env = "musl",
any(target_arch = "powerpc", target_arch = "powerpc64")
))]
__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 &= !c::CBAUD;
termios2.c_cflag |= speed::encode(output_speed).unwrap_or(c::BOTHER);
termios2.c_cflag &= !c::CIBAUD;
termios2.c_cflag |= speed::encode(input_speed).unwrap_or(c::BOTHER) << c::IBSHIFT;
// Copy in the control codes, since libc's `c_cc` array may have a
// different length from the ioctl's.
let nccs = termios2.c_cc.len();
termios2
.c_cc
.copy_from_slice(&termios.special_codes.0[..nccs]);
// Translate from `optional_actions` into a `TCSETS2` ioctl request
// code. On MIPS, `optional_actions` has `TCSETS` added to it.
let request = c::TCSETS2 as c::c_ulong
+ if cfg!(any(
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6"
)) {
optional_actions as c::c_ulong - c::TCSETS as c::c_ulong
} else {
optional_actions as c::c_ulong
};
// SAFETY: This invokes the `TCSETS2` ioctl.
unsafe {
match ret(c::ioctl(borrowed_fd(fd), request as _, &termios2)) {
Ok(()) => Ok(()),
// Similar to `tcgetattr_fallback`, `NOTTY` or `ACCESS` might
// mean the OS doesn't support `TCSETS2`. Fall back to the old
// `TCSETS`.
#[cfg(not(any(target_arch = "powerpc", target_arch = "powerpc64")))]
Err(io::Errno::NOTTY) | Err(io::Errno::ACCESS) => {
tcsetattr_fallback(fd, optional_actions, &termios2)
}
Err(err) => Err(err),
}
}
}
#[cfg(not(linux_kernel))]
unsafe {
ret(c::tcsetattr(
borrowed_fd(fd),
optional_actions as _,
crate::utils::as_ptr(termios).cast(),
))
}
}
/// Implement `tcsetattr` using the old `TCSETS` ioctl.
#[cfg(all(
linux_kernel,
not(any(target_arch = "powerpc", target_arch = "powerpc64"))
))]
#[cold]
fn tcsetattr_fallback(
fd: BorrowedFd<'_>,
optional_actions: OptionalActions,
termios2: &c::termios2,
) -> io::Result<()> {
// `TCSETS` silently accepts `BOTHER` in `c_cflag` even though it doesn't
// read `c_ispeed`/`c_ospeed`, so detect this case and fail if needed.
let encoded_out = termios2.c_cflag & c::CBAUD;
let encoded_in = (termios2.c_cflag & c::CIBAUD) >> c::IBSHIFT;
if encoded_out == c::BOTHER || encoded_in == c::BOTHER {
return Err(io::Errno::RANGE);
}
// Translate from `optional_actions` into a `TCSETS` ioctl request code. On
// MIPS, `optional_actions` already has `TCSETS` added to it.
let request = if cfg!(any(
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6"
)) {
optional_actions as c::c_ulong
} else {
optional_actions as c::c_ulong + c::TCSETS as c::c_ulong
};
// SAFETY: This invokes the `TCSETS` ioctl.
unsafe { ret(c::ioctl(borrowed_fd(fd), request as _, termios2)) }
}
#[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 = "horizon", 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 = "horizon", 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 {
// On AIX, cfmakeraw() has a return type of 'int' instead of 'void'.
// If the argument 'termios' is NULL, it returns -1; otherwise, it returns 0.
// We believe it is safe to ignore the return value.
#[cfg(target_os = "aix")]
{
let _ = c::cfmakeraw(as_mut_ptr(termios).cast());
}
#[cfg(not(target_os = "aix"))]
{
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(feature = "alloc")]
#[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)),
}
}
}

View File

@@ -0,0 +1,17 @@
//! Types for the `termios` module.
#![allow(non_camel_case_types)]
#[cfg(not(any(target_os = "espidf", target_os = "redox")))]
use crate::ffi;
// We don't want to use `tcflag_t` directly so we don't expose libc
// publicly. Redox uses `u32`, apple uses `c_ulong`, everything else
// seems to use `c_uint`.
#[cfg(apple)]
pub type tcflag_t = ffi::c_ulong;
#[cfg(target_os = "redox")]
pub type tcflag_t = u32;
#[cfg(not(any(apple, target_os = "espidf", target_os = "redox", target_os = "wasi")))]
pub type tcflag_t = ffi::c_uint;

View File

@@ -0,0 +1,68 @@
//! 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() }
}
#[inline]
pub(crate) fn CPU_EQUAL(this: &RawCpuSet, that: &RawCpuSet) -> bool {
#[cfg(any(linux_like, target_os = "fuchsia", target_os = "hurd"))]
unsafe {
c::CPU_EQUAL(this, that)
}
#[cfg(not(any(linux_like, target_os = "fuchsia", target_os = "hurd")))]
unsafe {
for i in 0..c::CPU_SETSIZE as usize {
if c::CPU_ISSET(i, this) != c::CPU_ISSET(i, that) {
return false;
}
}
true
}
}

View File

@@ -0,0 +1,91 @@
use crate::backend::c;
bitflags::bitflags! {
/// `FUTEX_*` flags for use with the functions in [`futex`].
///
/// [`futex`]: mod@crate::thread::futex
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct Flags: u32 {
/// `FUTEX_PRIVATE_FLAG`
const PRIVATE = bitcast!(c::FUTEX_PRIVATE_FLAG);
/// `FUTEX_CLOCK_REALTIME`
const CLOCK_REALTIME = bitcast!(c::FUTEX_CLOCK_REALTIME);
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}
bitflags::bitflags! {
/// `FUTEX2_*` flags for use with the functions in [`Waitv`].
///
/// Not to be confused with [`WaitvFlags`], which is passed as an argument
/// to the `waitv` function.
///
/// [`Waitv`]: crate::thread::futex::Waitv
/// [`WaitvFlags`]: crate::thread::futex::WaitvFlags
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct WaitFlags: u32 {
/// `FUTEX_U8`
const SIZE_U8 = linux_raw_sys::general::FUTEX2_SIZE_U8;
/// `FUTEX_U16`
const SIZE_U16 = linux_raw_sys::general::FUTEX2_SIZE_U16;
/// `FUTEX_U32`
const SIZE_U32 = linux_raw_sys::general::FUTEX2_SIZE_U32;
/// `FUTEX_U64`
const SIZE_U64 = linux_raw_sys::general::FUTEX2_SIZE_U64;
/// `FUTEX_SIZE_MASK`
const SIZE_MASK = linux_raw_sys::general::FUTEX2_SIZE_MASK;
/// `FUTEX2_NUMA`
const NUMA = linux_raw_sys::general::FUTEX2_NUMA;
/// `FUTEX2_PRIVATE`
const PRIVATE = linux_raw_sys::general::FUTEX2_PRIVATE;
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}
/// `FUTEX_*` operations for use with the futex syscall wrappers.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[repr(u32)]
pub(crate) enum Operation {
/// `FUTEX_WAIT`
Wait = bitcast!(c::FUTEX_WAIT),
/// `FUTEX_WAKE`
Wake = bitcast!(c::FUTEX_WAKE),
/// `FUTEX_FD`
Fd = bitcast!(c::FUTEX_FD),
/// `FUTEX_REQUEUE`
Requeue = bitcast!(c::FUTEX_REQUEUE),
/// `FUTEX_CMP_REQUEUE`
CmpRequeue = bitcast!(c::FUTEX_CMP_REQUEUE),
/// `FUTEX_WAKE_OP`
WakeOp = bitcast!(c::FUTEX_WAKE_OP),
/// `FUTEX_LOCK_PI`
LockPi = bitcast!(c::FUTEX_LOCK_PI),
/// `FUTEX_UNLOCK_PI`
UnlockPi = bitcast!(c::FUTEX_UNLOCK_PI),
/// `FUTEX_TRYLOCK_PI`
TrylockPi = bitcast!(c::FUTEX_TRYLOCK_PI),
/// `FUTEX_WAIT_BITSET`
WaitBitset = bitcast!(c::FUTEX_WAIT_BITSET),
/// `FUTEX_WAKE_BITSET`
WakeBitset = bitcast!(c::FUTEX_WAKE_BITSET),
/// `FUTEX_WAIT_REQUEUE_PI`
WaitRequeuePi = bitcast!(c::FUTEX_WAIT_REQUEUE_PI),
/// `FUTEX_CMP_REQUEUE_PI`
CmpRequeuePi = bitcast!(c::FUTEX_CMP_REQUEUE_PI),
/// `FUTEX_LOCK_PI2`
LockPi2 = bitcast!(c::FUTEX_LOCK_PI2),
}
/// `FUTEX_WAITERS`
pub const WAITERS: u32 = linux_raw_sys::general::FUTEX_WAITERS;
/// `FUTEX_OWNER_DIED`
pub const OWNER_DIED: u32 = linux_raw_sys::general::FUTEX_OWNER_DIED;

View File

@@ -0,0 +1,7 @@
#[cfg(any(freebsdlike, linux_kernel, target_os = "fuchsia"))]
pub(crate) mod cpu_set;
#[cfg(linux_kernel)]
pub(crate) mod futex;
#[cfg(not(windows))]
pub(crate) mod syscalls;
pub(crate) mod types;

View File

@@ -0,0 +1,792 @@
//! libc syscalls supporting `rustix::thread`.
#[cfg(any(freebsdlike, linux_kernel, target_os = "fuchsia"))]
use super::types::RawCpuSet;
use crate::backend::c;
use crate::backend::conv::ret;
use crate::io;
#[cfg(any(freebsdlike, linux_kernel, target_os = "fuchsia"))]
use crate::pid::Pid;
#[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;
#[cfg(linux_kernel)]
use crate::thread::{Cpuid, MembarrierCommand, MembarrierQuery};
#[cfg(not(target_os = "redox"))]
use crate::thread::{NanosleepRelativeResult, Timespec};
#[cfg(all(target_env = "gnu", fix_y2038))]
use crate::timespec::LibcTimespec;
#[cfg(not(fix_y2038))]
use crate::timespec::{as_libc_timespec_mut_ptr, as_libc_timespec_ptr};
#[cfg(linux_kernel)]
use crate::utils::option_as_ptr;
use core::mem::MaybeUninit;
#[cfg(linux_kernel)]
use core::sync::atomic::AtomicU32;
#[cfg(linux_kernel)]
use {
crate::backend::conv::{borrowed_fd, ret_c_int, ret_u32, ret_usize},
crate::fd::BorrowedFd,
crate::thread::futex,
crate::utils::as_mut_ptr,
};
#[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 = "horizon",
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,
as_libc_timespec_ptr(request),
as_libc_timespec_mut_ptr(&mut remain),
) {
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 = "horizon",
target_os = "vita"
))
))]
fn clock_nanosleep_relative_old(
id: crate::clockid::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 = "horizon",
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(),
core::ptr::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 _,
as_libc_timespec_ptr(request),
core::ptr::null_mut(),
)
} {
0 => Ok(()),
err => Err(io::Errno(err)),
}
}
}
#[cfg(all(
fix_y2038,
not(any(
apple,
target_os = "emscripten",
target_os = "haiku",
target_os = "horizon",
target_os = "vita"
))
))]
fn clock_nanosleep_absolute_old(id: crate::clockid::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,
core::ptr::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(
as_libc_timespec_ptr(request),
as_libc_timespec_mut_ptr(&mut remain),
)) {
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) unsafe fn unshare(flags: crate::thread::UnshareFlags) -> io::Result<()> {
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: Option<crate::ugid::Uid>,
euid: Option<crate::ugid::Uid>,
suid: Option<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.map_or(-1_i32 as u32, |x| x.as_raw()),
euid.map_or(-1_i32 as u32, |x| x.as_raw()),
suid.map_or(-1_i32 as u32, |x| x.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: Option<crate::ugid::Gid>,
egid: Option<crate::ugid::Gid>,
sgid: Option<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.map_or(-1_i32 as u32, |x| x.as_raw()),
egid.map_or(-1_i32 as u32, |x| x.as_raw()),
sgid.map_or(-1_i32 as u32, |x| x.as_raw()),
))
}
}
/// # Safety
///
/// The raw pointers must point to valid aligned memory.
#[cfg(linux_kernel)]
pub(crate) unsafe fn futex_val2(
uaddr: *const AtomicU32,
op: super::futex::Operation,
flags: futex::Flags,
val: u32,
val2: u32,
uaddr2: *const AtomicU32,
val3: u32,
) -> io::Result<usize> {
// Pass `val2` in the least-significant bytes of the `timeout` argument.
// [“the kernel casts the timeout value first to unsigned long, then to
// uint32_t”], so we perform that exact conversion in reverse to create
// the pointer.
//
// [“the kernel casts the timeout value first to unsigned long, then to uint32_t”]: https://man7.org/linux/man-pages/man2/futex.2.html
let timeout = val2 as usize as *const Timespec;
#[cfg(all(
target_pointer_width = "32",
not(any(target_arch = "aarch64", target_arch = "x86_64"))
))]
{
// TODO: Upstream this to the libc crate.
#[allow(non_upper_case_globals)]
const SYS_futex_time64: i32 = linux_raw_sys::general::__NR_futex_time64 as i32;
syscall! {
fn futex_time64(
uaddr: *const AtomicU32,
futex_op: c::c_int,
val: u32,
timeout: *const Timespec,
uaddr2: *const AtomicU32,
val3: u32
) via SYS_futex_time64 -> c::ssize_t
}
ret_usize(futex_time64(
uaddr,
op as i32 | flags.bits() as i32,
val,
timeout,
uaddr2,
val3,
))
}
#[cfg(any(
target_pointer_width = "64",
target_arch = "aarch64",
target_arch = "x86_64"
))]
{
syscall! {
fn futex(
uaddr: *const AtomicU32,
futex_op: c::c_int,
val: u32,
timeout: *const Timespec,
uaddr2: *const AtomicU32,
val3: u32
) via SYS_futex -> c::c_long
}
ret_usize(futex(
uaddr,
op as i32 | flags.bits() as i32,
val,
timeout.cast(),
uaddr2,
val3,
) as isize)
}
}
/// # Safety
///
/// The raw pointers must point to valid aligned memory.
#[cfg(linux_kernel)]
pub(crate) unsafe fn futex_timeout(
uaddr: *const AtomicU32,
op: super::futex::Operation,
flags: futex::Flags,
val: u32,
timeout: Option<&Timespec>,
uaddr2: *const AtomicU32,
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: *const AtomicU32,
futex_op: c::c_int,
val: u32,
timeout: *const Timespec,
uaddr2: *const AtomicU32,
val3: u32
) via SYS_futex_time64 -> c::ssize_t
}
ret_usize(futex_time64(
uaddr,
op as i32 | flags.bits() as i32,
val,
option_as_ptr(timeout),
uaddr2,
val3,
))
.or_else(|err| {
// See the comments in `clock_gettime_via_syscall` about emulation.
if err == io::Errno::NOSYS {
futex_old_timespec(uaddr, op, flags, val, timeout, uaddr2, val3)
} else {
Err(err)
}
})
}
#[cfg(any(
target_pointer_width = "64",
target_arch = "aarch64",
target_arch = "x86_64"
))]
{
syscall! {
fn futex(
uaddr: *const AtomicU32,
futex_op: c::c_int,
val: u32,
timeout: *const Timespec,
uaddr2: *const AtomicU32,
val3: u32
) via SYS_futex -> c::c_long
}
ret_usize(futex(
uaddr,
op as i32 | flags.bits() as i32,
val,
option_as_ptr(timeout).cast(),
uaddr2,
val3,
) as isize)
}
}
/// # Safety
///
/// The raw pointers must point to valid aligned memory.
#[cfg(linux_kernel)]
#[cfg(all(
target_pointer_width = "32",
not(any(target_arch = "aarch64", target_arch = "x86_64"))
))]
unsafe fn futex_old_timespec(
uaddr: *const AtomicU32,
op: super::futex::Operation,
flags: futex::Flags,
val: u32,
timeout: Option<&Timespec>,
uaddr2: *const AtomicU32,
val3: u32,
) -> io::Result<usize> {
syscall! {
fn futex(
uaddr: *const AtomicU32,
futex_op: c::c_int,
val: u32,
timeout: *const linux_raw_sys::general::__kernel_old_timespec,
uaddr2: *const AtomicU32,
val3: u32
) via SYS_futex -> c::c_long
}
let old_timeout = if let Some(timeout) = timeout {
Some(linux_raw_sys::general::__kernel_old_timespec {
tv_sec: timeout.tv_sec.try_into().map_err(|_| io::Errno::INVAL)?,
tv_nsec: timeout.tv_nsec.try_into().map_err(|_| io::Errno::INVAL)?,
})
} else {
None
};
ret_usize(futex(
uaddr,
op as i32 | flags.bits() as i32,
val,
option_as_ptr(old_timeout.as_ref()),
uaddr2,
val3,
) as isize)
}
#[cfg(linux_kernel)]
pub(crate) fn futex_waitv(
waiters: &[futex::Wait],
flags: futex::WaitvFlags,
timeout: Option<&Timespec>,
clockid: ClockId,
) -> io::Result<usize> {
use futex::Wait as FutexWait;
use linux_raw_sys::general::__kernel_clockid_t as clockid_t;
syscall! {
fn futex_waitv(
waiters: *const FutexWait,
nr_futexes: c::c_uint,
flags: c::c_uint,
timeout: *const Timespec,
clockid: clockid_t
) via SYS_futex_waitv -> c::c_int
}
let nr_futexes: c::c_uint = waiters.len().try_into().map_err(|_| io::Errno::INVAL)?;
unsafe {
ret_c_int(futex_waitv(
waiters.as_ptr(),
nr_futexes,
flags.bits(),
option_as_ptr(timeout).cast(),
clockid as _,
))
.map(|n| n as usize)
}
}
#[cfg(linux_kernel)]
#[inline]
pub(crate) fn setgroups_thread(groups: &[crate::ugid::Gid]) -> io::Result<()> {
syscall! {
fn setgroups(size: c::size_t, list: *const c::gid_t) via SYS_setgroups -> c::c_int
}
ret(unsafe { setgroups(groups.len(), groups.as_ptr().cast()) })
}
#[cfg(any(linux_kernel, target_os = "dragonfly"))]
#[inline]
pub(crate) fn sched_getcpu() -> usize {
let r = unsafe { c::sched_getcpu() };
debug_assert!(r >= 0);
r as usize
}
#[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();
}
}
// 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()),
))
}
}

View File

@@ -0,0 +1,60 @@
#[cfg(all(
any(freebsdlike, linux_kernel, target_os = "fuchsia"),
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::thread::membarrier
/// [`membarrier_cpu`]: crate::thread::membarrier_cpu
/// [`membarrier_query`]: crate::thread::membarrier_query
#[cfg(linux_kernel)]
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[repr(u32)]
#[non_exhaustive]
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 CPU identifier as a raw integer.
#[cfg(linux_kernel)]
pub type RawCpuid = u32;
#[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;

View File

@@ -0,0 +1,3 @@
#[cfg(not(windows))]
pub(crate) mod syscalls;
pub(crate) mod types;

View File

@@ -0,0 +1,526 @@
//! libc syscalls supporting `rustix::time`.
use crate::backend::c;
use crate::backend::conv::ret;
#[cfg(any(
linux_kernel,
target_os = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
target_os = "netbsd"
))]
#[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(not(fix_y2038))]
use crate::timespec::as_libc_timespec_mut_ptr;
#[cfg(not(fix_y2038))]
#[cfg(not(any(
target_os = "redox",
target_os = "wasi",
all(apple, not(target_os = "macos"))
)))]
use crate::timespec::as_libc_timespec_ptr;
#[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 = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
target_os = "netbsd"
))]
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 = "freebsd", target_os = "fuchsia"))]
#[cfg(all(target_env = "gnu", fix_y2038))]
weak!(fn __timerfd_gettime64(c::c_int, *mut LibcItimerspec) -> c::c_int);
#[cfg(any(linux_kernel, target_os = "freebsd", target_os = "fuchsia"))]
#[cfg(all(target_env = "gnu", fix_y2038))]
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, as_libc_timespec_mut_ptr(&mut timespec));
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,
as_libc_timespec_mut_ptr(&mut timespec),
))
.unwrap();
let timespec = timespec.assume_init();
#[cfg(apple)]
let timespec = fix_negative_timespec_nsecs(timespec);
timespec
}
}
#[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 as _;
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(any(linux_kernel, target_os = "fuchsia"))]
DynamicClockId::RealtimeAlarm => c::CLOCK_REALTIME_ALARM,
#[cfg(linux_kernel)]
DynamicClockId::Tai => c::CLOCK_TAI,
#[cfg(any(
linux_kernel,
target_os = "freebsd",
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,
as_libc_timespec_mut_ptr(&mut timespec),
))?;
let timespec = timespec.assume_init();
#[cfg(apple)]
let timespec = fix_negative_timespec_nsecs(timespec);
Ok(timespec)
}
}
#[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,
as_libc_timespec_ptr(&timespec),
))
}
}
#[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 = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
target_os = "netbsd"
))]
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 = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
target_os = "netbsd"
))]
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 {
use crate::backend::time::types::{as_libc_itimerspec_mut_ptr, as_libc_itimerspec_ptr};
let mut result = MaybeUninit::<LibcItimerspec>::uninit();
ret(c::timerfd_settime(
borrowed_fd(fd),
bitflags_bits!(flags),
as_libc_itimerspec_ptr(new_value),
as_libc_itimerspec_mut_ptr(&mut result),
))?;
Ok(result.assume_init())
}
}
#[cfg(any(
linux_kernel,
target_os = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
target_os = "netbsd"
))]
#[cfg(fix_y2038)]
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 = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
target_os = "netbsd"
))]
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 {
use crate::backend::time::types::as_libc_itimerspec_mut_ptr;
let mut result = MaybeUninit::<LibcItimerspec>::uninit();
ret(c::timerfd_gettime(
borrowed_fd(fd),
as_libc_itimerspec_mut_ptr(&mut result),
))?;
Ok(result.assume_init())
}
}
#[cfg(any(
linux_kernel,
target_os = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
target_os = "netbsd"
))]
#[cfg(fix_y2038)]
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 _,
},
})
}
/// See [`crate::timespec::fix_negative_nsecs`] for details.
#[cfg(apple)]
#[cfg(not(fix_y2038))]
fn fix_negative_timespec_nsecs(mut ts: Timespec) -> Timespec {
let (sec, nsec) = crate::timespec::fix_negative_nsecs(ts.tv_sec as _, ts.tv_nsec as _);
ts.tv_sec = sec as _;
ts.tv_nsec = nsec as _;
ts
}

View File

@@ -0,0 +1,266 @@
#[cfg(any(
linux_kernel,
target_os = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
target_os = "netbsd"
))]
use crate::backend::c;
#[cfg(any(
linux_kernel,
target_os = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
target_os = "netbsd"
))]
use crate::time::Itimerspec;
#[cfg(any(
linux_kernel,
target_os = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
target_os = "netbsd"
))]
#[cfg(fix_y2038)]
use crate::timespec::LibcTimespec;
#[cfg(any(
linux_kernel,
target_os = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
target_os = "netbsd"
))]
use bitflags::bitflags;
/// On most platforms, `LibcItimerspec` is just `Itimerspec`.
#[cfg(any(
linux_kernel,
target_os = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
target_os = "netbsd"
))]
#[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 = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
target_os = "netbsd"
))]
#[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 = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
target_os = "netbsd"
))]
#[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 = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
target_os = "netbsd"
))]
#[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 = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
target_os = "netbsd"
))]
#[cfg(not(fix_y2038))]
pub(crate) fn as_libc_itimerspec_ptr(itimerspec: &Itimerspec) -> *const c::itimerspec {
#[cfg(test)]
{
assert_eq_size!(Itimerspec, c::itimerspec);
}
crate::utils::as_ptr(itimerspec).cast::<c::itimerspec>()
}
#[cfg(any(
linux_kernel,
target_os = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
target_os = "netbsd"
))]
#[cfg(not(fix_y2038))]
pub(crate) fn as_libc_itimerspec_mut_ptr(
itimerspec: &mut core::mem::MaybeUninit<Itimerspec>,
) -> *mut c::itimerspec {
#[cfg(test)]
{
assert_eq_size!(Itimerspec, c::itimerspec);
}
itimerspec.as_mut_ptr().cast::<c::itimerspec>()
}
#[cfg(any(
linux_kernel,
target_os = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
target_os = "netbsd"
))]
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 = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
target_os = "netbsd"
))]
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(not(target_os = "fuchsia"))]
#[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 = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
target_os = "netbsd"
))]
#[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")]
#[cfg(any(linux_kernel, target_os = "fuchsia", target_os = "openbsd"))]
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.
#[cfg(linux_kernel)]
#[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.
#[cfg(any(linux_kernel, target_os = "cygwin", target_os = "fuchsia"))]
#[doc(alias = "CLOCK_BOOTTIME_ALARM")]
BoottimeAlarm = bitcast!(c::CLOCK_BOOTTIME_ALARM),
}
#[cfg(test)]
mod tests {
#[allow(unused_imports)]
use super::*;
#[cfg(any(
linux_kernel,
target_os = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
target_os = "netbsd"
))]
#[test]
fn test_types() {
assert_eq_size!(TimerfdFlags, c::c_int);
assert_eq_size!(TimerfdTimerFlags, c::c_int);
}
}

View File

@@ -0,0 +1 @@
pub(crate) mod syscalls;

View File

@@ -0,0 +1,42 @@
use crate::backend::c;
use crate::ugid::{Gid, RawGid, RawUid, Uid};
#[cfg(not(target_os = "wasi"))]
#[inline]
#[must_use]
pub(crate) fn getuid() -> Uid {
unsafe {
let uid = c::getuid() as RawUid;
Uid::from_raw(uid)
}
}
#[cfg(not(target_os = "wasi"))]
#[inline]
#[must_use]
pub(crate) fn geteuid() -> Uid {
unsafe {
let uid = c::geteuid() as RawUid;
Uid::from_raw(uid)
}
}
#[cfg(not(target_os = "wasi"))]
#[inline]
#[must_use]
pub(crate) fn getgid() -> Gid {
unsafe {
let gid = c::getgid() as RawGid;
Gid::from_raw(gid)
}
}
#[cfg(not(target_os = "wasi"))]
#[inline]
#[must_use]
pub(crate) fn getegid() -> Gid {
unsafe {
let gid = c::getegid() as RawGid;
Gid::from_raw(gid)
}
}

View File

@@ -0,0 +1,67 @@
//! 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 `i32`.
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, *,
};
// Windows doesn't have `timespec`, just `timeval`. Rustix only uses `timespec`
// in its public API. So define one, and we'll convert it internally.
pub struct timespec {
pub tv_sec: time_t,
pub tv_nsec: crate::ffi::c_long,
}
pub type time_t = i64;

View File

@@ -0,0 +1,269 @@
//! aarch64 Linux system calls.
use crate::backend::reg::{
ArgReg, FromAsm, RetReg, SyscallNumber, ToAsm as _, A0, A1, A2, A3, A4, A5, R0,
};
use core::arch::asm;
#[cfg(target_pointer_width = "32")]
compile_error!("arm64-ilp32 is not supported yet");
#[inline]
pub(in crate::backend) unsafe fn syscall0_readonly(nr: SyscallNumber<'_>) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("x8") nr.to_asm(),
lateout("x0") r0,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("x8") nr.to_asm(),
inlateout("x0") a0.to_asm() => r0,
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("x8") nr.to_asm(),
inlateout("x0") a0.to_asm() => r0,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_noreturn(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> ! {
asm!(
"svc 0",
"brk #0x1",
in("x8") nr.to_asm(),
in("x0") a0.to_asm(),
options(nostack, noreturn)
)
}
#[inline]
pub(in crate::backend) unsafe fn syscall2(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("x8") nr.to_asm(),
inlateout("x0") a0.to_asm() => r0,
in("x1") a1.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall2_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("x8") nr.to_asm(),
inlateout("x0") a0.to_asm() => r0,
in("x1") a1.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall3(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("x8") nr.to_asm(),
inlateout("x0") a0.to_asm() => r0,
in("x1") a1.to_asm(),
in("x2") a2.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall3_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("x8") nr.to_asm(),
inlateout("x0") a0.to_asm() => r0,
in("x1") a1.to_asm(),
in("x2") a2.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall4(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("x8") nr.to_asm(),
inlateout("x0") a0.to_asm() => r0,
in("x1") a1.to_asm(),
in("x2") a2.to_asm(),
in("x3") a3.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall4_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("x8") nr.to_asm(),
inlateout("x0") a0.to_asm() => r0,
in("x1") a1.to_asm(),
in("x2") a2.to_asm(),
in("x3") a3.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall5(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("x8") nr.to_asm(),
inlateout("x0") a0.to_asm() => r0,
in("x1") a1.to_asm(),
in("x2") a2.to_asm(),
in("x3") a3.to_asm(),
in("x4") a4.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall5_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("x8") nr.to_asm(),
inlateout("x0") a0.to_asm() => r0,
in("x1") a1.to_asm(),
in("x2") a2.to_asm(),
in("x3") a3.to_asm(),
in("x4") a4.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall6(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("x8") nr.to_asm(),
inlateout("x0") a0.to_asm() => r0,
in("x1") a1.to_asm(),
in("x2") a2.to_asm(),
in("x3") a3.to_asm(),
in("x4") a4.to_asm(),
in("x5") a5.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall6_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("x8") nr.to_asm(),
inlateout("x0") a0.to_asm() => r0,
in("x1") a1.to_asm(),
in("x2") a2.to_asm(),
in("x3") a3.to_asm(),
in("x4") a4.to_asm(),
in("x5") a5.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}

View File

@@ -0,0 +1,266 @@
//! arm Linux system calls.
use crate::backend::reg::{
ArgReg, FromAsm, RetReg, SyscallNumber, ToAsm as _, A0, A1, A2, A3, A4, A5, R0,
};
use core::arch::asm;
#[inline]
pub(in crate::backend) unsafe fn syscall0_readonly(nr: SyscallNumber<'_>) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("r7") nr.to_asm(),
lateout("r0") r0,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("r7") nr.to_asm(),
inlateout("r0") a0.to_asm() => r0,
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("r7") nr.to_asm(),
inlateout("r0") a0.to_asm() => r0,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_noreturn(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> ! {
asm!(
"svc 0",
"udf #16",
in("r7") nr.to_asm(),
in("r0") a0.to_asm(),
options(nostack, noreturn)
)
}
#[inline]
pub(in crate::backend) unsafe fn syscall2(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("r7") nr.to_asm(),
inlateout("r0") a0.to_asm() => r0,
in("r1") a1.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall2_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("r7") nr.to_asm(),
inlateout("r0") a0.to_asm() => r0,
in("r1") a1.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall3(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("r7") nr.to_asm(),
inlateout("r0") a0.to_asm() => r0,
in("r1") a1.to_asm(),
in("r2") a2.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall3_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("r7") nr.to_asm(),
inlateout("r0") a0.to_asm() => r0,
in("r1") a1.to_asm(),
in("r2") a2.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall4(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("r7") nr.to_asm(),
inlateout("r0") a0.to_asm() => r0,
in("r1") a1.to_asm(),
in("r2") a2.to_asm(),
in("r3") a3.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall4_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("r7") nr.to_asm(),
inlateout("r0") a0.to_asm() => r0,
in("r1") a1.to_asm(),
in("r2") a2.to_asm(),
in("r3") a3.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall5(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("r7") nr.to_asm(),
inlateout("r0") a0.to_asm() => r0,
in("r1") a1.to_asm(),
in("r2") a2.to_asm(),
in("r3") a3.to_asm(),
in("r4") a4.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall5_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("r7") nr.to_asm(),
inlateout("r0") a0.to_asm() => r0,
in("r1") a1.to_asm(),
in("r2") a2.to_asm(),
in("r3") a3.to_asm(),
in("r4") a4.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall6(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("r7") nr.to_asm(),
inlateout("r0") a0.to_asm() => r0,
in("r1") a1.to_asm(),
in("r2") a2.to_asm(),
in("r3") a3.to_asm(),
in("r4") a4.to_asm(),
in("r5") a5.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall6_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("r7") nr.to_asm(),
inlateout("r0") a0.to_asm() => r0,
in("r1") a1.to_asm(),
in("r2") a2.to_asm(),
in("r3") a3.to_asm(),
in("r4") a4.to_asm(),
in("r5") a5.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}

View File

@@ -0,0 +1,544 @@
//! mipsel Linux system calls.
//!
//! On mipsel, Linux indicates success or failure using `$a3` rather
//! than by returning a negative error code as most other architectures do.
//!
//! MIPS-family platforms have a special calling convention for `__NR_pipe`,
//! however we use `__NR_pipe2` instead to avoid having to implement it.
use crate::backend::reg::{
ArgReg, FromAsm, RetReg, SyscallNumber, ToAsm as _, A0, A1, A2, A3, A4, A5, A6, R0,
};
use core::arch::asm;
#[inline]
pub(in crate::backend) unsafe fn syscall0_readonly(nr: SyscallNumber) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
lateout("$7" /*$a3*/) err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall1(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_noreturn(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> ! {
asm!(
"syscall",
"teq $0,$0",
in("$2" /*$v0*/) nr.to_asm(),
in("$4" /*$a0*/) a0.to_asm(),
options(nostack, noreturn)
)
}
#[inline]
pub(in crate::backend) unsafe fn syscall2(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall2_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall3(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall3_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall4(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall4_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall5(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
".set noat",
"subu $sp, 32",
"sw {}, 16($sp)",
"syscall",
"addu $sp, 32",
".set at",
in(reg) a4.to_asm(),
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall5_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
".set noat",
"subu $sp, 32",
"sw {}, 16($sp)",
"syscall",
"addu $sp, 32",
".set at",
in(reg) a4.to_asm(),
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall6(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
".set noat",
"subu $sp, 32",
"sw {}, 16($sp)",
"sw {}, 20($sp)",
"syscall",
"addu $sp, 32",
".set at",
in(reg) a4.to_asm(),
in(reg) a5.to_asm(),
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall6_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
".set noat",
"subu $sp, 32",
"sw {}, 16($sp)",
"sw {}, 20($sp)",
"syscall",
"addu $sp, 32",
".set at",
in(reg) a4.to_asm(),
in(reg) a5.to_asm(),
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall7_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
a6: ArgReg<'_, A6>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
".set noat",
"subu $sp, 32",
"sw {}, 16($sp)",
"sw {}, 20($sp)",
"sw {}, 24($sp)",
"syscall",
"addu $sp, 32",
".set at",
in(reg) a4.to_asm(),
in(reg) a5.to_asm(),
in(reg) a6.to_asm(),
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}

View File

@@ -0,0 +1,544 @@
//! mipsisa32r6el Linux system calls.
//!
//! On mipsisa32r6el, Linux indicates success or failure using `$a3` rather
//! than by returning a negative error code as most other architectures do.
//!
//! MIPS-family platforms have a special calling convention for `__NR_pipe`,
//! however we use `__NR_pipe2` instead to avoid having to implement it.
use crate::backend::reg::{
ArgReg, FromAsm, RetReg, SyscallNumber, ToAsm as _, A0, A1, A2, A3, A4, A5, A6, R0,
};
use core::arch::asm;
#[inline]
pub(in crate::backend) unsafe fn syscall0_readonly(nr: SyscallNumber) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
lateout("$7" /*$a3*/) err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall1(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_noreturn(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> ! {
asm!(
"syscall",
"teq $0,$0",
in("$2" /*$v0*/) nr.to_asm(),
in("$4" /*$a0*/) a0.to_asm(),
options(nostack, noreturn)
)
}
#[inline]
pub(in crate::backend) unsafe fn syscall2(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall2_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall3(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall3_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall4(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall4_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall5(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
".set noat",
"subu $sp, 32",
"sw {}, 16($sp)",
"syscall",
"addu $sp, 32",
".set at",
in(reg) a4.to_asm(),
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall5_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
".set noat",
"subu $sp, 32",
"sw {}, 16($sp)",
"syscall",
"addu $sp, 32",
".set at",
in(reg) a4.to_asm(),
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall6(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
".set noat",
"subu $sp, 32",
"sw {}, 16($sp)",
"sw {}, 20($sp)",
"syscall",
"addu $sp, 32",
".set at",
in(reg) a4.to_asm(),
in(reg) a5.to_asm(),
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall6_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
".set noat",
"subu $sp, 32",
"sw {}, 16($sp)",
"sw {}, 20($sp)",
"syscall",
"addu $sp, 32",
".set at",
in(reg) a4.to_asm(),
in(reg) a5.to_asm(),
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall7_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
a6: ArgReg<'_, A6>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
".set noat",
"subu $sp, 32",
"sw {}, 16($sp)",
"sw {}, 20($sp)",
"sw {}, 24($sp)",
"syscall",
"addu $sp, 32",
".set at",
in(reg) a4.to_asm(),
in(reg) a5.to_asm(),
in(reg) a6.to_asm(),
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}

View File

@@ -0,0 +1,467 @@
//! mips64el Linux system calls.
//!
//! On mips64el, Linux indicates success or failure using `$a3` (`$7`) rather
//! than by returning a negative error code as most other architectures do.
//!
//! MIPS-family platforms have a special calling convention for `__NR_pipe`,
//! however we use `__NR_pipe2` instead to avoid having to implement it.
use crate::backend::reg::{
ArgReg, FromAsm, RetReg, SyscallNumber, ToAsm as _, A0, A1, A2, A3, A4, A5, R0,
};
use core::arch::asm;
#[inline]
pub(in crate::backend) unsafe fn syscall0_readonly(nr: SyscallNumber) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
lateout("$7" /*$a3*/) err,
lateout("$8" /*$a4*/) _,
lateout("$9" /*$a5*/) _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall1(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$a4*/) _,
lateout("$9" /*$a5*/) _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$a4*/) _,
lateout("$9" /*$a5*/) _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_noreturn(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> ! {
asm!(
"syscall",
"teq $0,$0",
in("$2" /*$v0*/) nr.to_asm(),
in("$4" /*$a0*/) a0.to_asm(),
options(nostack, noreturn)
)
}
#[inline]
pub(in crate::backend) unsafe fn syscall2(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$a4*/) _,
lateout("$9" /*$a5*/) _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall2_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$a4*/) _,
lateout("$9" /*$a5*/) _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall3(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$a4*/) _,
lateout("$9" /*$a5*/) _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall3_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$a4*/) _,
lateout("$9" /*$a5*/) _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall4(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
lateout("$8" /*$a4*/) _,
lateout("$9" /*$a5*/) _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall4_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
lateout("$8" /*$a4*/) _,
lateout("$9" /*$a5*/) _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall5(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
inlateout("$8" /*$a4*/) a4.to_asm() => _,
lateout("$9" /*$a5*/) _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall5_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
inlateout("$8" /*$a4*/) a4.to_asm() => _,
lateout("$9" /*$a5*/) _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall6(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
inlateout("$8" /*$a4*/) a4.to_asm() => _,
inlateout("$9" /*$a5*/) a5.to_asm() => _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall6_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
inlateout("$8" /*$a4*/) a4.to_asm() => _,
inlateout("$9" /*$a5*/) a5.to_asm() => _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}

View File

@@ -0,0 +1,471 @@
//! mipsisa64r6el Linux system calls.
//!
//! On mipsisa64r6el, Linux indicates success or failure using `$a3` (`$7`)
//! rather than by returning a negative error code as most other architectures
//! do.
//!
//! MIPS-family platforms have a special calling convention for `__NR_pipe`,
//! however we use `__NR_pipe2` instead to avoid having to implement it.
//!
//! MIPS R6 inline assembly currently doesn't differ from MIPS, because no
//! explicit call of R6-only or R2-only instructions exist here.
use crate::backend::reg::{
ArgReg, FromAsm, RetReg, SyscallNumber, ToAsm as _, A0, A1, A2, A3, A4, A5, R0,
};
use core::arch::asm;
#[inline]
pub(in crate::backend) unsafe fn syscall0_readonly(nr: SyscallNumber) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
lateout("$7" /*$a3*/) err,
lateout("$8" /*$a4*/) _,
lateout("$9" /*$a5*/) _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall1(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$a4*/) _,
lateout("$9" /*$a5*/) _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$a4*/) _,
lateout("$9" /*$a5*/) _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_noreturn(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> ! {
asm!(
"syscall",
"teq $0,$0",
in("$2" /*$v0*/) nr.to_asm(),
in("$4" /*$a0*/) a0.to_asm(),
options(nostack, noreturn)
)
}
#[inline]
pub(in crate::backend) unsafe fn syscall2(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$a4*/) _,
lateout("$9" /*$a5*/) _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall2_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$a4*/) _,
lateout("$9" /*$a5*/) _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall3(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$a4*/) _,
lateout("$9" /*$a5*/) _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall3_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$a4*/) _,
lateout("$9" /*$a5*/) _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall4(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
lateout("$8" /*$a4*/) _,
lateout("$9" /*$a5*/) _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall4_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
lateout("$8" /*$a4*/) _,
lateout("$9" /*$a5*/) _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall5(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
inlateout("$8" /*$a4*/) a4.to_asm() => _,
lateout("$9" /*$a5*/) _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall5_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
inlateout("$8" /*$a4*/) a4.to_asm() => _,
lateout("$9" /*$a5*/) _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall6(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
inlateout("$8" /*$a4*/) a4.to_asm() => _,
inlateout("$9" /*$a5*/) a5.to_asm() => _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall6_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
inlateout("$8" /*$a4*/) a4.to_asm() => _,
inlateout("$9" /*$a5*/) a5.to_asm() => _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}

View File

@@ -0,0 +1,321 @@
//! Architecture-specific syscall code.
//!
//! This module also has a `choose` submodule which chooses a scheme and is
//! what most of the `rustix` syscalls use.
//!
//! Compilers should really have intrinsics for making system calls. They're
//! much like regular calls, with custom calling conventions, and calling
//! conventions are otherwise the compiler's job. But for now, use inline asm.
//!
//! The calling conventions for Linux syscalls are [documented here].
//!
//! [documented here]: https://man7.org/linux/man-pages/man2/syscall.2.html
//!
//! # Safety
//!
//! This contains the inline `asm` statements performing the syscall
//! instructions.
#![allow(unsafe_code)]
#![cfg_attr(not(feature = "all-apis"), allow(unused_imports))]
// We'll use as many arguments as syscalls need.
#![allow(clippy::too_many_arguments)]
// These functions always use the machine's syscall instruction, even when it
// isn't the fastest option available.
#[cfg_attr(target_arch = "aarch64", path = "aarch64.rs")]
#[cfg_attr(all(target_arch = "arm", not(thumb_mode)), path = "arm.rs")]
#[cfg_attr(all(target_arch = "arm", thumb_mode), path = "thumb.rs")]
#[cfg_attr(target_arch = "mips", path = "mips.rs")]
#[cfg_attr(target_arch = "mips32r6", path = "mips32r6.rs")]
#[cfg_attr(target_arch = "mips64", path = "mips64.rs")]
#[cfg_attr(target_arch = "mips64r6", path = "mips64r6.rs")]
#[cfg_attr(target_arch = "powerpc", path = "powerpc.rs")]
#[cfg_attr(target_arch = "powerpc64", path = "powerpc64.rs")]
#[cfg_attr(target_arch = "riscv64", path = "riscv64.rs")]
#[cfg_attr(target_arch = "s390x", path = "s390x.rs")]
#[cfg_attr(target_arch = "x86", path = "x86.rs")]
#[cfg_attr(target_arch = "x86_64", path = "x86_64.rs")]
pub(in crate::backend) mod asm;
// On most architectures, the architecture syscall instruction is fast, so use
// it directly.
#[cfg(any(
target_arch = "arm",
target_arch = "aarch64",
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "riscv64",
target_arch = "s390x",
target_arch = "x86_64",
))]
pub(in crate::backend) use self::asm as choose;
// On 32-bit x86, use vDSO wrappers for all syscalls. We could use the
// architecture syscall instruction (`int 0x80`), but the vDSO
// `__kernel_vsyscall` mechanism is much faster.
#[cfg(target_arch = "x86")]
pub(in crate::backend) use super::vdso_wrappers::x86_via_vdso as choose;
// This would be the code for always using `int 0x80` on 32-bit x86.
//#[cfg(target_arch = "x86")]
//pub(in crate::backend) use self::asm as choose;
// Macros for invoking system calls.
//
// These factor out:
// - Calling `nr` on the syscall number to convert it into `SyscallNumber`.
// - Calling `.into()` on each of the arguments to convert them into `ArgReg`.
// - Qualifying the `syscall*` and `__NR_*` identifiers.
// - Counting the number of arguments.
macro_rules! syscall {
($nr:ident) => {
$crate::backend::arch::choose::syscall0($crate::backend::reg::nr(
linux_raw_sys::general::$nr,
))
};
($nr:ident, $a0:expr) => {
$crate::backend::arch::choose::syscall1(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
)
};
($nr:ident, $a0:expr, $a1:expr) => {
$crate::backend::arch::choose::syscall2(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
$a1.into(),
)
};
($nr:ident, $a0:expr, $a1:expr, $a2:expr) => {
$crate::backend::arch::choose::syscall3(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
$a1.into(),
$a2.into(),
)
};
($nr:ident, $a0:expr, $a1:expr, $a2:expr, $a3:expr) => {
$crate::backend::arch::choose::syscall4(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
$a1.into(),
$a2.into(),
$a3.into(),
)
};
($nr:ident, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr) => {
$crate::backend::arch::choose::syscall5(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
$a1.into(),
$a2.into(),
$a3.into(),
$a4.into(),
)
};
($nr:ident, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr, $a5:expr) => {
$crate::backend::arch::choose::syscall6(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
$a1.into(),
$a2.into(),
$a3.into(),
$a4.into(),
$a5.into(),
)
};
($nr:ident, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr, $a5:expr, $a6:expr) => {
$crate::backend::arch::choose::syscall7(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
$a1.into(),
$a2.into(),
$a3.into(),
$a4.into(),
$a5.into(),
$a6.into(),
)
};
}
// Macro to invoke a syscall that always uses direct assembly, rather than the
// vDSO. Useful when still finding the vDSO.
#[allow(unused_macros)]
macro_rules! syscall_always_asm {
($nr:ident) => {
$crate::backend::arch::asm::syscall0($crate::backend::reg::nr(linux_raw_sys::general::$nr))
};
($nr:ident, $a0:expr) => {
$crate::backend::arch::asm::syscall1(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
)
};
($nr:ident, $a0:expr, $a1:expr) => {
$crate::backend::arch::asm::syscall2(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
$a1.into(),
)
};
($nr:ident, $a0:expr, $a1:expr, $a2:expr) => {
$crate::backend::arch::asm::syscall3(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
$a1.into(),
$a2.into(),
)
};
($nr:ident, $a0:expr, $a1:expr, $a2:expr, $a3:expr) => {
$crate::backend::arch::asm::syscall4(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
$a1.into(),
$a2.into(),
$a3.into(),
)
};
($nr:ident, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr) => {
$crate::backend::arch::asm::syscall5(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
$a1.into(),
$a2.into(),
$a3.into(),
$a4.into(),
)
};
($nr:ident, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr, $a5:expr) => {
$crate::backend::arch::asm::syscall6(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
$a1.into(),
$a2.into(),
$a3.into(),
$a4.into(),
$a5.into(),
)
};
($nr:ident, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr, $a5:expr, $a6:expr) => {
$crate::backend::arch::asm::syscall7(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
$a1.into(),
$a2.into(),
$a3.into(),
$a4.into(),
$a5.into(),
$a6.into(),
)
};
}
/// Like `syscall`, but adds the `readonly` attribute to the inline asm, which
/// indicates that the syscall does not mutate any memory.
macro_rules! syscall_readonly {
($nr:ident) => {
$crate::backend::arch::choose::syscall0_readonly($crate::backend::reg::nr(
linux_raw_sys::general::$nr,
))
};
($nr:ident, $a0:expr) => {
$crate::backend::arch::choose::syscall1_readonly(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
)
};
($nr:ident, $a0:expr, $a1:expr) => {
$crate::backend::arch::choose::syscall2_readonly(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
$a1.into(),
)
};
($nr:ident, $a0:expr, $a1:expr, $a2:expr) => {
$crate::backend::arch::choose::syscall3_readonly(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
$a1.into(),
$a2.into(),
)
};
($nr:ident, $a0:expr, $a1:expr, $a2:expr, $a3:expr) => {
$crate::backend::arch::choose::syscall4_readonly(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
$a1.into(),
$a2.into(),
$a3.into(),
)
};
($nr:ident, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr) => {
$crate::backend::arch::choose::syscall5_readonly(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
$a1.into(),
$a2.into(),
$a3.into(),
$a4.into(),
)
};
($nr:ident, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr, $a5:expr) => {
$crate::backend::arch::choose::syscall6_readonly(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
$a1.into(),
$a2.into(),
$a3.into(),
$a4.into(),
$a5.into(),
)
};
($nr:ident, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr, $a5:expr, $a6:expr) => {
$crate::backend::arch::choose::syscall7_readonly(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
$a1.into(),
$a2.into(),
$a3.into(),
$a4.into(),
$a5.into(),
$a6.into(),
)
};
}
/// Like `syscall`, but indicates that the syscall does not return.
#[cfg(feature = "runtime")]
macro_rules! syscall_noreturn {
($nr:ident, $a0:expr) => {
$crate::backend::arch::choose::syscall1_noreturn(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
)
};
}

View File

@@ -0,0 +1,414 @@
//! powerpc Linux system calls.
//!
//! On powerpc, Linux indicates success or failure using `cr0.SO` rather
//! than by returning a negative error code as most other architectures do. In
//! theory we could immediately translate this into a `Result`, and it'd save a
//! few branches. And in theory we could have specialized sequences for use
//! with syscalls that are known to never fail. However, those would require
//! more extensive changes in rustix's platform-independent code. For now, we
//! check the flag and negate the error value to make PowerPC look like other
//! architectures.
use crate::backend::reg::{
ArgReg, FromAsm, RetReg, SyscallNumber, ToAsm, A0, A1, A2, A3, A4, A5, R0,
};
use core::arch::asm;
#[inline]
pub(in crate::backend) unsafe fn syscall0_readonly(nr: SyscallNumber<'_>) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
lateout("r3") r0,
lateout("r4") _,
lateout("r5") _,
lateout("r6") _,
lateout("r7") _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
lateout("r4") _,
lateout("r5") _,
lateout("r6") _,
lateout("r7") _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
lateout("r4") _,
lateout("r5") _,
lateout("r6") _,
lateout("r7") _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_noreturn(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> ! {
asm!(
"sc",
"trap",
in("r0") nr.to_asm(),
in("r3") a0.to_asm(),
options(nostack, noreturn)
)
}
#[inline]
pub(in crate::backend) unsafe fn syscall2(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
inlateout("r4") a1.to_asm() => _,
lateout("r5") _,
lateout("r6") _,
lateout("r7") _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall2_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
inlateout("r4") a1.to_asm() => _,
lateout("r5") _,
lateout("r6") _,
lateout("r7") _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall3(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
inlateout("r4") a1.to_asm() => _,
inlateout("r5") a2.to_asm() => _,
lateout("r6") _,
lateout("r7") _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall3_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
inlateout("r4") a1.to_asm() => _,
inlateout("r5") a2.to_asm() => _,
lateout("r6") _,
lateout("r7") _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall4(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
inlateout("r4") a1.to_asm() => _,
inlateout("r5") a2.to_asm() => _,
inlateout("r6") a3.to_asm() => _,
lateout("r7") _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall4_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
inlateout("r4") a1.to_asm() => _,
inlateout("r5") a2.to_asm() => _,
inlateout("r6") a3.to_asm() => _,
lateout("r7") _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall5(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
inlateout("r4") a1.to_asm() => _,
inlateout("r5") a2.to_asm() => _,
inlateout("r6") a3.to_asm() => _,
inlateout("r7") a4.to_asm() => _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall5_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
inlateout("r4") a1.to_asm() => _,
inlateout("r5") a2.to_asm() => _,
inlateout("r6") a3.to_asm() => _,
inlateout("r7") a4.to_asm() => _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall6(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
inlateout("r4") a1.to_asm() => _,
inlateout("r5") a2.to_asm() => _,
inlateout("r6") a3.to_asm() => _,
inlateout("r7") a4.to_asm() => _,
inlateout("r8") a5.to_asm() => _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall6_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
inlateout("r4") a1.to_asm() => _,
inlateout("r5") a2.to_asm() => _,
inlateout("r6") a3.to_asm() => _,
inlateout("r7") a4.to_asm() => _,
inlateout("r8") a5.to_asm() => _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}

View File

@@ -0,0 +1,414 @@
//! powerpc64le Linux system calls.
//!
//! On powerpc64le, Linux indicates success or failure using `cr0.SO` rather
//! than by returning a negative error code as most other architectures do. In
//! theory we could immediately translate this into a `Result`, and it'd save a
//! few branches. And in theory we could have specialized sequences for use
//! with syscalls that are known to never fail. However, those would require
//! more extensive changes in rustix's platform-independent code. For now, we
//! check the flag and negate the error value to make PowerPC64 look like other
//! architectures.
use crate::backend::reg::{
ArgReg, FromAsm, RetReg, SyscallNumber, ToAsm as _, A0, A1, A2, A3, A4, A5, R0,
};
use core::arch::asm;
#[inline]
pub(in crate::backend) unsafe fn syscall0_readonly(nr: SyscallNumber<'_>) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
lateout("r3") r0,
lateout("r4") _,
lateout("r5") _,
lateout("r6") _,
lateout("r7") _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
lateout("r4") _,
lateout("r5") _,
lateout("r6") _,
lateout("r7") _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
lateout("r4") _,
lateout("r5") _,
lateout("r6") _,
lateout("r7") _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_noreturn(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> ! {
asm!(
"sc",
"trap",
in("r0") nr.to_asm(),
in("r3") a0.to_asm(),
options(nostack, noreturn)
)
}
#[inline]
pub(in crate::backend) unsafe fn syscall2(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
inlateout("r4") a1.to_asm() => _,
lateout("r5") _,
lateout("r6") _,
lateout("r7") _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall2_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
inlateout("r4") a1.to_asm() => _,
lateout("r5") _,
lateout("r6") _,
lateout("r7") _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall3(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
inlateout("r4") a1.to_asm() => _,
inlateout("r5") a2.to_asm() => _,
lateout("r6") _,
lateout("r7") _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall3_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
inlateout("r4") a1.to_asm() => _,
inlateout("r5") a2.to_asm() => _,
lateout("r6") _,
lateout("r7") _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall4(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
inlateout("r4") a1.to_asm() => _,
inlateout("r5") a2.to_asm() => _,
inlateout("r6") a3.to_asm() => _,
lateout("r7") _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall4_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
inlateout("r4") a1.to_asm() => _,
inlateout("r5") a2.to_asm() => _,
inlateout("r6") a3.to_asm() => _,
lateout("r7") _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall5(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
inlateout("r4") a1.to_asm() => _,
inlateout("r5") a2.to_asm() => _,
inlateout("r6") a3.to_asm() => _,
inlateout("r7") a4.to_asm() => _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall5_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
inlateout("r4") a1.to_asm() => _,
inlateout("r5") a2.to_asm() => _,
inlateout("r6") a3.to_asm() => _,
inlateout("r7") a4.to_asm() => _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall6(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
inlateout("r4") a1.to_asm() => _,
inlateout("r5") a2.to_asm() => _,
inlateout("r6") a3.to_asm() => _,
inlateout("r7") a4.to_asm() => _,
inlateout("r8") a5.to_asm() => _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall6_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
inlateout("r4") a1.to_asm() => _,
inlateout("r5") a2.to_asm() => _,
inlateout("r6") a3.to_asm() => _,
inlateout("r7") a4.to_asm() => _,
inlateout("r8") a5.to_asm() => _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}

View File

@@ -0,0 +1,266 @@
//! riscv64 Linux system calls.
use crate::backend::reg::{
ArgReg, FromAsm, RetReg, SyscallNumber, ToAsm as _, A0, A1, A2, A3, A4, A5, R0,
};
use core::arch::asm;
#[inline]
pub(in crate::backend) unsafe fn syscall0_readonly(nr: SyscallNumber<'_>) -> RetReg<R0> {
let r0;
asm!(
"ecall",
in("a7") nr.to_asm(),
lateout("a0") r0,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> RetReg<R0> {
let r0;
asm!(
"ecall",
in("a7") nr.to_asm(),
inlateout("a0") a0.to_asm() => r0,
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
) -> RetReg<R0> {
let r0;
asm!(
"ecall",
in("a7") nr.to_asm(),
inlateout("a0") a0.to_asm() => r0,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_noreturn(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> ! {
asm!(
"ecall",
"unimp",
in("a7") nr.to_asm(),
in("a0") a0.to_asm(),
options(nostack, noreturn)
);
}
#[inline]
pub(in crate::backend) unsafe fn syscall2(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let r0;
asm!(
"ecall",
in("a7") nr.to_asm(),
inlateout("a0") a0.to_asm() => r0,
in("a1") a1.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall2_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let r0;
asm!(
"ecall",
in("a7") nr.to_asm(),
inlateout("a0") a0.to_asm() => r0,
in("a1") a1.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall3(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let r0;
asm!(
"ecall",
in("a7") nr.to_asm(),
inlateout("a0") a0.to_asm() => r0,
in("a1") a1.to_asm(),
in("a2") a2.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall3_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let r0;
asm!(
"ecall",
in("a7") nr.to_asm(),
inlateout("a0") a0.to_asm() => r0,
in("a1") a1.to_asm(),
in("a2") a2.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall4(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let r0;
asm!(
"ecall",
in("a7") nr.to_asm(),
inlateout("a0") a0.to_asm() => r0,
in("a1") a1.to_asm(),
in("a2") a2.to_asm(),
in("a3") a3.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall4_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let r0;
asm!(
"ecall",
in("a7") nr.to_asm(),
inlateout("a0") a0.to_asm() => r0,
in("a1") a1.to_asm(),
in("a2") a2.to_asm(),
in("a3") a3.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall5(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let r0;
asm!(
"ecall",
in("a7") nr.to_asm(),
inlateout("a0") a0.to_asm() => r0,
in("a1") a1.to_asm(),
in("a2") a2.to_asm(),
in("a3") a3.to_asm(),
in("a4") a4.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall5_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let r0;
asm!(
"ecall",
in("a7") nr.to_asm(),
inlateout("a0") a0.to_asm() => r0,
in("a1") a1.to_asm(),
in("a2") a2.to_asm(),
in("a3") a3.to_asm(),
in("a4") a4.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall6(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let r0;
asm!(
"ecall",
in("a7") nr.to_asm(),
inlateout("a0") a0.to_asm() => r0,
in("a1") a1.to_asm(),
in("a2") a2.to_asm(),
in("a3") a3.to_asm(),
in("a4") a4.to_asm(),
in("a5") a5.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall6_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let r0;
asm!(
"ecall",
in("a7") nr.to_asm(),
inlateout("a0") a0.to_asm() => r0,
in("a1") a1.to_asm(),
in("a2") a2.to_asm(),
in("a3") a3.to_asm(),
in("a4") a4.to_asm(),
in("a5") a5.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}

View File

@@ -0,0 +1,288 @@
//! s390x Linux system calls.
use crate::backend::reg::{
ArgReg, FromAsm, RetReg, SyscallNumber, ToAsm as _, A0, A1, A2, A3, A4, A5, R0,
};
use core::arch::asm;
#[inline]
pub(in crate::backend) unsafe fn syscall0_readonly(nr: SyscallNumber<'_>) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("r1") nr.to_asm(),
lateout("r2") r0,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("r1") nr.to_asm(),
inlateout("r2") a0.to_asm() => r0,
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("r1") nr.to_asm(),
inlateout("r2") a0.to_asm() => r0,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_noreturn(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> ! {
asm!(
"svc 0",
"j .+2",
in("r1") nr.to_asm(),
in("r2") a0.to_asm(),
options(nostack, noreturn)
)
}
#[inline]
pub(in crate::backend) unsafe fn syscall2(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("r1") nr.to_asm(),
inlateout("r2") a0.to_asm() => r0,
in("r3") a1.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall2_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("r1") nr.to_asm(),
inlateout("r2") a0.to_asm() => r0,
in("r3") a1.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall3(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("r1") nr.to_asm(),
inlateout("r2") a0.to_asm() => r0,
in("r3") a1.to_asm(),
in("r4") a2.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall3_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("r1") nr.to_asm(),
inlateout("r2") a0.to_asm() => r0,
in("r3") a1.to_asm(),
in("r4") a2.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall4(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("r1") nr.to_asm(),
inlateout("r2") a0.to_asm() => r0,
in("r3") a1.to_asm(),
in("r4") a2.to_asm(),
in("r5") a3.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall4_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("r1") nr.to_asm(),
inlateout("r2") a0.to_asm() => r0,
in("r3") a1.to_asm(),
in("r4") a2.to_asm(),
in("r5") a3.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall5(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("r1") nr.to_asm(),
inlateout("r2") a0.to_asm() => r0,
in("r3") a1.to_asm(),
in("r4") a2.to_asm(),
in("r5") a3.to_asm(),
in("r6") a4.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall5_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("r1") nr.to_asm(),
inlateout("r2") a0.to_asm() => r0,
in("r3") a1.to_asm(),
in("r4") a2.to_asm(),
in("r5") a3.to_asm(),
in("r6") a4.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall6(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
if nr.nr == linux_raw_sys::general::__NR_mmap as usize {
let mut a = [
a0.to_asm(),
a1.to_asm(),
a2.to_asm(),
a3.to_asm(),
a4.to_asm(),
a5.to_asm(),
];
return syscall1(nr, a.as_mut_ptr().into());
}
let r0;
asm!(
"svc 0",
in("r1") nr.to_asm(),
inlateout("r2") a0.to_asm() => r0,
in("r3") a1.to_asm(),
in("r4") a2.to_asm(),
in("r5") a3.to_asm(),
in("r6") a4.to_asm(),
in("r7") a5.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall6_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
if nr.nr == linux_raw_sys::general::__NR_mmap as usize {
let a = [
a0.to_asm(),
a1.to_asm(),
a2.to_asm(),
a3.to_asm(),
a4.to_asm(),
a5.to_asm(),
];
return syscall1_readonly(nr, a.as_ptr().into());
}
let r0;
asm!(
"svc 0",
in("r1") nr.to_asm(),
inlateout("r2") a0.to_asm() => r0,
in("r3") a1.to_asm(),
in("r4") a2.to_asm(),
in("r5") a3.to_asm(),
in("r6") a4.to_asm(),
in("r7") a5.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}

View File

@@ -0,0 +1,323 @@
//! arm Linux system calls, using thumb-mode.
//!
//! In thumb-mode, r7 is the frame pointer and is not permitted to be used in
//! an inline asm operand, so we have to use a different register and copy it
//! into r7 inside the inline asm.
use crate::backend::reg::{
ArgReg, FromAsm, RetReg, SyscallNumber, ToAsm as _, A0, A1, A2, A3, A4, A5, R0,
};
use core::arch::asm;
#[inline]
pub(in crate::backend) unsafe fn syscall0_readonly(nr: SyscallNumber<'_>) -> RetReg<R0> {
let r0;
asm!(
"mov {tmp}, r7",
"mov r7, {nr}",
"svc 0",
"mov r7, {tmp}",
nr = in(reg) nr.to_asm(),
tmp = out(reg) _,
lateout("r0") r0,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> RetReg<R0> {
let r0;
asm!(
"mov {tmp}, r7",
"mov r7, {nr}",
"svc 0",
"mov r7, {tmp}",
nr = in(reg) nr.to_asm(),
tmp = out(reg) _,
inlateout("r0") a0.to_asm() => r0,
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
) -> RetReg<R0> {
let r0;
asm!(
"mov {tmp}, r7",
"mov r7, {nr}",
"svc 0",
"mov r7, {tmp}",
nr = in(reg) nr.to_asm(),
tmp = out(reg) _,
inlateout("r0") a0.to_asm() => r0,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_noreturn(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> ! {
asm!(
"mov r7, {nr}",
"svc 0",
"udf #16",
nr = in(reg) nr.to_asm(),
in("r0") a0.to_asm(),
options(nostack, noreturn)
)
}
#[inline]
pub(in crate::backend) unsafe fn syscall2(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let r0;
asm!(
"mov {tmp}, r7",
"mov r7, {nr}",
"svc 0",
"mov r7, {tmp}",
nr = in(reg) nr.to_asm(),
tmp = out(reg) _,
inlateout("r0") a0.to_asm() => r0,
in("r1") a1.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall2_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let r0;
asm!(
"mov {tmp}, r7",
"mov r7, {nr}",
"svc 0",
"mov r7, {tmp}",
nr = in(reg) nr.to_asm(),
tmp = out(reg) _,
inlateout("r0") a0.to_asm() => r0,
in("r1") a1.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall3(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let r0;
asm!(
"mov {tmp}, r7",
"mov r7, {nr}",
"svc 0",
"mov r7, {tmp}",
nr = in(reg) nr.to_asm(),
tmp = out(reg) _,
inlateout("r0") a0.to_asm() => r0,
in("r1") a1.to_asm(),
in("r2") a2.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall3_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let r0;
asm!(
"mov {tmp}, r7",
"mov r7, {nr}",
"svc 0",
"mov r7, {tmp}",
nr = in(reg) nr.to_asm(),
tmp = out(reg) _,
inlateout("r0") a0.to_asm() => r0,
in("r1") a1.to_asm(),
in("r2") a2.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall4(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let r0;
asm!(
"mov {tmp}, r7",
"mov r7, {nr}",
"svc 0",
"mov r7, {tmp}",
nr = in(reg) nr.to_asm(),
tmp = out(reg) _,
inlateout("r0") a0.to_asm() => r0,
in("r1") a1.to_asm(),
in("r2") a2.to_asm(),
in("r3") a3.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall4_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let r0;
asm!(
"mov {tmp}, r7",
"mov r7, {nr}",
"svc 0",
"mov r7, {tmp}",
nr = in(reg) nr.to_asm(),
tmp = out(reg) _,
inlateout("r0") a0.to_asm() => r0,
in("r1") a1.to_asm(),
in("r2") a2.to_asm(),
in("r3") a3.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall5(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let r0;
asm!(
"mov {tmp}, r7",
"mov r7, {nr}",
"svc 0",
"mov r7, {tmp}",
nr = in(reg) nr.to_asm(),
tmp = out(reg) _,
inlateout("r0") a0.to_asm() => r0,
in("r1") a1.to_asm(),
in("r2") a2.to_asm(),
in("r3") a3.to_asm(),
in("r4") a4.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall5_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let r0;
asm!(
"mov {tmp}, r7",
"mov r7, {nr}",
"svc 0",
"mov r7, {tmp}",
nr = in(reg) nr.to_asm(),
tmp = out(reg) _,
inlateout("r0") a0.to_asm() => r0,
in("r1") a1.to_asm(),
in("r2") a2.to_asm(),
in("r3") a3.to_asm(),
in("r4") a4.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall6(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let r0;
asm!(
"mov {tmp}, r7",
"mov r7, {nr}",
"svc 0",
"mov r7, {tmp}",
nr = in(reg) nr.to_asm(),
tmp = out(reg) _,
inlateout("r0") a0.to_asm() => r0,
in("r1") a1.to_asm(),
in("r2") a2.to_asm(),
in("r3") a3.to_asm(),
in("r4") a4.to_asm(),
in("r5") a5.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall6_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let r0;
asm!(
"mov {tmp}, r7",
"mov r7, {nr}",
"svc 0",
"mov r7, {tmp}",
nr = in(reg) nr.to_asm(),
tmp = out(reg) _,
inlateout("r0") a0.to_asm() => r0,
in("r1") a1.to_asm(),
in("r2") a2.to_asm(),
in("r3") a3.to_asm(),
in("r4") a4.to_asm(),
in("r5") a5.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}

View File

@@ -0,0 +1,492 @@
//! 32-bit x86 Linux system calls.
//!
//! There are two forms; `indirect_*` which take a callee, which allow calling
//! through the vDSO when possible, and plain forms, which use the `int 0x80`
//! instruction.
//!
//! Most `rustix` syscalls use the vsyscall mechanism rather than going using
//! `int 0x80` sequences, as vsyscall is much faster.
//!
//! Syscalls made with `int 0x80` preserve the flags register, while syscalls
//! made using vsyscall do not.
#![allow(dead_code)]
use crate::backend::reg::{
ArgReg, FromAsm, RetReg, SyscallNumber, ToAsm as _, A0, A1, A2, A3, A4, A5, R0,
};
use crate::backend::vdso_wrappers::SyscallType;
use core::arch::asm;
#[inline]
pub(in crate::backend) unsafe fn indirect_syscall0(
callee: SyscallType,
nr: SyscallNumber<'_>,
) -> RetReg<R0> {
let r0;
asm!(
"call {callee}",
callee = in(reg) callee,
inlateout("eax") nr.to_asm() => r0,
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn indirect_syscall1(
callee: SyscallType,
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
) -> RetReg<R0> {
let r0;
asm!(
"call {callee}",
callee = in(reg) callee,
inlateout("eax") nr.to_asm() => r0,
in("ebx") a0.to_asm(),
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn indirect_syscall1_noreturn(
callee: SyscallType,
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
) -> ! {
asm!(
"call {callee}",
"ud2",
callee = in(reg) callee,
in("eax") nr.to_asm(),
in("ebx") a0.to_asm(),
options(noreturn)
)
}
#[inline]
pub(in crate::backend) unsafe fn indirect_syscall2(
callee: SyscallType,
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let r0;
asm!(
"call {callee}",
callee = in(reg) callee,
inlateout("eax") nr.to_asm() => r0,
in("ebx") a0.to_asm(),
in("ecx") a1.to_asm(),
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn indirect_syscall3(
callee: SyscallType,
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let r0;
asm!(
"call {callee}",
callee = in(reg) callee,
inlateout("eax") nr.to_asm() => r0,
in("ebx") a0.to_asm(),
in("ecx") a1.to_asm(),
in("edx") a2.to_asm(),
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn indirect_syscall4(
callee: SyscallType,
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let r0;
// a3 should go in esi, but `asm!` won't let us use it as an operand.
// Temporarily swap it into place, and then swap it back afterward.
//
// We hard-code the callee operand to use edi instead of `in(reg)` because
// even though we can't name esi as an operand, the compiler can use esi to
// satisfy `in(reg)`.
asm!(
"xchg esi, {a3}",
"call edi",
"xchg esi, {a3}",
a3 = in(reg) a3.to_asm(),
in("edi") callee,
inlateout("eax") nr.to_asm() => r0,
in("ebx") a0.to_asm(),
in("ecx") a1.to_asm(),
in("edx") a2.to_asm(),
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn indirect_syscall5(
callee: SyscallType,
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let r0;
// Oof. a3 should go in esi, and `asm!` won't let us use that register as
// an operand. And we can't request stack slots. And there are no other
// registers free. Use eax as a temporary pointer to a slice, since it gets
// clobbered as the return value anyway.
asm!(
"push esi",
"push [eax + 0]",
"mov esi, [eax + 4]",
"mov eax, [eax + 8]",
"call [esp]",
"pop esi",
"pop esi",
inout("eax") &[callee as _, a3.to_asm(), nr.to_asm()] => r0,
in("ebx") a0.to_asm(),
in("ecx") a1.to_asm(),
in("edx") a2.to_asm(),
in("edi") a4.to_asm(),
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn indirect_syscall6(
callee: SyscallType,
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let r0;
// Oof again. a3 should go in esi, and a5 should go in ebp, and `asm!`
// won't let us use either of those registers as operands. And we can't
// request stack slots. And there are no other registers free. Use eax as a
// temporary pointer to a slice, since it gets clobbered as the return
// value anyway.
//
// This is another reason that syscalls should be compiler intrinsics
// rather than inline asm.
asm!(
"push ebp",
"push esi",
"push [eax + 0]",
"mov esi, [eax + 4]",
"mov ebp, [eax + 8]",
"mov eax, [eax + 12]",
"call [esp]",
"pop esi",
"pop esi",
"pop ebp",
inout("eax") &[callee as _, a3.to_asm(), a5.to_asm(), nr.to_asm()] => r0,
in("ebx") a0.to_asm(),
in("ecx") a1.to_asm(),
in("edx") a2.to_asm(),
in("edi") a4.to_asm(),
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall0_readonly(nr: SyscallNumber<'_>) -> RetReg<R0> {
let r0;
asm!(
"int $$0x80",
inlateout("eax") nr.to_asm() => r0,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> RetReg<R0> {
let r0;
asm!(
"int $$0x80",
inlateout("eax") nr.to_asm() => r0,
in("ebx") a0.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
) -> RetReg<R0> {
let r0;
asm!(
"int $$0x80",
inlateout("eax") nr.to_asm() => r0,
in("ebx") a0.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_noreturn(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> ! {
asm!(
"int $$0x80",
"ud2",
in("eax") nr.to_asm(),
in("ebx") a0.to_asm(),
options(nostack, noreturn)
)
}
#[inline]
pub(in crate::backend) unsafe fn syscall2(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let r0;
asm!(
"int $$0x80",
inlateout("eax") nr.to_asm() => r0,
in("ebx") a0.to_asm(),
in("ecx") a1.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall2_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let r0;
asm!(
"int $$0x80",
inlateout("eax") nr.to_asm() => r0,
in("ebx") a0.to_asm(),
in("ecx") a1.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall3(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let r0;
asm!(
"int $$0x80",
inlateout("eax") nr.to_asm() => r0,
in("ebx") a0.to_asm(),
in("ecx") a1.to_asm(),
in("edx") a2.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall3_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let r0;
asm!(
"int $$0x80",
inlateout("eax") nr.to_asm() => r0,
in("ebx") a0.to_asm(),
in("ecx") a1.to_asm(),
in("edx") a2.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall4(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let r0;
// a3 should go in esi, but `asm!` won't let us use it as an operand.
// Temporarily swap it into place, and then swap it back afterward.
asm!(
"xchg esi, {a3}",
"int $$0x80",
"xchg esi, {a3}",
a3 = in(reg) a3.to_asm(),
inlateout("eax") nr.to_asm() => r0,
in("ebx") a0.to_asm(),
in("ecx") a1.to_asm(),
in("edx") a2.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall4_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let r0;
// See the comments in `syscall4`.
asm!(
"xchg esi, {a3}",
"int $$0x80",
"xchg esi, {a3}",
a3 = in(reg) a3.to_asm(),
inlateout("eax") nr.to_asm() => r0,
in("ebx") a0.to_asm(),
in("ecx") a1.to_asm(),
in("edx") a2.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall5(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let r0;
// As in `syscall4`, use xchg to handle a3. a4 should go in edi, and we can
// use that register as an operand. Unlike in `indirect_syscall5`, we don't
// have a `callee` operand taking up a register, so we have enough
// registers and don't need to use a slice.
asm!(
"xchg esi, {a3}",
"int $$0x80",
"xchg esi, {a3}",
a3 = in(reg) a3.to_asm(),
inlateout("eax") nr.to_asm() => r0,
in("ebx") a0.to_asm(),
in("ecx") a1.to_asm(),
in("edx") a2.to_asm(),
in("edi") a4.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall5_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let r0;
// See the comments in `syscall5`.
asm!(
"xchg esi, {a3}",
"int $$0x80",
"xchg esi, {a3}",
a3 = in(reg) a3.to_asm(),
inlateout("eax") nr.to_asm() => r0,
in("ebx") a0.to_asm(),
in("ecx") a1.to_asm(),
in("edx") a2.to_asm(),
in("edi") a4.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall6(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let r0;
// See the comments in `indirect_syscall6`.
asm!(
"push ebp",
"push esi",
"mov esi, [eax + 0]",
"mov ebp, [eax + 4]",
"mov eax, [eax + 8]",
"int $$0x80",
"pop esi",
"pop ebp",
inout("eax") &[a3.to_asm(), a5.to_asm(), nr.to_asm()] => r0,
in("ebx") a0.to_asm(),
in("ecx") a1.to_asm(),
in("edx") a2.to_asm(),
in("edi") a4.to_asm(),
options(preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall6_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let r0;
// See the comments in `indirect_syscall6`.
asm!(
"push ebp",
"push esi",
"mov esi, [eax + 0]",
"mov ebp, [eax + 4]",
"mov eax, [eax + 8]",
"int $$0x80",
"pop esi",
"pop ebp",
inout("eax") &[a3.to_asm(), a5.to_asm(), nr.to_asm()] => r0,
in("ebx") a0.to_asm(),
in("ecx") a1.to_asm(),
in("edx") a2.to_asm(),
in("edi") a4.to_asm(),
options(preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}

View File

@@ -0,0 +1,294 @@
//! x86-64 Linux system calls.
use crate::backend::reg::{
ArgReg, FromAsm, RetReg, SyscallNumber, ToAsm as _, A0, A1, A2, A3, A4, A5, R0,
};
use core::arch::asm;
#[cfg(target_pointer_width = "32")]
compile_error!("x32 is not yet supported");
#[inline]
pub(in crate::backend) unsafe fn syscall0_readonly(nr: SyscallNumber<'_>) -> RetReg<R0> {
let r0;
asm!(
"syscall",
inlateout("rax") nr.to_asm() => r0,
lateout("rcx") _,
lateout("r11") _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> RetReg<R0> {
let r0;
asm!(
"syscall",
inlateout("rax") nr.to_asm() => r0,
in("rdi") a0.to_asm(),
lateout("rcx") _,
lateout("r11") _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
) -> RetReg<R0> {
let r0;
asm!(
"syscall",
inlateout("rax") nr.to_asm() => r0,
in("rdi") a0.to_asm(),
lateout("rcx") _,
lateout("r11") _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_noreturn(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> ! {
asm!(
"syscall",
"ud2",
in("rax") nr.to_asm(),
in("rdi") a0.to_asm(),
options(nostack, noreturn)
)
}
#[inline]
pub(in crate::backend) unsafe fn syscall2(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let r0;
asm!(
"syscall",
inlateout("rax") nr.to_asm() => r0,
in("rdi") a0.to_asm(),
in("rsi") a1.to_asm(),
lateout("rcx") _,
lateout("r11") _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall2_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let r0;
asm!(
"syscall",
inlateout("rax") nr.to_asm() => r0,
in("rdi") a0.to_asm(),
in("rsi") a1.to_asm(),
lateout("rcx") _,
lateout("r11") _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall3(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let r0;
asm!(
"syscall",
inlateout("rax") nr.to_asm() => r0,
in("rdi") a0.to_asm(),
in("rsi") a1.to_asm(),
in("rdx") a2.to_asm(),
lateout("rcx") _,
lateout("r11") _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall3_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let r0;
asm!(
"syscall",
inlateout("rax") nr.to_asm() => r0,
in("rdi") a0.to_asm(),
in("rsi") a1.to_asm(),
in("rdx") a2.to_asm(),
lateout("rcx") _,
lateout("r11") _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall4(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let r0;
asm!(
"syscall",
inlateout("rax") nr.to_asm() => r0,
in("rdi") a0.to_asm(),
in("rsi") a1.to_asm(),
in("rdx") a2.to_asm(),
in("r10") a3.to_asm(),
lateout("rcx") _,
lateout("r11") _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall4_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let r0;
asm!(
"syscall",
inlateout("rax") nr.to_asm() => r0,
in("rdi") a0.to_asm(),
in("rsi") a1.to_asm(),
in("rdx") a2.to_asm(),
in("r10") a3.to_asm(),
lateout("rcx") _,
lateout("r11") _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall5(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let r0;
asm!(
"syscall",
inlateout("rax") nr.to_asm() => r0,
in("rdi") a0.to_asm(),
in("rsi") a1.to_asm(),
in("rdx") a2.to_asm(),
in("r10") a3.to_asm(),
in("r8") a4.to_asm(),
lateout("rcx") _,
lateout("r11") _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall5_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let r0;
asm!(
"syscall",
inlateout("rax") nr.to_asm() => r0,
in("rdi") a0.to_asm(),
in("rsi") a1.to_asm(),
in("rdx") a2.to_asm(),
in("r10") a3.to_asm(),
in("r8") a4.to_asm(),
lateout("rcx") _,
lateout("r11") _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall6(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let r0;
asm!(
"syscall",
inlateout("rax") nr.to_asm() => r0,
in("rdi") a0.to_asm(),
in("rsi") a1.to_asm(),
in("rdx") a2.to_asm(),
in("r10") a3.to_asm(),
in("r8") a4.to_asm(),
in("r9") a5.to_asm(),
lateout("rcx") _,
lateout("r11") _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall6_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let r0;
asm!(
"syscall",
inlateout("rax") nr.to_asm() => r0,
in("rdi") a0.to_asm(),
in("rsi") a1.to_asm(),
in("rdx") a2.to_asm(),
in("r10") a3.to_asm(),
in("r8") a4.to_asm(),
in("r9") a5.to_asm(),
lateout("rcx") _,
lateout("r11") _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}

401
vendor/rustix/src/backend/linux_raw/c.rs vendored Normal file
View File

@@ -0,0 +1,401 @@
//! Adapt the Linux API to resemble a POSIX-style libc API.
//!
//! The linux_raw backend doesn't use actual libc; this just defines certain
//! types that are convenient to have defined.
#![allow(unused_imports)]
#![allow(non_camel_case_types)]
pub(crate) type size_t = usize;
pub(crate) use linux_raw_sys::ctypes::*;
pub(crate) use linux_raw_sys::errno::{EBADF, EINVAL};
pub(crate) use linux_raw_sys::general::{__kernel_fd_set as fd_set, __FD_SETSIZE as FD_SETSIZE};
pub(crate) use linux_raw_sys::ioctl::{FIOCLEX, FIONBIO, FIONCLEX, FIONREAD};
// Import the kernel's `uid_t` and `gid_t` if they're 32-bit.
#[cfg(feature = "thread")]
pub(crate) use linux_raw_sys::general::futex_waitv;
#[cfg(not(any(target_arch = "arm", target_arch = "sparc", target_arch = "x86")))]
pub(crate) use linux_raw_sys::general::{__kernel_gid_t as gid_t, __kernel_uid_t as uid_t};
pub(crate) use linux_raw_sys::general::{
__kernel_pid_t as pid_t, __kernel_time64_t as time_t, __kernel_timespec as timespec, iovec,
O_CLOEXEC, O_NOCTTY, O_NONBLOCK, O_RDWR,
};
#[cfg(feature = "system")]
pub(crate) use linux_raw_sys::system::sysinfo;
#[cfg(feature = "fs")]
#[cfg(target_arch = "x86")]
#[cfg(test)]
pub(crate) use linux_raw_sys::general::stat64;
#[cfg(feature = "fs")]
#[cfg(test)]
pub(crate) use linux_raw_sys::general::{
__kernel_fsid_t as fsid_t, stat, statfs64, statx, statx_timestamp,
};
#[cfg(feature = "event")]
#[cfg(test)]
pub(crate) use linux_raw_sys::general::epoll_event;
#[cfg(feature = "mm")]
mod mm {
pub(crate) use linux_raw_sys::general::{MAP_HUGETLB, MAP_HUGE_SHIFT};
}
#[cfg(feature = "mm")]
pub(crate) use mm::*;
#[cfg(any(
feature = "fs",
all(
not(feature = "use-libc-auxv"),
not(feature = "use-explicitly-provided-auxv"),
any(
feature = "param",
feature = "runtime",
feature = "thread",
feature = "time",
target_arch = "x86",
)
)
))]
pub(crate) use linux_raw_sys::general::{
AT_FDCWD, NFS_SUPER_MAGIC, O_LARGEFILE, PROC_SUPER_MAGIC, UTIME_NOW, UTIME_OMIT, XATTR_CREATE,
XATTR_REPLACE,
};
pub(crate) use linux_raw_sys::ioctl::{BLKPBSZGET, BLKSSZGET, FICLONE};
#[cfg(target_pointer_width = "32")]
pub(crate) use linux_raw_sys::ioctl::{FS_IOC32_GETFLAGS, FS_IOC32_SETFLAGS};
#[cfg(target_pointer_width = "64")]
pub(crate) use linux_raw_sys::ioctl::{FS_IOC_GETFLAGS, FS_IOC_SETFLAGS};
#[cfg(feature = "io_uring")]
pub(crate) use linux_raw_sys::{general::open_how, io_uring::*};
#[cfg(feature = "net")]
pub(crate) use linux_raw_sys::{
cmsg_macros::*,
general::{O_CLOEXEC as SOCK_CLOEXEC, O_NONBLOCK as SOCK_NONBLOCK},
if_ether::*,
net::{
__kernel_sa_family_t as sa_family_t, __kernel_sockaddr_storage as sockaddr_storage,
cmsghdr, in6_addr, in_addr, ip_mreq, ip_mreq_source, ip_mreqn, ipv6_mreq, linger, mmsghdr,
msghdr, sockaddr, sockaddr_in, sockaddr_in6, sockaddr_un, socklen_t, AF_DECnet,
AF_APPLETALK, AF_ASH, AF_ATMPVC, AF_ATMSVC, AF_AX25, AF_BLUETOOTH, AF_BRIDGE, AF_CAN,
AF_ECONET, AF_IEEE802154, AF_INET, AF_INET6, AF_IPX, AF_IRDA, AF_ISDN, AF_IUCV, AF_KEY,
AF_LLC, AF_NETBEUI, AF_NETLINK, AF_NETROM, AF_PACKET, AF_PHONET, AF_PPPOX, AF_RDS, AF_ROSE,
AF_RXRPC, AF_SECURITY, AF_SNA, AF_TIPC, AF_UNIX, AF_UNSPEC, AF_VSOCK, AF_WANPIPE, AF_X25,
AF_XDP, IP6T_SO_ORIGINAL_DST, IPPROTO_FRAGMENT, IPPROTO_ICMPV6, IPPROTO_MH,
IPPROTO_ROUTING, IPV6_ADD_MEMBERSHIP, IPV6_DROP_MEMBERSHIP, IPV6_FREEBIND,
IPV6_MULTICAST_HOPS, IPV6_MULTICAST_LOOP, IPV6_PMTUDISC_DO, IPV6_PMTUDISC_DONT,
IPV6_PMTUDISC_INTERFACE, IPV6_PMTUDISC_OMIT, IPV6_PMTUDISC_PROBE, IPV6_PMTUDISC_WANT,
IPV6_RECVTCLASS, IPV6_TCLASS, IPV6_UNICAST_HOPS, IPV6_V6ONLY, IP_ADD_MEMBERSHIP,
IP_ADD_SOURCE_MEMBERSHIP, IP_DROP_MEMBERSHIP, IP_DROP_SOURCE_MEMBERSHIP, IP_FREEBIND,
IP_MULTICAST_LOOP, IP_MULTICAST_TTL, IP_PMTUDISC_DO, IP_PMTUDISC_DONT,
IP_PMTUDISC_INTERFACE, IP_PMTUDISC_OMIT, IP_PMTUDISC_PROBE, IP_PMTUDISC_WANT, IP_RECVTOS,
IP_TOS, IP_TTL, MSG_CMSG_CLOEXEC, MSG_CONFIRM, MSG_CTRUNC, MSG_DONTROUTE, MSG_DONTWAIT,
MSG_EOR, MSG_ERRQUEUE, MSG_MORE, MSG_NOSIGNAL, MSG_OOB, MSG_PEEK, MSG_TRUNC, MSG_WAITALL,
SCM_CREDENTIALS, SCM_RIGHTS, SHUT_RD, SHUT_RDWR, SHUT_WR, SOCK_DGRAM, SOCK_RAW, SOCK_RDM,
SOCK_SEQPACKET, SOCK_STREAM, SOL_SOCKET, SOL_XDP, SO_ACCEPTCONN, SO_BROADCAST, SO_COOKIE,
SO_DOMAIN, SO_ERROR, SO_INCOMING_CPU, SO_KEEPALIVE, SO_LINGER, SO_OOBINLINE,
SO_ORIGINAL_DST, SO_PASSCRED, SO_PROTOCOL, SO_RCVBUF, SO_RCVBUFFORCE, SO_RCVTIMEO_NEW,
SO_RCVTIMEO_NEW as SO_RCVTIMEO, SO_RCVTIMEO_OLD, SO_REUSEADDR, SO_REUSEPORT, SO_SNDBUF,
SO_SNDBUFFORCE, SO_SNDTIMEO_NEW, SO_SNDTIMEO_NEW as SO_SNDTIMEO, SO_SNDTIMEO_OLD, SO_TYPE,
TCP_CONGESTION, TCP_CORK, TCP_KEEPCNT, TCP_KEEPIDLE, TCP_KEEPINTVL, TCP_NODELAY,
TCP_QUICKACK, TCP_THIN_LINEAR_TIMEOUTS, TCP_USER_TIMEOUT,
},
netlink::*,
xdp::{
sockaddr_xdp, xdp_desc, xdp_mmap_offsets, xdp_mmap_offsets_v1, xdp_options,
xdp_ring_offset, xdp_ring_offset_v1, xdp_statistics, xdp_statistics_v1, xdp_umem_reg,
xdp_umem_reg_v1, XDP_COPY, XDP_MMAP_OFFSETS, XDP_OPTIONS, XDP_OPTIONS_ZEROCOPY,
XDP_PGOFF_RX_RING, XDP_PGOFF_TX_RING, XDP_PKT_CONTD, XDP_RING_NEED_WAKEUP, XDP_RX_RING,
XDP_SHARED_UMEM, XDP_STATISTICS, XDP_TX_RING, XDP_UMEM_COMPLETION_RING, XDP_UMEM_FILL_RING,
XDP_UMEM_PGOFF_COMPLETION_RING, XDP_UMEM_PGOFF_FILL_RING, XDP_UMEM_REG,
XDP_UMEM_UNALIGNED_CHUNK_FLAG, XDP_USE_NEED_WAKEUP, XDP_USE_SG, XDP_ZEROCOPY,
XSK_UNALIGNED_BUF_ADDR_MASK, XSK_UNALIGNED_BUF_OFFSET_SHIFT,
},
};
#[cfg(any(feature = "io_uring", feature = "time", feature = "thread"))]
pub use linux_raw_sys::general::__kernel_clockid_t as clockid_t;
#[cfg(feature = "net")]
pub use linux_raw_sys::net::{sock_txtime, SCM_TXTIME, SO_TXTIME};
#[cfg(all(feature = "net", feature = "time"))]
pub(crate) const SOF_TXTIME_DEADLINE_MODE: u32 =
linux_raw_sys::net::txtime_flags::SOF_TXTIME_DEADLINE_MODE as _;
#[cfg(all(feature = "net", feature = "time"))]
pub(crate) const SOF_TXTIME_REPORT_ERRORS: u32 =
linux_raw_sys::net::txtime_flags::SOF_TXTIME_REPORT_ERRORS as _;
// Cast away bindgen's `enum` type to make these consistent with the other
// `setsockopt`/`getsockopt` level values.
#[cfg(feature = "net")]
pub(crate) const IPPROTO_IP: u32 = linux_raw_sys::net::IPPROTO_IP as _;
#[cfg(feature = "net")]
pub(crate) const IPPROTO_ICMP: u32 = linux_raw_sys::net::IPPROTO_ICMP as _;
#[cfg(feature = "net")]
pub(crate) const IPPROTO_IGMP: u32 = linux_raw_sys::net::IPPROTO_IGMP as _;
#[cfg(feature = "net")]
pub(crate) const IPPROTO_IPIP: u32 = linux_raw_sys::net::IPPROTO_IPIP as _;
#[cfg(feature = "net")]
pub(crate) const IPPROTO_TCP: u32 = linux_raw_sys::net::IPPROTO_TCP as _;
#[cfg(feature = "net")]
pub(crate) const IPPROTO_EGP: u32 = linux_raw_sys::net::IPPROTO_EGP as _;
#[cfg(feature = "net")]
pub(crate) const IPPROTO_PUP: u32 = linux_raw_sys::net::IPPROTO_PUP as _;
#[cfg(feature = "net")]
pub(crate) const IPPROTO_UDP: u32 = linux_raw_sys::net::IPPROTO_UDP as _;
#[cfg(feature = "net")]
pub(crate) const IPPROTO_IDP: u32 = linux_raw_sys::net::IPPROTO_IDP as _;
#[cfg(feature = "net")]
pub(crate) const IPPROTO_TP: u32 = linux_raw_sys::net::IPPROTO_TP as _;
#[cfg(feature = "net")]
pub(crate) const IPPROTO_DCCP: u32 = linux_raw_sys::net::IPPROTO_DCCP as _;
#[cfg(feature = "net")]
pub(crate) const IPPROTO_IPV6: u32 = linux_raw_sys::net::IPPROTO_IPV6 as _;
#[cfg(feature = "net")]
pub(crate) const IPPROTO_RSVP: u32 = linux_raw_sys::net::IPPROTO_RSVP as _;
#[cfg(feature = "net")]
pub(crate) const IPPROTO_GRE: u32 = linux_raw_sys::net::IPPROTO_GRE as _;
#[cfg(feature = "net")]
pub(crate) const IPPROTO_ESP: u32 = linux_raw_sys::net::IPPROTO_ESP as _;
#[cfg(feature = "net")]
pub(crate) const IPPROTO_AH: u32 = linux_raw_sys::net::IPPROTO_AH as _;
#[cfg(feature = "net")]
pub(crate) const IPPROTO_MTP: u32 = linux_raw_sys::net::IPPROTO_MTP as _;
#[cfg(feature = "net")]
pub(crate) const IPPROTO_BEETPH: u32 = linux_raw_sys::net::IPPROTO_BEETPH as _;
#[cfg(feature = "net")]
pub(crate) const IPPROTO_ENCAP: u32 = linux_raw_sys::net::IPPROTO_ENCAP as _;
#[cfg(feature = "net")]
pub(crate) const IPPROTO_PIM: u32 = linux_raw_sys::net::IPPROTO_PIM as _;
#[cfg(feature = "net")]
pub(crate) const IPPROTO_COMP: u32 = linux_raw_sys::net::IPPROTO_COMP as _;
#[cfg(feature = "net")]
pub(crate) const IPPROTO_SCTP: u32 = linux_raw_sys::net::IPPROTO_SCTP as _;
#[cfg(feature = "net")]
pub(crate) const IPPROTO_UDPLITE: u32 = linux_raw_sys::net::IPPROTO_UDPLITE as _;
#[cfg(feature = "net")]
pub(crate) const IPPROTO_MPLS: u32 = linux_raw_sys::net::IPPROTO_MPLS as _;
#[cfg(feature = "net")]
pub(crate) const IPPROTO_ETHERNET: u32 = linux_raw_sys::net::IPPROTO_ETHERNET as _;
#[cfg(feature = "net")]
pub(crate) const IPPROTO_RAW: u32 = linux_raw_sys::net::IPPROTO_RAW as _;
#[cfg(feature = "net")]
pub(crate) const IPPROTO_MPTCP: u32 = linux_raw_sys::net::IPPROTO_MPTCP as _;
#[cfg(any(feature = "process", feature = "runtime"))]
pub(crate) use linux_raw_sys::general::siginfo_t;
#[cfg(any(feature = "process", feature = "runtime"))]
pub(crate) const EXIT_SUCCESS: c_int = 0;
#[cfg(any(feature = "process", feature = "runtime"))]
pub(crate) const EXIT_FAILURE: c_int = 1;
#[cfg(feature = "process")]
pub(crate) const EXIT_SIGNALED_SIGABRT: c_int = 128 + linux_raw_sys::general::SIGABRT as c_int;
#[cfg(feature = "runtime")]
pub(crate) const CLONE_CHILD_SETTID: c_int = linux_raw_sys::general::CLONE_CHILD_SETTID as c_int;
#[cfg(feature = "process")]
pub(crate) use linux_raw_sys::{
general::{
CLD_CONTINUED, CLD_DUMPED, CLD_EXITED, CLD_KILLED, CLD_STOPPED, CLD_TRAPPED, F_RDLCK,
F_UNLCK, F_WRLCK, O_NONBLOCK as PIDFD_NONBLOCK, P_ALL, P_PGID, P_PID, P_PIDFD, SEEK_CUR,
SEEK_END, SEEK_SET,
},
ioctl::TIOCSCTTY,
};
#[cfg(feature = "process")]
#[cfg(target_pointer_width = "32")]
pub(crate) use linux_raw_sys::general::{flock64 as flock, F_GETLK64};
#[cfg(feature = "process")]
#[cfg(target_pointer_width = "64")]
pub(crate) use linux_raw_sys::general::{flock, F_GETLK};
#[cfg(feature = "pty")]
pub(crate) use linux_raw_sys::ioctl::TIOCGPTPEER;
#[cfg(feature = "termios")]
pub(crate) use linux_raw_sys::{
general::{
cc_t, speed_t, tcflag_t, termios, termios2, winsize, B0, B1000000, B110, B115200, B1152000,
B1200, B134, B150, B1500000, B1800, B19200, B200, B2000000, B230400, B2400, B2500000, B300,
B3000000, B3500000, B38400, B4000000, B460800, B4800, B50, B500000, B57600, B576000, B600,
B75, B921600, B9600, BOTHER, BRKINT, BS0, BS1, BSDLY, CBAUD, CBAUDEX, CIBAUD, CLOCAL,
CMSPAR, CR0, CR1, CR2, CR3, CRDLY, CREAD, CRTSCTS, CS5, CS6, CS7, CS8, CSIZE, CSTOPB, ECHO,
ECHOCTL, ECHOE, ECHOK, ECHOKE, ECHONL, ECHOPRT, EXTA, EXTB, EXTPROC, FF0, FF1, FFDLY,
FLUSHO, HUPCL, IBSHIFT, ICANON, ICRNL, IEXTEN, IGNBRK, IGNCR, IGNPAR, IMAXBEL, INLCR,
INPCK, ISIG, ISTRIP, IUCLC, IUTF8, IXANY, IXOFF, IXON, NCCS, NL0, NL1, NLDLY, NOFLSH,
OCRNL, OFDEL, OFILL, OLCUC, ONLCR, ONLRET, ONOCR, OPOST, PARENB, PARMRK, PARODD, PENDIN,
TAB0, TAB1, TAB2, TAB3, TABDLY, TCIFLUSH, TCIOFF, TCIOFLUSH, TCION, TCOFLUSH, TCOOFF,
TCOON, TCSADRAIN, TCSAFLUSH, TCSANOW, TOSTOP, VDISCARD, VEOF, VEOL, VEOL2, VERASE, VINTR,
VKILL, VLNEXT, VMIN, VQUIT, VREPRINT, VSTART, VSTOP, VSUSP, VSWTC, VT0, VT1, VTDLY, VTIME,
VWERASE, XCASE, XTABS,
},
ioctl::{
TCFLSH, TCGETS, TCGETS2, TCSBRK, TCSETS, TCSETS2, TCSETSF2, TCSETSW2, TCXONC, TIOCEXCL,
TIOCGPGRP, TIOCGSID, TIOCGWINSZ, TIOCNXCL, TIOCSPGRP, TIOCSWINSZ,
},
};
// Define our own `uid_t` and `gid_t` if the kernel's versions are not 32-bit.
#[cfg(any(target_arch = "arm", target_arch = "sparc", target_arch = "x86"))]
pub(crate) type uid_t = u32;
#[cfg(any(target_arch = "arm", target_arch = "sparc", target_arch = "x86"))]
pub(crate) type gid_t = u32;
// Bindgen infers `u32` for many of these macro types which meant to be
// used with `c_int` in the C APIs, so cast them to `c_int`.
// Convert the signal constants from `u32` to `c_int`.
pub(crate) const SIGHUP: c_int = linux_raw_sys::general::SIGHUP as _;
pub(crate) const SIGINT: c_int = linux_raw_sys::general::SIGINT as _;
pub(crate) const SIGQUIT: c_int = linux_raw_sys::general::SIGQUIT as _;
pub(crate) const SIGILL: c_int = linux_raw_sys::general::SIGILL as _;
pub(crate) const SIGTRAP: c_int = linux_raw_sys::general::SIGTRAP as _;
pub(crate) const SIGABRT: c_int = linux_raw_sys::general::SIGABRT as _;
pub(crate) const SIGBUS: c_int = linux_raw_sys::general::SIGBUS as _;
pub(crate) const SIGFPE: c_int = linux_raw_sys::general::SIGFPE as _;
pub(crate) const SIGKILL: c_int = linux_raw_sys::general::SIGKILL as _;
pub(crate) const SIGUSR1: c_int = linux_raw_sys::general::SIGUSR1 as _;
pub(crate) const SIGSEGV: c_int = linux_raw_sys::general::SIGSEGV as _;
pub(crate) const SIGUSR2: c_int = linux_raw_sys::general::SIGUSR2 as _;
pub(crate) const SIGPIPE: c_int = linux_raw_sys::general::SIGPIPE as _;
pub(crate) const SIGALRM: c_int = linux_raw_sys::general::SIGALRM as _;
pub(crate) const SIGTERM: c_int = linux_raw_sys::general::SIGTERM as _;
#[cfg(not(any(
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "sparc",
target_arch = "sparc64"
)))]
pub(crate) const SIGSTKFLT: c_int = linux_raw_sys::general::SIGSTKFLT as _;
pub(crate) const SIGCHLD: c_int = linux_raw_sys::general::SIGCHLD as _;
pub(crate) const SIGCONT: c_int = linux_raw_sys::general::SIGCONT as _;
pub(crate) const SIGSTOP: c_int = linux_raw_sys::general::SIGSTOP as _;
pub(crate) const SIGTSTP: c_int = linux_raw_sys::general::SIGTSTP as _;
pub(crate) const SIGTTIN: c_int = linux_raw_sys::general::SIGTTIN as _;
pub(crate) const SIGTTOU: c_int = linux_raw_sys::general::SIGTTOU as _;
pub(crate) const SIGURG: c_int = linux_raw_sys::general::SIGURG as _;
pub(crate) const SIGXCPU: c_int = linux_raw_sys::general::SIGXCPU as _;
pub(crate) const SIGXFSZ: c_int = linux_raw_sys::general::SIGXFSZ as _;
pub(crate) const SIGVTALRM: c_int = linux_raw_sys::general::SIGVTALRM as _;
pub(crate) const SIGPROF: c_int = linux_raw_sys::general::SIGPROF as _;
pub(crate) const SIGWINCH: c_int = linux_raw_sys::general::SIGWINCH as _;
pub(crate) const SIGIO: c_int = linux_raw_sys::general::SIGIO as _;
pub(crate) const SIGPWR: c_int = linux_raw_sys::general::SIGPWR as _;
pub(crate) const SIGSYS: c_int = linux_raw_sys::general::SIGSYS as _;
#[cfg(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 _;
#[cfg(feature = "stdio")]
pub(crate) const STDIN_FILENO: c_int = linux_raw_sys::general::STDIN_FILENO as _;
#[cfg(feature = "stdio")]
pub(crate) const STDOUT_FILENO: c_int = linux_raw_sys::general::STDOUT_FILENO as _;
#[cfg(feature = "stdio")]
pub(crate) const STDERR_FILENO: c_int = linux_raw_sys::general::STDERR_FILENO as _;
pub(crate) const PIPE_BUF: usize = linux_raw_sys::general::PIPE_BUF as _;
pub(crate) const CLOCK_MONOTONIC: c_int = linux_raw_sys::general::CLOCK_MONOTONIC as _;
pub(crate) const CLOCK_REALTIME: c_int = linux_raw_sys::general::CLOCK_REALTIME as _;
pub(crate) const CLOCK_MONOTONIC_RAW: c_int = linux_raw_sys::general::CLOCK_MONOTONIC_RAW as _;
pub(crate) const CLOCK_MONOTONIC_COARSE: c_int =
linux_raw_sys::general::CLOCK_MONOTONIC_COARSE as _;
pub(crate) const CLOCK_REALTIME_COARSE: c_int = linux_raw_sys::general::CLOCK_REALTIME_COARSE as _;
pub(crate) const CLOCK_THREAD_CPUTIME_ID: c_int =
linux_raw_sys::general::CLOCK_THREAD_CPUTIME_ID as _;
pub(crate) const CLOCK_PROCESS_CPUTIME_ID: c_int =
linux_raw_sys::general::CLOCK_PROCESS_CPUTIME_ID as _;
#[cfg(any(feature = "thread", feature = "time"))]
pub(crate) const CLOCK_BOOTTIME: c_int = linux_raw_sys::general::CLOCK_BOOTTIME as _;
#[cfg(any(feature = "thread", feature = "time"))]
pub(crate) const CLOCK_BOOTTIME_ALARM: c_int = linux_raw_sys::general::CLOCK_BOOTTIME_ALARM as _;
#[cfg(any(feature = "thread", feature = "time"))]
pub(crate) const CLOCK_TAI: c_int = linux_raw_sys::general::CLOCK_TAI as _;
#[cfg(any(feature = "thread", feature = "time"))]
pub(crate) const CLOCK_REALTIME_ALARM: c_int = linux_raw_sys::general::CLOCK_REALTIME_ALARM as _;
#[cfg(feature = "system")]
mod reboot_symbols {
use super::c_int;
pub(crate) const LINUX_REBOOT_MAGIC1: c_int = linux_raw_sys::general::LINUX_REBOOT_MAGIC1 as _;
pub(crate) const LINUX_REBOOT_MAGIC2: c_int = linux_raw_sys::general::LINUX_REBOOT_MAGIC2 as _;
pub(crate) const LINUX_REBOOT_CMD_RESTART: c_int =
linux_raw_sys::general::LINUX_REBOOT_CMD_RESTART as _;
pub(crate) const LINUX_REBOOT_CMD_HALT: c_int =
linux_raw_sys::general::LINUX_REBOOT_CMD_HALT as _;
pub(crate) const LINUX_REBOOT_CMD_CAD_ON: c_int =
linux_raw_sys::general::LINUX_REBOOT_CMD_CAD_ON as _;
pub(crate) const LINUX_REBOOT_CMD_CAD_OFF: c_int =
linux_raw_sys::general::LINUX_REBOOT_CMD_CAD_OFF as _;
pub(crate) const LINUX_REBOOT_CMD_POWER_OFF: c_int =
linux_raw_sys::general::LINUX_REBOOT_CMD_POWER_OFF as _;
pub(crate) const LINUX_REBOOT_CMD_SW_SUSPEND: c_int =
linux_raw_sys::general::LINUX_REBOOT_CMD_SW_SUSPEND as _;
pub(crate) const LINUX_REBOOT_CMD_KEXEC: c_int =
linux_raw_sys::general::LINUX_REBOOT_CMD_KEXEC as _;
}
#[cfg(feature = "system")]
pub(crate) use reboot_symbols::*;
#[cfg(any(
feature = "fs",
all(
linux_raw,
not(feature = "use-libc-auxv"),
not(feature = "use-explicitly-provided-auxv"),
any(
feature = "param",
feature = "runtime",
feature = "thread",
feature = "time",
target_arch = "x86",
)
)
))]
mod statx_flags {
pub(crate) use linux_raw_sys::general::{
STATX_ALL, STATX_ATIME, STATX_BASIC_STATS, STATX_BLOCKS, STATX_BTIME, STATX_CTIME,
STATX_DIOALIGN, STATX_GID, STATX_INO, STATX_MNT_ID, STATX_MODE, STATX_MTIME, STATX_NLINK,
STATX_SIZE, STATX_TYPE, STATX_UID,
};
pub(crate) use linux_raw_sys::general::{
STATX_ATTR_APPEND, STATX_ATTR_AUTOMOUNT, STATX_ATTR_COMPRESSED, STATX_ATTR_DAX,
STATX_ATTR_ENCRYPTED, STATX_ATTR_IMMUTABLE, STATX_ATTR_MOUNT_ROOT, STATX_ATTR_NODUMP,
STATX_ATTR_VERITY,
};
}
#[cfg(any(
feature = "fs",
all(
linux_raw,
not(feature = "use-libc-auxv"),
not(feature = "use-explicitly-provided-auxv"),
any(
feature = "param",
feature = "runtime",
feature = "thread",
feature = "time",
target_arch = "x86",
)
)
))]
pub(crate) use statx_flags::*;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,74 @@
use crate::ffi;
use bitflags::bitflags;
bitflags! {
/// `EPOLL_*` for use with [`epoll::create`].
///
/// [`epoll::create`]: crate::event::epoll::create
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct CreateFlags: ffi::c_uint {
/// `EPOLL_CLOEXEC`
const CLOEXEC = linux_raw_sys::general::EPOLL_CLOEXEC;
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}
bitflags! {
/// `EPOLL*` for use with [`epoll::add`].
///
/// [`epoll::add`]: crate::event::epoll::add
#[repr(transparent)]
#[derive(Default, Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct EventFlags: u32 {
/// `EPOLLIN`
const IN = linux_raw_sys::general::EPOLLIN as u32;
/// `EPOLLOUT`
const OUT = linux_raw_sys::general::EPOLLOUT as u32;
/// `EPOLLPRI`
const PRI = linux_raw_sys::general::EPOLLPRI as u32;
/// `EPOLLERR`
const ERR = linux_raw_sys::general::EPOLLERR as u32;
/// `EPOLLHUP`
const HUP = linux_raw_sys::general::EPOLLHUP as u32;
/// `EPOLLRDNORM`
const RDNORM = linux_raw_sys::general::EPOLLRDNORM as u32;
/// `EPOLLRDBAND`
const RDBAND = linux_raw_sys::general::EPOLLRDBAND as u32;
/// `EPOLLWRNORM`
const WRNORM = linux_raw_sys::general::EPOLLWRNORM as u32;
/// `EPOLLWRBAND`
const WRBAND = linux_raw_sys::general::EPOLLWRBAND as u32;
/// `EPOLLMSG`
const MSG = linux_raw_sys::general::EPOLLMSG as u32;
/// `EPOLLRDHUP`
const RDHUP = linux_raw_sys::general::EPOLLRDHUP as u32;
/// `EPOLLET`
const ET = linux_raw_sys::general::EPOLLET as u32;
/// `EPOLLONESHOT`
const ONESHOT = linux_raw_sys::general::EPOLLONESHOT as u32;
/// `EPOLLWAKEUP`
const WAKEUP = linux_raw_sys::general::EPOLLWAKEUP as u32;
/// `EPOLLEXCLUSIVE`
const EXCLUSIVE = linux_raw_sys::general::EPOLLEXCLUSIVE as u32;
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}

View File

@@ -0,0 +1,4 @@
pub mod epoll;
pub(crate) mod poll_fd;
pub(crate) mod syscalls;
pub(crate) mod types;

View File

@@ -0,0 +1,98 @@
use crate::fd::{AsFd, BorrowedFd};
use bitflags::bitflags;
bitflags! {
/// `POLL*` flags for use with [`poll`].
///
/// [`poll`]: crate::event::poll
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct PollFlags: u16 {
/// `POLLIN`
const IN = linux_raw_sys::general::POLLIN as u16;
/// `POLLPRI`
const PRI = linux_raw_sys::general::POLLPRI as u16;
/// `POLLOUT`
const OUT = linux_raw_sys::general::POLLOUT as u16;
/// `POLLRDNORM`
const RDNORM = linux_raw_sys::general::POLLRDNORM as u16;
/// `POLLWRNORM`
const WRNORM = linux_raw_sys::general::POLLWRNORM as u16;
/// `POLLRDBAND`
const RDBAND = linux_raw_sys::general::POLLRDBAND as u16;
/// `POLLWRBAND`
const WRBAND = linux_raw_sys::general::POLLWRBAND as u16;
/// `POLLERR`
const ERR = linux_raw_sys::general::POLLERR as u16;
/// `POLLHUP`
const HUP = linux_raw_sys::general::POLLHUP as u16;
/// `POLLNVAL`
const NVAL = linux_raw_sys::general::POLLNVAL as u16;
/// `POLLRDHUP`
const RDHUP = linux_raw_sys::general::POLLRDHUP as u16;
/// <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")]
#[repr(C)]
#[derive(Debug, Clone)]
pub struct PollFd<'fd> {
pub(crate) fd: BorrowedFd<'fd>,
pub(crate) events: u16,
pub(crate) revents: u16,
}
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.fd = fd.as_fd();
}
/// Clears the ready events.
#[inline]
pub fn clear_revents(&mut self) {
self.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 {
fd,
events: events.bits(),
revents: 0,
}
}
/// 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.revents).unwrap()
}
}
impl<'fd> AsFd for PollFd<'fd> {
#[inline]
fn as_fd(&self) -> BorrowedFd<'_> {
self.fd.as_fd()
}
}

View File

@@ -0,0 +1,358 @@
//! linux_raw syscalls supporting `rustix::event`.
//!
//! # Safety
//!
//! See the `rustix::backend` module documentation for details.
#![allow(unsafe_code, clippy::undocumented_unsafe_blocks)]
use crate::backend::conv::{
by_ref, c_int, c_uint, opt_mut, opt_ref, pass_usize, ret, ret_c_int, ret_error, ret_owned_fd,
ret_usize, size_of, slice_mut, zero,
};
use crate::event::{epoll, EventfdFlags, FdSetElement, PollFd, Timespec};
use crate::fd::{BorrowedFd, OwnedFd};
use crate::io;
use core::ptr::null_mut;
use linux_raw_sys::general::{kernel_sigset_t, EPOLL_CTL_ADD, EPOLL_CTL_DEL, EPOLL_CTL_MOD};
#[inline]
pub(crate) fn poll(fds: &mut [PollFd<'_>], timeout: Option<&Timespec>) -> io::Result<usize> {
let (fds_addr_mut, fds_len) = slice_mut(fds);
#[cfg(target_pointer_width = "32")]
unsafe {
// If we don't have Linux 5.1, and the timeout fits in a
// `__kernel_old_timespec`, use plain `ppoll`.
//
// We do this unconditionally, rather than trying `ppoll_time64` and
// falling back on `Errno::NOSYS`, because seccomp configurations will
// sometimes abort the process on syscalls they don't recognize.
#[cfg(not(feature = "linux_5_1"))]
{
use linux_raw_sys::general::__kernel_old_timespec;
// If we don't have a timeout, or if we can convert the timeout to
// a `__kernel_old_timespec`, the use `__NR_ppoll`.
fn convert(timeout: &Timespec) -> Option<__kernel_old_timespec> {
Some(__kernel_old_timespec {
tv_sec: timeout.tv_sec.try_into().ok()?,
tv_nsec: timeout.tv_nsec.try_into().ok()?,
})
}
let old_timeout = if let Some(timeout) = timeout {
match convert(timeout) {
// Could not convert timeout.
None => None,
// Could convert timeout. Ok!
Some(old_timeout) => Some(Some(old_timeout)),
}
} else {
// No timeout. Ok!
Some(None)
};
if let Some(mut old_timeout) = old_timeout {
// Call `ppoll`.
//
// Linux's `ppoll` mutates the timeout argument. Our public
// interface does not do this, because it's not portable to other
// platforms, so we create a temporary value to hide this behavior.
return ret_usize(syscall!(
__NR_ppoll,
fds_addr_mut,
fds_len,
opt_mut(old_timeout.as_mut()),
zero(),
size_of::<kernel_sigset_t, _>()
));
}
}
// We either have Linux 5.1 or the timeout didn't fit in
// `__kernel_old_timespec` so `__NR_ppoll_time64` will either
// succeed or fail due to our having no other options.
// Call `ppoll_time64`.
//
// Linux's `ppoll_time64` mutates the timeout argument. Our public
// interface does not do this, because it's not portable to other
// platforms, so we create a temporary value to hide this behavior.
ret_usize(syscall!(
__NR_ppoll_time64,
fds_addr_mut,
fds_len,
opt_mut(timeout.copied().as_mut()),
zero(),
size_of::<kernel_sigset_t, _>()
))
}
#[cfg(target_pointer_width = "64")]
unsafe {
// Call `ppoll`.
//
// Linux's `ppoll` mutates the timeout argument. Our public interface
// does not do this, because it's not portable to other platforms, so
// we create a temporary value to hide this behavior.
ret_usize(syscall!(
__NR_ppoll,
fds_addr_mut,
fds_len,
opt_mut(timeout.copied().as_mut()),
zero(),
size_of::<kernel_sigset_t, _>()
))
}
}
pub(crate) unsafe fn select(
nfds: i32,
readfds: Option<&mut [FdSetElement]>,
writefds: Option<&mut [FdSetElement]>,
exceptfds: Option<&mut [FdSetElement]>,
timeout: Option<&crate::timespec::Timespec>,
) -> io::Result<i32> {
let len = crate::event::fd_set_num_elements_for_bitvector(nfds);
let readfds = match readfds {
Some(readfds) => {
assert!(readfds.len() >= len);
readfds.as_mut_ptr()
}
None => null_mut(),
};
let writefds = match writefds {
Some(writefds) => {
assert!(writefds.len() >= len);
writefds.as_mut_ptr()
}
None => null_mut(),
};
let exceptfds = match exceptfds {
Some(exceptfds) => {
assert!(exceptfds.len() >= len);
exceptfds.as_mut_ptr()
}
None => null_mut(),
};
#[cfg(target_pointer_width = "32")]
{
// If we don't have Linux 5.1, and the timeout fits in a
// `__kernel_old_timespec`, use plain `pselect6`.
//
// We do this unconditionally, rather than trying `pselect6_time64` and
// falling back on `Errno::NOSYS`, because seccomp configurations will
// sometimes abort the process on syscalls they don't recognize.
#[cfg(not(feature = "linux_5_1"))]
{
use linux_raw_sys::general::__kernel_old_timespec;
// If we don't have a timeout, or if we can convert the timeout to
// a `__kernel_old_timespec`, the use `__NR_pselect6`.
fn convert(timeout: &Timespec) -> Option<__kernel_old_timespec> {
Some(__kernel_old_timespec {
tv_sec: timeout.tv_sec.try_into().ok()?,
tv_nsec: timeout.tv_nsec.try_into().ok()?,
})
}
let old_timeout = if let Some(timeout) = timeout {
match convert(timeout) {
// Could not convert timeout.
None => None,
// Could convert timeout. Ok!
Some(old_timeout) => Some(Some(old_timeout)),
}
} else {
// No timeout. Ok!
Some(None)
};
if let Some(mut old_timeout) = old_timeout {
// Call `pselect6`.
//
// Linux's `pselect6` mutates the timeout argument. Our public
// interface does not do this, because it's not portable to other
// platforms, so we create a temporary value to hide this behavior.
return ret_c_int(syscall!(
__NR_pselect6,
c_int(nfds),
readfds,
writefds,
exceptfds,
opt_mut(old_timeout.as_mut()),
zero()
));
}
}
// We either have Linux 5.1 or the timeout didn't fit in
// `__kernel_old_timespec` so `__NR_pselect6_time64` will either
// succeed or fail due to our having no other options.
// Call `pselect6_time64`.
//
// Linux's `pselect6_time64` mutates the timeout argument. Our public
// interface does not do this, because it's not portable to other
// platforms, so we create a temporary value to hide this behavior.
ret_c_int(syscall!(
__NR_pselect6_time64,
c_int(nfds),
readfds,
writefds,
exceptfds,
opt_mut(timeout.copied().as_mut()),
zero()
))
}
#[cfg(target_pointer_width = "64")]
{
// Call `pselect6`.
//
// Linux's `pselect6` mutates the timeout argument. Our public interface
// does not do this, because it's not portable to other platforms, so we
// create a temporary value to hide this behavior.
ret_c_int(syscall!(
__NR_pselect6,
c_int(nfds),
readfds,
writefds,
exceptfds,
opt_mut(timeout.copied().as_mut()),
zero()
))
}
}
#[inline]
pub(crate) fn epoll_create(flags: epoll::CreateFlags) -> io::Result<OwnedFd> {
// SAFETY: `__NR_epoll_create1` doesn't access any user memory.
unsafe { ret_owned_fd(syscall_readonly!(__NR_epoll_create1, flags)) }
}
#[inline]
pub(crate) fn epoll_add(
epfd: BorrowedFd<'_>,
fd: BorrowedFd<'_>,
event: &epoll::Event,
) -> io::Result<()> {
// SAFETY: `__NR_epoll_ctl` with `EPOLL_CTL_ADD` doesn't modify any user
// memory, and it only reads from `event`.
unsafe {
ret(syscall_readonly!(
__NR_epoll_ctl,
epfd,
c_uint(EPOLL_CTL_ADD),
fd,
by_ref(event)
))
}
}
#[inline]
pub(crate) fn epoll_mod(
epfd: BorrowedFd<'_>,
fd: BorrowedFd<'_>,
event: &epoll::Event,
) -> io::Result<()> {
// SAFETY: `__NR_epoll_ctl` with `EPOLL_CTL_MOD` doesn't modify any user
// memory, and it only reads from `event`.
unsafe {
ret(syscall_readonly!(
__NR_epoll_ctl,
epfd,
c_uint(EPOLL_CTL_MOD),
fd,
by_ref(event)
))
}
}
#[inline]
pub(crate) fn epoll_del(epfd: BorrowedFd<'_>, fd: BorrowedFd<'_>) -> io::Result<()> {
// SAFETY: `__NR_epoll_ctl` with `EPOLL_CTL_DEL` doesn't access any user
// memory.
unsafe {
ret(syscall_readonly!(
__NR_epoll_ctl,
epfd,
c_uint(EPOLL_CTL_DEL),
fd,
zero()
))
}
}
#[inline]
pub(crate) unsafe fn epoll_wait(
epfd: BorrowedFd<'_>,
events: (*mut crate::event::epoll::Event, usize),
timeout: Option<&Timespec>,
) -> io::Result<usize> {
// If we don't have Linux 5.1, and the timeout fits in an `i32`, use plain
// `epoll_pwait`.
//
// We do this unconditionally, rather than trying `epoll_pwait2` and
// falling back on `Errno::NOSYS`, because seccomp configurations will
// sometimes abort the process on syscalls they don't recognize.
#[cfg(not(feature = "linux_5_11"))]
{
// If we don't have a timeout, or if we can convert the timeout to an
// `i32`, the use `__NR_epoll_pwait`.
let old_timeout = if let Some(timeout) = timeout {
// Try to convert the timeout; if this is `Some`, we're ok!
timeout.as_c_int_millis()
} else {
// No timeout. Ok!
Some(-1)
};
if let Some(old_timeout) = old_timeout {
// Call `epoll_pwait`.
return ret_usize(syscall!(
__NR_epoll_pwait,
epfd,
events.0,
pass_usize(events.1),
c_int(old_timeout),
zero()
));
}
}
// Call `epoll_pwait2`.
//
// We either have Linux 5.1 or the timeout didn't fit in an `i32`, so
// `__NR_epoll_pwait2` will either succeed or fail due to our having no
// other options.
ret_usize(syscall!(
__NR_epoll_pwait2,
epfd,
events.0,
pass_usize(events.1),
opt_ref(timeout),
zero()
))
}
#[inline]
pub(crate) fn eventfd(initval: u32, flags: EventfdFlags) -> io::Result<OwnedFd> {
unsafe { ret_owned_fd(syscall_readonly!(__NR_eventfd2, c_uint(initval), flags)) }
}
#[inline]
pub(crate) fn pause() {
unsafe {
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
let error = ret_error(syscall_readonly!(
__NR_ppoll,
zero(),
zero(),
zero(),
zero()
));
#[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))]
let error = ret_error(syscall_readonly!(__NR_pause));
debug_assert_eq!(error, io::Errno::INTR);
}
}

View File

@@ -0,0 +1,21 @@
use crate::ffi;
use bitflags::bitflags;
bitflags! {
/// `EFD_*` flags for use with [`eventfd`].
///
/// [`eventfd`]: crate::event::eventfd
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct EventfdFlags: ffi::c_uint {
/// `EFD_CLOEXEC`
const CLOEXEC = linux_raw_sys::general::EFD_CLOEXEC;
/// `EFD_NONBLOCK`
const NONBLOCK = linux_raw_sys::general::EFD_NONBLOCK;
/// `EFD_SEMAPHORE`
const SEMAPHORE = linux_raw_sys::general::EFD_SEMAPHORE;
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}

View File

@@ -0,0 +1,373 @@
use crate::fd::{AsFd, BorrowedFd, OwnedFd};
use crate::ffi::{CStr, CString};
use crate::fs::{
fcntl_getfl, fstat, fstatfs, fstatvfs, openat, FileType, Mode, OFlags, Stat, StatFs, StatVfs,
};
use crate::io;
#[cfg(feature = "process")]
use crate::process::fchdir;
use crate::utils::as_ptr;
use alloc::borrow::ToOwned as _;
use alloc::vec::Vec;
use core::fmt;
use core::mem::size_of;
use linux_raw_sys::general::{linux_dirent64, SEEK_SET};
/// `DIR*`
pub struct Dir {
/// The `OwnedFd` that we read directory entries from.
fd: OwnedFd,
/// Have we seen any errors in this iteration?
any_errors: bool,
/// Should we rewind the stream on the next iteration?
rewind: bool,
/// The buffer for `linux_dirent64` entries.
buf: Vec<u8>,
/// Where we are in the buffer.
pos: usize,
}
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> {
Ok(Self {
fd,
any_errors: false,
rewind: false,
buf: Vec::new(),
pos: 0,
})
}
/// 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]
fn _read_from(fd: BorrowedFd<'_>) -> io::Result<Self> {
let flags = fcntl_getfl(fd)?;
let fd_for_dir = openat(fd, cstr!("."), flags | OFlags::CLOEXEC, Mode::empty())?;
Ok(Self {
fd: fd_for_dir,
any_errors: false,
rewind: false,
buf: Vec::new(),
pos: 0,
})
}
/// `rewinddir(self)`
#[inline]
pub fn rewind(&mut self) {
self.any_errors = false;
self.rewind = true;
self.pos = self.buf.len();
}
/// `seekdir(self, offset)`
///
/// This function is only available on 64-bit platforms because it's
/// implemented using [`libc::seekdir`] which only supports offsets that
/// fit in a `c_long`.
///
/// [`libc::seekdir`]: https://docs.rs/libc/*/arm-unknown-linux-gnueabihf/libc/fn.seekdir.html
// In the linux_raw backend here, we don't use `libc::seekdir` and don't
// have this limitation, but it's a goal of rustix to support the same API
// on both the linux_raw and libc backends.
#[cfg(target_pointer_width = "64")]
#[cfg_attr(docsrs, doc(cfg(target_pointer_width = "64")))]
#[doc(alias = "seekdir")]
#[inline]
pub fn seek(&mut self, offset: i64) -> io::Result<()> {
self.any_errors = false;
self.rewind = false;
self.pos = self.buf.len();
match io::retry_on_intr(|| {
crate::backend::fs::syscalls::_seek(self.fd.as_fd(), offset, SEEK_SET)
}) {
Ok(_) => Ok(()),
Err(err) => {
self.any_errors = true;
Err(err)
}
}
}
/// `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 anything
// further.
if self.any_errors {
return None;
}
// If a rewind was requested, seek to the beginning.
if self.rewind {
self.rewind = false;
match io::retry_on_intr(|| {
crate::backend::fs::syscalls::_seek(self.fd.as_fd(), 0, SEEK_SET)
}) {
Ok(_) => (),
Err(err) => {
self.any_errors = true;
return Some(Err(err));
}
}
}
// Compute linux_dirent64 field offsets.
let z = linux_dirent64 {
d_ino: 0_u64,
d_off: 0_i64,
d_type: 0_u8,
d_reclen: 0_u16,
d_name: Default::default(),
};
let base = as_ptr(&z) as usize;
let offsetof_d_reclen = (as_ptr(&z.d_reclen) as usize) - base;
let offsetof_d_name = (as_ptr(&z.d_name) as usize) - base;
let offsetof_d_ino = (as_ptr(&z.d_ino) as usize) - base;
let offsetof_d_off = (as_ptr(&z.d_off) as usize) - base;
let offsetof_d_type = (as_ptr(&z.d_type) as usize) - base;
// Test if we need more entries, and if so, read more.
if self.buf.len() - self.pos < size_of::<linux_dirent64>() {
match self.read_more()? {
Ok(()) => (),
Err(err) => return Some(Err(err)),
}
}
// We successfully read an entry. Extract the fields.
let pos = self.pos;
// Do an unaligned u16 load.
let d_reclen = u16::from_ne_bytes([
self.buf[pos + offsetof_d_reclen],
self.buf[pos + offsetof_d_reclen + 1],
]);
assert!(self.buf.len() - pos >= d_reclen as usize);
self.pos += d_reclen as usize;
// Read the NUL-terminated name from the `d_name` field. Without
// `unsafe`, we need to scan for the NUL twice: once to obtain a size
// for the slice, and then once within `CStr::from_bytes_with_nul`.
let name_start = pos + offsetof_d_name;
let name_len = self.buf[name_start..]
.iter()
.position(|x| *x == b'\0')
.unwrap();
let name = CStr::from_bytes_with_nul(&self.buf[name_start..][..=name_len]).unwrap();
let name = name.to_owned();
assert!(name.as_bytes().len() <= self.buf.len() - name_start);
// Do an unaligned `u64` load for `d_ino`.
let d_ino = u64::from_ne_bytes([
self.buf[pos + offsetof_d_ino],
self.buf[pos + offsetof_d_ino + 1],
self.buf[pos + offsetof_d_ino + 2],
self.buf[pos + offsetof_d_ino + 3],
self.buf[pos + offsetof_d_ino + 4],
self.buf[pos + offsetof_d_ino + 5],
self.buf[pos + offsetof_d_ino + 6],
self.buf[pos + offsetof_d_ino + 7],
]);
// Do an unaligned `i64` load for `d_off`.
let d_off = i64::from_ne_bytes([
self.buf[pos + offsetof_d_off],
self.buf[pos + offsetof_d_off + 1],
self.buf[pos + offsetof_d_off + 2],
self.buf[pos + offsetof_d_off + 3],
self.buf[pos + offsetof_d_off + 4],
self.buf[pos + offsetof_d_off + 5],
self.buf[pos + offsetof_d_off + 6],
self.buf[pos + offsetof_d_off + 7],
]);
let d_type = self.buf[pos + offsetof_d_type];
// Check that our types correspond to the `linux_dirent64` types.
let _ = linux_dirent64 {
d_ino,
d_off,
d_type,
d_reclen,
d_name: Default::default(),
};
Some(Ok(DirEntry {
d_ino,
d_off,
d_type,
name,
}))
}
#[must_use]
fn read_more(&mut self) -> Option<io::Result<()>> {
// The first few times we're called, we allocate a relatively small
// buffer, because many directories are small. If we're called more,
// use progressively larger allocations, up to a fixed maximum.
//
// The specific sizes and policy here have not been tuned in detail yet
// and may need to be adjusted. In doing so, we should be careful to
// avoid unbounded buffer growth. This buffer only exists to share the
// cost of a `getdents` call over many entries, so if it gets too big,
// cache and heap usage will outweigh the benefit. And ultimately,
// directories can contain more entries than we can allocate contiguous
// memory for, so we'll always need to cap the size at some point.
if self.buf.len() < 1024 * size_of::<linux_dirent64>() {
self.buf.reserve(32 * size_of::<linux_dirent64>());
}
self.buf.resize(self.buf.capacity(), 0);
let nread = match io::retry_on_intr(|| {
crate::backend::fs::syscalls::getdents(self.fd.as_fd(), &mut self.buf)
}) {
Ok(nread) => nread,
Err(io::Errno::NOENT) => {
self.any_errors = true;
return None;
}
Err(err) => {
self.any_errors = true;
return Some(Err(err));
}
};
self.buf.resize(nread, 0);
self.pos = 0;
if nread == 0 {
None
} else {
Some(Ok(()))
}
}
/// `fstat(self)`
#[inline]
pub fn stat(&self) -> io::Result<Stat> {
fstat(&self.fd)
}
/// `fstatfs(self)`
#[inline]
pub fn statfs(&self) -> io::Result<StatFs> {
fstatfs(&self.fd)
}
/// `fstatvfs(self)`
#[inline]
pub fn statvfs(&self) -> io::Result<StatVfs> {
fstatvfs(&self.fd)
}
/// `fchdir(self)`
#[cfg(feature = "process")]
#[cfg_attr(docsrs, doc(cfg(feature = "process")))]
#[inline]
pub fn chdir(&self) -> io::Result<()> {
fchdir(&self.fd)
}
}
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 {
f.debug_struct("Dir").field("fd", &self.fd).finish()
}
}
/// `struct dirent`
#[derive(Debug)]
pub struct DirEntry {
d_ino: u64,
d_type: u8,
d_off: i64,
name: CString,
}
impl DirEntry {
/// Returns the file name of this directory entry.
#[inline]
pub fn file_name(&self) -> &CStr {
&self.name
}
/// Returns the “offset” of this directory entry. This is not a true
/// numerical offset but an opaque cookie that identifies a position in the
/// given stream.
#[inline]
pub fn offset(&self) -> i64 {
self.d_off
}
/// Returns the type of this directory entry.
#[inline]
pub fn file_type(&self) -> FileType {
FileType::from_dirent_d_type(self.d_type)
}
/// Return the inode number of this directory entry.
#[inline]
pub fn ino(&self) -> u64 {
self.d_ino
}
}
#[cfg(test)]
mod tests {
use super::*;
#[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 `getdents64` to fail.
crate::io::dup2(&file_fd, &mut dir.fd).unwrap();
assert!(matches!(dir.next(), Some(Err(_))));
assert!(dir.next().is_none());
}
}

View File

@@ -0,0 +1,124 @@
//! inotify support for working with inotify objects.
use crate::ffi;
use bitflags::bitflags;
bitflags! {
/// `IN_*` for use with [`inotify::init`].
///
/// [`inotify::init`]: crate::fs::inotify::init
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct CreateFlags: ffi::c_uint {
/// `IN_CLOEXEC`
const CLOEXEC = linux_raw_sys::general::IN_CLOEXEC;
/// `IN_NONBLOCK`
const NONBLOCK = linux_raw_sys::general::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::add_watch
#[repr(transparent)]
#[derive(Default, Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct WatchFlags: ffi::c_uint {
/// `IN_ACCESS`
const ACCESS = linux_raw_sys::general::IN_ACCESS;
/// `IN_ATTRIB`
const ATTRIB = linux_raw_sys::general::IN_ATTRIB;
/// `IN_CLOSE_NOWRITE`
const CLOSE_NOWRITE = linux_raw_sys::general::IN_CLOSE_NOWRITE;
/// `IN_CLOSE_WRITE`
const CLOSE_WRITE = linux_raw_sys::general::IN_CLOSE_WRITE;
/// `IN_CREATE`
const CREATE = linux_raw_sys::general::IN_CREATE;
/// `IN_DELETE`
const DELETE = linux_raw_sys::general::IN_DELETE;
/// `IN_DELETE_SELF`
const DELETE_SELF = linux_raw_sys::general::IN_DELETE_SELF;
/// `IN_MODIFY`
const MODIFY = linux_raw_sys::general::IN_MODIFY;
/// `IN_MOVE_SELF`
const MOVE_SELF = linux_raw_sys::general::IN_MOVE_SELF;
/// `IN_MOVED_FROM`
const MOVED_FROM = linux_raw_sys::general::IN_MOVED_FROM;
/// `IN_MOVED_TO`
const MOVED_TO = linux_raw_sys::general::IN_MOVED_TO;
/// `IN_OPEN`
const OPEN = linux_raw_sys::general::IN_OPEN;
/// `IN_CLOSE`
const CLOSE = linux_raw_sys::general::IN_CLOSE;
/// `IN_MOVE`
const MOVE = linux_raw_sys::general::IN_MOVE;
/// `IN_ALL_EVENTS`
const ALL_EVENTS = linux_raw_sys::general::IN_ALL_EVENTS;
/// `IN_DONT_FOLLOW`
const DONT_FOLLOW = linux_raw_sys::general::IN_DONT_FOLLOW;
/// `IN_EXCL_UNLINK`
const EXCL_UNLINK = linux_raw_sys::general::IN_EXCL_UNLINK;
/// `IN_MASK_ADD`
const MASK_ADD = linux_raw_sys::general::IN_MASK_ADD;
/// `IN_MASK_CREATE`
const MASK_CREATE = linux_raw_sys::general::IN_MASK_CREATE;
/// `IN_ONESHOT`
const ONESHOT = linux_raw_sys::general::IN_ONESHOT;
/// `IN_ONLYDIR`
const ONLYDIR = linux_raw_sys::general::IN_ONLYDIR;
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}
bitflags! {
/// `IN*` for use with [`inotify::Reader`].
///
/// [`inotify::Reader`]: crate::fs::inotify::InotifyReader
#[repr(transparent)]
#[derive(Default, Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct ReadFlags: ffi::c_uint {
/// `IN_ACCESS`
const ACCESS = linux_raw_sys::general::IN_ACCESS;
/// `IN_ATTRIB`
const ATTRIB = linux_raw_sys::general::IN_ATTRIB;
/// `IN_CLOSE_NOWRITE`
const CLOSE_NOWRITE = linux_raw_sys::general::IN_CLOSE_NOWRITE;
/// `IN_CLOSE_WRITE`
const CLOSE_WRITE = linux_raw_sys::general::IN_CLOSE_WRITE;
/// `IN_CREATE`
const CREATE = linux_raw_sys::general::IN_CREATE;
/// `IN_DELETE`
const DELETE = linux_raw_sys::general::IN_DELETE;
/// `IN_DELETE_SELF`
const DELETE_SELF = linux_raw_sys::general::IN_DELETE_SELF;
/// `IN_MODIFY`
const MODIFY = linux_raw_sys::general::IN_MODIFY;
/// `IN_MOVE_SELF`
const MOVE_SELF = linux_raw_sys::general::IN_MOVE_SELF;
/// `IN_MOVED_FROM`
const MOVED_FROM = linux_raw_sys::general::IN_MOVED_FROM;
/// `IN_MOVED_TO`
const MOVED_TO = linux_raw_sys::general::IN_MOVED_TO;
/// `IN_OPEN`
const OPEN = linux_raw_sys::general::IN_OPEN;
/// `IN_IGNORED`
const IGNORED = linux_raw_sys::general::IN_IGNORED;
/// `IN_ISDIR`
const ISDIR = linux_raw_sys::general::IN_ISDIR;
/// `IN_Q_OVERFLOW`
const QUEUE_OVERFLOW = linux_raw_sys::general::IN_Q_OVERFLOW;
/// `IN_UNMOUNT`
const UNMOUNT = linux_raw_sys::general::IN_UNMOUNT;
/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
}
}

View File

@@ -0,0 +1,19 @@
use crate::fs::Dev;
#[inline]
pub(crate) fn makedev(maj: u32, min: u32) -> Dev {
((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)
}
#[inline]
pub(crate) fn major(dev: Dev) -> u32 {
(((dev >> 31 >> 1) & 0xffff_f000) | ((dev >> 8) & 0x0000_0fff)) as u32
}
#[inline]
pub(crate) fn minor(dev: Dev) -> u32 {
(((dev >> 12) & 0xffff_ff00) | (dev & 0x0000_00ff)) as u32
}

Some files were not shown because too many files have changed in this diff Show More